From c20c935fa5195a749fc72052fbb89842d441ea96 Mon Sep 17 00:00:00 2001 From: NGPixel Date: Sat, 20 May 2017 23:21:16 -0400 Subject: [PATCH] refactor: Migrate to Vue components --- .eslintrc.json | 5 +- client/js/app.js | 85 +++++- client/js/components/anchor.vue | 17 ++ client/js/components/color-picker.vue | 15 + client/js/components/copy-path.vue | 17 -- client/js/components/loading-spinner.vue | 12 + client/js/components/search.js | 87 ------ client/js/components/search.vue | 101 +++++++ client/js/modals/create.vue | 54 ++++ client/js/pages/admin-profile.component.js | 30 ++ client/js/pages/admin-settings.component.js | 52 ++++ client/js/pages/admin.js | 79 +---- client/js/pages/view.js | 10 +- client/scss/components/search.scss | 41 ++- client/scss/layout/_header.scss | 4 +- fuse.js | 86 ++++-- package.json | 12 +- server/controllers/admin.js | 7 + server/locales/en/admin.json | 2 +- server/locales/en/browser.json | 16 + server/locales/en/common.json | 10 +- server/locales/fr/common.json | 4 +- server/views/common/header.pug | 19 +- server/views/layout.pug | 9 +- server/views/pages/admin/_layout.pug | 6 +- server/views/pages/admin/profile.pug | 90 +++--- server/views/pages/admin/settings.pug | 10 +- server/views/pages/admin/theme.pug | 11 + server/views/pages/view.pug | 4 +- yarn.lock | 312 ++++++++++---------- 30 files changed, 725 insertions(+), 482 deletions(-) create mode 100644 client/js/components/anchor.vue create mode 100644 client/js/components/color-picker.vue delete mode 100644 client/js/components/copy-path.vue create mode 100644 client/js/components/loading-spinner.vue delete mode 100644 client/js/components/search.js create mode 100644 client/js/components/search.vue create mode 100644 client/js/modals/create.vue create mode 100644 client/js/pages/admin-profile.component.js create mode 100644 client/js/pages/admin-settings.component.js create mode 100644 server/locales/en/browser.json create mode 100644 server/views/pages/admin/theme.pug diff --git a/.eslintrc.json b/.eslintrc.json index 72ce2be8..3c0701fc 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,12 +1,10 @@ { "extends": "standard", - "env": { "node": true, "es6": true, "jest": true }, - "globals": { "document": false, "navigator": false, @@ -16,5 +14,8 @@ "ROOTPATH": true, "SERVERPATH": true, "IS_DEBUG": true + }, + "rules": { + "space-before-function-paren": 0 } } diff --git a/client/js/app.js b/client/js/app.js index ab93d206..246c890d 100644 --- a/client/js/app.js +++ b/client/js/app.js @@ -1,14 +1,64 @@ 'use strict' -/* global alertsData */ +/* global alertsData, siteLang */ +/* eslint-disable no-new */ import $ from 'jquery' import _ from 'lodash' +import Vue from 'vue' +import Vuex from 'vuex' import io from 'socket.io-client' +import i18next from 'i18next' +import i18nextXHR from 'i18next-xhr-backend' +import VueI18Next from '@panter/vue-i18next' import Alerts from './components/alerts.js' import 'jquery-smooth-scroll' import 'jquery-sticky' +// ==================================== +// Load Vue Components +// ==================================== + +import anchorComponent from './components/anchor.vue' +import colorPickerComponent from './components/color-picker.vue' +import loadingSpinnerComponent from './components/loading-spinner.vue' +import searchComponent from './components/search.vue' + +import adminProfileComponent from './pages/admin-profile.component.js' +import adminSettingsComponent from './pages/admin-settings.component.js' + +// ==================================== +// Initialize i18next +// ==================================== + +Vue.use(VueI18Next) + +i18next + .use(i18nextXHR) + .init({ + backend: { + loadPath: '/js/i18n/{{lng}}.json' + }, + lng: siteLang, + fallbackLng: siteLang + }) + +// ==================================== +// Initialize Vuex +// ==================================== + +Vue.use(Vuex) + +const store = new Vuex.Store({ + state: { + loading: false + }, + mutations: { + startLoading: state => { state.loading = true }, + stopLoading: state => { state.loading = false } + } +}) + $(() => { // ==================================== // Scroll @@ -27,28 +77,47 @@ $(() => { // ==================================== $(window).bind('beforeunload', () => { - $('#notifload').addClass('active') + store.commit('startLoading') }) $(document).ajaxSend(() => { - $('#notifload').addClass('active') + store.commit('startLoading') }).ajaxComplete(() => { - $('#notifload').removeClass('active') + store.commit('stopLoading') }) - var alerts = new Alerts() + var alerts = {} + /*var alerts = new Alerts() if (alertsData) { _.forEach(alertsData, (alertRow) => { alerts.push(alertRow) }) - } + }*/ // ==================================== // Establish WebSocket connection // ==================================== - var socket = io(window.location.origin) + let socket = io(window.location.origin) + window.socket = socket - require('./components/search.js')(socket) + // ==================================== + // Bootstrap Vue + // ==================================== + + const i18n = new VueI18Next(i18next) + new Vue({ + components: { + adminProfile: adminProfileComponent, + adminSettings: adminSettingsComponent, + anchor: anchorComponent, + colorPicker: colorPickerComponent, + loadingSpinner: loadingSpinnerComponent, + search: searchComponent + }, + store, + i18n, + el: '#root' + }) // ==================================== // Pages logic diff --git a/client/js/components/anchor.vue b/client/js/components/anchor.vue new file mode 100644 index 00000000..72250aad --- /dev/null +++ b/client/js/components/anchor.vue @@ -0,0 +1,17 @@ + + + diff --git a/client/js/components/color-picker.vue b/client/js/components/color-picker.vue new file mode 100644 index 00000000..5e41a7f9 --- /dev/null +++ b/client/js/components/color-picker.vue @@ -0,0 +1,15 @@ + + + diff --git a/client/js/components/copy-path.vue b/client/js/components/copy-path.vue deleted file mode 100644 index 86567a54..00000000 --- a/client/js/components/copy-path.vue +++ /dev/null @@ -1,17 +0,0 @@ - - - diff --git a/client/js/components/loading-spinner.vue b/client/js/components/loading-spinner.vue new file mode 100644 index 00000000..d93d317f --- /dev/null +++ b/client/js/components/loading-spinner.vue @@ -0,0 +1,12 @@ + + + diff --git a/client/js/components/search.js b/client/js/components/search.js deleted file mode 100644 index 1c6177a2..00000000 --- a/client/js/components/search.js +++ /dev/null @@ -1,87 +0,0 @@ -'use strict' - -import $ from 'jquery' -import _ from 'lodash' -import Vue from 'vue' - -module.exports = (socket) => { - if ($('#search-input').length) { - $('#search-input').focus() - - $('.searchresults').css('display', 'block') - - var vueHeader = new Vue({ - el: '#header-container', - data: { - searchq: '', - searchres: [], - searchsuggest: [], - searchload: 0, - searchactive: false, - searchmoveidx: 0, - searchmovekey: '', - searchmovearr: [] - }, - watch: { - searchq: (val, oldVal) => { - vueHeader.searchmoveidx = 0 - if (val.length >= 3) { - vueHeader.searchactive = true - vueHeader.searchload++ - socket.emit('search', { terms: val }, (data) => { - vueHeader.searchres = data.match - vueHeader.searchsuggest = data.suggest - vueHeader.searchmovearr = _.concat([], vueHeader.searchres, vueHeader.searchsuggest) - if (vueHeader.searchload > 0) { vueHeader.searchload-- } - }) - } else { - vueHeader.searchactive = false - vueHeader.searchres = [] - vueHeader.searchsuggest = [] - vueHeader.searchmovearr = [] - vueHeader.searchload = 0 - } - }, - searchmoveidx: (val, oldVal) => { - if (val > 0) { - vueHeader.searchmovekey = (vueHeader.searchmovearr[val - 1]) - ? 'res.' + vueHeader.searchmovearr[val - 1].entryPath - : 'sug.' + vueHeader.searchmovearr[val - 1] - } else { - vueHeader.searchmovekey = '' - } - } - }, - methods: { - useSuggestion: (sug) => { - vueHeader.searchq = sug - }, - closeSearch: () => { - vueHeader.searchq = '' - }, - moveSelectSearch: () => { - if (vueHeader.searchmoveidx < 1) { return } - let i = vueHeader.searchmoveidx - 1 - - if (vueHeader.searchmovearr[i]) { - window.location.assign('/' + vueHeader.searchmovearr[i].entryPath) - } else { - vueHeader.searchq = vueHeader.searchmovearr[i] - } - }, - moveDownSearch: () => { - if (vueHeader.searchmoveidx < vueHeader.searchmovearr.length) { - vueHeader.searchmoveidx++ - } - }, - moveUpSearch: () => { - if (vueHeader.searchmoveidx > 0) { - vueHeader.searchmoveidx-- - } - } - } - }) - - $('main').on('click', vueHeader.closeSearch) - } -} diff --git a/client/js/components/search.vue b/client/js/components/search.vue new file mode 100644 index 00000000..1935512b --- /dev/null +++ b/client/js/components/search.vue @@ -0,0 +1,101 @@ + + + diff --git a/client/js/modals/create.vue b/client/js/modals/create.vue new file mode 100644 index 00000000..5ae92a05 --- /dev/null +++ b/client/js/modals/create.vue @@ -0,0 +1,54 @@ + + + diff --git a/client/js/pages/admin-profile.component.js b/client/js/pages/admin-profile.component.js new file mode 100644 index 00000000..4c4486b4 --- /dev/null +++ b/client/js/pages/admin-profile.component.js @@ -0,0 +1,30 @@ +'use strict' + +import * as $ from 'jquery' + +export default { + name: 'admin-profile', + props: ['email', 'name', 'provider'], + data() { + return { + password: '********', + passwordVerify: '********' + } + }, + methods: { + saveUser() { + if (this.password !== this.passwordVerify) { + //alerts.pushError('Error', "Passwords don't match!") + return + } + $.post(window.location.href, { + password: this.password, + name: this.name + }).done((resp) => { + //alerts.pushSuccess('Saved successfully', 'Changes have been applied.') + }).fail((jqXHR, txtStatus, resp) => { + //alerts.pushError('Error', resp) + }) + } + } +} diff --git a/client/js/pages/admin-settings.component.js b/client/js/pages/admin-settings.component.js new file mode 100644 index 00000000..f21b3eb2 --- /dev/null +++ b/client/js/pages/admin-settings.component.js @@ -0,0 +1,52 @@ +'use strict' + +import * as $ from 'jquery' + +export default { + name: 'admin-settings', + data() { + return { + upgradeModal: { + state: false, + step: 'confirm', + mode: 'upgrade', + error: 'Something went wrong.' + } + } + }, + methods: { + upgrade() { + this.upgradeModal.mode = 'upgrade' + this.upgradeModal.step = 'confirm' + this.upgradeModal.state = true + }, + reinstall() { + this.upgradeModal.mode = 're-install' + this.upgradeModal.step = 'confirm' + this.upgradeModal.state = true + }, + upgradeCancel() { + this.upgradeModal.state = false + }, + upgradeStart() { + this.upgradeModal.step = 'running' + $.post('/admin/settings/install', { + mode: this.upgradeModal.mode + }).done((resp) => { + // todo + }).fail((jqXHR, txtStatus, resp) => { + this.upgradeModal.step = 'error' + this.upgradeModal.error = jqXHR.responseText + }) + }, + flushcache() { + window.alert('Coming soon!') + }, + resetaccounts() { + window.alert('Coming soon!') + }, + flushsessions() { + window.alert('Coming soon!') + } + } +} diff --git a/client/js/pages/admin.js b/client/js/pages/admin.js index 08d0fa95..030d7c96 100644 --- a/client/js/pages/admin.js +++ b/client/js/pages/admin.js @@ -1,41 +1,13 @@ 'use strict' -/* global usrData, usrDataName */ +/* global usrData */ import $ from 'jquery' import _ from 'lodash' import Vue from 'vue' module.exports = (alerts) => { - if ($('#page-type-admin-profile').length) { - let vueProfile = new Vue({ - el: '#page-type-admin-profile', - data: { - password: '********', - passwordVerify: '********', - name: '' - }, - methods: { - saveUser: (ev) => { - if (vueProfile.password !== vueProfile.passwordVerify) { - alerts.pushError('Error', "Passwords don't match!") - return - } - $.post(window.location.href, { - password: vueProfile.password, - name: vueProfile.name - }).done((resp) => { - alerts.pushSuccess('Saved successfully', 'Changes have been applied.') - }).fail((jqXHR, txtStatus, resp) => { - alerts.pushError('Error', resp) - }) - } - }, - created: function () { - this.name = usrDataName - } - }) - } else if ($('#page-type-admin-users').length) { + if ($('#page-type-admin-users').length) { require('../modals/admin-users-create.js')(alerts) } else if ($('#page-type-admin-users-edit').length) { let vueEditUser = new Vue({ @@ -98,52 +70,5 @@ module.exports = (alerts) => { } }) require('../modals/admin-users-delete.js')(alerts) - } else if ($('#page-type-admin-settings').length) { - let vueSettings = new Vue({ // eslint-disable-line no-unused-vars - el: '#page-type-admin-settings', - data: { - upgradeModal: { - state: false, - step: 'confirm', - mode: 'upgrade', - error: 'Something went wrong.' - } - }, - methods: { - upgrade: (ev) => { - vueSettings.upgradeModal.mode = 'upgrade' - vueSettings.upgradeModal.step = 'confirm' - vueSettings.upgradeModal.state = true - }, - reinstall: (ev) => { - vueSettings.upgradeModal.mode = 're-install' - vueSettings.upgradeModal.step = 'confirm' - vueSettings.upgradeModal.state = true - }, - upgradeCancel: (ev) => { - vueSettings.upgradeModal.state = false - }, - upgradeStart: (ev) => { - vueSettings.upgradeModal.step = 'running' - $.post('/admin/settings/install', { - mode: vueSettings.upgradeModal.mode - }).done((resp) => { - // todo - }).fail((jqXHR, txtStatus, resp) => { - vueSettings.upgradeModal.step = 'error' - vueSettings.upgradeModal.error = jqXHR.responseText - }) - }, - flushcache: (ev) => { - window.alert('Coming soon!') - }, - resetaccounts: (ev) => { - window.alert('Coming soon!') - }, - flushsessions: (ev) => { - window.alert('Coming soon!') - } - } - }) } } diff --git a/client/js/pages/view.js b/client/js/pages/view.js index bde05222..0a6f4922 100644 --- a/client/js/pages/view.js +++ b/client/js/pages/view.js @@ -4,7 +4,7 @@ import $ from 'jquery' import MathJax from 'mathjax' -import * as CopyPath from '../components/copy-path.vue' +// import * as CopyPath from '../components/copy-path.vue' import Vue from 'vue' module.exports = (alerts) => { @@ -13,10 +13,10 @@ module.exports = (alerts) => { // Copy Path - new Vue({ - el: '.modal-copypath', - render: h => h(CopyPath) - }) + // new Vue({ + // el: '.modal-copypath', + // render: h => h(CopyPath) + // }) // MathJax Render diff --git a/client/scss/components/search.scss b/client/scss/components/search.scss index eafbef6d..fe30714e 100644 --- a/client/scss/components/search.scss +++ b/client/scss/components/search.scss @@ -1,47 +1,56 @@ .searchresults { position: fixed; - top: 45px; + top: 50px; left: 0; right: 0; margin: 0 auto; width: 500px; z-index: 1; - background-color: mc($primary, '700'); - border-bottom: 5px solid mc($primary, '800'); - box-shadow: 0 0 5px mc($primary, '500'); + background-color: darken(mc('blue-grey', '900'), 2%); + border: 1px solid mc('blue-grey', '900'); + box-shadow: 0 0 5px mc('blue-grey', '500'); color: #FFF; + transition: max-height 1s ease; - &.slideInDown { - @include prefix(animation-duration, .6s); - } + &-enter-active, &-leave-active { + overflow: hidden; + } + &-enter-to, &-leave { + max-height: 500px; + } + &-enter, &-leave-to { + max-height: 0px; + } .searchresults-label { - color: mc($primary, '200'); - padding: 15px 10px 10px; + background-color: mc('blue-grey', '800'); + color: mc('blue-grey', '300'); + padding: 8px; font-size: 13px; - text-transform: uppercase; - border-bottom: 1px dotted mc($primary, '400'); + letter-spacing: 1px; + text-transform: uppercase; + box-shadow: 0 0 5px rgba(0,0,0,0.3); } .searchresults-list { + padding-bottom: 5px; > li { display: flex; font-size: 14px; - transition: background-color .3s linear; + transition: background-color .2s linear; &:nth-child(odd) { - background-color: mc($primary, '600'); + background-color: mc('blue-grey', '900'); } &.is-active, &:hover { - background-color: mc($primary, '400'); + background-color: mc('blue-grey', '600'); color: #FFF; - border-left: 5px solid mc($primary, '200'); } a { - color: mc($primary, '50'); + color: mc('blue-grey', '50'); display: flex; align-items: center; height: 30px; diff --git a/client/scss/layout/_header.scss b/client/scss/layout/_header.scss index 83eab039..0a014dd5 100644 --- a/client/scss/layout/_header.scss +++ b/client/scss/layout/_header.scss @@ -24,7 +24,7 @@ @include spinner(mc('indigo', '100'),0.5s,24px); } - &.active { + &.is-active { opacity: 1; } @@ -33,4 +33,4 @@ #search-input { max-width: 300px; width: 33vw; -} \ No newline at end of file +} diff --git a/fuse.js b/fuse.js index 956d2e51..88a77b50 100644 --- a/fuse.js +++ b/fuse.js @@ -88,43 +88,66 @@ let globalTasks = Promise.mapSeries([ if (err.code === 'ENOENT') { console.info(colors.white(' └── ') + colors.green('Copy MathJax dependencies to assets...')) return fs.ensureDirAsync('./assets/js/mathjax').then(() => { - return fs.copyAsync('./node_modules/mathjax', './assets/js/mathjax', { filter: (src, dest) => { - let srcNormalized = src.replace(/\\/g, '/') - let shouldCopy = false - console.log(srcNormalized) - _.forEach([ - '/node_modules/mathjax', - '/node_modules/mathjax/jax', - '/node_modules/mathjax/jax/input', - '/node_modules/mathjax/jax/output' - ], chk => { - if (srcNormalized.endsWith(chk)) { - shouldCopy = true + return fs.copyAsync('./node_modules/mathjax', './assets/js/mathjax', { + filter: (src, dest) => { + let srcNormalized = src.replace(/\\/g, '/') + let shouldCopy = false + console.info(colors.white(' ' + srcNormalized)) + _.forEach([ + '/node_modules/mathjax', + '/node_modules/mathjax/jax', + '/node_modules/mathjax/jax/input', + '/node_modules/mathjax/jax/output' + ], chk => { + if (srcNormalized.endsWith(chk)) { + shouldCopy = true + } + }) + _.forEach([ + '/node_modules/mathjax/extensions', + '/node_modules/mathjax/MathJax.js', + '/node_modules/mathjax/jax/element', + '/node_modules/mathjax/jax/input/MathML', + '/node_modules/mathjax/jax/input/TeX', + '/node_modules/mathjax/jax/output/SVG' + ], chk => { + if (srcNormalized.indexOf(chk) > 0) { + shouldCopy = true + } + }) + if (shouldCopy && srcNormalized.indexOf('/fonts/') > 0 && srcNormalized.indexOf('/STIX-Web') <= 1) { + shouldCopy = false } - }) - _.forEach([ - '/node_modules/mathjax/extensions', - '/node_modules/mathjax/MathJax.js', - '/node_modules/mathjax/jax/element', - '/node_modules/mathjax/jax/input/MathML', - '/node_modules/mathjax/jax/input/TeX', - '/node_modules/mathjax/jax/output/SVG' - ], chk => { - if (srcNormalized.indexOf(chk) > 0) { - shouldCopy = true - } - }) - if (shouldCopy && srcNormalized.indexOf('/fonts/') > 0 && srcNormalized.indexOf('/STIX-Web') <= 1) { - shouldCopy = false + return shouldCopy } - return shouldCopy - }}) + }) }) } else { throw err } }) }, + /** + * i18n + */ + () => { + console.info(colors.white(' └── ') + colors.green('Copying i18n client files...')) + return fs.ensureDirAsync('./assets/js/i18n').then(() => { + return fs.readJsonAsync('./server/locales/en/browser.json').then(enContent => { + return fs.readdirAsync('./server/locales').then(langs => { + return Promise.map(langs, lang => { + console.info(colors.white(' ' + lang + '.json')) + let outputPath = path.join('./assets/js/i18n', lang + '.json') + return fs.readJsonAsync(path.join('./server/locales', lang + '.json'), 'utf8').then((content) => { + return fs.outputJsonAsync(outputPath, _.defaultsDeep(content, enContent)) + }).catch(err => { // eslint-disable-line handle-callback-err + return fs.outputJsonAsync(outputPath, enContent) + }) + }) + }) + }) + }) + }, /** * Bundle pre-init scripts */ @@ -144,6 +167,7 @@ let globalTasks = Promise.mapSeries([ * Delete Fusebox cache */ () => { + console.info(colors.white(' └── ') + colors.green('Clearing fuse-box cache...')) return fs.emptyDirAsync('./.fusebox') } ], f => { return f() }) @@ -156,7 +180,7 @@ const ALIASES = { 'brace-ext-modelist': 'brace/ext/modelist.js', 'simplemde': 'simplemde/dist/simplemde.min.js', 'socket.io-client': 'socket.io-client/dist/socket.io.js', - 'vue': 'vue/dist/vue.min.js' + 'vue': (dev) ? 'vue/dist/vue.js' : 'vue/dist/vue.min.js' } const SHIMS = { _preinit: { @@ -182,7 +206,7 @@ globalTasks.then(() => { plugins: [ fsbx.EnvPlugin({ NODE_ENV: (dev) ? 'development' : 'production' }), fsbx.VuePlugin(), - [ '.scss', fsbx.SassPlugin({ outputStyle: (dev) ? 'nested' : 'compressed' }), fsbx.CSSPlugin() ], + ['.scss', fsbx.SassPlugin({ outputStyle: (dev) ? 'nested' : 'compressed' }), fsbx.CSSPlugin()], fsbx.BabelPlugin({ comments: false, presets: ['es2015'] }), fsbx.JSONPlugin(), !dev && fsbx.UglifyJSPlugin({ diff --git a/package.json b/package.json index 8b328ec4..17503c36 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "fs-extra": "^3.0.1", "git-wrapper2-promise": "^0.2.9", "highlight.js": "^9.11.0", - "i18next": "^8.2.0", + "i18next": "^8.3.0", "i18next-express-middleware": "^1.0.5", "i18next-node-fs-backend": "^1.0.0", "image-size": "^0.5.4", @@ -78,7 +78,7 @@ "markdown-it": "^8.3.1", "markdown-it-abbr": "^1.0.4", "markdown-it-anchor": "^4.0.0", - "markdown-it-attrs": "^0.8.0", + "markdown-it-attrs": "^0.9.0", "markdown-it-emoji": "^1.3.0", "markdown-it-expand-tabs": "^1.0.12", "markdown-it-external-links": "0.0.6", @@ -126,11 +126,13 @@ }, "devDependencies": { "@glimpse/glimpse": "^0.20.9", + "@panter/vue-i18next": "^0.4.1", "babel-cli": "latest", "babel-jest": "latest", "babel-preset-es2015": "latest", "brace": "^0.10.0", "colors": "^1.1.2", + "consolidate": "^0.14.5", "eslint": "latest", "eslint-config-standard": "latest", "eslint-plugin-import": "latest", @@ -138,6 +140,7 @@ "eslint-plugin-promise": "latest", "eslint-plugin-standard": "latest", "fuse-box": "^2.0.0", + "i18next-xhr-backend": "^1.4.1", "jest": "latest", "jquery": "^3.2.1", "jquery-contextmenu": "^2.4.5", @@ -155,7 +158,8 @@ "vee-validate": "^2.0.0-rc.3", "vue": "^2.3.3", "vue-template-compiler": "^2.3.3", - "vue-template-es2015-compiler": "^1.5.2" + "vue-template-es2015-compiler": "^1.5.2", + "vuex": "^2.3.1" }, "jest": { "collectCoverage": false, @@ -166,4 +170,4 @@ "verbose": true }, "snyk": true -} \ No newline at end of file +} diff --git a/server/controllers/admin.js b/server/controllers/admin.js index 4514257d..09295faf 100644 --- a/server/controllers/admin.js +++ b/server/controllers/admin.js @@ -255,4 +255,11 @@ router.post('/settings/install', (req, res) => { res.status(400).send('Sorry, Upgrade/Re-Install via the web UI is not yet ready. You must use the npm upgrade method in the meantime.').end() }) +router.get('/theme', (req, res) => { + if (!res.locals.rights.manage) { + return res.render('error-forbidden') + } + res.render('pages/admin/theme', { adminTab: 'theme' }) +}) + module.exports = router diff --git a/server/locales/en/admin.json b/server/locales/en/admin.json index 7a46d992..e62976fb 100644 --- a/server/locales/en/admin.json +++ b/server/locales/en/admin.json @@ -48,4 +48,4 @@ "edituser": "Edit User", "uniqueid": "Unique ID" } -} \ No newline at end of file +} diff --git a/server/locales/en/browser.json b/server/locales/en/browser.json new file mode 100644 index 00000000..9ef3b278 --- /dev/null +++ b/server/locales/en/browser.json @@ -0,0 +1,16 @@ +{ + "profile": { + "displayname": "Display Name", + "displaynameexample": "John Smith", + "email": "Email", + "password": "Password", + "passwordverify": "Verify Password", + "savechanges": "Save Changes" + }, + "search": { + "placeholder": "Search...", + "results": "Search Results", + "nomatch": "No results matching your query", + "didyoumean": "Did you mean...?" + } +} diff --git a/server/locales/en/common.json b/server/locales/en/common.json index 5461985c..40703a40 100644 --- a/server/locales/en/common.json +++ b/server/locales/en/common.json @@ -9,12 +9,6 @@ "home": "Home", "top": "Return to top" }, - "search": { - "placeholder": "Search...", - "results": "Search Results", - "nomatch": "No results matching your query", - "didyoumean": "Did you mean...?" - }, "sidebar": { "nav": "NAV", "navigation": "Navigation", @@ -24,9 +18,11 @@ "nav": { "home": "Home", "account": "Account", + "settings": "Settings", "myprofile": "My Profile", "stats": "Stats", "syssettings": "System Settings", + "theme": "Color Theme", "users": "Users", "logout": "Logout", "create": "Create", @@ -51,4 +47,4 @@ "source": "Loading source...", "editor": "Loading editor..." } -} \ No newline at end of file +} diff --git a/server/locales/fr/common.json b/server/locales/fr/common.json index f3a0a455..f8a809e3 100644 --- a/server/locales/fr/common.json +++ b/server/locales/fr/common.json @@ -24,9 +24,11 @@ "nav": { "home": "Accueil", "account": "Compte", + "settings": "Paramètres", "myprofile": "Mon Profil", "stats": "Statistiques", "syssettings": "Paramètres système", + "theme": "Thème de couleur", "users": "Utilisateurs", "logout": "Se Déconnecter", "create": "Créer", @@ -51,4 +53,4 @@ "source": "Chargement de la source...", "editor": "Chargement de l'éditeur" } -} \ No newline at end of file +} diff --git a/server/views/common/header.pug b/server/views/common/header.pug index 79451f64..cff22a80 100644 --- a/server/views/common/header.pug +++ b/server/views/common/header.pug @@ -9,26 +9,11 @@ = appconfig.title .nav-center block rootNavCenter - .nav-item - p.control(v-bind:class='{ "is-loading": searchload > 0 }') - input.input#search-input(type='text', v-model='searchq', @keyup.esc='closeSearch', @keyup.down='moveDownSearch', @keyup.up='moveUpSearch', @keyup.enter='moveSelectSearch', debounce='400', placeholder=t('search.placeholder')) + search span.nav-toggle span span span .nav-right block rootNavRight - i.nav-item#notifload - - transition(name='searchresults-anim', enter-active-class='slideInDown', leave-active-class='fadeOutUp') - .searchresults.animated(v-show='searchactive', v-cloak, style={'display':'none'}) - p.searchresults-label= t('search.results') - ul.searchresults-list - li(v-if='searchres.length === 0') - a: em= t('search.nomatch') - li(v-for='sres in searchres', v-bind:class='{ "is-active": searchmovekey === "res." + sres.entryPath }') - a(v-bind:href='"/" + sres.entryPath') {{ sres.title }} - p.searchresults-label(v-if='searchsuggest.length > 0')= t('search.didyoumean') - ul.searchresults-list(v-if='searchsuggest.length > 0') - li(v-for='sug in searchsuggest', v-bind:class='{ "is-active": searchmovekey === "sug." + sug }') - a(v-on:click='useSuggestion(sug)') {{ sug }} + loading-spinner diff --git a/server/views/layout.pug b/server/views/layout.pug index c1e5a970..66bc8dcc 100644 --- a/server/views/layout.pug +++ b/server/views/layout.pug @@ -9,7 +9,7 @@ html meta(name='msapplication-TileImage', content='/favicons/ms-icon-144x144.png') title= appconfig.title - // Favicon + //- Favicon each favsize in [57, 60, 72, 76, 114, 120, 144, 152, 180] link(rel='apple-touch-icon', sizes=favsize + 'x' + favsize, href='/favicons/apple-icon-' + favsize + 'x' + favsize + '.png') link(rel='icon', type='image/png', sizes='192x192', href='/favicons/android-icon-192x192.png') @@ -17,7 +17,10 @@ html link(rel='icon', type='image/png', sizes=favsize + 'x' + favsize, href='/favicons/favicon-' + favsize + 'x' + favsize + '.png') link(rel='manifest', href='/manifest.json') - // JS / CSS + //- Site Lang + script var siteLang = '!{appconfig.lang}'; + + //- JS / CSS script(type='text/javascript', src='/js/libs.min.js') script(type='text/javascript', src='/js/app.min.js') @@ -26,7 +29,7 @@ html body #root.has-stickynav include ./common/header.pug - include ./common/alerts.pug + //-include ./common/alerts.pug main block content include ./common/footer.pug diff --git a/server/views/pages/admin/_layout.pug b/server/views/pages/admin/_layout.pug index dfcac89b..5fd87c55 100644 --- a/server/views/pages/admin/_layout.pug +++ b/server/views/pages/admin/_layout.pug @@ -4,7 +4,7 @@ block rootNavCenter h2.nav-item= t('nav.account') block rootNavRight - i.nav-item#notifload + loading-spinner .nav-item a.button.btn-edit-discard(href='/') i.icon-home @@ -48,6 +48,10 @@ block content a(href='/admin/settings') i.icon-cog span= t('nav.syssettings') + li + a(href='/admin/theme') + i.icon-drop + span= t('nav.theme') li a(href='/logout') i.icon-delete2 diff --git a/server/views/pages/admin/profile.pug b/server/views/pages/admin/profile.pug index b32a5154..0ba46dcc 100644 --- a/server/views/pages/admin/profile.pug +++ b/server/views/pages/admin/profile.pug @@ -1,53 +1,51 @@ extends ./_layout.pug block adminContent - #page-type-admin-profile - .hero - h1.title#title= t('nav.myprofile') - h2.subtitle= t('admin:profile.subtitle') - .form-sections - .columns.is-gapless - .column.is-two-thirds - section - label.label= t('admin:profile.email') - p.control.is-fullwidth - input.input(type='text', placeholder=t('admin:profile.email'), value=user.email, disabled) - if user.provider === 'local' + .hero + h1.title#title= t('nav.myprofile') + h2.subtitle= t('admin:profile.subtitle') + .form-sections + .columns.is-gapless + .column.is-two-thirds + admin-profile(inline-template, email=user.email, name=user.name, provider=user.provider) + div section - label.label= t('admin:profile.password') + label.label= t('admin:profile.email') p.control.is-fullwidth - input.input(type='password', placeholder=t('admin:profile.password'), value='********', v-model='password') + input.input(type='text', placeholder=t('admin:profile.email'), value=user.email, disabled) + if user.provider === 'local' + section + label.label= t('admin:profile.password') + p.control.is-fullwidth + input.input(type='password', placeholder=t('admin:profile.password'), value='********', v-model='password') + section + label.label= t('admin:profile.passwordverify') + p.control.is-fullwidth + input.input(type='password', placeholder=t('admin:profile.password'), value='********', v-model='passwordVerify') section - label.label= t('admin:profile.passwordverify') + label.label= t('admin:profile.displayname') p.control.is-fullwidth - input.input(type='password', placeholder=t('admin:profile.password'), value='********', v-model='passwordVerify') - section - label.label= t('admin:profile.displayname') - p.control.is-fullwidth - input.input(type='text', placeholder=t('admin:profile.displaynameexample'), v-model='name') - section - button.button.is-green(v-on:click='saveUser') - i.icon-check - span= t('admin:profile.savechanges') - .column - .panel-aside - label.label= t('admin:profile.provider') - p.control.account-profile-provider - case user.provider - when 'local': i.icon-server - when 'windowslive': i.icon-windows2.is-blue - when 'azure': i.icon-windows2.is-blue - when 'google': i.icon-google.is-blue - when 'facebook': i.icon-facebook.is-indigo - when 'github': i.icon-github.is-grey - when 'slack': i.icon-slack.is-purple - when 'ldap': i.icon-arrow-repeat-outline - default: i.icon-warning - = t('auth:providers.' + user.provider) - label.label= t('admin:profile.membersince') - p.control= moment(user.createdAt).format('LL') - label.label= t('admin:profile.lastprofileupdate') - p.control= moment(user.updatedAt).format('LL') - - script(type='text/javascript'). - var usrDataName = "!{user.name}"; + input.input(type='text', placeholder=t('admin:profile.displaynameexample'), v-model='name') + section + button.button.is-green(v-on:click='saveUser') + i.icon-check + span= t('admin:profile.savechanges') + .column + .panel-aside + label.label= t('admin:profile.provider') + p.control.account-profile-provider + case user.provider + when 'local': i.icon-server + when 'windowslive': i.icon-windows2.is-blue + when 'azure': i.icon-windows2.is-blue + when 'google': i.icon-google.is-blue + when 'facebook': i.icon-facebook.is-indigo + when 'github': i.icon-github.is-grey + when 'slack': i.icon-slack.is-purple + when 'ldap': i.icon-arrow-repeat-outline + default: i.icon-warning + = t('auth:providers.' + user.provider) + label.label= t('admin:profile.membersince') + p.control= moment(user.createdAt).format('LL') + label.label= t('admin:profile.lastprofileupdate') + p.control= moment(user.updatedAt).format('LL') diff --git a/server/views/pages/admin/settings.pug b/server/views/pages/admin/settings.pug index 29601be8..79d9926c 100644 --- a/server/views/pages/admin/settings.pug +++ b/server/views/pages/admin/settings.pug @@ -1,10 +1,10 @@ extends ./_layout.pug block adminContent - #page-type-admin-settings - .hero - h1.title#title= t('nav.syssettings') - h2.subtitle= t('admin:settings.subtitle') + .hero + h1.title#title= t('nav.syssettings') + h2.subtitle= t('admin:settings.subtitle') + admin-settings(inline-template) .form-sections section img(src='/images/logo.png', style={width:'200px', float:'right'}) @@ -34,4 +34,4 @@ block adminContent p.is-small= t('admin:settings.flushsessionstext') p: button.button.is-teal.is-outlined(v-on:click='flushsessions')= t('admin:settings.flushsessionsbtn') - include ../../modals/admin-upgrade.pug + include ../../modals/admin-upgrade.pug diff --git a/server/views/pages/admin/theme.pug b/server/views/pages/admin/theme.pug new file mode 100644 index 00000000..301f9d8d --- /dev/null +++ b/server/views/pages/admin/theme.pug @@ -0,0 +1,11 @@ +extends ./_layout.pug + +block adminContent + #page-type-admin-settings + .hero + h1.title#title= t('nav.theme') + h2.subtitle= t('admin:theme.subtitle') + .form-sections + section + label.label= t('admin:theme.primarycolor') + color-picker diff --git a/server/views/pages/view.pug b/server/views/pages/view.pug index fb4a8e21..f4a63c77 100644 --- a/server/views/pages/view.pug +++ b/server/views/pages/view.pug @@ -57,8 +57,8 @@ block content if !isGuest li a(href='/admin') - i.icon-head - span= t('nav.account') + i.icon-cog + span= t('nav.settings') else li a(href='/login') diff --git a/yarn.lock b/yarn.lock index c0886a64..fc32004a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -53,6 +53,10 @@ "@glimpse/glimpse-server" "0.20.9" lodash "^4.15.0" +"@panter/vue-i18next@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@panter/vue-i18next/-/vue-i18next-0.4.1.tgz#6b06b783cd4d8f2c80255457d3fa0db6aff1091c" + abab@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.3.tgz#b81de5f7274ec4e756d797cd834f303642724e5d" @@ -505,13 +509,13 @@ babel-helpers@^6.24.1: babel-runtime "^6.22.0" babel-template "^6.24.1" -babel-jest@^20.0.1, babel-jest@latest: - version "20.0.1" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-20.0.1.tgz#9cbe9a15bbe3f1ca1b727dc8e45a4161771d3655" +babel-jest@^20.0.3, babel-jest@latest: + version "20.0.3" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-20.0.3.tgz#e4a03b13dc10389e140fc645d09ffc4ced301671" dependencies: babel-core "^6.0.0" babel-plugin-istanbul "^4.0.0" - babel-preset-jest "^20.0.1" + babel-preset-jest "^20.0.3" babel-messages@^6.23.0: version "6.23.0" @@ -533,9 +537,9 @@ babel-plugin-istanbul@^4.0.0: istanbul-lib-instrument "^1.7.1" test-exclude "^4.1.0" -babel-plugin-jest-hoist@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-20.0.1.tgz#1b9cc322cff704d3812d1bca8dccd12205eedfd5" +babel-plugin-jest-hoist@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-20.0.3.tgz#afedc853bd3f8dc3548ea671fbe69d03cc2c1767" babel-plugin-transform-es2015-arrow-functions@^6.22.0: version "6.22.0" @@ -755,11 +759,11 @@ babel-preset-es2015@latest: babel-plugin-transform-es2015-unicode-regex "^6.24.1" babel-plugin-transform-regenerator "^6.24.1" -babel-preset-jest@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-20.0.1.tgz#8a9e23ce8a0f0c49835de53ed73ecf75dd6daa2e" +babel-preset-jest@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-20.0.3.tgz#cbacaadecb5d689ca1e1de1360ebfc66862c178a" dependencies: - babel-plugin-jest-hoist "^20.0.1" + babel-plugin-jest-hoist "^20.0.3" babel-register@^6.24.1: version "6.24.1" @@ -921,7 +925,7 @@ bluebird@2.10.2: version "2.10.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.10.2.tgz#024a5517295308857f14f91f1106fc3b555f446b" -bluebird@^3.0, bluebird@^3.4.1, bluebird@^3.5.0: +bluebird@^3.0, bluebird@^3.1.1, bluebird@^3.4.1, bluebird@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c" @@ -1399,6 +1403,12 @@ 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.5: + version "0.14.5" + resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.14.5.tgz#5a25047bc76f73072667c8cb52c989888f494c63" + dependencies: + bluebird "^3.1.1" + constantinople@^3.0.1: version "3.1.0" resolved "https://registry.yarnpkg.com/constantinople/-/constantinople-3.1.0.tgz#7569caa8aa3f8d5935d62e1fa96f9f702cd81c79" @@ -2936,9 +2946,13 @@ i18next-node-fs-backend@^1.0.0: js-yaml "3.5.4" json5 "0.5.0" -i18next@^8.2.0: - version "8.2.1" - resolved "https://registry.yarnpkg.com/i18next/-/i18next-8.2.1.tgz#6d2e8884516c320b4020c5af63e0316be626ac95" +i18next-xhr-backend@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/i18next-xhr-backend/-/i18next-xhr-backend-1.4.1.tgz#ade99356065f51742da9e4bc79c7bb4905a4b91d" + +i18next@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-8.3.0.tgz#04739294a665d204cab5fe4f4dd3e9bc3750ace4" iconv-lite@0.4.13: version "0.4.13" @@ -3361,13 +3375,13 @@ istanbul-reports@^1.1.0: dependencies: handlebars "^4.0.3" -jest-changed-files@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-20.0.1.tgz#ba9bd42c3fddb1b7c4ae40065199b44a2335e152" +jest-changed-files@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-20.0.3.tgz#9394d5cc65c438406149bef1bf4d52b68e03e3f8" -jest-cli@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-20.0.1.tgz#86ca0bc2e47215ad8e7dc85455c0210f86648502" +jest-cli@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-20.0.3.tgz#fe88ddbb7a9f3a16d0ed55339a0a2424f7f0d361" dependencies: ansi-escapes "^1.4.0" callsites "^2.0.0" @@ -3378,18 +3392,18 @@ jest-cli@^20.0.1: istanbul-lib-coverage "^1.0.1" istanbul-lib-instrument "^1.4.2" istanbul-lib-source-maps "^1.1.0" - jest-changed-files "^20.0.1" - jest-config "^20.0.1" - jest-docblock "^20.0.1" - jest-environment-jsdom "^20.0.1" - jest-haste-map "^20.0.1" - jest-jasmine2 "^20.0.1" - jest-message-util "^20.0.1" - jest-regex-util "^20.0.1" - jest-resolve-dependencies "^20.0.1" - jest-runtime "^20.0.1" - jest-snapshot "^20.0.1" - jest-util "^20.0.1" + jest-changed-files "^20.0.3" + jest-config "^20.0.3" + jest-docblock "^20.0.3" + jest-environment-jsdom "^20.0.3" + jest-haste-map "^20.0.3" + jest-jasmine2 "^20.0.3" + jest-message-util "^20.0.3" + jest-regex-util "^20.0.3" + jest-resolve-dependencies "^20.0.3" + jest-runtime "^20.0.3" + jest-snapshot "^20.0.3" + jest-util "^20.0.3" micromatch "^2.3.11" node-notifier "^5.0.2" pify "^2.3.0" @@ -3400,177 +3414,177 @@ jest-cli@^20.0.1: worker-farm "^1.3.1" yargs "^7.0.2" -jest-config@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-20.0.1.tgz#c6934f585c3e1775c96133efb302f986c3909ad8" +jest-config@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-20.0.3.tgz#a934f27eea764915801cdda26f6f8eec2ac79266" dependencies: chalk "^1.1.3" glob "^7.1.1" - jest-environment-jsdom "^20.0.1" - jest-environment-node "^20.0.1" - jest-jasmine2 "^20.0.1" - jest-matcher-utils "^20.0.1" - jest-regex-util "^20.0.1" - jest-resolve "^20.0.1" - jest-validate "^20.0.1" - pretty-format "^20.0.1" + jest-environment-jsdom "^20.0.3" + jest-environment-node "^20.0.3" + jest-jasmine2 "^20.0.3" + jest-matcher-utils "^20.0.3" + jest-regex-util "^20.0.3" + jest-resolve "^20.0.3" + jest-validate "^20.0.3" + pretty-format "^20.0.3" -jest-diff@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-20.0.1.tgz#2567c80c324243328321386f8871a28ec9d350ac" +jest-diff@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-20.0.3.tgz#81f288fd9e675f0fb23c75f1c2b19445fe586617" dependencies: chalk "^1.1.3" diff "^3.2.0" - jest-matcher-utils "^20.0.1" - pretty-format "^20.0.1" + jest-matcher-utils "^20.0.3" + pretty-format "^20.0.3" -jest-docblock@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-20.0.1.tgz#055e0bbcb76798198479901f92d2733bf619f854" +jest-docblock@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-20.0.3.tgz#17bea984342cc33d83c50fbe1545ea0efaa44712" -jest-environment-jsdom@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-20.0.1.tgz#2d29f81368987d387c70ce4f500c6aa560f9b4f7" +jest-environment-jsdom@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-20.0.3.tgz#048a8ac12ee225f7190417713834bb999787de99" dependencies: - jest-mock "^20.0.1" - jest-util "^20.0.1" + jest-mock "^20.0.3" + jest-util "^20.0.3" jsdom "^9.12.0" -jest-environment-node@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-20.0.1.tgz#75ab5358072ee1efebc54f43474357d7b3d674c7" +jest-environment-node@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-20.0.3.tgz#d488bc4612af2c246e986e8ae7671a099163d403" dependencies: - jest-mock "^20.0.1" - jest-util "^20.0.1" + jest-mock "^20.0.3" + jest-util "^20.0.3" -jest-haste-map@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-20.0.1.tgz#e6ba4db99ab512e7c081a5b0a0af731d0e193d56" +jest-haste-map@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-20.0.3.tgz#6377d537eaf34eb5f75121a691cae3fde82ba971" dependencies: fb-watchman "^2.0.0" graceful-fs "^4.1.11" - jest-docblock "^20.0.1" + jest-docblock "^20.0.3" micromatch "^2.3.11" sane "~1.6.0" worker-farm "^1.3.1" -jest-jasmine2@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-20.0.1.tgz#675772b1fd32ad74e92e8ae8282f8ea71d1de168" +jest-jasmine2@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-20.0.3.tgz#18c4e9d029da7ed1ae727c55300064d1a0542974" dependencies: chalk "^1.1.3" graceful-fs "^4.1.11" - jest-diff "^20.0.1" - jest-matcher-utils "^20.0.1" - jest-matchers "^20.0.1" - jest-message-util "^20.0.1" - jest-snapshot "^20.0.1" + jest-diff "^20.0.3" + jest-matcher-utils "^20.0.3" + jest-matchers "^20.0.3" + jest-message-util "^20.0.3" + jest-snapshot "^20.0.3" once "^1.4.0" p-map "^1.1.1" -jest-matcher-utils@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-20.0.1.tgz#31aef67f59535af3c2271a3a3685db604dbd1622" +jest-matcher-utils@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-20.0.3.tgz#b3a6b8e37ca577803b0832a98b164f44b7815612" dependencies: chalk "^1.1.3" - pretty-format "^20.0.1" + pretty-format "^20.0.3" -jest-matchers@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/jest-matchers/-/jest-matchers-20.0.1.tgz#053b7654ce60129268f39992886e987a5201bb90" +jest-matchers@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-matchers/-/jest-matchers-20.0.3.tgz#ca69db1c32db5a6f707fa5e0401abb55700dfd60" dependencies: - jest-diff "^20.0.1" - jest-matcher-utils "^20.0.1" - jest-message-util "^20.0.1" - jest-regex-util "^20.0.1" + jest-diff "^20.0.3" + jest-matcher-utils "^20.0.3" + jest-message-util "^20.0.3" + jest-regex-util "^20.0.3" -jest-message-util@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-20.0.1.tgz#ac21cb055a6a5786b7f127ac7e705df5ffb1c335" +jest-message-util@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-20.0.3.tgz#6aec2844306fcb0e6e74d5796c1006d96fdd831c" dependencies: chalk "^1.1.3" micromatch "^2.3.11" slash "^1.0.0" -jest-mock@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-20.0.1.tgz#f4cca2e87e441b66fabe4ead6a6d61773ec0773a" +jest-mock@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-20.0.3.tgz#8bc070e90414aa155c11a8d64c869a0d5c71da59" -jest-regex-util@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-20.0.1.tgz#ecbcca8fbe4e217bca7f6f42a9b831d051224dc4" +jest-regex-util@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-20.0.3.tgz#85bbab5d133e44625b19faf8c6aa5122d085d762" -jest-resolve-dependencies@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-20.0.1.tgz#38fc012191775b0b277fabebb37aa8282e26846f" +jest-resolve-dependencies@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-20.0.3.tgz#6e14a7b717af0f2cb3667c549de40af017b1723a" dependencies: - jest-regex-util "^20.0.1" + jest-regex-util "^20.0.3" -jest-resolve@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-20.0.1.tgz#cace553663f25c703dc977a4ce176e29eda92772" +jest-resolve@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-20.0.3.tgz#375307aa40f78532d40ff8b17d5300b1519f8dd4" dependencies: browser-resolve "^1.11.2" is-builtin-module "^1.0.0" resolve "^1.3.2" -jest-runtime@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-20.0.1.tgz#384f9298b8e8a177870c6d9ad0023db10ddcaedc" +jest-runtime@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-20.0.3.tgz#dddd22bbc429e26e6a96d1acd46ca55714b09252" dependencies: babel-core "^6.0.0" - babel-jest "^20.0.1" + babel-jest "^20.0.3" babel-plugin-istanbul "^4.0.0" chalk "^1.1.3" convert-source-map "^1.4.0" graceful-fs "^4.1.11" - jest-config "^20.0.1" - jest-haste-map "^20.0.1" - jest-regex-util "^20.0.1" - jest-resolve "^20.0.1" - jest-util "^20.0.1" + jest-config "^20.0.3" + jest-haste-map "^20.0.3" + jest-regex-util "^20.0.3" + jest-resolve "^20.0.3" + jest-util "^20.0.3" json-stable-stringify "^1.0.1" micromatch "^2.3.11" strip-bom "3.0.0" yargs "^7.0.2" -jest-snapshot@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-20.0.1.tgz#3704c599705042f20ec7c95ba76a4524c744dfac" +jest-snapshot@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-20.0.3.tgz#5b847e1adb1a4d90852a7f9f125086e187c76566" dependencies: chalk "^1.1.3" - jest-diff "^20.0.1" - jest-matcher-utils "^20.0.1" - jest-util "^20.0.1" + jest-diff "^20.0.3" + jest-matcher-utils "^20.0.3" + jest-util "^20.0.3" natural-compare "^1.4.0" - pretty-format "^20.0.1" + pretty-format "^20.0.3" -jest-util@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-20.0.1.tgz#a3e7afb67110b2c3ac77b82e9a51ca57f4ff72a1" +jest-util@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-20.0.3.tgz#0c07f7d80d82f4e5a67c6f8b9c3fe7f65cfd32ad" dependencies: chalk "^1.1.3" graceful-fs "^4.1.11" - jest-message-util "^20.0.1" - jest-mock "^20.0.1" - jest-validate "^20.0.1" + jest-message-util "^20.0.3" + jest-mock "^20.0.3" + jest-validate "^20.0.3" leven "^2.1.0" mkdirp "^0.5.1" -jest-validate@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-20.0.1.tgz#a236c29e3c29e9b92a1e5da211a732f0238da928" +jest-validate@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-20.0.3.tgz#d0cfd1de4f579f298484925c280f8f1d94ec3cab" dependencies: chalk "^1.1.3" - jest-matcher-utils "^20.0.1" + jest-matcher-utils "^20.0.3" leven "^2.1.0" - pretty-format "^20.0.1" + pretty-format "^20.0.3" jest@latest: - version "20.0.1" - resolved "https://registry.yarnpkg.com/jest/-/jest-20.0.1.tgz#4e268159ccc3b659966939de817c75bfe9e0157d" + version "20.0.3" + resolved "https://registry.yarnpkg.com/jest/-/jest-20.0.3.tgz#e4fd054c4f1170a116a00761da4cfdb73f1cdc33" dependencies: - jest-cli "^20.0.1" + jest-cli "^20.0.3" "jimp@https://github.com/ngpixel/jimp.git": version "0.2.27" @@ -4204,9 +4218,9 @@ markdown-it-anchor@^4.0.0: dependencies: string "^3.3.3" -markdown-it-attrs@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/markdown-it-attrs/-/markdown-it-attrs-0.8.0.tgz#11ad35725f01d7e249e897d7ce8b21403dc28f16" +markdown-it-attrs@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/markdown-it-attrs/-/markdown-it-attrs-0.9.0.tgz#dd4dfff1ad0b7acbf16bbfa3a97041da08c25fdd" markdown-it-emoji@^1.3.0: version "1.3.0" @@ -4517,11 +4531,7 @@ mv@~2: ncp "~2.0.0" rimraf "~2.4.0" -nan@^2.3.0, nan@^2.3.2, nan@^2.3.3: - version "2.6.2" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45" - -nan@~2.5.1: +nan@^2.3.0, nan@^2.3.2, nan@^2.3.3, nan@~2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.1.tgz#d5b01691253326a97a2bbee9e61c55d8d60351e2" @@ -4613,8 +4623,8 @@ node-pre-gyp@^0.6.29: tar-pack "^3.4.0" node-sass@latest: - version "4.5.2" - resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.5.2.tgz#4012fa2bd129b1d6365117e88d9da0500d99da64" + 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" @@ -5301,9 +5311,9 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" -pretty-format@^20.0.1: - version "20.0.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-20.0.1.tgz#ba95329771907c189643dd251e244061ff642350" +pretty-format@^20.0.3: + version "20.0.3" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-20.0.3.tgz#020e350a560a1fe1a98dc3beb6ccffb386de8b14" dependencies: ansi-regex "^2.1.1" ansi-styles "^3.0.0" @@ -6214,8 +6224,8 @@ snyk-try-require@^1.1.1, snyk-try-require@^1.2.0: then-fs "^2.0.0" snyk@latest: - version "1.30.0" - resolved "https://registry.yarnpkg.com/snyk/-/snyk-1.30.0.tgz#a323809ea477d6aff0e325f5995cb491c0d7ca3d" + version "1.30.1" + resolved "https://registry.yarnpkg.com/snyk/-/snyk-1.30.1.tgz#0cf14c1d73c7b6f63ca4e275ac8c2a090ec2ad52" dependencies: abbrev "^1.0.7" ansi-escapes "^1.3.0" @@ -6735,13 +6745,11 @@ uglify-js@^2.6, uglify-js@^2.6.1: uglify-to-browserify "~1.0.0" uglify-js@latest: - version "3.0.4" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.0.4.tgz#13da18bc4ecec20d29861b4ab7b60cb9ef9a9cc0" + version "3.0.9" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.0.9.tgz#974c5e638f5e2348f8509f0233667caedd52d813" dependencies: commander "~2.9.0" source-map "~0.5.1" - optionalDependencies: - uglify-to-browserify "~1.0.0" uglify-to-browserify@~1.0.0: version "1.0.2" @@ -6974,6 +6982,10 @@ 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" + vxx@^1.2.0: version "1.2.2" resolved "https://registry.yarnpkg.com/vxx/-/vxx-1.2.2.tgz#741fb51c6f11d3383da6f9b92018a5d7ba807611"