Sfoglia il codice sorgente

refactor: alerts + admin profile + user create dialog as Vue components

NGPixel 8 anni fa
parent
commit
6814c952bf

+ 38 - 47
client/js/app.js

@@ -6,12 +6,13 @@
 import $ from 'jquery'
 import _ from 'lodash'
 import Vue from 'vue'
-import Vuex from 'vuex'
+import VueResource from 'vue-resource'
+import store from './store'
 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 Alerts from './components/alerts.js'
 import 'jquery-smooth-scroll'
 import 'jquery-sticky'
 
@@ -19,18 +20,22 @@ import 'jquery-sticky'
 // Load Vue Components
 // ====================================
 
+import alertComponent from './components/alert.vue'
 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 adminUsersCreateComponent from './modals/admin-users-create.vue'
+
 import adminProfileComponent from './pages/admin-profile.component.js'
 import adminSettingsComponent from './pages/admin-settings.component.js'
 
 // ====================================
-// Initialize i18next
+// Initialize Vue Modules
 // ====================================
 
+Vue.use(VueResource)
 Vue.use(VueI18Next)
 
 i18next
@@ -43,46 +48,18 @@ i18next
     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
-  // ====================================
-
-  $('a').smoothScroll({
-    speed: 500,
-    offset: -50
-  })
-
-  $('#header').sticky({ topSpacing: 0 })
-  $('.sidebar-pagecontents').sticky({ topSpacing: 15, bottomSpacing: 75 })
-
   // ====================================
   // Notifications
   // ====================================
 
   $(window).bind('beforeunload', () => {
-    store.commit('startLoading')
+    store.dispatch('startLoading')
   })
   $(document).ajaxSend(() => {
-    store.commit('startLoading')
+    store.dispatch('startLoading')
   }).ajaxComplete(() => {
-    store.commit('stopLoading')
+    store.dispatch('stopLoading')
   })
 
   var alerts = {}
@@ -107,27 +84,41 @@ $(() => {
   const i18n = new VueI18Next(i18next)
   new Vue({
     components: {
+      alert: alertComponent,
       adminProfile: adminProfileComponent,
       adminSettings: adminSettingsComponent,
+      adminUsersCreate: adminUsersCreateComponent,
       anchor: anchorComponent,
       colorPicker: colorPickerComponent,
       loadingSpinner: loadingSpinnerComponent,
       search: searchComponent
     },
+    directives: {
+      // sticky: VueSticky
+    },
     store,
     i18n,
-    el: '#root'
+    el: '#root',
+    mounted() {
+      $('a').smoothScroll({
+        speed: 500,
+        offset: -50
+      })
+
+      $('#header').sticky({ topSpacing: 0 })
+      $('.sidebar-pagecontents').sticky({ topSpacing: 15, bottomSpacing: 75 })
+
+      // ====================================
+      // Pages logic
+      // ====================================
+
+      require('./pages/view.js')(alerts)
+      require('./pages/all.js')(alerts, socket)
+      require('./pages/create.js')(alerts, socket)
+      require('./pages/edit.js')(alerts, socket)
+      require('./pages/source.js')(alerts)
+      require('./pages/history.js')(alerts)
+      require('./pages/admin.js')(alerts)
+    }
   })
-
-  // ====================================
-  // Pages logic
-  // ====================================
-
-  require('./pages/view.js')(alerts)
-  require('./pages/all.js')(alerts, socket)
-  require('./pages/create.js')(alerts, socket)
-  require('./pages/edit.js')(alerts, socket)
-  require('./pages/source.js')(alerts)
-  require('./pages/history.js')(alerts)
-  require('./pages/admin.js')(alerts)
 })

+ 21 - 0
client/js/components/alert.vue

@@ -0,0 +1,21 @@
+<template lang="pug">
+  transition(name='alert', enter-active-class="animated zoomIn", leave-active-class="animated fadeOutRight")
+    .alert(v-if='shown', v-bind:class='style')
+      .alert-icon: i(v-bind:class='icon')
+      .alert-msg {{ msg }}
+</template>
+
+<script>
+  export default {
+    name: 'alert',
+    data () {
+      return {}
+    },
+    computed: {
+      shown() { return this.$store.state.alert.shown },
+      style() { return 'is-' + this.$store.state.alert.style },
+      icon() { return 'icon-' + this.$store.state.alert.icon },
+      msg() { return this.$store.state.alert.msg },
+    }
+  }
+</script>

+ 0 - 56
client/js/modals/admin-users-create.js

@@ -1,56 +0,0 @@
-'use strict'
-
-import $ from 'jquery'
-import Vue from 'vue'
-
-// Vue Create User instance
-
-module.exports = (alerts) => {
-  let vueCreateUser = new Vue({
-    el: '#modal-admin-users-create',
-    data: {
-      email: '',
-      provider: 'local',
-      password: '',
-      name: '',
-      loading: false
-    },
-    methods: {
-      open: (ev) => {
-        $('#modal-admin-users-create').addClass('is-active')
-        $('#modal-admin-users-create input').first().focus()
-      },
-      cancel: (ev) => {
-        $('#modal-admin-users-create').removeClass('is-active')
-        vueCreateUser.email = ''
-        vueCreateUser.provider = 'local'
-      },
-      create: (ev) => {
-        vueCreateUser.loading = true
-        $.ajax('/admin/users/create', {
-          data: {
-            email: vueCreateUser.email,
-            provider: vueCreateUser.provider,
-            password: vueCreateUser.password,
-            name: vueCreateUser.name
-          },
-          dataType: 'json',
-          method: 'POST'
-        }).then((rData, rStatus, rXHR) => {
-          vueCreateUser.loading = false
-          if (rData.ok) {
-            vueCreateUser.cancel()
-            window.location.reload(true)
-          } else {
-            alerts.pushError('Something went wrong', rData.msg)
-          }
-        }, (rXHR, rStatus, err) => {
-          vueCreateUser.loading = false
-          alerts.pushError('Error', rXHR.responseJSON.msg)
-        })
-      }
-    }
-  })
-
-  $('.btn-create-prompt').on('click', vueCreateUser.open)
-}

+ 93 - 0
client/js/modals/admin-users-create.vue

@@ -0,0 +1,93 @@
+<template lang="pug">
+  .modal(v-bind:class='{ "is-active": isShown }')
+    .modal-background
+    .modal-container
+      .modal-content
+        header.is-blue
+          span Create / Authorize User
+          p.modal-notify(v-bind:class='{ "is-active": loading }'): i
+        section
+          label.label Email address:
+          p.control.is-fullwidth
+            input.input(type='text', placeholder='e.g. john.doe@company.com', v-model='email', autofocus)
+        section
+          label.label Provider:
+          p.control.is-fullwidth
+            select(v-model='provider')
+              option(value='local') Local Database
+              option(value='windowslive') Microsoft Account
+              option(value='google') Google ID
+              option(value='facebook') Facebook
+              option(value='github') GitHub
+              option(value='slack') Slack
+        section(v-if='provider=="local"')
+          label.label Password:
+          p.control.is-fullwidth
+            input.input(type='password', placeholder='', v-model='password')
+        section(v-if='provider=="local"')
+          label.label Full Name:
+          p.control.is-fullwidth
+            input.input(type='text', placeholder='e.g. John Doe', v-model='name')
+        footer
+          a.button.is-grey.is-outlined(v-on:click='cancel') Discard
+          a.button(v-on:click='create', v-if='provider=="local"', v-bind:disabled='loading', v-bind:class='{ "is-disabled": loading, "is-blue": !loading }') Create User
+          a.button(v-on:click='create', v-if='provider!="local"', v-bind:disabled='loading', v-bind:class='{ "is-disabled": loading, "is-blue": !loading }') Authorize User
+</template>
+
+<script>
+  export default {
+    name: 'admin-users-create',
+    data () {
+      return {
+        email: '',
+        provider: 'local',
+        password: '',
+        name: '',
+        loading: false
+      }
+    },
+    computed: {
+      isShown () {
+        return this.$store.state.adminUsersCreate.shown
+      }
+    },
+    methods: {
+      cancel () {
+        this.$store.dispatch('adminUsersCreateClose')
+        this.email = ''
+        this.provider = 'local'
+      },
+      create () {
+        let self = this
+        this.loading = true
+        this.$http.post('/admin/users/create', {
+          email: this.email,
+          provider: this.provider,
+          password: this.password,
+          name: this.name
+        }).then(resp => {
+          return resp.json()
+        }).then(resp => {
+          this.loading = false
+          if (resp.ok) {
+            this.cancel()
+            window.location.reload(true)
+          } else {
+            self.$store.dispatch('alert', {
+              style: 'red',
+              icon: 'square-cross',
+              msg: resp.msg
+            })
+          }
+        }).catch(err => {
+          this.loading = false
+          self.$store.dispatch('alert', {
+            style: 'red',
+            icon: 'square-cross',
+            msg: 'Error: ' + err.body.msg
+          })
+        })
+      }
+    }
+  }
+</script>

+ 19 - 9
client/js/pages/admin-profile.component.js

@@ -1,7 +1,5 @@
 'use strict'
 
-import * as $ from 'jquery'
-
 export default {
   name: 'admin-profile',
   props: ['email', 'name', 'provider'],
@@ -13,17 +11,29 @@ export default {
   },
   methods: {
     saveUser() {
+      let self = this
       if (this.password !== this.passwordVerify) {
-        //alerts.pushError('Error', "Passwords don't match!")
-        return
+        return self.$store.dispatch('alert', {
+          style: 'red',
+          icon: 'square-cross',
+          msg: 'The passwords don\'t match. Try again.'
+        })
       }
-      $.post(window.location.href, {
+      this.$http.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)
+      }).then(resp => {
+        self.$store.dispatch('alert', {
+          style: 'green',
+          icon: 'check',
+          msg: 'Changes have been applied successfully.'
+        })
+      }).catch(err => {
+        self.$store.dispatch('alert', {
+          style: 'red',
+          icon: 'square-cross',
+          msg: 'Error: ' + err.body.msg
+        })
       })
     }
   }

+ 1 - 3
client/js/pages/admin.js

@@ -7,9 +7,7 @@ import _ from 'lodash'
 import Vue from 'vue'
 
 module.exports = (alerts) => {
-  if ($('#page-type-admin-users').length) {
-    require('../modals/admin-users-create.js')(alerts)
-  } else if ($('#page-type-admin-users-edit').length) {
+  if ($('#page-type-admin-users-edit').length) {
     let vueEditUser = new Vue({
       el: '#page-type-admin-users-edit',
       data: {

+ 0 - 2
client/js/pages/view.js

@@ -4,8 +4,6 @@
 
 import $ from 'jquery'
 import MathJax from 'mathjax'
-// import * as CopyPath from '../components/copy-path.vue'
-import Vue from 'vue'
 
 module.exports = (alerts) => {
   if ($('#page-type-view').length) {

+ 25 - 0
client/js/store/index.js

@@ -0,0 +1,25 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+
+import alert from './modules/alert'
+import adminUsersCreate from './modules/admin-users-create'
+
+Vue.use(Vuex)
+
+export default new Vuex.Store({
+  state: {
+    loading: false
+  },
+  mutations: {
+    loadingChange: (state, loadingState) => { state.loading = loadingState }
+  },
+  actions: {
+    startLoading({ commit }) { commit('loadingChange', true) },
+    stopLoading({ commit }) { commit('loadingChange', false) }
+  },
+  getters: {},
+  modules: {
+    alert,
+    adminUsersCreate
+  }
+})

+ 15 - 0
client/js/store/modules/admin-users-create.js

@@ -0,0 +1,15 @@
+'use strict'
+
+export default {
+  state: {
+    shown: false
+  },
+  getters: {},
+  mutations: {
+    shownChange: (state, shownState) => { state.shown = shownState }
+  },
+  actions: {
+    adminUsersCreateOpen({ commit }) { commit('shownChange', true) },
+    adminUsersCreateClose({ commit }) { commit('shownChange', false) }
+  }
+}

+ 32 - 0
client/js/store/modules/alert.js

@@ -0,0 +1,32 @@
+'use strict'
+
+import _ from 'lodash'
+
+export default {
+  state: {
+    shown: false,
+    style: 'green',
+    icon: 'check',
+    msg: ''
+  },
+  getters: {},
+  mutations: {
+    alertChange: (state, opts) => {
+      state.shown = (opts.shown === true)
+      state.style = opts.style || 'green'
+      state.icon = opts.icon || 'check'
+      state.msg = opts.msg || ''
+    }
+  },
+  actions: {
+    alert({ commit, dispatch }, opts) {
+      opts.shown = true
+      commit('alertChange', opts)
+      dispatch('alertDismiss')
+    },
+    alertDismiss: _.debounce(({ commit }) => {
+      let opts = { shown: false }
+      commit('alertChange', opts)
+    }, 3000)
+  }
+}

+ 42 - 108
client/scss/components/alert.scss

@@ -1,114 +1,48 @@
-/*#alerts {
-	position: fixed;
-	top: 60px;
-	right: 10px;
-	width: 350px;
-	z-index: 10;
-	text-shadow: 1px 1px 0 rgba(0,0,0,0.1);
-
-	.notification {
-		animation: 0.5s ease slideInRight;
-		margin-top: 5px;
-
-		&.exit {
-			animation: 0.5s ease fadeOutRight;
-		}
-
-	}
-
-	h3 {
-		font-size: 16px;
-		font-size: 500;
-	}
-
-}*/
-
-#alerts {
-	position: fixed;
-	top: 55px;
-	right: 10px;
-	width: 350px;
-	z-index: 100;
-
-	> ul {
-		margin: 0;
-		padding: 0;
-		list-style-type: none;
-
-		> li {
-			background-color: material-color('blue-grey', '800');
-			box-shadow: 5px 5px 0 transparentize(material-color('blue-grey', '900'), 0.7);
-			border: 1px solid material-color('blue-grey', '500');
-			border-left-width: 5px;
-			margin-top: 5px;
-			padding: 8px 12px;
-			animation-name: slideFromRight;
-			animation-duration: 1s;
-			cursor: pointer;
-			position: relative;
-
-			&:hover {
-				background-color: material-color('blue-grey', '900');
-			}
-
-			&.exit {
-				animation-name: zoomOut;
-				animation-duration: 1s;
-				transform-origin: top center;
-			}
-
-			> button {
-        background-color: transparent;
-        border: none;
-        color: #FFF;
-        width: 15px;
-        height: 15px;
-        padding: 0;
-        position: absolute;
-        top: 10px;
-        right: 10px;
-
-        &:before {
-          content: 'X';
-        }
-
+.alert {
+  background-color: #FFF;
+  border-right: 3px solid mc('grey', '500');
+  position: fixed;
+  top: 60px;
+  margin-left: 10px;
+  right: 10px;
+  max-width: 500px;
+  z-index: 1000;
+  display: flex;
+  box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);
+  animation-duration: .6s;
+
+  &-icon {
+    width: 50px;
+    height: 50px;
+    background-color: mc('grey', '500');
+    color: #FFF;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+
+  &-msg {
+    padding: 0 15px;
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+    border-top: 1px solid mc('grey', '200');
+    font-size: 14px;
+    font-weight: 500;
+  }
+
+  @each $color, $colorvalue in $material-colors {
+		&.is-#{$color} {
+			border-right-color: mc($color, '500');
+
+      .alert-icon {
+        background-color: mc($color, '500');
+      }
+      .alert-msg {
+        color: mc($color, '900');
       }
-
-			> strong {
-				display: block;
-				font-size: 13px;
-				font-weight: 500;
-				color: #FFF;
-
-				> i {
-					margin-right: 5px;
-				}
-
-			}
-
-			> span {
-				font-size: 12px;
-				font-weight: 500;
-				color: material-color('blue-grey', '100');
-			}
-
-			&.error {
-				border-color: material-color('red', '400');
-				background-color: material-color('red', '600');
-				> span {
-					color: material-color('red', '50');
-				}
-			}
-			&.success {
-				border-color: material-color('green', '400');
-				background-color: material-color('green', '700');
-				> span {
-					color: material-color('green', '50');
-				}
-			}
 
 		}
-
 	}
 
 }

+ 2 - 1
package.json

@@ -69,7 +69,7 @@
     "i18next-express-middleware": "^1.0.5",
     "i18next-node-fs-backend": "^1.0.0",
     "image-size": "^0.5.4",
-    "jimp": "https://github.com/ngpixel/jimp.git",
+    "jimp": "^0.2.28",
     "js-yaml": "^3.8.4",
     "jsonwebtoken": "^7.4.1",
     "klaw": "^1.3.1",
@@ -157,6 +157,7 @@
     "uglify-js": "latest",
     "vee-validate": "^2.0.0-rc.3",
     "vue": "^2.3.3",
+    "vue-resource": "^1.3.1",
     "vue-template-compiler": "^2.3.3",
     "vue-template-es2015-compiler": "^1.5.2",
     "vuex": "^2.3.1"

+ 1 - 1
server/views/layout.pug

@@ -29,7 +29,7 @@ html
   body
     #root.has-stickynav
       include ./common/header.pug
-      //-include ./common/alerts.pug
+      alert
       main
         block content
       include ./common/footer.pug

+ 35 - 36
server/views/pages/admin/users.pug

@@ -3,45 +3,44 @@ extends ./_layout.pug
 block rootNavRight
   i.nav-item#notifload
   .nav-item
-    a.button.btn-create-prompt
+    a.button(v-on:click='$store.dispatch("adminUsersCreateOpen")')
       i.icon-plus
       span= t('admin:users.createauthorize')
 
 block adminContent
-  #page-type-admin-users
-    .hero
-      h1.title#title= t('nav.users')
-      h2.subtitle= t('admin:users.subtitle')
-    table.table
-      thead
+  .hero
+    h1.title#title= t('nav.users')
+    h2.subtitle= t('admin:users.subtitle')
+  table.table
+    thead
+      tr
+        th
+        th= t('admin:users.name')
+        th= t('admin:users.email')
+        th= t('admin:users.provider')
+        th= t('admin:users.createdon')
+        th= t('admin:users.updatedon')
+    tbody
+      each usr in usrs
         tr
-          th
-          th= t('admin:users.name')
-          th= t('admin:users.email')
-          th= t('admin:users.provider')
-          th= t('admin:users.createdon')
-          th= t('admin:users.updatedon')
-      tbody
-        each usr in usrs
-          tr
-            td.is-icon
-              i.icon-user.is-grey
-            td
-              a(href='/admin/users/' + usr._id)= usr.name
-            td= usr.email
-            td.is-centered.has-icons
-              case usr.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.' + usr.provider)
-            td.is-centered= moment(usr.createdAt).format('lll')
-            td.is-centered= moment(usr.updatedAt).format('lll')
+          td.is-icon
+            i.icon-user.is-grey
+          td
+            a(href='/admin/users/' + usr._id)= usr.name
+          td= usr.email
+          td.is-centered.has-icons
+            case usr.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.' + usr.provider)
+          td.is-centered= moment(usr.createdAt).format('lll')
+          td.is-centered= moment(usr.updatedAt).format('lll')
 
-    include ../../modals/admin-createuser.pug
+  admin-users-create

+ 44 - 13
yarn.lock

@@ -929,9 +929,9 @@ 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"
 
-bmp-js@0.0.2:
-  version "0.0.2"
-  resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.0.2.tgz#c268321f8085df177dfcaaa059c19254862fa158"
+bmp-js@0.0.3:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.0.3.tgz#64113e9c7cf1202b376ed607bf30626ebe57b18a"
 
 body-parser@^1.14.2, body-parser@^1.17.2:
   version "1.17.2"
@@ -1496,7 +1496,7 @@ crc@3.4.4, crc@^3.4.0:
   version "3.4.4"
   resolved "https://registry.yarnpkg.com/crc/-/crc-3.4.4.tgz#9da1e980e3bd44fc5c93bf5ab3da3378d85e466b"
 
-create-error-class@^3.0.1:
+create-error-class@^3.0.0, create-error-class@^3.0.1:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6"
   dependencies:
@@ -1778,6 +1778,10 @@ duplexer2@^0.1.4:
   dependencies:
     readable-stream "^2.0.2"
 
+duplexer3@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
+
 duplexer@~0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
@@ -2780,6 +2784,22 @@ got@^5.0.0:
     unzip-response "^1.0.2"
     url-parse-lax "^1.0.0"
 
+got@^6.7.1:
+  version "6.7.1"
+  resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0"
+  dependencies:
+    create-error-class "^3.0.0"
+    duplexer3 "^0.1.4"
+    get-stream "^3.0.0"
+    is-redirect "^1.0.0"
+    is-retry-allowed "^1.0.0"
+    is-stream "^1.0.0"
+    lowercase-keys "^1.0.0"
+    safe-buffer "^5.0.1"
+    timed-out "^4.0.0"
+    unzip-response "^2.0.1"
+    url-parse-lax "^1.0.0"
+
 graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.4, 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"
@@ -3586,18 +3606,19 @@ jest@latest:
   dependencies:
     jest-cli "^20.0.3"
 
-"jimp@https://github.com/ngpixel/jimp.git":
-  version "0.2.27"
-  resolved "https://github.com/ngpixel/jimp.git#d5c3e98eb45875dce412397ff56d8a2caa714916"
+jimp@^0.2.28:
+  version "0.2.28"
+  resolved "https://registry.yarnpkg.com/jimp/-/jimp-0.2.28.tgz#dd529a937190f42957a7937d1acc3a7762996ea2"
   dependencies:
     bignumber.js "^2.1.0"
-    bmp-js "0.0.2"
+    bmp-js "0.0.3"
     es6-promise "^3.0.2"
     exif-parser "^0.1.9"
     file-type "^3.1.0"
     jpeg-js "^0.2.0"
     load-bmfont "^1.2.3"
     mime "^1.3.4"
+    mkdirp "0.5.1"
     pixelmatch "^4.0.0"
     pngjs "^3.0.0"
     read-chunk "^1.0.1"
@@ -4345,14 +4366,10 @@ mime-types@^2.1.12, mime-types@^2.1.15, mime-types@~2.1.11, mime-types@~2.1.15,
   dependencies:
     mime-db "~1.27.0"
 
-mime@1.3.4:
+mime@1.3.4, mime@^1.3.4:
   version "1.3.4"
   resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53"
 
-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"
@@ -6620,6 +6637,10 @@ timed-out@^3.0.0:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-3.1.3.tgz#95860bfcc5c76c277f8f8326fd0f5b2e20eba217"
 
+timed-out@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
+
 tinycolor2@^1.1.2:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8"
@@ -6801,6 +6822,10 @@ unzip-response@^1.0.0, unzip-response@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-1.0.2.tgz#b984f0877fc0a89c2c773cc1ef7b5b232b5b06fe"
 
+unzip-response@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97"
+
 update-notifier@0.5.0, update-notifier@^0.5.0:
   version "0.5.0"
   resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-0.5.0.tgz#07b5dc2066b3627ab3b4f530130f7eddda07a4cc"
@@ -6967,6 +6992,12 @@ void-elements@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
 
+vue-resource@^1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/vue-resource/-/vue-resource-1.3.1.tgz#bf2f7b70bfe21b397c9d7607878f776a3acea2cf"
+  dependencies:
+    got "^6.7.1"
+
 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"