commit 4622e4842cfe137557d8827c868cc760b49f69f1 Author: benweet Date: Sun Jul 23 19:42:08 2017 +0100 First commit diff --git a/.babelrc b/.babelrc new file mode 100644 index 00000000..13f0e471 --- /dev/null +++ b/.babelrc @@ -0,0 +1,14 @@ +{ + "presets": [ + ["env", { "modules": false }], + "stage-2" + ], + "plugins": ["transform-runtime"], + "comments": false, + "env": { + "test": { + "presets": ["env", "stage-2"], + "plugins": [ "istanbul" ] + } + } +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..9d08a1a8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..6cb140a6 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +build/*.js +config/*.js +src/cledit/*.js \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..36ae5337 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,40 @@ +// http://eslint.org/docs/user-guide/configuring + +module.exports = { + root: true, + parser: 'babel-eslint', + parserOptions: { + sourceType: 'module' + }, + env: { + browser: true, + }, + extends: 'airbnb-base', + // required to lint *.vue files + plugins: [ + 'html' + ], + // check if imports actually resolve + 'settings': { + 'import/resolver': { + 'webpack': { + 'config': 'build/webpack.base.conf.js' + } + } + }, + // add your custom rules here + 'rules': { + 'no-param-reassign': [2, { 'props': false }], + // don't require .vue extension when importing + 'import/extensions': ['error', 'always', { + 'js': 'never', + 'vue': 'never' + }], + // allow optionalDependencies + 'import/no-extraneous-dependencies': ['error', { + 'optionalDependencies': ['test/unit/index.js'] + }], + // allow debugger during development + 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..44bcd428 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +node_modules/ +dist/ +.history +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/.postcssrc.js b/.postcssrc.js new file mode 100644 index 00000000..ea9a5ab8 --- /dev/null +++ b/.postcssrc.js @@ -0,0 +1,8 @@ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + "plugins": { + // to edit target browsers: use "browserlist" field in package.json + "autoprefixer": {} + } +} diff --git a/.stylelintrc b/.stylelintrc new file mode 100644 index 00000000..e9549f87 --- /dev/null +++ b/.stylelintrc @@ -0,0 +1,7 @@ +{ + "processors": ["stylelint-processor-html"], + "extends": "stylelint-config-standard", + "rules": { + "no-empty-source": null + } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..2fa7d831 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# my-project + +> A Vue.js project + +## Build Setup + +``` bash +# install dependencies +npm install + +# serve with hot reload at localhost:8080 +npm run dev + +# build for production with minification +npm run build + +# build for production and view the bundle analyzer report +npm run build --report +``` + +For detailed explanation on how things work, checkout the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). diff --git a/build/build.js b/build/build.js new file mode 100644 index 00000000..6b8add10 --- /dev/null +++ b/build/build.js @@ -0,0 +1,35 @@ +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +var ora = require('ora') +var rm = require('rimraf') +var path = require('path') +var chalk = require('chalk') +var webpack = require('webpack') +var config = require('../config') +var webpackConfig = require('./webpack.prod.conf') + +var spinner = ora('building for production...') +spinner.start() + +rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { + if (err) throw err + webpack(webpackConfig, function (err, stats) { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, + chunks: false, + chunkModules: false + }) + '\n\n') + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) + }) +}) diff --git a/build/check-versions.js b/build/check-versions.js new file mode 100644 index 00000000..100f3a0f --- /dev/null +++ b/build/check-versions.js @@ -0,0 +1,48 @@ +var chalk = require('chalk') +var semver = require('semver') +var packageConfig = require('../package.json') +var shell = require('shelljs') +function exec (cmd) { + return require('child_process').execSync(cmd).toString().trim() +} + +var versionRequirements = [ + { + name: 'node', + currentVersion: semver.clean(process.version), + versionRequirement: packageConfig.engines.node + }, +] + +if (shell.which('npm')) { + versionRequirements.push({ + name: 'npm', + currentVersion: exec('npm --version'), + versionRequirement: packageConfig.engines.npm + }) +} + +module.exports = function () { + var warnings = [] + for (var i = 0; i < versionRequirements.length; i++) { + var mod = versionRequirements[i] + if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { + warnings.push(mod.name + ': ' + + chalk.red(mod.currentVersion) + ' should be ' + + chalk.green(mod.versionRequirement) + ) + } + } + + if (warnings.length) { + console.log('') + console.log(chalk.yellow('To use this template, you must update following to modules:')) + console.log() + for (var i = 0; i < warnings.length; i++) { + var warning = warnings[i] + console.log(' ' + warning) + } + console.log() + process.exit(1) + } +} diff --git a/build/dev-client.js b/build/dev-client.js new file mode 100644 index 00000000..18aa1e21 --- /dev/null +++ b/build/dev-client.js @@ -0,0 +1,9 @@ +/* eslint-disable */ +require('eventsource-polyfill') +var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true') + +hotClient.subscribe(function (event) { + if (event.action === 'reload') { + window.location.reload() + } +}) diff --git a/build/dev-server.js b/build/dev-server.js new file mode 100644 index 00000000..782dc6fc --- /dev/null +++ b/build/dev-server.js @@ -0,0 +1,89 @@ +require('./check-versions')() + +var config = require('../config') +if (!process.env.NODE_ENV) { + process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV) +} + +var opn = require('opn') +var path = require('path') +var express = require('express') +var webpack = require('webpack') +var proxyMiddleware = require('http-proxy-middleware') +var webpackConfig = require('./webpack.dev.conf') + +// default port where dev server listens for incoming traffic +var port = process.env.PORT || config.dev.port +// automatically open browser, if not set will be false +var autoOpenBrowser = !!config.dev.autoOpenBrowser +// Define HTTP proxies to your custom API backend +// https://github.com/chimurai/http-proxy-middleware +var proxyTable = config.dev.proxyTable + +var app = express() +var compiler = webpack(webpackConfig) + +var devMiddleware = require('webpack-dev-middleware')(compiler, { + publicPath: webpackConfig.output.publicPath, + quiet: true +}) + +var hotMiddleware = require('webpack-hot-middleware')(compiler, { + log: () => {} +}) +// force page reload when html-webpack-plugin template changes +compiler.plugin('compilation', function (compilation) { + compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { + hotMiddleware.publish({ action: 'reload' }) + cb() + }) +}) + +// proxy api requests +Object.keys(proxyTable).forEach(function (context) { + var options = proxyTable[context] + if (typeof options === 'string') { + options = { target: options } + } + app.use(proxyMiddleware(options.filter || context, options)) +}) + +// handle fallback for HTML5 history API +app.use(require('connect-history-api-fallback')()) + +// serve webpack bundle output +app.use(devMiddleware) + +// enable hot-reload and state-preserving +// compilation error display +app.use(hotMiddleware) + +// serve pure static assets +var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory) +app.use(staticPath, express.static('./static')) + +var uri = 'http://localhost:' + port + +var _resolve +var readyPromise = new Promise(resolve => { + _resolve = resolve +}) + +console.log('> Starting dev server...') +devMiddleware.waitUntilValid(() => { + console.log('> Listening at ' + uri + '\n') + // when env is testing, don't need open it + if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') { + opn(uri) + } + _resolve() +}) + +var server = app.listen(port) + +module.exports = { + ready: readyPromise, + close: () => { + server.close() + } +} diff --git a/build/utils.js b/build/utils.js new file mode 100644 index 00000000..b1d54b4d --- /dev/null +++ b/build/utils.js @@ -0,0 +1,71 @@ +var path = require('path') +var config = require('../config') +var ExtractTextPlugin = require('extract-text-webpack-plugin') + +exports.assetsPath = function (_path) { + var assetsSubDirectory = process.env.NODE_ENV === 'production' + ? config.build.assetsSubDirectory + : config.dev.assetsSubDirectory + return path.posix.join(assetsSubDirectory, _path) +} + +exports.cssLoaders = function (options) { + options = options || {} + + var cssLoader = { + loader: 'css-loader', + options: { + minimize: process.env.NODE_ENV === 'production', + sourceMap: options.sourceMap + } + } + + // generate loader string to be used with extract text plugin + function generateLoaders (loader, loaderOptions) { + var loaders = [cssLoader] + if (loader) { + loaders.push({ + loader: loader + '-loader', + options: Object.assign({}, loaderOptions, { + sourceMap: options.sourceMap + }) + }) + } + + // Extract CSS when that option is specified + // (which is the case during production build) + if (options.extract) { + return ExtractTextPlugin.extract({ + use: loaders, + fallback: 'vue-style-loader' + }) + } else { + return ['vue-style-loader'].concat(loaders) + } + } + + // https://vue-loader.vuejs.org/en/configurations/extract-css.html + return { + css: generateLoaders(), + postcss: generateLoaders(), + less: generateLoaders('less'), + sass: generateLoaders('sass', { indentedSyntax: true }), + scss: generateLoaders('sass'), + stylus: generateLoaders('stylus'), + styl: generateLoaders('stylus') + } +} + +// Generate loaders for standalone style files (outside of .vue) +exports.styleLoaders = function (options) { + var output = [] + var loaders = exports.cssLoaders(options) + for (var extension in loaders) { + var loader = loaders[extension] + output.push({ + test: new RegExp('\\.' + extension + '$'), + use: loader + }) + } + return output +} diff --git a/build/vue-loader.conf.js b/build/vue-loader.conf.js new file mode 100644 index 00000000..7aee79ba --- /dev/null +++ b/build/vue-loader.conf.js @@ -0,0 +1,12 @@ +var utils = require('./utils') +var config = require('../config') +var isProduction = process.env.NODE_ENV === 'production' + +module.exports = { + loaders: utils.cssLoaders({ + sourceMap: isProduction + ? config.build.productionSourceMap + : config.dev.cssSourceMap, + extract: isProduction + }) +} diff --git a/build/webpack.base.conf.js b/build/webpack.base.conf.js new file mode 100644 index 00000000..57dbb5fa --- /dev/null +++ b/build/webpack.base.conf.js @@ -0,0 +1,76 @@ +var path = require('path') +var utils = require('./utils') +var config = require('../config') +var vueLoaderConfig = require('./vue-loader.conf') +var StylelintPlugin = require('stylelint-webpack-plugin') + +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + +module.exports = { + entry: { + app: './src/' + }, + output: { + path: config.build.assetsRoot, + filename: '[name].js', + publicPath: process.env.NODE_ENV === 'production' + ? config.build.assetsPublicPath + : config.dev.assetsPublicPath + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + '@': resolve('src') + } + }, + module: { + rules: [ + { + test: /\.(js|vue)$/, + loader: 'eslint-loader', + enforce: 'pre', + include: [resolve('src'), resolve('test')], + options: { + formatter: require('eslint-friendly-formatter') + } + }, + { + test: /\.vue$/, + loader: 'vue-loader', + options: vueLoaderConfig + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test')] + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + }, + { + test: /\.md$/, + loader: 'raw-loader' + } + ] + }, + plugins: [ + new StylelintPlugin({ + files: ['**/*.vue', '**/*.scss'] + }) + ] +} diff --git a/build/webpack.dev.conf.js b/build/webpack.dev.conf.js new file mode 100644 index 00000000..cd92c4a3 --- /dev/null +++ b/build/webpack.dev.conf.js @@ -0,0 +1,35 @@ +var utils = require('./utils') +var webpack = require('webpack') +var config = require('../config') +var merge = require('webpack-merge') +var baseWebpackConfig = require('./webpack.base.conf') +var HtmlWebpackPlugin = require('html-webpack-plugin') +var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') + +// add hot-reload related code to entry chunks +Object.keys(baseWebpackConfig.entry).forEach(function (name) { + baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name]) +}) + +module.exports = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap }) + }, + // cheap-module-eval-source-map is faster for development + devtool: 'source-map', + plugins: [ + new webpack.DefinePlugin({ + 'process.env': config.dev.env + }), + // https://github.com/glenjamin/webpack-hot-middleware#installation--usage + new webpack.HotModuleReplacementPlugin(), + new webpack.NoEmitOnErrorsPlugin(), + // https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', + template: 'index.html', + inject: true + }), + new FriendlyErrorsPlugin() + ] +}) diff --git a/build/webpack.prod.conf.js b/build/webpack.prod.conf.js new file mode 100644 index 00000000..da44b656 --- /dev/null +++ b/build/webpack.prod.conf.js @@ -0,0 +1,120 @@ +var path = require('path') +var utils = require('./utils') +var webpack = require('webpack') +var config = require('../config') +var merge = require('webpack-merge') +var baseWebpackConfig = require('./webpack.base.conf') +var CopyWebpackPlugin = require('copy-webpack-plugin') +var HtmlWebpackPlugin = require('html-webpack-plugin') +var ExtractTextPlugin = require('extract-text-webpack-plugin') +var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') + +var env = config.build.env + +var webpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ + sourceMap: config.build.productionSourceMap, + extract: true + }) + }, + devtool: config.build.productionSourceMap ? '#source-map' : false, + output: { + path: config.build.assetsRoot, + filename: utils.assetsPath('js/[name].[chunkhash].js'), + chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') + }, + plugins: [ + // http://vuejs.github.io/vue-loader/en/workflow/production.html + new webpack.DefinePlugin({ + 'process.env': env + }), + new webpack.optimize.UglifyJsPlugin({ + compress: { + warnings: false + }, + sourceMap: true + }), + // extract css into its own file + new ExtractTextPlugin({ + filename: utils.assetsPath('css/[name].[contenthash].css') + }), + // Compress extracted CSS. We are using this plugin so that possible + // duplicated CSS from different components can be deduped. + new OptimizeCSSPlugin({ + cssProcessorOptions: { + safe: true + } + }), + // generate dist index.html with correct asset hash for caching. + // you can customize output by editing /index.html + // see https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: config.build.index, + template: 'index.html', + inject: true, + minify: { + removeComments: true, + collapseWhitespace: true, + removeAttributeQuotes: true + // more options: + // https://github.com/kangax/html-minifier#options-quick-reference + }, + // necessary to consistently work with multiple chunks via CommonsChunkPlugin + chunksSortMode: 'dependency' + }), + // split vendor js into its own file + new webpack.optimize.CommonsChunkPlugin({ + name: 'vendor', + minChunks: function (module, count) { + // any required modules inside node_modules are extracted to vendor + return ( + module.resource && + /\.js$/.test(module.resource) && + module.resource.indexOf( + path.join(__dirname, '../node_modules') + ) === 0 + ) + } + }), + // extract webpack runtime and module manifest to its own file in order to + // prevent vendor hash from being updated whenever app bundle is updated + new webpack.optimize.CommonsChunkPlugin({ + name: 'manifest', + chunks: ['vendor'] + }), + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.build.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +if (config.build.productionGzip) { + var CompressionWebpackPlugin = require('compression-webpack-plugin') + + webpackConfig.plugins.push( + new CompressionWebpackPlugin({ + asset: '[path].gz[query]', + algorithm: 'gzip', + test: new RegExp( + '\\.(' + + config.build.productionGzipExtensions.join('|') + + ')$' + ), + threshold: 10240, + minRatio: 0.8 + }) + ) +} + +if (config.build.bundleAnalyzerReport) { + var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin + webpackConfig.plugins.push(new BundleAnalyzerPlugin()) +} + +module.exports = webpackConfig diff --git a/config/dev.env.js b/config/dev.env.js new file mode 100644 index 00000000..efead7c8 --- /dev/null +++ b/config/dev.env.js @@ -0,0 +1,6 @@ +var merge = require('webpack-merge') +var prodEnv = require('./prod.env') + +module.exports = merge(prodEnv, { + NODE_ENV: '"development"' +}) diff --git a/config/index.js b/config/index.js new file mode 100644 index 00000000..196da1fa --- /dev/null +++ b/config/index.js @@ -0,0 +1,38 @@ +// see http://vuejs-templates.github.io/webpack for documentation. +var path = require('path') + +module.exports = { + build: { + env: require('./prod.env'), + index: path.resolve(__dirname, '../dist/index.html'), + assetsRoot: path.resolve(__dirname, '../dist'), + assetsSubDirectory: 'static', + assetsPublicPath: '/', + productionSourceMap: true, + // Gzip off by default as many popular static hosts such as + // Surge or Netlify already gzip all static assets for you. + // Before setting to `true`, make sure to: + // npm install --save-dev compression-webpack-plugin + productionGzip: false, + productionGzipExtensions: ['js', 'css'], + // Run the build command with an extra argument to + // View the bundle analyzer report after build finishes: + // `npm run build --report` + // Set to `true` or `false` to always turn it on or off + bundleAnalyzerReport: process.env.npm_config_report + }, + dev: { + env: require('./dev.env'), + port: 8080, + autoOpenBrowser: true, + assetsSubDirectory: 'static', + assetsPublicPath: '/', + proxyTable: {}, + // CSS Sourcemaps off by default because relative paths are "buggy" + // with this option, according to the CSS-Loader README + // (https://github.com/webpack/css-loader#sourcemaps) + // In our experience, they generally work as expected, + // just be aware of this issue when enabling this option. + cssSourceMap: false + } +} diff --git a/config/prod.env.js b/config/prod.env.js new file mode 100644 index 00000000..773d263d --- /dev/null +++ b/config/prod.env.js @@ -0,0 +1,3 @@ +module.exports = { + NODE_ENV: '"production"' +} diff --git a/index.html b/index.html new file mode 100644 index 00000000..a8261783 --- /dev/null +++ b/index.html @@ -0,0 +1,11 @@ + + + + + my-project + + +
+ + + diff --git a/package.json b/package.json new file mode 100644 index 00000000..62e1073b --- /dev/null +++ b/package.json @@ -0,0 +1,89 @@ +{ + "name": "my-project", + "version": "1.0.0", + "description": "A Vue.js project", + "author": "", + "private": true, + "scripts": { + "dev": "node build/dev-server.js", + "start": "node build/dev-server.js", + "build": "node build/build.js", + "lint": "eslint --ext .js,.vue src" + }, + "dependencies": { + "bezier-easing": "^1.1.0", + "clunderscore": "^1.0.3", + "diff-match-patch": "^1.0.0", + "markdown-it": "^8.3.1", + "markdown-it-abbr": "^1.0.4", + "markdown-it-deflist": "^2.0.2", + "markdown-it-emoji": "^1.3.0", + "markdown-it-footnote": "^3.0.1", + "markdown-it-mathjax": "^2.0.0", + "markdown-it-pandoc-renderer": "1.1.3", + "markdown-it-sub": "^1.0.0", + "markdown-it-sup": "^1.0.0", + "normalize-scss": "^7.0.0", + "prismjs": "^1.6.0", + "raw-loader": "^0.5.1", + "vue": "^2.3.3", + "vuex": "^2.3.1" + }, + "devDependencies": { + "autoprefixer": "^6.7.2", + "babel-core": "^6.22.1", + "babel-eslint": "^7.1.1", + "babel-loader": "^6.2.10", + "babel-plugin-transform-runtime": "^6.22.0", + "babel-preset-env": "^1.3.2", + "babel-preset-stage-2": "^6.22.0", + "babel-register": "^6.22.0", + "chalk": "^1.1.3", + "connect-history-api-fallback": "^1.3.0", + "copy-webpack-plugin": "^4.0.1", + "css-loader": "^0.28.4", + "eslint": "^3.19.0", + "eslint-config-airbnb-base": "^11.1.3", + "eslint-friendly-formatter": "^2.0.7", + "eslint-import-resolver-webpack": "^0.8.1", + "eslint-loader": "^1.7.1", + "eslint-plugin-html": "^2.0.0", + "eslint-plugin-import": "^2.2.0", + "eventsource-polyfill": "^0.9.6", + "express": "^4.14.1", + "extract-text-webpack-plugin": "^2.0.0", + "file-loader": "^0.11.1", + "friendly-errors-webpack-plugin": "^1.1.3", + "html-webpack-plugin": "^2.28.0", + "http-proxy-middleware": "^0.17.3", + "node-sass": "^4.5.3", + "opn": "^4.0.2", + "optimize-css-assets-webpack-plugin": "^1.3.0", + "ora": "^1.2.0", + "rimraf": "^2.6.0", + "sass-loader": "^6.0.5", + "semver": "^5.3.0", + "shelljs": "^0.7.6", + "stylelint-config-standard": "^16.0.0", + "stylelint-processor-html": "^1.0.0", + "stylelint-webpack-plugin": "^0.7.0", + "url-loader": "^0.5.8", + "vue-loader": "^12.1.0", + "vue-style-loader": "^3.0.1", + "vue-template-compiler": "^2.3.3", + "webpack": "^2.6.1", + "webpack-bundle-analyzer": "^2.2.1", + "webpack-dev-middleware": "^1.10.0", + "webpack-hot-middleware": "^2.18.0", + "webpack-merge": "^4.1.0" + }, + "engines": { + "node": ">= 4.0.0", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ] +} diff --git a/src/assets/logo.png b/src/assets/logo.png new file mode 100644 index 00000000..f3d2503f Binary files /dev/null and b/src/assets/logo.png differ diff --git a/src/cledit/cldiffutils.js b/src/cledit/cldiffutils.js new file mode 100644 index 00000000..1c1e2fb4 --- /dev/null +++ b/src/cledit/cldiffutils.js @@ -0,0 +1,482 @@ +import 'clunderscore'; +import DiffMatchPatch from 'diff-match-patch'; + +var clDiffUtils = { + cloneObject: cloneObject, + offsetToPatch: offsetToPatch, + patchToOffset: patchToOffset, + serializeObject: serializeObject, + flattenContent: flattenContent, + makePatchableText: makePatchableText, + restoreDiscussionOffsets: restoreDiscussionOffsets, + makeContentChange: makeContentChange, + applyContentChanges: applyContentChanges, + getTextPatches: getTextPatches, + getObjectPatches: getObjectPatches, + quickPatch: quickPatch, + mergeObjects: mergeObjects, + mergeFlattenContent: mergeFlattenContent +} + +var marker = '\uF111\uF222\uF333\uF444' +var DIFF_DELETE = -1 +var DIFF_INSERT = 1 +var DIFF_EQUAL = 0 +var diffMatchPatch = new DiffMatchPatch() // eslint-disable-line new-cap +var diffMatchPatchStrict = new DiffMatchPatch() // eslint-disable-line new-cap +diffMatchPatchStrict.Match_Threshold = 0 +diffMatchPatchStrict.Patch_DeleteThreshold = 0 +var diffMatchPatchPermissive = new DiffMatchPatch() // eslint-disable-line new-cap +diffMatchPatchPermissive.Match_Distance = 999999999 + +function cloneObject (obj) { + return JSON.parse(JSON.stringify(obj)) +} + +function offsetToPatch (text, offset) { + var patch = diffMatchPatchPermissive.patch_make(text, [ + [0, text.slice(0, offset)], + [1, marker], + [0, text.slice(offset)] + ])[0] + var diffs = patch.diffs.cl_map(function (diff) { + if (!diff[0]) { + return diff[1] + } else if (diff[1] === marker) { + return '' + } + }) + return { + diffs: diffs, + length: patch.length1, + start: patch.start1 + } +} + +function patchToOffset (text, patch) { + var markersLength = 0 + var diffs = patch.diffs.cl_map(function (diff) { + if (!diff) { + markersLength += marker.length + return [1, marker] + } else { + return [0, diff] + } + }) + return diffMatchPatchPermissive.patch_apply([{ + diffs: diffs, + length1: patch.length, + length2: patch.length + markersLength, + start1: patch.start, + start2: patch.start + }], text)[0].indexOf(marker) +} + +function flattenObject (obj) { + return obj.cl_reduce(function (result, value, key) { + result[key] = value[1] + return result + }, {}) +} + +function flattenContent (content) { + var result = ({}).cl_extend(content) + result.properties = flattenObject(content.properties) + result.discussions = flattenObject(content.discussions) + result.comments = flattenObject(content.comments) + result.text = content.text.cl_reduce(function (text, item) { + switch (item.type) { + case 'discussion': + if (result.discussions[item.id]) { + result.discussions[item.id][item.name] = text.length + } + return text + default: + return text + item[1] + } + }, '') + return result +} + +function getTextPatches (oldText, newText) { + var diffs = diffMatchPatch.diff_main(oldText, newText) + diffMatchPatch.diff_cleanupEfficiency(diffs) + var patches = [] + var startOffset = 0 + diffs.cl_each(function (change) { + var changeType = change[0] + var changeText = change[1] + switch (changeType) { + case DIFF_EQUAL: + startOffset += changeText.length + break + case DIFF_DELETE: + changeText && patches.push({ + o: startOffset, + d: changeText + }) + break + case DIFF_INSERT: + changeText && patches.push({ + o: startOffset, + a: changeText + }) + startOffset += changeText.length + break + } + }) + return patches.length ? patches : undefined +} + +function getObjectPatches (oldObject, newObjects) { + var valueHash = Object.create(null) + var valueArray = [] + oldObject = hashObject(oldObject, valueHash, valueArray) + newObjects = hashObject(newObjects, valueHash, valueArray) + var diffs = diffMatchPatch.diff_main(oldObject, newObjects) + var patches = [] + diffs.cl_each(function (change) { + var changeType = change[0] + var changeHash = change[1] + if (changeType === DIFF_EQUAL) { + return + } + changeHash.split('').cl_each(function (objHash) { + var obj = valueArray[objHash.charCodeAt(0)] + var patch = { + k: obj[0] + } + patch[changeType === DIFF_DELETE ? 'd' : 'a'] = obj[1] + patches.push(patch) + }) + }) + return patches.length ? patches : undefined +} + +function makePatchableText (content, markerKeys, markerIdxMap) { + var markers = [] + // Sort keys to have predictable marker positions, in case of same offset + var discussionKeys = Object.keys(content.discussions).sort() + discussionKeys.cl_each(function (discussionId) { + function addMarker (offsetName) { + var markerKey = discussionId + offsetName + if (discussion[offsetName] !== undefined) { + var idx = markerIdxMap[markerKey] + if (idx === undefined) { + idx = markerKeys.length + markerIdxMap[markerKey] = idx + markerKeys.push({ + id: discussionId, + offsetName: offsetName + }) + } + markers.push({ + idx: idx, + offset: discussion[offsetName] + }) + } + } + + var discussion = content.discussions[discussionId] + if (discussion.offset0 === discussion.offset1) { + // Remove discussion offsets if markers are at the same position + discussion.offset0 = discussion.offset1 = undefined + } else { + addMarker('offset0') + addMarker('offset1') + } + }) + + var lastOffset = 0 + var result = '' + markers + .sort(function (marker1, marker2) { + return marker1.offset - marker2.offset + }) + .cl_each(function (marker) { + result += + content.text.slice(lastOffset, marker.offset) + + String.fromCharCode(0xe000 + marker.idx) // Use a character from the private use area + lastOffset = marker.offset + }) + return result + content.text.slice(lastOffset) +} + +function stripDiscussionOffsets (objectMap) { + return objectMap.cl_reduce(function (result, object, id) { + result[id] = { + text: object.text + } + return result + }, {}) +} + +function restoreDiscussionOffsets (content, markerKeys) { + var len = content.text.length + var maxIdx = markerKeys.length + for (var i = 0; i < len; i++) { + var idx = content.text.charCodeAt(i) - 0xe000 + if (idx >= 0 && idx < maxIdx) { + var markerKey = markerKeys[idx] + content.text = content.text.slice(0, i) + content.text.slice(i + 1) + var discussion = content.discussions[markerKey.id] + if (discussion) { + discussion[markerKey.offsetName] = i + } + i-- // We just removed the current character, we may have multiple markers with same offset + } + } +} + +function makeContentChange (oldContent, newContent) { + var markerKeys = [] + var markerIdxMap = Object.create(null) + var oldText = makePatchableText(oldContent, markerKeys, markerIdxMap) + var newText = makePatchableText(newContent, markerKeys, markerIdxMap) + var textPatches = getTextPatches(oldText, newText) + textPatches && textPatches.cl_each(function (patch) { + // If markers are present, replace changeText with an array of text and markers + var changeText = patch.a || patch.d + var textItems = [] + var lastItem = '' + var len = changeText.length + var maxIdx = markerKeys.length + for (var i = 0; i < len; i++) { + var idx = changeText.charCodeAt(i) - 0xe000 + if (idx >= 0 && idx < maxIdx) { + var markerKey = markerKeys[idx] + lastItem.length && textItems.push(lastItem) + textItems.push({ + type: 'discussion', + name: markerKey.offsetName, + id: markerKey.id + }) + lastItem = '' + } else { + lastItem += changeText[i] + } + } + if (textItems.length) { + lastItem.length && textItems.push(lastItem) + if (patch.a) { + patch.a = textItems + } else { + patch.d = textItems + } + } + }) + var propertiesPatches = getObjectPatches(oldContent.properties, newContent.properties) + var discussionsPatches = getObjectPatches( + stripDiscussionOffsets(oldContent.discussions), + stripDiscussionOffsets(newContent.discussions) + ) + var commentsPatches = getObjectPatches(oldContent.comments, newContent.comments) + if (textPatches || propertiesPatches || discussionsPatches || commentsPatches) { + return { + text: textPatches, + properties: propertiesPatches, + discussions: discussionsPatches, + comments: commentsPatches + } + } +} + +function applyContentChanges (content, contentChanges, isBackward) { + function applyObjectPatches (obj, patches) { + if (patches) { + patches.cl_each(function (patch) { + if (!patch.a ^ !isBackward) { + obj[patch.k] = patch.a || patch.d + } else { + delete obj[patch.k] + } + }) + } + } + + var markerKeys = [] + var markerIdxMap = Object.create(null) + var result = { + text: makePatchableText(content, markerKeys, markerIdxMap), + properties: cloneObject(content.properties), + discussions: stripDiscussionOffsets(content.discussions), + comments: cloneObject(content.comments) + } + + contentChanges.cl_each(function (contentChange) { + var textPatches = contentChange.text || [] + if (isBackward) { + textPatches = textPatches.slice().reverse() + } + result.text = textPatches.cl_reduce(function (text, patch) { + var isAdd = !patch.a ^ !isBackward + var textChanges = patch.a || patch.d || '' + // When no marker is present, textChanges is a string + if (typeof textChanges === 'string') { + textChanges = [textChanges] + } + var textChange = textChanges.cl_map(function (textChange) { + if (!textChange.type) { + // textChange is a string + return textChange + } + // textChange is a marker + var markerKey = textChange.id + textChange.name + var idx = markerIdxMap[markerKey] + if (idx === undefined) { + idx = markerKeys.length + markerIdxMap[markerKey] = idx + markerKeys.push({ + id: textChange.id, + offsetName: textChange.name + }) + } + return String.fromCharCode(0xe000 + idx) + }).join('') + if (!textChange) { + return text + } else if (isAdd) { + return text.slice(0, patch.o).concat(textChange).concat(text.slice(patch.o)) + } else { + return text.slice(0, patch.o).concat(text.slice(patch.o + textChange.length)) + } + }, result.text) + + applyObjectPatches(result.properties, contentChange.properties) + applyObjectPatches(result.discussions, contentChange.discussions) + applyObjectPatches(result.comments, contentChange.comments) + }) + + restoreDiscussionOffsets(result, markerKeys) + return result +} + +function serializeObject (obj) { + return JSON.stringify(obj, function (key, value) { + return Object.prototype.toString.call(value) === '[object Object]' + ? Object.keys(value).sort().cl_reduce(function (sorted, key) { + sorted[key] = value[key] + return sorted + }, {}) + : value + }) +} + +function hashArray (arr, valueHash, valueArray) { + var hash = [] + arr.cl_each(function (obj) { + var serializedObj = serializeObject(obj) + var objHash = valueHash[serializedObj] + if (objHash === undefined) { + objHash = valueArray.length + valueArray.push(obj) + valueHash[serializedObj] = objHash + } + hash.push(objHash) + }) + return String.fromCharCode.apply(null, hash) +} + +function hashObject (obj, valueHash, valueArray) { + return hashArray(Object.keys(obj || {}).sort().cl_map(function (key) { + return [key, obj[key]] + }), valueHash, valueArray) +} + +function mergeText (oldText, newText, serverText) { + var diffs = diffMatchPatch.diff_main(oldText, newText) + diffMatchPatch.diff_cleanupSemantic(diffs) + var patches = diffMatchPatch.patch_make(oldText, diffs) + var patchResult = diffMatchPatch.patch_apply(patches, serverText) + if (!patchResult[1] + .cl_some(function (changeApplied) { + return !changeApplied + })) { + return patchResult[0] + } + + diffs = diffMatchPatchStrict.diff_main(patchResult[0], newText) + diffMatchPatch.diff_cleanupSemantic(diffs) + return diffs.cl_map(function (diff) { + return diff[1] + }).join('') +} + +function quickPatch (oldStr, newStr, destStr, strict) { + var dmp = strict ? diffMatchPatchStrict : diffMatchPatch + var diffs = dmp.diff_main(oldStr, newStr) + var patches = dmp.patch_make(oldStr, diffs) + var patchResult = dmp.patch_apply(patches, destStr) + return patchResult[0] +} + +function mergeObjects (oldObject, newObject, serverObject) { + var mergedObject = ({}).cl_extend(newObject).cl_extend(serverObject) + mergedObject.cl_each(function (value, key) { + if (!oldObject[key]) { + return // There might be conflict, keep the server value + } + var newValue = newObject[key] && serializeObject(newObject[key]) + var serverValue = serverObject[key] && serializeObject(serverObject[key]) + if (newValue === serverValue) { + return // no conflict + } + var oldValue = serializeObject(oldObject[key]) + if (oldValue !== newValue && !serverValue) { + return // Removed on server but changed on client + } + if (oldValue !== serverValue && !newValue) { + return // Removed on client but changed on server + } + if (oldValue !== newValue && oldValue === serverValue) { + // Take the client value + if (!newValue) { + delete mergedObject[key] + } else { + mergedObject[key] = newObject[key] + } + } else if (oldValue !== serverValue && oldValue === newValue) { + // Take the server value + if (!serverValue) { + delete mergedObject[key] + } + } + // Take the server value otherwise + }) + return cloneObject(mergedObject) +} + +function mergeFlattenContent (oldContent, newContent, serverContent) { + var markerKeys = [] + var markerIdxMap = Object.create(null) + var oldText = makePatchableText(oldContent, markerKeys, markerIdxMap) + var serverText = makePatchableText(serverContent, markerKeys, markerIdxMap) + var localText = makePatchableText(newContent, markerKeys, markerIdxMap) + var isServerTextChanges = oldText !== serverText + var isTextSynchronized = serverText === localText + + var result = { + text: isTextSynchronized || !isServerTextChanges + ? localText + : mergeText(oldText, serverText, localText), + properties: mergeObjects( + oldContent.properties, + newContent.properties, + serverContent.properties + ), + discussions: mergeObjects( + stripDiscussionOffsets(oldContent.discussions), + stripDiscussionOffsets(newContent.discussions), + stripDiscussionOffsets(serverContent.discussions) + ), + comments: mergeObjects( + oldContent.comments, + newContent.comments, + serverContent.comments + ) + } + restoreDiscussionOffsets(result, markerKeys) + return result +} + +export default clDiffUtils; diff --git a/src/cledit/cledit.js b/src/cledit/cledit.js new file mode 100644 index 00000000..966e11f2 --- /dev/null +++ b/src/cledit/cledit.js @@ -0,0 +1,11 @@ +import 'clunderscore'; +import cledit from './cleditCore'; +import './cleditHighlighter'; +import './cleditKeystroke'; +import './cleditMarker'; +import './cleditSelectionMgr'; +import './cleditUndoMgr'; +import './cleditUtils'; +import './cleditWatcher'; + +export default cledit; diff --git a/src/cledit/cleditCore.js b/src/cledit/cleditCore.js new file mode 100644 index 00000000..d6174aee --- /dev/null +++ b/src/cledit/cleditCore.js @@ -0,0 +1,368 @@ +var DiffMatchPatch = require('diff-match-patch'); + +function cledit(contentElt, scrollElt, windowParam) { + scrollElt = scrollElt || contentElt + var editor = { + $contentElt: contentElt, + $scrollElt: scrollElt, + $window: windowParam || window, + $keystrokes: [], + $markers: {} + } + editor.$document = editor.$window.document + cledit.Utils.createEventHooks(editor) + var debounce = cledit.Utils.debounce + + editor.toggleEditable = function (isEditable) { + if (isEditable === undefined) { + isEditable = !contentElt.contentEditable + } + contentElt.contentEditable = isEditable + } + editor.toggleEditable(true) + + function getTextContent() { + var textContent = contentElt.textContent.replace(/\r[\n\u0085]?|[\u2424\u2028\u0085]/g, '\n') // Markdown-it sanitization (Mac/DOS to Unix) + if (textContent.slice(-1) !== '\n') { + textContent += '\n' + } + return textContent + } + + var lastTextContent = getTextContent() + var highlighter = new cledit.Highlighter(editor) + + var sectionList + + function parseSections(content, isInit) { + sectionList = highlighter.parseSections(content, isInit) + editor.$allElements = Array.prototype.slice.call(contentElt.querySelectorAll('.cledit-section *')) + return sectionList + } + + // Used to detect editor changes + var watcher = new cledit.Watcher(editor, checkContentChange) + watcher.startWatching() + + /* eslint-disable new-cap */ + var diffMatchPatch = new DiffMatchPatch() + /* eslint-enable new-cap */ + var selectionMgr = new cledit.SelectionMgr(editor) + + function adjustCursorPosition(force) { + selectionMgr.saveSelectionState(true, true, force) + } + + function replaceContent(selectionStart, selectionEnd, replacement) { + var min = Math.min(selectionStart, selectionEnd) + var max = Math.max(selectionStart, selectionEnd) + var range = selectionMgr.createRange(min, max) + var rangeText = '' + range + // Range can contain a br element, which is not taken into account in rangeText + if (rangeText.length === max - min && rangeText === replacement) { + return + } + range.deleteContents() + range.insertNode(editor.$document.createTextNode(replacement)) + return range + } + + var ignoreUndo = false + var noContentFix = false + + function setContent(value, noUndo, maxStartOffset) { + var textContent = getTextContent() + maxStartOffset = maxStartOffset !== undefined && maxStartOffset < textContent.length ? maxStartOffset : textContent.length - 1 + var startOffset = Math.min( + diffMatchPatch.diff_commonPrefix(textContent, value), + maxStartOffset + ) + var endOffset = Math.min( + diffMatchPatch.diff_commonSuffix(textContent, value), + textContent.length - startOffset, + value.length - startOffset + ) + var replacement = value.substring(startOffset, value.length - endOffset) + var range = replaceContent(startOffset, textContent.length - endOffset, replacement) + if (range) { + ignoreUndo = noUndo + noContentFix = true + } + return { + start: startOffset, + end: value.length - endOffset, + range: range + } + } + + function replace(selectionStart, selectionEnd, replacement) { + undoMgr.setDefaultMode('single') + replaceContent(selectionStart, selectionEnd, replacement) + var endOffset = selectionStart + replacement.length + selectionMgr.setSelectionStartEnd(endOffset, endOffset) + selectionMgr.updateCursorCoordinates(true) + } + + function replaceAll(search, replacement) { + undoMgr.setDefaultMode('single') + var textContent = getTextContent() + var value = textContent.replace(search, replacement) + if (value !== textContent) { + var offset = editor.setContent(value) + selectionMgr.setSelectionStartEnd(offset.end, offset.end) + selectionMgr.updateCursorCoordinates(true) + } + } + + function focus() { + selectionMgr.restoreSelection() + } + + var undoMgr = new cledit.UndoMgr(editor) + + function addMarker(marker) { + editor.$markers[marker.id] = marker + } + + function removeMarker(marker) { + delete editor.$markers[marker.id] + } + + var triggerSpellCheck = debounce(function () { + var selection = editor.$window.getSelection() + if (!selectionMgr.hasFocus || highlighter.isComposing || selectionMgr.selectionStart !== selectionMgr.selectionEnd || !selection.modify) { + return + } + // Hack for Chrome to trigger the spell checker + if (selectionMgr.selectionStart) { + selection.modify('move', 'backward', 'character') + selection.modify('move', 'forward', 'character') + } else { + selection.modify('move', 'forward', 'character') + selection.modify('move', 'backward', 'character') + } + }, 10) + + function checkContentChange(mutations) { + watcher.noWatch(function () { + var removedSections = [] + var modifiedSections = [] + + function markModifiedSection(node) { + while (node && node !== contentElt) { + if (node.section) { + var array = node.parentNode ? modifiedSections : removedSections + return array.indexOf(node.section) === -1 && array.push(node.section) + } + node = node.parentNode + } + } + + mutations.cl_each(function (mutation) { + markModifiedSection(mutation.target) + mutation.addedNodes.cl_each(markModifiedSection) + mutation.removedNodes.cl_each(markModifiedSection) + }) + highlighter.fixContent(modifiedSections, removedSections, noContentFix) + noContentFix = false + }) + + var newTextContent = getTextContent() + var diffs = diffMatchPatch.diff_main(lastTextContent, newTextContent) + editor.$markers.cl_each(function (marker) { + marker.adjustOffset(diffs) + }) + + selectionMgr.saveSelectionState() + var sectionList = parseSections(newTextContent) + editor.$trigger('contentChanged', newTextContent, diffs, sectionList) + if (!ignoreUndo) { + undoMgr.addDiffs(lastTextContent, newTextContent, diffs) + undoMgr.setDefaultMode('typing') + undoMgr.saveState() + } + ignoreUndo = false + lastTextContent = newTextContent + triggerSpellCheck() + } + + function setSelection(start, end) { + end = end === undefined ? start : end + selectionMgr.setSelectionStartEnd(start, end) + selectionMgr.updateCursorCoordinates() + } + + function keydownHandler(handler) { + return function (evt) { + if ( + evt.which !== 17 && // Ctrl + evt.which !== 91 && // Cmd + evt.which !== 18 && // Alt + evt.which !== 16 // Shift + ) { + handler(evt) + } + } + } + + function tryDestroy() { + if (!editor.$window.document.contains(contentElt)) { + watcher.stopWatching() + editor.$window.removeEventListener('keydown', windowKeydownListener) + editor.$window.removeEventListener('mouseup', windowMouseupListener) + editor.$trigger('destroy') + return true + } + } + + // In case of Ctrl/Cmd+A outside the editor element + function windowKeydownListener(evt) { + if (!tryDestroy()) { + keydownHandler(function () { + adjustCursorPosition() + })(evt) + } + } + editor.$window.addEventListener('keydown', windowKeydownListener, false) + + // Mouseup can happen outside the editor element + function windowMouseupListener() { + if (!tryDestroy()) { + selectionMgr.saveSelectionState(true, false) + } + } + editor.$window.addEventListener('mouseup', windowMouseupListener) + // This can also provoke selection changes and does not fire mouseup event on Chrome/OSX + contentElt.addEventListener('contextmenu', selectionMgr.saveSelectionState.cl_bind(selectionMgr, true, false)) + + contentElt.addEventListener('keydown', keydownHandler(function (evt) { + selectionMgr.saveSelectionState() + adjustCursorPosition() + + // Perform keystroke + var textContent = getTextContent() + var min = Math.min(selectionMgr.selectionStart, selectionMgr.selectionEnd) + var max = Math.max(selectionMgr.selectionStart, selectionMgr.selectionEnd) + var state = { + before: textContent.slice(0, min), + after: textContent.slice(max), + selection: textContent.slice(min, max), + isBackwardSelection: selectionMgr.selectionStart > selectionMgr.selectionEnd + } + editor.$keystrokes.cl_some(function (keystroke) { + if (keystroke.handler(evt, state, editor)) { + editor.setContent(state.before + state.selection + state.after, false, min) + min = state.before.length + max = min + state.selection.length + selectionMgr.setSelectionStartEnd( + state.isBackwardSelection ? max : min, + state.isBackwardSelection ? min : max + ) + return true + } + }) + }), false) + + contentElt.addEventListener('compositionstart', function () { + highlighter.isComposing++ + }, false) + + contentElt.addEventListener('compositionend', function () { + setTimeout(function () { + highlighter.isComposing && highlighter.isComposing-- + }, 0) + }, false) + + contentElt.addEventListener('paste', function (evt) { + undoMgr.setCurrentMode('single') + evt.preventDefault() + var data + var clipboardData = evt.clipboardData + if (clipboardData) { + data = clipboardData.getData('text/plain') + } else { + clipboardData = editor.$window.clipboardData + data = clipboardData && clipboardData.getData('Text') + } + if (!data) { + return + } + replace(selectionMgr.selectionStart, selectionMgr.selectionEnd, data) + adjustCursorPosition() + }, false) + + contentElt.addEventListener('cut', function () { + undoMgr.setCurrentMode('single') + adjustCursorPosition() + }, false) + + contentElt.addEventListener('focus', function () { + selectionMgr.hasFocus = true + editor.$trigger('focus') + }, false) + + contentElt.addEventListener('blur', function () { + selectionMgr.hasFocus = false + editor.$trigger('blur') + }, false) + + function addKeystroke(keystrokes) { + if (!Array.isArray(keystrokes)) { + keystrokes = [keystrokes] + } + editor.$keystrokes = editor.$keystrokes.concat(keystrokes).sort(function (keystroke1, keystroke2) { + return keystroke1.priority - keystroke2.priority + }) + } + addKeystroke(cledit.defaultKeystrokes) + + editor.selectionMgr = selectionMgr + editor.undoMgr = undoMgr + editor.highlighter = highlighter + editor.watcher = watcher + editor.adjustCursorPosition = adjustCursorPosition + editor.setContent = setContent + editor.replace = replace + editor.replaceAll = replaceAll + editor.getContent = getTextContent + editor.focus = focus + editor.setSelection = setSelection + editor.addKeystroke = addKeystroke + editor.addMarker = addMarker + editor.removeMarker = removeMarker + + editor.init = function (options) { + options = ({ + cursorFocusRatio: 0.2, + sectionHighlighter: function (section) { + return section.text.replace(/&/g, '&').replace(/' + + this.fixContent = function (modifiedSections, removedSections, noContentFix) { + modifiedSections.cl_each(function (section) { + section.forceHighlighting = true + if (!noContentFix) { + if (useBr) { + section.elt.getElementsByClassName('hd-lf').cl_each(function (lfElt) { + lfElt.parentNode.removeChild(lfElt) + }) + section.elt.getElementsByTagName('br').cl_each(function (brElt) { + brElt.parentNode.replaceChild(editor.$document.createTextNode('\n'), brElt) + }) + } + if (section.elt.textContent.slice(-1) !== '\n') { + section.elt.appendChild(editor.$document.createTextNode('\n')) + } + } + }) + } + + this.addTrailingNode = function () { + this.trailingNode = editor.$document.createElement(trailingNodeTag) + contentElt.appendChild(this.trailingNode) + } + + function Section(text) { + this.text = text.text === undefined ? text : text.text + this.data = text.data + } + + Section.prototype.setElement = function (elt) { + this.elt = elt + elt.section = this + } + + this.parseSections = function (content, isInit) { + if (this.isComposing) { + return sectionList + } + + var newSectionList = editor.options.sectionParser ? editor.options.sectionParser(content) : [content] + newSectionList = newSectionList.cl_map(function (sectionText) { + return new Section(sectionText) + }) + + var modifiedSections = [] + var sectionsToRemove = [] + insertBeforeSection = undefined + + if (isInit) { + // Render everything if isInit + sectionsToRemove = sectionList + sectionList = newSectionList + modifiedSections = newSectionList + } else { + // Find modified section starting from top + var leftIndex = sectionList.length + sectionList.cl_some(function (section, index) { + var newSection = newSectionList[index] + if (index >= newSectionList.length || + section.forceHighlighting || + // Check text modification + section.text !== newSection.text || + // Check that section has not been detached or moved + section.elt.parentNode !== contentElt || + // Check also the content since nodes can be injected in sections via copy/paste + section.elt.textContent !== newSection.text) { + leftIndex = index + return true + } + }) + + // Find modified section starting from bottom + var rightIndex = -sectionList.length + sectionList.slice().reverse().cl_some(function (section, index) { + var newSection = newSectionList[newSectionList.length - index - 1] + if (index >= newSectionList.length || + section.forceHighlighting || + // Check modified + section.text !== newSection.text || + // Check that section has not been detached or moved + section.elt.parentNode !== contentElt || + // Check also the content since nodes can be injected in sections via copy/paste + section.elt.textContent !== newSection.text) { + rightIndex = -index + return true + } + }) + + if (leftIndex - rightIndex > sectionList.length) { + // Prevent overlap + rightIndex = leftIndex - sectionList.length + } + + var leftSections = sectionList.slice(0, leftIndex) + modifiedSections = newSectionList.slice(leftIndex, newSectionList.length + rightIndex) + var rightSections = sectionList.slice(sectionList.length + rightIndex, sectionList.length) + insertBeforeSection = rightSections[0] + sectionsToRemove = sectionList.slice(leftIndex, sectionList.length + rightIndex) + sectionList = leftSections.concat(modifiedSections).concat(rightSections) + } + + var newSectionEltList = editor.$document.createDocumentFragment() + modifiedSections.cl_each(function (section) { + section.forceHighlighting = false + highlight(section) + newSectionEltList.appendChild(section.elt) + }) + editor.watcher.noWatch(function () { + if (isInit) { + contentElt.innerHTML = '' + contentElt.appendChild(newSectionEltList) + return this.addTrailingNode() + } + + // Remove outdated sections + sectionsToRemove.cl_each(function (section) { + // section may be already removed + section.elt.parentNode === contentElt && contentElt.removeChild(section.elt) + // To detect sections that come back with built-in undo + section.elt.section = undefined + }) + + if (insertBeforeSection !== undefined) { + contentElt.insertBefore(newSectionEltList, insertBeforeSection.elt) + } else { + contentElt.appendChild(newSectionEltList) + } + + // Remove unauthorized nodes (text nodes outside of sections or duplicated sections via copy/paste) + var childNode = contentElt.firstChild + while (childNode) { + var nextNode = childNode.nextSibling + if (!childNode.section) { + contentElt.removeChild(childNode) + } + childNode = nextNode + } + this.addTrailingNode() + self.$trigger('highlighted') + editor.selectionMgr.restoreSelection() + editor.selectionMgr.updateCursorCoordinates() + }.cl_bind(this)) + + return sectionList + } + + function highlight(section) { + var html = editor.options.sectionHighlighter(section).replace(/\n/g, lfHtml) + var sectionElt = editor.$document.createElement('div') + sectionElt.className = 'cledit-section' + sectionElt.innerHTML = html + section.setElement(sectionElt) + self.$trigger('sectionHighlighted', section) + } +} + +cledit.Highlighter = Highlighter + diff --git a/src/cledit/cleditKeystroke.js b/src/cledit/cleditKeystroke.js new file mode 100644 index 00000000..0dedbbce --- /dev/null +++ b/src/cledit/cleditKeystroke.js @@ -0,0 +1,183 @@ +var cledit = require('./cleditCore') + +function Keystroke(handler, priority) { + this.handler = handler + this.priority = priority || 100 +} + +cledit.Keystroke = Keystroke + +var clearNewline +var charTypes = Object.create(null) + +// Word separators, as in Sublime Text +'./\\()"\'-:,.;<>~!@#$%^&*|+=[]{}`~?'.split('').cl_each(function (wordSeparator) { + charTypes[wordSeparator] = 'wordSeparator' +}) +charTypes[' '] = 'space' +charTypes['\t'] = 'space' +charTypes['\n'] = 'newLine' + +function getNextWordOffset(text, offset, isBackward) { + var previousType + while ((isBackward && offset > 0) || (!isBackward && offset < text.length)) { + var currentType = charTypes[isBackward ? text[offset - 1] : text[offset]] || 'word' + if (previousType && currentType !== previousType) { + if (previousType === 'word' || currentType === 'space' || previousType === 'newLine' || currentType === 'newLine') { + break + } + } + previousType = currentType + isBackward ? offset-- : offset++ + } + return offset +} + +cledit.defaultKeystrokes = [ + + new Keystroke(function (evt, state, editor) { + if ((!evt.ctrlKey && !evt.metaKey) || evt.altKey) { + return + } + var keyCode = evt.charCode || evt.keyCode + var keyCodeChar = String.fromCharCode(keyCode).toLowerCase() + var action + switch (keyCodeChar) { + case 'y': + action = 'redo' + break + case 'z': + action = evt.shiftKey ? 'redo' : 'undo' + break + } + if (action) { + evt.preventDefault() + setTimeout(function () { + editor.undoMgr[action]() + }, 10) + return true + } + }), + + new Keystroke(function (evt, state) { + if (evt.which !== 9 /* tab */ || evt.metaKey || evt.ctrlKey) { + return + } + + function strSplice(str, i, remove, add) { + remove = +remove || 0 + add = add || '' + return str.slice(0, i) + add + str.slice(i + remove) + } + + evt.preventDefault() + var isInverse = evt.shiftKey + var lf = state.before.lastIndexOf('\n') + 1 + if (isInverse) { + if (/\s/.test(state.before.charAt(lf))) { + state.before = strSplice(state.before, lf, 1) + } + state.selection = state.selection.replace(/^[ \t]/gm, '') + } else { + if (state.selection) { + state.before = strSplice(state.before, lf, 0, '\t') + state.selection = state.selection.replace(/\n(?=[\s\S])/g, '\n\t') + } else { + state.before += '\t' + } + } + return true + }), + + new Keystroke(function (evt, state, editor) { + if (evt.which !== 13 /* enter */) { + clearNewline = false + return + } + + evt.preventDefault() + var lf = state.before.lastIndexOf('\n') + 1 + if (clearNewline) { + state.before = state.before.substring(0, lf) + state.selection = '' + clearNewline = false + return true + } + clearNewline = false + var previousLine = state.before.slice(lf) + var indent = previousLine.match(/^\s*/)[0] + if (indent.length) { + clearNewline = true + } + + editor.undoMgr.setCurrentMode('single') + state.before += '\n' + indent + state.selection = '' + return true + }), + + new Keystroke(function (evt, state, editor) { + if (evt.which !== 8 /* backspace */ && evt.which !== 46 /* delete */) { + return + } + + editor.undoMgr.setCurrentMode('delete') + if (!state.selection) { + var isJump = (cledit.Utils.isMac && evt.altKey) || (!cledit.Utils.isMac && evt.ctrlKey) + if (isJump) { + // Custom kill word behavior + var text = state.before + state.after + var offset = getNextWordOffset(text, state.before.length, evt.which === 8) + if (evt.which === 8) { + state.before = state.before.slice(0, offset) + } else { + state.after = state.after.slice(offset - text.length) + } + evt.preventDefault() + return true + } else if (evt.which === 8 && state.before.slice(-1) === '\n') { + // Special treatment for end of lines + state.before = state.before.slice(0, -1) + evt.preventDefault() + return true + } else if (evt.which === 46 && state.after.slice(0, 1) === '\n') { + state.after = state.after.slice(1) + evt.preventDefault() + return true + } + } else { + state.selection = '' + evt.preventDefault() + return true + } + }), + + new Keystroke(function (evt, state, editor) { + if (evt.which !== 37 /* left arrow */ && evt.which !== 39 /* right arrow */) { + return + } + var isJump = (cledit.Utils.isMac && evt.altKey) || (!cledit.Utils.isMac && evt.ctrlKey) + if (!isJump) { + return + } + + // Custom jump behavior + var textContent = editor.getContent() + var offset = getNextWordOffset(textContent, editor.selectionMgr.selectionEnd, evt.which === 37) + if (evt.shiftKey) { + // rebuild the state completely + var min = Math.min(editor.selectionMgr.selectionStart, offset) + var max = Math.max(editor.selectionMgr.selectionStart, offset) + state.before = textContent.slice(0, min) + state.after = textContent.slice(max) + state.selection = textContent.slice(min, max) + state.isBackwardSelection = editor.selectionMgr.selectionStart > offset + } else { + state.before = textContent.slice(0, offset) + state.after = textContent.slice(offset) + state.selection = '' + } + evt.preventDefault() + return true + }) +] diff --git a/src/cledit/cleditMarker.js b/src/cledit/cleditMarker.js new file mode 100644 index 00000000..590688b1 --- /dev/null +++ b/src/cledit/cleditMarker.js @@ -0,0 +1,44 @@ +var cledit = require('./cleditCore') + +var DIFF_DELETE = -1 +var DIFF_INSERT = 1 +var DIFF_EQUAL = 0 + +var idCounter = 0 + +function Marker(offset, trailing) { + this.id = idCounter++ + this.offset = offset + this.trailing = trailing +} + +Marker.prototype.adjustOffset = function (diffs) { + var startOffset = 0 + diffs.cl_each(function (diff) { + var diffType = diff[0] + var diffText = diff[1] + var diffOffset = diffText.length + switch (diffType) { + case DIFF_EQUAL: + startOffset += diffOffset + break + case DIFF_INSERT: + if ( + this.trailing + ? this.offset > startOffset + : this.offset >= startOffset + ) { + this.offset += diffOffset + } + startOffset += diffOffset + break + case DIFF_DELETE: + if (this.offset > startOffset) { + this.offset -= Math.min(diffOffset, this.offset - startOffset) + } + break + } + }.cl_bind(this)) +} + +cledit.Marker = Marker diff --git a/src/cledit/cleditSelectionMgr.js b/src/cledit/cleditSelectionMgr.js new file mode 100644 index 00000000..2f8bad6f --- /dev/null +++ b/src/cledit/cleditSelectionMgr.js @@ -0,0 +1,399 @@ +var cledit = require('./cleditCore') + +function SelectionMgr(editor) { + var debounce = cledit.Utils.debounce + var contentElt = editor.$contentElt + var scrollElt = editor.$scrollElt + cledit.Utils.createEventHooks(this) + + var self = this + var lastSelectionStart = 0 + var lastSelectionEnd = 0 + this.selectionStart = 0 + this.selectionEnd = 0 + this.cursorCoordinates = {} + this.adjustTop = 0 + this.adjustBottom = 0 + + this.findContainer = function (offset) { + var result = cledit.Utils.findContainer(contentElt, offset) + if (result.container.nodeValue === '\n') { + var hdLfElt = result.container.parentNode + if (hdLfElt.className === 'hd-lf' && hdLfElt.previousSibling && hdLfElt.previousSibling.tagName === 'BR') { + result.container = hdLfElt.parentNode + result.offsetInContainer = Array.prototype.indexOf.call(result.container.childNodes, result.offsetInContainer === 0 ? hdLfElt.previousSibling : hdLfElt) + } + } + return result + } + + this.createRange = function (start, end) { + var range = editor.$document.createRange() + if (start === end) { + end = start = isNaN(start) ? start : this.findContainer(start < 0 ? 0 : start) + } else { + start = isNaN(start) ? start : this.findContainer(start < 0 ? 0 : start) + end = isNaN(end) ? end : this.findContainer(end < 0 ? 0 : end) + } + range.setStart(start.container, start.offsetInContainer) + range.setEnd(end.container, end.offsetInContainer) + return range + } + + var adjustScroll + var debouncedUpdateCursorCoordinates = debounce(function () { + var coordinates = this.getCoordinates(this.selectionEnd, this.selectionEndContainer, this.selectionEndOffset) + if (this.cursorCoordinates.top !== coordinates.top || + this.cursorCoordinates.height !== coordinates.height || + this.cursorCoordinates.left !== coordinates.left + ) { + this.cursorCoordinates = coordinates + this.$trigger('cursorCoordinatesChanged', coordinates) + } + if (adjustScroll) { + var adjustTop, adjustBottom + adjustTop = adjustBottom = scrollElt.clientHeight / 2 * editor.options.cursorFocusRatio + adjustTop = this.adjustTop || adjustTop + adjustBottom = this.adjustBottom || adjustTop + if (adjustTop && adjustBottom) { + var cursorMinY = scrollElt.scrollTop + adjustTop + var cursorMaxY = scrollElt.scrollTop + scrollElt.clientHeight - adjustBottom + if (this.cursorCoordinates.top < cursorMinY) { + scrollElt.scrollTop += this.cursorCoordinates.top - cursorMinY + } else if (this.cursorCoordinates.top + this.cursorCoordinates.height > cursorMaxY) { + scrollElt.scrollTop += this.cursorCoordinates.top + this.cursorCoordinates.height - cursorMaxY + } + } + } + adjustScroll = false + }.cl_bind(this)) + + this.updateCursorCoordinates = function (adjustScrollParam) { + adjustScroll = adjustScroll || adjustScrollParam + debouncedUpdateCursorCoordinates() + } + + var oldSelectionRange + + function checkSelection(selectionRange) { + if (!oldSelectionRange || + oldSelectionRange.startContainer !== selectionRange.startContainer || + oldSelectionRange.startOffset !== selectionRange.startOffset || + oldSelectionRange.endContainer !== selectionRange.endContainer || + oldSelectionRange.endOffset !== selectionRange.endOffset + ) { + oldSelectionRange = selectionRange + self.$trigger('selectionChanged', self.selectionStart, self.selectionEnd, selectionRange) + return true + } + } + + this.restoreSelection = function () { + var min = Math.min(this.selectionStart, this.selectionEnd) + var max = Math.max(this.selectionStart, this.selectionEnd) + var selectionRange = this.createRange(min, max) + if (editor.$document.contains(selectionRange.commonAncestorContainer)) { + var selection = editor.$window.getSelection() + selection.removeAllRanges() + var isBackward = this.selectionStart > this.selectionEnd + if (selection.extend) { + var beginRange = selectionRange.cloneRange() + beginRange.collapse(!isBackward) + selection.addRange(beginRange) + if (isBackward) { + selection.extend(selectionRange.startContainer, selectionRange.startOffset) + } else { + selection.extend(selectionRange.endContainer, selectionRange.endOffset) + } + } else { + selection.addRange(selectionRange) + } + checkSelection(selectionRange) + return selectionRange + } + } + + var saveLastSelection = debounce(function () { + lastSelectionStart = self.selectionStart + lastSelectionEnd = self.selectionEnd + }, 50) + + function setSelection(start, end) { + if (start === undefined) { + start = self.selectionStart + } + if (start < 0) { + start = 0 + } + if (end === undefined) { + end = this.selectionEnd + } + if (end < 0) { + end = 0 + } + self.selectionStart = start + self.selectionEnd = end + saveLastSelection() + } + + this.setSelectionStartEnd = function (start, end, focus) { + setSelection(start, end) + return focus !== false && this.restoreSelection() + } + + this.saveSelectionState = (function () { + // Credit: https://github.com/timdown/rangy + function arrayContains(arr, val) { + var i = arr.length + while (i--) { + if (arr[i] === val) { + return true + } + } + return false + } + + function getClosestAncestorIn(node, ancestor, selfIsAncestor) { + var p + var n = selfIsAncestor ? node : node.parentNode + while (n) { + p = n.parentNode + if (p === ancestor) { + return n + } + n = p + } + return null + } + + function getNodeIndex(node) { + var i = 0 + while ((node = node.previousSibling)) { + ++i + } + return i + } + + function getCommonAncestor(node1, node2) { + var ancestors = [] + var n + for (n = node1; n; n = n.parentNode) { + ancestors.push(n) + } + + for (n = node2; n; n = n.parentNode) { + if (arrayContains(ancestors, n)) { + return n + } + } + + return null + } + + function comparePoints(nodeA, offsetA, nodeB, offsetB) { + // See http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Comparing + var nodeC, root, childA, childB, n + if (nodeA === nodeB) { + // Case 1: nodes are the same + return offsetA === offsetB ? 0 : (offsetA < offsetB) ? -1 : 1 + } else if ( + (nodeC = getClosestAncestorIn(nodeB, nodeA, true)) + ) { + // Case 2: node C (container B or an ancestor) is a child node of A + return offsetA <= getNodeIndex(nodeC) ? -1 : 1 + } else if ( + (nodeC = getClosestAncestorIn(nodeA, nodeB, true)) + ) { + // Case 3: node C (container A or an ancestor) is a child node of B + return getNodeIndex(nodeC) < offsetB ? -1 : 1 + } else { + root = getCommonAncestor(nodeA, nodeB) + if (!root) { + throw new Error('comparePoints error: nodes have no common ancestor') + } + + // Case 4: containers are siblings or descendants of siblings + childA = (nodeA === root) ? root : getClosestAncestorIn(nodeA, root, true) + childB = (nodeB === root) ? root : getClosestAncestorIn(nodeB, root, true) + + if (childA === childB) { + // This shouldn't be possible + throw module.createError('comparePoints got to case 4 and childA and childB are the same!') + } else { + n = root.firstChild + while (n) { + if (n === childA) { + return -1 + } else if (n === childB) { + return 1 + } + n = n.nextSibling + } + } + } + } + + function save() { + var selectionStart = self.selectionStart + var selectionEnd = self.selectionEnd + var selection = editor.$window.getSelection() + var result + if (selection.rangeCount > 0) { + var selectionRange = selection.getRangeAt(0) + var node = selectionRange.startContainer + if ((contentElt.compareDocumentPosition(node) & window.Node.DOCUMENT_POSITION_CONTAINED_BY) || contentElt === node) { + var offset = selectionRange.startOffset + if (node.firstChild && offset > 0) { + node = node.childNodes[offset - 1] + offset = node.textContent.length + } + var container = node + while (node !== contentElt) { + while ((node = node.previousSibling)) { + offset += (node.textContent || '').length + } + node = container = container.parentNode + } + var selectionText = selectionRange + '' + // Fix end of line when only br is selected + var brElt = selectionRange.endContainer.firstChild + if (brElt && brElt.tagName === 'BR' && selectionRange.endOffset === 1) { + selectionText += '\n' + } + if (comparePoints(selection.anchorNode, selection.anchorOffset, selection.focusNode, selection.focusOffset) === 1) { + selectionStart = offset + selectionText.length + selectionEnd = offset + } else { + selectionStart = offset + selectionEnd = offset + selectionText.length + } + + if (selectionStart === selectionEnd && selectionStart === editor.getContent().length) { + // If cursor is after the trailingNode + selectionStart = --selectionEnd + result = self.setSelectionStartEnd(selectionStart, selectionEnd) + } else { + setSelection(selectionStart, selectionEnd) + result = checkSelection(selectionRange) + result = result || lastSelectionStart !== self.selectionStart // selectionRange doesn't change when selection is at the start of a section + } + } + } + return result + } + + function saveCheckChange() { + return save() && (lastSelectionStart !== self.selectionStart || lastSelectionEnd !== self.selectionEnd) + } + + var nextTickAdjustScroll = false + var debouncedSave = debounce(function () { + self.updateCursorCoordinates(saveCheckChange() && nextTickAdjustScroll) + // In some cases we have to wait a little longer to see the selection change (Cmd+A on Chrome OSX) + longerDebouncedSave() + }) + var longerDebouncedSave = debounce(function () { + self.updateCursorCoordinates(saveCheckChange() && nextTickAdjustScroll) + nextTickAdjustScroll = false + }, 10) + + return function (debounced, adjustScroll, forceAdjustScroll) { + if (forceAdjustScroll) { + lastSelectionStart = undefined + lastSelectionEnd = undefined + } + if (debounced) { + nextTickAdjustScroll = nextTickAdjustScroll || adjustScroll + return debouncedSave() + } else { + save() + } + } + })() + + this.getSelectedText = function () { + var min = Math.min(this.selectionStart, this.selectionEnd) + var max = Math.max(this.selectionStart, this.selectionEnd) + return editor.getContent().substring(min, max) + } + + this.getCoordinates = function (inputOffset, container, offsetInContainer) { + if (!container) { + var offset = this.findContainer(inputOffset) + container = offset.container + offsetInContainer = offset.offsetInContainer + } + var containerElt = container + if (!containerElt.hasChildNodes()) { + containerElt = container.parentNode + } + var isInvisible = false + var index = editor.$allElements.indexOf(containerElt) + while (containerElt.offsetHeight === 0 && index > 0) { + isInvisible = true + containerElt = editor.$allElements[--index] + } + var rect + var contentRect + var left = 'left' + if (isInvisible || container.textContent === '\n') { + rect = containerElt.getBoundingClientRect() + } else { + var selectedChar = editor.getContent()[inputOffset] + var startOffset = { + container: container, + offsetInContainer: offsetInContainer + } + var endOffset = { + container: container, + offsetInContainer: offsetInContainer + } + if (inputOffset > 0 && (selectedChar === undefined || selectedChar === '\n')) { + left = 'right' + if (startOffset.offsetInContainer === 0) { + // Need to calculate offset-1 + startOffset = inputOffset - 1 + } else { + startOffset.offsetInContainer -= 1 + } + } else { + if (endOffset.offsetInContainer === container.textContent.length) { + // Need to calculate offset+1 + endOffset = inputOffset + 1 + } else { + endOffset.offsetInContainer += 1 + } + } + var range = this.createRange(startOffset, endOffset) + rect = range.getBoundingClientRect() + } + contentRect = contentElt.getBoundingClientRect() + return { + top: Math.round(rect.top - contentRect.top + contentElt.scrollTop), + height: Math.round(rect.height), + left: Math.round(rect[left] - contentRect.left + contentElt.scrollLeft) + } + } + + this.getClosestWordOffset = function (offset) { + var offsetStart = 0 + var offsetEnd = 0 + var nextOffset = 0 + editor.getContent().split(/\s/).cl_some(function (word) { + if (word) { + offsetStart = nextOffset + offsetEnd = nextOffset + word.length + if (offsetEnd > offset) { + return true + } + } + nextOffset += word.length + 1 + }) + return { + start: offsetStart, + end: offsetEnd + } + } +} + +cledit.SelectionMgr = SelectionMgr diff --git a/src/cledit/cleditUndoMgr.js b/src/cledit/cleditUndoMgr.js new file mode 100644 index 00000000..a097778f --- /dev/null +++ b/src/cledit/cleditUndoMgr.js @@ -0,0 +1,178 @@ +var DiffMatchPatch = require('diff-match-patch'); +var cledit = require('./cleditCore') + +function UndoMgr(editor) { + cledit.Utils.createEventHooks(this) + + /* eslint-disable new-cap */ + var diffMatchPatch = new DiffMatchPatch() + /* eslint-enable new-cap */ + + var self = this + var selectionMgr + var undoStack = [] + var redoStack = [] + var currentState + var previousPatches = [] + var currentPatches = [] + var debounce = cledit.Utils.debounce + + self.options = { + undoStackMaxSize: 200, + bufferStateUntilIdle: 1000, + patchHandler: { + makePatches: function (oldContent, newContent, diffs) { + return diffMatchPatch.patch_make(oldContent, diffs) + }, + applyPatches: function (patches, content) { + return diffMatchPatch.patch_apply(patches, content)[0] + }, + reversePatches: function (patches) { + patches = diffMatchPatch.patch_deepCopy(patches).reverse() + patches.cl_each(function (patch) { + patch.diffs.cl_each(function (diff) { + diff[0] = -diff[0] + }) + }) + return patches + } + } + } + + function State() { } + + function StateMgr() { + var currentTime, lastTime + var lastMode + + this.isBufferState = function () { + currentTime = Date.now() + return this.currentMode !== 'single' && + this.currentMode === lastMode && + currentTime - lastTime < self.options.bufferStateUntilIdle + } + + this.setDefaultMode = function (mode) { + this.currentMode = this.currentMode || mode + } + + this.resetMode = function () { + stateMgr.currentMode = undefined + lastMode = undefined + } + + this.saveMode = function () { + lastMode = this.currentMode + this.currentMode = undefined + lastTime = currentTime + } + } + + function addToStack(stack) { + return function () { + stack.push(this) + this.patches = previousPatches + previousPatches = [] + } + } + + State.prototype.addToUndoStack = addToStack(undoStack) + State.prototype.addToRedoStack = addToStack(redoStack) + + var stateMgr = new StateMgr() + this.setCurrentMode = function (mode) { + stateMgr.currentMode = mode + } + this.setDefaultMode = stateMgr.setDefaultMode.cl_bind(stateMgr) + + this.addDiffs = function (oldContent, newContent, diffs) { + var patches = self.options.patchHandler.makePatches(oldContent, newContent, diffs) + currentPatches.push.apply(currentPatches, patches) + } + + function saveCurrentPatches() { + // Move currentPatches into previousPatches + Array.prototype.push.apply(previousPatches, currentPatches) + currentPatches = [] + } + + this.saveState = debounce(function () { + redoStack.length = 0 + if (!stateMgr.isBufferState()) { + currentState.addToUndoStack() + + // Limit the size of the stack + while (undoStack.length > self.options.undoStackMaxSize) { + undoStack.shift() + } + } + saveCurrentPatches() + currentState = new State() + stateMgr.saveMode() + self.$trigger('undoStateChange') + }) + + this.canUndo = function () { + return !!undoStack.length + } + + this.canRedo = function () { + return !!redoStack.length + } + + function restoreState(patches, isForward) { + // Update editor + var content = editor.getContent() + if (!isForward) { + patches = self.options.patchHandler.reversePatches(patches) + } + + var newContent = self.options.patchHandler.applyPatches(patches, content) + var newContentText = newContent.text || newContent + var range = editor.setContent(newContentText, true) + var selection = newContent.selection || { + start: range.end, + end: range.end + } + + selectionMgr.setSelectionStartEnd(selection.start, selection.end) + selectionMgr.updateCursorCoordinates(true) + + stateMgr.resetMode() + self.$trigger('undoStateChange') + editor.adjustCursorPosition() + } + + this.undo = function () { + var state = undoStack.pop() + if (!state) { + return + } + saveCurrentPatches() + currentState.addToRedoStack() + restoreState(currentState.patches) + previousPatches = state.patches + currentState = state + } + + this.redo = function () { + var state = redoStack.pop() + if (!state) { + return + } + currentState.addToUndoStack() + restoreState(state.patches, true) + previousPatches = state.patches + currentState = state + } + + this.init = function (options) { + self.options.cl_extend(options || {}) + selectionMgr = editor.selectionMgr + if (!currentState) { + currentState = new State() + } + } +} + +cledit.UndoMgr = UndoMgr diff --git a/src/cledit/cleditUtils.js b/src/cledit/cleditUtils.js new file mode 100644 index 00000000..c8f532d2 --- /dev/null +++ b/src/cledit/cleditUtils.js @@ -0,0 +1,123 @@ +var cledit = require('./cleditCore') + +var Utils = { + isGecko: 'MozAppearance' in document.documentElement.style, + isWebkit: 'WebkitAppearance' in document.documentElement.style, + isMsie: 'msTransform' in document.documentElement.style, + isMac: navigator.userAgent.indexOf('Mac OS X') !== -1 +} + +// Faster than setTimeout(0). Credit: https://github.com/stefanpenner/es6-promise +Utils.defer = (function () { + var queue = new Array(1000) + var queueLength = 0 + function flush() { + for (var i = 0; i < queueLength; i++) { + try { + queue[i]() + } catch (e) { + console.error(e.message, e.stack) + } + queue[i] = undefined + } + queueLength = 0 + } + + var iterations = 0 + var observer = new window.MutationObserver(flush) + var node = document.createTextNode('') + observer.observe(node, { characterData: true }) + + return function (fn) { + queue[queueLength++] = fn + if (queueLength === 1) { + node.data = (iterations = ++iterations % 2) + } + } +})() + +Utils.debounce = function (func, wait) { + var timeoutId, isExpected + return wait + ? function () { + clearTimeout(timeoutId) + timeoutId = setTimeout(func, wait) + } + : function () { + if (!isExpected) { + isExpected = true + Utils.defer(function () { + isExpected = false + func() + }) + } + } +} + +Utils.createEventHooks = function (object) { + var listenerMap = Object.create(null) + object.$trigger = function (eventType) { + var listeners = listenerMap[eventType] + if (listeners) { + var args = Array.prototype.slice.call(arguments, 1) + listeners.cl_each(function (listener) { + try { + listener.apply(object, args) + } catch (e) { + console.error(e.message, e.stack) + } + }) + } + } + object.on = function (eventType, listener) { + var listeners = listenerMap[eventType] + if (!listeners) { + listeners = [] + listenerMap[eventType] = listeners + } + listeners.push(listener) + } + object.off = function (eventType, listener) { + var listeners = listenerMap[eventType] + if (listeners) { + var index = listeners.indexOf(listener) + if (~index) { + listeners.splice(index, 1) + } + } + } +} + +Utils.findContainer = function (elt, offset) { + var containerOffset = 0 + var container + do { + container = elt + elt = elt.firstChild + if (elt) { + do { + var len = elt.textContent.length + if (containerOffset <= offset && containerOffset + len > offset) { + break + } + containerOffset += len + } while ((elt = elt.nextSibling)) + } + } while (elt && elt.firstChild && elt.nodeType !== 3) + + if (elt) { + return { + container: elt, + offsetInContainer: offset - containerOffset + } + } + while (container.lastChild) { + container = container.lastChild + } + return { + container: container, + offsetInContainer: container.nodeType === 3 ? container.textContent.length : 0 + } +} + +cledit.Utils = Utils diff --git a/src/cledit/cleditWatcher.js b/src/cledit/cleditWatcher.js new file mode 100644 index 00000000..942597c0 --- /dev/null +++ b/src/cledit/cleditWatcher.js @@ -0,0 +1,33 @@ +var cledit = require('./cleditCore') + +function Watcher(editor, listener) { + this.isWatching = false + var contentObserver + this.startWatching = function () { + this.stopWatching() + this.isWatching = true + contentObserver = new window.MutationObserver(listener) + contentObserver.observe(editor.$contentElt, { + childList: true, + subtree: true, + characterData: true + }) + } + this.stopWatching = function () { + if (contentObserver) { + contentObserver.disconnect() + contentObserver = undefined + } + this.isWatching = false + } + this.noWatch = function (cb) { + if (this.isWatching === true) { + this.stopWatching() + cb() + return this.startWatching() + } + cb() + } +} + +cledit.Watcher = Watcher diff --git a/src/cledit/clunderscore.js b/src/cledit/clunderscore.js new file mode 100644 index 00000000..d6667167 --- /dev/null +++ b/src/cledit/clunderscore.js @@ -0,0 +1,166 @@ +var arrayProperties = {} +var liveCollectionProperties = {} +var functionProperties = {} +var objectProperties = {} +var slice = Array.prototype.slice + +arrayProperties.cl_each = function (cb) { + var i = 0 + var length = this.length + for (; i < length; i++) { + cb(this[i], i, this) + } +} + +arrayProperties.cl_map = function (cb) { + var i = 0 + var length = this.length + var result = Array(length) + for (; i < length; i++) { + result[i] = cb(this[i], i, this) + } + return result +} + +arrayProperties.cl_reduce = function (cb, memo) { + var i = 0 + var length = this.length + for (; i < length; i++) { + memo = cb(memo, this[i], i, this) + } + return memo +} + +arrayProperties.cl_some = function (cb) { + var i = 0 + var length = this.length + for (; i < length; i++) { + if (cb(this[i], i, this)) { + return true + } + } +} + +arrayProperties.cl_filter = function (cb) { + var i = 0 + var length = this.length + var result = [] + for (; i < length; i++) { + cb(this[i], i, this) && result.push(this[i]) + } + return result +} + +liveCollectionProperties.cl_each = function (cb) { + slice.call(this).cl_each(cb) +} + +liveCollectionProperties.cl_map = function (cb) { + return slice.call(this).cl_map(cb) +} + +liveCollectionProperties.cl_reduce = function (cb, memo) { + return slice.call(this).cl_reduce(cb, memo) +} + +functionProperties.cl_bind = function (context) { + var self = this + var args = slice.call(arguments, 1) + context = context || null + return args.length + ? function () { + return arguments.length + ? self.apply(context, args.concat(slice.call(arguments))) + : self.apply(context, args) + } + : function () { + return arguments.length + ? self.apply(context, arguments) + : self.call(context) + } +} + +objectProperties.cl_each = function (cb) { + var i = 0 + var keys = Object.keys(this) + var length = keys.length + for (; i < length; i++) { + cb(this[keys[i]], keys[i], this) + } +} + +objectProperties.cl_map = function (cb) { + var i = 0 + var keys = Object.keys(this) + var length = keys.length + var result = Array(length) + for (; i < length; i++) { + result[i] = cb(this[keys[i]], keys[i], this) + } + return result +} + +objectProperties.cl_reduce = function (cb, memo) { + var i = 0 + var keys = Object.keys(this) + var length = keys.length + for (; i < length; i++) { + memo = cb(memo, this[keys[i]], keys[i], this) + } + return memo +} + +objectProperties.cl_some = function (cb) { + var i = 0 + var keys = Object.keys(this) + var length = keys.length + for (; i < length; i++) { + if (cb(this[keys[i]], keys[i], this)) { + return true + } + } +} + +objectProperties.cl_extend = function (obj) { + if (obj) { + var i = 0 + var keys = Object.keys(obj) + var length = keys.length + for (; i < length; i++) { + this[keys[i]] = obj[keys[i]] + } + } + return this +} + +function build(properties) { + return objectProperties.cl_reduce.call(properties, function (memo, value, key) { + memo[key] = { + value: value + } + return memo + }, {}) +} + +arrayProperties = build(arrayProperties) +liveCollectionProperties = build(liveCollectionProperties) +functionProperties = build(functionProperties) +objectProperties = build(objectProperties) + +/* eslint-disable no-extend-native */ +Object.defineProperties(Array.prototype, arrayProperties) +Object.defineProperties(Int8Array.prototype, arrayProperties) +Object.defineProperties(Uint8Array.prototype, arrayProperties) +Object.defineProperties(Uint8ClampedArray.prototype, arrayProperties) +Object.defineProperties(Int16Array.prototype, arrayProperties) +Object.defineProperties(Uint16Array.prototype, arrayProperties) +Object.defineProperties(Int32Array.prototype, arrayProperties) +Object.defineProperties(Uint32Array.prototype, arrayProperties) +Object.defineProperties(Float32Array.prototype, arrayProperties) +Object.defineProperties(Float64Array.prototype, arrayProperties) +Object.defineProperties(Function.prototype, functionProperties) +Object.defineProperties(Object.prototype, objectProperties) +if (typeof window !== 'undefined') { + Object.defineProperties(HTMLCollection.prototype, liveCollectionProperties) + Object.defineProperties(NodeList.prototype, liveCollectionProperties) +} diff --git a/src/cledit/htmlSanitizer.js b/src/cledit/htmlSanitizer.js new file mode 100644 index 00000000..b11f29b7 --- /dev/null +++ b/src/cledit/htmlSanitizer.js @@ -0,0 +1,424 @@ +const aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/; +const imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/; + +const urlParsingNode = window.document.createElement('a'); + +function sanitizeUri(uri, isImage) { + const regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist; + urlParsingNode.setAttribute('href', uri); + const normalizedVal = urlParsingNode.href; + if (normalizedVal !== '' && !normalizedVal.match(regex)) { + return `unsafe:${normalizedVal}`; + } + return uri; +} + +var buf; + +/* jshint -W083 */ + +// Regular Expressions for parsing tags and attributes +var START_TAG_REGEXP = + /^<((?:[a-zA-Z])[\w:-]*)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*(>?)/, + END_TAG_REGEXP = /^<\/\s*([\w:-]+)[^>]*>/, + ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g, + BEGIN_TAG_REGEXP = /^/g, + DOCTYPE_REGEXP = /]*?)>/i, + CDATA_REGEXP = //g, + SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g, + // Match everything outside of normal chars and " (quote character) + NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; + + +// Good source of info about elements and attributes +// http://dev.w3.org/html5/spec/Overview.html#semantics +// http://simon.html5.org/html-elements + +// Safe Void Elements - HTML5 +// http://dev.w3.org/html5/spec/Overview.html#void-elements +var voidElements = makeMap("area,br,col,hr,img,wbr"); + +// Elements that you can, intentionally, leave open (and which close themselves) +// http://dev.w3.org/html5/spec/Overview.html#optional-tags +var optionalEndTagBlockElements = makeMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"), + optionalEndTagInlineElements = makeMap("rp,rt"), + optionalEndTagElements = { + ...optionalEndTagInlineElements, + ...optionalEndTagBlockElements, + }; + +// Safe Block Elements - HTML5 +var blockElements = { + ...optionalEndTagBlockElements, + ...makeMap("address,article," + + "aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," + + "h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul") +}; + +// benweet: Add iframe +blockElements.iframe = true; + +// Inline Elements - HTML5 +var inlineElements = { + ...optionalEndTagInlineElements, + ...makeMap("a,abbr,acronym,b," + + "bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," + + "samp,small,span,strike,strong,sub,sup,time,tt,u,var") +}; + +// SVG Elements +// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements +// Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted. +// They can potentially allow for arbitrary javascript to be executed. See #11290 +var svgElements = makeMap("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph," + + "hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline," + + "radialGradient,rect,stop,svg,switch,text,title,tspan,use"); + +// Special Elements (can contain anything) +var specialElements = makeMap("script,style"); + +var validElements = { + ...voidElements, + ...blockElements, + ...inlineElements, + ...optionalEndTagElements, + ...svgElements, +}; + +//Attributes that have href and hence need to be sanitized +var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap,xlink:href"); + +var htmlAttrs = makeMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' + + 'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' + + 'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' + + 'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' + + 'valign,value,vspace,width'); + +// SVG attributes (without "id" and "name" attributes) +// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes +var svgAttrs = makeMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' + + 'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' + + 'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' + + 'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' + + 'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' + + 'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' + + 'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' + + 'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' + + 'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' + + 'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' + + 'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' + + 'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' + + 'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' + + 'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' + + 'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true); + +var validAttrs = { + ...uriAttrs, + ...svgAttrs, + ...htmlAttrs, +}; + +// benweet: Add id and allowfullscreen (YouTube iframe) +validAttrs.id = true; +validAttrs.allowfullscreen = true; + +function makeMap(str, lowercaseKeys) { + var obj = {}, + items = str.split(','), + i; + for (i = 0; i < items.length; i++) { + obj[lowercaseKeys ? items[i].toLowerCase() : items[i]] = true; + } + return obj; +} + + +/** + * @example + * htmlParser(htmlString, { + * start: function(tag, attrs, unary) {}, + * end: function(tag) {}, + * chars: function(text) {}, + * comment: function(text) {} + * }); + * + * @param {string} html string + * @param {object} handler + */ +function htmlParser(html, handler) { + if (typeof html !== 'string') { + if (html === null || typeof html === 'undefined') { + html = ''; + } else { + html = '' + html; + } + } + var index, chars, match, stack = [], + last = html, + text; + stack.last = function () { + return stack[stack.length - 1]; + }; + + while (html) { + text = ''; + chars = true; + + // Make sure we're not in a script or style element + if (!stack.last() || !specialElements[stack.last()]) { + + // Comment + if (html.indexOf("", index) === index) { + if (handler.comment) handler.comment(html.substring(4, index)); + html = html.substring(index + 3); + chars = false; + } + // DOCTYPE + } else if (DOCTYPE_REGEXP.test(html)) { + match = html.match(DOCTYPE_REGEXP); + + if (match) { + html = html.replace(match[0], ''); + chars = false; + } + // end tag + } else if (BEGING_END_TAGE_REGEXP.test(html)) { + match = html.match(END_TAG_REGEXP); + + if (match) { + html = html.substring(match[0].length); + match[0].replace(END_TAG_REGEXP, parseEndTag); + chars = false; + } + + // start tag + } else if (BEGIN_TAG_REGEXP.test(html)) { + match = html.match(START_TAG_REGEXP); + + if (match) { + // We only have a valid start-tag if there is a '>'. + if (match[4]) { + html = html.substring(match[0].length); + match[0].replace(START_TAG_REGEXP, parseStartTag); + } + chars = false; + } else { + // no ending tag found --- this piece should be encoded as an entity. + text += '<'; + html = html.substring(1); + } + } + + if (chars) { + index = html.indexOf("<"); + + text += index < 0 ? html : html.substring(0, index); + html = index < 0 ? "" : html.substring(index); + + if (handler.chars) handler.chars(decodeEntities(text)); + } + + } else { + // IE versions 9 and 10 do not understand the regex '[^]', so using a workaround with [\W\w]. + html = html.replace(new RegExp("([\\W\\w]*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'), + function (all, text) { + text = text.replace(COMMENT_REGEXP, "$1").replace(CDATA_REGEXP, "$1"); + + if (handler.chars) handler.chars(decodeEntities(text)); + + return ""; + }); + + parseEndTag("", stack.last()); + } + + if (html == last) { + // benweet + // throw $sanitizeMinErr('badparse', "The sanitizer was unable to parse the following block " + + // "of html: {0}", html); + stack.reverse(); + return stack.cl_each(function (tag) { + buf.push(''); + }); + } + last = html; + } + + // Clean up any remaining tags + parseEndTag(); + + function parseStartTag(tag, tagName, rest, unary) { + tagName = tagName && tagName.toLowerCase(); + if (blockElements[tagName]) { + while (stack.last() && inlineElements[stack.last()]) { + parseEndTag("", stack.last()); + } + } + + if (optionalEndTagElements[tagName] && stack.last() == tagName) { + parseEndTag("", tagName); + } + + unary = voidElements[tagName] || !!unary; + + if (!unary) { + stack.push(tagName); + } + + var attrs = {}; + + rest.replace(ATTR_REGEXP, + function (match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) { + var value = doubleQuotedValue || singleQuotedValue || unquotedValue || ''; + + attrs[name] = decodeEntities(value); + }); + if (handler.start) handler.start(tagName, attrs, unary); + } + + function parseEndTag(tag, tagName) { + var pos = 0, + i; + tagName = tagName && tagName.toLowerCase(); + if (tagName) { + // Find the closest opened tag of the same type + for (pos = stack.length - 1; pos >= 0; pos--) { + if (stack[pos] == tagName) break; + } + } + + if (pos >= 0) { + // Close all the open elements, up the stack + for (i = stack.length - 1; i >= pos; i--) + if (handler.end) handler.end(stack[i]); + + // Remove the open elements from the stack + stack.length = pos; + } + } +} + +var hiddenPre = document.createElement("pre"); +/** + * decodes all entities into regular string + * @param value + * @returns {string} A string with decoded entities. + */ +function decodeEntities(value) { + if (!value) { + return ''; + } + + hiddenPre.innerHTML = value.replace(//g, '>'); +} + +/** + * create an HTML/XML writer which writes to buffer + * @param {Array} buf use buf.jain('') to get out sanitized html string + * @returns {object} in the form of { + * start: function(tag, attrs, unary) {}, + * end: function(tag) {}, + * chars: function(text) {}, + * comment: function(text) {} + * } + */ +function htmlSanitizeWriter(buf, uriValidator) { + var ignore = false; + var out = buf.push.bind(buf); + return { + start: function (tag, attrs, unary) { + tag = tag && tag.toLowerCase(); + if (!ignore && specialElements[tag]) { + ignore = tag; + } + if (!ignore && validElements[tag] === true) { + out('<'); + out(tag); + Object.keys(attrs).forEach(function (key) { + var value = attrs[key]; + var lkey = key && key.toLowerCase(); + var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background'); + if (validAttrs[lkey] === true && + (uriAttrs[lkey] !== true || uriValidator(value, isImage))) { + out(' '); + out(key); + out('="'); + out(encodeEntities(value)); + out('"'); + } + }); + out(unary ? '/>' : '>'); + } + }, + end: function (tag) { + tag = tag && tag.toLowerCase(); + if (!ignore && validElements[tag] === true) { + out(''); + } + if (tag == ignore) { + ignore = false; + } + }, + chars: function (chars) { + if (!ignore) { + out(encodeEntities(chars)); + } + }, + comment: function (comment) { + if (!ignore) { + out(''); + } + } + }; +} + +function sanitizeHtml(html) { + buf = []; + htmlParser(html, htmlSanitizeWriter(buf, function (uri, isImage) { + return !/^unsafe/.test(sanitizeUri(uri, isImage)); + })); + return buf.join(''); +} + + +export default { + sanitizeHtml, + sanitizeUri, +} diff --git a/src/cledit/mdGrammar.js b/src/cledit/mdGrammar.js new file mode 100644 index 00000000..faa634b8 --- /dev/null +++ b/src/cledit/mdGrammar.js @@ -0,0 +1,422 @@ +var charInsideUrl = '(&|[-A-Z0-9+@#/%?=~_|[\\]()!:,.;])' +var charEndingUrl = '(&|[-A-Z0-9+@#/%=~_|[\\])])' +var urlPattern = new RegExp('(https?|ftp)(://' + charInsideUrl + '*' + charEndingUrl + ')(?=$|\\W)', 'gi') +var emailPattern = /(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)/gi + +var markup = { + 'comment': //g, + 'tag': { + pattern: /<\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|[^\s'">=]+))?\s*)*\/?>/gi, + inside: { + 'tag': { + pattern: /^<\/?[\w:-]+/i, + inside: { + 'punctuation': /^<\/?/, + 'namespace': /^[\w-]+?:/ + } + }, + 'attr-value': { + pattern: /=(?:('|")[\w\W]*?(\1)|[^\s>]+)/gi, + inside: { + 'punctuation': /=|>|"/g + } + }, + 'punctuation': /\/?>/g, + 'attr-name': { + pattern: /[\w:-]+/g, + inside: { + 'namespace': /^[\w-]+?:/ + } + } + } + }, + 'entity': /&#?[\da-z]{1,8};/gi +} + +var latex = { + // A tex command e.g. \foo + 'keyword': /\\(?:[^a-zA-Z]|[a-zA-Z]+)/g, + // Curly and square braces + 'lparen': /[[({]/g, + // Curly and square braces + 'rparen': /[\])}]/g, + // A comment. Tex comments start with % and go to + // the end of the line + 'comment': /%.*/g +} + +module.exports = function (options) { + options = options || {} + var grammar = {} + var insideFences = options.insideFences || {} + insideFences['cl cl-pre'] = /`{3}|~{3}/ + if (options.fences) { + grammar['pre gfm'] = { + pattern: /^(`{3}|~{3}).*\n(?:[\s\S]*?)\n\1 *$/gm, + inside: insideFences + } + } + grammar.li = { + pattern: new RegExp( + [ + '^ {0,3}(?:[*+\\-]|\\d+\\.)[ \\t].+\\n', // Item line + '(?:', + '(?:', + '.*\\S.*\\n', // Non-empty line + '|', + '[ \\t]*\\n(?! ?\\S)', // Or empty line not followed by unindented line + ')', + ')*' + ].join(''), + 'gm' + ), + inside: { + 'cl cl-li': /^[ \t]*([*+\-]|\d+\.)[ \t]/gm + } + } + if (options.fences) { + grammar.li.inside['pre gfm'] = { + pattern: /^((?: {4}|\t)+)(`{3}|~{3}).*\n(?:[\s\S]*?)\n\1\2\s*$/gm, + inside: insideFences + } + } + grammar.blockquote = { + pattern: /^ {0,3}>.+(?:\n[ \t]*\S.*)*/gm, + inside: { + 'cl cl-gt': /^\s*>/gm, + 'li': grammar.li + } + } + grammar['h1 alt'] = { + pattern: /^.+\n=+[ \t]*$/gm, + inside: { + 'cl cl-hash': /=+[ \t]*$/ + } + } + grammar['h2 alt'] = { + pattern: /^.+\n-+[ \t]*$/gm, + inside: { + 'cl cl-hash': /-+[ \t]*$/ + } + } + for (var i = 6; i >= 1; i--) { + grammar['h' + i] = { + pattern: new RegExp('^#{' + i + '}[ \t].+$', 'gm'), + inside: { + 'cl cl-hash': new RegExp('^#{' + i + '}') + } + } + } + if (options.tables) { + grammar.table = { + pattern: new RegExp( + [ + '^', + '[ ]{0,3}', + '[|]', // Initial pipe + '.+\\n', // Header Row + '[ ]{0,3}', + '[|][ ]*[-:]+[-| :]*\\n', // Separator + '(?:[ \t]*[|].*\\n?)*', // Table rows + '$' + ].join(''), + 'gm' + ), + inside: {} + } + grammar['table alt'] = { + pattern: new RegExp( + [ + '^', + '[ ]{0,3}', + '\\S.*[|].*\\n', // Header Row + '[ ]{0,3}', + '[-:]+[ ]*[|][-| :]*\\n', // Separator + '(?:.*[|].*\\n?)*', // Table rows + '$' // Stop at final newline + ].join(''), + 'gm' + ), + inside: {} + } + } + if (options.deflists) { + grammar.deflist = { + pattern: new RegExp( + [ + '^ {0,3}\\S.*\\n', // Description line + '(?:[ \\t]*\\n)?', // Optional empty line + '(?:', + '[ \\t]*:[ \\t].*\\n', // Colon line + '(?:', + '(?:', + '.*\\S.*\\n', // Non-empty line + '|', + '[ \\t]*\\n(?! ?\\S)', // Or empty line not followed by unindented line + ')', + ')*', + '(?:[ \\t]*\\n)*', // Empty lines + ')+' + ].join(''), + 'gm' + ), + inside: { + 'deflist-desc': { + pattern: /( {0,3}\S.*\n(?:[ \t]*\n)?)[\s\S]*/, + lookbehind: true, + inside: { + 'cl': /^[ \t]*:[ \t]/gm + } + }, + 'term': /.+/g + } + } + if (options.fences) { + grammar.deflist.inside['deflist-desc'].inside['pre gfm'] = { + pattern: /^((?: {4}|\t)+)(`{3}|~{3}).*\n(?:[\s\S]*?)\n\1\2\s*$/gm, + inside: insideFences + } + } + } + grammar.hr = { + pattern: /^ {0,3}([*\-_] *){3,}$/gm + } + if (options.footnotes) { + grammar.fndef = { + pattern: /^ {0,3}\[\^.*?\]:.*$/gm, + inside: { + 'ref-id': { + pattern: /^ {0,3}\[\^.*?\]/, + inside: { + cl: /(\[\^|\])/ + } + } + } + } + } + if (options.abbrs) { + grammar.abbrdef = { + pattern: /^ {0,3}\*\[.*?\]:.*$/gm, + inside: { + 'abbr-id': { + pattern: /^ {0,3}\*\[.*?\]/, + inside: { + cl: /(\*\[|\])/ + } + } + } + } + } + grammar.linkdef = { + pattern: /^ {0,3}\[.*?\]:.*$/gm, + inside: { + 'link-id': { + pattern: /^ {0,3}\[.*?\]/, + inside: { + cl: /[\[\]]/ + } + }, + url: urlPattern + } + } + grammar.p = { + pattern: /^ {0,3}\S.*$(\n.*\S.*)*/gm, + inside: {} + } + if (options.tocs) { + grammar.p.inside['cl cl-toc'] = /^[ \t]*\[toc\]$/mi + } + grammar.pre = { + pattern: /(?: {4}|\t).*\S.*\n((?: {4}|\t).*\n)*/g + } + + var rest = {} + rest.code = { + pattern: /(`+)[\s\S]*?\1/g, + inside: { + 'cl cl-code': /`/ + } + } + if (options.maths) { + rest['math block'] = { + pattern: /\\\\\[[\s\S]*?\\\\\]/g, + inside: { + 'cl cl-bracket-start': /^\\\\\[/, + 'cl cl-bracket-end': /\\\\\]$/, + rest: latex + } + } + rest['math inline'] = { + pattern: /\\\\\([\s\S]*?\\\\\)/g, + inside: { + 'cl cl-bracket-start': /^\\\\\(/, + 'cl cl-bracket-end': /\\\\\)$/, + rest: latex + } + } + rest['math expr block'] = { + pattern: /(\$\$)[\s\S]*?\1/g, + inside: { + 'cl cl-bracket-start': /^\$\$/, + 'cl cl-bracket-end': /\$\$$/, + rest: latex + } + } + rest['math expr inline'] = { + pattern: /\$(?!\s)[\s\S]*?\S\$(?!\d)/g, + inside: { + 'cl cl-bracket-start': /^\$/, + 'cl cl-bracket-end': /\$$/, + rest: latex + } + } + rest['latex block'] = { + pattern: /\\begin\{([a-z]*\*?)\}[\s\S]*?\\?\\end\{\1\}/g, + inside: { + 'keyword': /\\(begin|end)/, + rest: latex + } + } + } + if (options.footnotes) { + rest.inlinefn = { + pattern: /\^\[.+?\]/g, + inside: { + 'cl': /(\^\[|\])/ + } + } + rest.fn = { + pattern: /\[\^.+?\]/g, + inside: { + 'cl': /(\[\^|\])/ + } + } + } + rest.img = { + pattern: /!\[.*?\]\(.+?\)/g, + inside: { + 'cl cl-title': /['‘][^'’]*['’]|["“][^"”]*["”](?=\)$)/, + 'cl cl-src': { + pattern: /(\]\()[^\('" \t]+(?=[\)'" \t])/, + lookbehind: true + } + } + } + rest.link = { + pattern: /\[.*?\]\(.+?\)/gm, + inside: { + 'cl cl-underlined-text': { + pattern: /(\[)[^\]]*/, + lookbehind: true + }, + 'cl cl-title': /['‘][^'’]*['’]|["“][^"”]*["”](?=\)$)/ + } + } + rest.imgref = { + pattern: /!\[.*?\][ \t]*\[.*?\]/g + } + rest.linkref = { + pattern: /\[.*?\][ \t]*\[.*?\]/g, + inside: { + 'cl cl-underlined-text': { + pattern: /^(\[)[^\]]*(?=\][ \t]*\[)/, + lookbehind: true + } + } + } + rest.comment = markup.comment + rest.tag = markup.tag + rest.url = urlPattern + rest.email = emailPattern + rest.strong = { + pattern: /(^|[^\w*])([_\*])\2(?![_\*])[\s\S]*?\2{2}(?=([^\w*]|$))/gm, + lookbehind: true, + inside: { + 'cl cl-strong cl-start': /^([_\*])\1/, + 'cl cl-strong cl-close': /([_\*])\1$/ + } + } + rest.em = { + pattern: /(^|[^\w*])([_\*])(?![_\*])[\s\S]*?\2(?=([^\w*]|$))/gm, + lookbehind: true, + inside: { + 'cl cl-em cl-start': /^[_\*]/, + 'cl cl-em cl-close': /[_\*]$/ + } + } + if (options.dels) { + rest.del = { + pattern: /(^|[^\w*])(~~)[\s\S]*?\2(?=([^\w*]|$))/gm, + lookbehind: true, + inside: { + 'cl': /~~/, + 'cl-del-text': /[^~]+/ + } + } + } + if (options.subs) { + rest.sub = { + pattern: /(~)(?=\S)(.*?\S)\1/gm, + inside: { + 'cl': /~/ + } + } + } + if (options.sups) { + rest.sup = { + pattern: /(\^)(?=\S)(.*?\S)\1/gm, + inside: { + 'cl': /\^/ + } + } + } + rest.entity = markup.entity + + for (var c = 6; c >= 1; c--) { + grammar['h' + c].inside.rest = rest + } + grammar['h1 alt'].inside.rest = rest + grammar['h2 alt'].inside.rest = rest + if (options.tables) { + grammar.table.inside.rest = rest + grammar['table alt'].inside.rest = rest + } + grammar.p.inside.rest = rest + grammar.blockquote.inside.rest = rest + grammar.li.inside.rest = rest + if (options.footnotes) { + grammar.fndef.inside.rest = rest + } + if (options.deflists) { + grammar.deflist.inside['deflist-desc'].inside.rest = rest + } + + var restLight = { + code: rest.code, + inlinefn: rest.inlinefn, + fn: rest.fn, + link: rest.link, + linkref: rest.linkref + } + rest.strong.inside.rest = restLight + rest.em.inside.rest = restLight + if (options.dels) { + rest.del.inside.rest = restLight + } + + var inside = { + code: rest.code, + comment: rest.comment, + tag: rest.tag, + strong: rest.strong, + em: rest.em, + del: rest.del, + sub: rest.sub, + sup: rest.sup, + entity: markup.entity + } + rest.link.inside['cl cl-underlined-text'].inside = inside + rest.linkref.inside['cl cl-underlined-text'].inside = inside + + return grammar +} diff --git a/src/cledit/pagedown.js b/src/cledit/pagedown.js new file mode 100644 index 00000000..3e77eaef --- /dev/null +++ b/src/cledit/pagedown.js @@ -0,0 +1,1362 @@ +var util = {}, + re = window.RegExp, + SETTINGS = { + lineLength: 72 + }; + +var defaultsStrings = { + bold: "Strong Ctrl/Cmd+B", + boldexample: "strong text", + + italic: "Emphasis Ctrl/Cmd+I", + italicexample: "emphasized text", + + strikethrough: "Strikethrough Ctrl/Cmd+I", + strikethroughexample: "strikethrough text", + + link: "Hyperlink Ctrl/Cmd+L", + linkdescription: "enter link description here", + linkdialog: "

Insert Hyperlink

http://example.com/ \"optional title\"

", + + quote: "Blockquote
Ctrl/Cmd+Q", + quoteexample: "Blockquote", + + code: "Code Sample
 Ctrl/Cmd+K",
+  codeexample: "enter code here",
+
+  image: "Image  Ctrl/Cmd+G",
+  imagedescription: "enter image description here",
+  imagedialog: "

Insert Image

http://example.com/images/diagram.jpg \"optional title\"

Need
free image hosting?

", + + olist: "Numbered List
    Ctrl/Cmd+O", + ulist: "Bulleted List
      Ctrl/Cmd+U", + litem: "List item", + + heading: "Heading

      /

      Ctrl/Cmd+H", + headingexample: "Heading", + + hr: "Horizontal Rule
      Ctrl/Cmd+R", + + undo: "Undo - Ctrl/Cmd+Z", + redo: "Redo - Ctrl/Cmd+Y", + + help: "Markdown Editing Help" +}; + +// options, if given, can have the following properties: +// options.helpButton = { handler: yourEventHandler } +// options.strings = { italicexample: "slanted text" } +// `yourEventHandler` is the click handler for the help button. +// If `options.helpButton` isn't given, not help button is created. +// `options.strings` can have any or all of the same properties as +// `defaultStrings` above, so you can just override some string displayed +// to the user on a case-by-case basis, or translate all strings to +// a different language. +// +// For backwards compatibility reasons, the `options` argument can also +// be just the `helpButton` object, and `strings.help` can also be set via +// `helpButton.title`. This should be considered legacy. +// +// The constructed editor object has the methods: +// - getConverter() returns the markdown converter object that was passed to the constructor +// - run() actually starts the editor; should be called after all necessary plugins are registered. Calling this more than once is a no-op. +// - refreshPreview() forces the preview to be updated. This method is only available after run() was called. +function Pagedown(options) { + + options = options || {}; + + if (typeof options.handler === "function") { //backwards compatible behavior + options = { + helpButton: options + }; + } + options.strings = options.strings || {}; + var getString = function (identifier) { + return options.strings[identifier] || defaultsStrings[identifier]; + }; + + function identity(x) { + return x; + } + + function returnFalse() { + return false; + } + + function HookCollection() { } + HookCollection.prototype = { + + chain: function (hookname, func) { + var original = this[hookname]; + if (!original) { + throw new Error("unknown hook " + hookname); + } + + if (original === identity) { + this[hookname] = func; + } else { + this[hookname] = function () { + var args = Array.prototype.slice.call(arguments, 0); + args[0] = original.apply(null, args); + return func.apply(null, args); + }; + } + }, + set: function (hookname, func) { + if (!this[hookname]) { + throw new Error("unknown hook " + hookname); + } + this[hookname] = func; + }, + addNoop: function (hookname) { + this[hookname] = identity; + }, + addFalse: function (hookname) { + this[hookname] = returnFalse; + } + }; + + var hooks = this.hooks = new HookCollection(); + hooks.addNoop("onPreviewRefresh"); // called with no arguments after the preview has been refreshed + hooks.addNoop("postBlockquoteCreation"); // called with the user's selection *after* the blockquote was created; should return the actual to-be-inserted text + hooks.addFalse("insertImageDialog"); + /* called with one parameter: a callback to be called with the URL of the image. If the application creates + * its own image insertion dialog, this hook should return true, and the callback should be called with the chosen + * image url (or null if the user cancelled). If this hook returns false, the default dialog will be used. + */ + hooks.addFalse("insertLinkDialog"); + + var that = this, + input; + + this.run = function () { + if (input) + return; // already initialized + + input = options.input; + var commandManager = new CommandManager(hooks, getString); + var uiManager; + + uiManager = new UIManager(input, commandManager); + + that.uiManager = uiManager; + }; + +} + +// before: contains all the text in the input box BEFORE the selection. +// after: contains all the text in the input box AFTER the selection. +function Chunks() { } + +// startRegex: a regular expression to find the start tag +// endRegex: a regular expresssion to find the end tag +Chunks.prototype.findTags = function (startRegex, endRegex) { + + var chunkObj = this; + var regex; + + if (startRegex) { + + regex = util.extendRegExp(startRegex, "", "$"); + + this.before = this.before.replace(regex, + function (match) { + chunkObj.startTag = chunkObj.startTag + match; + return ""; + }); + + regex = util.extendRegExp(startRegex, "^", ""); + + this.selection = this.selection.replace(regex, + function (match) { + chunkObj.startTag = chunkObj.startTag + match; + return ""; + }); + } + + if (endRegex) { + + regex = util.extendRegExp(endRegex, "", "$"); + + this.selection = this.selection.replace(regex, + function (match) { + chunkObj.endTag = match + chunkObj.endTag; + return ""; + }); + + regex = util.extendRegExp(endRegex, "^", ""); + + this.after = this.after.replace(regex, + function (match) { + chunkObj.endTag = match + chunkObj.endTag; + return ""; + }); + } +}; + +// If remove is false, the whitespace is transferred +// to the before/after regions. +// +// If remove is true, the whitespace disappears. +Chunks.prototype.trimWhitespace = function (remove) { + var beforeReplacer, afterReplacer, that = this; + if (remove) { + beforeReplacer = afterReplacer = ""; + } else { + beforeReplacer = function (s) { + that.before += s; + return ""; + }; + afterReplacer = function (s) { + that.after = s + that.after; + return ""; + }; + } + + this.selection = this.selection.replace(/^(\s*)/, beforeReplacer).replace(/(\s*)$/, afterReplacer); +}; + + +Chunks.prototype.skipLines = function (nLinesBefore, nLinesAfter, findExtraNewlines) { + + if (nLinesBefore === undefined) { + nLinesBefore = 1; + } + + if (nLinesAfter === undefined) { + nLinesAfter = 1; + } + + nLinesBefore++; + nLinesAfter++; + + var regexText; + var replacementText; + + // chrome bug ... documented at: http://meta.stackoverflow.com/questions/63307/blockquote-glitch-in-editor-in-chrome-6-and-7/65985#65985 + if (navigator.userAgent.match(/Chrome/)) { + "X".match(/()./); + } + + this.selection = this.selection.replace(/(^\n*)/, ""); + + this.startTag = this.startTag + re.$1; + + this.selection = this.selection.replace(/(\n*$)/, ""); + this.endTag = this.endTag + re.$1; + this.startTag = this.startTag.replace(/(^\n*)/, ""); + this.before = this.before + re.$1; + this.endTag = this.endTag.replace(/(\n*$)/, ""); + this.after = this.after + re.$1; + + if (this.before) { + + regexText = replacementText = ""; + + while (nLinesBefore--) { + regexText += "\\n?"; + replacementText += "\n"; + } + + if (findExtraNewlines) { + regexText = "\\n*"; + } + this.before = this.before.replace(new re(regexText + "$", ""), replacementText); + } + + if (this.after) { + + regexText = replacementText = ""; + + while (nLinesAfter--) { + regexText += "\\n?"; + replacementText += "\n"; + } + if (findExtraNewlines) { + regexText = "\\n*"; + } + + this.after = this.after.replace(new re(regexText, ""), replacementText); + } +}; + +// end of Chunks + +// Converts \r\n and \r to \n. +util.fixEolChars = function (text) { + text = text.replace(/\r\n/g, "\n"); + text = text.replace(/\r/g, "\n"); + return text; +}; + +// Extends a regular expression. Returns a new RegExp +// using pre + regex + post as the expression. +// Used in a few functions where we have a base +// expression and we want to pre- or append some +// conditions to it (e.g. adding "$" to the end). +// The flags are unchanged. +// +// regex is a RegExp, pre and post are strings. +util.extendRegExp = function (regex, pre, post) { + + if (pre === null || pre === undefined) { + pre = ""; + } + if (post === null || post === undefined) { + post = ""; + } + + var pattern = regex.toString(); + var flags; + + // Replace the flags with empty space and store them. + pattern = pattern.replace(/\/([gim]*)$/, function (wholeMatch, flagsPart) { + flags = flagsPart; + return ""; + }); + + // Remove the slash delimiters on the regular expression. + pattern = pattern.replace(/(^\/|\/$)/g, ""); + pattern = pre + pattern + post; + + return new re(pattern, flags); +}; + +// The input textarea state/contents. +// This is used to implement undo/redo by the undo manager. +function TextareaState(input) { + + // Aliases + var stateObj = this; + var inputArea = input; + this.init = function () { + this.setInputAreaSelectionStartEnd(); + this.text = inputArea.getContent(); + }; + + // Sets the selected text in the input box after we've performed an + // operation. + this.setInputAreaSelection = function () { + inputArea.focus(); + inputArea.setSelection(stateObj.start, stateObj.end); + }; + + this.setInputAreaSelectionStartEnd = function () { + stateObj.start = Math.min( + inputArea.selectionMgr.selectionStart, + inputArea.selectionMgr.selectionEnd + ); + stateObj.end = Math.max( + inputArea.selectionMgr.selectionStart, + inputArea.selectionMgr.selectionEnd + ); + }; + + // Restore this state into the input area. + this.restore = function () { + + if (stateObj.text !== undefined && stateObj.text != inputArea.getContent()) { + inputArea.setContent(stateObj.text); + } + this.setInputAreaSelection(); + }; + + // Gets a collection of HTML chunks from the inptut textarea. + this.getChunks = function () { + + var chunk = new Chunks(); + chunk.before = util.fixEolChars(stateObj.text.substring(0, stateObj.start)); + chunk.startTag = ""; + chunk.selection = util.fixEolChars(stateObj.text.substring(stateObj.start, stateObj.end)); + chunk.endTag = ""; + chunk.after = util.fixEolChars(stateObj.text.substring(stateObj.end)); + + return chunk; + }; + + // Sets the TextareaState properties given a chunk of markdown. + this.setChunks = function (chunk) { + + chunk.before = chunk.before + chunk.startTag; + chunk.after = chunk.endTag + chunk.after; + + this.start = chunk.before.length; + this.end = chunk.before.length + chunk.selection.length; + this.text = chunk.before + chunk.selection + chunk.after; + }; + this.init(); +} + +function UIManager(input, commandManager) { + + var inputBox = input, + buttons = {}; // buttons.undo, buttons.link, etc. The actual DOM elements. + + makeSpritedButtonRow(); + + // Perform the button's action. + function doClick(buttonName) { + var button = buttons[buttonName]; + if (!button) { + return; + } + + inputBox.focus(); + var linkOrImage = button === buttons.link || button.id === buttons.image; + + var state = new TextareaState(input); + + if (!state) { + return; + } + + var chunks = state.getChunks(); + + // Some commands launch a "modal" prompt dialog. Javascript + // can't really make a modal dialog box and the WMD code + // will continue to execute while the dialog is displayed. + // This prevents the dialog pattern I'm used to and means + // I can't do something like this: + // + // var link = CreateLinkDialog(); + // makeMarkdownLink(link); + // + // Instead of this straightforward method of handling a + // dialog I have to pass any code which would execute + // after the dialog is dismissed (e.g. link creation) + // in a function parameter. + // + // Yes this is awkward and I think it sucks, but there's + // no real workaround. Only the image and link code + // create dialogs and require the function pointers. + var fixupInputArea = function () { + + inputBox.focus(); + + if (chunks) { + state.setChunks(chunks); + } + + state.restore(); + }; + + var noCleanup = button(chunks, fixupInputArea); + + if (!noCleanup) { + fixupInputArea(); + if (!linkOrImage) { + inputBox.adjustCursorPosition(); + } + } + } + + function bindCommand(method) { + if (typeof method === "string") + method = commandManager[method]; + return function () { + method.apply(commandManager, arguments); + }; + } + + function makeSpritedButtonRow() { + + buttons.bold = bindCommand("doBold"); + buttons.italic = bindCommand("doItalic"); + buttons.strikethrough = bindCommand("doStrikethrough"); + buttons.link = bindCommand(function (chunk, postProcessing) { + return this.doLinkOrImage(chunk, postProcessing, false); + }); + buttons.quote = bindCommand("doBlockquote"); + buttons.code = bindCommand("doCode"); + buttons.image = bindCommand(function (chunk, postProcessing) { + return this.doLinkOrImage(chunk, postProcessing, true); + }); + buttons.olist = bindCommand(function (chunk, postProcessing) { + this.doList(chunk, postProcessing, true); + }); + buttons.ulist = bindCommand(function (chunk, postProcessing) { + this.doList(chunk, postProcessing, false); + }); + buttons.heading = bindCommand("doHeading"); + buttons.hr = bindCommand("doHorizontalRule"); + buttons.table = bindCommand("doTable"); + } + + this.doClick = doClick; + +} + +function CommandManager(pluginHooks, getString) { + this.hooks = pluginHooks; + this.getString = getString; +} + +var commandProto = CommandManager.prototype; + +// The markdown symbols - 4 spaces = code, > = blockquote, etc. +commandProto.prefixes = "(?:\\s{4,}|\\s*>|\\s*-\\s+|\\s*\\d+\\.|=|\\+|-|_|\\*|#|\\s*\\[[^\n]]+\\]:)"; + +// Remove markdown symbols from the chunk selection. +commandProto.unwrap = function (chunk) { + var txt = new re("([^\\n])\\n(?!(\\n|" + this.prefixes + "))", "g"); + chunk.selection = chunk.selection.replace(txt, "$1 $2"); +}; + +commandProto.wrap = function (chunk, len) { + this.unwrap(chunk); + var regex = new re("(.{1," + len + "})( +|$\\n?)", "gm"), + that = this; + + chunk.selection = chunk.selection.replace(regex, function (line, marked) { + if (new re("^" + that.prefixes, "").test(line)) { + return line; + } + return marked + "\n"; + }); + + chunk.selection = chunk.selection.replace(/\s+$/, ""); +}; + +commandProto.doBold = function (chunk, postProcessing) { + return this.doBorI(chunk, postProcessing, 2, this.getString("boldexample")); +}; + +commandProto.doItalic = function (chunk, postProcessing) { + return this.doBorI(chunk, postProcessing, 1, this.getString("italicexample")); +}; + +// chunk: The selected region that will be enclosed with */** +// nStars: 1 for italics, 2 for bold +// insertText: If you just click the button without highlighting text, this gets inserted +commandProto.doBorI = function (chunk, postProcessing, nStars, insertText) { + + // Get rid of whitespace and fixup newlines. + chunk.trimWhitespace(); + chunk.selection = chunk.selection.replace(/\n{2,}/g, "\n"); + + // Look for stars before and after. Is the chunk already marked up? + // note that these regex matches cannot fail + var starsBefore = /(\**$)/.exec(chunk.before)[0]; + var starsAfter = /(^\**)/.exec(chunk.after)[0]; + + var prevStars = Math.min(starsBefore.length, starsAfter.length); + + // Remove stars if we have to since the button acts as a toggle. + if ((prevStars >= nStars) && (prevStars != 2 || nStars != 1)) { + chunk.before = chunk.before.replace(re("[*]{" + nStars + "}$", ""), ""); + chunk.after = chunk.after.replace(re("^[*]{" + nStars + "}", ""), ""); + } else if (!chunk.selection && starsAfter) { + // It's not really clear why this code is necessary. It just moves + // some arbitrary stuff around. + chunk.after = chunk.after.replace(/^([*_]*)/, ""); + chunk.before = chunk.before.replace(/(\s?)$/, ""); + var whitespace = re.$1; + chunk.before = chunk.before + starsAfter + whitespace; + } else { + + // In most cases, if you don't have any selected text and click the button + // you'll get a selected, marked up region with the default text inserted. + if (!chunk.selection && !starsAfter) { + chunk.selection = insertText; + } + + // Add the true markup. + var markup = nStars <= 1 ? "*" : "**"; // shouldn't the test be = ? + chunk.before = chunk.before + markup; + chunk.after = markup + chunk.after; + } + + return; +}; + +commandProto.doStrikethrough = function (chunk, postProcessing) { + + // Get rid of whitespace and fixup newlines. + chunk.trimWhitespace(); + chunk.selection = chunk.selection.replace(/\n{2,}/g, "\n"); + + // Look for stars before and after. Is the chunk already marked up? + // note that these regex matches cannot fail + var starsBefore = /(~*$)/.exec(chunk.before)[0]; + var starsAfter = /(^~*)/.exec(chunk.after)[0]; + + var prevStars = Math.min(starsBefore.length, starsAfter.length); + + var nStars = 2; + + // Remove stars if we have to since the button acts as a toggle. + if ((prevStars >= nStars) && (prevStars != 2 || nStars != 1)) { + chunk.before = chunk.before.replace(re("[~]{" + nStars + "}$", ""), ""); + chunk.after = chunk.after.replace(re("^[~]{" + nStars + "}", ""), ""); + } else if (!chunk.selection && starsAfter) { + // It's not really clear why this code is necessary. It just moves + // some arbitrary stuff around. + chunk.after = chunk.after.replace(/^(~*)/, ""); + chunk.before = chunk.before.replace(/(\s?)$/, ""); + var whitespace = re.$1; + chunk.before = chunk.before + starsAfter + whitespace; + } else { + + // In most cases, if you don't have any selected text and click the button + // you'll get a selected, marked up region with the default text inserted. + if (!chunk.selection && !starsAfter) { + chunk.selection = this.getString("strikethroughexample"); + } + + // Add the true markup. + var markup = "~~"; // shouldn't the test be = ? + chunk.before = chunk.before + markup; + chunk.after = markup + chunk.after; + } + + return; +}; + +commandProto.stripLinkDefs = function (text, defsToAdd) { + + text = text.replace(/^[ ]{0,3}\[(\d+)\]:[ \t]*\n?[ \t]*?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|$)/gm, + function (totalMatch, id, link, newlines, title) { + defsToAdd[id] = totalMatch.replace(/\s*$/, ""); + if (newlines) { + // Strip the title and return that separately. + defsToAdd[id] = totalMatch.replace(/["(](.+?)[")]$/, ""); + return newlines + title; + } + return ""; + }); + + return text; +}; + +commandProto.addLinkDef = function (chunk, linkDef) { + + var refNumber = 0; // The current reference number + var defsToAdd = {}; // + // Start with a clean slate by removing all previous link definitions. + chunk.before = this.stripLinkDefs(chunk.before, defsToAdd); + chunk.selection = this.stripLinkDefs(chunk.selection, defsToAdd); + chunk.after = this.stripLinkDefs(chunk.after, defsToAdd); + + var defs = ""; + var regex = /(\[)((?:\[[^\]]*\]|[^\[\]])*)(\][ ]?(?:\n[ ]*)?\[)(\d+)(\])/g; + + var addDefNumber = function (def) { + refNumber++; + def = def.replace(/^[ ]{0,3}\[(\d+)\]:/, " [" + refNumber + "]:"); + defs += "\n" + def; + }; + + // note that + // a) the recursive call to getLink cannot go infinite, because by definition + // of regex, inner is always a proper substring of wholeMatch, and + // b) more than one level of nesting is neither supported by the regex + // nor making a lot of sense (the only use case for nesting is a linked image) + var getLink = function (wholeMatch, before, inner, afterInner, id, end) { + inner = inner.replace(regex, getLink); + if (defsToAdd[id]) { + addDefNumber(defsToAdd[id]); + return before + inner + afterInner + refNumber + end; + } + return wholeMatch; + }; + + chunk.before = chunk.before.replace(regex, getLink); + + if (linkDef) { + addDefNumber(linkDef); + } else { + chunk.selection = chunk.selection.replace(regex, getLink); + } + + var refOut = refNumber; + + chunk.after = chunk.after.replace(regex, getLink); + + if (chunk.after) { + chunk.after = chunk.after.replace(/\n*$/, ""); + } + if (!chunk.after) { + chunk.selection = chunk.selection.replace(/\n*$/, ""); + } + + chunk.after += "\n\n" + defs; + + return refOut; +}; + +// takes the line as entered into the add link/as image dialog and makes +// sure the URL and the optinal title are "nice". +function properlyEncoded(linkdef) { + return linkdef.replace(/^\s*(.*?)(?:\s+"(.+)")?\s*$/, function (wholematch, link, title) { + link = link.replace(/\?.*$/, function (querypart) { + return querypart.replace(/\+/g, " "); // in the query string, a plus and a space are identical + }); + link = decodeURIComponent(link); // unencode first, to prevent double encoding + link = encodeURI(link).replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29'); + link = link.replace(/\?.*$/, function (querypart) { + return querypart.replace(/\+/g, "%2b"); // since we replaced plus with spaces in the query part, all pluses that now appear where originally encoded + }); + if (title) { + title = title.trim ? title.trim() : title.replace(/^\s*/, "").replace(/\s*$/, ""); + title = title.replace(/"/g, "quot;").replace(/\(/g, "(").replace(/\)/g, ")").replace(//g, ">"); + } + return title ? link + ' "' + title + '"' : link; + }); +} + +commandProto.doLinkOrImage = function (chunk, postProcessing, isImage) { + + chunk.trimWhitespace(); + //chunk.findTags(/\s*!?\[/, /\][ ]?(?:\n[ ]*)?(\[.*?\])?/); + chunk.findTags(/\s*!?\[/, /\][ ]?(?:\n[ ]*)?(\(.*?\))?/); + + if (chunk.endTag.length > 1 && chunk.startTag.length > 0) { + + chunk.startTag = chunk.startTag.replace(/!?\[/, ""); + chunk.endTag = ""; + this.addLinkDef(chunk, null); + + } else { + + // We're moving start and end tag back into the selection, since (as we're in the else block) we're not + // *removing* a link, but *adding* one, so whatever findTags() found is now back to being part of the + // link text. linkEnteredCallback takes care of escaping any brackets. + chunk.selection = chunk.startTag + chunk.selection + chunk.endTag; + chunk.startTag = chunk.endTag = ""; + + if (/\n\n/.test(chunk.selection)) { + this.addLinkDef(chunk, null); + return; + } + var that = this; + // The function to be executed when you enter a link and press OK or Cancel. + // Marks up the link and adds the ref. + var linkEnteredCallback = function (link) { + + if (link !== null) { + // ( $1 + // [^\\] anything that's not a backslash + // (?:\\\\)* an even number (this includes zero) of backslashes + // ) + // (?= followed by + // [[\]] an opening or closing bracket + // ) + // + // In other words, a non-escaped bracket. These have to be escaped now to make sure they + // don't count as the end of the link or similar. + // Note that the actual bracket has to be a lookahead, because (in case of to subsequent brackets), + // the bracket in one match may be the "not a backslash" character in the next match, so it + // should not be consumed by the first match. + // The "prepend a space and finally remove it" steps makes sure there is a "not a backslash" at the + // start of the string, so this also works if the selection begins with a bracket. We cannot solve + // this by anchoring with ^, because in the case that the selection starts with two brackets, this + // would mean a zero-width match at the start. Since zero-width matches advance the string position, + // the first bracket could then not act as the "not a backslash" for the second. + chunk.selection = (" " + chunk.selection).replace(/([^\\](?:\\\\)*)(?=[[\]])/g, "$1\\").substr(1); + + /* + var linkDef = " [999]: " + properlyEncoded(link); + + var num = that.addLinkDef(chunk, linkDef); + */ + chunk.startTag = isImage ? "![" : "["; + //chunk.endTag = "][" + num + "]"; + chunk.endTag = "](" + properlyEncoded(link) + ")"; + + if (!chunk.selection) { + if (isImage) { + chunk.selection = that.getString("imagedescription"); + } else { + chunk.selection = that.getString("linkdescription"); + } + } + } + postProcessing(); + }; + + if (isImage) { + this.hooks.insertImageDialog(linkEnteredCallback); + } else { + this.hooks.insertLinkDialog(linkEnteredCallback); + } + return true; + } +}; + +// When making a list, hitting shift-enter will put your cursor on the next line +// at the current indent level. +commandProto.doAutoindent = function (chunk) { + + var commandMgr = this, + fakeSelection = false; + + chunk.before = chunk.before.replace(/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]*\n$/, "\n\n"); + chunk.before = chunk.before.replace(/(\n|^)[ ]{0,3}>[ \t]*\n$/, "\n\n"); + chunk.before = chunk.before.replace(/(\n|^)[ \t]+\n$/, "\n\n"); + + // There's no selection, end the cursor wasn't at the end of the line: + // The user wants to split the current list item / code line / blockquote line + // (for the latter it doesn't really matter) in two. Temporarily select the + // (rest of the) line to achieve this. + if (!chunk.selection && !/^[ \t]*(?:\n|$)/.test(chunk.after)) { + chunk.after = chunk.after.replace(/^[^\n]*/, function (wholeMatch) { + chunk.selection = wholeMatch; + return ""; + }); + fakeSelection = true; + } + + if (/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]+.*\n$/.test(chunk.before)) { + if (commandMgr.doList) { + commandMgr.doList(chunk); + } + } + if (/(\n|^)[ ]{0,3}>[ \t]+.*\n$/.test(chunk.before)) { + if (commandMgr.doBlockquote) { + commandMgr.doBlockquote(chunk); + } + } + if (/(\n|^)(\t|[ ]{4,}).*\n$/.test(chunk.before)) { + if (commandMgr.doCode) { + commandMgr.doCode(chunk); + } + } + + if (fakeSelection) { + chunk.after = chunk.selection + chunk.after; + chunk.selection = ""; + } +}; + +commandProto.doBlockquote = function (chunk) { + + chunk.selection = chunk.selection.replace(/^(\n*)([^\r]+?)(\n*)$/, + function (totalMatch, newlinesBefore, text, newlinesAfter) { + chunk.before += newlinesBefore; + chunk.after = newlinesAfter + chunk.after; + return text; + }); + + chunk.before = chunk.before.replace(/(>[ \t]*)$/, + function (totalMatch, blankLine) { + chunk.selection = blankLine + chunk.selection; + return ""; + }); + + chunk.selection = chunk.selection.replace(/^(\s|>)+$/, ""); + chunk.selection = chunk.selection || this.getString("quoteexample"); + + // The original code uses a regular expression to find out how much of the + // text *directly before* the selection already was a blockquote: + + /* + if (chunk.before) { + chunk.before = chunk.before.replace(/\n?$/, "\n"); + } + chunk.before = chunk.before.replace(/(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*$)/, + function (totalMatch) { + chunk.startTag = totalMatch; + return ""; + }); + */ + + // This comes down to: + // Go backwards as many lines a possible, such that each line + // a) starts with ">", or + // b) is almost empty, except for whitespace, or + // c) is preceeded by an unbroken chain of non-empty lines + // leading up to a line that starts with ">" and at least one more character + // and in addition + // d) at least one line fulfills a) + // + // Since this is essentially a backwards-moving regex, it's susceptible to + // catstrophic backtracking and can cause the browser to hang; + // see e.g. http://meta.stackoverflow.com/questions/9807. + // + // Hence we replaced this by a simple state machine that just goes through the + // lines and checks for a), b), and c). + + var match = "", + leftOver = "", + line; + if (chunk.before) { + var lines = chunk.before.replace(/\n$/, "").split("\n"); + var inChain = false; + for (var i = 0; i < lines.length; i++) { + var good = false; + line = lines[i]; + inChain = inChain && line.length > 0; // c) any non-empty line continues the chain + if (/^>/.test(line)) { // a) + good = true; + if (!inChain && line.length > 1) // c) any line that starts with ">" and has at least one more character starts the chain + inChain = true; + } else if (/^[ \t]*$/.test(line)) { // b) + good = true; + } else { + good = inChain; // c) the line is not empty and does not start with ">", so it matches if and only if we're in the chain + } + if (good) { + match += line + "\n"; + } else { + leftOver += match + line; + match = "\n"; + } + } + if (!/(^|\n)>/.test(match)) { // d) + leftOver += match; + match = ""; + } + } + + chunk.startTag = match; + chunk.before = leftOver; + + // end of change + + if (chunk.after) { + chunk.after = chunk.after.replace(/^\n?/, "\n"); + } + + chunk.after = chunk.after.replace(/^(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*)/, + function (totalMatch) { + chunk.endTag = totalMatch; + return ""; + } + ); + + var replaceBlanksInTags = function (useBracket) { + + var replacement = useBracket ? "> " : ""; + + if (chunk.startTag) { + chunk.startTag = chunk.startTag.replace(/\n((>|\s)*)\n$/, + function (totalMatch, markdown) { + return "\n" + markdown.replace(/^[ ]{0,3}>?[ \t]*$/gm, replacement) + "\n"; + }); + } + if (chunk.endTag) { + chunk.endTag = chunk.endTag.replace(/^\n((>|\s)*)\n/, + function (totalMatch, markdown) { + return "\n" + markdown.replace(/^[ ]{0,3}>?[ \t]*$/gm, replacement) + "\n"; + }); + } + }; + + if (/^(?![ ]{0,3}>)/m.test(chunk.selection)) { + this.wrap(chunk, SETTINGS.lineLength - 2); + chunk.selection = chunk.selection.replace(/^/gm, "> "); + replaceBlanksInTags(true); + chunk.skipLines(); + } else { + chunk.selection = chunk.selection.replace(/^[ ]{0,3}> ?/gm, ""); + this.unwrap(chunk); + replaceBlanksInTags(false); + + if (!/^(\n|^)[ ]{0,3}>/.test(chunk.selection) && chunk.startTag) { + chunk.startTag = chunk.startTag.replace(/\n{0,2}$/, "\n\n"); + } + + if (!/(\n|^)[ ]{0,3}>.*$/.test(chunk.selection) && chunk.endTag) { + chunk.endTag = chunk.endTag.replace(/^\n{0,2}/, "\n\n"); + } + } + + chunk.selection = this.hooks.postBlockquoteCreation(chunk.selection); + + if (!/\n/.test(chunk.selection)) { + chunk.selection = chunk.selection.replace(/^(> *)/, + function (wholeMatch, blanks) { + chunk.startTag += blanks; + return ""; + }); + } +}; + +commandProto.doCode = function (chunk) { + + var hasTextBefore = /\S[ ]*$/.test(chunk.before); + var hasTextAfter = /^[ ]*\S/.test(chunk.after); + + // Use 'four space' markdown if the selection is on its own + // line or is multiline. + if ((!hasTextAfter && !hasTextBefore) || /\n/.test(chunk.selection)) { + + chunk.before = chunk.before.replace(/[ ]{4}$/, + function (totalMatch) { + chunk.selection = totalMatch + chunk.selection; + return ""; + }); + + var nLinesBack = 1; + var nLinesForward = 1; + + if (/(\n|^)(\t|[ ]{4,}).*\n$/.test(chunk.before)) { + nLinesBack = 0; + } + if (/^\n(\t|[ ]{4,})/.test(chunk.after)) { + nLinesForward = 0; + } + + chunk.skipLines(nLinesBack, nLinesForward); + + if (!chunk.selection) { + chunk.startTag = " "; + chunk.selection = this.getString("codeexample"); + } else { + if (/^[ ]{0,3}\S/m.test(chunk.selection)) { + if (/\n/.test(chunk.selection)) + chunk.selection = chunk.selection.replace(/^/gm, " "); + else // if it's not multiline, do not select the four added spaces; this is more consistent with the doList behavior + chunk.before += " "; + } else { + chunk.selection = chunk.selection.replace(/^(?:[ ]{4}|[ ]{0,3}\t)/gm, ""); + } + } + } else { + // Use backticks (`) to delimit the code block. + + chunk.trimWhitespace(); + chunk.findTags(/`/, /`/); + + if (!chunk.startTag && !chunk.endTag) { + chunk.startTag = chunk.endTag = "`"; + if (!chunk.selection) { + chunk.selection = this.getString("codeexample"); + } + } else if (chunk.endTag && !chunk.startTag) { + chunk.before += chunk.endTag; + chunk.endTag = ""; + } else { + chunk.startTag = chunk.endTag = ""; + } + } +}; + +commandProto.doList = function (chunk, postProcessing, isNumberedList) { + + // These are identical except at the very beginning and end. + // Should probably use the regex extension function to make this clearer. + var previousItemsRegex = /(\n|^)(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*$/; + var nextItemsRegex = /^\n*(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*/; + + // The default bullet is a dash but others are possible. + // This has nothing to do with the particular HTML bullet, + // it's just a markdown bullet. + var bullet = "-"; + + // The number in a numbered list. + var num = 1; + + // Get the item prefix - e.g. " 1. " for a numbered list, " - " for a bulleted list. + var getItemPrefix = function () { + var prefix; + if (isNumberedList) { + prefix = " " + num + ". "; + num++; + } else { + prefix = " " + bullet + " "; + } + return prefix; + }; + + // Fixes the prefixes of the other list items. + var getPrefixedItem = function (itemText) { + + // The numbering flag is unset when called by autoindent. + if (isNumberedList === undefined) { + isNumberedList = /^\s*\d/.test(itemText); + } + + // Renumber/bullet the list element. + itemText = itemText.replace(/^[ ]{0,3}([*+-]|\d+[.])\s/gm, + function () { + return getItemPrefix(); + }); + + return itemText; + }; + + chunk.findTags(/(\n|^)*[ ]{0,3}([*+-]|\d+[.])\s+/, null); + + if (chunk.before && !/\n$/.test(chunk.before) && !/^\n/.test(chunk.startTag)) { + chunk.before += chunk.startTag; + chunk.startTag = ""; + } + + if (chunk.startTag) { + + var hasDigits = /\d+[.]/.test(chunk.startTag); + chunk.startTag = ""; + chunk.selection = chunk.selection.replace(/\n[ ]{4}/g, "\n"); + this.unwrap(chunk); + chunk.skipLines(); + + if (hasDigits) { + // Have to renumber the bullet points if this is a numbered list. + chunk.after = chunk.after.replace(nextItemsRegex, getPrefixedItem); + } + if (isNumberedList == hasDigits) { + return; + } + } + + var nLinesUp = 1; + + chunk.before = chunk.before.replace(previousItemsRegex, + function (itemText) { + if (/^\s*([*+-])/.test(itemText)) { + bullet = re.$1; + } + nLinesUp = /[^\n]\n\n[^\n]/.test(itemText) ? 1 : 0; + return getPrefixedItem(itemText); + }); + + if (!chunk.selection) { + chunk.selection = this.getString("litem"); + } + + var prefix = getItemPrefix(); + + var nLinesDown = 1; + + chunk.after = chunk.after.replace(nextItemsRegex, + function (itemText) { + nLinesDown = /[^\n]\n\n[^\n]/.test(itemText) ? 1 : 0; + return getPrefixedItem(itemText); + }); + + chunk.trimWhitespace(true); + chunk.skipLines(nLinesUp, nLinesDown, true); + chunk.startTag = prefix; + var spaces = prefix.replace(/./g, " "); + this.wrap(chunk, SETTINGS.lineLength - spaces.length); + chunk.selection = chunk.selection.replace(/\n/g, "\n" + spaces); + +}; + +commandProto.doTable = function (chunk) { + // Credit: https://github.com/fcrespo82/atom-markdown-table-formatter + + var keepFirstAndLastPipes = true, + /* + ( # header capture + (?: + (?:[^\n]*?\|[^\n]*) # line w/ at least one pipe + \ * # maybe trailing whitespace + )? # maybe header + (?:\n|^) # newline + ) + ( # format capture + (?: + \|\ *:?-+:?\ * # format starting w/pipe + |\|?(?:\ *:?-+:?\ *\|)+ # or separated by pipe + ) + (?:\ *:?-+:?\ *)? # maybe w/o trailing pipe + \ * # maybe trailing whitespace + \n # newline + ) + ( # body capture + (?: + (?:[^\n]*?\|[^\n]*) # line w/ at least one pipe + \ * # maybe trailing whitespace + (?:\n|$) # newline + )+ # at least one + ) + */ + regex = /((?:(?:[^\n]*?\|[^\n]*) *)?(?:\r?\n|^))((?:\| *:?-+:? *|\|?(?: *:?-+:? *\|)+)(?: *:?-+:? *)? *\r?\n)((?:(?:[^\n]*?\|[^\n]*) *(?:\r?\n|$))+)/; + + + function padding(len, str) { + var result = ''; + str = str || ' '; + len = Math.floor(len); + for (var i = 0; i < len; i++) { + result += str; + } + return result; + } + + function stripTailPipes(str) { + return str.trim().replace(/(^\||\|$)/g, ""); + } + + function splitCells(str) { + return str.split('|'); + } + + function addTailPipes(str) { + if (keepFirstAndLastPipes) { + return "|" + str + "|"; + } else { + return str; + } + } + + function joinCells(arr) { + return arr.join('|'); + } + + function formatTable(text, appendNewline) { + var i, j, len1, ref1, ref2, ref3, k, len2, results, formatline, headerline, just, formatrow, data, line, lines, justify, cell, cells, first, last, ends, columns, content, widths, formatted, front, back; + formatline = text[2].trim(); + headerline = text[1].trim(); + ref1 = headerline.length === 0 ? [0, text[3]] : [1, text[1] + text[3]], formatrow = ref1[0], data = ref1[1]; + lines = data.trim().split('\n'); + justify = []; + ref2 = splitCells(stripTailPipes(formatline)); + for (j = 0, len1 = ref2.length; j < len1; j++) { + cell = ref2[j]; + ref3 = cell.trim(), first = ref3[0], last = ref3[ref3.length - 1]; + switch ((ends = (first ? first : ':') + (last ? last : ''))) { + case '::': + case '-:': + case ':-': + justify.push(ends); + break; + default: + justify.push('--'); + } + } + columns = justify.length; + content = []; + for (j = 0, len1 = lines.length; j < len1; j++) { + line = lines[j]; + cells = splitCells(stripTailPipes(line)); + cells[columns - 1] = joinCells(cells.slice(columns - 1)); + results = []; + for (k = 0, len2 = cells.length; k < len2; k++) { + cell = cells[k]; + results.push(padding(' ') + ((ref2 = cell ? typeof cell.trim === "function" ? cell.trim() : void 0 : void 0) ? ref2 : '') + padding(' ')); + } + content.push(results); + } + widths = []; + for (i = j = 0, ref2 = columns - 1; 0 <= ref2 ? j <= ref2 : j >= ref2; i = 0 <= ref2 ? ++j : --j) { + results = []; + for (k = 0, len1 = content.length; k < len1; k++) { + cells = content[k]; + results.push(cells[i].length); + } + widths.push(Math.max.apply(Math, [2].concat(results))); + } + just = function (string, col) { + var back, front, length; + length = widths[col] - string.length; + switch (justify[col]) { + case '::': + front = padding[0], back = padding[1]; + return padding(length / 2) + string + padding((length + 1) / 2); + case '-:': + return padding(length) + string; + default: + return string + padding(length); + } + }; + formatted = []; + for (j = 0, len1 = content.length; j < len1; j++) { + cells = content[j]; + results = []; + for (i = k = 0, ref2 = columns - 1; 0 <= ref2 ? k <= ref2 : k >= ref2; i = 0 <= ref2 ? ++k : --k) { + results.push(just(cells[i], i)); + } + formatted.push(addTailPipes(joinCells(results))); + } + formatline = addTailPipes(joinCells((function () { + var j, ref2, ref3, results; + results = []; + for (i = j = 0, ref2 = columns - 1; 0 <= ref2 ? j <= ref2 : j >= ref2; i = 0 <= ref2 ? ++j : --j) { + ref3 = justify[i], front = ref3[0], back = ref3[1]; + results.push(front + padding(widths[i] - 2, '-') + back); + } + return results; + })())); + formatted.splice(formatrow, 0, formatline); + var result = (headerline.length === 0 && text[1] !== '' ? '\n' : '') + formatted.join('\n'); + if (appendNewline !== false) { + result += '\n' + } + return result; + } + + if (chunk.before.slice(-1) !== '\n') { + chunk.before += '\n'; + } + var match = chunk.selection.match(regex); + if (match) { + chunk.selection = formatTable(match, chunk.selection.slice(-1) === '\n'); + } else { + var table = chunk.selection + '|\n-|-\n|'; + match = table.match(regex); + if (!match || match[0].slice(0, table.length) !== table) { + return; + } + table = formatTable(match); + var selectionOffset = keepFirstAndLastPipes ? 1 : 0; + var pipePos = table.indexOf('|', selectionOffset); + chunk.before += table.slice(0, selectionOffset); + chunk.selection = table.slice(selectionOffset, pipePos); + chunk.after = table.slice(pipePos) + chunk.after; + } +}; + +commandProto.doHeading = function (chunk) { + + // Remove leading/trailing whitespace and reduce internal spaces to single spaces. + chunk.selection = chunk.selection.replace(/\s+/g, " "); + chunk.selection = chunk.selection.replace(/(^\s+|\s+$)/g, ""); + + // If we clicked the button with no selected text, we just + // make a level 2 hash header around some default text. + if (!chunk.selection) { + chunk.startTag = "## "; + chunk.selection = this.getString("headingexample"); + return; + } + + var headerLevel = 0; // The existing header level of the selected text. + + // Remove any existing hash heading markdown and save the header level. + chunk.findTags(/#+[ ]*/, /[ ]*#+/); + if (/#+/.test(chunk.startTag)) { + headerLevel = re.lastMatch.length; + } + chunk.startTag = chunk.endTag = ""; + + // Try to get the current header level by looking for - and = in the line + // below the selection. + chunk.findTags(null, /\s?(-+|=+)/); + if (/=+/.test(chunk.endTag)) { + headerLevel = 1; + } + if (/-+/.test(chunk.endTag)) { + headerLevel = 2; + } + + // Skip to the next line so we can create the header markdown. + chunk.startTag = chunk.endTag = ""; + chunk.skipLines(1, 1); + + // We make a level 2 header if there is no current header. + // If there is a header level, we substract one from the header level. + // If it's already a level 1 header, it's removed. + var headerLevelToCreate = headerLevel === 0 ? 2 : headerLevel - 1; + + if (headerLevelToCreate > 0) { + + chunk.startTag = ''; + while (headerLevelToCreate--) { + chunk.startTag += '#'; + } + chunk.startTag += ' '; + } +}; + +commandProto.doHorizontalRule = function (chunk) { + chunk.startTag = "----------\n"; + chunk.selection = ""; + chunk.skipLines(2, 1, true); +}; + +export default function (options) { + return new Pagedown(options); +}; diff --git a/src/components/App.vue b/src/components/App.vue new file mode 100644 index 00000000..04508ff8 --- /dev/null +++ b/src/components/App.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/src/components/ButtonBar.vue b/src/components/ButtonBar.vue new file mode 100644 index 00000000..cfb6ef6a --- /dev/null +++ b/src/components/ButtonBar.vue @@ -0,0 +1,88 @@ + + + + + + diff --git a/src/components/Editor.vue b/src/components/Editor.vue new file mode 100644 index 00000000..a67ab249 --- /dev/null +++ b/src/components/Editor.vue @@ -0,0 +1,64 @@ + + + + + + diff --git a/src/components/Layout.vue b/src/components/Layout.vue new file mode 100644 index 00000000..a4dd32ed --- /dev/null +++ b/src/components/Layout.vue @@ -0,0 +1,165 @@ + + + + + diff --git a/src/components/NavigationBar.vue b/src/components/NavigationBar.vue new file mode 100644 index 00000000..fb1fa32a --- /dev/null +++ b/src/components/NavigationBar.vue @@ -0,0 +1,76 @@ + + + + + diff --git a/src/components/Preview.vue b/src/components/Preview.vue new file mode 100644 index 00000000..89b003c5 --- /dev/null +++ b/src/components/Preview.vue @@ -0,0 +1,48 @@ + + + + + + diff --git a/src/components/SideBar.vue b/src/components/SideBar.vue new file mode 100644 index 00000000..e343ccdf --- /dev/null +++ b/src/components/SideBar.vue @@ -0,0 +1,17 @@ + + + + diff --git a/src/components/StatusBar.vue b/src/components/StatusBar.vue new file mode 100644 index 00000000..2d6b481b --- /dev/null +++ b/src/components/StatusBar.vue @@ -0,0 +1,122 @@ + + + + + diff --git a/src/components/Toc.vue b/src/components/Toc.vue new file mode 100644 index 00000000..254d5b76 --- /dev/null +++ b/src/components/Toc.vue @@ -0,0 +1,6 @@ + diff --git a/src/components/common/base.scss b/src/components/common/base.scss new file mode 100644 index 00000000..8c2f50a9 --- /dev/null +++ b/src/components/common/base.scss @@ -0,0 +1,145 @@ +@import '../../node_modules/normalize-scss/sass/normalize'; + +@include normalize(); + +html, +body { + color: rgba(0, 0, 0, 0.75); + font-family: $font-family-editor; + font-variant-ligatures: common-ligatures; + line-height: 1.65; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +h1, +h2, +h3, +h4, +h5, +h6, +p, +blockquote, +pre, +ul, +ol, +dl { + margin: 1.2em 0; +} + +ol ul, +ul ol, +ul ul, +ol ol { + margin: 0; +} + +a { + color: $link-color; +} + +code, +kbd, +pre, +samp { + font-family: $font-family-monospace; + font-size: $font-size-monospace; + + * { + font-size: inherit; + } +} + +blockquote { + color: rgba(0, 0, 0, 0.4); + margin-left: 1em; + margin-right: 1em; +} + +code { + background-color: $code-bg; + border-radius: $border-radius-base; + padding: 2px 4px; +} + +hr { + border: 0; + border-top: 1px solid rgba(128, 128, 128, 0.15); + margin: 2em 0; +} + +pre > code { + background-color: $code-bg; + display: block; + overflow-x: auto; + padding: 0.5em; + -webkit-text-size-adjust: none; +} + +.toc ul { + list-style-type: none; + padding-left: 20px; +} + +table { + background-color: transparent; + border-collapse: collapse; + border-spacing: 0; +} + +td, +th { + border-right: 1px solid #e0e0e0; + padding: 8px 12px; + + &:last-child { + border-right: 0; + } +} + +td { + border-top: 1px solid #e0e0e0; +} + +kbd { + background-color: #fff; + border: 1px solid rgba(63, 63, 63, 0.25); + border-radius: 3px; + box-shadow: 0 1px 0 rgba(63, 63, 63, 0.25); + color: #333; + display: inline-block; + font-size: 0.7em; + margin: 0 0.1em; + padding: 0.1em 0.6em; + white-space: nowrap; +} + +abbr { + &[title] { + border-bottom: 1px dotted #777; + cursor: help; + } +} + +.footnote { + font-size: 0.8em; + position: relative; + top: -0.25em; + vertical-align: top; +} + +.export-container { + margin-bottom: 180px; + margin-left: auto; + margin-right: auto; + + > :not(h1):not(h2):not(h3):not(h4):not(h5):not(h6):not([align]) { + text-align: justify; + } +} + +@media (min-width: 768px) { + .export-container { + width: 750px; + } +} diff --git a/src/components/common/flex.scss b/src/components/common/flex.scss new file mode 100644 index 00000000..58ee9d08 --- /dev/null +++ b/src/components/common/flex.scss @@ -0,0 +1,19 @@ +.flex { + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + display: flex; +} + +.flex--row { + -webkit-box-orient: horizontal; + -webkit-flex-direction: row; + flex-direction: row; +} + +.flex--column { + -webkit-box-orient: vertical; + -webkit-flex-direction: column; + flex-direction: column; +} diff --git a/src/components/common/markdownHighlighting.scss b/src/components/common/markdownHighlighting.scss new file mode 100644 index 00000000..8c798b4c --- /dev/null +++ b/src/components/common/markdownHighlighting.scss @@ -0,0 +1,232 @@ +@import './variables'; + +.markdown-highlighting { + color: $editor-color; + font-family: inherit; + font-size: inherit; + -webkit-font-smoothing: auto; + -moz-osx-font-smoothing: auto; + font-weight: $editor-font-weight-base; + + .code { + font-family: $font-family-monospace; + font-size: $font-size-monospace; + + * { + font-size: inherit !important; + } + } + + .pre { + color: $editor-color; + font-family: $font-family-monospace; + font-size: $font-size-monospace; + word-break: break-all; + + [class*='language-'] { + color: $editor-color-dark; + } + + * { + font-size: inherit !important; + } + } + + .tag { + color: $editor-color; + font-family: $font-family-monospace; + font-size: $font-size-monospace; + font-weight: $editor-font-weight-bold; + + .punctuation, + .attr-value, + .attr-name { + font-weight: $editor-font-weight-base; + } + + * { + font-size: inherit !important; + } + } + + .latex, + .math { + color: $editor-color; + } + + .entity { + color: $editor-color; + font-family: $font-family-monospace; + font-size: $font-size-monospace; + font-style: italic; + + * { + font-size: inherit !important; + } + } + + .table { + font-family: $font-family-monospace; + font-size: $font-size-monospace; + + * { + font-size: inherit !important; + } + } + + .comment { + color: $editor-color-light; + } + + .keyword { + color: $editor-color-dark; + font-weight: $editor-font-weight-bold; + } + + .code, + .img, + .img-wrapper, + .imgref, + .cl-toc { + background-color: $code-bg; + border-radius: $code-border-radius; + padding: 0.15em 0; + } + + .img-wrapper { + display: inline-block; + + .img { + display: inline-block; + padding: 0; + } + + img { + max-width: 100%; + padding: 0 0.15em; + } + } + + .cl-toc { + font-size: 2.8em; + padding: 0.15em; + } + + .blockquote { + color: rgba(0, 0, 0, 0.48); + } + + .h1, + .h2, + .h3, + .h4, + .h5, + .h6 { + font-weight: $editor-font-weight-bold; + } + + .h1, + .h11 { + font-size: 2em; + } + + .h2, + .h22 { + font-size: 1.5em; + } + + .h3 { + font-size: 1.17em; + } + + .h4 { + font-size: 1em; + } + + .h5 { + font-size: 0.83em; + } + + .h6 { + font-size: 0.75em; + } + + .cl-hash { + color: $editor-color-light; + } + + .cl, + .hr { + color: $editor-color-light; + font-style: normal; + font-weight: $editor-font-weight-base; + } + + .em, + .em .cl { + font-style: italic; + } + + .strong, + .strong .cl, + .term { + font-weight: $editor-font-weight-bold; + } + + .cl-del-text { + text-decoration: line-through; + } + + .url, + .email, + .cl-underlined-text { + text-decoration: underline; + } + + .linkdef .url { + color: $editor-color-light; + } + + .fn, + .inlinefn, + .sup { + font-size: smaller; + position: relative; + top: -0.5em; + } + + .sub { + bottom: -0.25em; + font-size: smaller; + position: relative; + } + + .img, + .imgref, + .link, + .linkref { + color: $editor-color-light; + + .cl-underlined-text { + color: $editor-color-dark; + } + } + + .cl-title { + color: $editor-color; + } +} + +.markdown-highlighting--inline { + .h1, + .h11, + .h2, + .h22, + .h3, + .h4, + .h5, + .h6, + .cl-toc { + font-size: inherit; + } +} diff --git a/src/components/common/prism.scss b/src/components/common/prism.scss new file mode 100644 index 00000000..e9332d1a --- /dev/null +++ b/src/components/common/prism.scss @@ -0,0 +1,73 @@ +.token.pre.gfm, +.prism { + * { + font-weight: inherit !important; + } + + .token.comment, + .token.prolog, + .token.doctype, + .token.cdata { + color: #708090; + } + + .token.punctuation { + color: #999; + } + + .namespace { + opacity: 0.7; + } + + .token.property, + .token.tag, + .token.boolean, + .token.number, + .token.constant, + .token.symbol, + .token.deleted { + color: #905; + } + + .token.selector, + .token.attr-name, + .token.string, + .token.char, + .token.builtin, + .token.inserted { + color: #690; + } + + .token.operator, + .token.entity, + .token.url, + .language-css .token.string, + .style .token.string { + color: #a67f59; + } + + .token.atrule, + .token.attr-value, + .token.keyword { + color: #07a; + } + + .token.function { + color: #dd4a68; + } + + .token.regex, + .token.important, + .token.variable { + color: #e90; + } + + .token.important, + .token.bold { + font-weight: 500; + } + + .token.italic { + font-style: italic; + } +} diff --git a/src/components/common/variables.scss b/src/components/common/variables.scss new file mode 100644 index 00000000..217e8351 --- /dev/null +++ b/src/components/common/variables.scss @@ -0,0 +1,13 @@ +$font-family-editor: "Helvetica Neue", Helvetica, sans-serif; +$font-family-monospace: "Lucida Sans Typewriter", "Lucida Console", monaco, Courrier, monospace; +$font-size-monospace: 0.9em; +$code-bg: rgba(0, 0, 0, 0.05); +$code-border-radius: 3px; +$link-color: #0366d6; +$border-radius-base: 3px; + +$editor-color: rgba(0, 0, 0, 0.8); +$editor-color-light: rgba(0, 0, 0, 0.28); +$editor-color-dark: #000; +$editor-font-weight-base: 400; +$editor-font-weight-bold: 600; diff --git a/src/extensions/index.js b/src/extensions/index.js new file mode 100644 index 00000000..56fedeea --- /dev/null +++ b/src/extensions/index.js @@ -0,0 +1 @@ +import './markdownExt'; diff --git a/src/extensions/markdownExt.js b/src/extensions/markdownExt.js new file mode 100644 index 00000000..2b5060a8 --- /dev/null +++ b/src/extensions/markdownExt.js @@ -0,0 +1,197 @@ +import Prism from 'prismjs'; +import markdownitAbbr from 'markdown-it-abbr'; +import markdownitDeflist from 'markdown-it-deflist'; +import markdownitFootnote from 'markdown-it-footnote'; +import markdownitSub from 'markdown-it-sub'; +import markdownitSup from 'markdown-it-sup'; +import extensionSvc from '../services/extensionSvc'; + +const coreBaseRules = [ + 'normalize', + 'block', + 'inline', + 'linkify', + 'replacements', + 'smartquotes', +]; +const blockBaseRules = [ + 'code', + 'fence', + 'blockquote', + 'hr', + 'list', + 'reference', + 'heading', + 'lheading', + 'html_block', + 'table', + 'paragraph', +]; +const inlineBaseRules = [ + 'text', + 'newline', + 'escape', + 'backticks', + 'strikethrough', + 'emphasis', + 'link', + 'image', + 'autolink', + 'html_inline', + 'entity', +]; +const inlineBaseRules2 = [ + 'balance_pairs', + 'strikethrough', + 'emphasis', + 'text_collapse', +]; + +extensionSvc.onGetOptions((options, properties) => { + options.abbr = properties['ext:markdown:abbr'] !== 'false'; + options.breaks = properties['ext:markdown:breaks'] === 'true'; + options.deflist = properties['ext:markdown:deflist'] !== 'false'; + options.del = properties['ext:markdown:del'] !== 'false'; + options.fence = properties['ext:markdown:fence'] !== 'false'; + options.footnote = properties['ext:markdown:footnote'] !== 'false'; + options.linkify = properties['ext:markdown:linkify'] !== 'false'; + options.sub = properties['ext:markdown:sub'] !== 'false'; + options.sup = properties['ext:markdown:sup'] !== 'false'; + options.table = properties['ext:markdown:table'] !== 'false'; + options.typographer = properties['ext:markdown:typographer'] !== 'false'; +}); + +extensionSvc.onInitConverter(0, (markdown, options) => { + markdown.set({ + html: true, + breaks: !!options.breaks, + linkify: !!options.linkify, + typographer: !!options.typographer, + langPrefix: 'prism language-', + }); + + markdown.core.ruler.enable(coreBaseRules); + + const blockRules = blockBaseRules.slice(); + if (!options.fence) { + blockRules.splice(blockRules.indexOf('fence'), 1); + } + if (!options.table) { + blockRules.splice(blockRules.indexOf('table'), 1); + } + markdown.block.ruler.enable(blockRules); + + const inlineRules = inlineBaseRules.slice(); + const inlineRules2 = inlineBaseRules2.slice(); + if (!options.del) { + inlineRules.splice(blockRules.indexOf('strikethrough'), 1); + inlineRules2.splice(blockRules.indexOf('strikethrough'), 1); + } + markdown.inline.ruler.enable(inlineRules); + markdown.inline.ruler2.enable(inlineRules2); + + if (options.abbr) { + markdown.use(markdownitAbbr); + } + if (options.deflist) { + markdown.use(markdownitDeflist); + } + if (options.footnote) { + markdown.use(markdownitFootnote); + } + if (options.sub) { + markdown.use(markdownitSub); + } + if (options.sup) { + markdown.use(markdownitSup); + } + + markdown.core.ruler.before('replacements', 'anchors', (state) => { + const anchorHash = {}; + let headingOpenToken; + let headingContent; + state.tokens.cl_each((token) => { + if (token.type === 'heading_open') { + headingContent = ''; + headingOpenToken = token; + } else if (token.type === 'heading_close') { + headingOpenToken.headingContent = headingContent; + + // Slugify according to http://pandoc.org/README.html#extension-auto_identifiers + let slug = headingContent + .replace(/\s/g, '-') // Replace all spaces and newlines with hyphens + .replace(/[\0-,/:-@[-^`{-~]/g, '') // Remove all punctuation, except underscores, hyphens, and periods + .toLowerCase(); // Convert all alphabetic characters to lowercase + + // Remove everything up to the first letter + let i; + for (i = 0; i < slug.length; i += 1) { + const charCode = slug.charCodeAt(i); + if ((charCode >= 0x61 && charCode <= 0x7A) || charCode > 0x7E) { + break; + } + } + + // If nothing is left after this, use the identifier `section` + slug = slug.slice(i) || 'section'; + + let anchor = slug; + let index = 1; + while (Object.prototype.hasOwnProperty.call(anchorHash, anchor)) { + anchor = `${slug}-${index}`; + index += 1; + } + anchorHash[anchor] = true; + headingOpenToken.headingAnchor = anchor; + headingOpenToken.attrs = [ + ['id', anchor], + ]; + headingOpenToken = undefined; + } else if (headingOpenToken) { + headingContent += token.children.cl_reduce((result, child) => { + if (child.type !== 'footnote_ref') { + return result + child.content; + } + return result; + }, ''); + } + }); + }); + + // Wrap tables into a div for scrolling + markdown.renderer.rules.table_open = (tokens, idx, opts) => + `
      ${markdown.renderer.renderToken(tokens, idx, opts)}`; + markdown.renderer.rules.table_close = (tokens, idx, opts) => + `${markdown.renderer.renderToken(tokens, idx, opts)}
      `; + + // Transform style into align attribute to pass the HTML sanitizer + const textAlignLength = 'text-align:'.length; + markdown.renderer.rules.td_open = (tokens, idx, opts) => { + const token = tokens[idx]; + if (token.attrs && token.attrs.length && token.attrs[0][0] === 'style') { + token.attrs = [ + ['align', token.attrs[0][1].slice(textAlignLength)], + ]; + } + return markdown.renderer.renderToken(tokens, idx, opts); + }; + markdown.renderer.rules.th_open = markdown.renderer.rules.td_open; + + markdown.renderer.rules.footnote_ref = (tokens, idx) => { + const n = Number(tokens[idx].meta.id + 1).toString(); + let id = `fnref${n}`; + if (tokens[idx].meta.subId > 0) { + id += `:${tokens[idx].meta.subId}`; + } + return `${n}`; + }; +}); + +extensionSvc.onSectionPreview((elt) => { + elt.querySelectorAll('.prism').cl_each((prismElt) => { + if (!prismElt.highlighted) { + Prism.highlightElement(prismElt); + } + prismElt.highlighted = true; + }); +}); diff --git a/src/icons/CodeBraces.vue b/src/icons/CodeBraces.vue new file mode 100644 index 00000000..90423321 --- /dev/null +++ b/src/icons/CodeBraces.vue @@ -0,0 +1,5 @@ + diff --git a/src/icons/Eye.vue b/src/icons/Eye.vue new file mode 100644 index 00000000..2c543fef --- /dev/null +++ b/src/icons/Eye.vue @@ -0,0 +1,5 @@ + diff --git a/src/icons/FileImage.vue b/src/icons/FileImage.vue new file mode 100644 index 00000000..dd3fa0a0 --- /dev/null +++ b/src/icons/FileImage.vue @@ -0,0 +1,5 @@ + diff --git a/src/icons/FormatBold.vue b/src/icons/FormatBold.vue new file mode 100644 index 00000000..8cd78707 --- /dev/null +++ b/src/icons/FormatBold.vue @@ -0,0 +1,5 @@ + diff --git a/src/icons/FormatHorizontalRule.vue b/src/icons/FormatHorizontalRule.vue new file mode 100644 index 00000000..a0437d6e --- /dev/null +++ b/src/icons/FormatHorizontalRule.vue @@ -0,0 +1,5 @@ + diff --git a/src/icons/FormatItalic.vue b/src/icons/FormatItalic.vue new file mode 100644 index 00000000..de698f89 --- /dev/null +++ b/src/icons/FormatItalic.vue @@ -0,0 +1,5 @@ + diff --git a/src/icons/FormatListBulleted.vue b/src/icons/FormatListBulleted.vue new file mode 100644 index 00000000..dbdb0a01 --- /dev/null +++ b/src/icons/FormatListBulleted.vue @@ -0,0 +1,5 @@ + diff --git a/src/icons/FormatListNumbers.vue b/src/icons/FormatListNumbers.vue new file mode 100644 index 00000000..6bd088ba --- /dev/null +++ b/src/icons/FormatListNumbers.vue @@ -0,0 +1,5 @@ + diff --git a/src/icons/FormatQuoteClose.vue b/src/icons/FormatQuoteClose.vue new file mode 100644 index 00000000..3001e74f --- /dev/null +++ b/src/icons/FormatQuoteClose.vue @@ -0,0 +1,5 @@ + diff --git a/src/icons/FormatSize.vue b/src/icons/FormatSize.vue new file mode 100644 index 00000000..77e03dd8 --- /dev/null +++ b/src/icons/FormatSize.vue @@ -0,0 +1,5 @@ + diff --git a/src/icons/FormatStrikethrough.vue b/src/icons/FormatStrikethrough.vue new file mode 100644 index 00000000..50d794a3 --- /dev/null +++ b/src/icons/FormatStrikethrough.vue @@ -0,0 +1,5 @@ + diff --git a/src/icons/LinkVariant.vue b/src/icons/LinkVariant.vue new file mode 100644 index 00000000..090cff99 --- /dev/null +++ b/src/icons/LinkVariant.vue @@ -0,0 +1,5 @@ + diff --git a/src/icons/NavigationBar.vue b/src/icons/NavigationBar.vue new file mode 100644 index 00000000..d4b83ddc --- /dev/null +++ b/src/icons/NavigationBar.vue @@ -0,0 +1,6 @@ + diff --git a/src/icons/SidePreview.vue b/src/icons/SidePreview.vue new file mode 100644 index 00000000..4ad35827 --- /dev/null +++ b/src/icons/SidePreview.vue @@ -0,0 +1,5 @@ + diff --git a/src/icons/StatusBar.vue b/src/icons/StatusBar.vue new file mode 100644 index 00000000..68299652 --- /dev/null +++ b/src/icons/StatusBar.vue @@ -0,0 +1,6 @@ + diff --git a/src/icons/Table.vue b/src/icons/Table.vue new file mode 100644 index 00000000..ed408a9b --- /dev/null +++ b/src/icons/Table.vue @@ -0,0 +1,5 @@ + diff --git a/src/icons/index.js b/src/icons/index.js new file mode 100644 index 00000000..f25701f7 --- /dev/null +++ b/src/icons/index.js @@ -0,0 +1,34 @@ +import Vue from 'vue'; +import FormatBold from './FormatBold'; +import FormatItalic from './FormatItalic'; +import FormatQuoteClose from './FormatQuoteClose'; +import CodeBraces from './CodeBraces'; +import LinkVariant from './LinkVariant'; +import FileImage from './FileImage'; +import Table from './Table'; +import FormatListNumbers from './FormatListNumbers'; +import FormatListBulleted from './FormatListBulleted'; +import FormatSize from './FormatSize'; +import FormatHorizontalRule from './FormatHorizontalRule'; +import FormatStrikethrough from './FormatStrikethrough'; +import StatusBar from './StatusBar'; +import NavigationBar from './NavigationBar'; +import SidePreview from './SidePreview'; +import Eye from './Eye'; + +Vue.component('iconFormatBold', FormatBold); +Vue.component('iconFormatItalic', FormatItalic); +Vue.component('iconFormatQuoteClose', FormatQuoteClose); +Vue.component('iconCodeBraces', CodeBraces); +Vue.component('iconLinkVariant', LinkVariant); +Vue.component('iconFileImage', FileImage); +Vue.component('iconTable', Table); +Vue.component('iconFormatListNumbers', FormatListNumbers); +Vue.component('iconFormatListBulleted', FormatListBulleted); +Vue.component('iconFormatSize', FormatSize); +Vue.component('iconFormatHorizontalRule', FormatHorizontalRule); +Vue.component('iconFormatStrikethrough', FormatStrikethrough); +Vue.component('iconStatusBar', StatusBar); +Vue.component('iconNavigationBar', NavigationBar); +Vue.component('iconSidePreview', SidePreview); +Vue.component('iconEye', Eye); diff --git a/src/index.js b/src/index.js new file mode 100644 index 00000000..e8a832e7 --- /dev/null +++ b/src/index.js @@ -0,0 +1,15 @@ +import Vue from 'vue'; +import App from './components/App'; +import store from './store'; +import './extensions/'; +import './services/optional'; +import './icons/'; + +Vue.config.productionTip = false; + +/* eslint-disable no-new */ +new Vue({ + el: '#app', + store, + render: h => h(App), +}); diff --git a/src/markdown/sample.md b/src/markdown/sample.md new file mode 100644 index 00000000..78dcc5aa --- /dev/null +++ b/src/markdown/sample.md @@ -0,0 +1,117 @@ +# Basic writing + +## Styling text + +Make text **bold** or *italic* by using either `*` or `_` around the text. + +_This text will be italic_ +__This text will be bold__ + +Create strikethrough text by using `~~`. + +~~Mistaken text.~~ + +## Blockquotes + +Indicate blockquotes with a `>`. + +> Blockquote + +## Headings + +Create a heading by adding one or more `#` symbols before your heading text. + +##### Heading level 5 +###### Heading level 6 + +## Horizontal rules + +Insert a horizontal rule by putting three or more `-`, `*`, or `_` on a line by themselves. + +---------- + +## Table of contents + +Insert a table of contents using the marker `[TOC]`. + +[TOC] + + +# Lists + +## Unordered lists + +Make an unordered list by preceding list items with either a `*` or a `-`. + +- Item +- Item +* Item + +## Ordered lists + +Make an ordered list by preceding list items with a number. + +1. Item 1 +2. Item 2 +3. Item 3 + + +# Code formatting + +## Inline formats + +Use single backticks to format text in a special `monospace format`. + +## Multiple lines + +Indent four spaces or a tab to format text as its own distinct block. + + var foo = 'bar'; // baz + +## Code highlighting + +Use triple backticks including the language identifier to have syntax highlighting. + +```js +var foo = 'bar'; // baz +``` + + +# Links and images + +## Links + +Create a link by wrapping link text in brackets, and then wrapping the link in parentheses. + +[Visit Classeur](http://classeur.io) + +## Images + +Images are like links, but have an exclamation point in front of them. + +![Classeur Logo](http://classeur.io/images/logo.png) + +## Footnotes + +To create footnotes, add a label starting with a `^` between a set of square brackets like this[^footnote], and then, declare the linked content. + + [^footnote]: Here is the content of the footnote. + + +# Tables + +Create tables by assembling a list of words and dividing them with hyphens (for the first row), and then separating each column with a pipe. + +First Header | Second Header +------------- | ------------- +Content Cell | Content Cell +Content Cell | Content Cell + +By including colons within the header row, you can define text to be left-aligned, right-aligned, or center-aligned. + +| Left-Aligned | Center Aligned | Right Aligned | +| :------------ |:---------------:| -----:| +| col 3 is | some wordy text | $1600 | +| col 2 is | centered | $12 | +| zebra stripes | are neat | $1 | + diff --git a/src/services/animationSvc.js b/src/services/animationSvc.js new file mode 100644 index 00000000..3ec7fe0c --- /dev/null +++ b/src/services/animationSvc.js @@ -0,0 +1,228 @@ +import bezierEasing from 'bezier-easing'; + +const easings = { + materialIn: bezierEasing(0.75, 0, 0.8, 0.25), + materialOut: bezierEasing(0.25, 0.8, 0.25, 1.0), +}; + +const vendors = ['moz', 'webkit']; +for (let x = 0; x < vendors.length && !window.requestAnimationFrame; x += 1) { + window.requestAnimationFrame = window[`${vendors[x]}RequestAnimationFrame`]; + window.cancelAnimationFrame = window[`${vendors[x]}CancelAnimationFrame`] || + window[`${vendors[x]}CancelRequestAnimationFrame`]; +} + +const transformStyles = [ + 'WebkitTransform', + 'MozTransform', + 'msTransform', + 'OTransform', + 'transform', +]; + +const transitionEndEvents = { + WebkitTransition: 'webkitTransitionEnd', + MozTransition: 'transitionend', + msTransition: 'MSTransitionEnd', + OTransition: 'oTransitionEnd', + transition: 'transitionend', +}; + +function getStyle(styles) { + const elt = document.createElement('div'); + return styles.reduce((result, style) => { + if (elt.style[style] === undefined) { + return undefined; + } + return style; + }, undefined); +} + +const transformStyle = getStyle(transformStyles); +const transitionStyle = getStyle(Object.keys(transitionEndEvents)); +const transitionEndEvent = transitionEndEvents[transitionStyle]; + +function identity(x) { + return x; +} + +function ElementAttribute(name) { + this.name = name; + this.setStart = (animation) => { + const value = animation.elt[name]; + animation.$start[name] = value; + return value !== undefined && animation.$end[name] !== undefined; + }; + this.applyCurrent = (animation) => { + animation.elt[name] = animation.$current[name]; + }; +} + +function StyleAttribute(name, unit, defaultValue, wrap = identity) { + this.name = name; + this.setStart = (animation) => { + let value = parseFloat(animation.elt.style[name]); + if (isNaN(value)) { + value = animation.$current[name] || defaultValue; + } + animation.$start[name] = value; + return animation.$end[name] !== undefined; + }; + this.applyCurrent = (animation) => { + animation.elt.style[name] = wrap(animation.$current[name]) + unit; + }; +} + +function TransformAttribute(name, unit, defaultValue, wrap = identity) { + this.name = name; + this.setStart = (animation) => { + let value = animation.$current[name]; + if (value === undefined) { + value = defaultValue; + } + animation.$start[name] = value; + if (animation.$end[name] === undefined) { + animation.$end[name] = value; + } + return value !== undefined; + }; + this.applyCurrent = (animation) => { + const value = animation.$current[name]; + return value !== defaultValue && `${name}(${wrap(value)}${unit})`; + }; +} + +const attributes = [ + new ElementAttribute('scrollTop'), + new ElementAttribute('scrollLeft'), + new StyleAttribute('opacity', '', 1), + new StyleAttribute('zIndex', '', 0), + new TransformAttribute('translateX', 'px', 0, Math.round), + new TransformAttribute('translateY', 'px', 0, Math.round), + new TransformAttribute('scale', '', 1), + new TransformAttribute('rotate', 'deg', 0), +].concat([ + 'width', + 'height', + 'top', + 'right', + 'bottom', + 'left', +].map(name => new StyleAttribute(name, 'px', 0, Math.round))); + +class Animation { + constructor(elt) { + this.elt = elt; + this.$current = {}; + this.$pending = {}; + } + + start(param1, param2, param3) { + let endCb = param1; + let stepCb = param2; + let useTransition = false; + if (typeof param1 === 'boolean') { + useTransition = param1; + endCb = param2; + stepCb = param3; + } + + this.stop(); + this.$start = {}; + this.$end = this.$pending; + this.$pending = {}; + this.$attributes = attributes.filter(attribute => attribute.setStart(this)); + this.$end.duration = this.$end.duration || 0; + this.$end.delay = this.$end.delay || 0; + this.$end.easing = easings[this.$end.easing] || easings.materialOut; + this.$end.endCb = typeof endCb === 'function' && endCb; + this.$end.stepCb = typeof stepCb === 'function' && stepCb; + this.$startTime = Date.now() + this.$end.delay; + this.loop(this.$end.duration && useTransition); + return this.elt; + } + + stop() { + window.cancelAnimationFrame(this.$requestId); + } + + loop(useTransition) { + const onTransitionEnd = (evt) => { + if (evt.target === this.elt) { + this.elt.removeEventListener(transitionEndEvent, onTransitionEnd); + const endCb = this.$end.endCb; + this.$end.endCb = undefined; + if (endCb) { + endCb(); + } + } + }; + + let progress = (Date.now() - this.$startTime) / this.$end.duration; + let transition = ''; + if (useTransition) { + progress = 1; + const transitions = [ + 'all', + `${this.$end.duration}ms`, + this.$end.easing.toCSS(), + ]; + if (this.$end.delay) { + transitions.push(`${this.$end.duration}ms`); + } + transition = transitions.join(' '); + if (this.$end.endCb) { + this.elt.addEventListener(transitionEndEvent, onTransitionEnd); + } + } else if (progress < 1) { + this.$requestId = window.requestAnimationFrame(() => this.loop(false)); + if (progress < 0) { + return; + } + } else if (this.$end.endCb) { + this.$requestId = window.requestAnimationFrame(this.$end.endCb); + } + + const coeff = this.$end.easing.get(progress); + const transforms = this.$attributes.reduce((result, attribute) => { + if (progress < 1) { + const diff = this.$end[attribute.name] - this.$start[attribute.name]; + this.$current[attribute.name] = this.$start[attribute.name] + (diff * coeff); + } else { + this.$current[attribute.name] = this.$end[attribute.name]; + } + const transform = attribute.applyCurrent(this); + if (transform) { + result.push(transform); + } + return result; + }, []); + + if (transforms.length) { + transforms.push('translateZ(0)'); // activate GPU + } + const transform = transforms.join(' '); + this.elt.style[transformStyle] = transform; + this.elt.style[transitionStyle] = transition; + if (this.$end.stepCb) { + this.$end.stepCb(); + } + } +} + +attributes.map(attribute => attribute.name).concat('duration', 'easing', 'delay') + .forEach((name) => { + Animation.prototype[name] = function setter(val) { + this.$pending[name] = val; + return this; + }; + }); + +function animate(elt) { + if (!elt.$animation) { + elt.$animation = new Animation(elt); + } + return elt.$animation; +} + +export default { animate }; diff --git a/src/services/constants.js b/src/services/constants.js new file mode 100644 index 00000000..a769bf60 --- /dev/null +++ b/src/services/constants.js @@ -0,0 +1,3 @@ +export default { + scrollOffset: 20, +}; diff --git a/src/services/editorEngineSvc.js b/src/services/editorEngineSvc.js new file mode 100644 index 00000000..1b1fa233 --- /dev/null +++ b/src/services/editorEngineSvc.js @@ -0,0 +1,158 @@ +import DiffMatchPatch from 'diff-match-patch'; +import cledit from '../cledit/cledit'; +import clDiffUtils from '../cledit/cldiffutils'; +import store from '../store'; + +let clEditor; +const newDiscussionMarker0 = new cledit.Marker(0); +const newDiscussionMarker1 = new cledit.Marker(0, true); +let markerKeys; +let markerIdxMap; +let previousPatchableText; +let currentPatchableText; +let discussionMarkers; +let content; +let isChangePatch; + +function getDiscussionMarkers(discussion, discussionId, onMarker) { + function getMarker(offsetName) { + const markerOffset = discussion[offsetName]; + const markerKey = discussionId + offsetName; + let marker = discussionMarkers[markerKey]; + if (markerOffset !== undefined) { + if (!marker) { + marker = new cledit.Marker(markerOffset, offsetName === 'offset1'); + marker.discussionId = discussionId; + marker.offsetName = offsetName; + clEditor.addMarker(marker); + discussionMarkers[markerKey] = marker; + } + onMarker(marker); + } + } + getMarker('offset0'); + getMarker('offset1'); +} + +function syncDiscussionMarkers() { + Object.keys(discussionMarkers) + .forEach((markerKey) => { + const marker = discussionMarkers[markerKey]; + // Remove marker if discussion was removed + const discussion = content.discussions[marker.discussionId]; + if (!discussion || discussion[marker.offsetName] === undefined) { + clEditor.removeMarker(marker); + delete discussionMarkers[markerKey]; + } + }); + + Object.keys(content.discussions) + .forEach((discussionId) => { + const discussion = content.discussions[discussionId]; + getDiscussionMarkers(discussion, discussionId, (marker) => { + discussion[marker.offsetName] = marker.offset; + }); + }); +} + +const diffMatchPatch = new DiffMatchPatch(); + +function makePatches() { + const diffs = diffMatchPatch.diff_main(previousPatchableText, currentPatchableText); + return diffMatchPatch.patch_make(previousPatchableText, diffs); +} + +function applyPatches(patches) { + const newPatchableText = diffMatchPatch.patch_apply(patches, currentPatchableText)[0]; + let result = newPatchableText; + if (markerKeys.length) { + // Strip text markers + result = result.replace(new RegExp(`[\ue000-${String.fromCharCode((0xe000 + markerKeys.length) - 1)}]`, 'g'), ''); + } + // Expect a `contentChanged` event + if (result !== clEditor.getContent()) { + previousPatchableText = currentPatchableText; + currentPatchableText = newPatchableText; + isChangePatch = true; + } + return result; +} + +function reversePatches(patches) { + const result = diffMatchPatch.patch_deepCopy(patches).reverse(); + result.forEach((patch) => { + patch.diffs.forEach((diff) => { + diff[0] = -diff[0]; + }); + }); + return result; +} + +export default { + clEditor: null, + lastChange: 0, + lastExternalChange: 0, + createClEditor(editorElt) { + this.clEditor = cledit(editorElt, editorElt.parentNode); + clEditor = this.clEditor; + markerKeys = []; + markerIdxMap = Object.create(null); + discussionMarkers = {}; + clEditor.on('contentChanged', (text) => { + store.commit('files/setCurrentFileContentText', text); + syncDiscussionMarkers(); + if (!isChangePatch) { + previousPatchableText = currentPatchableText; + currentPatchableText = clDiffUtils.makePatchableText(content, markerKeys, markerIdxMap); + } else { + // Take a chance to restore discussion offsets on undo/redo + content.text = currentPatchableText; + clDiffUtils.restoreDiscussionOffsets(content, markerKeys); + content.discussions.cl_each((discussion, discussionId) => { + getDiscussionMarkers(discussion, discussionId, (marker) => { + marker.offset = discussion[marker.offsetName]; + }); + }); + } + isChangePatch = false; + this.lastChange = Date.now(); + }); + clEditor.addMarker(newDiscussionMarker0); + clEditor.addMarker(newDiscussionMarker1); + }, + initClEditor(opts, reinit) { + if (store.state.files.currentFile.content) { + const options = Object.assign({}, opts); + + if (content !== store.state.files.currentFile.content) { + content = store.state.files.currentFile.content; + currentPatchableText = clDiffUtils.makePatchableText(content, markerKeys, markerIdxMap); + previousPatchableText = currentPatchableText; + syncDiscussionMarkers(); + } + + if (reinit) { + options.content = content.text; + options.selectionStart = content.state.selectionStart; + options.selectionEnd = content.state.selectionEnd; + } + + options.patchHandler = { + makePatches, + applyPatches: patches => applyPatches(patches), + reversePatches, + }; + clEditor.init(options); + } + }, + applyContent(isExternal) { + if (!clEditor) { + return null; + } + if (isExternal) { + this.lastExternalChange = Date.now(); + } + syncDiscussionMarkers(); + return clEditor.setContent(content.text, isExternal); + }, +}; diff --git a/src/services/editorSvc.js b/src/services/editorSvc.js new file mode 100644 index 00000000..6b652a28 --- /dev/null +++ b/src/services/editorSvc.js @@ -0,0 +1,734 @@ +import Vue from 'vue'; +import DiffMatchPatch from 'diff-match-patch'; +import Prism from 'prismjs'; +import markdownItPandocRenderer from 'markdown-it-pandoc-renderer'; +import cledit from '../cledit/cledit'; +import pagedown from '../cledit/pagedown'; +import htmlSanitizer from '../cledit/htmlSanitizer'; +import markdownConversionSvc from './markdownConversionSvc'; +import markdownGrammarSvc from './markdownGrammarSvc'; +import sectionUtils from './sectionUtils'; +import extensionSvc from './extensionSvc'; +import constants from './constants'; +import animationSvc from './animationSvc'; +import editorEngineSvc from './editorEngineSvc'; +import store from '../store'; + +const debounce = cledit.Utils.debounce; + +const allowDebounce = (action, wait) => { + let timeoutId; + return (doDebounce = false) => { + clearTimeout(timeoutId); + if (doDebounce) { + timeoutId = setTimeout(() => action(), wait); + } else { + action(); + } + }; +}; + +const diffMatchPatch = new DiffMatchPatch(); +let reinitClEditor = true; +let isPreviewRefreshed = false; +let tokens; +const anchorHash = {}; + +const editorSvc = Object.assign(new Vue(), { // Use a vue instance as an event bus + // Elements + editorElt: null, + previewElt: null, + tocElt: null, + // Other objects + pagedownEditor: null, + options: {}, + prismGrammars: {}, + converter: null, + parsingCtx: null, + conversionCtx: null, + sectionList: null, + sectionDescList: [], + sectionDescMeasuredList: null, + sectionDescWithDiffsList: null, + selectionRange: null, + previewSelectionRange: null, + previewSelectionStartOffset: null, + previewHtml: null, + previewText: null, + + /** + * Get element and dimension that handles scrolling. + */ + getObjectToScroll() { + let elt = this.editorElt.parentNode; + let dimensionKey = 'editorDimension'; + if (!store.state.layout.showEditor) { + elt = this.previewElt.parentNode; + dimensionKey = 'previewDimension'; + } + return { + elt, + dimensionKey, + }; + }, + + /** + * Get an object describing the position of the scroll bar in the file. + */ + getScrollPosition() { + const objToScroll = this.getObjectToScroll(); + const scrollTop = objToScroll.elt.scrollTop; + let result; + if (this.sectionDescMeasuredList) { + this.sectionDescMeasuredList.some((sectionDesc, sectionIdx) => { + if (scrollTop >= sectionDesc[objToScroll.dimensionKey].endOffset) { + return false; + } + const posInSection = (scrollTop - sectionDesc[objToScroll.dimensionKey].startOffset) / + (sectionDesc[objToScroll.dimensionKey].height || 1); + result = { + sectionIdx, + posInSection, + }; + return true; + }); + } + return result; + }, + + /** + * Get the offset in the preview corresponding to the offset of the markdown in the editor + */ + getPreviewOffset(editorOffset) { + let previewOffset = 0; + let offset = editorOffset; + this.sectionDescList.some((sectionDesc) => { + if (!sectionDesc.textToPreviewDiffs) { + previewOffset = undefined; + return true; + } + if (sectionDesc.section.text.length >= offset) { + previewOffset += diffMatchPatch.diff_xIndex(sectionDesc.textToPreviewDiffs, offset); + return true; + } + offset -= sectionDesc.section.text.length; + previewOffset += sectionDesc.previewText.length; + return false; + }); + return previewOffset; + }, + + /** + * Get the offset of the markdown in the editor corresponding to the offset in the preview + */ + getEditorOffset(previewOffset) { + let offset = previewOffset; + let editorOffset = 0; + this.sectionDescList.some((sectionDesc) => { + if (!sectionDesc.textToPreviewDiffs) { + editorOffset = undefined; + return true; + } + if (sectionDesc.previewText.length >= offset) { + const previewToTextDiffs = sectionDesc.textToPreviewDiffs + .map(diff => [-diff[0], diff[1]]); + editorOffset += diffMatchPatch.diff_xIndex(previewToTextDiffs, offset); + return true; + } + offset -= sectionDesc.previewText.length; + editorOffset += sectionDesc.section.text.length; + return false; + }); + return editorOffset; + }, + + /** + * Returns the pandoc AST generated from the file tokens and the converter options + */ + getPandocAst() { + return tokens && markdownItPandocRenderer(tokens, this.converter.options); + }, + + /** + * Initialize the Prism grammar with the options + */ + initPrism() { + const options = { + insideFences: markdownConversionSvc.defaultOptions.insideFences, + ...this.options, + }; + this.prismGrammars = markdownGrammarSvc.makeGrammars(options); + }, + + /** + * Initialize the markdown-it converter with the options + */ + initConverter() { + this.converter = markdownConversionSvc.createConverter(this.options, true); + }, + + /** + * Initialize the cledit editor with markdown-it section parser and Prism highlighter + */ + initClEditor() { + if (!store.state.files.currentFile.isLoaded) { + reinitClEditor = true; + isPreviewRefreshed = false; + editorEngineSvc.clEditor.toggleEditable(false); + return; + } + const options = { + sectionHighlighter: section => Prism.highlight( + section.text, this.prismGrammars[section.data]), + sectionParser: (text) => { + this.parsingCtx = markdownConversionSvc.parseSections(this.converter, text); + return this.parsingCtx.sections; + }, + }; + editorEngineSvc.initClEditor(options, reinitClEditor); + editorEngineSvc.clEditor.toggleEditable(true); + reinitClEditor = false; + this.restoreScrollPosition(); + }, + + /** + * Finish the conversion initiated by the section parser + */ + convert() { + this.conversionCtx = markdownConversionSvc.convert(this.parsingCtx, this.conversionCtx); + this.$emit('conversionCtx', this.conversionCtx); + tokens = this.parsingCtx.markdownState.tokens; + }, + + /** + * Refresh the preview with the result of `convert()` + */ + refreshPreview() { + const newSectionDescList = []; + let sectionPreviewElt; + let sectionTocElt; + let sectionIdx = 0; + let sectionDescIdx = 0; + let insertBeforePreviewElt = this.previewElt.firstChild; + let insertBeforeTocElt = this.tocElt.firstChild; + let previewHtml = ''; + let heading; + this.conversionCtx.htmlSectionDiff.forEach((item) => { + for (let i = 0; i < item[1].length; i += 1) { + const section = this.conversionCtx.sectionList[sectionIdx]; + if (item[0] === 0) { + const sectionDesc = this.sectionDescList[sectionDescIdx]; + sectionDescIdx += 1; + sectionDesc.editorElt = section.elt; + newSectionDescList.push(sectionDesc); + previewHtml += sectionDesc.html; + sectionIdx += 1; + insertBeforePreviewElt.classList.remove('modified'); + insertBeforePreviewElt = insertBeforePreviewElt.nextSibling; + insertBeforeTocElt.classList.remove('modified'); + insertBeforeTocElt = insertBeforeTocElt.nextSibling; + } else if (item[0] === -1) { + sectionDescIdx += 1; + sectionPreviewElt = insertBeforePreviewElt; + insertBeforePreviewElt = insertBeforePreviewElt.nextSibling; + this.previewElt.removeChild(sectionPreviewElt); + sectionTocElt = insertBeforeTocElt; + insertBeforeTocElt = insertBeforeTocElt.nextSibling; + this.tocElt.removeChild(sectionTocElt); + } else if (item[0] === 1) { + const html = this.conversionCtx.htmlSectionList[sectionIdx]; + sectionIdx += 1; + + // Create preview section element + sectionPreviewElt = document.createElement('div'); + sectionPreviewElt.className = 'cl-preview-section modified'; + sectionPreviewElt.innerHTML = htmlSanitizer.sanitizeHtml(html); + if (insertBeforePreviewElt) { + this.previewElt.insertBefore(sectionPreviewElt, insertBeforePreviewElt); + } else { + this.previewElt.appendChild(sectionPreviewElt); + } + extensionSvc.sectionPreview(sectionPreviewElt, this.options); + + // Create TOC section element + sectionTocElt = document.createElement('div'); + sectionTocElt.className = 'cl-toc-section modified'; + let headingElt = sectionPreviewElt.querySelector('h1, h2, h3, h4, h5, h6'); + heading = undefined; + if (headingElt) { + heading = { + title: headingElt.textContent, + anchor: headingElt.id, + level: parseInt(headingElt.tagName.slice(1), 10), + }; + headingElt = headingElt.cloneNode(true); + headingElt.removeAttribute('id'); + sectionTocElt.appendChild(headingElt); + } + if (insertBeforeTocElt) { + this.tocElt.insertBefore(sectionTocElt, insertBeforeTocElt); + } else { + this.tocElt.appendChild(sectionTocElt); + } + + const clonedElt = sectionPreviewElt.cloneNode(true); + // Unwrap tables + clonedElt.querySelectorAll('.table-wrapper').cl_each((elt) => { + while (elt.firstChild) { + elt.parentNode.appendChild(elt.firstChild); + } + elt.parentNode.removeChild(elt); + }); + + previewHtml += clonedElt.innerHTML; + newSectionDescList.push({ + section, + editorElt: section.elt, + previewElt: sectionPreviewElt, + tocElt: sectionTocElt, + html: clonedElt.innerHTML, + heading, + }); + } + } + }); + this.sectionDescList = newSectionDescList; + this.previewHtml = previewHtml.replace(/^\s+|\s+$/g, ''); + this.tocElt.classList[ + this.tocElt.querySelector('.cl-toc-section *') ? 'remove' : 'add' + ]('toc-tab--empty'); + + // Run preview async operations (image loading, mathjax...) + const loadingImages = this.previewElt.querySelectorAll('.cl-preview-section.modified img'); + const loadedPromises = loadingImages.cl_map(imgElt => new Promise((resolve) => { + if (!imgElt.src) { + resolve(); + return; + } + const img = new window.Image(); + img.onload = resolve; + img.onerror = resolve; + img.src = imgElt.src; + })); + + Promise.all(loadedPromises.concat(extensionSvc.asyncPreview(this.options))) + .then(() => { + this.previewText = this.previewElt.textContent; + this.$emit('previewText', this.previewText); + // Debounce if sections have already been mesured + this.measureSectionDimensions(!!this.sectionDescMeasuredList); + this.makeTextToPreviewDiffs(true); + }); + }, + + /** + * Measure the height of each section in editor, preview and toc. + */ + measureSectionDimensions: allowDebounce(() => { + if (editorSvc.sectionDescList && this.sectionDescList !== editorSvc.sectionDescMeasuredList) { + sectionUtils.measureSectionDimensions(editorSvc); + editorSvc.sectionDescMeasuredList = editorSvc.sectionDescList; + editorSvc.$emit('sectionDescMeasuredList', editorSvc.sectionDescMeasuredList); + } + }, 500), + + /** + * Make the diff between editor's markdown and preview's html. + */ + makeTextToPreviewDiffs: allowDebounce(() => { + if (editorSvc.sectionDescList + && editorSvc.sectionDescList !== editorSvc.sectionDescMeasuredList) { + editorSvc.sectionDescList + .forEach((sectionDesc) => { + if (!sectionDesc.textToPreviewDiffs) { + sectionDesc.previewText = sectionDesc.previewElt.textContent; + sectionDesc.textToPreviewDiffs = diffMatchPatch.diff_main( + sectionDesc.section.text, sectionDesc.previewText); + } + }); + editorSvc.sectionDescWithDiffsList = editorSvc.sectionDescList; + } + }, 50), + + /** + * Save editor selection/scroll state into the current file content. + */ + saveContentState: allowDebounce(() => { + const scrollPosition = editorSvc.getScrollPosition() || + store.state.files.currentFile.content.state.scrollPosition; + store.commit('files/setCurrentFileContentState', { + selectionStart: editorEngineSvc.clEditor.selectionMgr.selectionStart, + selectionEnd: editorEngineSvc.clEditor.selectionMgr.selectionEnd, + scrollPosition, + }, { root: true }); + }, 100), + + /** + * Restore the scroll position from the current file content state. + */ + restoreScrollPosition() { + const scrollPosition = store.state.files.currentFile.content.state.scrollPosition; + if (scrollPosition && this.sectionDescMeasuredList) { + const objectToScroll = this.getObjectToScroll(); + const sectionDesc = this.sectionDescMeasuredList[scrollPosition.sectionIdx]; + if (sectionDesc) { + const scrollTop = sectionDesc[objectToScroll.dimensionKey].startOffset + + (sectionDesc[objectToScroll.dimensionKey].height * scrollPosition.posInSection); + objectToScroll.elt.scrollTop = scrollTop; + } + } + }, + + /** + * Report selection from the preview to the editor. + */ + saveSelection: allowDebounce(() => { + const selection = window.getSelection(); + let range = selection.rangeCount && selection.getRangeAt(0); + if (range) { + if ( + /* eslint-disable no-bitwise */ + !(editorSvc.previewElt.compareDocumentPosition(range.startContainer) + & window.Node.DOCUMENT_POSITION_CONTAINED_BY) || + !(editorSvc.previewElt.compareDocumentPosition(range.endContainer) + & window.Node.DOCUMENT_POSITION_CONTAINED_BY) + /* eslint-enable no-bitwise */ + ) { + range = null; + } + } + if (editorSvc.previewSelectionRange !== range) { + let previewSelectionStartOffset; + let previewSelectionEndOffset; + if (range) { + const startRange = document.createRange(); + startRange.setStart(editorSvc.previewElt, 0); + startRange.setEnd(range.startContainer, range.startOffset); + previewSelectionStartOffset = `${startRange}`.length; + previewSelectionEndOffset = previewSelectionStartOffset + `${range}`.length; + const editorStartOffset = editorSvc.getEditorOffset(previewSelectionStartOffset); + const editorEndOffset = editorSvc.getEditorOffset(previewSelectionEndOffset); + if (editorStartOffset !== undefined && editorEndOffset !== undefined) { + editorEngineSvc.clEditor.selectionMgr.setSelectionStartEnd( + editorStartOffset, editorEndOffset, false); + } + } + editorSvc.previewSelectionRange = range; + } + }, 50), + + /** + * Scroll the preview (or the editor if preview is hidden) to the specified anchor + */ + scrollToAnchor(anchor) { + let scrollTop = 0; + let scrollerElt = this.previewElt.parentNode; + const sectionDesc = anchorHash[anchor]; + if (sectionDesc) { + if (store.state.layout.showSidePreview || !store.state.layout.showEditor) { + scrollTop = sectionDesc.previewDimension.startOffset; + } else { + scrollTop = sectionDesc.editorDimension.startOffset - constants.scrollOffset; + scrollerElt = this.editorElt.parentNode; + } + } else { + const elt = document.getElementById(anchor); + if (elt) { + scrollTop = elt.offsetTop; + } + } + const maxScrollTop = scrollerElt.scrollHeight - scrollerElt.offsetHeight; + if (scrollTop < 0) { + scrollTop = 0; + } else if (scrollTop > maxScrollTop) { + scrollTop = maxScrollTop; + } + animationSvc.animate(scrollerElt) + .scrollTop(scrollTop) + .duration(360) + .start(); + }, + + /** + * Apply the template to the file content + */ + // applyTemplate({ state, commit, dispatch, rootState }, template) { + // function groupToc(array, level = 1) { + // const result = []; + // let currentItem; + + // function pushCurrentItem() { + // if (currentItem) { + // if (currentItem.children.length > 0) { + // currentItem.children = groupToc(currentItem.children, level + 1); + // } + // result.push(currentItem); + // } + // } + // array.forEach((item) => { + // if (item.level !== level) { + // currentItem = currentItem || { + // children: [], + // }; + // currentItem.children.push(item); + // } else { + // pushCurrentItem(); + // currentItem = item; + // } + // }); + // pushCurrentItem(); + // return result; + // } + + // let toc = []; + // state.sectionDescList.cl_each((sectionDesc) => { + // if (sectionDesc.heading) { + // toc.push({ + // title: sectionDesc.heading.title, + // level: sectionDesc.heading.level, + // anchor: sectionDesc.heading.anchor, + // children: [], + // }); + // } + // }); + // toc = groupToc(toc); + + // const view = { + // file: { + // name: rootState.files.currentFile.name, + // content: { + // properties: rootState.files.currentFile.content.properties, + // text: rootState.files.currentFile.content.text, + // html: state.previewHtml, + // toc, + // }, + // }, + // }; + // const worker = new window.Worker(clVersion.getAssetPath('templateWorker-min.js')); + // worker.postMessage([template, view, clSettingSvc.values.handlebarsHelpers]); + // return new Promise((resolve, reject) => { + // worker.addEventListener('message', (e) => { + // resolve(e.data.toString()); + // }); + // setTimeout(() => { + // worker.terminate(); + // reject('Template generation timeout.'); + // }, 10000); + // }); + // }, + + /** + * Pass the elements to the store and initialize the editor. + */ + init(editorElt, previewElt, tocElt) { + this.editorElt = editorElt; + this.previewElt = previewElt; + this.tocElt = tocElt; + + editorEngineSvc.createClEditor(editorElt); + editorEngineSvc.clEditor.toggleEditable(false); + editorEngineSvc.clEditor.on('contentChanged', (content, diffs, sectionList) => { + const parsingCtx = { + ...this.parsingCtx, + sectionList, + }; + this.parsingCtx = parsingCtx; + }); + this.pagedownEditor = pagedown({ + input: Object.create(editorEngineSvc.clEditor), + }); + this.pagedownEditor.run(); + this.editorElt.addEventListener('focus', () => { + // if (clEditorLayoutSvc.currentControl === 'menu') { + // clEditorLayoutSvc.currentControl = undefined + // } + }); + // state.pagedownEditor.hooks.set('insertLinkDialog', (callback) => { + // clEditorSvc.linkDialogCallback = callback + // clEditorLayoutSvc.currentControl = 'linkDialog' + // scope.$evalAsync() + // return true + // }) + // state.pagedownEditor.hooks.set('insertImageDialog', (callback) => { + // clEditorSvc.imageDialogCallback = callback + // clEditorLayoutSvc.currentControl = 'imageDialog' + // scope.$evalAsync() + // return true + // }) + + this.editorElt.addEventListener('scroll', () => this.saveContentState(true)); + + const refreshPreview = () => { + this.convert(); + if (!isPreviewRefreshed) { + this.refreshPreview(); + this.measureSectionDimensions(); + this.restoreScrollPosition(); + } else { + setTimeout(() => this.refreshPreview(), 10); + } + isPreviewRefreshed = true; + }; + + const debouncedRefreshPreview = debounce(refreshPreview, 20); + + let newSectionList; + let newSelectionRange; + const debouncedEditorChanged = debounce(() => { + if (this.sectionList !== newSectionList) { + this.sectionList = newSectionList; + this.$emit('sectionList', this.sectionList); + if (isPreviewRefreshed) { + debouncedRefreshPreview(); + } else { + refreshPreview(); + } + } + if (this.selectionRange !== newSelectionRange) { + this.selectionRange = newSelectionRange; + this.$emit('selectionRange', this.selectionRange); + } + this.saveContentState(); + }, 10); + + editorEngineSvc.clEditor.selectionMgr.on('selectionChanged', (start, end, selectionRange) => { + newSelectionRange = selectionRange; + debouncedEditorChanged(); + }); + + /* ----------------------------- + * Inline images + */ + + const imgCache = Object.create(null); + + const addToImgCache = (imgElt) => { + let entries = imgCache[imgElt.src]; + if (!entries) { + entries = []; + imgCache[imgElt.src] = entries; + } + entries.push(imgElt); + }; + + const getFromImgCache = (src) => { + const entries = imgCache[src]; + if (!entries) { + return null; + } + let imgElt; + return entries + .some((entry) => { + if (this.editorElt.contains(entry)) { + return false; + } + imgElt = entry; + return true; + }) && imgElt; + }; + + const triggerImgCacheGc = debounce(() => { + Object.keys(imgCache).forEach((src) => { + const entries = imgCache[src] + .filter(imgElt => this.editorElt.contains(imgElt)); + if (entries.length) { + imgCache[src] = entries; + } else { + delete imgCache[src]; + } + }); + }, 100); + + let imgEltsToCache = []; + if (store.state.editor.inlineImages) { + editorEngineSvc.clEditor.highlighter.on('sectionHighlighted', (section) => { + section.elt.getElementsByClassName('token img').cl_each((imgTokenElt) => { + const srcElt = imgTokenElt.querySelector('.token.cl-src'); + if (srcElt) { + // Create an img element before the .img.token and wrap both elements + // into a .token.img-wrapper + const imgElt = document.createElement('img'); + imgElt.style.display = 'none'; + const uri = srcElt.textContent; + if (!/^unsafe/.test(htmlSanitizer.sanitizeUri(uri, true))) { + imgElt.onload = () => { + imgElt.style.display = ''; + }; + imgElt.src = uri; + imgEltsToCache.push(imgElt); + } + const imgTokenWrapper = document.createElement('span'); + imgTokenWrapper.className = 'token img-wrapper'; + imgTokenElt.parentNode.insertBefore(imgTokenWrapper, imgTokenElt); + imgTokenWrapper.appendChild(imgElt); + imgTokenWrapper.appendChild(imgTokenElt); + } + }); + }); + } + + editorEngineSvc.clEditor.highlighter.on('highlighted', () => { + imgEltsToCache.forEach((imgElt) => { + const cachedImgElt = getFromImgCache(imgElt.src); + if (cachedImgElt) { + // Found a previously loaded image that has just been released + imgElt.parentNode.replaceChild(cachedImgElt, imgElt); + } else { + addToImgCache(imgElt); + } + }); + imgEltsToCache = []; + // Eject released images from cache + triggerImgCacheGc(); + }); + + editorEngineSvc.clEditor.on('contentChanged', (content, diffs, sectionList) => { + newSectionList = sectionList; + debouncedEditorChanged(); + }); + + // scope.$watch('editorLayoutSvc.isEditorOpen', function (isOpen) { + // clEditorSvc.cledit.toggleEditable(isOpen) + // }) + + // scope.$watch('editorLayoutSvc.currentControl', function (currentControl) { + // !currentControl && setTimeout(function () { + // !scope.isDialogOpen && clEditorSvc.cledit && clEditorSvc.cledit.focus() + // }, 1) + // }) + + // clEditorSvc.setPreviewElt(element[0].querySelector('.preview__inner')) + // var previewElt = element[0].querySelector('.preview') + // clEditorSvc.isPreviewTop = previewElt.scrollTop < 10 + // previewElt.addEventListener('scroll', function () { + // var isPreviewTop = previewElt.scrollTop < 10 + // if (isPreviewTop !== clEditorSvc.isPreviewTop) { + // clEditorSvc.isPreviewTop = isPreviewTop + // scope.$apply() + // } + // }) + + store.watch( + state => state.files.currentFile.content.properties, + (properties) => { + const options = properties && extensionSvc.getOptions(properties, true); + if (JSON.stringify(options) !== JSON.stringify(editorSvc.options)) { + editorSvc.options = options; + editorSvc.initPrism(); + editorSvc.initConverter(); + editorSvc.initClEditor(); + } + }, { + immediate: true, + }); + + store.watch(state => `${state.layout.editorWidth},${state.layout.previewWidth}`, + () => editorSvc.measureSectionDimensions(true)); + store.watch(state => state.layout.showSidePreview, + showSidePreview => showSidePreview && editorSvc.measureSectionDimensions()); + + this.$emit('inited'); + }, +}); + +export default editorSvc; diff --git a/src/services/extensionSvc.js b/src/services/extensionSvc.js new file mode 100644 index 00000000..5fad6d2b --- /dev/null +++ b/src/services/extensionSvc.js @@ -0,0 +1,46 @@ +const getOptionsListeners = []; +const initConverterListeners = []; +const sectionPreviewListeners = []; +const asyncPreviewListeners = []; + +export default { + onGetOptions(listener) { + getOptionsListeners.push(listener); + }, + + onInitConverter(priority, listener) { + initConverterListeners[priority] = listener; + }, + + onSectionPreview(listener) { + sectionPreviewListeners.push(listener); + }, + + onAsyncPreview(listener) { + asyncPreviewListeners.push(listener); + }, + + getOptions(properties, isCurrentFile) { + return getOptionsListeners.reduce((options, listener) => { + listener(options, properties, isCurrentFile); + return options; + }, {}); + }, + + initConverter(markdown, options, isCurrentFile) { + // Use forEach as it's a sparsed array + initConverterListeners.forEach((listener) => { + listener(markdown, options, isCurrentFile); + }); + }, + + sectionPreview(elt, options) { + sectionPreviewListeners.forEach((listener) => { + listener(elt, options); + }); + }, + + asyncPreview(options) { + return Promise.all(asyncPreviewListeners.map(listener => listener(options))); + }, +}; diff --git a/src/services/markdownConversionSvc.js b/src/services/markdownConversionSvc.js new file mode 100644 index 00000000..c26a7da5 --- /dev/null +++ b/src/services/markdownConversionSvc.js @@ -0,0 +1,262 @@ +import DiffMatchPatch from 'diff-match-patch'; +import Prism from 'prismjs'; +import MarkdownIt from 'markdown-it'; +import markdownGrammarSvc from './markdownGrammarSvc'; +import extensionSvc from './extensionSvc'; + +const htmlSectionMarker = '\uF111\uF222\uF333\uF444'; +const diffMatchPatch = new DiffMatchPatch(); + +// Create aliases for syntax highlighting +const languageAliases = ({ + js: 'javascript', + json: 'javascript', + html: 'markup', + svg: 'markup', + xml: 'markup', + py: 'python', + rb: 'ruby', + ps1: 'powershell', + psm1: 'powershell', +}); +Object.keys(languageAliases).forEach((alias) => { + const language = languageAliases[alias]; + Prism.languages[alias] = Prism.languages[language]; +}); + +// Add language parsing capability to markdown fences +const insideFences = {}; +Object.keys(Prism.languages).forEach((name) => { + const language = Prism.languages[name]; + if (Prism.util.type(language) === 'Object') { + insideFences[`language-${name}`] = { + pattern: new RegExp(`(\`\`\`|~~~)${name}\\W[\\s\\S]*`), + inside: { + 'cl cl-pre': /(```|~~~).*/, + rest: language, + }, + }; + } +}); + +// Disable spell checking in specific tokens +const noSpellcheckTokens = Object.create(null); +[ + 'code', + 'pre', + 'pre gfm', + 'math block', + 'math inline', + 'math expr block', + 'math expr inline', + 'latex block', +] + .forEach((key) => { + noSpellcheckTokens[key] = true; + }); +Prism.hooks.add('wrap', (env) => { + if (noSpellcheckTokens[env.type]) { + env.attributes.spellcheck = 'false'; + } +}); + +function createFlagMap(arr) { + return arr.reduce((map, type) => ({ ...map, [type]: true }), {}); +} +const startSectionBlockTypeMap = createFlagMap([ + 'paragraph_open', + 'blockquote_open', + 'heading_open', + 'code', + 'fence', + 'table_open', + 'html_block', + 'bullet_list_open', + 'ordered_list_open', + 'hr', + 'dl_open', +]); +const listBlockTypeMap = createFlagMap([ + 'bullet_list_open', + 'ordered_list_open', +]); +const blockquoteBlockTypeMap = createFlagMap([ + 'blockquote_open', +]); +const tableBlockTypeMap = createFlagMap([ + 'table_open', +]); +const deflistBlockTypeMap = createFlagMap([ + 'dl_open', +]); + +function hashArray(arr, valueHash, valueArray) { + const hash = []; + arr.cl_each((str) => { + let strHash = valueHash[str]; + if (strHash === undefined) { + strHash = valueArray.length; + valueArray.push(str); + valueHash[str] = strHash; + } + hash.push(strHash); + }); + return String.fromCharCode.apply(null, hash); +} + +// Default options for the markdown converter and the grammar +const defaultOptions = { + abbr: true, + breaks: true, + deflist: true, + del: true, + fence: true, + footnote: true, + linkify: true, + math: true, + sub: true, + sup: true, + table: true, + typographer: true, + insideFences, +}; + +const markdownConversionSvc = { + defaultOptions, + + /** + * Creates a converter and init it with extensions. + * @returns {Object} A converter. + */ + createConverter(options, isCurrentFile) { + // Let the listeners add the rules + const converter = new MarkdownIt('zero'); + converter.core.ruler.enable([], true); + converter.block.ruler.enable([], true); + converter.inline.ruler.enable([], true); + extensionSvc.initConverter(converter, options, isCurrentFile); + Object.keys(startSectionBlockTypeMap).forEach((type) => { + const rule = converter.renderer.rules[type] === true || converter.renderer.renderToken; + converter.renderer.rules[type] = (tokens, idx, opts) => { + if (tokens[idx].sectionDelimiter) { + // Add section delimiter + return htmlSectionMarker + rule.call(converter.renderer, tokens, idx, opts); + } + return rule.call(converter.renderer, tokens, idx, opts); + }; + }); + return converter; + }, + + /** + * Parse markdown sections by passing the 2 first block rules of the markdown-it converter. + * @param {Object} converter The markdown-it converter. + * @param {String} text The text to be parsed. + * @returns {Object} A parsing context to be passed to `convert`. + */ + parseSections(converter, text) { + const markdownState = new converter.core.State(text, converter, {}); + const markdownCoreRules = converter.core.ruler.getRules(''); + markdownCoreRules[0](markdownState); // Pass the normalize rule + markdownCoreRules[1](markdownState); // Pass the block rule + const lines = text.split('\n'); + if (!lines[lines.length - 1]) { + // In cledit, last char is always '\n'. + // Remove it as one will be added by addSection + lines.pop(); + } + const parsingCtx = { + sections: [], + converter, + markdownState, + markdownCoreRules, + }; + let data = 'main'; + let i = 0; + + function addSection(maxLine) { + const section = { + text: '', + data, + }; + for (; i < maxLine; i += 1) { + section.text += `${lines[i]}\n`; + } + if (section) { + parsingCtx.sections.push(section); + } + } + markdownState.tokens.forEach((token, index) => { + // index === 0 means there are empty lines at the begining of the file + if (token.level === 0 && startSectionBlockTypeMap[token.type] === true) { + if (index > 0) { + token.sectionDelimiter = true; + addSection(token.map[0]); + } + if (listBlockTypeMap[token.type] === true) { + data = 'list'; + } else if (blockquoteBlockTypeMap[token.type] === true) { + data = 'blockquote'; + } else if (tableBlockTypeMap[token.type] === true) { + data = 'table'; + } else if (deflistBlockTypeMap[token.type] === true) { + data = 'deflist'; + } else { + data = 'main'; + } + } + }); + addSection(lines.length); + return parsingCtx; + }, + + /** + * Convert markdown sections previously parsed with `parseSections`. + * @param {Object} parsingCtx The parsing context returned by `parseSections`. + * @param {Object} previousConversionCtx The conversion context returned by a previous call + * to `convert`, in order to calculate the `htmlSectionDiff` of the returned conversion context. + * @returns {Object} A conversion context. + */ + convert(parsingCtx, previousConversionCtx) { + // This function can be called twice without editor modification + // so prevent from converting it again. + if (!parsingCtx.markdownState.isConverted) { + // Skip 2 first rules previously passed in parseSections + parsingCtx.markdownCoreRules.slice(2).forEach(rule => rule(parsingCtx.markdownState)); + parsingCtx.markdownState.isConverted = true; + } + const tokens = parsingCtx.markdownState.tokens; + const html = parsingCtx.converter.renderer.render( + tokens, + parsingCtx.converter.options, + parsingCtx.markdownState.env, + ); + const htmlSectionList = html.split(htmlSectionMarker); + if (htmlSectionList[0] === '') { + htmlSectionList.shift(); + } + const valueHash = Object.create(null); + const valueArray = []; + const newSectionHash = hashArray(htmlSectionList, valueHash, valueArray); + let htmlSectionDiff; + if (previousConversionCtx) { + const oldSectionHash = hashArray( + previousConversionCtx.htmlSectionList, valueHash, valueArray); + htmlSectionDiff = diffMatchPatch.diff_main(oldSectionHash, newSectionHash, false); + } else { + htmlSectionDiff = [ + [1, newSectionHash], + ]; + } + return { + sectionList: parsingCtx.sectionList, + htmlSectionList, + htmlSectionDiff, + }; + }, +}; + +markdownConversionSvc.defaultConverter = markdownConversionSvc.createConverter(defaultOptions); +markdownConversionSvc.defaultPrismGrammars = markdownGrammarSvc.makeGrammars(defaultOptions); + +export default markdownConversionSvc; diff --git a/src/services/markdownGrammarSvc.js b/src/services/markdownGrammarSvc.js new file mode 100644 index 00000000..9d89ddab --- /dev/null +++ b/src/services/markdownGrammarSvc.js @@ -0,0 +1,414 @@ +const charInsideUrl = '(&|[-A-Z0-9+@#/%?=~_|[\\]()!:,.;])'; +const charEndingUrl = '(&|[-A-Z0-9+@#/%=~_|[\\])])'; +const urlPattern = new RegExp(`(https?|ftp)(://${charInsideUrl}*${charEndingUrl})(?=$|\\W)`, 'gi'); +const emailPattern = /(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)/gi; + +const markup = { + comment: //g, + tag: { + pattern: /<\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|[^\s'">=]+))?\s*)*\/?>/gi, + inside: { + tag: { + pattern: /^<\/?[\w:-]+/i, + inside: { + punctuation: /^<\/?/, + namespace: /^[\w-]+?:/, + }, + }, + 'attr-value': { + pattern: /=(?:('|")[\w\W]*?(\1)|[^\s>]+)/gi, + inside: { + punctuation: /=|>|"/g, + }, + }, + punctuation: /\/?>/g, + 'attr-name': { + pattern: /[\w:-]+/g, + inside: { + namespace: /^[\w-]+?:/, + }, + }, + }, + }, + entity: /&#?[\da-z]{1,8};/gi, +}; + +const latex = { + // A tex command e.g. \foo + keyword: /\\(?:[^a-zA-Z]|[a-zA-Z]+)/g, + // Curly and square braces + lparen: /[[({]/g, + // Curly and square braces + rparen: /[\])}]/g, + // A comment. Tex comments start with % and go to + // the end of the line + comment: /%.*/g, +}; + +export default { + makeGrammars(options) { + const grammars = { + main: {}, + list: {}, + blockquote: {}, + table: {}, + deflist: {}, + }; + + grammars.deflist.deflist = { + pattern: new RegExp( + [ + '^ {0,3}\\S.*\\n', // Description line + '(?:[ \\t]*\\n)?', // Optional empty line + '(?:', + '[ \\t]*:[ \\t].*\\n', // Colon line + '(?:', + '(?:', + '.*\\S.*\\n', // Non-empty line + '|', + '[ \\t]*\\n(?! ?\\S)', // Or empty line not followed by unindented line + ')', + ')*', + '(?:[ \\t]*\\n)*', // Empty lines + ')+', + ].join(''), + 'm', + ), + inside: { + term: /^.+/, + cl: /^[ \t]*:[ \t]/gm, + }, + }; + + const insideFences = options.insideFences || {}; + insideFences['cl cl-pre'] = /```|~~~/; + if (options.fence) { + grammars.main['pre gfm'] = { + pattern: /^(```|~~~)[\s\S]*?\n\1 *$/gm, + inside: insideFences, + }; + grammars.list['pre gfm'] = { + pattern: /^(?: {4}|\t)(```|~~~)[\s\S]*?\n(?: {4}|\t)\1\s*$/gm, + inside: insideFences, + }; + grammars.deflist.deflist.inside['pre gfm'] = grammars.list['pre gfm']; + } + + grammars.main['h1 alt'] = { + pattern: /^.+\n=+[ \t]*$/gm, + inside: { + 'cl cl-hash': /=+[ \t]*$/, + }, + }; + grammars.main['h2 alt'] = { + pattern: /^.+\n-+[ \t]*$/gm, + inside: { + 'cl cl-hash': /-+[ \t]*$/, + }, + }; + for (let i = 6; i >= 1; i -= 1) { + grammars.main[`h${i}`] = { + pattern: new RegExp(`^#{${i}}[ \t].+$`, 'gm'), + inside: { + 'cl cl-hash': new RegExp(`^#{${i}}`), + }, + }; + } + + const list = /^[ \t]*([*+-]|\d+\.)[ \t]/gm; + const blockquote = { + pattern: /^\s*>.*(?:\n[ \t]*\S.*)*/gm, + inside: { + 'cl cl-gt': /^\s*>/gm, + 'cl cl-li': list, + }, + }; + grammars.list.blockquote = blockquote; + grammars.blockquote.blockquote = blockquote; + grammars.deflist.deflist.inside.blockquote = blockquote; + grammars.list['cl cl-li'] = list; + grammars.blockquote['cl cl-li'] = list; + grammars.deflist.deflist.inside['cl cl-li'] = list; + + grammars.table.table = { + pattern: new RegExp( + [ + '^\\s*\\S.*[|].*\\n', // Header Row + '[-| :]+\\n', // Separator + '(?:.*[|].*\\n?)*', // Table rows + '$', + ].join(''), + 'gm', + ), + inside: { + 'cl cl-title-separator': /^[-| :]+$/gm, + 'cl cl-pipe': /[|]/gm, + }, + }; + + grammars.main.hr = { + pattern: /^ {0,3}([*\-_] *){3,}$/gm, + }; + + const defs = {}; + if (options.footnote) { + defs.fndef = { + pattern: /^ {0,3}\[\^.*?\]:.*$/gm, + inside: { + 'ref-id': { + pattern: /^ {0,3}\[\^.*?\]/, + inside: { + cl: /(\[\^|\])/, + }, + }, + }, + }; + } + if (options.abbr) { + defs.abbrdef = { + pattern: /^ {0,3}\*\[.*?\]:.*$/gm, + inside: { + 'abbr-id': { + pattern: /^ {0,3}\*\[.*?\]/, + inside: { + cl: /(\*\[|\])/, + }, + }, + }, + }; + } + defs.linkdef = { + pattern: /^ {0,3}\[.*?\]:.*$/gm, + inside: { + 'link-id': { + pattern: /^ {0,3}\[.*?\]/, + inside: { + cl: /[[\]]/, + }, + }, + url: urlPattern, + }, + }; + + Object.keys(defs).forEach((name) => { + const def = defs[name]; + grammars.main[name] = def; + grammars.list[name] = def; + grammars.blockquote[name] = def; + grammars.table[name] = def; + grammars.deflist[name] = def; + }); + + grammars.main.pre = { + pattern: /^\s*\n(?: {4}|\t).*\S.*\n((?: {4}|\t).*\n)*/gm, + }; + + const rest = {}; + rest.code = { + pattern: /(`+)[\s\S]*?\1/g, + inside: { + 'cl cl-code': /`/, + }, + }; + if (options.math) { + rest['math block'] = { + pattern: /\\\\\[[\s\S]*?\\\\\]/g, + inside: { + 'cl cl-bracket-start': /^\\\\\[/, + 'cl cl-bracket-end': /\\\\\]$/, + rest: latex, + }, + }; + rest['math inline'] = { + pattern: /\\\\\([\s\S]*?\\\\\)/g, + inside: { + 'cl cl-bracket-start': /^\\\\\(/, + 'cl cl-bracket-end': /\\\\\)$/, + rest: latex, + }, + }; + rest['math expr block'] = { + pattern: /(\$\$)[\s\S]*?\1/g, + inside: { + 'cl cl-bracket-start': /^\$\$/, + 'cl cl-bracket-end': /\$\$$/, + rest: latex, + }, + }; + rest['math expr inline'] = { + pattern: /\$(?!\s)[\s\S]*?\S\$(?!\d)/g, + inside: { + 'cl cl-bracket-start': /^\$/, + 'cl cl-bracket-end': /\$$/, + rest: latex, + }, + }; + rest['latex block'] = { + pattern: /\\begin\{([a-z]*\*?)\}[\s\S]*?\\?\\end\{\1\}/g, + inside: { + keyword: /\\(begin|end)/, + rest: latex, + }, + }; + } + if (options.footnote) { + rest.inlinefn = { + pattern: /\^\[.+?\]/g, + inside: { + cl: /(\^\[|\])/, + }, + }; + rest.fn = { + pattern: /\[\^.+?\]/g, + inside: { + cl: /(\[\^|\])/, + }, + }; + } + rest.img = { + pattern: /!\[.*?\]\(.+?\)/g, + inside: { + 'cl cl-title': /['‘][^'’]*['’]|["“][^"”]*["”](?=\)$)/, + 'cl cl-src': { + pattern: /(\]\()[^('" \t]+(?=[)'" \t])/, + lookbehind: true, + }, + }, + }; + rest.link = { + pattern: /\[.*?\]\(.+?\)/gm, + inside: { + 'cl cl-underlined-text': { + pattern: /(\[)[^\]]*/, + lookbehind: true, + }, + 'cl cl-title': /['‘][^'’]*['’]|["“][^"”]*["”](?=\)$)/, + }, + }; + rest.imgref = { + pattern: /!\[.*?\][ \t]*\[.*?\]/g, + }; + rest.linkref = { + pattern: /\[.*?\][ \t]*\[.*?\]/g, + inside: { + 'cl cl-underlined-text': { + pattern: /^(\[)[^\]]*(?=\][ \t]*\[)/, + lookbehind: true, + }, + }, + }; + rest.comment = markup.comment; + rest.tag = markup.tag; + rest.url = urlPattern; + rest.email = emailPattern; + rest.strong = { + pattern: /(^|[^\w*])(__|\*\*)(?![_*])[\s\S]*?\2(?=([^\w*]|$))/gm, + lookbehind: true, + inside: { + 'cl cl-strong cl-start': /^(__|\*\*)/, + 'cl cl-strong cl-close': /(__|\*\*)$/, + }, + }; + rest.em = { + pattern: /(^|[^\w*])(_|\*)(?![_*])[\s\S]*?\2(?=([^\w*]|$))/gm, + lookbehind: true, + inside: { + 'cl cl-em cl-start': /^(_|\*)/, + 'cl cl-em cl-close': /(_|\*)$/, + }, + }; + rest['strong em'] = { + pattern: /(^|[^\w*])(__|\*\*)(_|\*)(?![_*])[\s\S]*?\3\2(?=([^\w*]|$))/gm, + lookbehind: true, + inside: { + 'cl cl-strong cl-start': /^(__|\*\*)(_|\*)/, + 'cl cl-strong cl-close': /(_|\*)(__|\*\*)$/, + }, + }; + rest['strong em inv'] = { + pattern: /(^|[^\w*])(_|\*)(__|\*\*)(?![_*])[\s\S]*?\3\2(?=([^\w*]|$))/gm, + lookbehind: true, + inside: { + 'cl cl-strong cl-start': /^(_|\*)(__|\*\*)/, + 'cl cl-strong cl-close': /(__|\*\*)(_|\*)$/, + }, + }; + if (options.del) { + rest.del = { + pattern: /(^|[^\w*])(~~)[\s\S]*?\2(?=([^\w*]|$))/gm, + lookbehind: true, + inside: { + cl: /~~/, + 'cl-del-text': /[^~]+/, + }, + }; + } + if (options.sub) { + rest.sub = { + pattern: /(~)(?=\S)(.*?\S)\1/gm, + inside: { + cl: /~/, + }, + }; + } + if (options.sup) { + rest.sup = { + pattern: /(\^)(?=\S)(.*?\S)\1/gm, + inside: { + cl: /\^/, + }, + }; + } + rest.entity = markup.entity; + + for (let c = 6; c >= 1; c -= 1) { + grammars.main[`h${c}`].inside.rest = rest; + } + grammars.main['h1 alt'].inside.rest = rest; + grammars.main['h2 alt'].inside.rest = rest; + grammars.table.table.inside.rest = rest; + grammars.main.rest = rest; + grammars.list.rest = rest; + grammars.blockquote.blockquote.inside.rest = rest; + grammars.deflist.deflist.inside.rest = rest; + if (options.footnote) { + grammars.main.fndef.inside.rest = rest; + } + + const restLight = { + code: rest.code, + inlinefn: rest.inlinefn, + fn: rest.fn, + link: rest.link, + linkref: rest.linkref, + }; + rest.strong.inside.rest = restLight; + rest.em.inside.rest = restLight; + if (options.del) { + rest.del.inside.rest = restLight; + } + + const inside = { + code: rest.code, + comment: rest.comment, + tag: rest.tag, + strong: rest.strong, + em: rest.em, + del: rest.del, + sub: rest.sub, + sup: rest.sup, + entity: markup.entity, + }; + rest.link.inside['cl cl-underlined-text'].inside = inside; + rest.linkref.inside['cl cl-underlined-text'].inside = inside; + + // Wrap any other characters to allow paragraph folding + Object.keys(grammars).forEach((key) => { + const grammar = grammars[key]; + grammar.rest = grammar.rest || {}; + grammar.rest.p = /.+/; + }); + + return grammars; + }, +}; diff --git a/src/services/optional/index.js b/src/services/optional/index.js new file mode 100644 index 00000000..e1e5094c --- /dev/null +++ b/src/services/optional/index.js @@ -0,0 +1 @@ +import './scrollSync'; diff --git a/src/services/optional/scrollSync.js b/src/services/optional/scrollSync.js new file mode 100644 index 00000000..e82c6f7c --- /dev/null +++ b/src/services/optional/scrollSync.js @@ -0,0 +1,185 @@ +import store from '../../store'; +import constants from '../constants'; +import animationSvc from '../animationSvc'; +import editorSvc from '../editorSvc'; + +let editorScrollerElt; +let previewScrollerElt; +let previewElt; +let editorFinishTimeoutId; +let previewFinishTimeoutId; +let skipAnimation; +let isScrollEditor; +let isScrollPreview; +let isEditorMoving; +let isPreviewMoving; +let sectionDescList; + +let throttleTimeoutId; +let throttleLastTime = 0; + +function throttle(func, wait) { + clearTimeout(throttleTimeoutId); + const currentTime = Date.now(); + const localWait = (wait + throttleLastTime) - currentTime; + if (localWait < 1) { + throttleLastTime = currentTime; + func(); + } else { + throttleTimeoutId = setTimeout(() => { + throttleLastTime = Date.now(); + func(); + }, localWait); + } +} + +const doScrollSync = () => { + const localSkipAnimation = skipAnimation + || !store.state.layout.showSidePreview + || !store.state.layout.showEditor; + skipAnimation = false; + if (!store.state.editor.scrollSync || !sectionDescList || sectionDescList.length === 0) { + return; + } + let editorScrollTop = editorScrollerElt.scrollTop; + if (editorScrollTop < 0) { + editorScrollTop = 0; + } + let previewScrollTop = previewScrollerElt.scrollTop; + let scrollTo; + if (isScrollEditor) { + // Scroll the preview + isScrollEditor = false; + editorScrollTop += constants.scrollOffset; + sectionDescList.some((sectionDesc) => { + if (editorScrollTop > sectionDesc.editorDimension.endOffset) { + return false; + } + const posInSection = (editorScrollTop - sectionDesc.editorDimension.startOffset) + / (sectionDesc.editorDimension.height || 1); + scrollTo = (sectionDesc.previewDimension.startOffset + + (sectionDesc.previewDimension.height * posInSection)) - constants.scrollOffset; + return true; + }); + scrollTo = Math.min( + scrollTo, + previewScrollerElt.scrollHeight - previewScrollerElt.offsetHeight, + ); + + throttle(() => { + clearTimeout(previewFinishTimeoutId); + animationSvc.animate(previewScrollerElt) + .scrollTop(scrollTo) + .duration(!localSkipAnimation && 100) + .start(() => { + previewFinishTimeoutId = setTimeout(() => { + isPreviewMoving = false; + }, 100); + }, () => { + isPreviewMoving = true; + }); + }, localSkipAnimation ? 500 : 10); + } else if (!store.state.layout.showEditor || isScrollPreview) { + // Scroll the editor + isScrollPreview = false; + previewScrollTop += constants.scrollOffset; + sectionDescList.some((sectionDesc) => { + if (previewScrollTop > sectionDesc.previewDimension.endOffset) { + return false; + } + const posInSection = (previewScrollTop - sectionDesc.previewDimension.startOffset) + / (sectionDesc.previewDimension.height || 1); + scrollTo = (sectionDesc.editorDimension.startOffset + + (sectionDesc.editorDimension.height * posInSection)) - constants.scrollOffset; + return true; + }); + scrollTo = Math.min( + scrollTo, + editorScrollerElt.scrollHeight - editorScrollerElt.offsetHeight, + ); + + throttle(() => { + clearTimeout(editorFinishTimeoutId); + animationSvc.animate(editorScrollerElt) + .scrollTop(scrollTo) + .duration(!localSkipAnimation && 100) + .start(() => { + editorFinishTimeoutId = setTimeout(() => { + isEditorMoving = false; + }, 100); + }, () => { + isEditorMoving = true; + }); + }, localSkipAnimation ? 500 : 10); + } +}; + +let isPreviewRefreshing; +let timeoutId; + +const forceScrollSync = () => { + if (!isPreviewRefreshing) { + doScrollSync(!store.state.layout.showSidePreview); + } +}; +store.watch(state => state.editor.scrollSync, forceScrollSync); + +editorSvc.$on('inited', () => { + editorScrollerElt = editorSvc.editorElt.parentNode; + previewScrollerElt = editorSvc.previewElt.parentNode; + previewElt = editorSvc.previewElt; + + editorScrollerElt.addEventListener('scroll', () => { + if (isEditorMoving) { + return; + } + isScrollEditor = true; + isScrollPreview = false; + doScrollSync(!store.state.layout.showSidePreview); + }); + + previewScrollerElt.addEventListener('scroll', () => { + if (isPreviewMoving || isPreviewRefreshing) { + return; + } + isScrollPreview = true; + isScrollEditor = false; + doScrollSync(!store.state.layout.showSidePreview); + }); +}); + +editorSvc.$on('sectionList', () => { + clearTimeout(timeoutId); + isPreviewRefreshing = true; + sectionDescList = undefined; +}); + +editorSvc.$on('conversionCtx', () => { + // Set the preview height to prevent scrollbar from jumping + previewElt.style.height = `${previewElt.offsetHeight}px`; +}); + +editorSvc.$on('previewText', () => { + // Remove height property once the preview as been refreshed + previewElt.style.removeProperty('height'); + // Assume the user is writing in the editor + isScrollEditor = store.state.layout.showEditor; + // A preview scrolling event can occur if height is smaller + timeoutId = setTimeout(() => { + isPreviewRefreshing = false; + }, 100); +}); + +store.watch(state => state.layout.showSidePreview, + (showSidePreview) => { + if (showSidePreview) { + isScrollEditor = true; + isScrollPreview = false; + skipAnimation = true; + } + }); + +editorSvc.$on('sectionDescMeasuredList', (sectionDescMeasuredList) => { + sectionDescList = sectionDescMeasuredList; + forceScrollSync(); +}); diff --git a/src/services/sectionUtils.js b/src/services/sectionUtils.js new file mode 100644 index 00000000..aa3253d2 --- /dev/null +++ b/src/services/sectionUtils.js @@ -0,0 +1,104 @@ +function SectionDimension(startOffset, endOffset) { + this.startOffset = startOffset; + this.endOffset = endOffset; + this.height = endOffset - startOffset; +} + +function dimensionNormalizer(dimensionName) { + return (editorSvc) => { + const dimensionList = editorSvc.sectionDescList.map(sectionDesc => sectionDesc[dimensionName]); + let dimension; + let i; + let j; + for (i = 0; i < dimensionList.length; i += 1) { + dimension = dimensionList[i]; + if (dimension.height) { + for (j = i + 1; j < dimensionList.length && dimensionList[j].height === 0; j += 1) { + // Loop + } + const normalizeFactor = j - i; + if (normalizeFactor !== 1) { + const normalizedHeight = dimension.height / normalizeFactor; + dimension.height = normalizedHeight; + dimension.endOffset = dimension.startOffset + dimension.height; + for (j = i + 1; j < i + normalizeFactor; j += 1) { + const startOffset = dimension.endOffset; + dimension = dimensionList[j]; + dimension.startOffset = startOffset; + dimension.height = normalizedHeight; + dimension.endOffset = dimension.startOffset + dimension.height; + } + i = j - 1; + } + } + } + }; +} + +const normalizeEditorDimensions = dimensionNormalizer('editorDimension'); +const normalizePreviewDimensions = dimensionNormalizer('previewDimension'); +const normalizeTocDimensions = dimensionNormalizer('tocDimension'); + +function measureSectionDimensions(editorSvc) { + let editorSectionOffset = 0; + let previewSectionOffset = 0; + let tocSectionOffset = 0; + let sectionDesc = editorSvc.sectionDescList[0]; + let nextSectionDesc; + let i = 1; + for (; i < editorSvc.sectionDescList.length; i += 1) { + nextSectionDesc = editorSvc.sectionDescList[i]; + + // Measure editor section + let newEditorSectionOffset = nextSectionDesc.editorElt + ? nextSectionDesc.editorElt.offsetTop + : editorSectionOffset; + newEditorSectionOffset = newEditorSectionOffset > editorSectionOffset + ? newEditorSectionOffset + : editorSectionOffset; + sectionDesc.editorDimension = new SectionDimension(editorSectionOffset, newEditorSectionOffset); + editorSectionOffset = newEditorSectionOffset; + + // Measure preview section + let newPreviewSectionOffset = nextSectionDesc.previewElt + ? nextSectionDesc.previewElt.offsetTop + : previewSectionOffset; + newPreviewSectionOffset = newPreviewSectionOffset > previewSectionOffset + ? newPreviewSectionOffset + : previewSectionOffset; + sectionDesc.previewDimension = new SectionDimension( + previewSectionOffset, newPreviewSectionOffset); + previewSectionOffset = newPreviewSectionOffset; + + // Measure TOC section + let newTocSectionOffset = nextSectionDesc.tocElt + ? nextSectionDesc.tocElt.offsetTop + (nextSectionDesc.tocElt.offsetHeight / 2) + : tocSectionOffset; + newTocSectionOffset = newTocSectionOffset > tocSectionOffset + ? newTocSectionOffset + : tocSectionOffset; + sectionDesc.tocDimension = new SectionDimension(tocSectionOffset, newTocSectionOffset); + tocSectionOffset = newTocSectionOffset; + + sectionDesc = nextSectionDesc; + } + + // Last section + sectionDesc = editorSvc.sectionDescList[i - 1]; + if (sectionDesc) { + sectionDesc.editorDimension = new SectionDimension( + editorSectionOffset, editorSvc.editorElt.scrollHeight); + sectionDesc.previewDimension = new SectionDimension( + previewSectionOffset, editorSvc.previewElt.scrollHeight); + sectionDesc.tocDimension = new SectionDimension( + tocSectionOffset, editorSvc.tocElt.scrollHeight); + } + + normalizeEditorDimensions(editorSvc); + normalizePreviewDimensions(editorSvc); + normalizeTocDimensions(editorSvc); +} + +export default { + measureSectionDimensions, +}; diff --git a/src/services/utils.js b/src/services/utils.js new file mode 100644 index 00000000..4b1dd55b --- /dev/null +++ b/src/services/utils.js @@ -0,0 +1,11 @@ +const crypto = window.crypto || window.msCrypto; +const alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''); +const radix = alphabet.length; +const array = new Uint32Array(20); + +export default { + uid() { + crypto.getRandomValues(array); + return array.map(value => alphabet[value % radix]).join(''); + }, +}; diff --git a/src/store/index.js b/src/store/index.js new file mode 100644 index 00000000..93497307 --- /dev/null +++ b/src/store/index.js @@ -0,0 +1,22 @@ +import createLogger from 'vuex/dist/logger'; +import Vue from 'vue'; +import Vuex from 'vuex'; +import files from './modules/files'; +import layout from './modules/layout'; +import editor from './modules/editor'; + +Vue.use(Vuex); + +const debug = process.env.NODE_ENV !== 'production'; + +const store = new Vuex.Store({ + modules: { + files, + layout, + editor, + }, + strict: debug, + plugins: debug ? [createLogger()] : [], +}); + +export default store; diff --git a/src/store/modules/editor.js b/src/store/modules/editor.js new file mode 100644 index 00000000..43bfe24b --- /dev/null +++ b/src/store/modules/editor.js @@ -0,0 +1,16 @@ +const setter = propertyName => (state, value) => { + state[propertyName] = value; +}; + +export default { + namespaced: true, + state: { + // Configuration + inlineImages: true, + scrollSync: true, + }, + mutations: { + setInlineImages: setter('inlineImages'), + setScrollSync: setter('scrollSync'), + }, +}; diff --git a/src/store/modules/files.js b/src/store/modules/files.js new file mode 100644 index 00000000..9a259853 --- /dev/null +++ b/src/store/modules/files.js @@ -0,0 +1,31 @@ +import mdSample from '../../markdown/sample.md'; + +export default { + namespaced: true, + state: { + files: [], + currentFile: { + name: null, + folderId: null, + isLoaded: true, + content: { + state: {}, + text: mdSample, + properties: {}, + discussions: {}, + comments: {}, + }, + }, + }, + mutations: { + setCurrentFile: (state, value) => { + state.currentFile = value; + }, + setCurrentFileContentText: (state, value) => { + state.currentFile.content.text = value; + }, + setCurrentFileContentState: (state, value) => { + state.currentFile.content.state = value; + }, + }, +}; diff --git a/src/store/modules/layout.js b/src/store/modules/layout.js new file mode 100644 index 00000000..9587396f --- /dev/null +++ b/src/store/modules/layout.js @@ -0,0 +1,167 @@ +const navigationBarHeight = 44; +const sideBarWidth = 280; +const editorMinWidth = 280; +const buttonBarWidth = 30; +const statusBarHeight = 20; +const outOfScreenMargin = 50; +const minPadding = 20; + +const setter = propertyName => (state, value) => { + state[propertyName] = value; +}; + +const toggler = (propertyName, setterName) => ({ state, commit, dispatch }, show) => { + commit(setterName, show === undefined ? !state[propertyName] : show); + dispatch('updateStyle'); +}; + +export default { + namespaced: true, + state: { + // Configuration + showNavigationBar: true, + showEditor: true, + showSidePreview: true, + showSideBar: false, + showStatusBar: false, + editorWidthFactor: 1, + fontSizeFactor: 1, + // Style + fontSize: 0, + inner1Y: 0, + inner1Height: 0, + inner2Height: 0, + inner3X: 0, + inner3Width: 0, + navigationBarY: 0, + sideBarX: 0, + statusBarY: 0, + editorWidth: 0, + editorPadding: 0, + previewWidth: 0, + previewPadding: 0, + }, + mutations: { + setShowNavigationBar: setter('showNavigationBar'), + setShowEditor: setter('showEditor'), + setShowSidePreview: setter('showSidePreview'), + setShowSideBar: setter('showSideBar'), + setShowStatusBar: setter('showStatusBar'), + setEditorWidthFactor: setter('editorWidthFactor'), + setFontSizeFactor: setter('fontSizeFactor'), + setFontSize: setter('fontSize'), + setInner1Y: setter('inner1Y'), + setInner1Height: setter('inner1Height'), + setInner2Height: setter('inner2Height'), + setInner3X: setter('inner3X'), + setInner3Width: setter('inner3Width'), + setNavigationBarY: setter('navigationBarY'), + setSideBarX: setter('sideBarX'), + setStatusBarY: setter('statusBarY'), + setEditorWidth: setter('editorWidth'), + setEditorPadding: setter('editorPadding'), + setPreviewWidth: setter('previewWidth'), + setPreviewPadding: setter('previewPadding'), + }, + actions: { + toggleNavigationBar: toggler('showNavigationBar', 'setShowNavigationBar'), + toggleEditor: toggler('showEditor', 'setShowEditor'), + toggleSidePreview: toggler('showSidePreview', 'setShowSidePreview'), + toggleSideBar: toggler('showSideBar', 'setShowSideBar'), + toggleStatusBar: toggler('showStatusBar', 'setShowStatusBar'), + updateStyle({ state, commit, dispatch }) { + const bodyWidth = document.body.clientWidth; + const bodyHeight = document.body.clientHeight; + + const showNavigationBar = state.showEditor && state.showNavigationBar; + const inner1Y = showNavigationBar + ? navigationBarHeight + : 0; + const inner1Height = bodyHeight - inner1Y; + const inner2Height = state.showStatusBar + ? inner1Height - statusBarHeight + : inner1Height; + const navigationBarY = showNavigationBar + ? 0 + : -navigationBarHeight - outOfScreenMargin; + const sideBarX = state.showSideBar + ? bodyWidth - sideBarWidth + : bodyWidth + outOfScreenMargin; + const statusBarY = state.showStatusBar + ? inner2Height + : inner2Height + outOfScreenMargin; + + let doublePanelWidth = bodyWidth - buttonBarWidth; + if (state.showSideBar) { + doublePanelWidth -= sideBarWidth; + } + if (doublePanelWidth < editorMinWidth) { + doublePanelWidth = editorMinWidth; + } + const splitPanel = state.showEditor && state.showSidePreview; + if (splitPanel && doublePanelWidth / 2 < editorMinWidth) { + dispatch('toggleSidePreview', false); + return; + } + if (state.showSideBar && bodyWidth < editorMinWidth + sideBarWidth) { + dispatch('toggleSideBar', false); + return; + } + + let fontSize = 18; + let textWidth = 990; + if (doublePanelWidth < 1120) { + fontSize -= 1; + textWidth = 910; + } + if (doublePanelWidth < 1040) { + textWidth = 830; + } + if (textWidth < 640) { + fontSize -= 1; + } + textWidth *= state.editorWidthFactor; + fontSize *= state.fontSizeFactor; + + const panelWidth = doublePanelWidth / 2; + let inner3X = panelWidth; + if (!splitPanel) { + inner3X = state.showEditor + ? doublePanelWidth + : -buttonBarWidth; + } + const inner3Width = splitPanel + ? panelWidth + buttonBarWidth + : doublePanelWidth + buttonBarWidth; + + const previewWidth = splitPanel + ? panelWidth + : bodyWidth; + let previewPadding = (previewWidth - textWidth) / 2; + if (previewPadding < minPadding) { + previewPadding = minPadding; + } + const editorWidth = splitPanel + ? panelWidth + : doublePanelWidth; + let editorPadding = (editorWidth - textWidth) / 2; + if (editorPadding < minPadding) { + editorPadding = minPadding; + } + + commit('setFontSize', fontSize); + commit('setInner1Y', inner1Y); + commit('setInner1Height', inner1Height); + commit('setInner2Height', inner2Height); + commit('setInner3X', inner3X); + commit('setInner3Width', inner3Width); + commit('setNavigationBarY', navigationBarY); + commit('setSideBarX', sideBarX); + commit('setStatusBarY', statusBarY); + commit('setPreviewWidth', previewWidth); + commit('setPreviewPadding', previewPadding); + commit('setEditorWidth', editorWidth); + commit('setEditorPadding', editorPadding); + }, + }, +}; diff --git a/static/.gitkeep b/static/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000..f94a90fa --- /dev/null +++ b/yarn.lock @@ -0,0 +1,5622 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +JSONStream@^0.8.4: + version "0.8.4" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-0.8.4.tgz#91657dfe6ff857483066132b4618b62e8f4887bd" + dependencies: + jsonparse "0.0.5" + through ">=2.2.7 <3" + +abbrev@1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f" + +accepts@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" + dependencies: + mime-types "~2.1.11" + negotiator "0.6.1" + +acorn-dynamic-import@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4" + dependencies: + acorn "^4.0.3" + +acorn-jsx@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" + dependencies: + acorn "^3.0.4" + +acorn@^3.0.4: + version "3.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" + +acorn@^4.0.3: + version "4.0.13" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" + +acorn@^5.0.0, acorn@^5.0.1, acorn@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.0.3.tgz#c460df08491463f028ccb82eab3730bf01087b3d" + +ajv-keywords@^1.0.0, ajv-keywords@^1.1.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" + +ajv@^4.11.2, ajv@^4.7.0, ajv@^4.9.1: + version "4.11.8" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" + dependencies: + co "^4.6.0" + json-stable-stringify "^1.0.1" + +align-text@^0.1.1, align-text@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" + dependencies: + kind-of "^3.0.2" + longest "^1.0.1" + repeat-string "^1.5.2" + +alphanum-sort@^1.0.1, alphanum-sort@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" + +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + +ansi-escapes@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" + +ansi-html@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +anymatch@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507" + dependencies: + arrify "^1.0.0" + micromatch "^2.1.5" + +aproba@^1.0.3: + version "1.1.1" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.1.tgz#95d3600f07710aa0e9298c726ad5ecf2eacbabab" + +are-we-there-yet@~1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +argparse@^1.0.7: + version "1.0.9" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + dependencies: + arr-flatten "^1.0.1" + +arr-flatten@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.3.tgz#a274ed85ac08849b6bd7847c4580745dc51adfb1" + +array-differ@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + +array-find@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-find/-/array-find-1.0.0.tgz#6c8e286d11ed768327f8e62ecee87353ca3e78b8" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + +array-union@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + dependencies: + array-uniq "^1.0.1" + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + +arrify@^1.0.0, arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + +asn1.js@^4.0.0: + version "4.9.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.1.tgz#48ba240b45a9280e94748990ba597d216617fd40" + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +asn1@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + +assert-plus@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" + +assert@^1.1.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" + dependencies: + util "0.10.3" + +async-each@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" + +async-foreach@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" + +async@^2.1.2, async@^2.1.5: + version "2.4.1" + resolved "https://registry.yarnpkg.com/async/-/async-2.4.1.tgz#62a56b279c98a11d0987096a01cc3eeb8eb7bbd7" + dependencies: + lodash "^4.14.0" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +autoprefixer@^6.0.0, autoprefixer@^6.3.1, autoprefixer@^6.7.2: + version "6.7.7" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014" + dependencies: + browserslist "^1.7.6" + caniuse-db "^1.0.30000634" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^5.2.16" + postcss-value-parser "^3.2.3" + +aws-sign2@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" + +aws4@^1.2.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" + +babel-code-frame@^6.11.0, babel-code-frame@^6.16.0, babel-code-frame@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4" + dependencies: + chalk "^1.1.0" + esutils "^2.0.2" + js-tokens "^3.0.0" + +babel-core@^6.22.1, babel-core@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.24.1.tgz#8c428564dce1e1f41fb337ec34f4c3b022b5ad83" + dependencies: + babel-code-frame "^6.22.0" + babel-generator "^6.24.1" + babel-helpers "^6.24.1" + babel-messages "^6.23.0" + babel-register "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + babylon "^6.11.0" + convert-source-map "^1.1.0" + debug "^2.1.1" + json5 "^0.5.0" + lodash "^4.2.0" + minimatch "^3.0.2" + path-is-absolute "^1.0.0" + private "^0.1.6" + slash "^1.0.0" + source-map "^0.5.0" + +babel-eslint@^7.1.1: + version "7.2.3" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-7.2.3.tgz#b2fe2d80126470f5c19442dc757253a897710827" + dependencies: + babel-code-frame "^6.22.0" + babel-traverse "^6.23.1" + babel-types "^6.23.0" + babylon "^6.17.0" + +babel-generator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.24.1.tgz#e715f486c58ded25649d888944d52aa07c5d9497" + dependencies: + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.2.0" + source-map "^0.5.0" + trim-right "^1.0.1" + +babel-helper-bindify-decorators@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz#14c19e5f142d7b47f19a52431e52b1ccbc40a330" + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-builder-binary-assignment-operator-visitor@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664" + dependencies: + babel-helper-explode-assignable-expression "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-call-delegate@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d" + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-define-map@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.24.1.tgz#7a9747f258d8947d32d515f6aa1c7bd02204a080" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + lodash "^4.2.0" + +babel-helper-explode-assignable-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa" + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-explode-class@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz#7dc2a3910dee007056e1e31d640ced3d54eaa9eb" + dependencies: + babel-helper-bindify-decorators "^6.24.1" + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-function-name@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" + dependencies: + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-get-function-arity@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-hoist-variables@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-optimise-call-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-regex@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.24.1.tgz#d36e22fab1008d79d88648e32116868128456ce8" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + lodash "^4.2.0" + +babel-helper-remap-async-to-generator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz#5ec581827ad723fecdd381f1c928390676e4551b" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-replace-supers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" + dependencies: + babel-helper-optimise-call-expression "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helpers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-loader@^6.2.10: + version "6.4.1" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-6.4.1.tgz#0b34112d5b0748a8dcdbf51acf6f9bd42d50b8ca" + dependencies: + find-cache-dir "^0.1.1" + loader-utils "^0.2.16" + mkdirp "^0.5.1" + object-assign "^4.0.1" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-check-es2015-constants@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-syntax-async-functions@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" + +babel-plugin-syntax-async-generators@^6.5.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz#6bc963ebb16eccbae6b92b596eb7f35c342a8b9a" + +babel-plugin-syntax-class-properties@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de" + +babel-plugin-syntax-decorators@^6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz#312563b4dbde3cc806cee3e416cceeaddd11ac0b" + +babel-plugin-syntax-dynamic-import@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz#8d6a26229c83745a9982a441051572caa179b1da" + +babel-plugin-syntax-exponentiation-operator@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" + +babel-plugin-syntax-object-rest-spread@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" + +babel-plugin-syntax-trailing-function-commas@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" + +babel-plugin-transform-async-generator-functions@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz#f058900145fd3e9907a6ddf28da59f215258a5db" + dependencies: + babel-helper-remap-async-to-generator "^6.24.1" + babel-plugin-syntax-async-generators "^6.5.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-async-to-generator@^6.22.0, babel-plugin-transform-async-to-generator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761" + dependencies: + babel-helper-remap-async-to-generator "^6.24.1" + babel-plugin-syntax-async-functions "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-class-properties@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz#6a79763ea61d33d36f37b611aa9def81a81b46ac" + dependencies: + babel-helper-function-name "^6.24.1" + babel-plugin-syntax-class-properties "^6.8.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-decorators@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz#788013d8f8c6b5222bdf7b344390dfd77569e24d" + dependencies: + babel-helper-explode-class "^6.24.1" + babel-plugin-syntax-decorators "^6.13.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-arrow-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoping@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.24.1.tgz#76c295dc3a4741b1665adfd3167215dcff32a576" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + lodash "^4.2.0" + +babel-plugin-transform-es2015-classes@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" + dependencies: + babel-helper-define-map "^6.24.1" + babel-helper-function-name "^6.24.1" + babel-helper-optimise-call-expression "^6.24.1" + babel-helper-replace-supers "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-computed-properties@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-destructuring@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-duplicate-keys@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-for-of@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-function-name@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015-modules-amd@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154" + dependencies: + babel-plugin-transform-es2015-modules-commonjs "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.1.tgz#d3e310b40ef664a36622200097c6d440298f2bfe" + dependencies: + babel-plugin-transform-strict-mode "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-modules-systemjs@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23" + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-umd@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468" + dependencies: + babel-plugin-transform-es2015-modules-amd "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-object-super@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d" + dependencies: + babel-helper-replace-supers "^6.24.1" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-parameters@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" + dependencies: + babel-helper-call-delegate "^6.24.1" + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-shorthand-properties@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-spread@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-sticky-regex@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc" + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-template-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-typeof-symbol@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-unicode-regex@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9" + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + regexpu-core "^2.0.0" + +babel-plugin-transform-exponentiation-operator@^6.22.0, babel-plugin-transform-exponentiation-operator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e" + dependencies: + babel-helper-builder-binary-assignment-operator-visitor "^6.24.1" + babel-plugin-syntax-exponentiation-operator "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-object-rest-spread@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.23.0.tgz#875d6bc9be761c58a2ae3feee5dc4895d8c7f921" + dependencies: + babel-plugin-syntax-object-rest-spread "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-regenerator@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.24.1.tgz#b8da305ad43c3c99b4848e4fe4037b770d23c418" + dependencies: + regenerator-transform "0.9.11" + +babel-plugin-transform-runtime@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz#88490d446502ea9b8e7efb0fe09ec4d99479b1ee" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-strict-mode@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-preset-env@^1.3.2: + version "1.5.1" + resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.5.1.tgz#d2eca6af179edf27cdc305a84820f601b456dd0b" + dependencies: + babel-plugin-check-es2015-constants "^6.22.0" + babel-plugin-syntax-trailing-function-commas "^6.22.0" + babel-plugin-transform-async-to-generator "^6.22.0" + babel-plugin-transform-es2015-arrow-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoped-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoping "^6.23.0" + babel-plugin-transform-es2015-classes "^6.23.0" + babel-plugin-transform-es2015-computed-properties "^6.22.0" + babel-plugin-transform-es2015-destructuring "^6.23.0" + babel-plugin-transform-es2015-duplicate-keys "^6.22.0" + babel-plugin-transform-es2015-for-of "^6.23.0" + babel-plugin-transform-es2015-function-name "^6.22.0" + babel-plugin-transform-es2015-literals "^6.22.0" + babel-plugin-transform-es2015-modules-amd "^6.22.0" + babel-plugin-transform-es2015-modules-commonjs "^6.23.0" + babel-plugin-transform-es2015-modules-systemjs "^6.23.0" + babel-plugin-transform-es2015-modules-umd "^6.23.0" + babel-plugin-transform-es2015-object-super "^6.22.0" + babel-plugin-transform-es2015-parameters "^6.23.0" + babel-plugin-transform-es2015-shorthand-properties "^6.22.0" + babel-plugin-transform-es2015-spread "^6.22.0" + babel-plugin-transform-es2015-sticky-regex "^6.22.0" + babel-plugin-transform-es2015-template-literals "^6.22.0" + babel-plugin-transform-es2015-typeof-symbol "^6.23.0" + babel-plugin-transform-es2015-unicode-regex "^6.22.0" + babel-plugin-transform-exponentiation-operator "^6.22.0" + babel-plugin-transform-regenerator "^6.22.0" + browserslist "^2.1.2" + invariant "^2.2.2" + semver "^5.3.0" + +babel-preset-stage-2@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz#d9e2960fb3d71187f0e64eec62bc07767219bdc1" + dependencies: + babel-plugin-syntax-dynamic-import "^6.18.0" + babel-plugin-transform-class-properties "^6.24.1" + babel-plugin-transform-decorators "^6.24.1" + babel-preset-stage-3 "^6.24.1" + +babel-preset-stage-3@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz#836ada0a9e7a7fa37cb138fb9326f87934a48395" + dependencies: + babel-plugin-syntax-trailing-function-commas "^6.22.0" + babel-plugin-transform-async-generator-functions "^6.24.1" + babel-plugin-transform-async-to-generator "^6.24.1" + babel-plugin-transform-exponentiation-operator "^6.24.1" + babel-plugin-transform-object-rest-spread "^6.22.0" + +babel-register@^6.22.0, babel-register@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.24.1.tgz#7e10e13a2f71065bdfad5a1787ba45bca6ded75f" + dependencies: + babel-core "^6.24.1" + babel-runtime "^6.22.0" + core-js "^2.4.0" + home-or-tmp "^2.0.0" + lodash "^4.2.0" + mkdirp "^0.5.1" + source-map-support "^0.4.2" + +babel-runtime@^6.18.0, babel-runtime@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.10.0" + +babel-template@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.24.1.tgz#04ae514f1f93b3a2537f2a0f60a5a45fb8308333" + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + babylon "^6.11.0" + lodash "^4.2.0" + +babel-traverse@^6.23.1, babel-traverse@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.24.1.tgz#ab36673fd356f9a0948659e7b338d5feadb31695" + dependencies: + babel-code-frame "^6.22.0" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + babylon "^6.15.0" + debug "^2.2.0" + globals "^9.0.0" + invariant "^2.2.0" + lodash "^4.2.0" + +babel-types@^6.19.0, babel-types@^6.23.0, babel-types@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.24.1.tgz#a136879dc15b3606bda0d90c1fc74304c2ff0975" + dependencies: + babel-runtime "^6.22.0" + esutils "^2.0.2" + lodash "^4.2.0" + to-fast-properties "^1.0.1" + +babylon@^6.11.0, babylon@^6.15.0, babylon@^6.17.0: + version "6.17.2" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.2.tgz#201d25ef5f892c41bae49488b08db0dd476e9f5c" + +balanced-match@^0.4.0, balanced-match@^0.4.1, balanced-match@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" + +base64-js@^1.0.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1" + +bcrypt-pbkdf@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" + dependencies: + tweetnacl "^0.14.3" + +bezier-easing@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/bezier-easing/-/bezier-easing-1.1.1.tgz#9e2f0e270b1ab204ff243d216d1fab52172da26c" + +big.js@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.1.3.tgz#4cada2193652eb3ca9ec8e55c9015669c9806978" + +binary-extensions@^1.0.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.8.0.tgz#48ec8d16df4377eae5fa5884682480af4d95c774" + +block-stream@*: + version "0.0.9" + resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + dependencies: + inherits "~2.0.0" + +bluebird@^2.10.2: + version "2.11.0" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" + +bluebird@^3.0.5, bluebird@^3.1.1, bluebird@^3.4.7: + version "3.5.0" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c" + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: + version "4.11.6" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + +boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + +boom@2.x.x: + version "2.10.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" + dependencies: + hoek "2.x.x" + +brace-expansion@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59" + dependencies: + balanced-match "^0.4.1" + concat-map "0.0.1" + +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.0.6.tgz#5e7725dbdef1fd5930d4ebab48567ce451c48a0a" + dependencies: + buffer-xor "^1.0.2" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + inherits "^2.0.1" + +browserify-cipher@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.0.tgz#9988244874bf5ed4e28da95666dcd66ac8fc363a" + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.0.tgz#daa277717470922ed2fe18594118a175439721dd" + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + +browserify-rsa@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" + dependencies: + bn.js "^4.1.1" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.2" + elliptic "^6.0.0" + inherits "^2.0.1" + parse-asn1 "^5.0.0" + +browserify-zlib@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" + dependencies: + pako "~0.2.0" + +browserslist@^1.1.1, browserslist@^1.1.3, browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: + version "1.7.7" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9" + dependencies: + caniuse-db "^1.0.30000639" + electron-to-chromium "^1.2.7" + +browserslist@^2.1.2: + version "2.1.4" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.1.4.tgz#cc526af4a1312b7d2e05653e56d0c8ab70c0e053" + dependencies: + caniuse-lite "^1.0.30000670" + electron-to-chromium "^1.3.11" + +buffer-shims@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" + +buffer-xor@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + +buffer@^4.3.0: + version "4.9.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +builtin-modules@^1.0.0, builtin-modules@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + +caller-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" + dependencies: + callsites "^0.2.0" + +callsites@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" + +camel-case@3.0.x: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" + dependencies: + no-case "^2.2.0" + upper-case "^1.1.1" + +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" + +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + +caniuse-api@^1.5.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c" + dependencies: + browserslist "^1.3.6" + caniuse-db "^1.0.30000529" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-db@^1.0.30000187, caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: + version "1.0.30000676" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000676.tgz#82ea578237637c8ff34a28acaade373b624c4ea8" + +caniuse-lite@^1.0.30000670: + version "1.0.30000676" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000676.tgz#1e962123f48073f0c51c4ea0651dd64d25786498" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + +center-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" + dependencies: + align-text "^0.1.3" + lazy-cache "^1.0.3" + +chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chokidar@^1.4.3: + version "1.7.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" + dependencies: + anymatch "^1.3.0" + async-each "^1.0.0" + glob-parent "^2.0.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^2.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + optionalDependencies: + fsevents "^1.0.0" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.3.tgz#eeabf194419ce900da3018c207d212f2a6df0a07" + dependencies: + inherits "^2.0.1" + +circular-json@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d" + +clap@^1.0.9: + version "1.1.3" + resolved "https://registry.yarnpkg.com/clap/-/clap-1.1.3.tgz#b3bd36e93dd4cbfb395a3c26896352445265c05b" + dependencies: + chalk "^1.1.3" + +clean-css@4.1.x: + version "4.1.3" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.3.tgz#07cfe8980edb20d455ddc23aadcf1e04c6e509ce" + dependencies: + source-map "0.5.x" + +cli-cursor@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" + dependencies: + restore-cursor "^1.0.1" + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + dependencies: + restore-cursor "^2.0.0" + +cli-spinners@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.0.0.tgz#ef987ed3d48391ac3dab9180b406a742180d6e6a" + +cli-width@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a" + +clipboard@^1.5.5: + version "1.7.1" + resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-1.7.1.tgz#360d6d6946e99a7a1fef395e42ba92b5e9b5a16b" + dependencies: + good-listener "^1.2.2" + select "^1.1.2" + tiny-emitter "^2.0.0" + +cliui@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" + dependencies: + center-align "^0.1.1" + right-align "^0.1.1" + wordwrap "0.0.2" + +cliui@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +clone-deep@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-0.2.4.tgz#4e73dd09e9fb971cc38670c5dced9c1896481cc6" + dependencies: + for-own "^0.1.3" + is-plain-object "^2.0.1" + kind-of "^3.0.2" + lazy-cache "^1.0.3" + shallow-clone "^0.1.2" + +clone-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/clone-regexp/-/clone-regexp-1.0.0.tgz#eae0a2413f55c0942f818c229fefce845d7f3b1c" + dependencies: + is-regexp "^1.0.0" + is-supported-regexp-flag "^1.0.0" + +clone@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149" + +clunderscore@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/clunderscore/-/clunderscore-1.0.3.tgz#a9f99088702d919404d82781f067872ff209d92c" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + +coa@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.2.tgz#2ba9fec3b4aa43d7a49d7e6c3561e92061b6bcec" + dependencies: + q "^1.1.2" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +color-convert@^1.3.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a" + dependencies: + color-name "^1.1.1" + +color-diff@^0.1.3: + version "0.1.7" + resolved "https://registry.yarnpkg.com/color-diff/-/color-diff-0.1.7.tgz#6db78cd9482a8e459d40821eaf4b503283dcb8e2" + +color-name@^1.0.0, color-name@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.2.tgz#5c8ab72b64bd2215d617ae9559ebb148475cf98d" + +color-string@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991" + dependencies: + color-name "^1.0.0" + +color@^0.11.0: + version "0.11.4" + resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764" + dependencies: + clone "^1.0.2" + color-convert "^1.3.0" + color-string "^0.3.0" + +colorguard@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/colorguard/-/colorguard-1.2.0.tgz#f3facaf5caaeba4ef54653d9fb25bb73177c0d84" + dependencies: + chalk "^1.1.1" + color-diff "^0.1.3" + log-symbols "^1.0.2" + object-assign "^4.0.1" + pipetteur "^2.0.0" + plur "^2.0.0" + postcss "^5.0.4" + postcss-reporter "^1.2.1" + text-table "^0.2.0" + yargs "^1.2.6" + +colormin@^1.0.5: + version "1.1.2" + resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133" + dependencies: + color "^0.11.0" + css-color-names "0.0.4" + has "^1.0.1" + +colors@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" + +combined-stream@^1.0.5, combined-stream@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" + dependencies: + delayed-stream "~1.0.0" + +commander@2.9.x, commander@^2.9.0, commander@~2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" + dependencies: + graceful-readlink ">= 1.0.0" + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +concat-stream@^1.5.2: + version "1.6.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" + dependencies: + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +config-chain@~1.1.5: + version "1.1.11" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.11.tgz#aba09747dfbe4c3e70e766a6e41586e1859fc6f2" + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + +connect-history-api-fallback@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz#e51d17f8f0ef0db90a64fdb47de3051556e9f169" + +console-browserify@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" + dependencies: + date-now "^0.1.4" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + +consolidate@^0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.14.5.tgz#5a25047bc76f73072667c8cb52c989888f494c63" + dependencies: + bluebird "^3.1.1" + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + +contains-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + +content-type@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed" + +convert-source-map@^1.1.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + +cookie@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" + +copy-webpack-plugin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.0.1.tgz#9728e383b94316050d0c7463958f2b85c0aa8200" + dependencies: + bluebird "^2.10.2" + fs-extra "^0.26.4" + glob "^6.0.4" + is-glob "^3.1.0" + loader-utils "^0.2.15" + lodash "^4.3.0" + minimatch "^3.0.0" + node-dir "^0.1.10" + +core-js@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +cosmiconfig@^2.1.0, cosmiconfig@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-2.1.3.tgz#952771eb0dddc1cb3fa2f6fbe51a522e93b3ee0a" + dependencies: + is-directory "^0.3.1" + js-yaml "^3.4.3" + minimist "^1.2.0" + object-assign "^4.1.0" + os-homedir "^1.0.1" + parse-json "^2.2.0" + require-from-string "^1.1.0" + +create-ecdh@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d" + dependencies: + bn.js "^4.1.0" + elliptic "^6.0.0" + +create-hash@^1.1.0, create-hash@^1.1.1, create-hash@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.3.tgz#606042ac8b9262750f483caddab0f5819172d8fd" + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + ripemd160 "^2.0.0" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: + version "1.1.6" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.6.tgz#acb9e221a4e17bdb076e90657c42b93e3726cf06" + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-spawn@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" + dependencies: + lru-cache "^4.0.1" + which "^1.2.9" + +cryptiles@2.x.x: + version "2.0.5" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" + dependencies: + boom "2.x.x" + +crypto-browserify@^3.11.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.11.0.tgz#3652a0906ab9b2a7e0c3ce66a408e957a2485522" + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + +css-color-names@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.3.tgz#de0cef16f4d8aa8222a320d5b6d7e9bbada7b9f6" + +css-color-names@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" + +css-loader@^0.28.4: + version "0.28.4" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.4.tgz#6cf3579192ce355e8b38d5f42dd7a1f2ec898d0f" + dependencies: + babel-code-frame "^6.11.0" + css-selector-tokenizer "^0.7.0" + cssnano ">=2.6.1 <4" + icss-utils "^2.1.0" + loader-utils "^1.0.2" + lodash.camelcase "^4.3.0" + object-assign "^4.0.1" + postcss "^5.0.6" + postcss-modules-extract-imports "^1.0.0" + postcss-modules-local-by-default "^1.0.1" + postcss-modules-scope "^1.0.0" + postcss-modules-values "^1.1.0" + postcss-value-parser "^3.3.0" + source-list-map "^0.1.7" + +css-rule-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/css-rule-stream/-/css-rule-stream-1.1.0.tgz#3786e7198983d965a26e31957e09078cbb7705a2" + dependencies: + css-tokenize "^1.0.1" + duplexer2 "0.0.2" + ldjson-stream "^1.2.1" + through2 "^0.6.3" + +css-select@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + +css-selector-tokenizer@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz#e6988474ae8c953477bf5e7efecfceccd9cf4c86" + dependencies: + cssesc "^0.1.0" + fastparse "^1.1.1" + regexpu-core "^1.0.0" + +css-tokenize@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/css-tokenize/-/css-tokenize-1.0.1.tgz#4625cb1eda21c143858b7f81d6803c1d26fc14be" + dependencies: + inherits "^2.0.1" + readable-stream "^1.0.33" + +css-what@2.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" + +cssesc@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4" + +"cssnano@>=2.6.1 <4", cssnano@^3.4.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38" + dependencies: + autoprefixer "^6.3.1" + decamelize "^1.1.2" + defined "^1.0.0" + has "^1.0.1" + object-assign "^4.0.1" + postcss "^5.0.14" + postcss-calc "^5.2.0" + postcss-colormin "^2.1.8" + postcss-convert-values "^2.3.4" + postcss-discard-comments "^2.0.4" + postcss-discard-duplicates "^2.0.1" + postcss-discard-empty "^2.0.1" + postcss-discard-overridden "^0.1.1" + postcss-discard-unused "^2.2.1" + postcss-filter-plugins "^2.0.0" + postcss-merge-idents "^2.1.5" + postcss-merge-longhand "^2.0.1" + postcss-merge-rules "^2.0.3" + postcss-minify-font-values "^1.0.2" + postcss-minify-gradients "^1.0.1" + postcss-minify-params "^1.0.4" + postcss-minify-selectors "^2.0.4" + postcss-normalize-charset "^1.1.0" + postcss-normalize-url "^3.0.7" + postcss-ordered-values "^2.1.0" + postcss-reduce-idents "^2.2.2" + postcss-reduce-initial "^1.0.0" + postcss-reduce-transforms "^1.0.3" + postcss-svgo "^2.1.1" + postcss-unique-selectors "^2.0.2" + postcss-value-parser "^3.2.3" + postcss-zindex "^2.0.1" + +csso@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/csso/-/csso-2.3.2.tgz#ddd52c587033f49e94b71fc55569f252e8ff5f85" + dependencies: + clap "^1.0.9" + source-map "^0.5.3" + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + dependencies: + array-find-index "^1.0.1" + +d@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" + dependencies: + es5-ext "^0.10.9" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + dependencies: + assert-plus "^1.0.0" + +date-now@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" + +de-indent@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" + +debug@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" + dependencies: + ms "0.7.1" + +debug@2.6.7: + version "2.6.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.7.tgz#92bad1f6d05bbb6bba22cca88bcd0ec894c2861e" + dependencies: + ms "2.0.0" + +debug@^2.1.1, debug@^2.2.0, debug@^2.6.0: + version "2.6.8" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" + dependencies: + ms "2.0.0" + +decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + +deep-extend@~0.4.0: + version "0.4.2" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + +defined@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + +del@^2.0.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" + dependencies: + globby "^5.0.0" + is-path-cwd "^1.0.0" + is-path-in-cwd "^1.0.0" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + rimraf "^2.2.8" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +delegate@^3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.1.3.tgz#9a8251a777d7025faa55737bc3b071742127a9fd" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + +depd@1.1.0, depd@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3" + +des.js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + dependencies: + repeating "^2.0.0" + +diff-match-patch@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.0.tgz#1cc3c83a490d67f95d91e39f6ad1f2e086b63048" + +diffie-hellman@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e" + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +doctrine@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + +doctrine@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63" + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + +doiuse@^2.4.1: + version "2.6.0" + resolved "https://registry.yarnpkg.com/doiuse/-/doiuse-2.6.0.tgz#1892d10b61a9a356addbf2b614933e81f8bb3834" + dependencies: + browserslist "^1.1.1" + caniuse-db "^1.0.30000187" + css-rule-stream "^1.1.0" + duplexer2 "0.0.2" + jsonfilter "^1.1.2" + ldjson-stream "^1.2.1" + lodash "^4.0.0" + multimatch "^2.0.0" + postcss "^5.0.8" + source-map "^0.4.2" + through2 "^0.6.3" + yargs "^3.5.4" + +dom-converter@~0.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.1.4.tgz#a45ef5727b890c9bffe6d7c876e7b19cb0e17f3b" + dependencies: + utila "~0.3" + +dom-serializer@0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" + dependencies: + domelementtype "~1.1.1" + entities "~1.1.1" + +domain-browser@^1.1.1: + version "1.1.7" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" + +domelementtype@1, domelementtype@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" + +domelementtype@~1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" + +domhandler@2.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.1.0.tgz#d2646f5e57f6c3bab11cf6cb05d3c0acf7412594" + dependencies: + domelementtype "1" + +domhandler@^2.3.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.1.tgz#892e47000a99be55bbf3774ffea0561d8879c259" + dependencies: + domelementtype "1" + +domutils@1.1: + version "1.1.6" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.1.6.tgz#bddc3de099b9a2efacc51c623f28f416ecc57485" + dependencies: + domelementtype "1" + +domutils@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^1.5.1: + version "1.6.2" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.6.2.tgz#1958cc0b4c9426e9ed367fb1c8e854891b0fa3ff" + dependencies: + dom-serializer "0" + domelementtype "1" + +duplexer2@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" + dependencies: + readable-stream "~1.1.9" + +duplexer@^0.1.1, duplexer@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" + +ecc-jsbn@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + dependencies: + jsbn "~0.1.0" + +editorconfig@^0.13.2: + version "0.13.2" + resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.13.2.tgz#8e57926d9ee69ab6cb999f027c2171467acceb35" + dependencies: + bluebird "^3.0.5" + commander "^2.9.0" + lru-cache "^3.2.0" + sigmund "^1.0.1" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + +ejs@^2.5.6: + version "2.5.6" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.6.tgz#479636bfa3fe3b1debd52087f0acb204b4f19c88" + +electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.11: + version "1.3.13" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.13.tgz#1b3a5eace6e087bb5e257a100b0cbfe81b2891fc" + +elliptic@^6.0.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +emojis-list@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + +encodeurl@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" + +enhanced-resolve@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.1.0.tgz#9f4b626f577245edcf4b2ad83d86e17f4f421dec" + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.4.0" + object-assign "^4.0.1" + tapable "^0.2.5" + +enhanced-resolve@~0.9.0: + version "0.9.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz#4d6e689b3725f86090927ccc86cd9f1635b89e2e" + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.2.0" + tapable "^0.1.8" + +entities@^1.1.1, entities@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" + +errno@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d" + dependencies: + prr "~0.0.0" + +error-ex@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" + dependencies: + is-arrayish "^0.2.1" + +error-stack-parser@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.1.tgz#a3202b8fb03114aa9b40a0e3669e48b2b65a010a" + dependencies: + stackframe "^1.0.3" + +es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14: + version "0.10.22" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.22.tgz#1876c51f990769c112c781ea3ebe89f84fd39071" + dependencies: + es6-iterator "2" + es6-symbol "~3.1" + +es6-iterator@2, es6-iterator@^2.0.1, es6-iterator@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.1.tgz#8e319c9f0453bf575d374940a655920e59ca5512" + dependencies: + d "1" + es5-ext "^0.10.14" + es6-symbol "^3.1" + +es6-map@^0.1.3: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" + dependencies: + d "1" + es5-ext "~0.10.14" + es6-iterator "~2.0.1" + es6-set "~0.1.5" + es6-symbol "~3.1.1" + event-emitter "~0.3.5" + +es6-set@~0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" + dependencies: + d "1" + es5-ext "~0.10.14" + es6-iterator "~2.0.1" + es6-symbol "3.1.1" + event-emitter "~0.3.5" + +es6-symbol@3.1.1, es6-symbol@^3.1, es6-symbol@^3.1.1, es6-symbol@~3.1, es6-symbol@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" + dependencies: + d "1" + es5-ext "~0.10.14" + +es6-weak-map@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f" + dependencies: + d "1" + es5-ext "^0.10.14" + es6-iterator "^2.0.1" + es6-symbol "^3.1.1" + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +escope@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" + dependencies: + es6-map "^0.1.3" + es6-weak-map "^2.0.1" + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-config-airbnb-base@^11.1.3: + version "11.2.0" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-11.2.0.tgz#19a9dc4481a26f70904545ec040116876018f853" + +eslint-friendly-formatter@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/eslint-friendly-formatter/-/eslint-friendly-formatter-2.0.7.tgz#657f95a19af4989636afebb1cc9de6cebbd088ee" + dependencies: + chalk "^1.0.0" + extend "^3.0.0" + minimist "^1.2.0" + text-table "^0.2.0" + +eslint-import-resolver-node@^0.2.0: + version "0.2.3" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz#5add8106e8c928db2cba232bcd9efa846e3da16c" + dependencies: + debug "^2.2.0" + object-assign "^4.0.1" + resolve "^1.1.6" + +eslint-import-resolver-webpack@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.8.1.tgz#c7f8b4d5bd3c5b489457e5728c5db1c4ffbac9aa" + dependencies: + array-find "^1.0.0" + debug "^2.2.0" + enhanced-resolve "~0.9.0" + find-root "^0.1.1" + has "^1.0.1" + interpret "^1.0.0" + is-absolute "^0.2.3" + lodash.get "^3.7.0" + node-libs-browser "^1.0.0" + resolve "^1.2.0" + semver "^5.3.0" + +eslint-loader@^1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/eslint-loader/-/eslint-loader-1.7.1.tgz#50b158dd6272dcefb97e984254837f81a5802ce0" + dependencies: + find-cache-dir "^0.1.1" + loader-fs-cache "^1.0.0" + loader-utils "^1.0.2" + object-assign "^4.0.1" + object-hash "^1.1.4" + rimraf "^2.6.1" + +eslint-module-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.0.0.tgz#a6f8c21d901358759cdc35dbac1982ae1ee58bce" + dependencies: + debug "2.2.0" + pkg-dir "^1.0.0" + +eslint-plugin-html@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-html/-/eslint-plugin-html-2.0.3.tgz#7c89883ab0c85fa5d28b666a14a4e906aa90b897" + dependencies: + htmlparser2 "^3.8.2" + +eslint-plugin-import@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.3.0.tgz#37c801e0ada0e296cbdf20c3f393acb5b52af36b" + dependencies: + builtin-modules "^1.1.1" + contains-path "^0.1.0" + debug "^2.2.0" + doctrine "1.5.0" + eslint-import-resolver-node "^0.2.0" + eslint-module-utils "^2.0.0" + has "^1.0.1" + lodash.cond "^4.3.0" + minimatch "^3.0.3" + read-pkg-up "^2.0.0" + +eslint@^3.19.0: + version "3.19.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.19.0.tgz#c8fc6201c7f40dd08941b87c085767386a679acc" + dependencies: + babel-code-frame "^6.16.0" + chalk "^1.1.3" + concat-stream "^1.5.2" + debug "^2.1.1" + doctrine "^2.0.0" + escope "^3.6.0" + espree "^3.4.0" + esquery "^1.0.0" + estraverse "^4.2.0" + esutils "^2.0.2" + file-entry-cache "^2.0.0" + glob "^7.0.3" + globals "^9.14.0" + ignore "^3.2.0" + imurmurhash "^0.1.4" + inquirer "^0.12.0" + is-my-json-valid "^2.10.0" + is-resolvable "^1.0.0" + js-yaml "^3.5.1" + json-stable-stringify "^1.0.0" + levn "^0.3.0" + lodash "^4.0.0" + mkdirp "^0.5.0" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.1" + pluralize "^1.2.1" + progress "^1.1.8" + require-uncached "^1.0.2" + shelljs "^0.7.5" + strip-bom "^3.0.0" + strip-json-comments "~2.0.1" + table "^3.7.8" + text-table "~0.2.0" + user-home "^2.0.0" + +espree@^3.4.0: + version "3.4.3" + resolved "https://registry.yarnpkg.com/espree/-/espree-3.4.3.tgz#2910b5ccd49ce893c2ffffaab4fd8b3a31b82374" + dependencies: + acorn "^5.0.1" + acorn-jsx "^3.0.0" + +esprima@^2.6.0: + version "2.7.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" + +esprima@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + +esquery@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.0.tgz#cfba8b57d7fba93f17298a8a006a04cda13d80fa" + dependencies: + estraverse "^4.0.0" + +esrecurse@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.1.0.tgz#4713b6536adf7f2ac4f327d559e7756bff648220" + dependencies: + estraverse "~4.1.0" + object-assign "^4.0.1" + +estraverse@^4.0.0, estraverse@^4.1.1, estraverse@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + +estraverse@~4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.1.1.tgz#f6caca728933a850ef90661d0e17982ba47111a2" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +etag@~1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.0.tgz#6f631aef336d6c46362b51764044ce216be3c051" + +event-emitter@~0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + dependencies: + d "1" + es5-ext "~0.10.14" + +eventemitter3@1.x.x: + version "1.2.0" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508" + +events@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + +eventsource-polyfill@^0.9.6: + version "0.9.6" + resolved "https://registry.yarnpkg.com/eventsource-polyfill/-/eventsource-polyfill-0.9.6.tgz#10e0d187f111b167f28fdab918843ce7d818f13c" + +evp_bytestokey@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz#497b66ad9fef65cd7c08a6180824ba1476b66e53" + dependencies: + create-hash "^1.1.1" + +execall@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execall/-/execall-1.0.0.tgz#73d0904e395b3cab0658b08d09ec25307f29bb73" + dependencies: + clone-regexp "^1.0.0" + +exit-hook@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + dependencies: + is-posix-bracket "^0.1.0" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + dependencies: + fill-range "^2.1.0" + +express@^4.14.1, express@^4.15.2: + version "4.15.3" + resolved "https://registry.yarnpkg.com/express/-/express-4.15.3.tgz#bab65d0f03aa80c358408972fc700f916944b662" + dependencies: + accepts "~1.3.3" + array-flatten "1.1.1" + content-disposition "0.5.2" + content-type "~1.0.2" + cookie "0.3.1" + cookie-signature "1.0.6" + debug "2.6.7" + depd "~1.1.0" + encodeurl "~1.0.1" + escape-html "~1.0.3" + etag "~1.8.0" + finalhandler "~1.0.3" + fresh "0.5.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.1" + path-to-regexp "0.1.7" + proxy-addr "~1.1.4" + qs "6.4.0" + range-parser "~1.2.0" + send "0.15.3" + serve-static "1.12.3" + setprototypeof "1.0.3" + statuses "~1.3.1" + type-is "~1.6.15" + utils-merge "1.0.0" + vary "~1.1.1" + +extend@^3.0.0, extend@~3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" + +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + dependencies: + is-extglob "^1.0.0" + +extract-text-webpack-plugin@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/extract-text-webpack-plugin/-/extract-text-webpack-plugin-2.1.0.tgz#69315b885f876dbf96d3819f6a9f1cca7aebf159" + dependencies: + ajv "^4.11.2" + async "^2.1.2" + loader-utils "^1.0.2" + webpack-sources "^0.1.0" + +extsprintf@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" + +fast-levenshtein@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + +fastparse@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8" + +figures@^1.3.5: + version "1.7.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + dependencies: + escape-string-regexp "^1.0.5" + object-assign "^4.1.0" + +file-entry-cache@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" + dependencies: + flat-cache "^1.2.1" + object-assign "^4.0.1" + +file-loader@^0.11.1: + version "0.11.1" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-0.11.1.tgz#6b328ee1234a729e4e47d36375dd6d35c0e1db84" + dependencies: + loader-utils "^1.0.2" + +filename-regex@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" + +filesize@^3.5.9: + version "3.5.10" + resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.5.10.tgz#fc8fa23ddb4ef9e5e0ab6e1e64f679a24a56761f" + +fill-range@^2.1.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^1.1.3" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +finalhandler@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.3.tgz#ef47e77950e999780e86022a560e3217e0d0cc89" + dependencies: + debug "2.6.7" + encodeurl "~1.0.1" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.1" + statuses "~1.3.1" + unpipe "~1.0.0" + +find-cache-dir@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9" + dependencies: + commondir "^1.0.1" + mkdirp "^0.5.1" + pkg-dir "^1.0.0" + +find-root@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/find-root/-/find-root-0.1.2.tgz#98d2267cff1916ccaf2743b3a0eea81d79d7dcd1" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + dependencies: + locate-path "^2.0.0" + +flat-cache@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96" + dependencies: + circular-json "^0.3.1" + del "^2.0.2" + graceful-fs "^4.1.2" + write "^0.2.1" + +flatten@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" + +for-in@^0.1.3: + version "0.1.8" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1" + +for-in@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + +for-own@^0.1.3, for-own@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + dependencies: + for-in "^1.0.1" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + +form-data@~2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +forwarded@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363" + +fresh@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.0.tgz#f474ca5e6a9246d6fd8e0953cfa9b9c805afa78e" + +friendly-errors-webpack-plugin@^1.1.3: + version "1.6.1" + resolved "https://registry.yarnpkg.com/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.6.1.tgz#e32781c4722f546a06a9b5d7a7cfa28520375d70" + dependencies: + chalk "^1.1.3" + error-stack-parser "^2.0.0" + string-length "^1.0.1" + +fs-extra@^0.26.4: + version "0.26.7" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.26.7.tgz#9ae1fdd94897798edab76d0918cf42d0c3184fa9" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + klaw "^1.0.0" + path-is-absolute "^1.0.0" + rimraf "^2.2.8" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.1.tgz#f19fd28f43eeaf761680e519a203c4d0b3d31aff" + dependencies: + nan "^2.3.0" + node-pre-gyp "^0.6.29" + +fstream-ignore@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" + dependencies: + fstream "^1.0.0" + inherits "2" + minimatch "^3.0.0" + +fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: + version "1.0.11" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + +function-bind@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" + +gather-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gather-stream/-/gather-stream-1.0.0.tgz#b33994af457a8115700d410f317733cbe7a0904b" + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +gaze@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.2.tgz#847224677adb8870d679257ed3388fdb61e40105" + dependencies: + globule "^1.0.0" + +generate-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" + +generate-object-property@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" + dependencies: + is-property "^1.0.0" + +get-caller-file@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + +get-stdin@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + dependencies: + assert-plus "^1.0.0" + +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + dependencies: + is-glob "^2.0.0" + +glob@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@~7.1.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^9.0.0, globals@^9.14.0: + version "9.17.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.17.0.tgz#0c0ca696d9b9bb694d2e5470bd37777caad50286" + +globby@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" + dependencies: + array-union "^1.0.1" + arrify "^1.0.0" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +globby@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" + dependencies: + array-union "^1.0.1" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +globjoin@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43" + +globule@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/globule/-/globule-1.1.0.tgz#c49352e4dc183d85893ee825385eb994bb6df45f" + dependencies: + glob "~7.1.1" + lodash "~4.16.4" + minimatch "~3.0.2" + +good-listener@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" + dependencies: + delegate "^3.1.2" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +"graceful-readlink@>= 1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + +gzip-size@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-3.0.0.tgz#546188e9bdc337f673772f81660464b389dce520" + dependencies: + duplexer "^0.1.1" + +har-schema@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" + +har-validator@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" + dependencies: + ajv "^4.9.1" + har-schema "^1.0.5" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + +has@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" + dependencies: + function-bind "^1.0.2" + +hash-base@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-2.0.2.tgz#66ea1d856db4e8a5470cadf6fce23ae5244ef2e1" + dependencies: + inherits "^2.0.1" + +hash-sum@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.0.3.tgz#1332ff00156c0a0ffdd8236013d07b77a0451573" + dependencies: + inherits "^2.0.1" + +hawk@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" + dependencies: + boom "2.x.x" + cryptiles "2.x.x" + hoek "2.x.x" + sntp "1.x.x" + +he@1.1.x, he@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" + +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +hoek@2.x.x: + version "2.16.3" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" + +home-or-tmp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.1" + +hosted-git-info@^2.1.4: + version "2.4.2" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.4.2.tgz#0076b9f46a270506ddbaaea56496897460612a67" + +html-comment-regex@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e" + +html-entities@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" + +html-minifier@^3.2.3: + version "3.5.2" + resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.2.tgz#d73bc3ff448942408818ce609bf3fb0ea7ef4eb7" + dependencies: + camel-case "3.0.x" + clean-css "4.1.x" + commander "2.9.x" + he "1.1.x" + ncname "1.0.x" + param-case "2.1.x" + relateurl "0.2.x" + uglify-js "3.0.x" + +html-tags@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-1.1.1.tgz#869f43859f12d9bdc3892419e494a628aa1b204e" + +html-webpack-plugin@^2.28.0: + version "2.28.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-2.28.0.tgz#2e7863b57e5fd48fe263303e2ffc934c3064d009" + dependencies: + bluebird "^3.4.7" + html-minifier "^3.2.3" + loader-utils "^0.2.16" + lodash "^4.17.3" + pretty-error "^2.0.2" + toposort "^1.0.0" + +htmlparser2@^3.8.2, htmlparser2@^3.9.1: + version "3.9.2" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" + dependencies: + domelementtype "^1.3.0" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^2.0.2" + +htmlparser2@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.3.0.tgz#cc70d05a59f6542e43f0e685c982e14c924a9efe" + dependencies: + domelementtype "1" + domhandler "2.1" + domutils "1.1" + readable-stream "1.0" + +http-errors@~1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.1.tgz#5f8b8ed98aca545656bf572997387f904a722257" + dependencies: + depd "1.1.0" + inherits "2.0.3" + setprototypeof "1.0.3" + statuses ">= 1.3.1 < 2" + +http-proxy-middleware@^0.17.3: + version "0.17.4" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz#642e8848851d66f09d4f124912846dbaeb41b833" + dependencies: + http-proxy "^1.16.2" + is-glob "^3.1.0" + lodash "^4.17.2" + micromatch "^2.3.11" + +http-proxy@^1.16.2: + version "1.16.2" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.16.2.tgz#06dff292952bf64dbe8471fa9df73066d4f37742" + dependencies: + eventemitter3 "1.x.x" + requires-port "1.x.x" + +http-signature@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" + dependencies: + assert-plus "^0.2.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" + +icss-replace-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" + +icss-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-2.1.0.tgz#83f0a0ec378bf3246178b6c2ad9136f135b1c962" + dependencies: + postcss "^6.0.1" + +ieee754@^1.1.4: + version "1.1.8" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" + +ignore@^3.2.0: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + +in-publish@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51" + +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + dependencies: + repeating "^2.0.0" + +indexes-of@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" + +indexof@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + +ini@^1.3.4, ini@~1.3.0: + version "1.3.4" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" + +inquirer@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" + dependencies: + ansi-escapes "^1.1.0" + ansi-regex "^2.0.0" + chalk "^1.0.0" + cli-cursor "^1.0.1" + cli-width "^2.0.0" + figures "^1.3.5" + lodash "^4.3.0" + readline2 "^1.0.1" + run-async "^0.1.0" + rx-lite "^3.1.2" + string-width "^1.0.1" + strip-ansi "^3.0.0" + through "^2.3.6" + +interpret@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90" + +invariant@^2.2.0, invariant@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" + dependencies: + loose-envify "^1.0.0" + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + +ipaddr.js@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.3.0.tgz#1e03a52fdad83a8bbb2b25cbf4998b4cffcd3dec" + +irregular-plurals@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-1.2.0.tgz#38f299834ba8c00c30be9c554e137269752ff3ac" + +is-absolute-url@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" + +is-absolute@^0.2.3: + version "0.2.6" + resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-0.2.6.tgz#20de69f3db942ef2d87b9c2da36f172235b1b5eb" + dependencies: + is-relative "^0.2.1" + is-windows "^0.2.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.0.2, is-buffer@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc" + +is-builtin-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + dependencies: + builtin-modules "^1.0.0" + +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + +is-dotfile@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + +is-extglob@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + dependencies: + is-extglob "^1.0.0" + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + dependencies: + is-extglob "^2.1.0" + +is-my-json-valid@^2.10.0: + version "2.16.0" + resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz#f079dd9bfdae65ee2038aae8acbc86ab109e3693" + dependencies: + generate-function "^2.0.0" + generate-object-property "^1.1.0" + jsonpointer "^4.0.0" + xtend "^4.0.0" + +is-number@^2.0.2, is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + dependencies: + kind-of "^3.0.2" + +is-path-cwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" + +is-path-in-cwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc" + dependencies: + is-path-inside "^1.0.0" + +is-path-inside@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f" + dependencies: + path-is-inside "^1.0.1" + +is-plain-obj@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + +is-plain-object@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.3.tgz#c15bf3e4b66b62d72efaf2925848663ecbc619b6" + dependencies: + isobject "^3.0.0" + +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + +is-property@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + +is-relative@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-0.2.1.tgz#d27f4c7d516d175fb610db84bbeef23c3bc97aa5" + dependencies: + is-unc-path "^0.1.1" + +is-resolvable@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62" + dependencies: + tryit "^1.0.1" + +is-supported-regexp-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-supported-regexp-flag/-/is-supported-regexp-flag-1.0.0.tgz#8b520c85fae7a253382d4b02652e045576e13bb8" + +is-svg@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9" + dependencies: + html-comment-regex "^1.1.0" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + +is-unc-path@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-0.1.2.tgz#6ab053a72573c10250ff416a3814c35178af39b9" + dependencies: + unc-path-regex "^0.1.0" + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + +is-windows@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isobject@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.0.tgz#39565217f3661789e8a0a0c080d5f7e6bc46e1a0" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + +jodid25519@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967" + dependencies: + jsbn "~0.1.0" + +js-base64@^2.1.8, js-base64@^2.1.9: + version "2.1.9" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.1.9.tgz#f0e80ae039a4bd654b5f281fc93f04a914a7fcce" + +js-beautify@^1.6.3: + version "1.6.14" + resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.6.14.tgz#d3b8f7322d02b9277d58bd238264c327e58044cd" + dependencies: + config-chain "~1.1.5" + editorconfig "^0.13.2" + mkdirp "~0.5.0" + nopt "~3.0.1" + +js-tokens@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" + +js-yaml@^3.4.3, js-yaml@^3.5.1: + version "3.8.4" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.4.tgz#520b4564f86573ba96662af85a8cafa7b4b5a6f6" + dependencies: + argparse "^1.0.7" + esprima "^3.1.1" + +js-yaml@~3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80" + dependencies: + argparse "^1.0.7" + esprima "^2.6.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + +json-loader@^0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.4.tgz#8baa1365a632f58a3c46d20175fc6002c96e37de" + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + +json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + +json5@^0.5.0, json5@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + +jsonfile@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfilter@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/jsonfilter/-/jsonfilter-1.1.2.tgz#21ef7cedc75193813c75932e96a98be205ba5a11" + dependencies: + JSONStream "^0.8.4" + minimist "^1.1.0" + stream-combiner "^0.2.1" + through2 "^0.6.3" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + +jsonparse@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-0.0.5.tgz#330542ad3f0a654665b778f3eb2d9a9fa507ac64" + +jsonpointer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" + +jsprim@^1.2.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.0.tgz#a3b87e40298d8c380552d8cc7628a0bb95a22918" + dependencies: + assert-plus "1.0.0" + extsprintf "1.0.2" + json-schema "0.2.3" + verror "1.3.6" + +kind-of@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-2.0.1.tgz#018ec7a4ce7e3a86cb9141be519d24c8faa981b5" + dependencies: + is-buffer "^1.0.2" + +kind-of@^3.0.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + dependencies: + is-buffer "^1.1.5" + +klaw@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + optionalDependencies: + graceful-fs "^4.1.9" + +known-css-properties@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.0.7.tgz#9104343a2adfd8ef3b07bdee7a325e4d44ed9371" + +lazy-cache@^0.2.3: + version "0.2.7" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65" + +lazy-cache@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + dependencies: + invert-kv "^1.0.0" + +ldjson-stream@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ldjson-stream/-/ldjson-stream-1.2.1.tgz#91beceda5ac4ed2b17e649fb777e7abfa0189c2b" + dependencies: + split2 "^0.2.1" + through2 "^0.6.1" + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +linkify-it@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.0.3.tgz#d94a4648f9b1c179d64fa97291268bdb6ce9434f" + dependencies: + uc.micro "^1.0.1" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + +loader-fs-cache@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/loader-fs-cache/-/loader-fs-cache-1.0.1.tgz#56e0bf08bd9708b26a765b68509840c8dec9fdbc" + dependencies: + find-cache-dir "^0.1.1" + mkdirp "0.5.1" + +loader-runner@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" + +loader-utils@^0.2.15, loader-utils@^0.2.16: + version "0.2.17" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" + dependencies: + big.js "^3.1.3" + emojis-list "^2.0.0" + json5 "^0.5.0" + object-assign "^4.0.1" + +loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" + dependencies: + big.js "^3.1.3" + emojis-list "^2.0.0" + json5 "^0.5.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +lodash._baseget@^3.0.0: + version "3.7.2" + resolved "https://registry.yarnpkg.com/lodash._baseget/-/lodash._baseget-3.7.2.tgz#1b6ae1d5facf3c25532350a13c1197cb8bb674f4" + +lodash._topath@^3.0.0: + version "3.8.1" + resolved "https://registry.yarnpkg.com/lodash._topath/-/lodash._topath-3.8.1.tgz#3ec5e2606014f4cb97f755fe6914edd8bfc00eac" + dependencies: + lodash.isarray "^3.0.0" + +lodash.assign@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + +lodash.clonedeep@^4.3.2: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + +lodash.cond@^4.3.0: + version "4.5.2" + resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5" + +lodash.get@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-3.7.0.tgz#3ce68ae2c91683b281cc5394128303cbf75e691f" + dependencies: + lodash._baseget "^3.0.0" + lodash._topath "^3.0.0" + +lodash.isarray@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + +lodash.mergewith@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55" + +lodash.tail@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.tail/-/lodash.tail-4.1.1.tgz#d2333a36d9e7717c8ad2f7cacafec7c32b444664" + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + +lodash@^4.0.0, lodash@^4.1.0, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0: + version "4.17.4" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" + +lodash@~4.16.4: + version "4.16.6" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.16.6.tgz#d22c9ac660288f3843e16ba7d2b5d06cca27d777" + +log-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" + dependencies: + chalk "^1.0.0" + +longest@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" + +loose-envify@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" + dependencies: + js-tokens "^3.0.0" + +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +lower-case@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + +lru-cache@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-3.2.0.tgz#71789b3b7f5399bec8565dda38aa30d2a097efee" + dependencies: + pseudomap "^1.0.1" + +lru-cache@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.0.2.tgz#1d17679c069cda5d040991a09dbc2c0db377e55e" + dependencies: + pseudomap "^1.0.1" + yallist "^2.0.0" + +macaddress@^0.2.8: + version "0.2.8" + resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12" + +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + +markdown-it-abbr@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/markdown-it-abbr/-/markdown-it-abbr-1.0.4.tgz#d66b5364521cbb3dd8aa59dadfba2fb6865c8fd8" + +markdown-it-deflist@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/markdown-it-deflist/-/markdown-it-deflist-2.0.2.tgz#33253190400dfa5398bef9d0adacac0d0676bc58" + +markdown-it-emoji@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/markdown-it-emoji/-/markdown-it-emoji-1.3.0.tgz#903ae1a9968c3f17d4e142f115d4ec575e56d2cb" + +markdown-it-footnote@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/markdown-it-footnote/-/markdown-it-footnote-3.0.1.tgz#7f3730747cacc86e2fe0bf8a17a710f34791517a" + +markdown-it-mathjax@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/markdown-it-mathjax/-/markdown-it-mathjax-2.0.0.tgz#ae2b4f4c5c719a03f9e475c664f7b2685231d9e9" + +markdown-it-pandoc-renderer@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/markdown-it-pandoc-renderer/-/markdown-it-pandoc-renderer-1.1.3.tgz#77f1a5b488c5460ab1e1dcbcfcc07d12674f7a3a" + +markdown-it-sub@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/markdown-it-sub/-/markdown-it-sub-1.0.0.tgz#375fd6026eae7ddcb012497f6411195ea1e3afe8" + +markdown-it-sup@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/markdown-it-sup/-/markdown-it-sup-1.0.0.tgz#cb9c9ff91a5255ac08f3fd3d63286e15df0a1fc3" + +markdown-it@^8.3.1: + version "8.3.1" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.3.1.tgz#2f4b622948ccdc193d66f3ca2d43125ac4ac7323" + dependencies: + argparse "^1.0.7" + entities "~1.1.1" + linkify-it "^2.0.0" + mdurl "^1.0.1" + uc.micro "^1.0.3" + +math-expression-evaluator@^1.2.14: + version "1.2.17" + resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac" + +mdurl@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + +memory-fs@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.2.0.tgz#f2bb25368bc121e391c2520de92969caee0a0290" + +memory-fs@^0.4.0, memory-fs@~0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +meow@^3.3.0, meow@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + +micromatch@^2.1.5, micromatch@^2.3.11: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +miller-rabin@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.0.tgz#4a62fb1d42933c05583982f4c716f6fb9e6c6d3d" + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@~1.27.0: + version "1.27.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1" + +mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.7: + version "2.1.15" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed" + dependencies: + mime-db "~1.27.0" + +mime@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" + +mime@1.3.x, mime@^1.3.4: + version "1.3.6" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.6.tgz#591d84d3653a6b0b4a3b9df8de5aa8108e72e5e0" + +mimic-fn@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" + +minimalistic-assert@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + +"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +mixin-object@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e" + dependencies: + for-in "^0.1.3" + is-extendable "^0.1.1" + +mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +ms@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +multimatch@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-2.1.0.tgz#9c7906a22fb4c02919e2f5f75161b4cdbd4b2a2b" + dependencies: + array-differ "^1.0.0" + array-union "^1.0.1" + arrify "^1.0.0" + minimatch "^3.0.0" + +mute-stream@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" + +nan@^2.3.0, nan@^2.3.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + +ncname@1.0.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/ncname/-/ncname-1.0.0.tgz#5b57ad18b1ca092864ef62b0b1ed8194f383b71c" + dependencies: + xml-char-classes "^1.0.0" + +negotiator@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" + +no-case@^2.2.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.1.tgz#7aeba1c73a52184265554b7dc03baf720df80081" + dependencies: + lower-case "^1.1.1" + +node-dir@^0.1.10: + version "0.1.17" + resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" + dependencies: + minimatch "^3.0.2" + +node-gyp@^3.3.1: + version "3.6.2" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.6.2.tgz#9bfbe54562286284838e750eac05295853fa1c60" + dependencies: + fstream "^1.0.0" + glob "^7.0.3" + graceful-fs "^4.1.2" + minimatch "^3.0.2" + mkdirp "^0.5.0" + nopt "2 || 3" + npmlog "0 || 1 || 2 || 3 || 4" + osenv "0" + request "2" + rimraf "2" + semver "~5.3.0" + tar "^2.0.0" + which "1" + +node-libs-browser@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-1.1.1.tgz#2a38243abedd7dffcd07a97c9aca5668975a6fea" + dependencies: + assert "^1.1.1" + browserify-zlib "^0.1.4" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^1.0.0" + https-browserify "0.0.1" + os-browserify "^0.2.0" + path-browserify "0.0.0" + process "^0.11.0" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.0.5" + stream-browserify "^2.0.1" + stream-http "^2.3.1" + string_decoder "^0.10.25" + timers-browserify "^1.4.2" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.10.3" + vm-browserify "0.0.4" + +node-libs-browser@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.0.0.tgz#a3a59ec97024985b46e958379646f96c4b616646" + dependencies: + assert "^1.1.1" + browserify-zlib "^0.1.4" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^1.0.0" + https-browserify "0.0.1" + os-browserify "^0.2.0" + path-browserify "0.0.0" + process "^0.11.0" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.0.5" + stream-browserify "^2.0.1" + stream-http "^2.3.1" + string_decoder "^0.10.25" + timers-browserify "^2.0.2" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.10.3" + vm-browserify "0.0.4" + +node-pre-gyp@^0.6.29: + version "0.6.36" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz#db604112cb74e0d477554e9b505b17abddfab786" + dependencies: + mkdirp "^0.5.1" + nopt "^4.0.1" + npmlog "^4.0.2" + rc "^1.1.7" + request "^2.81.0" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^2.2.1" + tar-pack "^3.4.0" + +node-sass@^4.5.3: + version "4.5.3" + resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.5.3.tgz#d09c9d1179641239d1b97ffc6231fdcec53e1568" + dependencies: + async-foreach "^0.1.3" + chalk "^1.1.1" + cross-spawn "^3.0.0" + gaze "^1.0.0" + get-stdin "^4.0.1" + glob "^7.0.3" + in-publish "^2.0.0" + lodash.assign "^4.2.0" + lodash.clonedeep "^4.3.2" + lodash.mergewith "^4.6.0" + meow "^3.7.0" + mkdirp "^0.5.1" + nan "^2.3.2" + node-gyp "^3.3.1" + npmlog "^4.0.0" + request "^2.79.0" + sass-graph "^2.1.1" + stdout-stream "^1.4.0" + +"nopt@2 || 3", nopt@~3.0.1: + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + dependencies: + abbrev "1" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: + version "2.3.8" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.8.tgz#d819eda2a9dedbd1ffa563ea4071d936782295bb" + dependencies: + hosted-git-info "^2.1.4" + is-builtin-module "^1.0.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + +normalize-scss@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/normalize-scss/-/normalize-scss-7.0.0.tgz#92eaac6554cc376336c06682a0d6a2099bbb0889" + +normalize-selector@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/normalize-selector/-/normalize-selector-0.2.0.tgz#d0b145eb691189c63a78d201dc4fdb1293ef0c03" + +normalize-url@^1.4.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" + dependencies: + object-assign "^4.0.1" + prepend-http "^1.0.0" + query-string "^4.1.0" + sort-keys "^1.0.0" + +"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2: + version "4.1.0" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.0.tgz#dc59bee85f64f00ed424efb2af0783df25d1c0b5" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +nth-check@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" + dependencies: + boolbase "~1.0.0" + +num2fraction@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +oauth-sign@~0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" + +object-assign@^4.0.1, object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object-hash@^1.1.4: + version "1.1.8" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.1.8.tgz#28a659cf987d96a4dabe7860289f3b5326c4a03c" + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.3: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +onecolor@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/onecolor/-/onecolor-3.0.4.tgz#75a46f80da6c7aaa5b4daae17a47198bd9652494" + +onetime@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + dependencies: + mimic-fn "^1.0.0" + +opener@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8" + +opn@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95" + dependencies: + object-assign "^4.0.1" + pinkie-promise "^2.0.0" + +optimize-css-assets-webpack-plugin@^1.3.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-1.3.2.tgz#eb27456e21eefbd8080f31e8368c59684e585a2c" + dependencies: + cssnano "^3.4.0" + underscore "^1.8.3" + webpack-sources "^0.1.0" + +optionator@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + +ora@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-1.2.0.tgz#32fb3183500efe83f5ea89101785f0ee6060fec9" + dependencies: + chalk "^1.1.1" + cli-cursor "^2.1.0" + cli-spinners "^1.0.0" + log-symbols "^1.0.2" + +os-browserify@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.2.1.tgz#63fc4ccee5d2d7763d26bbf8601078e6c2e0044f" + +os-homedir@^1.0.0, os-homedir@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + dependencies: + lcid "^1.0.0" + +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +osenv@0, osenv@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +p-limit@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + dependencies: + p-limit "^1.1.0" + +pako@~0.2.0: + version "0.2.9" + resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" + +param-case@2.1.x: + version "2.1.1" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" + dependencies: + no-case "^2.2.0" + +parse-asn1@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.0.tgz#37c4f9b7ed3ab65c74817b5f2480937fbf97c712" + dependencies: + asn1.js "^4.0.0" + browserify-aes "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + dependencies: + error-ex "^1.2.0" + +parseurl@~1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56" + +path-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-is-inside@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + +path-parse@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + dependencies: + pify "^2.0.0" + +pbkdf2@^3.0.3: + version "3.0.12" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.12.tgz#be36785c5067ea48d806ff923288c5f750b6b8a2" + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +performance-now@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" + +pify@^2.0.0, pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + +pipetteur@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/pipetteur/-/pipetteur-2.0.3.tgz#1955760959e8d1a11cb2a50ec83eec470633e49f" + dependencies: + onecolor "^3.0.4" + synesthesia "^1.0.1" + +pkg-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" + dependencies: + find-up "^1.0.0" + +plur@^2.0.0, plur@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/plur/-/plur-2.1.2.tgz#7482452c1a0f508e3e344eaec312c91c29dc655a" + dependencies: + irregular-plurals "^1.0.0" + +pluralize@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" + +postcss-calc@^5.2.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e" + dependencies: + postcss "^5.0.2" + postcss-message-helpers "^2.0.0" + reduce-css-calc "^1.2.6" + +postcss-colormin@^2.1.8: + version "2.2.2" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.2.tgz#6631417d5f0e909a3d7ec26b24c8a8d1e4f96e4b" + dependencies: + colormin "^1.0.5" + postcss "^5.0.13" + postcss-value-parser "^3.2.3" + +postcss-convert-values@^2.3.4: + version "2.6.1" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz#bbd8593c5c1fd2e3d1c322bb925dcae8dae4d62d" + dependencies: + postcss "^5.0.11" + postcss-value-parser "^3.1.2" + +postcss-discard-comments@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d" + dependencies: + postcss "^5.0.14" + +postcss-discard-duplicates@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz#b9abf27b88ac188158a5eb12abcae20263b91932" + dependencies: + postcss "^5.0.4" + +postcss-discard-empty@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz#d2b4bd9d5ced5ebd8dcade7640c7d7cd7f4f92b5" + dependencies: + postcss "^5.0.14" + +postcss-discard-overridden@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz#8b1eaf554f686fb288cd874c55667b0aa3668d58" + dependencies: + postcss "^5.0.16" + +postcss-discard-unused@^2.2.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz#bce30b2cc591ffc634322b5fb3464b6d934f4433" + dependencies: + postcss "^5.0.14" + uniqs "^2.0.0" + +postcss-filter-plugins@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz#6d85862534d735ac420e4a85806e1f5d4286d84c" + dependencies: + postcss "^5.0.4" + uniqid "^4.0.0" + +postcss-less@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/postcss-less/-/postcss-less-0.14.0.tgz#c631b089c6cce422b9a10f3a958d2bedd3819324" + dependencies: + postcss "^5.0.21" + +postcss-load-config@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-1.2.0.tgz#539e9afc9ddc8620121ebf9d8c3673e0ce50d28a" + dependencies: + cosmiconfig "^2.1.0" + object-assign "^4.1.0" + postcss-load-options "^1.2.0" + postcss-load-plugins "^2.3.0" + +postcss-load-options@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postcss-load-options/-/postcss-load-options-1.2.0.tgz#b098b1559ddac2df04bc0bb375f99a5cfe2b6d8c" + dependencies: + cosmiconfig "^2.1.0" + object-assign "^4.1.0" + +postcss-load-plugins@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz#745768116599aca2f009fad426b00175049d8d92" + dependencies: + cosmiconfig "^2.1.1" + object-assign "^4.1.0" + +postcss-media-query-parser@^0.2.0: + version "0.2.3" + resolved "https://registry.yarnpkg.com/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz#27b39c6f4d94f81b1a73b8f76351c609e5cef244" + +postcss-merge-idents@^2.1.5: + version "2.1.7" + resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270" + dependencies: + has "^1.0.1" + postcss "^5.0.10" + postcss-value-parser "^3.1.1" + +postcss-merge-longhand@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz#23d90cd127b0a77994915332739034a1a4f3d658" + dependencies: + postcss "^5.0.4" + +postcss-merge-rules@^2.0.3: + version "2.1.2" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz#d1df5dfaa7b1acc3be553f0e9e10e87c61b5f721" + dependencies: + browserslist "^1.5.2" + caniuse-api "^1.5.2" + postcss "^5.0.4" + postcss-selector-parser "^2.2.2" + vendors "^1.0.0" + +postcss-message-helpers@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz#a4f2f4fab6e4fe002f0aed000478cdf52f9ba60e" + +postcss-minify-font-values@^1.0.2: + version "1.0.5" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz#4b58edb56641eba7c8474ab3526cafd7bbdecb69" + dependencies: + object-assign "^4.0.1" + postcss "^5.0.4" + postcss-value-parser "^3.0.2" + +postcss-minify-gradients@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz#5dbda11373703f83cfb4a3ea3881d8d75ff5e6e1" + dependencies: + postcss "^5.0.12" + postcss-value-parser "^3.3.0" + +postcss-minify-params@^1.0.4: + version "1.2.2" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz#ad2ce071373b943b3d930a3fa59a358c28d6f1f3" + dependencies: + alphanum-sort "^1.0.1" + postcss "^5.0.2" + postcss-value-parser "^3.0.2" + uniqs "^2.0.0" + +postcss-minify-selectors@^2.0.4: + version "2.1.1" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz#b2c6a98c0072cf91b932d1a496508114311735bf" + dependencies: + alphanum-sort "^1.0.2" + has "^1.0.1" + postcss "^5.0.14" + postcss-selector-parser "^2.0.0" + +postcss-modules-extract-imports@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz#66140ecece38ef06bf0d3e355d69bf59d141ea85" + dependencies: + postcss "^6.0.1" + +postcss-modules-local-by-default@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069" + dependencies: + css-selector-tokenizer "^0.7.0" + postcss "^6.0.1" + +postcss-modules-scope@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90" + dependencies: + css-selector-tokenizer "^0.7.0" + postcss "^6.0.1" + +postcss-modules-values@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20" + dependencies: + icss-replace-symbols "^1.1.0" + postcss "^6.0.1" + +postcss-normalize-charset@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz#ef9ee71212d7fe759c78ed162f61ed62b5cb93f1" + dependencies: + postcss "^5.0.5" + +postcss-normalize-url@^3.0.7: + version "3.0.8" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz#108f74b3f2fcdaf891a2ffa3ea4592279fc78222" + dependencies: + is-absolute-url "^2.0.0" + normalize-url "^1.4.0" + postcss "^5.0.14" + postcss-value-parser "^3.2.3" + +postcss-ordered-values@^2.1.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz#eec6c2a67b6c412a8db2042e77fe8da43f95c11d" + dependencies: + postcss "^5.0.4" + postcss-value-parser "^3.0.1" + +postcss-reduce-idents@^2.2.2: + version "2.4.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz#c2c6d20cc958284f6abfbe63f7609bf409059ad3" + dependencies: + postcss "^5.0.4" + postcss-value-parser "^3.0.2" + +postcss-reduce-initial@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz#68f80695f045d08263a879ad240df8dd64f644ea" + dependencies: + postcss "^5.0.4" + +postcss-reduce-transforms@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz#ff76f4d8212437b31c298a42d2e1444025771ae1" + dependencies: + has "^1.0.1" + postcss "^5.0.8" + postcss-value-parser "^3.0.1" + +postcss-reporter@^1.2.1, postcss-reporter@^1.3.3: + version "1.4.1" + resolved "https://registry.yarnpkg.com/postcss-reporter/-/postcss-reporter-1.4.1.tgz#c136f0a5b161915f379dd3765c61075f7e7b9af2" + dependencies: + chalk "^1.0.0" + lodash "^4.1.0" + log-symbols "^1.0.2" + postcss "^5.0.0" + +postcss-reporter@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-reporter/-/postcss-reporter-3.0.0.tgz#09ea0f37a444c5693878606e09b018ebeff7cf8f" + dependencies: + chalk "^1.0.0" + lodash "^4.1.0" + log-symbols "^1.0.2" + postcss "^5.0.0" + +postcss-resolve-nested-selector@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz#29ccbc7c37dedfac304e9fff0bf1596b3f6a0e4e" + +postcss-scss@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-0.4.1.tgz#ad771b81f0f72f5f4845d08aa60f93557653d54c" + dependencies: + postcss "^5.2.13" + +postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.1.1, postcss-selector-parser@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz#f9437788606c3c9acee16ffe8d8b16297f27bb90" + dependencies: + flatten "^1.0.2" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-svgo@^2.1.1: + version "2.1.6" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d" + dependencies: + is-svg "^2.0.0" + postcss "^5.0.14" + postcss-value-parser "^3.2.3" + svgo "^0.7.0" + +postcss-unique-selectors@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz#981d57d29ddcb33e7b1dfe1fd43b8649f933ca1d" + dependencies: + alphanum-sort "^1.0.1" + postcss "^5.0.4" + uniqs "^2.0.0" + +postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15" + +postcss-zindex@^2.0.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz#d2109ddc055b91af67fc4cb3b025946639d2af22" + dependencies: + has "^1.0.1" + postcss "^5.0.4" + uniqs "^2.0.0" + +postcss@^5.0.0, postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.18, postcss@^5.0.2, postcss@^5.0.20, postcss@^5.0.21, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.6, postcss@^5.0.8, postcss@^5.2.13, postcss@^5.2.16, postcss@^5.2.4: + version "5.2.17" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.17.tgz#cf4f597b864d65c8a492b2eabe9d706c879c388b" + dependencies: + chalk "^1.1.3" + js-base64 "^2.1.9" + source-map "^0.5.6" + supports-color "^3.2.3" + +postcss@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.1.tgz#000dbd1f8eef217aa368b9a212c5fc40b2a8f3f2" + dependencies: + chalk "^1.1.3" + source-map "^0.5.6" + supports-color "^3.2.3" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + +prepend-http@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + +pretty-error@^2.0.2: + version "2.1.0" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.0.tgz#87f4e9d706a24c87d6cbee9fabec001fcf8c75d8" + dependencies: + renderkid "^2.0.1" + utila "~0.4" + +prismjs@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.6.0.tgz#118d95fb7a66dba2272e343b345f5236659db365" + optionalDependencies: + clipboard "^1.5.5" + +private@^0.1.6: + version "0.1.7" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1" + +process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + +process@^0.11.0, process@~0.11.0: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + +progress@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" + +proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + +proxy-addr@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.4.tgz#27e545f6960a44a627d9b44467e35c1b6b4ce2f3" + dependencies: + forwarded "~0.1.0" + ipaddr.js "1.3.0" + +prr@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a" + +pseudomap@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + +public-encrypt@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6" + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + +punycode@^1.2.4, punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +q@^1.1.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.0.tgz#dd01bac9d06d30e6f219aecb8253ee9ebdc308f1" + +qs@6.4.0, qs@~6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" + +query-string@^4.1.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" + dependencies: + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +querystring-es3@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + +querystring@0.2.0, querystring@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + +ramda@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.23.0.tgz#ccd13fff73497a93974e3e86327bfd87bd6e8e2b" + +randomatic@^1.1.3: + version "1.1.6" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.6.tgz#110dcabff397e9dcff7c0789ccc0a49adf1ec5bb" + dependencies: + is-number "^2.0.2" + kind-of "^3.0.2" + +randombytes@^2.0.0, randombytes@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.3.tgz#674c99760901c3c4112771a31e521dc349cc09ec" + +range-parser@^1.0.3, range-parser@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + +raw-loader@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa" + +rc@^1.1.7: + version "1.2.1" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95" + dependencies: + deep-extend "~0.4.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +read-file-stdin@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/read-file-stdin/-/read-file-stdin-0.2.1.tgz#25eccff3a153b6809afacb23ee15387db9e0ee61" + dependencies: + gather-stream "^1.0.0" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + +readable-stream@1.0, "readable-stream@>=1.0.33-1 <1.1.0-0": + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^1.0.33, readable-stream@~1.1.9: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.2.2, readable-stream@^2.2.6: + version "2.2.9" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.9.tgz#cf78ec6f4a6d1eb43d26488cac97f042e74b7fc8" + dependencies: + buffer-shims "~1.0.0" + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + string_decoder "~1.0.0" + util-deprecate "~1.0.1" + +readdirp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" + dependencies: + graceful-fs "^4.1.2" + minimatch "^3.0.2" + readable-stream "^2.0.2" + set-immediate-shim "^1.0.1" + +readline2@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + mute-stream "0.0.5" + +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + dependencies: + resolve "^1.1.6" + +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + +reduce-css-calc@^1.2.6: + version "1.3.0" + resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" + dependencies: + balanced-match "^0.4.2" + math-expression-evaluator "^1.2.14" + reduce-function-call "^1.0.1" + +reduce-function-call@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.2.tgz#5a200bf92e0e37751752fe45b0ab330fd4b6be99" + dependencies: + balanced-match "^0.4.2" + +regenerate@^1.2.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260" + +regenerator-runtime@^0.10.0: + version "0.10.5" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" + +regenerator-transform@0.9.11: + version "0.9.11" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.9.11.tgz#3a7d067520cb7b7176769eb5ff868691befe1283" + dependencies: + babel-runtime "^6.18.0" + babel-types "^6.19.0" + private "^0.1.6" + +regex-cache@^0.4.2: + version "0.4.3" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" + dependencies: + is-equal-shallow "^0.1.3" + is-primitive "^2.0.0" + +regexpu-core@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +regexpu-core@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +regjsgen@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + +regjsparser@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + dependencies: + jsesc "~0.5.0" + +relateurl@0.2.x: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + +remove-trailing-separator@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.0.1.tgz#615ebb96af559552d4bf4057c8436d486ab63cc4" + +renderkid@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.1.tgz#898cabfc8bede4b7b91135a3ffd323e58c0db319" + dependencies: + css-select "^1.1.0" + dom-converter "~0.1" + htmlparser2 "~3.3.0" + strip-ansi "^3.0.0" + utila "~0.3" + +repeat-element@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + +repeat-string@^1.5.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + dependencies: + is-finite "^1.0.0" + +request@2, request@^2.79.0, request@^2.81.0: + version "2.81.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~4.2.1" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + performance-now "^0.2.0" + qs "~6.4.0" + safe-buffer "^5.0.1" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "^0.6.0" + uuid "^3.0.0" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + +require-from-string@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418" + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + +require-uncached@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" + dependencies: + caller-path "^0.1.0" + resolve-from "^1.0.0" + +requires-port@1.x.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + +resolve-from@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" + +resolve-from@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57" + +resolve@^1.1.6, resolve@^1.2.0, resolve@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.3.tgz#655907c3469a8680dc2de3a275a8fdd69691f0e5" + dependencies: + path-parse "^1.0.5" + +restore-cursor@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" + dependencies: + exit-hook "^1.0.0" + onetime "^1.0.0" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +right-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" + dependencies: + align-text "^0.1.1" + +rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.6.0, rimraf@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" + dependencies: + glob "^7.0.5" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.1.tgz#0f4584295c53a3628af7e6d79aca21ce57d1c6e7" + dependencies: + hash-base "^2.0.0" + inherits "^2.0.1" + +run-async@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" + dependencies: + once "^1.3.0" + +rx-lite@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" + +safe-buffer@^5.0.1, safe-buffer@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" + +sass-graph@^2.1.1: + version "2.2.4" + resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" + dependencies: + glob "^7.0.0" + lodash "^4.0.0" + scss-tokenizer "^0.2.3" + yargs "^7.0.0" + +sass-loader@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-6.0.5.tgz#a847910f36442aa56c5985879d54eb519e24a328" + dependencies: + async "^2.1.5" + clone-deep "^0.2.4" + loader-utils "^1.0.1" + lodash.tail "^4.1.1" + pify "^2.3.0" + +sax@~1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.2.tgz#fd8631a23bc7826bef5d871bdb87378c95647828" + +scss-tokenizer@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1" + dependencies: + js-base64 "^2.1.8" + source-map "^0.4.2" + +select@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" + +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + +send@0.15.3: + version "0.15.3" + resolved "https://registry.yarnpkg.com/send/-/send-0.15.3.tgz#5013f9f99023df50d1bd9892c19e3defd1d53309" + dependencies: + debug "2.6.7" + depd "~1.1.0" + destroy "~1.0.4" + encodeurl "~1.0.1" + escape-html "~1.0.3" + etag "~1.8.0" + fresh "0.5.0" + http-errors "~1.6.1" + mime "1.3.4" + ms "2.0.0" + on-finished "~2.3.0" + range-parser "~1.2.0" + statuses "~1.3.1" + +serve-static@1.12.3: + version "1.12.3" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.12.3.tgz#9f4ba19e2f3030c547f8af99107838ec38d5b1e2" + dependencies: + encodeurl "~1.0.1" + escape-html "~1.0.3" + parseurl "~1.3.1" + send "0.15.3" + +set-blocking@^2.0.0, set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + +setimmediate@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + +setprototypeof@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.8" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f" + dependencies: + inherits "^2.0.1" + +shallow-clone@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-0.1.2.tgz#5909e874ba77106d73ac414cfec1ffca87d97060" + dependencies: + is-extendable "^0.1.1" + kind-of "^2.0.1" + lazy-cache "^0.2.3" + mixin-object "^2.0.1" + +shelljs@^0.7.5, shelljs@^0.7.6: + version "0.7.7" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.7.tgz#b2f5c77ef97148f4b4f6e22682e10bba8667cff1" + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + +sigmund@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + +slice-ansi@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" + +sntp@1.x.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" + dependencies: + hoek "2.x.x" + +sort-keys@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" + dependencies: + is-plain-obj "^1.0.0" + +source-list-map@^0.1.7, source-list-map@~0.1.7: + version "0.1.8" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106" + +source-list-map@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-1.1.2.tgz#9889019d1024cce55cdc069498337ef6186a11a1" + +source-map-support@^0.4.2: + version "0.4.15" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7ec2362a193056fef8d3b1" + dependencies: + source-map "^0.5.6" + +source-map@0.5.x, source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.3: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + +source-map@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" + dependencies: + amdefine ">=0.0.4" + +spdx-correct@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" + dependencies: + spdx-license-ids "^1.0.2" + +spdx-expression-parse@~1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c" + +spdx-license-ids@^1.0.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" + +specificity@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/specificity/-/specificity-0.3.0.tgz#332472d4e5eb5af20821171933998a6bc3b1ce6f" + +split2@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/split2/-/split2-0.2.1.tgz#02ddac9adc03ec0bb78c1282ec079ca6e85ae900" + dependencies: + through2 "~0.6.1" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + +sshpk@^1.7.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.0.tgz#ff2a3e4fd04497555fed97b39a0fd82fafb3a33c" + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + dashdash "^1.12.0" + getpass "^0.1.1" + optionalDependencies: + bcrypt-pbkdf "^1.0.0" + ecc-jsbn "~0.1.1" + jodid25519 "^1.0.0" + jsbn "~0.1.0" + tweetnacl "~0.14.0" + +stackframe@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.0.3.tgz#fe64ab20b170e4ce49044b126c119dfa0e5dc7cc" + +"statuses@>= 1.3.1 < 2", statuses@~1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" + +stdout-stream@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.0.tgz#a2c7c8587e54d9427ea9edb3ac3f2cd522df378b" + dependencies: + readable-stream "^2.0.1" + +stream-browserify@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" + dependencies: + inherits "~2.0.1" + readable-stream "^2.0.2" + +stream-combiner@^0.2.1: + version "0.2.2" + resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.2.2.tgz#aec8cbac177b56b6f4fa479ced8c1912cee52858" + dependencies: + duplexer "~0.1.1" + through "~2.3.4" + +stream-http@^2.3.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.1.tgz#546a51741ad5a6b07e9e31b0b10441a917df528a" + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.2.6" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + +string-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-1.0.1.tgz#56970fb1c38558e9e70b728bf3de269ac45adfac" + dependencies: + strip-ansi "^3.0.0" + +string-width@^1.0.1, string-width@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string-width@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.0.0.tgz#635c5436cc72a6e0c387ceca278d4e2eec52687e" + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^3.0.0" + +string_decoder@^0.10.25, string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + +string_decoder@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.1.tgz#62e200f039955a6810d8df0a33ffc0f013662d98" + dependencies: + safe-buffer "^5.0.1" + +stringstream@~0.0.4: + version "0.0.5" + resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + dependencies: + is-utf8 "^0.2.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + dependencies: + get-stdin "^4.0.1" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +style-search@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902" + +stylehacks@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-2.3.2.tgz#64c83e0438a68c9edf449e8c552a7d9ab6009b0b" + dependencies: + browserslist "^1.1.3" + chalk "^1.1.1" + log-symbols "^1.0.2" + minimist "^1.2.0" + plur "^2.1.2" + postcss "^5.0.18" + postcss-reporter "^1.3.3" + postcss-selector-parser "^2.0.0" + read-file-stdin "^0.2.1" + text-table "^0.2.0" + write-file-stdout "0.0.2" + +stylelint-config-standard@^16.0.0: + version "16.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-standard/-/stylelint-config-standard-16.0.0.tgz#bb7387bff1d7dd7186a52b3ebf885b2405d691bf" + +stylelint-processor-html@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stylelint-processor-html/-/stylelint-processor-html-1.0.0.tgz#6892b6b2855a45f0291cd845191d6908130a2918" + dependencies: + htmlparser2 "^3.9.1" + +stylelint-webpack-plugin@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/stylelint-webpack-plugin/-/stylelint-webpack-plugin-0.7.0.tgz#48d6683fddf93b758e194c4468fada4771230c39" + dependencies: + arrify "^1.0.1" + chalk "^1.1.3" + minimatch "^3.0.3" + object-assign "^4.1.0" + ramda "^0.23.0" + stylelint "^7.7.0" + +stylelint@^7.7.0: + version "7.10.1" + resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-7.10.1.tgz#209a7ce5e781fc2a62489fbb31ec0201ec675db2" + dependencies: + autoprefixer "^6.0.0" + balanced-match "^0.4.0" + chalk "^1.1.1" + colorguard "^1.2.0" + cosmiconfig "^2.1.1" + debug "^2.6.0" + doiuse "^2.4.1" + execall "^1.0.0" + file-entry-cache "^2.0.0" + get-stdin "^5.0.0" + globby "^6.0.0" + globjoin "^0.1.4" + html-tags "^1.1.1" + ignore "^3.2.0" + imurmurhash "^0.1.4" + known-css-properties "^0.0.7" + lodash "^4.17.4" + log-symbols "^1.0.2" + meow "^3.3.0" + micromatch "^2.3.11" + normalize-selector "^0.2.0" + postcss "^5.0.20" + postcss-less "^0.14.0" + postcss-media-query-parser "^0.2.0" + postcss-reporter "^3.0.0" + postcss-resolve-nested-selector "^0.1.1" + postcss-scss "^0.4.0" + postcss-selector-parser "^2.1.1" + postcss-value-parser "^3.1.1" + resolve-from "^2.0.0" + specificity "^0.3.0" + string-width "^2.0.0" + style-search "^0.1.0" + stylehacks "^2.3.2" + sugarss "^0.2.0" + svg-tags "^1.0.0" + table "^4.0.1" + +sugarss@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/sugarss/-/sugarss-0.2.0.tgz#ac34237563327c6ff897b64742bf6aec190ad39e" + dependencies: + postcss "^5.2.4" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +supports-color@^3.1.0, supports-color@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + dependencies: + has-flag "^1.0.0" + +svg-tags@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" + +svgo@^0.7.0: + version "0.7.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5" + dependencies: + coa "~1.0.1" + colors "~1.1.2" + csso "~2.3.1" + js-yaml "~3.7.0" + mkdirp "~0.5.1" + sax "~1.2.1" + whet.extend "~0.9.9" + +synesthesia@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/synesthesia/-/synesthesia-1.0.1.tgz#5ef95ea548c0d5c6e6f9bb4b0d0731dff864a777" + dependencies: + css-color-names "0.0.3" + +table@^3.7.8: + version "3.8.3" + resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" + dependencies: + ajv "^4.7.0" + ajv-keywords "^1.0.0" + chalk "^1.1.1" + lodash "^4.0.0" + slice-ansi "0.0.4" + string-width "^2.0.0" + +table@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/table/-/table-4.0.1.tgz#a8116c133fac2c61f4a420ab6cdf5c4d61f0e435" + dependencies: + ajv "^4.7.0" + ajv-keywords "^1.0.0" + chalk "^1.1.1" + lodash "^4.0.0" + slice-ansi "0.0.4" + string-width "^2.0.0" + +tapable@^0.1.8: + version "0.1.10" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4" + +tapable@^0.2.5, tapable@~0.2.5: + version "0.2.6" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.6.tgz#206be8e188860b514425375e6f1ae89bfb01fd8d" + +tar-pack@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.0.tgz#23be2d7f671a8339376cbdb0b8fe3fdebf317984" + dependencies: + debug "^2.2.0" + fstream "^1.0.10" + fstream-ignore "^1.0.5" + once "^1.3.3" + readable-stream "^2.1.4" + rimraf "^2.5.1" + tar "^2.2.1" + uid-number "^0.0.6" + +tar@^2.0.0, tar@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" + dependencies: + block-stream "*" + fstream "^1.0.2" + inherits "2" + +text-table@^0.2.0, text-table@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + +through2@^0.6.1, through2@^0.6.3, through2@~0.6.1: + version "0.6.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" + dependencies: + readable-stream ">=1.0.33-1 <1.1.0-0" + xtend ">=4.0.0 <4.1.0-0" + +"through@>=2.2.7 <3", through@^2.3.6, through@~2.3.4: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + +timers-browserify@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d" + dependencies: + process "~0.11.0" + +timers-browserify@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.2.tgz#ab4883cf597dcd50af211349a00fbca56ac86b86" + dependencies: + setimmediate "^1.0.4" + +tiny-emitter@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.0.0.tgz#bad327adb1804b42a231afa741532bd884cd09ad" + +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + +to-fast-properties@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + +toposort@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.3.tgz#f02cd8a74bd8be2fc0e98611c3bacb95a171869c" + +tough-cookie@~2.3.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a" + dependencies: + punycode "^1.4.1" + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + +tryit@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb" + +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + +type-is@~1.6.15: + version "1.6.15" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410" + dependencies: + media-typer "0.3.0" + mime-types "~2.1.15" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + +uc.micro@^1.0.1, uc.micro@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.3.tgz#7ed50d5e0f9a9fb0a573379259f2a77458d50192" + +uglify-js@3.0.x: + version "3.0.14" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.0.14.tgz#2697af126fd215aac556facb1edfb17875204760" + dependencies: + commander "~2.9.0" + source-map "~0.5.1" + +uglify-js@^2.8.27: + version "2.8.27" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.27.tgz#47787f912b0f242e5b984343be8e35e95f694c9c" + dependencies: + source-map "~0.5.1" + yargs "~3.10.0" + optionalDependencies: + uglify-to-browserify "~1.0.0" + +uglify-to-browserify@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" + +uid-number@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + +ultron@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.0.tgz#b07a2e6a541a815fc6a34ccd4533baec307ca864" + +unc-path-regex@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" + +underscore@^1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" + +uniq@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + +uniqid@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/uniqid/-/uniqid-4.1.1.tgz#89220ddf6b751ae52b5f72484863528596bb84c1" + dependencies: + macaddress "^0.2.8" + +uniqs@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" + +unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + +upper-case@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" + +url-loader@^0.5.8: + version "0.5.8" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-0.5.8.tgz#b9183b1801e0f847718673673040bc9dc1c715c5" + dependencies: + loader-utils "^1.0.2" + mime "1.3.x" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +user-home@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" + dependencies: + os-homedir "^1.0.0" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +util@0.10.3, util@^0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + dependencies: + inherits "2.0.1" + +utila@~0.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.3.3.tgz#d7e8e7d7e309107092b05f8d9688824d633a4226" + +utila@~0.4: + version "0.4.0" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" + +utils-merge@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" + +uuid@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" + +validate-npm-package-license@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" + dependencies: + spdx-correct "~1.0.0" + spdx-expression-parse "~1.0.0" + +vary@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.1.tgz#67535ebb694c1d52257457984665323f587e8d37" + +vendors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22" + +verror@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" + dependencies: + extsprintf "1.0.2" + +vm-browserify@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" + dependencies: + indexof "0.0.1" + +vue-hot-reload-api@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.1.0.tgz#9ca58a6e0df9078554ce1708688b6578754d86de" + +vue-loader@^12.1.0: + version "12.2.1" + resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-12.2.1.tgz#53f27c0973d386768f5a75156f4129b5efc6ba55" + dependencies: + consolidate "^0.14.0" + hash-sum "^1.0.2" + js-beautify "^1.6.3" + loader-utils "^1.1.0" + lru-cache "^4.0.1" + postcss "^5.0.21" + postcss-load-config "^1.1.0" + postcss-selector-parser "^2.0.0" + resolve "^1.3.3" + source-map "^0.5.6" + vue-hot-reload-api "^2.1.0" + vue-style-loader "^3.0.0" + vue-template-es2015-compiler "^1.2.2" + +vue-style-loader@^3.0.0, vue-style-loader@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-3.0.1.tgz#c8b639bb2f24baf9d78274dc17e4f264c1deda08" + dependencies: + hash-sum "^1.0.2" + loader-utils "^1.0.2" + +vue-template-compiler@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.3.3.tgz#b5bab9ec57309c906b82a78c81a02179dbc2f470" + dependencies: + de-indent "^1.0.2" + he "^1.1.0" + +vue-template-es2015-compiler@^1.2.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.5.2.tgz#a0a6c50c941d2a4abda963f2f42c337ac450ee95" + +vue@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/vue/-/vue-2.3.3.tgz#d1eaa8fde5240735a4563e74f2c7fead9cbb064c" + +vuex@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/vuex/-/vuex-2.3.1.tgz#cde8e997c1f9957719bc7dea154f9aa691d981a6" + +watchpack@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.3.1.tgz#7d8693907b28ce6013e7f3610aa2a1acf07dad87" + dependencies: + async "^2.1.2" + chokidar "^1.4.3" + graceful-fs "^4.1.2" + +webpack-bundle-analyzer@^2.2.1: + version "2.8.2" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.8.2.tgz#8b6240c29a9d63bc72f09d920fb050adbcce9fe8" + dependencies: + acorn "^5.0.3" + chalk "^1.1.3" + commander "^2.9.0" + ejs "^2.5.6" + express "^4.15.2" + filesize "^3.5.9" + gzip-size "^3.0.0" + lodash "^4.17.4" + mkdirp "^0.5.1" + opener "^1.4.3" + ws "^2.3.1" + +webpack-dev-middleware@^1.10.0: + version "1.10.2" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.10.2.tgz#2e252ce1dfb020dbda1ccb37df26f30ab014dbd1" + dependencies: + memory-fs "~0.4.1" + mime "^1.3.4" + path-is-absolute "^1.0.0" + range-parser "^1.0.3" + +webpack-hot-middleware@^2.18.0: + version "2.18.0" + resolved "https://registry.yarnpkg.com/webpack-hot-middleware/-/webpack-hot-middleware-2.18.0.tgz#a16bb535b83a6ac94a78ac5ebce4f3059e8274d3" + dependencies: + ansi-html "0.0.7" + html-entities "^1.2.0" + querystring "^0.2.0" + strip-ansi "^3.0.0" + +webpack-merge@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.1.0.tgz#6ad72223b3e0b837e531e4597c199f909361511e" + dependencies: + lodash "^4.17.4" + +webpack-sources@^0.1.0: + version "0.1.5" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-0.1.5.tgz#aa1f3abf0f0d74db7111c40e500b84f966640750" + dependencies: + source-list-map "~0.1.7" + source-map "~0.5.3" + +webpack-sources@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-0.2.3.tgz#17c62bfaf13c707f9d02c479e0dcdde8380697fb" + dependencies: + source-list-map "^1.1.1" + source-map "~0.5.3" + +webpack@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-2.6.1.tgz#2e0457f0abb1ac5df3ab106c69c672f236785f07" + dependencies: + acorn "^5.0.0" + acorn-dynamic-import "^2.0.0" + ajv "^4.7.0" + ajv-keywords "^1.1.1" + async "^2.1.2" + enhanced-resolve "^3.0.0" + interpret "^1.0.0" + json-loader "^0.5.4" + json5 "^0.5.1" + loader-runner "^2.3.0" + loader-utils "^0.2.16" + memory-fs "~0.4.1" + mkdirp "~0.5.0" + node-libs-browser "^2.0.0" + source-map "^0.5.3" + supports-color "^3.1.0" + tapable "~0.2.5" + uglify-js "^2.8.27" + watchpack "^1.3.1" + webpack-sources "^0.2.3" + yargs "^6.0.0" + +whet.extend@~0.9.9: + version "0.9.9" + resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1" + +which-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + +which@1, which@^1.2.9: + version "1.2.14" + resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710" + dependencies: + string-width "^1.0.2" + +window-size@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" + +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + +wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +write-file-stdout@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/write-file-stdout/-/write-file-stdout-0.0.2.tgz#c252d7c7c5b1b402897630e3453c7bfe690d9ca1" + +write@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" + dependencies: + mkdirp "^0.5.1" + +ws@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-2.3.1.tgz#6b94b3e447cb6a363f785eaf94af6359e8e81c80" + dependencies: + safe-buffer "~5.0.1" + ultron "~1.1.0" + +xml-char-classes@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/xml-char-classes/-/xml-char-classes-1.0.0.tgz#64657848a20ffc5df583a42ad8a277b4512bbc4d" + +"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + +y18n@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + +yallist@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + +yargs-parser@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9f74871c" + dependencies: + camelcase "^3.0.0" + +yargs-parser@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a" + dependencies: + camelcase "^3.0.0" + +yargs@^1.2.6: + version "1.3.3" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-1.3.3.tgz#054de8b61f22eefdb7207059eaef9d6b83fb931a" + +yargs@^3.5.4, yargs@~3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" + dependencies: + camelcase "^1.0.2" + cliui "^2.1.0" + decamelize "^1.0.0" + window-size "0.1.0" + +yargs@^6.0.0: + version "6.6.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208" + dependencies: + camelcase "^3.0.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.2" + which-module "^1.0.0" + y18n "^3.2.1" + yargs-parser "^4.2.0" + +yargs@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" + dependencies: + camelcase "^3.0.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.2" + which-module "^1.0.0" + y18n "^3.2.1" + yargs-parser "^5.0.0"