فهرست منبع

chore: purge legacy code

NGPixel 2 سال پیش
والد
کامیت
b979e50830
100فایلهای تغییر یافته به همراه21 افزوده شده و 15448 حذف شده
  1. 0 38
      .babelrc
  2. 7 0
      .devcontainer/app-init.sh
  3. 0 2
      .npmrc
  4. 1 1
      .vscode/tasks.json
  5. 12 30
      README.md
  6. 1 0
      SECURITY.md
  7. 0 7
      client/.modernizrrc.js
  8. 0 216
      client/client-app.js
  9. 0 553
      client/components/comments.vue
  10. 0 119
      client/components/common/duration-picker.vue
  11. 0 67
      client/components/common/loader.vue
  12. 0 566
      client/components/common/nav-header.vue
  13. 0 62
      client/components/common/notify.vue
  14. 0 120
      client/components/common/page-convert.vue
  15. 0 125
      client/components/common/page-delete.vue
  16. 0 329
      client/components/common/page-selector.vue
  17. 0 79
      client/components/common/password-strength.vue
  18. 0 246
      client/components/common/search-results.vue
  19. 0 106
      client/components/common/social-sharing.vue
  20. 0 136
      client/components/common/user-search.vue
  21. 0 10
      client/components/common/v-card-chin.vue
  22. 0 64
      client/components/common/v-card-info.vue
  23. 0 568
      client/components/editor.vue
  24. 0 9
      client/components/editor/api/server-selector.vue
  25. 0 129
      client/components/editor/ckeditor/conflict.vue
  26. 0 147
      client/components/editor/common/katex.js
  27. 0 433
      client/components/editor/editor-api.vue
  28. 0 253
      client/components/editor/editor-ckeditor.vue
  29. 0 452
      client/components/editor/editor-code.vue
  30. 0 1141
      client/components/editor/editor-markdown.vue
  31. 0 81
      client/components/editor/editor-modal-blocks.vue
  32. 0 222
      client/components/editor/editor-modal-conflict.vue
  33. 0 126
      client/components/editor/editor-modal-drawio.vue
  34. 0 90
      client/components/editor/editor-modal-editorselect.vue
  35. 0 629
      client/components/editor/editor-modal-media.vue
  36. 0 447
      client/components/editor/editor-modal-properties.vue
  37. 0 40
      client/components/editor/editor-modal-unsaved.vue
  38. 0 224
      client/components/editor/editor-redirect.vue
  39. 0 62
      client/components/editor/markdown/fold.js
  40. 0 352
      client/components/editor/markdown/help.vue
  41. 0 190
      client/components/editor/markdown/plantuml.js
  42. 0 16
      client/components/editor/markdown/tabset.js
  43. 0 577
      client/components/history.vue
  44. 0 37
      client/components/new-page.vue
  45. 0 24
      client/components/not-found.vue
  46. 0 106
      client/components/source.vue
  47. 0 341
      client/components/tags.vue
  48. 0 33
      client/components/unauthorized.vue
  49. BIN
      client/fonts/arabic/BalooBhaijaan-Regular.woff
  50. BIN
      client/fonts/arabic/BalooBhaijaan-Regular.woff2
  51. BIN
      client/fonts/arabic/Tajawal-Bold.woff
  52. BIN
      client/fonts/arabic/Tajawal-Bold.woff2
  53. BIN
      client/fonts/arabic/Tajawal-Medium.woff
  54. BIN
      client/fonts/arabic/Tajawal-Medium.woff2
  55. BIN
      client/fonts/arabic/Tajawal-Regular.woff
  56. BIN
      client/fonts/arabic/Tajawal-Regular.woff2
  57. BIN
      client/fonts/default/Roboto-Bold.woff
  58. BIN
      client/fonts/default/Roboto-Bold.woff2
  59. BIN
      client/fonts/default/Roboto-BoldItalic.woff
  60. BIN
      client/fonts/default/Roboto-BoldItalic.woff2
  61. BIN
      client/fonts/default/Roboto-Italic.woff
  62. BIN
      client/fonts/default/Roboto-Italic.woff2
  63. BIN
      client/fonts/default/Roboto-Medium.woff
  64. BIN
      client/fonts/default/Roboto-Medium.woff2
  65. BIN
      client/fonts/default/Roboto-MediumItalic.woff
  66. BIN
      client/fonts/default/Roboto-MediumItalic.woff2
  67. BIN
      client/fonts/default/Roboto-Regular.woff
  68. BIN
      client/fonts/default/Roboto-Regular.woff2
  69. BIN
      client/fonts/default/RobotoMono-Regular.woff
  70. BIN
      client/fonts/default/RobotoMono-Regular.woff2
  71. 0 8
      client/graph/common/common-localization-query-translations.gql
  72. 0 12
      client/graph/common/common-pages-mutation-delete.gql
  73. 0 12
      client/graph/common/common-pages-mutation-move.gql
  74. 0 14
      client/graph/common/common-pages-query-list.gql
  75. 0 15
      client/graph/common/common-pages-query-search.gql
  76. 0 8
      client/graph/common/common-pages-query-tags.gql
  77. 0 12
      client/graph/common/common-pages-query-tree.gql
  78. 0 12
      client/graph/editor/editor-media-mutation-asset-delete.gql
  79. 0 12
      client/graph/editor/editor-media-mutation-asset-rename.gql
  80. 0 12
      client/graph/editor/editor-media-mutation-folder-create.gql
  81. 0 9
      client/graph/editor/editor-media-query-folder-list.gql
  82. 0 14
      client/graph/editor/editor-media-query-list.gql
  83. 0 8
      client/helpers/compatibility.js
  84. 0 66
      client/helpers/index.js
  85. 0 25
      client/index-app.js
  86. 0 3508
      client/libs/animate/animate.scss
  87. 0 60
      client/libs/codemirror-merge/diff-match-patch.js
  88. 0 12
      client/libs/markdown-it-underline/index.js
  89. 0 3
      client/libs/modernizr/modernizr.js
  90. 0 172
      client/libs/prism/prism.css
  91. 0 2
      client/libs/prism/prism.js
  92. 0 1106
      client/libs/twemoji/twemoji-awesome.scss
  93. 0 68
      client/modules/boot.js
  94. 0 65
      client/modules/localization.js
  95. 0 78
      client/polyfills/array-from.js
  96. 0 31
      client/scss/app.scss
  97. 0 12
      client/scss/base/animation.scss
  98. 0 86
      client/scss/base/base.scss
  99. 0 68
      client/scss/base/icons.scss
  100. 0 333
      client/scss/base/material.scss

+ 0 - 38
.babelrc

@@ -1,38 +0,0 @@
-{
-  "comments": true,
-  "plugins": [
-    "lodash",
-    "graphql-tag",
-    "@babel/plugin-syntax-dynamic-import",
-    "@babel/plugin-syntax-import-meta",
-    "@babel/plugin-proposal-class-properties",
-    "@babel/plugin-proposal-json-strings",
-    [
-      "@babel/plugin-proposal-decorators",
-      {
-        "legacy": true
-      }
-    ],
-    "@babel/plugin-proposal-function-sent",
-    "@babel/plugin-proposal-export-namespace-from",
-    "@babel/plugin-proposal-numeric-separator",
-    "@babel/plugin-proposal-throw-expressions",
-    [
-      "prismjs", {
-        "languages": ["clike", "markup"],
-        "plugins": ["line-numbers", "autoloader", "normalize-whitespace", "copy-to-clipboard", "toolbar"],
-        "theme": "twilight",
-        "css": true
-      }
-    ]
-  ],
-  "presets": [
-    [
-      "@babel/preset-env", {
-        "useBuiltIns": "entry",
-        "corejs": 3,
-        "debug": false
-      }
-    ]
-  ]
-}

+ 7 - 0
.devcontainer/app-init.sh

@@ -9,4 +9,11 @@ git config oh-my-zsh.hide-info 1
 echo "Waiting for DB container to come online..."
 /usr/local/bin/wait-for localhost:5432 -- echo "DB ready"
 
+echo "Installing dependencies..."
+cd server
+npm install
+cd ../ux
+npm install
+cd ..
+
 echo "Ready!"

+ 0 - 2
.npmrc

@@ -1,2 +0,0 @@
-save-exact = true
-save-prefix = ""

+ 1 - 1
.vscode/tasks.json

@@ -33,7 +33,7 @@
       "args": [],
       // Set the shell type
       "options": {
-        "cwd": "/workspace"
+        "cwd": "/workspace/server"
       },
       // Mark as a background task to avoid the spinner animation on the terminal tab
       "isBackground": true,

+ 12 - 30
README.md

@@ -43,7 +43,6 @@ The current stable release (2.x) is available at https://js.wiki
 ### Requirements
 
 - Node.js **18.x** or later
-- Yarn
 - PostgreSQL **11** or later
 
 ### Usage
@@ -53,18 +52,18 @@ The current stable release (2.x) is available at https://js.wiki
 1. Edit `config.yml` and fill in the database details. **You need an empty PostgreSQL database.**
 1. Run the following commands to install dependencies and generate the client assets:
     ```sh
-    yarn
-    yarn legacy:build
-    cd ux
-    yarn
-    yarn build
+    cd server
+    npm install
+    cd ../ux
+    npm install
+    npm run build
     cd ..
     ```
 1. Run this command to start the server:
     ```sh
     node server
     ```
-1. In your browser, navigate to `http://localhost:5000` *(or the IP/hostname of the server and the PORT you defined earlier.)*
+1. In your browser, navigate to `http://localhost:3000` *(or the IP/hostname of the server and the PORT you defined earlier.)*
 1. Login using the default administrator user:
     - Email: `admin@example.com`
     - Password: `12345678`
@@ -93,17 +92,13 @@ The current stable release (2.x) is available at https://js.wiki
 1. Two terminals will launch in split-screen mode at the bottom of the screen. **Server** on the left and **UX** on the right.
 1. In the left-side terminal (Server), run the command:
     ```sh
-    yarn legacy:build
+    npm run dev
     ```
 1. In the right-side terminal (UX), run the command:
     ```sh
-    yarn build
+    npm run dev
     ```
-1. Back in the left-side terminal (Server), run the command:
-    ```sh
-    yarn dev
-    ```
-1. Open your browser to `http://localhost:5000`
+1. Open your browser to `http://localhost:3000`
 1. Login using the default administrator user:
     - Email: `admin@example.com`
     - Password: `12345678`
@@ -115,7 +110,7 @@ The current stable release (2.x) is available at https://js.wiki
 From the left-side terminal (Server), run the command:
 
 ```sh
-yarn dev
+npm run dev
 ```
 
 This will launch the server and automatically restart upon modification of any server files.
@@ -124,31 +119,18 @@ Only precompiled client assets are served in this mode. See the sections below o
 
 ### Frontend Development (Quasar/Vue 3)
 
-> Make sure you are running `yarn dev` in the left-side terminal (Server) first! Requests still need to be forwarded to the server, even in SPA mode!
+> Make sure you are running `npm run dev` in the left-side terminal (Server) first! Requests still need to be forwarded to the server, even in SPA mode!
 
 If you wish to modify any frontend content (under `/ux`), you need to start the Quasar Dev Server in the right-side terminal (UX):
 
 ```sh
-yarn dev
+npm run dev
 ```
 
 You can then access the site at `http://localhost:5001`. Notice the port being `5001` rather than `5000`. The app runs in a SPA (single-page application) mode and automatically hot-reload any modified component. Any requests made to the `/graphql` endpoint are automatically forwarded to the server running on port `5000`, which is why both must be running at the same time.
 
-Note that not all sections/features are available from this mode, notably the page editing features which still relies on the old client code (Vuetify/Vue 2). For example, trying to edit a page will simply not work. You must use the normal mode (port 5000) to edit pages as it relies on legacy client code. As more features gets ported / developed for Vue 3, they will become available in the SPA mode.
-
 Any change you make to the frontend will not be reflected on port 5000 until you run the command `yarn build` in the right-side terminal.
 
-### Legacy Frontend Development (Vuetify/Vue 2)
-
-Client code from Wiki.js 2.x is located under `/client`. Some sections still rely on this legacy code (notably the page editing features). Code is gradually being removed from this location and replaced with newer code in `/ux`.
-
-In the unlikely event that you need to modify legacy code and regenerate the old client files, you can do so by running in this command in the left-side terminal (Server):
-```sh
-yarn legacy:build
-```
-
-Then run `yarn dev` to start the server again.
-
 ### pgAdmin
 
 A web version of pgAdmin (a PostgreSQL administration tool) is available at `http://localhost:8000`. Use the login `dev` / `123123` to login.

+ 1 - 0
SECURITY.md

@@ -8,6 +8,7 @@ If you find such vulnerability, it's important to disclose it in a quick and sec
 
 | Version | Supported          |
 | ------- | ------------------ |
+| 3.x.x   | :white_check_mark: |
 | 2.x.x   | :white_check_mark: |
 | 1.x.x   | :x:                |
 

+ 0 - 7
client/.modernizrrc.js

@@ -1,7 +0,0 @@
-module.exports = {
-  classPrefix: 'mdz-',
-  options: ['setClasses'],
-  'feature-detects': [
-    'css/backdropfilter'
-  ]
-}

+ 0 - 216
client/client-app.js

@@ -1,216 +0,0 @@
-/* global siteConfig */
-
-import Vue from 'vue'
-import VueRouter from 'vue-router'
-import VueClipboards from 'vue-clipboards'
-import { ApolloClient } from 'apollo-client'
-import { BatchHttpLink } from 'apollo-link-batch-http'
-import { ApolloLink } from 'apollo-link'
-import { ErrorLink } from 'apollo-link-error'
-import { InMemoryCache } from 'apollo-cache-inmemory'
-import VueApollo from 'vue-apollo'
-import Vuetify from 'vuetify/lib'
-import Velocity from 'velocity-animate'
-import Vuescroll from 'vuescroll/dist/vuescroll-native'
-import Hammer from 'hammerjs'
-import moment from 'moment-timezone'
-import VueMoment from 'vue-moment'
-import store from './store'
-import Cookies from 'js-cookie'
-
-// ====================================
-// Load Modules
-// ====================================
-
-import boot from './modules/boot'
-import localization from './modules/localization'
-
-// ====================================
-// Load Helpers
-// ====================================
-
-import helpers from './helpers'
-
-// ====================================
-// Initialize Global Vars
-// ====================================
-
-window.WIKI = null
-window.boot = boot
-window.Hammer = Hammer
-
-moment.locale(siteConfig.lang)
-
-store.commit('user/REFRESH_AUTH')
-
-// ====================================
-// Initialize Apollo Client (GraphQL)
-// ====================================
-
-const graphQLEndpoint = window.location.protocol + '//' + window.location.host + '/_graphql'
-
-const graphQLLink = ApolloLink.from([
-  new ErrorLink(({ graphQLErrors, networkError }) => {
-    if (graphQLErrors) {
-      let isAuthError = false
-      graphQLErrors.map(({ message, locations, path }) => {
-        if (message === `Forbidden`) {
-          isAuthError = true
-        }
-        console.error(`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`)
-      })
-      store.commit('showNotification', {
-        style: 'red',
-        message: isAuthError ? `You are not authorized to access this resource.` : `An unexpected error occurred.`,
-        icon: 'alert'
-      })
-    }
-    if (networkError) {
-      console.error(networkError)
-      store.commit('showNotification', {
-        style: 'red',
-        message: `Network Error: ${networkError.message}`,
-        icon: 'alert'
-      })
-    }
-  }),
-  new BatchHttpLink({
-    includeExtensions: true,
-    uri: graphQLEndpoint,
-    credentials: 'include',
-    fetch: async (uri, options) => {
-      // Strip __typename fields from variables
-      let body = JSON.parse(options.body)
-      body = body.map(bd => {
-        return ({
-          ...bd,
-          variables: JSON.parse(JSON.stringify(bd.variables), (key, value) => { return key === '__typename' ? undefined : value })
-        })
-      })
-      options.body = JSON.stringify(body)
-
-      // Inject authentication token
-      const jwtToken = Cookies.get('jwt')
-      if (jwtToken) {
-        options.headers.Authorization = `Bearer ${jwtToken}`
-      }
-
-      const resp = await fetch(uri, options)
-
-      // Handle renewed JWT
-      const newJWT = resp.headers.get('new-jwt')
-      if (newJWT) {
-        Cookies.set('jwt', newJWT, { expires: 365 })
-      }
-      return resp
-    }
-  })
-])
-
-window.graphQL = new ApolloClient({
-  link: graphQLLink,
-  cache: new InMemoryCache(),
-  connectToDevTools: (process.env.node_env === 'development')
-})
-
-// ====================================
-// Initialize Vue Modules
-// ====================================
-
-Vue.config.productionTip = false
-
-Vue.use(VueRouter)
-Vue.use(VueApollo)
-Vue.use(VueClipboards)
-Vue.use(localization.VueI18Next)
-Vue.use(helpers)
-Vue.use(Vuetify)
-Vue.use(VueMoment, { moment })
-Vue.use(Vuescroll)
-
-Vue.prototype.Velocity = Velocity
-
-// ====================================
-// Register Vue Components
-// ====================================
-
-Vue.component('Comments', () => import(/* webpackChunkName: "comments" */ './components/comments.vue'))
-Vue.component('Editor', () => import(/* webpackPrefetch: -100, webpackChunkName: "editor" */ './components/editor.vue'))
-Vue.component('History', () => import(/* webpackChunkName: "history" */ './components/history.vue'))
-Vue.component('Loader', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/loader.vue'))
-Vue.component('NavHeader', () => import(/* webpackMode: "eager" */ './components/common/nav-header.vue'))
-Vue.component('NewPage', () => import(/* webpackChunkName: "new-page" */ './components/new-page.vue'))
-Vue.component('Notify', () => import(/* webpackMode: "eager" */ './components/common/notify.vue'))
-Vue.component('NotFound', () => import(/* webpackChunkName: "not-found" */ './components/not-found.vue'))
-Vue.component('PageSelector', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/page-selector.vue'))
-Vue.component('PageSource', () => import(/* webpackChunkName: "source" */ './components/source.vue'))
-Vue.component('SearchResults', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/search-results.vue'))
-Vue.component('SocialSharing', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/social-sharing.vue'))
-Vue.component('Tags', () => import(/* webpackChunkName: "tags" */ './components/tags.vue'))
-Vue.component('Unauthorized', () => import(/* webpackChunkName: "unauthorized" */ './components/unauthorized.vue'))
-Vue.component('VCardChin', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/v-card-chin.vue'))
-Vue.component('VCardInfo', () => import(/* webpackPrefetch: true, webpackChunkName: "ui-extra" */ './components/common/v-card-info.vue'))
-
-Vue.component('NavFooter', () => import(/* webpackChunkName: "theme" */ './themes/' + siteConfig.theme + '/components/nav-footer.vue'))
-Vue.component('Page', () => import(/* webpackChunkName: "theme" */ './themes/' + siteConfig.theme + '/components/page.vue'))
-
-let bootstrap = () => {
-  // ====================================
-  // Notifications
-  // ====================================
-
-  window.addEventListener('beforeunload', () => {
-    store.dispatch('startLoading')
-  })
-
-  const apolloProvider = new VueApollo({
-    defaultClient: window.graphQL
-  })
-
-  // ====================================
-  // Bootstrap Vue
-  // ====================================
-
-  const i18n = localization.init()
-
-  let darkModeEnabled = siteConfig.darkMode
-  if ((store.get('user/appearance') || '').length > 0) {
-    darkModeEnabled = (store.get('user/appearance') === 'dark')
-  }
-
-  window.WIKI = new Vue({
-    el: '#root',
-    components: {},
-    mixins: [helpers],
-    apolloProvider,
-    store,
-    i18n,
-    vuetify: new Vuetify({
-      rtl: siteConfig.rtl,
-      theme: {
-        dark: darkModeEnabled
-      }
-    }),
-    mounted () {
-      this.$moment.locale(siteConfig.lang)
-      if ((store.get('user/dateFormat') || '').length > 0) {
-        this.$moment.updateLocale(this.$moment.locale(), {
-          longDateFormat: {
-            'L': store.get('user/dateFormat')
-          }
-        })
-      }
-      if ((store.get('user/timezone') || '').length > 0) {
-        this.$moment.tz.setDefault(store.get('user/timezone'))
-      }
-    }
-  })
-
-  // ----------------------------------
-  // Dispatch boot ready
-  // ----------------------------------
-
-  window.boot.notify('vue')
-}
-
-window.boot.onDOMReady(bootstrap)

+ 0 - 553
client/components/comments.vue

@@ -1,553 +0,0 @@
-<template lang="pug">
-  div(v-intersect.once='onIntersect')
-    v-textarea#discussion-new(
-      outlined
-      flat
-      :placeholder='$t(`common:comments.newPlaceholder`)'
-      auto-grow
-      dense
-      rows='3'
-      hide-details
-      v-model='newcomment'
-      color='blue-grey darken-2'
-      :background-color='$vuetify.theme.dark ? `grey darken-5` : `white`'
-      v-if='permissions.write'
-      :aria-label='$t(`common:comments.fieldContent`)'
-    )
-    v-row.mt-2(dense, v-if='!isAuthenticated && permissions.write')
-      v-col(cols='12', lg='6')
-        v-text-field(
-          outlined
-          color='blue-grey darken-2'
-          :background-color='$vuetify.theme.dark ? `grey darken-5` : `white`'
-          :placeholder='$t(`common:comments.fieldName`)'
-          hide-details
-          dense
-          autocomplete='name'
-          v-model='guestName'
-          :aria-label='$t(`common:comments.fieldName`)'
-        )
-      v-col(cols='12', lg='6')
-        v-text-field(
-          outlined
-          color='blue-grey darken-2'
-          :background-color='$vuetify.theme.dark ? `grey darken-5` : `white`'
-          :placeholder='$t(`common:comments.fieldEmail`)'
-          hide-details
-          type='email'
-          dense
-          autocomplete='email'
-          v-model='guestEmail'
-          :aria-label='$t(`common:comments.fieldEmail`)'
-        )
-    .d-flex.align-center.pt-3(v-if='permissions.write')
-      v-icon.mr-1(color='blue-grey') mdi-language-markdown-outline
-      .caption.blue-grey--text {{$t('common:comments.markdownFormat')}}
-      v-spacer
-      .caption.mr-3(v-if='isAuthenticated')
-        i18next(tag='span', path='common:comments.postingAs')
-          strong(place='name') {{userDisplayName}}
-      v-btn(
-        dark
-        color='blue-grey darken-2'
-        @click='postComment'
-        depressed
-        :aria-label='$t(`common:comments.postComment`)'
-        )
-        v-icon(left) mdi-comment
-        span.text-none {{$t('common:comments.postComment')}}
-    v-divider.mt-3(v-if='permissions.write')
-    .pa-5.d-flex.align-center.justify-center(v-if='isLoading && !hasLoadedOnce')
-      v-progress-circular(
-        indeterminate
-        size='20'
-        width='1'
-        color='blue-grey'
-      )
-      .caption.blue-grey--text.pl-3: em {{$t('common:comments.loading')}}
-    v-timeline(
-      dense
-      v-else-if='comments && comments.length > 0'
-      )
-      v-timeline-item.comments-post(
-        color='pink darken-4'
-        large
-        v-for='cm of comments'
-        :key='`comment-` + cm.id'
-        :id='`comment-post-id-` + cm.id'
-        )
-        template(v-slot:icon)
-          v-avatar(color='blue-grey')
-            //- v-img(src='http://i.pravatar.cc/64')
-            span.white--text.title {{cm.initials}}
-        v-card.elevation-1
-          v-card-text
-            .comments-post-actions(v-if='permissions.manage && !isBusy && commentEditId === 0')
-              v-icon.mr-3(small, @click='editComment(cm)') mdi-pencil
-              v-icon(small, @click='deleteCommentConfirm(cm)') mdi-delete
-            .comments-post-name.caption: strong {{cm.authorName}}
-            .comments-post-date.overline.grey--text {{cm.createdAt | moment('from') }} #[em(v-if='cm.createdAt !== cm.updatedAt') - {{$t('common:comments.modified', { reldate: $options.filters.moment(cm.updatedAt, 'from') })}}]
-            .comments-post-content.mt-3(v-if='commentEditId !== cm.id', v-html='cm.render')
-            .comments-post-editcontent.mt-3(v-else)
-              v-textarea(
-                outlined
-                flat
-                auto-grow
-                dense
-                rows='3'
-                hide-details
-                v-model='commentEditContent'
-                color='blue-grey darken-2'
-                :background-color='$vuetify.theme.dark ? `grey darken-5` : `white`'
-              )
-              .d-flex.align-center.pt-3
-                v-spacer
-                v-btn.mr-3(
-                  dark
-                  color='blue-grey darken-2'
-                  @click='editCommentCancel'
-                  outlined
-                  )
-                  v-icon(left) mdi-close
-                  span.text-none {{$t('common:actions.cancel')}}
-                v-btn(
-                  dark
-                  color='blue-grey darken-2'
-                  @click='updateComment'
-                  depressed
-                  )
-                  v-icon(left) mdi-comment
-                  span.text-none {{$t('common:comments.updateComment')}}
-    .pt-5.text-center.body-2.blue-grey--text(v-else-if='permissions.write') {{$t('common:comments.beFirst')}}
-    .text-center.body-2.blue-grey--text(v-else) {{$t('common:comments.none')}}
-
-    v-dialog(v-model='deleteCommentDialogShown', max-width='500')
-      v-card
-        .dialog-header.is-red {{$t('common:comments.deleteConfirmTitle')}}
-        v-card-text.pt-5
-          span {{$t('common:comments.deleteWarn')}}
-          .caption: strong {{$t('common:comments.deletePermanentWarn')}}
-        v-card-chin
-          v-spacer
-          v-btn(text, @click='deleteCommentDialogShown = false') {{$t('common:actions.cancel')}}
-          v-btn(color='red', dark, @click='deleteComment') {{$t('common:actions.delete')}}
-</template>
-
-<script>
-import gql from 'graphql-tag'
-import { get } from 'vuex-pathify'
-import validate from 'validate.js'
-import _ from 'lodash'
-
-export default {
-  data () {
-    return {
-      newcomment: '',
-      isLoading: true,
-      hasLoadedOnce: false,
-      comments: [],
-      guestName: '',
-      guestEmail: '',
-      commentToDelete: {},
-      commentEditId: 0,
-      commentEditContent: null,
-      deleteCommentDialogShown: false,
-      isBusy: false,
-      scrollOpts: {
-        duration: 1500,
-        offset: 0,
-        easing: 'easeInOutCubic'
-      }
-    }
-  },
-  computed: {
-    pageId: get('page/id'),
-    permissions: get('page/effectivePermissions@comments'),
-    isAuthenticated: get('user/authenticated'),
-    userDisplayName: get('user/name')
-  },
-  methods: {
-    onIntersect (entries, observer, isIntersecting) {
-      if (isIntersecting) {
-        this.fetch(true)
-      }
-    },
-    async fetch (silent = false) {
-      this.isLoading = true
-      try {
-        const results = await this.$apollo.query({
-          query: gql`
-            query ($locale: String!, $path: String!) {
-              comments {
-                list(locale: $locale, path: $path) {
-                  id
-                  render
-                  authorName
-                  createdAt
-                  updatedAt
-                }
-              }
-            }
-          `,
-          variables: {
-            locale: this.$store.get('page/locale'),
-            path: this.$store.get('page/path')
-          },
-          fetchPolicy: 'network-only'
-        })
-        this.comments = _.get(results, 'data.comments.list', []).map(c => {
-          const nameParts = c.authorName.toUpperCase().split(' ')
-          let initials = _.head(nameParts).charAt(0)
-          if (nameParts.length > 1) {
-            initials += _.last(nameParts).charAt(0)
-          }
-          c.initials = initials
-          return c
-        })
-      } catch (err) {
-        console.warn(err)
-        if (!silent) {
-          this.$store.commit('showNotification', {
-            style: 'red',
-            message: err.message,
-            icon: 'alert'
-          })
-        }
-      }
-      this.isLoading = false
-      this.hasLoadedOnce = true
-    },
-    /**
-     * Post New Comment
-     */
-    async postComment () {
-      let rules = {
-        comment: {
-          presence: {
-            allowEmpty: false
-          },
-          length: {
-            minimum: 2
-          }
-        }
-      }
-      if (!this.isAuthenticated && this.permissions.write) {
-        rules.name = {
-          presence: {
-            allowEmpty: false
-          },
-          length: {
-            minimum: 2,
-            maximum: 255
-          }
-        }
-        rules.email = {
-          presence: {
-            allowEmpty: false
-          },
-          email: true
-        }
-      }
-      const validationResults = validate({
-        comment: this.newcomment,
-        name: this.guestName,
-        email: this.guestEmail
-      }, rules, { format: 'flat' })
-
-      if (validationResults) {
-        this.$store.commit('showNotification', {
-          style: 'red',
-          message: validationResults[0],
-          icon: 'alert'
-        })
-        return
-      }
-
-      try {
-        const resp = await this.$apollo.mutate({
-          mutation: gql`
-            mutation (
-              $pageId: Int!
-              $replyTo: Int
-              $content: String!
-              $guestName: String
-              $guestEmail: String
-            ) {
-              comments {
-                create (
-                  pageId: $pageId
-                  replyTo: $replyTo
-                  content: $content
-                  guestName: $guestName
-                  guestEmail: $guestEmail
-                ) {
-                  responseResult {
-                    succeeded
-                    errorCode
-                    slug
-                    message
-                  }
-                  id
-                }
-              }
-            }
-          `,
-          variables: {
-            pageId: this.pageId,
-            replyTo: 0,
-            content: this.newcomment,
-            guestName: this.guestName,
-            guestEmail: this.guestEmail
-          }
-        })
-
-        if (_.get(resp, 'data.comments.create.responseResult.succeeded', false)) {
-          this.$store.commit('showNotification', {
-            style: 'success',
-            message: this.$t('common:comments.postSuccess'),
-            icon: 'check'
-          })
-
-          this.newcomment = ''
-          await this.fetch()
-          this.$nextTick(() => {
-            this.$vuetify.goTo(`#comment-post-id-${_.get(resp, 'data.comments.create.id', 0)}`, this.scrollOpts)
-          })
-        } else {
-          throw new Error(_.get(resp, 'data.comments.create.responseResult.message', 'An unexpected error occurred.'))
-        }
-      } catch (err) {
-        this.$store.commit('showNotification', {
-          style: 'red',
-          message: err.message,
-          icon: 'alert'
-        })
-      }
-    },
-    /**
-     * Show Comment Editing Form
-     */
-    async editComment (cm) {
-      this.$store.commit(`loadingStart`, 'comments-edit')
-      this.isBusy = true
-      try {
-        const results = await this.$apollo.query({
-          query: gql`
-            query ($id: Int!) {
-              comments {
-                single(id: $id) {
-                  content
-                }
-              }
-            }
-          `,
-          variables: {
-            id: cm.id
-          },
-          fetchPolicy: 'network-only'
-        })
-        this.commentEditContent = _.get(results, 'data.comments.single.content', null)
-        if (this.commentEditContent === null) {
-          throw new Error('Failed to load comment content.')
-        }
-      } catch (err) {
-        console.warn(err)
-        this.$store.commit('showNotification', {
-          style: 'red',
-          message: err.message,
-          icon: 'alert'
-        })
-      }
-      this.commentEditId = cm.id
-      this.isBusy = false
-      this.$store.commit(`loadingStop`, 'comments-edit')
-    },
-    /**
-     * Cancel Comment Edit
-     */
-    editCommentCancel () {
-      this.commentEditId = 0
-      this.commentEditContent = null
-    },
-    /**
-     * Update Comment with new content
-     */
-    async updateComment () {
-      this.$store.commit(`loadingStart`, 'comments-edit')
-      this.isBusy = true
-      try {
-        if (this.commentEditContent.length < 2) {
-          throw new Error(this.$t('common:comments.contentMissingError'))
-        }
-        const resp = await this.$apollo.mutate({
-          mutation: gql`
-            mutation (
-              $id: Int!
-              $content: String!
-            ) {
-              comments {
-                update (
-                  id: $id,
-                  content: $content
-                ) {
-                  responseResult {
-                    succeeded
-                    errorCode
-                    slug
-                    message
-                  }
-                  render
-                }
-              }
-            }
-          `,
-          variables: {
-            id: this.commentEditId,
-            content: this.commentEditContent
-          }
-        })
-
-        if (_.get(resp, 'data.comments.update.responseResult.succeeded', false)) {
-          this.$store.commit('showNotification', {
-            style: 'success',
-            message: this.$t('common:comments.updateSuccess'),
-            icon: 'check'
-          })
-
-          const cm = _.find(this.comments, ['id', this.commentEditId])
-          cm.render = _.get(resp, 'data.comments.update.render', '-- Failed to load updated comment --')
-          cm.updatedAt = (new Date()).toISOString()
-
-          this.editCommentCancel()
-        } else {
-          throw new Error(_.get(resp, 'data.comments.delete.responseResult.message', 'An unexpected error occurred.'))
-        }
-      } catch (err) {
-        console.warn(err)
-        this.$store.commit('showNotification', {
-          style: 'red',
-          message: err.message,
-          icon: 'alert'
-        })
-      }
-      this.isBusy = false
-      this.$store.commit(`loadingStop`, 'comments-edit')
-    },
-    /**
-     * Show Delete Comment Confirmation Dialog
-     */
-    deleteCommentConfirm (cm) {
-      this.commentToDelete = cm
-      this.deleteCommentDialogShown = true
-    },
-    /**
-     * Delete Comment
-     */
-    async deleteComment () {
-      this.$store.commit(`loadingStart`, 'comments-delete')
-      this.isBusy = true
-      this.deleteCommentDialogShown = false
-
-      try {
-        const resp = await this.$apollo.mutate({
-          mutation: gql`
-            mutation (
-              $id: Int!
-            ) {
-              comments {
-                delete (
-                  id: $id
-                ) {
-                  responseResult {
-                    succeeded
-                    errorCode
-                    slug
-                    message
-                  }
-                }
-              }
-            }
-          `,
-          variables: {
-            id: this.commentToDelete.id
-          }
-        })
-
-        if (_.get(resp, 'data.comments.delete.responseResult.succeeded', false)) {
-          this.$store.commit('showNotification', {
-            style: 'success',
-            message: this.$t('common:comments.deleteSuccess'),
-            icon: 'check'
-          })
-
-          this.comments = _.reject(this.comments, ['id', this.commentToDelete.id])
-        } else {
-          throw new Error(_.get(resp, 'data.comments.delete.responseResult.message', 'An unexpected error occurred.'))
-        }
-      } catch (err) {
-        this.$store.commit('showNotification', {
-          style: 'red',
-          message: err.message,
-          icon: 'alert'
-        })
-      }
-      this.isBusy = false
-      this.$store.commit(`loadingStop`, 'comments-delete')
-    }
-  }
-}
-</script>
-
-<style lang="scss">
-.comments-post {
-  position: relative;
-
-  &:hover {
-    .comments-post-actions {
-      opacity: 1;
-    }
-  }
-
-  &-actions {
-    position: absolute;
-    top: 16px;
-    right: 16px;
-    opacity: 0;
-    transition: opacity .4s ease;
-  }
-
-  &-content {
-    > p:first-child {
-      padding-top: 0;
-    }
-
-    p {
-      padding-top: 1rem;
-      margin-bottom: 0;
-    }
-
-    img {
-      max-width: 100%;
-      border-radius: 5px;
-    }
-
-    code {
-      background-color: rgba(mc('pink', '500'), .1);
-      box-shadow: none;
-    }
-
-    pre > code {
-      margin-top: 1rem;
-      padding: 12px;
-      background-color: #111;
-      box-shadow: none;
-      border-radius: 5px;
-      width: 100%;
-      color: #FFF;
-      font-weight: 400;
-      font-size: .85rem;
-      font-family: Roboto Mono, monospace;
-    }
-  }
-}
-</style>

+ 0 - 119
client/components/common/duration-picker.vue

@@ -1,119 +0,0 @@
-<template lang='pug'>
-  v-toolbar.radius-7(flat, :color='$vuetify.theme.dark ? "grey darken-4-l3" : "grey lighten-3"')
-    .body-2.mr-3 {{$t('common:duration.every')}}
-    v-text-field(
-      solo
-      hide-details
-      flat
-      reverse
-      v-model='minutes'
-      style='flex: 1 1 70px;'
-    )
-    .body-2.mx-3 {{$t('common:duration.minutes')}}
-    v-divider.mr-3
-    v-text-field(
-      solo
-      hide-details
-      flat
-      reverse
-      v-model='hours'
-      style='flex: 1 1 70px;'
-    )
-    .body-2.mx-3 {{$t('common:duration.hours')}}
-    v-divider.mr-3
-    v-text-field(
-      solo
-      hide-details
-      flat
-      reverse
-      v-model='days'
-      style='flex: 1 1 70px;'
-    )
-    .body-2.mx-3 {{$t('common:duration.days')}}
-    v-divider.mr-3
-    v-text-field(
-      solo
-      hide-details
-      flat
-      reverse
-      v-model='months'
-      style='flex: 1 1 70px;'
-    )
-    .body-2.mx-3 {{$t('common:duration.months')}}
-    v-divider.mr-3
-    v-text-field(
-      solo
-      hide-details
-      flat
-      reverse
-      v-model='years'
-      style='flex: 1 1 70px;'
-    )
-    .body-2.mx-3 {{$t('common:duration.years')}}
-</template>
-
-<script>
-import _ from 'lodash'
-import moment from 'moment'
-
-export default {
-  props: {
-    value: {
-      type: String,
-      default: 'PT5M'
-    }
-  },
-  data() {
-    return {
-      duration: moment.duration(0)
-    }
-  },
-  computed: {
-    years: {
-      get() { return this.duration.years() || 0 },
-      set(val) { this.rebuild(_.toNumber(val), 'years') }
-    },
-    months: {
-      get() { return this.duration.months() || 0 },
-      set(val) { this.rebuild(_.toNumber(val), 'months') }
-    },
-    days: {
-      get() { return this.duration.days() || 0 },
-      set(val) { this.rebuild(_.toNumber(val), 'days') }
-    },
-    hours: {
-      get() { return this.duration.hours() || 0 },
-      set(val) { this.rebuild(_.toNumber(val), 'hours') }
-    },
-    minutes: {
-      get() { return this.duration.minutes() || 0 },
-      set(val) { this.rebuild(_.toNumber(val), 'minutes') }
-    }
-  },
-  watch: {
-    value(newValue, oldValue) {
-      this.duration = moment.duration(newValue)
-    }
-  },
-  methods: {
-    rebuild(val, unit) {
-      if (!_.isFinite(val) || val < 0) {
-        val = 0
-      }
-      const newDuration = {
-        minutes: this.duration.minutes(),
-        hours: this.duration.hours(),
-        days: this.duration.days(),
-        months: this.duration.months(),
-        years: this.duration.years()
-      }
-      _.set(newDuration, unit, val)
-      this.duration = moment.duration(newDuration)
-      this.$emit('input', this.duration.toISOString())
-    }
-  },
-  mounted() {
-    this.duration = moment.duration(this.value)
-  }
-}
-</script>

+ 0 - 67
client/components/common/loader.vue

@@ -1,67 +0,0 @@
-<template lang='pug'>
-  v-dialog(v-model='value', persistent, max-width='350', :overlay-color='color', overlay-opacity='.7')
-    v-card.loader-dialog.radius-7(:color='color', dark)
-      v-card-text.text-center.py-4
-        atom-spinner.is-inline(
-          v-if='mode === `loading`'
-          :animation-duration='1000'
-          :size='60'
-          color='#FFF'
-          )
-        img(v-else-if='mode === `icon`', :src='`/_assets-legacy/svg/icon-` + icon + `.svg`', :alt='icon')
-        .subtitle-1.white--text {{ title }}
-        .caption {{ subtitle }}
-</template>
-
-<script>
-import { AtomSpinner } from 'epic-spinners'
-
-export default {
-  components: {
-    AtomSpinner
-  },
-  props: {
-    value: {
-      type: Boolean,
-      default: false
-    },
-    color: {
-      type: String,
-      default: 'blue darken-3'
-    },
-    title: {
-      type: String,
-      default: 'Working...'
-    },
-    subtitle: {
-      type: String,
-      default: 'Please wait'
-    },
-    mode: {
-      type: String,
-      default: 'loading'
-    },
-    icon: {
-      type: String,
-      default: 'checkmark'
-    }
-  }
-}
-</script>
-
-<style lang='scss'>
-  .loader-dialog {
-    transition: all .4s ease;
-
-    .atom-spinner.is-inline {
-      display: inline-block;
-    }
-    .caption {
-      color: rgba(255,255,255,.7);
-    }
-
-    img {
-      width: 80px;
-    }
-  }
-</style>

+ 0 - 566
client/components/common/nav-header.vue

@@ -1,566 +0,0 @@
-<template lang='pug'>
-  v-app-bar.nav-header(color='black', dark, app, :clipped-left='!$vuetify.rtl', :clipped-right='$vuetify.rtl', fixed, flat, :extended='searchIsShown && $vuetify.breakpoint.smAndDown')
-    v-toolbar(color='deep-purple', flat, slot='extension', v-if='searchIsShown && $vuetify.breakpoint.smAndDown')
-      v-text-field(
-        ref='searchFieldMobile'
-        v-model='search'
-        clearable
-        background-color='deep-purple'
-        color='white'
-        :label='$t(`common:header.search`)'
-        single-line
-        solo
-        flat
-        hide-details
-        prepend-inner-icon='mdi-magnify'
-        :loading='searchIsLoading'
-        @keyup.enter='searchEnter'
-        autocomplete='none'
-      )
-    v-layout(row)
-      v-flex(xs5, md4)
-        v-toolbar.nav-header-inner(color='black', dark, flat, :class='$vuetify.rtl ? `pr-3` : `pl-3`')
-          v-avatar(tile, size='34', @click='goHome')
-            v-img.org-logo(:src='logoUrl')
-          //- v-menu(open-on-hover, offset-y, bottom, left, min-width='250', transition='slide-y-transition')
-          //-   template(v-slot:activator='{ on }')
-          //-     v-app-bar-nav-icon.btn-animate-app(v-on='on', :class='$vuetify.rtl ? `mx-0` : ``')
-          //-       v-icon mdi-menu
-          //-   v-list(nav, :light='!$vuetify.theme.dark', :dark='$vuetify.theme.dark', :class='$vuetify.theme.dark ? `grey darken-4` : ``')
-          //-     v-list-item.pl-4(href='/')
-          //-       v-list-item-avatar(size='24'): v-icon(color='blue') mdi-home
-          //-       v-list-item-title.body-2 {{$t('common:header.home')}}
-          //-     v-list-item.pl-4(@click='')
-          //-       v-list-item-avatar(size='24'): v-icon(color='grey lighten-2') mdi-file-tree
-          //-       v-list-item-content
-          //-         v-list-item-title.body-2.grey--text.text--ligten-2 {{$t('common:header.siteMap')}}
-          //-         v-list-item-subtitle.overline.grey--text.text--lighten-2 Coming soon
-          //-     v-list-item.pl-4(href='/t')
-          //-       v-list-item-avatar(size='24'): v-icon(color='teal') mdi-tag-multiple
-          //-       v-list-item-title.body-2 {{$t('common:header.browseTags')}}
-          //-     v-list-item.pl-4(@click='assets')
-          //-       v-list-item-avatar(size='24'): v-icon(color='grey lighten-2') mdi-folder-multiple-image
-          //-       v-list-item-content
-          //-         v-list-item-title.body-2.grey--text.text--ligten-2 {{$t('common:header.imagesFiles')}}
-          //-         v-list-item-subtitle.overline.grey--text.text--lighten-2 Coming soon
-          v-toolbar-title(:class='{ "mx-3": $vuetify.breakpoint.mdAndUp, "mx-1": $vuetify.breakpoint.smAndDown }')
-            span.subheading {{title}}
-      v-flex(md4, v-if='$vuetify.breakpoint.mdAndUp')
-        v-toolbar.nav-header-inner(color='black', dark, flat)
-          slot(name='mid')
-            transition(name='navHeaderSearch', v-if='searchIsShown')
-              v-text-field(
-                ref='searchField',
-                v-if='searchIsShown && $vuetify.breakpoint.mdAndUp',
-                v-model='search',
-                color='white',
-                :label='$t(`common:header.search`)',
-                single-line,
-                solo
-                flat
-                rounded
-                hide-details,
-                prepend-inner-icon='mdi-magnify',
-                :loading='searchIsLoading',
-                @keyup.enter='searchEnter'
-                @keyup.esc='searchClose'
-                @focus='searchFocus'
-                @blur='searchBlur'
-                @keyup.down='searchMove(`down`)'
-                @keyup.up='searchMove(`up`)'
-                autocomplete='none'
-              )
-            v-tooltip(bottom)
-              template(v-slot:activator='{ on }')
-                v-btn.ml-2.mr-0(icon, v-on='on', href='/t', :aria-label='$t(`common:header.browseTags`)')
-                  v-icon(color='grey') mdi-tag-multiple
-              span {{$t('common:header.browseTags')}}
-      v-flex(xs7, md4)
-        v-toolbar.nav-header-inner.pr-4(color='black', dark, flat)
-          v-spacer
-          .navHeaderLoading.mr-3
-            v-progress-circular(indeterminate, color='blue', :size='22', :width='2' v-show='isLoading')
-
-          slot(name='actions')
-
-          //- (mobile) SEARCH TOGGLE
-
-          v-btn(
-            v-if='!hideSearch && $vuetify.breakpoint.smAndDown'
-            @click='searchToggle'
-            icon
-            )
-            v-icon(color='grey') mdi-magnify
-
-          //- LANGUAGES
-
-          template(v-if='mode === `view` && locales.length > 0')
-            v-menu(offset-y, bottom, transition='slide-y-transition', max-height='320px', min-width='210px', left)
-              template(v-slot:activator='{ on: menu, attrs }')
-                v-tooltip(bottom)
-                  template(v-slot:activator='{ on: tooltip }')
-                    v-btn(
-                      icon
-                      v-bind='attrs'
-                      v-on='{ ...menu, ...tooltip }'
-                      :class='$vuetify.rtl ? `ml-3` : ``'
-                      tile
-                      height='64'
-                      :aria-label='$t(`common:header.language`)'
-                      )
-                      v-icon(color='grey') mdi-web
-                  span {{$t('common:header.language')}}
-              v-list(nav)
-                template(v-for='(lc, idx) of locales')
-                  v-list-item(@click='changeLocale(lc)')
-                    v-list-item-action(style='min-width:auto;'): v-chip(:color='lc.code === locale ? `blue` : `grey`', small, label, dark) {{lc.code.toUpperCase()}}
-                    v-list-item-title {{lc.name}}
-            v-divider(vertical)
-
-          //- PAGE ACTIONS
-
-          template(v-if='hasAnyPagePermissions && path && mode !== `edit`')
-            v-menu(offset-y, bottom, transition='slide-y-transition', left)
-              template(v-slot:activator='{ on: menu, attrs }')
-                v-tooltip(bottom)
-                  template(v-slot:activator='{ on: tooltip }')
-                    v-btn(
-                      icon
-                      v-bind='attrs'
-                      v-on='{ ...menu, ...tooltip }'
-                      :class='$vuetify.rtl ? `ml-3` : ``'
-                      tile
-                      height='64'
-                      :aria-label='$t(`common:header.pageActions`)'
-                      )
-                      v-icon(color='grey') mdi-file-document-edit-outline
-                  span {{$t('common:header.pageActions')}}
-              v-list(nav, :light='!$vuetify.theme.dark', :dark='$vuetify.theme.dark', :class='$vuetify.theme.dark ? `grey darken-4` : ``')
-                .overline.pa-4.grey--text {{$t('common:header.currentPage')}}
-                v-list-item.pl-4(@click='pageView', v-if='mode !== `view`')
-                  v-list-item-avatar(size='24', tile): v-icon(color='indigo') mdi-file-document-outline
-                  v-list-item-title.body-2 {{$t('common:header.view')}}
-                v-list-item.pl-4(@click='pageEdit', v-if='mode !== `edit` && hasWritePagesPermission')
-                  v-list-item-avatar(size='24', tile): v-icon(color='indigo') mdi-file-document-edit-outline
-                  v-list-item-title.body-2 {{$t('common:header.edit')}}
-                v-list-item.pl-4(@click='pageHistory', v-if='mode !== `history` && hasReadHistoryPermission')
-                  v-list-item-avatar(size='24', tile): v-icon(color='indigo') mdi-history
-                  v-list-item-content
-                    v-list-item-title.body-2 {{$t('common:header.history')}}
-                v-list-item.pl-4(@click='pageSource', v-if='mode !== `source` && hasReadSourcePermission')
-                  v-list-item-avatar(size='24', tile): v-icon(color='indigo') mdi-code-tags
-                  v-list-item-title.body-2 {{$t('common:header.viewSource')}}
-                v-list-item.pl-4(@click='pageConvert', v-if='hasWritePagesPermission')
-                  v-list-item-avatar(size='24', tile): v-icon(color='indigo') mdi-lightning-bolt
-                  v-list-item-title.body-2 {{$t('common:header.convert')}}
-                v-list-item.pl-4(@click='pageDuplicate', v-if='hasWritePagesPermission')
-                  v-list-item-avatar(size='24', tile): v-icon(color='indigo') mdi-content-duplicate
-                  v-list-item-title.body-2 {{$t('common:header.duplicate')}}
-                v-list-item.pl-4(@click='pageMove', v-if='hasManagePagesPermission')
-                  v-list-item-avatar(size='24', tile): v-icon(color='indigo') mdi-content-save-move-outline
-                  v-list-item-content
-                    v-list-item-title.body-2 {{$t('common:header.move')}}
-                v-list-item.pl-4(@click='pageDelete', v-if='hasDeletePagesPermission')
-                  v-list-item-avatar(size='24', tile): v-icon(color='red darken-2') mdi-trash-can-outline
-                  v-list-item-title.body-2 {{$t('common:header.delete')}}
-            v-divider(vertical)
-
-          //- NEW PAGE
-
-          template(v-if='hasNewPagePermission && path && mode !== `edit`')
-            v-tooltip(bottom)
-              template(v-slot:activator='{ on }')
-                v-btn(icon, tile, height='64', v-on='on', @click='pageNew', :aria-label='$t(`common:header.newPage`)')
-                  v-icon(color='grey') mdi-text-box-plus-outline
-              span {{$t('common:header.newPage')}}
-            v-divider(vertical)
-
-          //- ADMIN
-
-          template(v-if='isAuthenticated && isAdmin')
-            v-tooltip(bottom, v-if='mode !== `admin`')
-              template(v-slot:activator='{ on }')
-                v-btn(icon, tile, height='64', v-on='on', href='/a', :aria-label='$t(`common:header.admin`)')
-                  v-icon(color='grey') mdi-cog
-              span {{$t('common:header.admin')}}
-            v-btn(v-else, text, tile, height='64', href='/', :aria-label='$t(`common:actions.exit`)')
-              v-icon(left, color='grey') mdi-exit-to-app
-              span {{$t('common:actions.exit')}}
-            v-divider(vertical)
-
-          //- ACCOUNT
-
-          v-menu(v-if='isAuthenticated', offset-y, bottom, min-width='300', transition='slide-y-transition', left)
-            template(v-slot:activator='{ on: menu, attrs }')
-              v-tooltip(bottom)
-                template(v-slot:activator='{ on: tooltip }')
-                  v-btn(
-                    icon
-                    v-bind='attrs'
-                    v-on='{ ...menu, ...tooltip }'
-                    :class='$vuetify.rtl ? `ml-0` : ``'
-                    tile
-                    height='64'
-                    :aria-label='$t(`common:header.account`)'
-                    )
-                    v-icon(v-if='picture.kind === `initials`', color='grey') mdi-account-circle
-                    v-avatar(v-else-if='picture.kind === `image`', :size='34')
-                      v-img(:src='picture.url')
-                span {{$t('common:header.account')}}
-            v-list(nav)
-              v-list-item.py-3.grey(:class='$vuetify.theme.dark ? `darken-4-l5` : `lighten-5`')
-                v-list-item-avatar
-                  v-avatar.blue(v-if='picture.kind === `initials`', :size='40')
-                    span.white--text.subheading {{picture.initials}}
-                  v-avatar(v-else-if='picture.kind === `image`', :size='40')
-                    v-img(:src='picture.url')
-                v-list-item-content
-                  v-list-item-title {{name}}
-                  v-list-item-subtitle {{email}}
-              //- v-list-item(href='/w', disabled)
-              //-   v-list-item-action: v-icon(color='blue') mdi-view-compact-outline
-              //-   v-list-item-content
-              //-     v-list-item-title {{$t('common:header.myWiki')}}
-              //-     v-list-item-subtitle.overline Coming soon
-              v-list-item(href='/_profile')
-                v-list-item-action: v-icon(color='blue-grey') mdi-face-profile
-                v-list-item-content
-                  v-list-item-title(:class='$vuetify.theme.dark ? `blue-grey--text text--lighten-3` : `blue-grey--text`') {{$t('common:header.profile')}}
-              v-list-item(href='/logout')
-                v-list-item-action: v-icon(color='red') mdi-logout
-                v-list-item-title.red--text {{$t('common:header.logout')}}
-
-          v-tooltip(v-else, left)
-            template(v-slot:activator='{ on }')
-              v-btn(icon, v-on='on', color='grey darken-3', href='/login', :aria-label='$t(`common:header.login`)')
-                v-icon(color='grey') mdi-account-circle
-            span {{$t('common:header.login')}}
-
-    page-selector(mode='create', v-model='newPageModal', :open-handler='pageNewCreate', :locale='locale')
-    page-selector(mode='move', v-model='movePageModal', :open-handler='pageMoveRename', :path='path', :locale='locale')
-    page-selector(mode='create', v-model='duplicateOpts.modal', :open-handler='pageDuplicateHandle', :path='duplicateOpts.path', :locale='duplicateOpts.locale')
-    page-delete(v-model='deletePageModal', v-if='path && path.length')
-    page-convert(v-model='convertPageModal', v-if='path && path.length')
-</template>
-
-<script>
-import { get, sync } from 'vuex-pathify'
-import _ from 'lodash'
-
-import movePageMutation from 'gql/common/common-pages-mutation-move.gql'
-
-/* global siteConfig, siteLangs */
-
-export default {
-  components: {
-    PageDelete: () => import('./page-delete.vue'),
-    PageConvert: () => import('./page-convert.vue')
-  },
-  props: {
-    dense: {
-      type: Boolean,
-      default: false
-    },
-    hideSearch: {
-      type: Boolean,
-      default: false
-    }
-  },
-  data() {
-    return {
-      menuIsShown: true,
-      searchIsShown: true,
-      searchAdvMenuShown: false,
-      newPageModal: false,
-      movePageModal: false,
-      convertPageModal: false,
-      deletePageModal: false,
-      locales: siteLangs,
-      isDevMode: false,
-      duplicateOpts: {
-        locale: 'en',
-        path: 'new-page',
-        modal: false
-      }
-    }
-  },
-  computed: {
-    search: sync('site/search'),
-    searchIsFocused: sync('site/searchIsFocused'),
-    searchIsLoading: sync('site/searchIsLoading'),
-    searchRestrictLocale: sync('site/searchRestrictLocale'),
-    searchRestrictPath: sync('site/searchRestrictPath'),
-    isLoading: get('isLoading'),
-    title: get('site/title'),
-    logoUrl: get('site/logoUrl'),
-    path: get('page/path'),
-    locale: get('page/locale'),
-    mode: get('page/mode'),
-    name: get('user/name'),
-    email: get('user/email'),
-    pictureUrl: get('user/pictureUrl'),
-    isAuthenticated: get('user/authenticated'),
-    permissions: get('user/permissions'),
-    picture () {
-      if (this.pictureUrl && this.pictureUrl.length > 1) {
-        return {
-          kind: 'image',
-          url: (this.pictureUrl === 'internal') ? `/_userav/${this.$store.get('user/id')}` : this.pictureUrl
-        }
-      } else {
-        const nameParts = ['X', 'X'] // this.name.toUpperCase().split(' ')
-        let initials = _.head(nameParts).charAt(0)
-        if (nameParts.length > 1) {
-          initials += _.last(nameParts).charAt(0)
-        }
-        return {
-          kind: 'initials',
-          initials
-        }
-      }
-    },
-    isAdmin () {
-      return _.intersection(this.permissions, ['manage:system', 'write:users', 'manage:users', 'write:groups', 'manage:groups', 'manage:navigation', 'manage:theme', 'manage:api']).length > 0
-    },
-    hasNewPagePermission () {
-      return this.hasAdminPermission || _.intersection(this.permissions, ['write:pages']).length > 0
-    },
-    hasAdminPermission: get('page/effectivePermissions@system.manage'),
-    hasWritePagesPermission: get('page/effectivePermissions@pages.write'),
-    hasManagePagesPermission: get('page/effectivePermissions@pages.manage'),
-    hasDeletePagesPermission: get('page/effectivePermissions@pages.delete'),
-    hasReadSourcePermission: get('page/effectivePermissions@source.read'),
-    hasReadHistoryPermission: get('page/effectivePermissions@history.read'),
-    hasAnyPagePermissions () {
-      return this.hasAdminPermission || this.hasWritePagesPermission || this.hasManagePagesPermission ||
-        this.hasDeletePagesPermission || this.hasReadSourcePermission || this.hasReadHistoryPermission
-    }
-  },
-  created () {
-    if (this.hideSearch || this.dense || this.$vuetify.breakpoint.smAndDown) {
-      this.searchIsShown = false
-    }
-  },
-  mounted () {
-    this.$root.$on('pageEdit', () => {
-      this.pageEdit()
-    })
-    this.$root.$on('pageHistory', () => {
-      this.pageHistory()
-    })
-    this.$root.$on('pageSource', () => {
-      this.pageSource()
-    })
-    this.$root.$on('pageMove', () => {
-      this.pageMove()
-    })
-    this.$root.$on('pageConvert', () => {
-      this.pageConvert()
-    })
-    this.$root.$on('pageDuplicate', () => {
-      this.pageDuplicate()
-    })
-    this.$root.$on('pageDelete', () => {
-      this.pageDelete()
-    })
-    this.isDevMode = siteConfig.devMode === true
-  },
-  methods: {
-    searchFocus () {
-      this.searchIsFocused = true
-    },
-    searchBlur () {
-      this.searchIsFocused = false
-    },
-    searchClose () {
-      this.search = ''
-      this.searchBlur()
-    },
-    searchToggle () {
-      this.searchIsShown = !this.searchIsShown
-      if (this.searchIsShown) {
-        _.delay(() => {
-          this.$refs.searchFieldMobile.focus()
-        }, 200)
-      }
-    },
-    searchEnter () {
-      this.$root.$emit('searchEnter', true)
-    },
-    searchMove(dir) {
-      this.$root.$emit('searchMove', dir)
-    },
-    pageNew () {
-      this.newPageModal = true
-    },
-    pageNewCreate ({ path, locale }) {
-      window.location.assign(`/e/${locale}/${path}`)
-    },
-    pageView () {
-      window.location.assign(`/${this.locale}/${this.path}`)
-    },
-    pageEdit () {
-      window.location.assign(`/e/${this.locale}/${this.path}`)
-    },
-    pageHistory () {
-      window.location.assign(`/h/${this.locale}/${this.path}`)
-    },
-    pageSource () {
-      window.location.assign(`/s/${this.locale}/${this.path}`)
-    },
-    pageDuplicate () {
-      const pathParts = this.path.split('/')
-      this.duplicateOpts = {
-        locale: this.locale,
-        path: (pathParts.length > 1) ? _.initial(pathParts).join('/') + `/new-page` : `new-page`,
-        modal: true
-      }
-    },
-    pageDuplicateHandle ({ locale, path }) {
-      window.location.assign(`/e/${locale}/${path}?from=${this.$store.get('page/id')}`)
-    },
-    pageConvert () {
-      this.convertPageModal = true
-    },
-    pageMove () {
-      this.movePageModal = true
-    },
-    async pageMoveRename ({ path, locale }) {
-      this.$store.commit(`loadingStart`, 'page-move')
-      try {
-        const resp = await this.$apollo.mutate({
-          mutation: movePageMutation,
-          variables: {
-            id: this.$store.get('page/id'),
-            destinationLocale: locale,
-            destinationPath: path
-          }
-        })
-        if (_.get(resp, 'data.pages.move.responseResult.succeeded', false)) {
-          window.location.replace(`/${locale}/${path}`)
-        } else {
-          throw new Error(_.get(resp, 'data.pages.move.responseResult.message', this.$t('common:error.unexpected')))
-        }
-      } catch (err) {
-        this.$store.commit('pushGraphError', err)
-        this.$store.commit(`loadingStop`, 'page-move')
-      }
-    },
-    pageDelete () {
-      this.deletePageModal = true
-    },
-    assets () {
-      // window.location.assign(`/f`)
-      this.$store.commit('showNotification', {
-        style: 'indigo',
-        message: `Coming soon...`,
-        icon: 'ferry'
-      })
-    },
-    async changeLocale (locale) {
-      await this.$i18n.i18next.changeLanguage(locale.code)
-      switch (this.mode) {
-        case 'view':
-        case 'history':
-          window.location.assign(`/${locale.code}/${this.path}`)
-          break
-      }
-    },
-    logout () {
-      window.location.assign('/logout')
-    },
-    goHome () {
-      window.location.assign('/')
-    }
-  }
-}
-</script>
-
-<style lang='scss'>
-
-.nav-header {
-  //z-index: 1000;
-
-  .v-toolbar__extension {
-    padding: 0;
-
-    .v-toolbar__content {
-      padding: 0;
-    }
-    .v-text-field .v-input__prepend-inner {
-      padding: 0 14px 0 5px;
-      padding-right: 14px;
-    }
-  }
-
-  .org-logo {
-    cursor: pointer;
-  }
-
-  &-inner {
-    .v-toolbar__content {
-      padding: 0;
-    }
-  }
-
-  &-search-adv {
-    position: absolute;
-    top: 7px;
-    right: 12px;
-    border-radius: 4px !important;
-
-    @at-root .v-application--is-rtl & {
-      right: initial;
-      left: 12px;
-    }
-
-    &::before {
-      border-radius: 4px !important;
-    }
-
-    &:hover, &:focus {
-      position: absolute !important;
-
-      &::before {
-        border-radius: 4px;
-      }
-    }
-  }
-
-  &-dev {
-    background-color: mc('red', '600');
-    position: absolute;
-    top: 11px;
-    left: 255px;
-    padding: 5px 15px;
-    border-radius: 5px;
-    display: flex;
-
-    .v-icon {
-      margin-right: 15px;
-    }
-
-    .overline:nth-child(2) {
-      text-transform: none;
-    }
-  }
-}
-
-.navHeaderSearch {
-  &-enter-active, &-leave-active {
-    transition: opacity .25s ease, transform .25s ease;
-    opacity: 1;
-  }
-  &-enter-active {
-    transition-delay: .25s;
-  }
-  &-enter, &-leave-to {
-    opacity: 0;
-    transform: scale(.7, .7);
-  }
-}
-.navHeaderLoading { // To avoid search bar jumping
-  width: 22px;
-}
-
-</style>

+ 0 - 62
client/components/common/notify.vue

@@ -1,62 +0,0 @@
-<template lang='pug'>
-  v-snackbar.nav-notify(
-    :color='notification.style'
-    top
-    multi-line
-    v-model='notificationState'
-    :timeout='6000'
-    )
-    .text-left
-      v-icon.mr-3(dark) mdi-{{ notification.icon }}
-      span {{ notification.message }}
-</template>
-
-<script>
-import { get, sync } from 'vuex-pathify'
-
-export default {
-  data() {
-    return { }
-  },
-  computed: {
-    notification: get('notification'),
-    notificationState: sync('notification@isActive')
-  }
-}
-</script>
-
-<style lang='scss'>
-.nav-notify {
-  top: -64px;
-  padding-top: 0;
-  z-index: 999;
-
-  .v-snack__wrapper {
-    border-top-left-radius: 0;
-    border-top-right-radius: 0;
-    position: relative;
-    margin-top: 0;
-
-    &::after {
-      content: '';
-      display: block;
-      width: 100%;
-      height: 2px;
-      background-color: rgba(255,255,255,.4);
-      position: absolute;
-      bottom: 0;
-      left: 0;
-      animation: nav-notify-anim 6s linear;
-    }
-  }
-}
-
-@keyframes nav-notify-anim {
-  0% {
-    width: 100%;
-  }
-  100% {
-    width: 0%;
-  }
-}
-</style>

+ 0 - 120
client/components/common/page-convert.vue

@@ -1,120 +0,0 @@
-<template lang='pug'>
-  v-dialog(
-    v-model='isShown'
-    max-width='550'
-    persistent
-    overlay-color='blue-grey darken-4'
-    overlay-opacity='.7'
-    )
-    v-card
-      .dialog-header.is-short.is-dark
-        v-icon.mr-2(color='white') mdi-lightning-bolt
-        span {{$t('common:page.convert')}}
-      v-card-text.pt-5
-        i18next.body-2(path='common:page.convertTitle', tag='div')
-          span.blue-grey--text.text--darken-2(place='title') {{pageTitle}}
-        v-select.mt-5(
-          :items=`[
-            { value: 'markdown', text: 'Markdown' },
-            { value: 'ckeditor', text: 'Visual Editor' },
-            { value: 'code', text: 'Raw HTML' }
-          ]`
-          outlined
-          dense
-          hide-details
-          v-model='newEditor'
-        )
-        .caption.mt-5 {{$t('common:page.convertSubtitle')}}
-      v-card-chin
-        v-spacer
-        v-btn(text, @click='discard', :disabled='loading') {{$t('common:actions.cancel')}}
-        v-btn.px-4(color='grey darken-3', @click='convertPage', :loading='loading').white--text {{$t('common:actions.convert')}}
-</template>
-
-<script>
-import _ from 'lodash'
-import { get } from 'vuex-pathify'
-import gql from 'graphql-tag'
-
-export default {
-  props: {
-    value: {
-      type: Boolean,
-      default: false
-    }
-  },
-  data() {
-    return {
-      loading: false,
-      newEditor: ''
-    }
-  },
-  computed: {
-    isShown: {
-      get() { return this.value },
-      set(val) { this.$emit('input', val) }
-    },
-    pageTitle: get('page/title'),
-    pagePath: get('page/path'),
-    pageLocale: get('page/locale'),
-    pageId: get('page/id'),
-    pageEditor: get('page/editor')
-  },
-  mounted () {
-    this.newEditor = this.pageEditor
-  },
-  methods: {
-    discard() {
-      this.isShown = false
-    },
-    async convertPage() {
-      this.loading = true
-      this.$store.commit(`loadingStart`, 'page-convert')
-      this.$nextTick(async () => {
-        try {
-          const resp = await this.$apollo.mutate({
-            mutation: gql`
-              mutation (
-                $id: Int!
-                $editor: String!
-                ) {
-                  pages {
-                    convert(
-                      id: $id
-                      editor: $editor
-                    ) {
-                      responseResult {
-                        succeeded
-                        errorCode
-                        slug
-                        message
-                      }
-                    }
-                  }
-              }
-            `,
-            variables: {
-              id: this.pageId,
-              editor: this.newEditor
-            }
-          })
-          if (_.get(resp, 'data.pages.convert.responseResult.succeeded', false)) {
-            this.isShown = false
-            window.location.assign(`/e/${this.pageLocale}/${this.pagePath}`)
-          } else {
-            throw new Error(_.get(resp, 'data.pages.convert.responseResult.message', this.$t('common:error.unexpected')))
-          }
-        } catch (err) {
-          this.$store.commit('pushGraphError', err)
-        }
-        this.$store.commit(`loadingStop`, 'page-convert')
-        this.loading = false
-      })
-    }
-  }
-}
-</script>
-
-<style lang='scss'>
-
-</style>

+ 0 - 125
client/components/common/page-delete.vue

@@ -1,125 +0,0 @@
-<template lang='pug'>
-  v-dialog(
-    v-model='isShown'
-    max-width='550'
-    persistent
-    overlay-color='red darken-4'
-    overlay-opacity='.7'
-    )
-    v-card
-      .dialog-header.is-short.is-red
-        v-icon.mr-2(color='white') mdi-file-document-box-remove-outline
-        span {{$t('common:page.delete')}}
-      v-card-text.pt-5
-        i18next.body-1(path='common:page.deleteTitle', tag='div')
-          span.red--text.text--darken-2(place='title') {{pageTitle}}
-        .caption {{$t('common:page.deleteSubtitle')}}
-        v-chip.mt-3.ml-0.mr-1(label, color='red lighten-4', small)
-          .caption.red--text.text--darken-2 {{pageLocale.toUpperCase()}}
-        v-chip.mt-3.mx-0(label, color='red lighten-5', small)
-          span.red--text.text--darken-2 /{{pagePath}}
-      v-card-chin
-        v-spacer
-        v-btn(text, @click='discard', :disabled='loading') {{$t('common:actions.cancel')}}
-        v-btn.px-4(color='red darken-2', @click='deletePage', :loading='loading').white--text {{$t('common:actions.delete')}}
-</template>
-
-<script>
-import _ from 'lodash'
-import { get } from 'vuex-pathify'
-
-import deletePageMutation from 'gql/common/common-pages-mutation-delete.gql'
-
-export default {
-  props: {
-    value: {
-      type: Boolean,
-      default: false
-    }
-  },
-  data() {
-    return {
-      loading: false
-    }
-  },
-  computed: {
-    isShown: {
-      get() { return this.value },
-      set(val) { this.$emit('input', val) }
-    },
-    pageTitle: get('page/title'),
-    pagePath: get('page/path'),
-    pageLocale: get('page/locale'),
-    pageId: get('page/id')
-  },
-  watch: {
-    isShown(newValue, oldValue) {
-      if (newValue) {
-        document.body.classList.add('page-deleted-pending')
-      }
-    }
-  },
-  methods: {
-    discard() {
-      document.body.classList.remove('page-deleted-pending')
-      this.isShown = false
-    },
-    async deletePage() {
-      this.loading = true
-      this.$store.commit(`loadingStart`, 'page-delete')
-      this.$nextTick(async () => {
-        try {
-          const resp = await this.$apollo.mutate({
-            mutation: deletePageMutation,
-            variables: {
-              id: this.pageId
-            }
-          })
-          if (_.get(resp, 'data.pages.delete.responseResult.succeeded', false)) {
-            this.isShown = false
-            _.delay(() => {
-              document.body.classList.add('page-deleted')
-              _.delay(() => {
-                window.location.assign('/')
-              }, 1200)
-            }, 400)
-          } else {
-            throw new Error(_.get(resp, 'data.pages.delete.responseResult.message', this.$t('common:error.unexpected')))
-          }
-        } catch (err) {
-          this.$store.commit('pushGraphError', err)
-        }
-        this.$store.commit(`loadingStop`, 'page-delete')
-        this.loading = false
-      })
-    }
-  }
-}
-</script>
-
-<style lang='scss'>
-  body.page-deleted-pending {
-    perspective: 50vw;
-    height: 100vh;
-    overflow: hidden;
-
-    .application {
-      background-color: mc('grey', '900');
-    }
-    .application--wrap {
-      transform-style: preserve-3d;
-      transform: translateZ(-5vw) rotateX(2deg);
-      border-radius: 7px;
-      overflow: hidden;
-    }
-  }
-  body.page-deleted {
-    perspective: 50vw;
-
-    .application--wrap {
-      transform-style: preserve-3d;
-      transform: translateZ(-1000vw) rotateX(60deg);
-      opacity: 0;
-    }
-  }
-</style>

+ 0 - 329
client/components/common/page-selector.vue

@@ -1,329 +0,0 @@
-<template lang="pug">
-  v-dialog(
-    v-model='isShown'
-    max-width='850px'
-    overlay-color='blue darken-4'
-    overlay-opacity='.7'
-    )
-    v-card.page-selector
-      .dialog-header.is-blue
-        v-icon.mr-3(color='white') mdi-page-next-outline
-        .body-1(v-if='mode === `create`') {{$t('common:pageSelector.createTitle')}}
-        .body-1(v-else-if='mode === `move`') {{$t('common:pageSelector.moveTitle')}}
-        .body-1(v-else-if='mode === `select`') {{$t('common:pageSelector.selectTitle')}}
-        v-spacer
-        v-progress-circular(
-          indeterminate
-          color='white'
-          :size='20'
-          :width='2'
-          v-show='searchLoading'
-          )
-      .d-flex
-        v-flex.grey(xs5, :class='$vuetify.theme.dark ? `darken-4` : `lighten-3`')
-          v-toolbar(color='grey darken-3', dark, dense, flat)
-            .body-2 {{$t('common:pageSelector.virtualFolders')}}
-            v-spacer
-            v-btn(icon, tile, href='https://docs.requarks.io/guide/pages#folders', target='_blank')
-              v-icon mdi-help-box
-          div(style='height:400px;')
-            vue-scroll(:ops='scrollStyle')
-              v-treeview(
-                :key='`pageTree-` + treeViewCacheId'
-                :active.sync='currentNode'
-                :open.sync='openNodes'
-                :items='tree'
-                :load-children='fetchFolders'
-                dense
-                expand-icon='mdi-menu-down-outline'
-                item-id='path'
-                item-text='title'
-                activatable
-                hoverable
-                )
-                template(slot='prepend', slot-scope='{ item, open, leaf }')
-                  v-icon mdi-{{ open ? 'folder-open' : 'folder' }}
-        v-flex(xs7)
-          v-toolbar(color='blue darken-2', dark, dense, flat)
-            .body-2 {{$t('common:pageSelector.pages')}}
-            //- v-spacer
-            //- v-btn(icon, tile, disabled): v-icon mdi-content-save-move-outline
-            //- v-btn(icon, tile, disabled): v-icon mdi-trash-can-outline
-          div(v-if='currentPages.length > 0', style='height:400px;')
-            vue-scroll(:ops='scrollStyle')
-              v-list.py-0(dense)
-                v-list-item-group(
-                  v-model='currentPage'
-                  color='primary'
-                  )
-                  template(v-for='(page, idx) of currentPages')
-                    v-list-item(:key='`page-` + page.id', :value='page')
-                      v-list-item-icon: v-icon mdi-text-box
-                      v-list-item-title {{page.title}}
-                    v-divider(v-if='idx < pages.length - 1')
-          v-alert.animated.fadeIn(
-            v-else
-            text
-            color='orange'
-            prominent
-            icon='mdi-alert'
-            )
-            .body-2 {{$t('common:pageSelector.folderEmptyWarning')}}
-      v-card-actions.grey.pa-2(:class='$vuetify.theme.dark ? `darken-2` : `lighten-1`', v-if='!mustExist')
-        v-select(
-          solo
-          dark
-          flat
-          background-color='grey darken-3-d2'
-          hide-details
-          single-line
-          :items='namespaces'
-          style='flex: 0 0 100px; border-radius: 4px 0 0 4px;'
-          v-model='currentLocale'
-          )
-        v-text-field(
-          ref='pathIpt'
-          solo
-          hide-details
-          prefix='/'
-          v-model='currentPath'
-          flat
-          clearable
-          style='border-radius: 0 4px 4px 0;'
-        )
-      v-card-chin
-        v-spacer
-        v-btn(text, @click='close') {{$t('common:actions.cancel')}}
-        v-btn.px-4(color='primary', @click='open', :disabled='!isValidPath')
-          v-icon(left) mdi-check
-          span {{$t('common:actions.select')}}
-</template>
-
-<script>
-import _ from 'lodash'
-import gql from 'graphql-tag'
-
-const localeSegmentRegex = /^[A-Z]{2}(-[A-Z]{2})?$/i
-
-/* global siteLangs, siteConfig */
-
-export default {
-  props: {
-    value: {
-      type: Boolean,
-      default: false
-    },
-    path: {
-      type: String,
-      default: 'new-page'
-    },
-    locale: {
-      type: String,
-      default: 'en'
-    },
-    mode: {
-      type: String,
-      default: 'create'
-    },
-    openHandler: {
-      type: Function,
-      default: () => {}
-    },
-    mustExist: {
-      type: Boolean,
-      default: false
-    }
-  },
-  data() {
-    return {
-      treeViewCacheId: 0,
-      searchLoading: false,
-      currentLocale: siteConfig.lang,
-      currentFolderPath: '',
-      currentPath: 'new-page',
-      currentPage: null,
-      currentNode: [0],
-      openNodes: [0],
-      tree: [
-        {
-          id: 0,
-          title: '/ (root)',
-          children: []
-        }
-      ],
-      pages: [],
-      all: [],
-      namespaces: siteLangs.length ? siteLangs.map(ns => ns.code) : [siteConfig.lang],
-      scrollStyle: {
-        vuescroll: {},
-        scrollPanel: {
-          initialScrollX: 0.01, // fix scrollbar not disappearing on load
-          scrollingX: false,
-          speed: 50
-        },
-        rail: {
-          gutterOfEnds: '2px'
-        },
-        bar: {
-          onlyShowBarOnScroll: false,
-          background: '#999',
-          hoverStyle: {
-            background: '#64B5F6'
-          }
-        }
-      }
-    }
-  },
-  computed: {
-    isShown: {
-      get() { return this.value },
-      set(val) { this.$emit('input', val) }
-    },
-    currentPages () {
-      return _.sortBy(_.filter(this.pages, ['parent', _.head(this.currentNode) || 0]), ['title', 'path'])
-    },
-    isValidPath () {
-      if (!this.currentPath) {
-        return false
-      }
-      if (this.mustExist && !this.currentPage) {
-        return false
-      }
-      const firstSection = _.head(this.currentPath.split('/'))
-      if (firstSection.length <= 1) {
-        return false
-      } else if (localeSegmentRegex.test(firstSection)) {
-        return false
-      } else if (
-        _.some(['login', 'logout', 'register', 'verify', 'favicons', 'fonts', 'img', 'js', 'svg'], p => {
-          return p === firstSection
-        })) {
-        return false
-      } else {
-        return true
-      }
-    }
-  },
-  watch: {
-    isShown (newValue, oldValue) {
-      if (newValue && !oldValue) {
-        this.currentPath = this.path
-        this.currentLocale = this.locale
-        _.delay(() => {
-          this.$refs.pathIpt.focus()
-        })
-      }
-    },
-    currentNode (newValue, oldValue) {
-      if (newValue.length < 1) { // force a selection
-        this.$nextTick(() => {
-          this.currentNode = oldValue
-        })
-      } else {
-        const current = _.find(this.all, ['id', newValue[0]])
-
-        if (this.openNodes.indexOf(newValue[0]) < 0) { // auto open and load children
-          if (current) {
-            if (this.openNodes.indexOf(current.parent) < 0) {
-              this.$nextTick(() => {
-                this.openNodes.push(current.parent)
-              })
-            }
-          }
-          this.$nextTick(() => {
-            this.openNodes.push(newValue[0])
-          })
-        }
-
-        this.currentPath = _.compact([_.get(current, 'path', ''), _.last(this.currentPath.split('/'))]).join('/')
-      }
-    },
-    currentPage (newValue, oldValue) {
-      if (!_.isEmpty(newValue)) {
-        this.currentPath = newValue.path
-      }
-    },
-    currentLocale (newValue, oldValue) {
-      this.$nextTick(() => {
-        this.tree = [
-          {
-            id: 0,
-            title: '/ (root)',
-            children: []
-          }
-        ]
-        this.currentNode = [0]
-        this.openNodes = [0]
-        this.pages = []
-        this.all = []
-        this.treeViewCacheId += 1
-      })
-    }
-  },
-  methods: {
-    close() {
-      this.isShown = false
-    },
-    open() {
-      const exit = this.openHandler({
-        locale: this.currentLocale,
-        path: this.currentPath,
-        id: (this.mustExist && this.currentPage) ? this.currentPage.pageId : 0
-      })
-      if (exit !== false) {
-        this.close()
-      }
-    },
-    async fetchFolders (item) {
-      this.searchLoading = true
-      const resp = await this.$apollo.query({
-        query: gql`
-          query ($parent: Int!, $mode: PageTreeMode!, $locale: String!) {
-            pages {
-              tree(parent: $parent, mode: $mode, locale: $locale) {
-                id
-                path
-                title
-                isFolder
-                pageId
-                parent
-              }
-            }
-          }
-        `,
-        fetchPolicy: 'network-only',
-        variables: {
-          parent: item.id,
-          mode: 'ALL',
-          locale: this.currentLocale
-        }
-      })
-      const items = _.get(resp, 'data.pages.tree', [])
-      const itemFolders = _.filter(items, ['isFolder', true]).map(f => ({...f, children: []}))
-      const itemPages = _.filter(items, i => i.pageId > 0)
-      if (itemFolders.length > 0) {
-        item.children = itemFolders
-      } else {
-        item.children = undefined
-      }
-      this.pages = _.unionBy(this.pages, itemPages, 'id')
-      this.all = _.unionBy(this.all, items, 'id')
-
-      this.searchLoading = false
-    }
-  }
-}
-</script>
-
-<style lang='scss'>
-
-.page-selector {
-  .v-treeview-node__label {
-    font-size: 13px;
-  }
-  .v-treeview-node__content {
-    cursor: pointer;
-  }
-}
-
-</style>

+ 0 - 79
client/components/common/password-strength.vue

@@ -1,79 +0,0 @@
-<template lang="pug">
-  .password-strength
-    v-progress-linear(
-      :color='passwordStrengthColor'
-      v-model='passwordStrength'
-      height='2'
-    )
-    .caption(v-if='!hideText', :class='passwordStrengthColor + "--text"') {{passwordStrengthText}}
-</template>
-
-<script>
-import zxcvbn from 'zxcvbn'
-import _ from 'lodash'
-
-export default {
-  props: {
-    value: {
-      type: String,
-      default: ''
-    },
-    hideText: {
-      type: Boolean,
-      default: false
-    }
-  },
-  data() {
-    return {
-      passwordStrength: 0,
-      passwordStrengthColor: 'grey',
-      passwordStrengthText: ''
-    }
-  },
-  watch: {
-    value(newValue) {
-      this.checkPasswordStrength(newValue)
-    }
-  },
-  methods: {
-    checkPasswordStrength: _.debounce(function (pwd) {
-      if (!pwd || pwd.length < 1) {
-        this.passwordStrength = 0
-        this.passwordStrengthColor = 'grey'
-        this.passwordStrengthText = ''
-        return
-      }
-      const strength = zxcvbn(pwd)
-      this.passwordStrength = _.round((strength.score + 1) / 5 * 100)
-      if (this.passwordStrength <= 20) {
-        this.passwordStrengthColor = 'red'
-        this.passwordStrengthText = this.$t('common:password.veryWeak')
-      } else if (this.passwordStrength <= 40) {
-        this.passwordStrengthColor = 'orange'
-        this.passwordStrengthText = this.$t('common:password.weak')
-      } else if (this.passwordStrength <= 60) {
-        this.passwordStrengthColor = 'teal'
-        this.passwordStrengthText = this.$t('common:password.average')
-      } else if (this.passwordStrength <= 80) {
-        this.passwordStrengthColor = 'green'
-        this.passwordStrengthText = this.$t('common:password.strong')
-      } else {
-        this.passwordStrengthColor = 'green'
-        this.passwordStrengthText = this.$t('common:password.veryStrong')
-      }
-    }, 100)
-  }
-}
-</script>
-
-<style lang="scss">
-
-.password-strength > .caption {
-  width: 100%;
-  left: 0;
-  margin: 0;
-  position: absolute;
-  top: calc(100% + 5px);
-}
-
-</style>

+ 0 - 246
client/components/common/search-results.vue

@@ -1,246 +0,0 @@
-<template lang="pug">
-  .search-results(v-if='searchIsFocused || (search && search.length > 1)')
-    .search-results-container
-      .search-results-help(v-if='!search || (search && search.length < 2)')
-        img(src='/_assets-legacy/svg/icon-search-alt.svg')
-        .mt-4 {{$t('common:header.searchHint')}}
-      .search-results-loader(v-else-if='searchIsLoading && (!results || results.length < 1)')
-        orbit-spinner(
-          :animation-duration='1000'
-          :size='100'
-          color='#FFF'
-        )
-        .headline.mt-5 {{$t('common:header.searchLoading')}}
-      .search-results-none(v-else-if='!searchIsLoading && (!results || results.length < 1)')
-        img(src='/_assets-legacy/svg/icon-no-results.svg', alt='No Results')
-        .subheading {{$t('common:header.searchNoResult')}}
-      template(v-if='search && search.length >= 2 && results && results.length > 0')
-        v-subheader.white--text {{$t('common:header.searchResultsCount', { total: response.totalHits })}}
-        v-list.search-results-items.radius-7.py-0(two-line, dense)
-          template(v-for='(item, idx) of results')
-            v-list-item(@click='goToPage(item)', @click.middle="goToPageInNewTab(item)", :key='item.id', :class='idx === cursor ? `highlighted` : ``')
-              v-list-item-avatar(tile)
-                img(src='/_assets-legacy/svg/icon-selective-highlighting.svg')
-              v-list-item-content
-                v-list-item-title(v-text='item.title')
-                v-list-item-subtitle.caption(v-text='item.description')
-                .caption.grey--text(v-text='item.path')
-              v-list-item-action
-                v-chip(label, outlined) {{item.locale.toUpperCase()}}
-            v-divider(v-if='idx < results.length - 1')
-        v-pagination.mt-3(
-          v-if='paginationLength > 1'
-          dark
-          v-model='pagination'
-          :length='paginationLength'
-          circle
-        )
-      template(v-if='suggestions && suggestions.length > 0')
-        v-subheader.white--text.mt-3 {{$t('common:header.searchDidYouMean')}}
-        v-list.search-results-suggestions.radius-7(dense, dark)
-          template(v-for='(term, idx) of suggestions')
-            v-list-item(:key='term', @click='setSearchTerm(term)', :class='idx + results.length === cursor ? `highlighted` : ``')
-              v-list-item-avatar
-                v-icon mdi-magnify
-              v-list-item-content
-                v-list-item-title(v-text='term')
-            v-divider(v-if='idx < suggestions.length - 1')
-      .text-xs-center.pt-5(v-if='search && search.length > 1')
-        //- v-btn.mx-2(outlined, color='orange', @click='search = ``', v-if='results.length > 0')
-        //-   v-icon(left) mdi-content-save
-        //-   span {{$t('common:header.searchCopyLink')}}
-        v-btn.mx-2(outlined, color='pink', @click='search = ``')
-          v-icon(left) mdi-close
-          span {{$t('common:header.searchClose')}}
-</template>
-
-<script>
-import _ from 'lodash'
-import { sync } from 'vuex-pathify'
-import { OrbitSpinner } from 'epic-spinners'
-
-import searchPagesQuery from 'gql/common/common-pages-query-search.gql'
-
-export default {
-  components: {
-    OrbitSpinner
-  },
-  data() {
-    return {
-      cursor: 0,
-      pagination: 1,
-      perPage: 10,
-      response: {
-        results: [],
-        suggestions: [],
-        totalHits: 0
-      }
-    }
-  },
-  computed: {
-    search: sync('site/search'),
-    searchIsFocused: sync('site/searchIsFocused'),
-    searchIsLoading: sync('site/searchIsLoading'),
-    searchRestrictLocale: sync('site/searchRestrictLocale'),
-    searchRestrictPath: sync('site/searchRestrictPath'),
-    results() {
-      const currentIndex = (this.pagination - 1) * this.perPage
-      return this.response.results ? _.slice(this.response.results, currentIndex, currentIndex + this.perPage) : []
-    },
-    hits() {
-      return this.response.totalHits ? this.response.totalHits : 0
-    },
-    suggestions() {
-      return this.response.suggestions ? this.response.suggestions : []
-    },
-    paginationLength() {
-      return (this.response.totalHits > 0) ? Math.ceil(this.response.totalHits / this.perPage) : 0
-    }
-  },
-  watch: {
-    search(newValue, oldValue) {
-      this.cursor = 0
-      if (!newValue || (newValue && newValue.length < 2)) {
-        this.searchIsLoading = false
-      } else {
-        this.searchIsLoading = true
-      }
-    }
-  },
-  mounted() {
-    this.$root.$on('searchMove', (dir) => {
-      this.cursor += ((dir === 'up') ? -1 : 1)
-      if (this.cursor < -1) {
-        this.cursor = -1
-      } else if (this.cursor > this.results.length + this.suggestions.length - 1) {
-        this.cursor = this.results.length + this.suggestions.length - 1
-      }
-    })
-    this.$root.$on('searchEnter', () => {
-      if (!this.results) {
-        return
-      }
-
-      if (this.cursor >= 0 && this.cursor < this.results.length) {
-        this.goToPage(_.nth(this.results, this.cursor))
-      } else if (this.cursor >= 0) {
-        this.setSearchTerm(_.nth(this.suggestions, this.cursor - this.results.length))
-      }
-    })
-  },
-  methods: {
-    setSearchTerm(term) {
-      this.search = term
-    },
-    goToPage(item) {
-      window.location.assign(`/${item.locale}/${item.path}`)
-    },
-    goToPageInNewTab(item) {
-      window.open(`/${item.locale}/${item.path}`, '_blank')
-    }
-  },
-  apollo: {
-    response: {
-      query: searchPagesQuery,
-      variables() {
-        return {
-          query: this.search
-        }
-      },
-      fetchPolicy: 'network-only',
-      debounce: 300,
-      throttle: 1000,
-      skip() {
-        return !this.search || this.search.length < 2
-      },
-      update: (data) => _.get(data, 'pages.search', {}),
-      watchLoading (isLoading) {
-        this.searchIsLoading = isLoading
-      }
-    }
-  }
-}
-</script>
-
-<style lang="scss">
-.search-results {
-  position: fixed;
-  top: 64px;
-  left: 0;
-  overflow-y: auto;
-  width: 100%;
-  height: calc(100% - 64px);
-  background-color: rgba(0,0,0,.9);
-  z-index: 100;
-  text-align: center;
-  animation: searchResultsReveal .6s ease;
-
-  @media #{map-get($display-breakpoints, 'sm-and-down')} {
-    top: 112px;
-  }
-
-  &-container {
-    margin: 12px auto;
-    width: 90vw;
-    max-width: 1024px;
-  }
-
-  &-help {
-    text-align: center;
-    padding: 32px 0;
-    font-size: 18px;
-    font-weight: 300;
-    color: #FFF;
-
-    img {
-      width: 104px;
-    }
-  }
-
-  &-loader {
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    flex-direction: column;
-    padding: 32px 0;
-    color: #FFF;
-  }
-
-  &-none {
-    color: #FFF;
-
-    img {
-      width: 200px;
-    }
-  }
-
-  &-items {
-    text-align: left;
-
-    .highlighted {
-      background: #FFF linear-gradient(to bottom, #FFF, mc('orange', '100'));
-
-      @at-root .theme--dark & {
-        background: mc('grey', '900') linear-gradient(to bottom, mc('orange', '900'), darken(mc('orange', '900'), 15%));
-      }
-    }
-  }
-
-  &-suggestions {
-    .highlighted {
-      background: transparent linear-gradient(to bottom, mc('blue', '500'), mc('blue', '700'));
-    }
-  }
-}
-
-@keyframes searchResultsReveal {
-  0% {
-    background-color: rgba(0,0,0,0);
-    padding-top: 32px;
-  }
-  100% {
-    background-color: rgba(0,0,0,.9);
-    padding-top: 0;
-  }
-}
-</style>

+ 0 - 106
client/components/common/social-sharing.vue

@@ -1,106 +0,0 @@
-<template lang="pug">
-  v-list(nav, dense)
-    v-list-item(@click='', ref='copyUrlButton')
-      v-icon(color='grey', small) mdi-content-copy
-      v-list-item-title.px-3 Copy URL
-    v-list-item(:href='`mailto:?subject=` + encodeURIComponent(title) + `&body=` + encodeURIComponent(url) + `%0D%0A%0D%0A` + encodeURIComponent(description)')
-      v-icon(color='grey', small) mdi-email-outline
-      v-list-item-title.px-3 Email
-    v-list-item(@click='openSocialPop(`https://www.facebook.com/sharer/sharer.php?u=` + encodeURIComponent(url) + `&title=` + encodeURIComponent(title) + `&description=` + encodeURIComponent(description))')
-      v-icon(color='grey', small) mdi-facebook
-      v-list-item-title.px-3 Facebook
-    v-list-item(@click='openSocialPop(`https://www.linkedin.com/shareArticle?mini=true&url=` + encodeURIComponent(url) + `&title=` + encodeURIComponent(title) + `&summary=` + encodeURIComponent(description))')
-      v-icon(color='grey', small) mdi-linkedin
-      v-list-item-title.px-3 LinkedIn
-    v-list-item(@click='openSocialPop(`https://www.reddit.com/submit?url=` + encodeURIComponent(url) + `&title=` + encodeURIComponent(title))')
-      v-icon(color='grey', small) mdi-reddit
-      v-list-item-title.px-3 Reddit
-    v-list-item(@click='openSocialPop(`https://t.me/share/url?url=` + encodeURIComponent(url) + `&text=` + encodeURIComponent(title))')
-      v-icon(color='grey', small) mdi-telegram
-      v-list-item-title.px-3 Telegram
-    v-list-item(@click='openSocialPop(`https://twitter.com/intent/tweet?url=` + encodeURIComponent(url) + `&text=` + encodeURIComponent(title))')
-      v-icon(color='grey', small) mdi-twitter
-      v-list-item-title.px-3 Twitter
-    v-list-item(:href='`viber://forward?text=` + encodeURIComponent(url) + ` ` + encodeURIComponent(description)')
-      v-icon(color='grey', small) mdi-phone-in-talk
-      v-list-item-title.px-3 Viber
-    v-list-item(@click='openSocialPop(`http://service.weibo.com/share/share.php?url=` + encodeURIComponent(url) + `&title=` + encodeURIComponent(title))')
-      v-icon(color='grey', small) mdi-sina-weibo
-      v-list-item-title.px-3 Weibo
-    v-list-item(@click='openSocialPop(`https://api.whatsapp.com/send?text=` + encodeURIComponent(title) + `%0D%0A` + encodeURIComponent(url))')
-      v-icon(color='grey', small) mdi-whatsapp
-      v-list-item-title.px-3 Whatsapp
-</template>
-
-<script>
-import ClipboardJS from 'clipboard'
-
-export default {
-  props: {
-    url: {
-      type: String,
-      default: window.location.url
-    },
-    title: {
-      type: String,
-      default: 'Untitled Page'
-    },
-    description: {
-      type: String,
-      default: ''
-    }
-  },
-  data () {
-    return {
-      width: 626,
-      height: 436,
-      left: 0,
-      top: 0
-    }
-  },
-  methods: {
-    openSocialPop (url) {
-      const popupWindow = window.open(
-        url,
-        'sharer',
-        `status=no,height=${this.height},width=${this.width},resizable=yes,left=${this.left},top=${this.top},screenX=${this.left},screenY=${this.top},toolbar=no,menubar=no,scrollbars=no,location=no,directories=no`
-      )
-
-      popupWindow.focus()
-    }
-  },
-  mounted () {
-    const clip = new ClipboardJS(this.$refs.copyUrlButton.$el, {
-      text: () => { return this.url }
-    })
-
-    clip.on('success', () => {
-      this.$store.commit('showNotification', {
-        style: 'success',
-        message: `URL copied successfully`,
-        icon: 'content-copy'
-      })
-    })
-    clip.on('error', () => {
-      this.$store.commit('showNotification', {
-        style: 'red',
-        message: `Failed to copy to clipboard`,
-        icon: 'alert'
-      })
-    })
-
-    /**
-     * Center the popup on dual screens
-     * http://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen/32261263
-     */
-    const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left
-    const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top
-
-    const width = window.innerWidth ? window.innerWidth : (document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width)
-    const height = window.innerHeight ? window.innerHeight : (document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height)
-
-    this.left = ((width / 2) - (this.width / 2)) + dualScreenLeft
-    this.top = ((height / 2) - (this.height / 2)) + dualScreenTop
-  }
-}
-</script>

+ 0 - 136
client/components/common/user-search.vue

@@ -1,136 +0,0 @@
-<template lang="pug">
-  v-dialog(
-    v-model='dialogOpen'
-    max-width='650'
-    )
-    v-card
-      .dialog-header
-        span {{$t('common:user.search')}}
-        v-spacer
-        v-progress-circular(
-          indeterminate
-          color='white'
-          :size='20'
-          :width='2'
-          v-show='searchLoading'
-          )
-      v-card-text.pt-5
-        v-text-field(
-          outlined
-          :label='$t(`common:user.searchPlaceholder`)'
-          v-model='search'
-          prepend-inner-icon='mdi-account-search-outline'
-          color='primary'
-          ref='searchIpt'
-          hide-details
-          )
-        v-list.grey.mt-3.py-0.radius-7(
-          :class='$vuetify.theme.dark ? `darken-3-d5` : `lighten-3`'
-          two-line
-          dense
-          )
-          template(v-for='(usr, idx) in items')
-            v-list-item(:key='usr.id', @click='setUser(usr)')
-              v-list-item-avatar(size='40', color='primary')
-                span.body-1.white--text {{usr.name | initials}}
-              v-list-item-content
-                v-list-item-title.body-2 {{usr.name}}
-                v-list-item-subtitle {{usr.email}}
-              v-list-item-action
-                v-icon(color='primary') mdi-arrow-right
-            v-divider.my-0(v-if='idx < items.length - 1')
-      v-card-chin
-        v-spacer
-        v-btn(
-          text
-          @click='close'
-          :disabled='loading'
-          ) {{$t('common:actions.cancel')}}
-</template>
-
-<script>
-import _ from 'lodash'
-import gql from 'graphql-tag'
-
-export default {
-  filters: {
-    initials(val) {
-      return val.split(' ').map(v => v.substring(0, 1)).join('')
-    }
-  },
-  props: {
-    multiple: {
-      type: Boolean,
-      default: false
-    },
-    value: {
-      type: Boolean,
-      default: false
-    }
-  },
-  data() {
-    return {
-      loading: false,
-      searchLoading: false,
-      search: '',
-      items: []
-    }
-  },
-  computed: {
-    dialogOpen: {
-      get() { return this.value },
-      set(value) { this.$emit('input', value) }
-    }
-  },
-  watch: {
-    value(newValue, oldValue) {
-      if (newValue && !oldValue) {
-        this.search = ''
-        this.selectedItems = null
-        _.delay(() => { this.$refs.searchIpt.focus() }, 100)
-      }
-    }
-  },
-  methods: {
-    close() {
-      this.$emit('input', false)
-    },
-    setUser(usr) {
-      this.$emit('select', usr)
-      this.close()
-    },
-    searchFilter(item, queryText, itemText) {
-      return _.includes(_.toLower(item.email), _.toLower(queryText)) || _.includes(_.toLower(item.name), _.toLower(queryText))
-    }
-  },
-  apollo: {
-    items: {
-      query: gql`
-        query ($query: String!) {
-          users {
-            search(query:$query) {
-              id
-              name
-              email
-              providerKey
-            }
-          }
-        }
-      `,
-      variables() {
-        return {
-          query: this.search
-        }
-      },
-      fetchPolicy: 'cache-and-network',
-      skip() {
-        return !this.search || this.search.length < 2
-      },
-      update: (data) => data.users.search,
-      watchLoading (isLoading) {
-        this.searchLoading = isLoading
-      }
-    }
-  }
-}
-</script>

+ 0 - 10
client/components/common/v-card-chin.vue

@@ -1,10 +0,0 @@
-<template lang='pug'>
-  div
-    v-divider.my-0
-    v-card-actions(:class='$vuetify.theme.dark ? "grey darken-4-l5" : "grey lighten-4"')
-      slot
-</template>
-
-<script>
-export default { }
-</script>

+ 0 - 64
client/components/common/v-card-info.vue

@@ -1,64 +0,0 @@
-<template lang='pug'>
-  .v-card-info(:class='`is-` + color')
-    v-card-text.d-flex.align-center(:class='colors.cls')
-      v-icon(:color='colors.icon', left) {{icon}}
-      slot
-</template>
-
-<script>
-export default {
-  props: {
-    color: {
-      type: String,
-      default: 'blue'
-    },
-    icon: {
-      type: String,
-      default: 'mdi-information-outline'
-    }
-  },
-  computed: {
-    colors () {
-      switch (this.color) {
-        case 'blue':
-          return {
-            cls: this.$vuetify.theme.dark ? 'grey darken-4-l5 blue--text text--lighten-4' : 'blue lighten-5 blue--text text--darken-3',
-            icon: 'blue lighten-3'
-          }
-        case 'red':
-          return {
-            cls: this.$vuetify.theme.dark ? 'grey darken-4-l5 red--text text--lighten-4' : 'red lighten-5 red--text text--darken-2',
-            icon: 'red lighten-3'
-          }
-        default:
-          return {
-            cls: this.$vuetify.theme.dark ? 'grey darken-4-l5' : 'grey lighten-4',
-            icon: 'grey darken-2'
-          }
-      }
-    }
-  }
-}
-</script>
-
-<style lang="scss">
-.v-card-info {
-  border-bottom: 1px solid #EEE;
-
-  &.is-blue {
-    border-bottom-color: mc('blue', '100');
-
-    @at-root .theme--dark & {
-      border-bottom-color: rgba(mc('blue', '100'), .3);
-    }
-  }
-
-  &.is-red {
-    border-bottom-color: mc('red', '100');
-
-    @at-root .theme--dark & {
-      border-bottom-color: rgba(mc('red', '100'), .3);
-    }
-  }
-}
-</style>

+ 0 - 568
client/components/editor.vue

@@ -1,568 +0,0 @@
-<template lang="pug">
-  v-app.editor(:dark='$vuetify.theme.dark')
-    nav-header(dense)
-      template(slot='mid')
-        v-text-field.editor-title-input(
-          dark
-          solo
-          flat
-          v-model='currentPageTitle'
-          hide-details
-          background-color='black'
-          dense
-          full-width
-        )
-      template(slot='actions')
-        v-btn.mr-3.animated.fadeIn(color='amber', outlined, small, v-if='isConflict', @click='openConflict')
-          .overline.amber--text.mr-3 Conflict
-          status-indicator(intermediary, pulse)
-        v-btn.animated.fadeInDown(
-          text
-          color='green'
-          @click.exact='save'
-          @click.ctrl.exact='saveAndClose'
-          :class='{ "is-icon": $vuetify.breakpoint.mdAndDown }'
-          )
-          v-icon(color='green', :left='$vuetify.breakpoint.lgAndUp') mdi-check
-          span.grey--text(v-if='$vuetify.breakpoint.lgAndUp && mode !== `create` && !isDirty') {{ $t('editor:save.saved') }}
-          span.white--text(v-else-if='$vuetify.breakpoint.lgAndUp') {{ mode === 'create' ? $t('common:actions.create') : $t('common:actions.save') }}
-        v-btn.animated.fadeInDown.wait-p1s(
-          text
-          color='blue'
-          @click='openPropsModal'
-          :class='{ "is-icon": $vuetify.breakpoint.mdAndDown, "mx-0": !welcomeMode, "ml-0": welcomeMode }'
-          )
-          v-icon(color='blue', :left='$vuetify.breakpoint.lgAndUp') mdi-tag-text-outline
-          span.white--text(v-if='$vuetify.breakpoint.lgAndUp') {{ $t('common:actions.page') }}
-        v-btn.animated.fadeInDown.wait-p2s(
-          v-if='!welcomeMode'
-          text
-          color='red'
-          :class='{ "is-icon": $vuetify.breakpoint.mdAndDown }'
-          @click='exit'
-          )
-          v-icon(color='red', :left='$vuetify.breakpoint.lgAndUp') mdi-close
-          span.white--text(v-if='$vuetify.breakpoint.lgAndUp') {{ $t('common:actions.close') }}
-        v-divider.ml-3(vertical)
-    v-main
-      component(:is='currentEditor', :save='save')
-      editor-modal-properties(v-model='dialogProps')
-      editor-modal-editorselect(v-model='dialogEditorSelector')
-      editor-modal-unsaved(v-model='dialogUnsaved', @discard='exitGo')
-      component(:is='activeModal')
-
-    loader(v-model='dialogProgress', :title='$t(`editor:save.processing`)', :subtitle='$t(`editor:save.pleaseWait`)')
-    notify
-</template>
-
-<script>
-import _ from 'lodash'
-import gql from 'graphql-tag'
-import { get, sync } from 'vuex-pathify'
-import { AtomSpinner } from 'epic-spinners'
-import { Base64 } from 'js-base64'
-import { StatusIndicator } from 'vue-status-indicator'
-
-import editorStore from '../store/editor'
-
-/* global WIKI */
-
-WIKI.$store.registerModule('editor', editorStore)
-
-export default {
-  i18nOptions: { namespaces: 'editor' },
-  components: {
-    AtomSpinner,
-    StatusIndicator,
-    editorApi: () => import(/* webpackChunkName: "editor-api", webpackMode: "lazy" */ './editor/editor-api.vue'),
-    editorCode: () => import(/* webpackChunkName: "editor-code", webpackMode: "lazy" */ './editor/editor-code.vue'),
-    editorCkeditor: () => import(/* webpackChunkName: "editor-ckeditor", webpackMode: "lazy" */ './editor/editor-ckeditor.vue'),
-    editorMarkdown: () => import(/* webpackChunkName: "editor-markdown", webpackMode: "lazy" */ './editor/editor-markdown.vue'),
-    editorRedirect: () => import(/* webpackChunkName: "editor-redirect", webpackMode: "lazy" */ './editor/editor-redirect.vue'),
-    editorModalEditorselect: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-editorselect.vue'),
-    editorModalProperties: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-properties.vue'),
-    editorModalUnsaved: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-unsaved.vue'),
-    editorModalMedia: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-media.vue'),
-    editorModalBlocks: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-blocks.vue'),
-    editorModalConflict: () => import(/* webpackChunkName: "editor-conflict", webpackMode: "lazy" */ './editor/editor-modal-conflict.vue'),
-    editorModalDrawio: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-drawio.vue')
-  },
-  props: {
-    locale: {
-      type: String,
-      default: 'en'
-    },
-    path: {
-      type: String,
-      default: 'home'
-    },
-    title: {
-      type: String,
-      default: 'Untitled Page'
-    },
-    description: {
-      type: String,
-      default: ''
-    },
-    tags: {
-      type: Array,
-      default: () => ([])
-    },
-    isPublished: {
-      type: Boolean,
-      default: true
-    },
-    scriptCss: {
-      type: String,
-      default: ''
-    },
-    publishStartDate: {
-      type: String,
-      default: ''
-    },
-    publishEndDate: {
-      type: String,
-      default: ''
-    },
-    scriptJs: {
-      type: String,
-      default: ''
-    },
-    initEditor: {
-      type: String,
-      default: null
-    },
-    initMode: {
-      type: String,
-      default: 'create'
-    },
-    initContent: {
-      type: String,
-      default: null
-    },
-    pageId: {
-      type: String,
-      default: ''
-    },
-    checkoutDate: {
-      type: String,
-      default: new Date().toISOString()
-    },
-    effectivePermissions: {
-      type: String,
-      default: ''
-    }
-  },
-  data() {
-    return {
-      isSaving: false,
-      isConflict: false,
-      dialogProps: false,
-      dialogProgress: false,
-      dialogEditorSelector: false,
-      dialogUnsaved: false,
-      exitConfirmed: false,
-      initContentParsed: '',
-      savedState: {
-        description: '',
-        isPublished: false,
-        publishEndDate: '',
-        publishStartDate: '',
-        tags: '',
-        title: '',
-        css: '',
-        js: ''
-      }
-    }
-  },
-  computed: {
-    currentEditor: sync('editor/editor'),
-    activeModal: sync('editor/activeModal'),
-    mode: get('editor/mode'),
-    welcomeMode() { return this.mode === `create` && this.path === `home` },
-    currentPageTitle: sync('page/title'),
-    checkoutDateActive: sync('editor/checkoutDateActive'),
-    currentStyling: get('page/scriptCss'),
-    isDirty () {
-      return _.some([
-        this.initContentParsed !== this.$store.get('editor/content'),
-        this.locale !== this.$store.get('page/locale'),
-        this.path !== this.$store.get('page/path'),
-        this.savedState.title !== this.$store.get('page/title'),
-        this.savedState.description !== this.$store.get('page/description'),
-        this.savedState.tags !== this.$store.get('page/tags'),
-        this.savedState.isPublished !== this.$store.get('page/isPublished'),
-        this.savedState.publishStartDate !== this.$store.get('page/publishStartDate'),
-        this.savedState.publishEndDate !== this.$store.get('page/publishEndDate'),
-        this.savedState.css !== this.$store.get('page/scriptCss'),
-        this.savedState.js !== this.$store.get('page/scriptJs')
-      ], Boolean)
-    }
-  },
-  watch: {
-    currentEditor(newValue, oldValue) {
-      if (newValue !== '' && this.mode === 'create') {
-        _.delay(() => {
-          this.dialogProps = true
-        }, 500)
-      }
-    },
-    currentStyling(newValue) {
-      this.injectCustomCss(newValue)
-    }
-  },
-  created() {
-    this.$store.set('page/id', this.pageId)
-    this.$store.set('page/description', this.description)
-    this.$store.set('page/isPublished', this.isPublished)
-    this.$store.set('page/publishStartDate', this.publishStartDate)
-    this.$store.set('page/publishEndDate', this.publishEndDate)
-    this.$store.set('page/locale', this.locale)
-    this.$store.set('page/path', this.path)
-    this.$store.set('page/tags', this.tags)
-    this.$store.set('page/title', this.title)
-    this.$store.set('page/scriptCss', this.scriptCss)
-    this.$store.set('page/scriptJs', this.scriptJs)
-
-    this.$store.set('page/mode', 'edit')
-
-    this.setCurrentSavedState()
-
-    this.checkoutDateActive = this.checkoutDate
-
-    if (this.effectivePermissions) {
-      this.$store.set('page/effectivePermissions', JSON.parse(Buffer.from(this.effectivePermissions, 'base64').toString()))
-    }
-  },
-  mounted() {
-    this.$store.set('editor/mode', this.initMode || 'create')
-
-    this.initContentParsed = this.initContent ? Base64.decode(this.initContent) : ''
-    this.$store.set('editor/content', this.initContentParsed)
-    if (this.mode === 'create' && !this.initEditor) {
-      _.delay(() => {
-        this.dialogEditorSelector = true
-      }, 500)
-    } else {
-      this.currentEditor = `editor${_.startCase(this.initEditor || 'markdown')}`
-    }
-
-    window.onbeforeunload = () => {
-      if (!this.exitConfirmed && this.initContentParsed !== this.$store.get('editor/content')) {
-        return this.$t('editor:unsavedWarning')
-      } else {
-        return undefined
-      }
-    }
-
-    this.$root.$on('resetEditorConflict', () => {
-      this.isConflict = false
-    })
-
-    // this.$store.set('editor/mode', 'edit')
-    // this.currentEditor = `editorApi`
-  },
-  methods: {
-    openPropsModal(name) {
-      this.dialogProps = true
-    },
-    showProgressDialog(textKey) {
-      this.dialogProgress = true
-    },
-    hideProgressDialog() {
-      this.dialogProgress = false
-    },
-    openConflict() {
-      this.$root.$emit('saveConflict')
-    },
-    async save({ rethrow = false, overwrite = false } = {}) {
-      this.showProgressDialog('saving')
-      this.isSaving = true
-
-      const saveTimeoutHandle = setTimeout(() => {
-        throw new Error('Save operation timed out.')
-      }, 30000)
-
-      try {
-        if (this.$store.get('editor/mode') === 'create') {
-          // --------------------------------------------
-          // -> CREATE PAGE
-          // --------------------------------------------
-
-          let resp = await this.$apollo.mutate({
-            mutation: gql`
-              mutation (
-                $content: String!
-                $description: String!
-                $editor: String!
-                $publishState: PagePublishState!
-                $locale: String!
-                $path: String!
-                $publishEndDate: Date
-                $publishStartDate: Date
-                $scriptCss: String
-                $scriptJsLoad: String
-                $siteId: UUID!
-                $tags: [String!]
-                $title: String!
-              ) {
-                createPage(
-                  content: $content
-                  description: $description
-                  editor: $editor
-                  publishState: $publishState
-                  locale: $locale
-                  path: $path
-                  publishEndDate: $publishEndDate
-                  publishStartDate: $publishStartDate
-                  scriptCss: $scriptCss
-                  scriptJsLoad: $scriptJsLoad
-                  siteId: $siteId
-                  tags: $tags
-                  title: $title
-                ) {
-                  operation {
-                    succeeded
-                    message
-                  }
-                  page {
-                    id
-                    updatedAt
-                  }
-                }
-              }
-            `,
-            variables: {
-              content: this.$store.get('editor/content'),
-              description: this.$store.get('page/description'),
-              editor: this.$store.get('editor/editorKey'),
-              locale: this.$store.get('page/locale'),
-              publishState: this.$store.get('page/isPublished') ? 'published' : 'draft',
-              path: this.$store.get('page/path'),
-              publishEndDate: this.$store.get('page/publishEndDate') || '',
-              publishStartDate: this.$store.get('page/publishStartDate') || '',
-              scriptCss: this.$store.get('page/scriptCss'),
-              scriptJsLoad: this.$store.get('page/scriptJs'),
-              siteId: this.$store.get('site/id'),
-              tags: this.$store.get('page/tags'),
-              title: this.$store.get('page/title')
-            }
-          })
-          resp = resp?.data?.createPage || {}
-          if (resp?.operation?.succeeded) {
-            this.checkoutDateActive = resp?.page?.updatedAt ?? this.checkoutDateActive
-            this.isConflict = false
-            this.$store.commit('showNotification', {
-              message: this.$t('editor:save.createSuccess'),
-              style: 'success',
-              icon: 'check'
-            })
-            this.$store.set('editor/id', resp?.page?.id)
-            this.$store.set('editor/mode', 'update')
-            this.exitConfirmed = true
-            window.location.assign(`/${this.$store.get('page/locale')}/${this.$store.get('page/path')}`)
-          } else {
-            throw new Error(resp?.operation?.message)
-          }
-        } else {
-          // --------------------------------------------
-          // -> UPDATE EXISTING PAGE
-          // --------------------------------------------
-
-          // const conflictResp = await this.$apollo.query({
-          //   query: gql`
-          //     query ($id: Int!, $checkoutDate: Date!) {
-          //       pages {
-          //         checkConflicts(id: $id, checkoutDate: $checkoutDate)
-          //       }
-          //     }
-          //   `,
-          //   fetchPolicy: 'network-only',
-          //   variables: {
-          //     id: this.pageId,
-          //     checkoutDate: this.checkoutDateActive
-          //   }
-          // })
-          // if (_.get(conflictResp, 'data.pages.checkConflicts', false)) {
-          //   this.$root.$emit('saveConflict')
-          //   throw new Error(this.$t('editor:conflict.warning'))
-          // }
-
-          let resp = await this.$apollo.mutate({
-            mutation: gql`
-              mutation (
-                $id: UUID!
-                $patch: PageUpdateInput!
-              ) {
-                updatePage(
-                  id: $id
-                  patch: $patch
-                ) {
-                  operation {
-                    succeeded
-                    message
-                  }
-                  page {
-                    updatedAt
-                  }
-                }
-              }
-            `,
-            variables: {
-              id: this.$store.get('page/id'),
-              patch: {
-                content: this.$store.get('editor/content'),
-                description: this.$store.get('page/description'),
-                locale: this.$store.get('page/locale'),
-                publishState: this.$store.get('page/isPublished') ? 'published' : 'draft',
-                path: this.$store.get('page/path'),
-                publishEndDate: this.$store.get('page/publishEndDate') || '',
-                publishStartDate: this.$store.get('page/publishStartDate') || '',
-                scriptCss: this.$store.get('page/scriptCss'),
-                scriptJsLoad: this.$store.get('page/scriptJs'),
-                tags: this.$store.get('page/tags'),
-                title: this.$store.get('page/title')
-              }
-            }
-          })
-          resp = _.get(resp, 'data.updatePage', {})
-          if (_.get(resp, 'operation.succeeded')) {
-            this.checkoutDateActive = _.get(resp, 'page.updatedAt', this.checkoutDateActive)
-            this.isConflict = false
-            this.$store.commit('showNotification', {
-              message: this.$t('editor:save.updateSuccess'),
-              style: 'success',
-              icon: 'check'
-            })
-            if (this.locale !== this.$store.get('page/locale') || this.path !== this.$store.get('page/path')) {
-              _.delay(() => {
-                window.location.replace(`/e/${this.$store.get('page/locale')}/${this.$store.get('page/path')}`)
-              }, 1000)
-            }
-          } else {
-            throw new Error(_.get(resp, 'operation.message'))
-          }
-        }
-
-        this.initContentParsed = this.$store.get('editor/content')
-        this.setCurrentSavedState()
-      } catch (err) {
-        this.$store.commit('showNotification', {
-          message: err.message,
-          style: 'error',
-          icon: 'warning'
-        })
-        if (rethrow === true) {
-          clearTimeout(saveTimeoutHandle)
-          this.isSaving = false
-          this.hideProgressDialog()
-          throw err
-        }
-      }
-      clearTimeout(saveTimeoutHandle)
-      this.isSaving = false
-      this.hideProgressDialog()
-    },
-    async saveAndClose() {
-      try {
-        if (this.$store.get('editor/mode') === 'create') {
-          await this.save()
-        } else {
-          await this.save({ rethrow: true })
-          await this.exit()
-        }
-      } catch (err) {
-        // Error is already handled
-      }
-    },
-    async exit() {
-      if (this.isDirty) {
-        this.dialogUnsaved = true
-      } else {
-        this.exitGo()
-      }
-    },
-    exitGo() {
-      this.$store.commit(`loadingStart`, 'editor-close')
-      this.currentEditor = ''
-      this.exitConfirmed = true
-      _.delay(() => {
-        if (this.$store.get('editor/mode') === 'create') {
-          window.location.assign(`/`)
-        } else {
-          window.location.assign(`/${this.$store.get('page/locale')}/${this.$store.get('page/path')}`)
-        }
-      }, 500)
-    },
-    setCurrentSavedState () {
-      this.savedState = {
-        description: this.$store.get('page/description'),
-        isPublished: this.$store.get('page/isPublished'),
-        publishEndDate: this.$store.get('page/publishEndDate') || '',
-        publishStartDate: this.$store.get('page/publishStartDate') || '',
-        tags: this.$store.get('page/tags'),
-        title: this.$store.get('page/title'),
-        css: this.$store.get('page/scriptCss'),
-        js: this.$store.get('page/scriptJs')
-      }
-    },
-    injectCustomCss: _.debounce(css => {
-      const oldStyl = document.querySelector('#editor-script-css')
-      if (oldStyl) {
-        document.head.removeChild(oldStyl)
-      }
-      if (!_.isEmpty(css)) {
-        const styl = document.createElement('style')
-        styl.type = 'text/css'
-        styl.id = 'editor-script-css'
-        document.head.appendChild(styl)
-        styl.appendChild(document.createTextNode(css))
-      }
-    }, 1000)
-  }
-  // apollo: {
-  //   isConflict: {
-  //     query: gql`
-  //       query ($id: Int!, $checkoutDate: Date!) {
-  //         pages {
-  //           checkConflicts(id: $id, checkoutDate: $checkoutDate)
-  //         }
-  //       }
-  //     `,
-  //     fetchPolicy: 'network-only',
-  //     pollInterval: 5000,
-  //     variables () {
-  //       return {
-  //         id: this.pageId,
-  //         checkoutDate: this.checkoutDateActive
-  //       }
-  //     },
-  //     update: (data) => _.cloneDeep(data.pages.checkConflicts),
-  //     skip () {
-  //       return this.mode === 'create' || this.isSaving || !this.isDirty
-  //     }
-  //   }
-  // }
-}
-</script>
-
-<style lang='scss'>
-
-  .editor {
-    background-color: mc('grey', '900') !important;
-    min-height: 100vh;
-
-    .application--wrap {
-      background-color: mc('grey', '900');
-    }
-
-    &-title-input input {
-      text-align: center;
-    }
-  }
-
-  .atom-spinner.is-inline {
-    display: inline-block;
-  }
-
-</style>

+ 0 - 9
client/components/editor/api/server-selector.vue

@@ -1,9 +0,0 @@
-<template lang="pug">
-
-</template>
-
-<script>
-export default {
-
-}
-</script>

+ 0 - 129
client/components/editor/ckeditor/conflict.vue

@@ -1,129 +0,0 @@
-<template lang="pug">
-  v-dialog(
-    v-model='isShown'
-    max-width='700'
-    )
-    v-card
-      .dialog-header.is-short.is-indigo
-        v-icon.mr-2(color='white') mdi-alert
-        span {{$t('editor:conflict.title')}}
-      v-card-text.pt-4
-        i18next.body-2(tag='div', path='editor:conflict.infoGeneric')
-          strong(place='authorName') {{latest.authorName}}
-          span(place='date', :title='$options.filters.moment(latest.updatedAt, `LLL`)') {{ latest.updatedAt | moment('from') }}.
-        v-btn.mt-2(outlined, color='indigo', small, :href='`/` + latest.locale + `/` + latest.path', target='_blank')
-          v-icon(left) mdi-open-in-new
-          span {{$t('editor:conflict.viewLatestVersion')}}
-        .body-2.mt-5: strong {{$t('editor:conflict.whatToDo')}}
-        .body-2.mt-1 #[v-icon(color='indigo') mdi-alpha-l-box] {{$t('editor:conflict.whatToDoLocal')}}
-        .body-2.mt-1 #[v-icon(color='indigo') mdi-alpha-r-box] {{$t('editor:conflict.whatToDoRemote')}}
-      v-card-chin
-        v-spacer
-        v-btn(text, @click='close') {{$t('common:actions.cancel')}}
-        v-btn.px-4(color='indigo', @click='useLocal', dark, :title='$t(`editor:conflict.useLocalHint`)')
-          v-icon(left) mdi-alpha-l-box
-          span {{$t('editor:conflict.useLocal')}}
-        v-dialog(
-          v-model='isRemoteConfirmDiagShown'
-          width='500'
-          )
-          template(v-slot:activator='{ on }')
-            v-btn.ml-3(color='indigo', dark, v-on='on', :title='$t(`editor:conflict.useRemoteHint`)')
-              v-icon(left) mdi-alpha-r-box
-              span {{$t('editor:conflict.useRemote')}}
-          v-card
-            .dialog-header.is-short.is-indigo
-              v-icon.mr-3(color='white') mdi-alpha-r-box
-              span {{$t('editor:conflict.overwrite.title')}}
-            v-card-text.pa-4
-              i18next.body-2(tag='div', path='editor:conflict.overwrite.description')
-                strong(place='refEditsLost') {{$t('editor:conflict.overwrite.editsLost')}}
-            v-card-chin
-              v-spacer
-              v-btn(outlined, color='indigo', @click='isRemoteConfirmDiagShown = false')
-                v-icon(left) mdi-close
-                span {{$t('common:actions.cancel')}}
-              v-btn(@click='useRemote', color='indigo', dark)
-                v-icon(left) mdi-check
-                span {{$t('common:actions.confirm')}}
-</template>
-
-<script>
-import _ from 'lodash'
-import gql from 'graphql-tag'
-
-export default {
-  props: {
-    value: {
-      type: Boolean,
-      default: false
-    }
-  },
-  data() {
-    return {
-      latest: {
-        updatedAt: '',
-        authorName: '',
-        content: '',
-        locale: '',
-        path: ''
-      },
-      isRemoteConfirmDiagShown: false
-    }
-  },
-  computed: {
-    isShown: {
-      get() { return this.value },
-      set(val) { this.$emit('input', val) }
-    }
-  },
-  methods: {
-    close () {
-      this.isShown = false
-    },
-    useLocal () {
-      this.$store.set('editor/checkoutDateActive', this.latest.updatedAt)
-      this.$root.$emit('resetEditorConflict')
-      this.close()
-    },
-    useRemote () {
-      this.$store.set('editor/checkoutDateActive', this.latest.updatedAt)
-      this.$store.set('editor/content', this.latest.content)
-      this.$root.$emit('overwriteEditorContent')
-      this.$root.$emit('resetEditorConflict')
-      this.close()
-    }
-  },
-  async mounted () {
-    let resp = await this.$apollo.query({
-      query: gql`
-        query ($id: Int!) {
-          pages {
-            conflictLatest(id: $id) {
-              authorName
-              locale
-              path
-              content
-              updatedAt
-            }
-          }
-        }
-      `,
-      fetchPolicy: 'network-only',
-      variables: {
-        id: this.$store.get('page/id')
-      }
-    })
-    resp = _.get(resp, 'data.pages.conflictLatest', false)
-
-    if (!resp) {
-      return this.$store.commit('showNotification', {
-        message: 'Failed to fetch latest version.',
-        style: 'warning',
-        icon: 'warning'
-      })
-    }
-    this.latest = resp
-  }
-}
-</script>

+ 0 - 147
client/components/editor/common/katex.js

@@ -1,147 +0,0 @@
-// Test if potential opening or closing delimieter
-// Assumes that there is a "$" at state.src[pos]
-function isValidDelim (state, pos) {
-  let prevChar
-  let nextChar
-  let max = state.posMax
-  let canOpen = true
-  let canClose = true
-
-  prevChar = pos > 0 ? state.src.charCodeAt(pos - 1) : -1
-  nextChar = pos + 1 <= max ? state.src.charCodeAt(pos + 1) : -1
-
-  // Check non-whitespace conditions for opening and closing, and
-  // check that closing delimeter isn't followed by a number
-  if (prevChar === 0x20/* " " */ || prevChar === 0x09/* \t */ ||
-          (nextChar >= 0x30/* "0" */ && nextChar <= 0x39/* "9" */)) {
-    canClose = false
-  }
-  if (nextChar === 0x20/* " " */ || nextChar === 0x09/* \t */) {
-    canOpen = false
-  }
-
-  return {
-    canOpen: canOpen,
-    canClose: canClose
-  }
-}
-
-export default {
-  katexInline (state, silent) {
-    let start, match, token, res, pos
-
-    if (state.src[state.pos] !== '$') { return false }
-
-    res = isValidDelim(state, state.pos)
-    if (!res.canOpen) {
-      if (!silent) { state.pending += '$' }
-      state.pos += 1
-      return true
-    }
-
-    // First check for and bypass all properly escaped delimieters
-    // This loop will assume that the first leading backtick can not
-    // be the first character in state.src, which is known since
-    // we have found an opening delimieter already.
-    start = state.pos + 1
-    match = start
-    while ((match = state.src.indexOf('$', match)) !== -1) {
-      // Found potential $, look for escapes, pos will point to
-      // first non escape when complete
-      pos = match - 1
-      while (state.src[pos] === '\\') { pos -= 1 }
-
-      // Even number of escapes, potential closing delimiter found
-      if (((match - pos) % 2) === 1) { break }
-      match += 1
-    }
-
-    // No closing delimter found.  Consume $ and continue.
-    if (match === -1) {
-      if (!silent) { state.pending += '$' }
-      state.pos = start
-      return true
-    }
-
-    // Check if we have empty content, ie: $$.  Do not parse.
-    if (match - start === 0) {
-      if (!silent) { state.pending += '$$' }
-      state.pos = start + 1
-      return true
-    }
-
-    // Check for valid closing delimiter
-    res = isValidDelim(state, match)
-    if (!res.canClose) {
-      if (!silent) { state.pending += '$' }
-      state.pos = start
-      return true
-    }
-
-    if (!silent) {
-      token = state.push('katex_inline', 'math', 0)
-      token.markup = '$'
-      token.content = state.src
-        // Extract the math part without the $
-        .slice(start, match)
-        // Escape the curly braces since they will be interpreted as
-        // attributes by markdown-it-attrs (the "curly_attributes"
-        // core rule)
-        .replaceAll("{", "{{")
-        .replaceAll("}", "}}")
-    }
-
-    state.pos = match + 1
-    return true
-  },
-
-  katexBlock (state, start, end, silent) {
-    let firstLine; let lastLine; let next; let lastPos; let found = false; let token
-    let pos = state.bMarks[start] + state.tShift[start]
-    let max = state.eMarks[start]
-
-    if (pos + 2 > max) { return false }
-    if (state.src.slice(pos, pos + 2) !== '$$') { return false }
-
-    pos += 2
-    firstLine = state.src.slice(pos, max)
-
-    if (silent) { return true }
-    if (firstLine.trim().slice(-2) === '$$') {
-      // Single line expression
-      firstLine = firstLine.trim().slice(0, -2)
-      found = true
-    }
-
-    for (next = start; !found;) {
-      next++
-
-      if (next >= end) { break }
-
-      pos = state.bMarks[next] + state.tShift[next]
-      max = state.eMarks[next]
-
-      if (pos < max && state.tShift[next] < state.blkIndent) {
-        // non-empty line with negative indent should stop the list:
-        break
-      }
-
-      if (state.src.slice(pos, max).trim().slice(-2) === '$$') {
-        lastPos = state.src.slice(0, max).lastIndexOf('$$')
-        lastLine = state.src.slice(pos, lastPos)
-        found = true
-      }
-    }
-
-    state.line = next + 1
-
-    token = state.push('katex_block', 'math', 0)
-    token.block = true
-    token.content = (firstLine && firstLine.trim() ? firstLine + '\n' : '') +
-    state.getLines(start + 1, next, state.tShift[start], true) +
-    (lastLine && lastLine.trim() ? lastLine : '')
-    token.map = [ start, state.line ]
-    token.markup = '$$'
-    return true
-  }
-}

+ 0 - 433
client/components/editor/editor-api.vue

@@ -1,433 +0,0 @@
-<template lang='pug'>
-  .editor-api
-    .editor-api-main
-      v-list.editor-api-sidebar.radius-0(nav, :class='$vuetify.theme.dark ? `grey darken-4` : `primary`', dark)
-        v-list-item-group(v-model='tab')
-          v-list-item.animated.fadeInLeft(value='info')
-            v-list-item-icon: v-icon mdi-book-information-variant
-            v-list-item-title Info
-          v-list-item.mt-3.animated.fadeInLeft.wait-p2s(value='servers')
-            v-list-item-icon: v-icon mdi-server
-            v-list-item-title Servers
-          v-list-item.mt-3.animated.fadeInLeft.wait-p3s(value='endpoints')
-            v-list-item-icon: v-icon mdi-code-braces
-            v-list-item-title Endpoints
-          v-list-item.mt-3.animated.fadeInLeft.wait-p4s(value='models')
-            v-list-item-icon: v-icon mdi-buffer
-            v-list-item-title Models
-          v-list-item.mt-3.animated.fadeInLeft.wait-p5s(value='auth')
-            v-list-item-icon: v-icon mdi-lock
-            v-list-item-title Authentication
-      .editor-api-editor
-        template(v-if='tab === `info`')
-          v-container.px-2.pt-1(fluid)
-            v-row(dense)
-              v-col(cols='12')
-                .pa-3
-                  .subtitle-2 API General Information
-                  .caption.grey--text.text--darken-1 Global metadata about the API
-              v-col(cols='12', lg='6')
-                v-card.pt-2
-                  v-card-text
-                    v-text-field(
-                      label='Title'
-                      outlined
-                      hint='Required - Title of the API'
-                      persistent-hint
-                      v-model='info.title'
-                    )
-                    v-divider.mt-2.mb-4
-                    v-text-field(
-                      label='Version'
-                      outlined
-                      hint='Required - Semantic versioning like 1.0.0 or an arbitrary string like 0.99-beta.'
-                      persistent-hint
-                      v-model='info.version'
-                    )
-                    v-divider.mt-2.mb-4
-                    v-textarea(
-                      label='Description'
-                      outlined
-                      hint='Optional - Markdown formatting is supported.'
-                      persistent-hint
-                      v-model='info.description'
-                    )
-              v-col(cols='12', lg='6')
-                v-card.pt-2
-                  v-card-text
-                    v-list(nav, two-line)
-                      v-list-item-group(v-model='kind', mandatory, color='primary')
-                        v-list-item(value='rest')
-                          v-list-item-avatar
-                            img(src='/_assets-legacy/svg/icon-transaction-list.svg', alt='REST')
-                          v-list-item-content
-                            v-list-item-title REST API
-                            v-list-item-subtitle Classic REST Endpoints
-                          v-list-item-avatar
-                            v-icon(:color='kind === `rest` ? `primary` : `grey lighten-3`') mdi-check-circle
-                        v-list-item(value='graphql', disabled)
-                          v-list-item-avatar
-                            img(src='/_assets-legacy/svg/icon-graphql.svg', alt='GraphQL')
-                          v-list-item-content
-                            v-list-item-title GraphQL
-                            v-list-item-subtitle.grey--text.text--lighten-1 Schema-based API
-                          v-list-item-action
-                            //- v-icon(:color='kind === `graphql` ? `primary` : `grey lighten-3`') mdi-check-circle
-                            v-chip(label, small) Coming soon
-        template(v-else-if='tab === `servers`')
-          v-container.px-2.pt-1(fluid)
-            v-row(dense)
-              v-col(cols='12')
-                .pa-3
-                  .d-flex.align-center.justify-space-between
-                    div
-                      .subtitle-2 List of servers / load balancers where this API reside
-                      .caption.grey--text.text--darken-1 Enter all environments, e.g. Integration, QA, Pre-production, Production, etc.
-                    v-btn(color='primary', large, @click='addServer')
-                      v-icon(left) mdi-plus
-                      span Add Server
-              v-col(cols='12', lg='6', v-for='srv of servers', :key='srv.id')
-                v-card.pt-1
-                  v-card-text
-                    .d-flex
-                      .d-flex.flex-column.justify-space-between
-                        v-menu(offset-y, min-width='200')
-                          template(v-slot:activator='{ on }')
-                            v-btn(text, x-large, style='min-width: 0;', v-on='on')
-                              v-icon(large, :color='iconColor(srv.icon)') {{iconKey(srv.icon)}}
-                          v-list(nav, dense)
-                            v-list-item-group(v-model='srv.icon', mandatory)
-                              v-list-item(:value='srvKey', v-for='(srv, srvKey) in serverTypes', :key='srvKey')
-                                v-list-item-icon: v-icon(large, :color='srv.color', v-text='srv.icon')
-                                v-list-item-content: v-list-item-title(v-text='srv.title')
-                        v-btn.mb-2(depressed, small, @click='removeServer(srv.id)')
-                          v-icon(left) mdi-close
-                          span Delete
-                      v-divider.ml-5(vertical)
-                      .pl-5(style='flex: 1 1 100%;')
-                        v-text-field(
-                          label='Environment / Server Name'
-                          outlined
-                          hint='Required - Name of the environment (e.g. QA, Production)'
-                          persistent-hint
-                          v-model='srv.name'
-                        )
-                        v-text-field.mt-4(
-                          label='URL'
-                          outlined
-                          hint='Required - URL of the environment (e.g. https://api.example.com/v1)'
-                          persistent-hint
-                          v-model='srv.url'
-                        )
-
-        template(v-else-if='tab === `endpoints`')
-          v-container.px-2.pt-1(fluid)
-            v-row(dense)
-              v-col(cols='12')
-                .pa-3
-                  .d-flex.align-center.justify-space-between
-                    div
-                      .subtitle-2 List of endpoints
-                      .caption.grey--text.text--darken-1 Groups of REST endpoints (GET, POST, PUT, DELETE).
-                    v-btn(color='primary', large, @click='addGroup')
-                      v-icon(left) mdi-plus
-                      span Add Group
-              v-col(cols='12', v-for='grp of endpointGroups', :key='grp.id')
-                v-card(color='grey darken-2')
-                  v-card-text
-                    v-toolbar(color='grey darken-2', flat, height='86')
-                      v-text-field.mr-1(
-                        flat
-                        dark
-                        label='Group Name'
-                        solo
-                        hint='Group Name'
-                        persistent-hint
-                        v-model='grp.name'
-                      )
-                      v-text-field.mx-1(
-                        flat
-                        dark
-                        label='Group Description'
-                        solo
-                        hint='Group Description'
-                        persistent-hint
-                        v-model='grp.description'
-                      )
-                      v-divider.mx-3(vertical, dark)
-                      v-btn.mx-1.align-self-start(color='grey lighten-2', @click='addEndpoint(grp)', dark, text, height='48')
-                        v-icon(left) mdi-trash-can
-                        span Delete
-                      v-divider.mx-3(vertical, dark)
-                      v-btn.ml-1.align-self-start(color='pink', @click='addEndpoint(grp)', dark, depressed, height='48')
-                        v-icon(left) mdi-plus
-                        span Add Endpoint
-                    v-container.pa-0.mt-2(fluid)
-                      v-row(dense)
-                        v-col(cols='12', v-for='ept of grp.endpoints', :key='ept.id')
-                          v-card.pt-1
-                            v-card-text
-                              .d-flex
-                                .d-flex.flex-column
-                                  v-menu(offset-y, min-width='140')
-                                    template(v-slot:activator='{ on }')
-                                      v-btn.subtitle-1(depressed, large, dark, style='min-width: 140px;', height='48', v-on='on', :color='methodColor(ept.method)')
-                                        strong {{ept.method}}
-                                    v-list(nav, dense)
-                                      v-list-item-group(v-model='ept.method', mandatory)
-                                        v-list-item(:value='mtd.key', v-for='mtd of endpointMethods', :key='mtd.key')
-                                          v-list-item-content
-                                            v-chip.text-center(label, :color='mtd.color', dark) {{mtd.key}}
-                                  v-btn.mt-2(v-if='!ept.expanded', small, @click='ept.expanded = true', color='pink', outlined)
-                                    v-icon(left) mdi-arrow-down-box
-                                    span Expand
-                                  v-btn.mt-2(v-else, small, @click='ept.expanded = false', color='pink', outlined)
-                                    v-icon(left) mdi-arrow-up-box
-                                    span Collapse
-                                  template(v-if='ept.expanded')
-                                    v-spacer
-                                    v-btn.my-2(depressed, small, @click='removeEndpoint(grp, ept.id)')
-                                      v-icon(left) mdi-close
-                                      span Delete
-                                v-divider.ml-5(vertical)
-                                .pl-5(style='flex: 1 1 100%;')
-                                  .d-flex
-                                    v-text-field.mr-2(
-                                      label='Path'
-                                      outlined
-                                      hint='Required - Path to the endpoint (e.g. /planets/{planetId})'
-                                      persistent-hint
-                                      v-model='ept.path'
-                                    )
-                                    v-text-field.ml-2(
-                                      label='Summary'
-                                      outlined
-                                      hint='Required - A short summary of the endpoint (a few words).'
-                                      persistent-hint
-                                      v-model='ept.summary'
-                                    )
-                                  template(v-if='ept.expanded')
-                                    v-text-field.mt-3(
-                                      label='Description'
-                                      outlined
-                                      v-model='ept.description'
-                                    )
-
-    v-system-bar.editor-api-sysbar(dark, status, color='grey darken-3')
-      .caption.editor-api-sysbar-locale {{locale.toUpperCase()}}
-      .caption.px-3 /{{path}}
-      template(v-if='$vuetify.breakpoint.mdAndUp')
-        v-spacer
-        .caption API Docs
-        v-spacer
-        .caption OpenAPI 3.0
-</template>
-
-<script>
-import _ from 'lodash'
-import { v4 as uuid } from 'uuid'
-import { get, sync } from 'vuex-pathify'
-
-export default {
-  data() {
-    return {
-      tab: `endpoints`,
-      kind: 'rest',
-      kinds: [
-        { text: 'REST', value: 'rest' },
-        { text: 'GraphQL', value: 'graphql' }
-      ],
-      info: {
-        title: '',
-        version: '1.0.0',
-        description: ''
-      },
-      servers: [
-        { name: 'Production', url: 'https://api.example.com/v1', icon: 'server', id: '123456' }
-      ],
-      serverTypes: {
-        aws: {
-          color: 'orange',
-          icon: 'mdi-aws',
-          title: 'AWS'
-        },
-        azure: {
-          color: 'blue darken-2',
-          icon: 'mdi-azure',
-          title: 'Azure'
-        },
-        digitalocean: {
-          color: 'blue',
-          icon: 'mdi-digital-ocean',
-          title: 'DigitalOcean'
-        },
-        docker: {
-          color: 'blue',
-          icon: 'mdi-docker',
-          title: 'Docker'
-        },
-        google: {
-          color: 'red',
-          icon: 'mdi-google',
-          title: 'Google'
-        },
-        kubernetes: {
-          color: 'blue darken-2',
-          icon: 'mdi-kubernetes',
-          title: 'Kubernetes'
-        },
-        linux: {
-          color: 'grey darken-3',
-          icon: 'mdi-linux',
-          title: 'Linux'
-        },
-        mac: {
-          color: 'grey darken-2',
-          icon: 'mdi-apple',
-          title: 'Mac'
-        },
-        server: {
-          color: 'grey',
-          icon: 'mdi-server',
-          title: 'Server'
-        },
-        windows: {
-          color: 'blue darken-2',
-          icon: 'mdi-windows',
-          title: 'Windows'
-        }
-      },
-      endpointGroups: [
-        {
-          id: '345678',
-          name: '',
-          description: '',
-          endpoints: [
-            { method: 'GET', path: '/pet', expanded: false, id: '234567' }
-          ]
-        }
-      ],
-      endpointMethods: [
-        { key: 'GET', color: 'blue' },
-        { key: 'POST', color: 'green' },
-        { key: 'PUT', color: 'orange' },
-        { key: 'PATCH', color: 'cyan' },
-        { key: 'DELETE', color: 'red' },
-        { key: 'HEAD', color: 'deep-purple' },
-        { key: 'OPTIONS', color: 'blue-grey' }
-      ]
-    }
-  },
-  computed: {
-    isMobile() {
-      return this.$vuetify.breakpoint.smAndDown
-    },
-    locale: get('page/locale'),
-    path: get('page/path'),
-    mode: get('editor/mode'),
-    activeModal: sync('editor/activeModal')
-  },
-  methods: {
-    iconColor (val) {
-      return _.get(this.serverTypes, `${val}.color`, 'white')
-    },
-    iconKey (val) {
-      return _.get(this.serverTypes, `${val}.icon`, 'mdi-server')
-    },
-    methodColor (val) {
-      return _.get(_.find(this.endpointMethods, ['key', val]), 'color', 'grey')
-    },
-    addServer () {
-      this.servers.push({
-        id: uuid(),
-        name: 'Production',
-        url: 'https://api.example.com/v1',
-        icon: 'server'
-      })
-    },
-    removeServer (id) {
-      this.servers = _.reject(this.servers, ['id', id])
-    },
-    addGroup () {
-      this.endpointGroups.push({
-        id: uuid(),
-        name: '',
-        description: '',
-        endpoints: []
-      })
-    },
-    addEndpoint (grp) {
-      grp.endpoints.push({
-        id: uuid(),
-        method: 'GET',
-        path: '/pet',
-        expanded: false
-      })
-    },
-    removeEndpoint (grp, eptId) {
-      grp.endpoints = _.reject(grp.endpoints, ['id', eptId])
-    },
-    toggleModal(key) {
-      this.activeModal = (this.activeModal === key) ? '' : key
-      this.helpShown = false
-    },
-    closeAllModal() {
-      this.activeModal = ''
-      this.helpShown = false
-    }
-  },
-  mounted() {
-    this.$store.set('editor/editorKey', 'api')
-
-    if (this.mode === 'create') {
-      this.$store.set('editor/content', '<h1>Title</h1>\n\n<p>Some text here</p>')
-    }
-  },
-  beforeDestroy() {
-    this.$root.$off('editorInsert')
-  }
-}
-</script>
-
-<style lang='scss'>
-$editor-height: calc(100vh - 64px - 24px);
-$editor-height-mobile: calc(100vh - 56px - 16px);
-
-.editor-api {
-  &-main {
-    display: flex;
-    width: 100%;
-  }
-
-  &-editor {
-    background-color: darken(mc('grey', '100'), 4.5%);
-    flex: 1 1 50%;
-    display: block;
-    height: $editor-height;
-    position: relative;
-
-    @at-root .theme--dark & {
-      background-color: darken(mc('grey', '900'), 4.5%);
-    }
-  }
-
-  &-sidebar {
-    width: 200px;
-  }
-
-  &-sysbar {
-    padding-left: 0 !important;
-
-    &-locale {
-      background-color: rgba(255,255,255,.25);
-      display:inline-flex;
-      padding: 0 12px;
-      height: 24px;
-      width: 63px;
-      justify-content: center;
-      align-items: center;
-    }
-  }
-
-}
-</style>

+ 0 - 253
client/components/editor/editor-ckeditor.vue

@@ -1,253 +0,0 @@
-<template lang='pug'>
-  .editor-ckeditor
-    div(ref='toolbarContainer')
-    div.contents(ref='editor')
-    v-system-bar.editor-ckeditor-sysbar(dark, status, color='grey darken-3')
-      .caption.editor-ckeditor-sysbar-locale {{locale.toUpperCase()}}
-      .caption.px-3 /{{path}}
-      template(v-if='$vuetify.breakpoint.mdAndUp')
-        v-spacer
-        .caption Visual Editor
-        v-spacer
-        .caption {{$t('editor:ckeditor.stats', { chars: stats.characters, words: stats.words })}}
-    editor-conflict(v-model='isConflict', v-if='isConflict')
-    page-selector(mode='select', v-model='insertLinkDialog', :open-handler='insertLinkHandler', :path='path', :locale='locale')
-</template>
-
-<script>
-import _ from 'lodash'
-import { get, sync } from 'vuex-pathify'
-import DecoupledEditor from '@requarks/ckeditor5'
-// import DecoupledEditor from '../../../../wiki-ckeditor5/build/ckeditor'
-import EditorConflict from './ckeditor/conflict.vue'
-import { html as beautify } from 'js-beautify/js/lib/beautifier.min.js'
-
-/* global siteLangs */
-
-export default {
-  components: {
-    EditorConflict
-  },
-  props: {
-    save: {
-      type: Function,
-      default: () => {}
-    }
-  },
-  data() {
-    return {
-      editor: null,
-      stats: {
-        characters: 0,
-        words: 0
-      },
-      content: '',
-      isConflict: false,
-      insertLinkDialog: false
-    }
-  },
-  computed: {
-    isMobile() {
-      return this.$vuetify.breakpoint.smAndDown
-    },
-    locale: get('page/locale'),
-    path: get('page/path'),
-    activeModal: sync('editor/activeModal')
-  },
-  methods: {
-    insertLink () {
-      this.insertLinkDialog = true
-    },
-    insertLinkHandler ({ locale, path }) {
-      this.editor.execute('link', siteLangs.length > 0 ? `/${locale}/${path}` : `/${path}`)
-    }
-  },
-  async mounted () {
-    this.$store.set('editor/editorKey', 'ckeditor')
-
-    this.editor = await DecoupledEditor.create(this.$refs.editor, {
-      language: this.locale,
-      placeholder: 'Type the page content here',
-      disableNativeSpellChecker: false,
-      // TODO: Mention autocomplete
-      //
-      // mention: {
-      //   feeds: [
-      //     {
-      //       marker: '@',
-      //       feed: [ '@Barney', '@Lily', '@Marshall', '@Robin', '@Ted' ],
-      //       minimumCharacters: 1
-      //     }
-      //   ]
-      // },
-      wordCount: {
-        onUpdate: stats => {
-          this.stats = {
-            characters: stats.characters,
-            words: stats.words
-          }
-        }
-      }
-    })
-    this.$refs.toolbarContainer.appendChild(this.editor.ui.view.toolbar.element)
-
-    if (this.mode !== 'create') {
-      this.editor.setData(this.$store.get('editor/content'))
-    }
-
-    this.editor.model.document.on('change:data', _.debounce(evt => {
-      this.$store.set('editor/content', beautify(this.editor.getData(), { indent_size: 2, end_with_newline: true }))
-    }, 300))
-
-    this.$root.$on('editorInsert', opts => {
-      switch (opts.kind) {
-        case 'IMAGE':
-          this.editor.execute('imageInsert', {
-            source: opts.path
-          })
-          break
-        case 'BINARY':
-          this.editor.execute('link', opts.path, {
-            linkIsDownloadable: true
-          })
-          break
-        case 'DIAGRAM':
-          this.editor.execute('imageInsert', {
-            source: `data:image/svg+xml;base64,${opts.text}`
-          })
-          break
-      }
-    })
-
-    this.$root.$on('editorLinkToPage', opts => {
-      this.insertLink()
-    })
-
-    // Handle save conflict
-    this.$root.$on('saveConflict', () => {
-      this.isConflict = true
-    })
-    this.$root.$on('overwriteEditorContent', () => {
-      this.editor.setData(this.$store.get('editor/content'))
-    })
-  },
-  beforeDestroy () {
-    if (this.editor) {
-      this.editor.destroy()
-      this.editor = null
-    }
-  }
-}
-</script>
-
-<style lang="scss">
-
-$editor-height: calc(100vh - 64px - 24px);
-$editor-height-mobile: calc(100vh - 56px - 16px);
-
-.editor-ckeditor {
-  background-color: mc('grey', '200');
-  flex: 1 1 50%;
-  display: flex;
-  flex-flow: column nowrap;
-  height: $editor-height;
-  max-height: $editor-height;
-  position: relative;
-
-  @at-root .theme--dark & {
-    background-color: mc('grey', '900');
-  }
-
-  @include until($tablet) {
-    height: $editor-height-mobile;
-    max-height: $editor-height-mobile;
-  }
-
-  &-sysbar {
-    padding-left: 0;
-
-    &-locale {
-      background-color: rgba(255,255,255,.25);
-      display:inline-flex;
-      padding: 0 12px;
-      height: 24px;
-      width: 63px;
-      justify-content: center;
-      align-items: center;
-    }
-  }
-
-  .contents {
-    table {
-      margin: inherit;
-    }
-    pre > code {
-      background-color: unset;
-      color: unset;
-      padding: .15em;
-    }
-  }
-
-  .ck.ck-toolbar {
-    border: none;
-    justify-content: center;
-    background-color: mc('grey', '300');
-    color: #FFF;
-  }
-
-  .ck.ck-toolbar__items {
-    justify-content: center;
-  }
-
-  > .ck-editor__editable {
-    background-color: mc('grey', '100');
-    overflow-y: auto;
-    overflow-x: hidden;
-    padding: 2rem;
-    box-shadow: 0 0 5px hsla(0, 0, 0, .1);
-    margin: 1rem auto 0;
-    width: calc(100vw - 256px - 16vw);
-    min-height: calc(100vh - 64px - 24px - 1rem - 40px);
-    border-radius: 5px;
-
-    @at-root .theme--dark & {
-      background-color: #303030;
-      color: #FFF;
-    }
-
-    @include until($widescreen) {
-      width: calc(100vw - 2rem);
-      margin: 1rem 1rem 0 1rem;
-      min-height: calc(100vh - 64px - 24px - 1rem - 40px);
-    }
-
-    @include until($tablet) {
-      width: 100%;
-      margin: 0;
-      min-height: calc(100vh - 56px - 24px - 76px);
-    }
-
-    &.ck.ck-editor__editable:not(.ck-editor__nested-editable).ck-focused {
-      border-color: #FFF;
-      box-shadow: 0 0 10px rgba(mc('blue', '700'), .25);
-
-      @at-root .theme--dark & {
-        border-color: #444;
-        border-bottom: none;
-        box-shadow: 0 0 10px rgba(#000, .25);
-      }
-    }
-
-    &.ck .ck-editor__nested-editable.ck-editor__nested-editable_focused,
-    &.ck .ck-editor__nested-editable:focus,
-    .ck-widget.table td.ck-editor__nested-editable.ck-editor__nested-editable_focused,
-    .ck-widget.table th.ck-editor__nested-editable.ck-editor__nested-editable_focused {
-      background-color: mc('grey', '100');
-
-      @at-root .theme--dark & {
-        background-color: mc('grey', '900');
-      }
-    }
-  }
-}
-</style>

+ 0 - 452
client/components/editor/editor-code.vue

@@ -1,452 +0,0 @@
-<template lang='pug'>
-  .editor-code
-    .editor-code-main
-      .editor-code-sidebar
-        v-tooltip(right, color='teal')
-          template(v-slot:activator='{ on }')
-            v-btn.animated.fadeInLeft(icon, tile, v-on='on', dark, disabled).mx-0
-              v-icon mdi-link-plus
-          span {{$t('editor:markup.insertLink')}}
-        v-tooltip(right, color='teal')
-          template(v-slot:activator='{ on }')
-            v-btn.mt-3.animated.fadeInLeft.wait-p1s(icon, tile, v-on='on', dark, @click='toggleModal(`editorModalMedia`)').mx-0
-              v-icon(:color='activeModal === `editorModalMedia` ? `teal` : ``') mdi-folder-multiple-image
-          span {{$t('editor:markup.insertAssets')}}
-        v-tooltip(right, color='teal')
-          template(v-slot:activator='{ on }')
-            v-btn.mt-3.animated.fadeInLeft.wait-p2s(icon, tile, v-on='on', dark, @click='toggleModal(`editorModalBlocks`)', disabled).mx-0
-              v-icon(:color='activeModal === `editorModalBlocks` ? `teal` : ``') mdi-view-dashboard-outline
-          span {{$t('editor:markup.insertBlock')}}
-        v-tooltip(right, color='teal')
-          template(v-slot:activator='{ on }')
-            v-btn.mt-3.animated.fadeInLeft.wait-p3s(icon, tile, v-on='on', dark, disabled).mx-0
-              v-icon mdi-code-braces
-          span {{$t('editor:markup.insertCodeBlock')}}
-        v-tooltip(right, color='teal')
-          template(v-slot:activator='{ on }')
-            v-btn.mt-3.animated.fadeInLeft.wait-p4s(icon, tile, v-on='on', dark, disabled).mx-0
-              v-icon mdi-library-video
-          span {{$t('editor:markup.insertVideoAudio')}}
-        v-tooltip(right, color='teal')
-          template(v-slot:activator='{ on }')
-            v-btn.mt-3.animated.fadeInLeft.wait-p5s(icon, tile, v-on='on', dark, disabled).mx-0
-              v-icon mdi-chart-multiline
-          span {{$t('editor:markup.insertDiagram')}}
-        v-tooltip(right, color='teal')
-          template(v-slot:activator='{ on }')
-            v-btn.mt-3.animated.fadeInLeft.wait-p6s(icon, tile, v-on='on', dark, disabled).mx-0
-              v-icon mdi-function-variant
-          span {{$t('editor:markup.insertMathExpression')}}
-        template(v-if='$vuetify.breakpoint.mdAndUp')
-          v-spacer
-          v-tooltip(right, color='teal')
-            template(v-slot:activator='{ on }')
-              v-btn.mt-3.animated.fadeInLeft.wait-p8s(icon, tile, v-on='on', dark, @click='toggleFullscreen').mx-0
-                v-icon mdi-arrow-expand-all
-            span {{$t('editor:markup.distractionFreeMode')}}
-      .editor-code-editor
-        textarea(ref='cm')
-    v-system-bar.editor-code-sysbar(dark, status, color='grey darken-3')
-      .caption.editor-code-sysbar-locale {{locale.toUpperCase()}}
-      .caption.px-3 /{{path}}
-      template(v-if='$vuetify.breakpoint.mdAndUp')
-        v-spacer
-        .caption Code
-        v-spacer
-        .caption Ln {{cursorPos.line + 1}}, Col {{cursorPos.ch + 1}}
-</template>
-
-<script>
-import _ from 'lodash'
-import { get, sync } from 'vuex-pathify'
-
-// ========================================
-// IMPORTS
-// ========================================
-
-// Code Mirror
-import CodeMirror from 'codemirror'
-import 'codemirror/lib/codemirror.css'
-
-// Language
-import 'codemirror/mode/htmlmixed/htmlmixed.js'
-
-// Addons
-import 'codemirror/addon/selection/active-line.js'
-import 'codemirror/addon/display/fullscreen.js'
-import 'codemirror/addon/display/fullscreen.css'
-import 'codemirror/addon/selection/mark-selection.js'
-import 'codemirror/addon/search/searchcursor.js'
-
-// ========================================
-// INIT
-// ========================================
-
-// Platform detection
-// const CtrlKey = /Mac/.test(navigator.platform) ? 'Cmd' : 'Ctrl'
-
-// ========================================
-// Vue Component
-// ========================================
-
-export default {
-  data() {
-    return {
-      cm: null,
-      cursorPos: { ch: 0, line: 1 }
-    }
-  },
-  computed: {
-    isMobile() {
-      return this.$vuetify.breakpoint.smAndDown
-    },
-    locale: get('page/locale'),
-    path: get('page/path'),
-    mode: get('editor/mode'),
-    activeModal: sync('editor/activeModal')
-  },
-  methods: {
-    toggleModal(key) {
-      this.activeModal = (this.activeModal === key) ? '' : key
-      this.helpShown = false
-    },
-    closeAllModal() {
-      this.activeModal = ''
-      this.helpShown = false
-    },
-    /**
-     * Insert content at cursor
-     */
-    insertAtCursor({ content }) {
-      const cursor = this.cm.doc.getCursor('head')
-      this.cm.doc.replaceRange(content, cursor)
-    },
-    /**
-     * Insert content after current line
-     */
-    insertAfter({ content, newLine }) {
-      const curLine = this.cm.doc.getCursor('to').line
-      const lineLength = this.cm.doc.getLine(curLine).length
-      this.cm.doc.replaceRange(newLine ? `\n${content}\n` : content, { line: curLine, ch: lineLength + 1 })
-    },
-    /**
-     * Insert content before current line
-     */
-    insertBeforeEachLine({ content, after }) {
-      let lines = []
-      if (!this.cm.doc.somethingSelected()) {
-        lines.push(this.cm.doc.getCursor('head').line)
-      } else {
-        lines = _.flatten(this.cm.doc.listSelections().map(sl => {
-          const range = Math.abs(sl.anchor.line - sl.head.line) + 1
-          const lowestLine = (sl.anchor.line > sl.head.line) ? sl.head.line : sl.anchor.line
-          return _.times(range, l => l + lowestLine)
-        }))
-      }
-      lines.forEach(ln => {
-        let lineContent = this.cm.doc.getLine(ln)
-        const lineLength = lineContent.length
-        if (_.startsWith(lineContent, content)) {
-          lineContent = lineContent.substring(content.length)
-        }
-
-        this.cm.doc.replaceRange(content + lineContent, { line: ln, ch: 0 }, { line: ln, ch: lineLength })
-      })
-      if (after) {
-        const lastLine = _.last(lines)
-        this.cm.doc.replaceRange(`\n${after}\n`, { line: lastLine, ch: this.cm.doc.getLine(lastLine).length + 1 })
-      }
-    },
-    /**
-     * Update cursor state
-     */
-    positionSync(cm) {
-      this.cursorPos = cm.getCursor('head')
-    },
-    toggleFullscreen () {
-      this.cm.setOption('fullScreen', true)
-    },
-    refresh() {
-      this.$nextTick(() => {
-        this.cm.refresh()
-      })
-    }
-  },
-  mounted() {
-    this.$store.set('editor/editorKey', 'code')
-
-    if (this.mode === 'create') {
-      this.$store.set('editor/content', '<h1>Title</h1>\n\n<p>Some text here</p>')
-    }
-
-    // Initialize CodeMirror
-
-    this.cm = CodeMirror.fromTextArea(this.$refs.cm, {
-      tabSize: 2,
-      mode: 'text/html',
-      theme: 'wikijs-dark',
-      lineNumbers: true,
-      lineWrapping: true,
-      line: true,
-      styleActiveLine: true,
-      highlightSelectionMatches: {
-        annotateScrollbar: true
-      },
-      viewportMargin: 50,
-      inputStyle: 'contenteditable',
-      allowDropFileTypes: ['image/jpg', 'image/png', 'image/svg', 'image/jpeg', 'image/gif']
-    })
-    this.cm.setValue(this.$store.get('editor/content'))
-    this.cm.on('change', c => {
-      this.$store.set('editor/content', c.getValue())
-    })
-    if (this.$vuetify.breakpoint.mdAndUp) {
-      this.cm.setSize(null, 'calc(100vh - 64px - 24px)')
-    } else {
-      this.cm.setSize(null, 'calc(100vh - 56px - 16px)')
-    }
-
-    // Set Keybindings
-
-    const keyBindings = {
-      'F11' (c) {
-        c.setOption('fullScreen', !c.getOption('fullScreen'))
-      },
-      'Esc' (c) {
-        if (c.getOption('fullScreen')) c.setOption('fullScreen', false)
-      }
-    }
-    this.cm.setOption('extraKeys', keyBindings)
-
-    // Handle cursor movement
-
-    this.cm.on('cursorActivity', c => {
-      this.positionSync(c)
-    })
-
-    // Render initial preview
-
-    this.$root.$on('editorInsert', opts => {
-      switch (opts.kind) {
-        case 'IMAGE':
-          let img = `<img src="${opts.path}" alt="${opts.text}"`
-          if (opts.align && opts.align !== '') {
-            img += ` class="align-${opts.align}"`
-          }
-          img += ` />`
-          this.insertAtCursor({
-            content: img
-          })
-          break
-        case 'BINARY':
-          this.insertAtCursor({
-            content: `<a href="${opts.path}" title="${opts.text}">${opts.text}</a>`
-          })
-          break
-      }
-    })
-
-    // Handle save conflict
-    this.$root.$on('saveConflict', () => {
-      this.toggleModal(`editorModalConflict`)
-    })
-    this.$root.$on('overwriteEditorContent', () => {
-      this.cm.setValue(this.$store.get('editor/content'))
-    })
-  },
-  beforeDestroy() {
-    this.$root.$off('editorInsert')
-  }
-}
-</script>
-
-<style lang='scss'>
-$editor-height: calc(100vh - 64px - 24px);
-$editor-height-mobile: calc(100vh - 56px - 16px);
-
-.editor-code {
-  &-main {
-    display: flex;
-    width: 100%;
-  }
-
-  &-editor {
-    background-color: darken(mc('grey', '900'), 4.5%);
-    flex: 1 1 50%;
-    display: block;
-    height: $editor-height;
-    position: relative;
-
-    &-title {
-      background-color: mc('grey', '800');
-      border-bottom-left-radius: 5px;
-      display: inline-flex;
-      height: 30px;
-      justify-content: center;
-      align-items: center;
-      padding: 0 1rem;
-      color: mc('grey', '500');
-      position: absolute;
-      top: 0;
-      right: 0;
-      z-index: 7;
-      text-transform: uppercase;
-      font-size: .7rem;
-      cursor: pointer;
-
-      @include until($tablet) {
-        display: none;
-      }
-    }
-  }
-
-  &-sidebar {
-    background-color: mc('grey', '900');
-    width: 64px;
-    display: flex;
-    flex-direction: column;
-    justify-content: flex-start;
-    align-items: center;
-    padding: 24px 0;
-
-    @include until($tablet) {
-      padding: 12px 0;
-      width: 40px;
-    }
-  }
-
-  &-sysbar {
-    padding-left: 0;
-
-    &-locale {
-      background-color: rgba(255,255,255,.25);
-      display:inline-flex;
-      padding: 0 12px;
-      height: 24px;
-      width: 63px;
-      justify-content: center;
-      align-items: center;
-    }
-  }
-
-  // ==========================================
-  // CODE MIRROR
-  // ==========================================
-
-  .CodeMirror {
-    height: auto;
-
-    .cm-header-1 {
-      font-size: 1.5rem;
-    }
-    .cm-header-2 {
-      font-size: 1.25rem;
-    }
-    .cm-header-3 {
-      font-size: 1.15rem;
-    }
-    .cm-header-4 {
-      font-size: 1.1rem;
-    }
-    .cm-header-5 {
-      font-size: 1.05rem;
-    }
-    .cm-header-6 {
-      font-size: 1.025rem;
-    }
-  }
-
-  .CodeMirror-focused .cm-matchhighlight {
-    background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);
-    background-position: bottom;
-    background-repeat: repeat-x;
-  }
-  .cm-matchhighlight {
-    background-color: mc('grey', '800');
-  }
-  .CodeMirror-selection-highlight-scrollbar {
-    background-color: mc('green', '600');
-  }
-
-  .cm-s-wikijs-dark.CodeMirror {
-    background: darken(mc('grey','900'), 3%);
-    color: #e0e0e0;
-  }
-  .cm-s-wikijs-dark div.CodeMirror-selected {
-    background: mc('blue','800');
-  }
-  .cm-s-wikijs-dark .cm-matchhighlight {
-    background: mc('blue','800');
-  }
-  .cm-s-wikijs-dark .CodeMirror-line::selection, .cm-s-wikijs-dark .CodeMirror-line > span::selection, .cm-s-wikijs-dark .CodeMirror-line > span > span::selection {
-    background: mc('amber', '500');
-  }
-  .cm-s-wikijs-dark .CodeMirror-line::-moz-selection, .cm-s-wikijs-dark .CodeMirror-line > span::-moz-selection, .cm-s-wikijs-dark .CodeMirror-line > span > span::-moz-selection {
-    background: mc('amber', '500');
-  }
-  .cm-s-wikijs-dark .CodeMirror-gutters {
-    background: darken(mc('grey','900'), 6%);
-    border-right: 1px solid mc('grey','900');
-  }
-  .cm-s-wikijs-dark .CodeMirror-guttermarker {
-    color: #ac4142;
-  }
-  .cm-s-wikijs-dark .CodeMirror-guttermarker-subtle {
-    color: #505050;
-  }
-  .cm-s-wikijs-dark .CodeMirror-linenumber {
-    color: mc('grey','800');
-  }
-  .cm-s-wikijs-dark .CodeMirror-cursor {
-    border-left: 1px solid #b0b0b0;
-  }
-  .cm-s-wikijs-dark span.cm-comment {
-    color: mc('orange','800');
-  }
-  .cm-s-wikijs-dark span.cm-atom {
-    color: #aa759f;
-  }
-  .cm-s-wikijs-dark span.cm-number {
-    color: #aa759f;
-  }
-  .cm-s-wikijs-dark span.cm-property, .cm-s-wikijs-dark span.cm-attribute {
-    color: #90a959;
-  }
-  .cm-s-wikijs-dark span.cm-keyword {
-    color: #ac4142;
-  }
-  .cm-s-wikijs-dark span.cm-string {
-    color: #f4bf75;
-  }
-  .cm-s-wikijs-dark span.cm-variable {
-    color: #90a959;
-  }
-  .cm-s-wikijs-dark span.cm-variable-2 {
-    color: #6a9fb5;
-  }
-  .cm-s-wikijs-dark span.cm-def {
-    color: #d28445;
-  }
-  .cm-s-wikijs-dark span.cm-bracket {
-    color: #e0e0e0;
-  }
-  .cm-s-wikijs-dark span.cm-tag {
-    color: #ac4142;
-  }
-  .cm-s-wikijs-dark span.cm-link {
-    color: #aa759f;
-  }
-  .cm-s-wikijs-dark span.cm-error {
-    background: #ac4142;
-    color: #b0b0b0;
-  }
-  .cm-s-wikijs-dark .CodeMirror-activeline-background {
-    background: mc('grey','900');
-  }
-  .cm-s-wikijs-dark .CodeMirror-matchingbracket {
-    text-decoration: underline;
-    color: white !important;
-  }
-
-}
-</style>

+ 0 - 1141
client/components/editor/editor-markdown.vue

@@ -1,1141 +0,0 @@
-<template lang='pug'>
-  .editor-markdown
-    v-toolbar.editor-markdown-toolbar(dense, color='primary', dark, flat, style='overflow-x: hidden;')
-      template(v-if='isModalShown')
-        v-spacer
-        v-btn.animated.fadeInRight(text, @click='closeAllModal')
-          v-icon(left) mdi-arrow-left-circle
-          span {{$t('editor:backToEditor')}}
-      template(v-else)
-        v-tooltip(bottom, color='primary')
-          template(v-slot:activator='{ on }')
-            v-btn.animated.fadeIn(icon, tile, v-on='on', @click='toggleMarkup({ start: `**` })').mx-0
-              v-icon mdi-format-bold
-          span {{$t('editor:markup.bold')}}
-        v-tooltip(bottom, color='primary')
-          template(v-slot:activator='{ on }')
-            v-btn.animated.fadeIn.wait-p1s(icon, tile, v-on='on', @click='toggleMarkup({ start: `*` })').mx-0
-              v-icon mdi-format-italic
-          span {{$t('editor:markup.italic')}}
-        v-tooltip(bottom, color='primary')
-          template(v-slot:activator='{ on }')
-            v-btn.animated.fadeIn.wait-p2s(icon, tile, v-on='on', @click='toggleMarkup({ start: `~~` })').mx-0
-              v-icon mdi-format-strikethrough
-          span {{$t('editor:markup.strikethrough')}}
-        v-menu(offset-y, open-on-hover)
-          template(v-slot:activator='{ on }')
-            v-btn.animated.fadeIn.wait-p3s(icon, tile, v-on='on').mx-0
-              v-icon mdi-format-header-pound
-          v-list.py-0
-            template(v-for='(n, idx) in 6')
-              v-list-item(@click='setHeaderLine(n)', :key='idx')
-                v-list-item-action
-                  v-icon(:size='24 - (idx - 1) * 2') mdi-format-header-{{n}}
-                v-list-item-title {{$t('editor:markup.heading', { level: n })}}
-              v-divider(v-if='idx < 5')
-        v-tooltip(bottom, color='primary')
-          template(v-slot:activator='{ on }')
-            v-btn.animated.fadeIn.wait-p4s(icon, tile, v-on='on', @click='toggleMarkup({ start: `~` })').mx-0
-              v-icon mdi-format-subscript
-          span {{$t('editor:markup.subscript')}}
-        v-tooltip(bottom, color='primary')
-          template(v-slot:activator='{ on }')
-            v-btn.animated.fadeIn.wait-p5s(icon, tile, v-on='on', @click='toggleMarkup({ start: `^` })').mx-0
-              v-icon mdi-format-superscript
-          span {{$t('editor:markup.superscript')}}
-        v-menu(offset-y, open-on-hover)
-          template(v-slot:activator='{ on }')
-            v-btn.animated.fadeIn.wait-p6s(icon, tile, v-on='on').mx-0
-              v-icon mdi-alpha-t-box-outline
-          v-list.py-0
-            v-list-item(@click='insertBeforeEachLine({ content: `> `})')
-              v-list-item-action
-                v-icon mdi-alpha-t-box-outline
-              v-list-item-title {{$t('editor:markup.blockquote')}}
-            v-divider
-            v-list-item(@click='insertBeforeEachLine({ content: `> `, after: `{.is-info}`})')
-              v-list-item-action
-                v-icon(color='blue') mdi-alpha-i-box-outline
-              v-list-item-title {{$t('editor:markup.blockquoteInfo')}}
-            v-divider
-            v-list-item(@click='insertBeforeEachLine({ content: `> `, after: `{.is-success}`})')
-              v-list-item-action
-                v-icon(color='success') mdi-alpha-s-box-outline
-              v-list-item-title {{$t('editor:markup.blockquoteSuccess')}}
-            v-divider
-            v-list-item(@click='insertBeforeEachLine({ content: `> `, after: `{.is-warning}`})')
-              v-list-item-action
-                v-icon(color='warning') mdi-alpha-w-box-outline
-              v-list-item-title {{$t('editor:markup.blockquoteWarning')}}
-            v-divider
-            v-list-item(@click='insertBeforeEachLine({ content: `> `, after: `{.is-danger}`})')
-              v-list-item-action
-                v-icon(color='error') mdi-alpha-e-box-outline
-              v-list-item-title {{$t('editor:markup.blockquoteError')}}
-            v-divider
-        v-tooltip(bottom, color='primary')
-          template(v-slot:activator='{ on }')
-            v-btn.animated.fadeIn.wait-p7s(icon, tile, v-on='on', @click='insertBeforeEachLine({ content: `- `})').mx-0
-              v-icon mdi-format-list-bulleted
-          span {{$t('editor:markup.unorderedList')}}
-        v-tooltip(bottom, color='primary')
-          template(v-slot:activator='{ on }')
-            v-btn.animated.fadeIn.wait-p8s(icon, tile, v-on='on', @click='insertBeforeEachLine({ content: `1. `})').mx-0
-              v-icon mdi-format-list-numbered
-          span {{$t('editor:markup.orderedList')}}
-        v-tooltip(bottom, color='primary')
-          template(v-slot:activator='{ on }')
-            v-btn.animated.fadeIn.wait-p9s(icon, tile, v-on='on', @click='toggleMarkup({ start: "`" })').mx-0
-              v-icon mdi-code-tags
-          span {{$t('editor:markup.inlineCode')}}
-        v-tooltip(bottom, color='primary')
-          template(v-slot:activator='{ on }')
-            v-btn.animated.fadeIn.wait-p10s(icon, tile, v-on='on', @click='toggleMarkup({ start: `<kbd>`, end: `</kbd>` })').mx-0
-              v-icon mdi-keyboard-variant
-          span {{$t('editor:markup.keyboardKey')}}
-        v-tooltip(bottom, color='primary')
-          template(v-slot:activator='{ on }')
-            v-btn.animated.fadeIn.wait-p11s(icon, tile, v-on='on', @click='insertAfter({ content: `---`, newLine: true })').mx-0
-              v-icon mdi-minus
-          span {{$t('editor:markup.horizontalBar')}}
-        template(v-if='$vuetify.breakpoint.mdAndUp')
-          v-spacer
-          v-tooltip(bottom, color='primary', v-if='previewShown')
-            template(v-slot:activator='{ on }')
-              v-btn.animated.fadeIn.wait-p1s(icon, tile, v-on='on', @click='spellModeActive = !spellModeActive').mx-0
-                v-icon(:color='spellModeActive ? `amber` : `white`') mdi-spellcheck
-            span {{$t('editor:markup.toggleSpellcheck')}}
-          v-tooltip(bottom, color='primary')
-            template(v-slot:activator='{ on }')
-              v-btn.animated.fadeIn.wait-p2s(icon, tile, v-on='on', @click='previewShown = !previewShown').mx-0
-                v-icon mdi-book-open-outline
-            span {{$t('editor:markup.togglePreviewPane')}}
-    .editor-markdown-main
-      .editor-markdown-sidebar
-        v-tooltip(right, color='teal')
-          template(v-slot:activator='{ on }')
-            v-btn.animated.fadeInLeft(icon, tile, v-on='on', dark, @click='insertLink').mx-0
-              v-icon mdi-link-plus
-          span {{$t('editor:markup.insertLink')}}
-        v-tooltip(right, color='teal')
-          template(v-slot:activator='{ on }')
-            v-btn.mt-3.animated.fadeInLeft.wait-p1s(icon, tile, v-on='on', dark, @click='toggleModal(`editorModalMedia`)').mx-0
-              v-icon(:color='activeModal === `editorModalMedia` ? `teal` : ``') mdi-folder-multiple-image
-          span {{$t('editor:markup.insertAssets')}}
-        v-tooltip(right, color='teal')
-          template(v-slot:activator='{ on }')
-            v-btn.mt-3.animated.fadeInLeft.wait-p2s(icon, tile, v-on='on', dark, disabled, @click='toggleModal(`editorModalBlocks`)').mx-0
-              v-icon(:color='activeModal === `editorModalBlocks` ? `teal` : ``') mdi-view-dashboard-outline
-          span {{$t('editor:markup.insertBlock')}}
-        v-tooltip(right, color='teal')
-          template(v-slot:activator='{ on }')
-            v-btn.mt-3.animated.fadeInLeft.wait-p3s(icon, tile, v-on='on', dark, disabled).mx-0
-              v-icon mdi-code-braces
-          span {{$t('editor:markup.insertCodeBlock')}}
-        v-tooltip(right, color='teal')
-          template(v-slot:activator='{ on }')
-            v-btn.mt-3.animated.fadeInLeft.wait-p4s(icon, tile, v-on='on', dark, disabled).mx-0
-              v-icon mdi-movie
-          span {{$t('editor:markup.insertVideoAudio')}}
-        v-tooltip(right, color='teal')
-          template(v-slot:activator='{ on }')
-            v-btn.mt-3.animated.fadeInLeft.wait-p5s(icon, tile, v-on='on', dark, @click='toggleModal(`editorModalDrawio`)').mx-0
-              v-icon mdi-chart-multiline
-          span {{$t('editor:markup.insertDiagram')}}
-        v-tooltip(right, color='teal')
-          template(v-slot:activator='{ on }')
-            v-btn.mt-3.animated.fadeInLeft.wait-p6s(icon, tile, v-on='on', dark, disabled).mx-0
-              v-icon mdi-function-variant
-          span {{$t('editor:markup.insertMathExpression')}}
-        v-tooltip(right, color='teal')
-          template(v-slot:activator='{ on }')
-            v-btn.mt-3.animated.fadeInLeft.wait-p7s(icon, tile, v-on='on', dark, disabled).mx-0
-              v-icon mdi-table-plus
-          span {{$t('editor:markup.tableHelper')}}
-        template(v-if='$vuetify.breakpoint.mdAndUp')
-          v-spacer
-          v-tooltip(right, color='teal')
-            template(v-slot:activator='{ on }')
-              v-btn.mt-3.animated.fadeInLeft.wait-p8s(icon, tile, v-on='on', dark, @click='toggleFullscreen').mx-0
-                v-icon mdi-arrow-expand-all
-            span {{$t('editor:markup.distractionFreeMode')}}
-          v-tooltip(right, color='teal')
-            template(v-slot:activator='{ on }')
-              v-btn.mt-3.animated.fadeInLeft.wait-p9s(icon, tile, v-on='on', dark, @click='toggleHelp').mx-0
-                v-icon(:color='helpShown ? `teal` : ``') mdi-help-circle
-            span {{$t('editor:markup.markdownFormattingHelp')}}
-      .editor-markdown-editor
-        textarea(ref='cm')
-      transition(name='editor-markdown-preview')
-        .editor-markdown-preview(v-if='previewShown')
-          .editor-markdown-preview-content.contents(ref='editorPreviewContainer')
-            div(
-              ref='editorPreview'
-              v-html='previewHTML'
-              :spellcheck='spellModeActive'
-              :contenteditable='spellModeActive'
-              @blur='spellModeActive = false'
-              )
-
-    v-system-bar.editor-markdown-sysbar(dark, status, color='grey darken-3')
-      .caption.editor-markdown-sysbar-locale {{locale.toUpperCase()}}
-      .caption.px-3 /{{path}}
-      template(v-if='$vuetify.breakpoint.mdAndUp')
-        v-spacer
-        .caption Markdown
-        v-spacer
-        .caption Ln {{cursorPos.line + 1}}, Col {{cursorPos.ch + 1}}
-
-    markdown-help(v-if='helpShown')
-    page-selector(mode='select', v-model='insertLinkDialog', :open-handler='insertLinkHandler', :path='path', :locale='locale')
-</template>
-
-<script>
-import _ from 'lodash'
-import { get, sync } from 'vuex-pathify'
-import markdownHelp from './markdown/help.vue'
-import gql from 'graphql-tag'
-import DOMPurify from 'dompurify'
-
-/* global siteConfig, siteLangs */
-
-// ========================================
-// IMPORTS
-// ========================================
-
-// Code Mirror
-import CodeMirror from 'codemirror'
-import 'codemirror/lib/codemirror.css'
-
-// Language
-import 'codemirror/mode/markdown/markdown.js'
-
-// Addons
-import 'codemirror/addon/selection/active-line.js'
-import 'codemirror/addon/display/fullscreen.js'
-import 'codemirror/addon/display/fullscreen.css'
-import 'codemirror/addon/selection/mark-selection.js'
-import 'codemirror/addon/search/searchcursor.js'
-import 'codemirror/addon/hint/show-hint.js'
-import 'codemirror/addon/fold/foldcode.js'
-import 'codemirror/addon/fold/foldgutter.js'
-import 'codemirror/addon/fold/foldgutter.css'
-import './markdown/fold'
-
-// Markdown-it
-import MarkdownIt from 'markdown-it'
-import mdAttrs from 'markdown-it-attrs'
-import mdEmoji from 'markdown-it-emoji'
-import mdTaskLists from 'markdown-it-task-lists'
-import mdExpandTabs from 'markdown-it-expand-tabs'
-import mdAbbr from 'markdown-it-abbr'
-import mdSup from 'markdown-it-sup'
-import mdSub from 'markdown-it-sub'
-import mdMark from 'markdown-it-mark'
-import mdMultiTable from 'markdown-it-multimd-table'
-import mdFootnote from 'markdown-it-footnote'
-import mdImsize from 'markdown-it-imsize'
-import katex from 'katex'
-import underline from '../../libs/markdown-it-underline'
-import 'katex/dist/contrib/mhchem'
-import twemoji from 'twemoji'
-import plantuml from './markdown/plantuml'
-
-// Prism (Syntax Highlighting)
-import Prism from 'prismjs'
-
-// Mermaid
-import mermaid from 'mermaid'
-
-// Helpers
-import katexHelper from './common/katex'
-import tabsetHelper from './markdown/tabset'
-
-// ========================================
-// INIT
-// ========================================
-
-// Platform detection
-const CtrlKey = /Mac/.test(navigator.platform) ? 'Cmd' : 'Ctrl'
-
-// Prism Config
-Prism.plugins.autoloader.languages_path = '/_assets-legacy/js/prism/'
-Prism.plugins.NormalizeWhitespace.setDefaults({
-  'remove-trailing': true,
-  'remove-indent': true,
-  'left-trim': true,
-  'right-trim': true,
-  'remove-initial-line-feed': true,
-  'tabs-to-spaces': 2
-})
-
-// Markdown Instance
-const md = new MarkdownIt({
-  html: true,
-  breaks: true,
-  linkify: true,
-  typography: true,
-  highlight(str, lang) {
-    if (lang === 'diagram') {
-      return `<pre class="diagram">` + Buffer.from(str, 'base64').toString() + `</pre>`
-    } else if (['mermaid', 'plantuml'].includes(lang)) {
-      return `<pre class="codeblock-${lang}"><code>${_.escape(str)}</code></pre>`
-    } else {
-      return `<pre class="line-numbers"><code class="language-${lang}">${_.escape(str)}</code></pre>`
-    }
-  }
-})
-  .use(mdAttrs, {
-    allowedAttributes: ['id', 'class', 'target']
-  })
-  .use(underline)
-  .use(mdEmoji)
-  .use(mdTaskLists, { label: false, labelAfter: false })
-  .use(mdExpandTabs)
-  .use(mdAbbr)
-  .use(mdSup)
-  .use(mdSub)
-  .use(mdMultiTable, { multiline: true, rowspan: true, headerless: true })
-  .use(mdMark)
-  .use(mdFootnote)
-  .use(mdImsize)
-
-// DOMPurify fix for draw.io
-DOMPurify.addHook('uponSanitizeElement', (elm) => {
-  if (elm.querySelectorAll) {
-    const breaks = elm.querySelectorAll('foreignObject br, foreignObject p')
-    if (breaks && breaks.length) {
-      for (let i = 0; i < breaks.length; i++) {
-        breaks[i].parentNode.replaceChild(
-          document.createElement('div'),
-          breaks[i]
-        )
-      }
-    }
-  }
-})
-
-// ========================================
-// HELPER FUNCTIONS
-// ========================================
-
-// Inject line numbers for preview scroll sync
-let linesMap = []
-function injectLineNumbers (tokens, idx, options, env, slf) {
-  let line
-  if (tokens[idx].map && tokens[idx].level === 0) {
-    line = tokens[idx].map[0]
-    tokens[idx].attrJoin('class', 'line')
-    tokens[idx].attrSet('data-line', String(line))
-    linesMap.push(line)
-  }
-  return slf.renderToken(tokens, idx, options, env, slf)
-}
-md.renderer.rules.paragraph_open = injectLineNumbers
-md.renderer.rules.heading_open = injectLineNumbers
-md.renderer.rules.blockquote_open = injectLineNumbers
-
-// ========================================
-// PLANTUML
-// ========================================
-
-// TODO: Use same options as defined in backend
-plantuml.init(md, {})
-
-// ========================================
-// KATEX
-// ========================================
-
-md.inline.ruler.after('escape', 'katex_inline', katexHelper.katexInline)
-md.renderer.rules.katex_inline = (tokens, idx) => {
-  try {
-    return katex.renderToString(tokens[idx].content, {
-      displayMode: false
-    })
-  } catch (err) {
-    console.warn(err)
-    return tokens[idx].content
-  }
-}
-md.block.ruler.after('blockquote', 'katex_block', katexHelper.katexBlock, {
-  alt: [ 'paragraph', 'reference', 'blockquote', 'list' ]
-})
-md.renderer.rules.katex_block = (tokens, idx) => {
-  try {
-    return `<p>` + katex.renderToString(tokens[idx].content, {
-      displayMode: true
-    }) + `</p>`
-  } catch (err) {
-    console.warn(err)
-    return tokens[idx].content
-  }
-}
-
-// ========================================
-// TWEMOJI
-// ========================================
-
-md.renderer.rules.emoji = (token, idx) => {
-  return twemoji.parse(token[idx].content, {
-    callback (icon, opts) {
-      return `/_assets-legacy/svg/twemoji/${icon}.svg`
-    }
-  })
-}
-
-// ========================================
-// Vue Component
-// ========================================
-
-let mermaidId = 0
-
-export default {
-  components: {
-    markdownHelp
-  },
-  props: {
-    save: {
-      type: Function,
-      default: () => {}
-    }
-  },
-  data() {
-    return {
-      fabInsertMenu: false,
-      cm: null,
-      cursorPos: { ch: 0, line: 1 },
-      previewShown: true,
-      previewHTML: '',
-      helpShown: false,
-      spellModeActive: false,
-      insertLinkDialog: false
-    }
-  },
-  computed: {
-    isMobile() {
-      return this.$vuetify.breakpoint.smAndDown
-    },
-    isModalShown() {
-      return this.helpShown || this.activeModal !== ''
-    },
-    locale: get('page/locale'),
-    path: get('page/path'),
-    mode: get('editor/mode'),
-    activeModal: sync('editor/activeModal')
-  },
-  watch: {
-    previewShown (newValue, oldValue) {
-      if (newValue && !oldValue) {
-        this.$nextTick(() => {
-          this.renderMermaidDiagrams()
-          Prism.highlightAllUnder(this.$refs.editorPreview)
-          Array.from(this.$refs.editorPreview.querySelectorAll('pre.line-numbers')).forEach(pre => pre.classList.add('prismjs'))
-        })
-      }
-    },
-    spellModeActive (newValue, oldValue) {
-      if (newValue) {
-        this.$nextTick(() => {
-          this.$refs.editorPreview.focus()
-        })
-      }
-    }
-  },
-  methods: {
-    toggleModal(key) {
-      this.activeModal = (this.activeModal === key) ? '' : key
-      this.helpShown = false
-    },
-    closeAllModal() {
-      this.activeModal = ''
-      this.helpShown = false
-    },
-    onCmInput: _.debounce(function (newContent) {
-      this.processContent(newContent)
-    }, 600),
-    onCmPaste (cm, ev) {
-      // const clipItems = (ev.clipboardData || ev.originalEvent.clipboardData).items
-      // for (let clipItem of clipItems) {
-      //   if (_.startsWith(clipItem.type, 'image/')) {
-      //     const file = clipItem.getAsFile()
-      //     const reader = new FileReader()
-      //     reader.onload = evt => {
-      //       this.$store.commit(`loadingStart`, 'editor-paste-image')
-      //       this.insertAfter({
-      //         content: `![${file.name}](${evt.target.result})`,
-      //         newLine: true
-      //       })
-      //     }
-      //     reader.readAsDataURL(file)
-      //   }
-      // }
-    },
-    processContent (newContent) {
-      linesMap = []
-      // this.$store.set('editor/content', newContent)
-      this.processMarkers(this.cm.firstLine(), this.cm.lastLine())
-      this.previewHTML = DOMPurify.sanitize(md.render(newContent), {
-        ADD_TAGS: ['foreignObject']
-      })
-      this.$nextTick(() => {
-        tabsetHelper.format()
-        this.renderMermaidDiagrams()
-        Prism.highlightAllUnder(this.$refs.editorPreview)
-        Array.from(this.$refs.editorPreview.querySelectorAll('pre.line-numbers')).forEach(pre => pre.classList.add('prismjs'))
-        this.scrollSync(this.cm)
-      })
-    },
-    /**
-     * Update cursor state
-     */
-    positionSync(cm) {
-      this.cursorPos = cm.getCursor('head')
-    },
-    /**
-     * Wrap selection with start / end tags
-     */
-    toggleMarkup({ start, end }) {
-      if (!end) { end = start }
-      if (!this.cm.doc.somethingSelected()) {
-        return this.$store.commit('showNotification', {
-          message: this.$t('editor:markup.noSelectionError'),
-          style: 'warning',
-          icon: 'warning'
-        })
-      }
-      this.cm.doc.replaceSelections(this.cm.doc.getSelections().map(s => start + s + end))
-    },
-    /**
-     * Set current line as header
-     */
-    setHeaderLine(lvl) {
-      const curLine = this.cm.doc.getCursor('head').line
-      let lineContent = this.cm.doc.getLine(curLine)
-      const lineLength = lineContent.length
-      if (_.startsWith(lineContent, '#')) {
-        lineContent = lineContent.replace(/^(#+ )/, '')
-      }
-      lineContent = _.times(lvl, n => '#').join('') + ` ` + lineContent
-      this.cm.doc.replaceRange(lineContent, { line: curLine, ch: 0 }, { line: curLine, ch: lineLength })
-    },
-    /**
-     * Get the header lever of the current line
-     */
-    getHeaderLevel(cm) {
-      const curLine = this.cm.doc.getCursor('head').line
-      let lineContent = this.cm.doc.getLine(curLine)
-      let lvl = 0
-
-      const result = lineContent.match(/^(#+) /)
-      if (result) {
-        lvl = _.get(result, '[1]', '').length
-      }
-      return lvl
-    },
-    /**
-     * Insert content at cursor
-     */
-    insertAtCursor({ content }) {
-      const cursor = this.cm.doc.getCursor('head')
-      this.cm.doc.replaceRange(content, cursor)
-    },
-    /**
-     * Insert content after current line
-     */
-    insertAfter({ content, newLine }) {
-      const curLine = this.cm.doc.getCursor('to').line
-      const lineLength = this.cm.doc.getLine(curLine).length
-      this.cm.doc.replaceRange(newLine ? `\n${content}\n` : content, { line: curLine, ch: lineLength + 1 })
-    },
-    /**
-     * Insert content before current line
-     */
-    insertBeforeEachLine({ content, after }) {
-      let lines = []
-      if (!this.cm.doc.somethingSelected()) {
-        lines.push(this.cm.doc.getCursor('head').line)
-      } else {
-        lines = _.flatten(this.cm.doc.listSelections().map(sl => {
-          const range = Math.abs(sl.anchor.line - sl.head.line) + 1
-          const lowestLine = (sl.anchor.line > sl.head.line) ? sl.head.line : sl.anchor.line
-          return _.times(range, l => l + lowestLine)
-        }))
-      }
-      lines.forEach(ln => {
-        let lineContent = this.cm.doc.getLine(ln)
-        const lineLength = lineContent.length
-        if (_.startsWith(lineContent, content)) {
-          lineContent = lineContent.substring(content.length)
-        }
-
-        this.cm.doc.replaceRange(content + lineContent, { line: ln, ch: 0 }, { line: ln, ch: lineLength })
-      })
-      if (after) {
-        const lastLine = _.last(lines)
-        this.cm.doc.replaceRange(`\n${after}\n`, { line: lastLine, ch: this.cm.doc.getLine(lastLine).length + 1 })
-      }
-    },
-    /**
-     * Update scroll sync
-     */
-    scrollSync: _.debounce(function (cm) {
-      if (!this.previewShown || cm.somethingSelected()) { return }
-      let currentLine = cm.getCursor().line
-      if (currentLine < 3) {
-        this.Velocity(this.$refs.editorPreview, 'stop', true)
-        this.Velocity(this.$refs.editorPreview.firstChild, 'scroll', { offset: '-50', duration: 1000, container: this.$refs.editorPreviewContainer })
-      } else {
-        let closestLine = _.findLast(linesMap, n => n <= currentLine)
-        let destElm = this.$refs.editorPreview.querySelector(`[data-line='${closestLine}']`)
-        if (destElm) {
-          this.Velocity(this.$refs.editorPreview, 'stop', true)
-          this.Velocity(destElm, 'scroll', { offset: '-100', duration: 1000, container: this.$refs.editorPreviewContainer })
-        }
-      }
-    }, 500),
-    toggleHelp () {
-      this.helpShown = !this.helpShown
-      this.activeModal = ''
-    },
-    toggleFullscreen () {
-      this.cm.setOption('fullScreen', true)
-    },
-    refresh() {
-      this.$nextTick(() => {
-        this.cm.refresh()
-      })
-    },
-    renderMermaidDiagrams () {
-      document.querySelectorAll('.editor-markdown-preview pre.codeblock-mermaid > code').forEach(elm => {
-        mermaidId++
-        const mermaidDef = elm.innerText
-        const mmElm = document.createElement('div')
-        mmElm.innerHTML = `<div id="mermaid-id-${mermaidId}">${mermaid.render(`mermaid-id-${mermaidId}`, mermaidDef)}</div>`
-        elm.parentElement.replaceWith(mmElm)
-      })
-    },
-    autocomplete (cm, change) {
-      if (cm.getModeAt(cm.getCursor()).name !== 'markdown') {
-        return
-      }
-
-      // Links
-      if (change.text[0] === '(') {
-        const curLine = cm.getLine(change.from.line).substring(0, change.from.ch)
-        if (curLine[curLine.length - 1] === ']') {
-          cm.showHint({
-            hint: async (cm, options) => {
-              const cur = cm.getCursor()
-              const curLine = cm.getLine(cur.line).substring(0, cur.ch)
-              const queryString = curLine.substring(curLine.lastIndexOf('[') + 1, curLine.length - 2)
-              const token = cm.getTokenAt(cur)
-              try {
-                const respRaw = await this.$apollo.query({
-                  query: gql`
-                    query ($query: String!, $locale: String) {
-                      pages {
-                        search(query:$query, locale:$locale) {
-                          results {
-                            title
-                            path
-                            locale
-                          }
-                          totalHits
-                        }
-                      }
-                    }
-                  `,
-                  variables: {
-                    query: queryString,
-                    locale: this.locale
-                  },
-                  fetchPolicy: 'cache-first'
-                })
-                const resp = _.get(respRaw, 'data.pages.search', {})
-                if (resp && resp.totalHits > 0) {
-                  return {
-                    list: resp.results.map(r => ({
-                      text: '(' + (siteLangs.length > 0 ? `/${r.locale}/${r.path}` : `/${r.path}`) + ')',
-                      displayText: siteLangs.length > 0 ? `/${r.locale}/${r.path} - ${r.title}` : `/${r.path} - ${r.title}`
-                    })),
-                    from: CodeMirror.Pos(cur.line, token.start),
-                    to: CodeMirror.Pos(cur.line, token.end)
-                  }
-                }
-              } catch (err) {}
-              return {
-                list: [],
-                from: CodeMirror.Pos(cur.line, token.start),
-                to: CodeMirror.Pos(cur.line, token.end)
-              }
-            }
-          })
-        }
-      }
-    },
-    insertLink () {
-      this.insertLinkDialog = true
-    },
-    insertLinkHandler ({ locale, path }) {
-      const lastPart = _.last(path.split('/'))
-      this.insertAtCursor({
-        content: siteLangs.length > 0 ? `[${lastPart}](/${locale}/${path})` : `[${lastPart}](/${path})`
-      })
-    },
-    processMarkers (from, to) {
-      let found = null
-      let foundStart = 0
-      this.cm.doc.getAllMarks().forEach(mk => {
-        if (mk.__kind) {
-          mk.clear()
-        }
-      })
-      this.cm.eachLine(from, to, ln => {
-        const line = ln.lineNo()
-        if (ln.text.startsWith('```diagram')) {
-          found = 'diagram'
-          foundStart = line
-        } else if (ln.text === '```' && found) {
-          switch (found) {
-            // ------------------------------
-            // -> DIAGRAM
-            // ------------------------------
-            case 'diagram': {
-              if (line - foundStart !== 2) {
-                return
-              }
-              this.addMarker({
-                kind: 'diagram',
-                from: { line: foundStart, ch: 3 },
-                to: { line: foundStart, ch: 10 },
-                text: 'Edit Diagram',
-                action: ((start, end) => {
-                  return (ev) => {
-                    this.cm.doc.setSelection({ line: start, ch: 0 }, { line: end, ch: 3 })
-                    try {
-                      const raw = this.cm.doc.getLine(end - 1)
-                      this.$store.set('editor/activeModalData', Buffer.from(raw, 'base64').toString())
-                      this.toggleModal(`editorModalDrawio`)
-                    } catch (err) {
-                      return this.$store.commit('showNotification', {
-                        message: 'Failed to process diagram data.',
-                        style: 'warning',
-                        icon: 'warning'
-                      })
-                    }
-                  }
-                })(foundStart, line)
-              })
-              if (ln.height > 0) {
-                this.cm.foldCode(foundStart)
-              }
-              break
-            }
-          }
-          found = null
-        }
-      })
-    },
-    addMarker ({ kind, from, to, text, action }) {
-      const markerElm = document.createElement('span')
-      markerElm.appendChild(document.createTextNode(text))
-      markerElm.className = 'CodeMirror-buttonmarker'
-      markerElm.addEventListener('click', action)
-      this.cm.markText(from, to, { replacedWith: markerElm, __kind: kind })
-    }
-  },
-  mounted() {
-    this.$store.set('editor/editorKey', 'markdown')
-
-    if (this.mode === 'create' && !this.$store.get('editor/content')) {
-      this.$store.set('editor/content', '# Header\nYour content here')
-    }
-
-    // Initialize Mermaid API
-    mermaid.initialize({
-      startOnLoad: false,
-      theme: this.$vuetify.theme.dark ? `dark` : `default`
-    })
-
-    // Initialize CodeMirror
-
-    this.cm = CodeMirror.fromTextArea(this.$refs.cm, {
-      tabSize: 2,
-      mode: 'text/markdown',
-      theme: 'wikijs-dark',
-      lineNumbers: true,
-      lineWrapping: true,
-      line: true,
-      styleActiveLine: true,
-      highlightSelectionMatches: {
-        annotateScrollbar: true
-      },
-      viewportMargin: 50,
-      inputStyle: 'contenteditable',
-      allowDropFileTypes: ['image/jpg', 'image/png', 'image/svg', 'image/jpeg', 'image/gif'],
-      direction: siteConfig.rtl ? 'rtl' : 'ltr',
-      foldGutter: true,
-      gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter']
-    })
-    this.cm.setValue(this.$store.get('editor/content'))
-    this.cm.on('change', c => {
-      this.$store.set('editor/content', c.getValue())
-      this.onCmInput(this.$store.get('editor/content'))
-    })
-    if (this.$vuetify.breakpoint.mdAndUp) {
-      this.cm.setSize(null, 'calc(100vh - 112px - 24px)')
-    } else {
-      this.cm.setSize(null, 'calc(100vh - 112px - 16px)')
-    }
-
-    // Set Keybindings
-
-    const keyBindings = {
-      'F11' (c) {
-        c.setOption('fullScreen', !c.getOption('fullScreen'))
-      },
-      'Esc' (c) {
-        if (c.getOption('fullScreen')) c.setOption('fullScreen', false)
-      }
-    }
-    _.set(keyBindings, `${CtrlKey}-S`, c => {
-      this.save()
-      return false
-    })
-    _.set(keyBindings, `${CtrlKey}-B`, c => {
-      this.toggleMarkup({ start: `**` })
-      return false
-    })
-    _.set(keyBindings, `${CtrlKey}-I`, c => {
-      this.toggleMarkup({ start: `*` })
-      return false
-    })
-    _.set(keyBindings, `${CtrlKey}-Alt-Right`, c => {
-      let lvl = this.getHeaderLevel(c)
-      if (lvl >= 6) { lvl = 5 }
-      this.setHeaderLine(lvl + 1)
-      return false
-    })
-    _.set(keyBindings, `${CtrlKey}-Alt-Left`, c => {
-      let lvl = this.getHeaderLevel(c)
-      if (lvl <= 1) { lvl = 2 }
-      this.setHeaderLine(lvl - 1)
-      return false
-    })
-    this.cm.setOption('extraKeys', keyBindings)
-
-    this.cm.on('inputRead', this.autocomplete)
-
-    // Handle cursor movement
-
-    this.cm.on('cursorActivity', c => {
-      this.positionSync(c)
-      this.scrollSync(c)
-    })
-
-    // Handle special paste
-
-    this.cm.on('paste', this.onCmPaste)
-
-    // Render initial preview
-
-    this.processContent(this.$store.get('editor/content'))
-    this.refresh()
-
-    this.$root.$on('editorInsert', opts => {
-      switch (opts.kind) {
-        case 'IMAGE':
-          let img = `![${opts.text}](${opts.path})`
-          if (opts.align && opts.align !== '') {
-            img += `{.align-${opts.align}}`
-          }
-          this.insertAtCursor({
-            content: img
-          })
-          break
-        case 'BINARY':
-          this.insertAtCursor({
-            content: `[${opts.text}](${opts.path})`
-          })
-          break
-        case 'DIAGRAM':
-          const selStartLine = this.cm.getCursor('from').line
-          const selEndLine = this.cm.getCursor('to').line + 1
-          this.cm.doc.replaceSelection('```diagram\n' + opts.text + '\n```\n', 'start')
-          this.processMarkers(selStartLine, selEndLine)
-          break
-      }
-    })
-
-    // Handle save conflict
-    this.$root.$on('saveConflict', () => {
-      this.toggleModal(`editorModalConflict`)
-    })
-    this.$root.$on('overwriteEditorContent', () => {
-      this.cm.setValue(this.$store.get('editor/content'))
-    })
-  },
-  beforeDestroy() {
-    this.$root.$off('editorInsert')
-  }
-}
-</script>
-
-<style lang='scss'>
-
-$editor-height: calc(100vh - 112px - 24px);
-$editor-height-mobile: calc(100vh - 112px - 16px);
-
-.editor-markdown {
-  &-main {
-    display: flex;
-    width: 100%;
-  }
-
-  &-editor {
-    background-color: darken(mc('grey', '900'), 4.5%);
-    flex: 1 1 50%;
-    display: block;
-    height: $editor-height;
-    position: relative;
-
-    @include until($tablet) {
-      height: $editor-height-mobile;
-    }
-  }
-
-  &-preview {
-    flex: 1 1 50%;
-    background-color: mc('grey', '100');
-    position: relative;
-    height: $editor-height;
-    overflow: hidden;
-    padding: 1rem;
-
-    @at-root .theme--dark & {
-      background-color: mc('grey', '900');
-    }
-
-    @include until($tablet) {
-      display: none;
-    }
-
-    &-enter-active, &-leave-active {
-      transition: max-width .5s ease;
-      max-width: 50vw;
-
-      .editor-code-preview-content {
-        width: 50vw;
-        overflow:hidden;
-      }
-    }
-    &-enter, &-leave-to {
-      max-width: 0;
-    }
-
-    &-content {
-      height: $editor-height;
-      overflow-y: scroll;
-      padding: 0;
-      width: calc(100% + 17px);
-      // -ms-overflow-style: none;
-
-      // &::-webkit-scrollbar {
-      //   width: 0px;
-      //   background: transparent;
-      // }
-
-      @include until($tablet) {
-        height: $editor-height-mobile;
-      }
-
-      > div {
-        outline: none;
-      }
-
-      p.line {
-        overflow-wrap: break-word;
-      }
-
-      .tabset {
-        background-color: mc('teal', '700');
-        color: mc('teal', '100') !important;
-        padding: 5px 12px;
-        font-size: 14px;
-        font-weight: 500;
-        border-radius: 5px 0 0 0;
-        font-style: italic;
-
-        &::after {
-          display: none;
-        }
-
-        &-header {
-          background-color: mc('teal', '500');
-          color: #FFF !important;
-          padding: 5px 12px;
-          font-size: 14px;
-          font-weight: 500;
-          margin-top: 0 !important;
-
-          &::after {
-            display: none;
-          }
-        }
-
-        &-content {
-          border-left: 5px solid mc('teal', '500');
-          background-color: mc('teal', '50');
-          padding: 0 15px 15px;
-          overflow: hidden;
-
-          @at-root .theme--dark & {
-            background-color: rgba(mc('teal', '500'), .1);
-          }
-        }
-      }
-    }
-  }
-
-  &-toolbar {
-    background-color: mc('blue', '700');
-    background-image: linear-gradient(to bottom, mc('blue', '700') 0%, mc('blue','800') 100%);
-    color: #FFF;
-
-    .v-toolbar__content {
-      padding-left: 64px;
-
-      @include until($tablet) {
-        padding-left: 8px;
-      }
-    }
-  }
-
-  &-insert:not(.v-speed-dial--right) {
-    @include from($tablet) {
-      left: 50%;
-      margin-left: -28px;
-    }
-  }
-
-  &-sidebar {
-    background-color: mc('grey', '900');
-    width: 64px;
-    display: flex;
-    flex-direction: column;
-    justify-content: flex-start;
-    align-items: center;
-    padding: 24px 0;
-
-    @include until($tablet) {
-      padding: 12px 0;
-      width: 40px;
-    }
-  }
-
-  &-sysbar {
-    padding-left: 0;
-
-    &-locale {
-      background-color: rgba(255,255,255,.25);
-      display:inline-flex;
-      padding: 0 12px;
-      height: 24px;
-      width: 63px;
-      justify-content: center;
-      align-items: center;
-    }
-  }
-
-  // ==========================================
-  // Fix FAB revealing under codemirror
-  // ==========================================
-
-  .speed-dial--fixed {
-    z-index: 8;
-  }
-
-  // ==========================================
-  // CODE MIRROR
-  // ==========================================
-
-  .CodeMirror {
-    height: auto;
-    font-family: 'Roboto Mono', monospace;
-    font-size: .9rem;
-
-    .cm-header-1 {
-      font-size: 1.5rem;
-    }
-    .cm-header-2 {
-      font-size: 1.25rem;
-    }
-    .cm-header-3 {
-      font-size: 1.15rem;
-    }
-    .cm-header-4 {
-      font-size: 1.1rem;
-    }
-    .cm-header-5 {
-      font-size: 1.05rem;
-    }
-    .cm-header-6 {
-      font-size: 1.025rem;
-    }
-  }
-
-  .CodeMirror-wrap pre.CodeMirror-line, .CodeMirror-wrap pre.CodeMirror-line-like {
-    word-break: break-word;
-  }
-
-  .CodeMirror-focused .cm-matchhighlight {
-    background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);
-    background-position: bottom;
-    background-repeat: repeat-x;
-  }
-  .cm-matchhighlight {
-    background-color: mc('grey', '800');
-  }
-  .CodeMirror-selection-highlight-scrollbar {
-    background-color: mc('green', '600');
-  }
-}
-
-// HINT DROPDOWN
-
-.CodeMirror-hints {
-  position: absolute;
-  z-index: 10;
-  overflow: hidden;
-  list-style: none;
-
-  margin: 0;
-  padding: 1px;
-
-  box-shadow: 2px 3px 5px rgba(0,0,0,.2);
-  border: 1px solid mc('grey', '700');
-
-  background: mc('grey', '900');
-  font-family: 'Roboto Mono', monospace;
-  font-size: .9rem;
-
-  max-height: 150px;
-  overflow-y: auto;
-
-  min-width: 250px;
-  max-width: 80vw;
-}
-
-.CodeMirror-hint {
-  margin: 0;
-  padding: 0 4px;
-  white-space: pre;
-  color: #FFF;
-  cursor: pointer;
-}
-
-li.CodeMirror-hint-active {
-  background: mc('blue', '500');
-  color: #FFF;
-}
-</style>

+ 0 - 81
client/components/editor/editor-modal-blocks.vue

@@ -1,81 +0,0 @@
-<template lang='pug'>
-  v-card.editor-modal-blocks.animated.fadeInLeft(flat, tile)
-    v-container.pa-3(grid-list-lg, fluid)
-      v-row(dense)
-        v-col(
-          v-for='(item, idx) of blocks'
-          :key='`block-` + item.key'
-          cols='12'
-          lg='4'
-          xl='3'
-          )
-          v-card.radius-7(light, flat, @click='selectBlock(item)')
-            v-card-text
-              .d-flex.align-center
-                v-avatar.radius-7(color='teal')
-                  v-icon(dark) {{item.icon}}
-                .pl-3
-                  .body-2: strong.teal--text {{item.title}}
-                  .caption.grey--text {{item.description}}
-</template>
-
-<script>
-import _ from 'lodash'
-import { sync } from 'vuex-pathify'
-
-export default {
-  props: {
-    value: {
-      type: Boolean,
-      default: false
-    }
-  },
-  data() {
-    return {
-      blocks: [
-        {
-          key: 'childlist',
-          title: 'List Children Pages',
-          description: 'Display a links list of all children of this page.',
-          icon: 'mdi-format-list-text'
-        },
-        {
-          key: 'tabs',
-          title: 'Tabs',
-          description: 'Organize content within tabs.',
-          icon: 'mdi-tab'
-        }
-      ]
-    }
-  },
-  computed: {
-    isShown: {
-      get() { return this.value },
-      set(val) { this.$emit('input', val) }
-    },
-    activeModal: sync('editor/activeModal')
-  },
-  methods: {
-    selectBlock (item) {
-      this.block = _.cloneDeep(item)
-    }
-  }
-}
-</script>
-
-<style lang='scss'>
-.editor-modal-blocks {
-    position: fixed;
-    top: 112px;
-    left: 64px;
-    z-index: 10;
-    width: calc(100vw - 64px - 17px);
-    height: calc(100vh - 112px - 24px);
-    background-color: rgba(darken(mc('grey', '900'), 3%), .9) !important;
-
-    @include until($tablet) {
-      left: 40px;
-      width: calc(100vw - 40px);
-    }
-}
-</style>

+ 0 - 222
client/components/editor/editor-modal-conflict.vue

@@ -1,222 +0,0 @@
-<template lang='pug'>
-  v-card.editor-modal-conflict.animated.fadeIn(flat, tile)
-    .pa-4
-      v-toolbar.radius-7(flat, color='indigo', style='border-bottom-left-radius: 0; border-bottom-right-radius: 0;', dark)
-        v-icon.mr-3 mdi-merge
-        .subtitle-1 {{$t('editor:conflict.title')}}
-        v-spacer
-        v-btn(outlined, color='white', @click='useLocal', :title='$t(`editor:conflict.useLocalHint`)')
-          v-icon(left) mdi-alpha-l-box
-          span {{$t('editor:conflict.useLocal')}}
-        v-dialog(
-          v-model='isRemoteConfirmDiagShown'
-          width='500'
-          )
-          template(v-slot:activator='{ on }')
-            v-btn.ml-3(outlined, color='white', v-on='on', :title='$t(`editor:conflict.useRemoteHint`)')
-              v-icon(left) mdi-alpha-r-box
-              span {{$t('editor:conflict.useRemote')}}
-          v-card
-            .dialog-header.is-short.is-indigo
-              v-icon.mr-3(color='white') mdi-alpha-r-box
-              span {{$t('editor:conflict.overwrite.title')}}
-            v-card-text.pa-4
-              i18next.body-2(tag='div', path='editor:conflict.overwrite.description')
-                strong(place='refEditsLost') {{$t('editor:conflict.overwrite.editsLost')}}
-            v-card-chin
-              v-spacer
-              v-btn(outlined, color='indigo', @click='isRemoteConfirmDiagShown = false')
-                v-icon(left) mdi-close
-                span {{$t('common:actions.cancel')}}
-              v-btn(@click='useRemote', color='indigo', dark)
-                v-icon(left) mdi-check
-                span {{$t('common:actions.confirm')}}
-        v-divider.mx-3(vertical)
-        v-btn(outlined, color='indigo lighten-4', @click='close')
-          v-icon(left) mdi-close
-          span {{$t('common:actions.cancel')}}
-      v-row.indigo.darken-1.body-2(no-gutters)
-        v-col.pa-4
-          v-icon.mr-3(color='white') mdi-alpha-l-box
-          i18next.white--text(tag='span', path='editor:conflict.localVersion')
-            em.indigo--text.text--lighten-4(place='refEditable') {{$t('editor:conflict.editable')}}
-        v-divider(vertical)
-        v-col.pa-4
-          v-icon.mr-3(color='white') mdi-alpha-r-box
-          i18next.white--text(tag='span', path='editor:conflict.remoteVersion')
-            em.indigo--text.text--lighten-4(place='refReadOnly') {{$t('editor:conflict.readonly')}}
-      v-row.grey.lighten-2.body-2(no-gutters)
-        v-col.px-4.py-2
-          i18next.grey--text.text--darken-2(tag='em', path='editor:conflict.leftPanelInfo')
-            span(place='date', :title='$options.filters.moment(checkoutDateActive, `LLL`)') {{ checkoutDateActive | moment('from') }}
-        v-divider(vertical)
-        v-col.px-4.py-2
-          i18next.grey--text.text--darken-2(tag='em', path='editor:conflict.rightPanelInfo')
-            strong(place='authorName') {{latest.authorName}}
-            span(place='date', :title='$options.filters.moment(latest.updatedAt, `LLL`)') {{ latest.updatedAt | moment('from') }}
-      v-row.grey.lighten-3.grey--text.text--darken-3(no-gutters)
-        v-col.pa-4
-          .body-2
-            strong.indigo--text {{$t('editor:conflict.pageTitle')}}
-            strong.pl-2 {{title}}
-          .caption
-            strong.indigo--text {{$t('editor:conflict.pageDescription')}}
-            span.pl-2 {{description}}
-        v-divider(vertical, light)
-        v-col.pa-4
-          .body-2
-            strong.indigo--text {{$t('editor:conflict.pageTitle')}}
-            strong.pl-2 {{latest.title}}
-          .caption
-            strong.indigo--text {{$t('editor:conflict.pageDescription')}}
-            span.pl-2 {{latest.description}}
-      v-card.radius-7(:light='!$vuetify.theme.dark', :dark='$vuetify.theme.dark')
-        div(ref='cm')
-</template>
-
-<script>
-import _ from 'lodash'
-import gql from 'graphql-tag'
-import { sync, get } from 'vuex-pathify'
-
-/* global siteConfig */
-
-// ========================================
-// IMPORTS
-// ========================================
-
-import '../../libs/codemirror-merge/diff-match-patch.js'
-
-// Code Mirror
-import CodeMirror from 'codemirror'
-import 'codemirror/lib/codemirror.css'
-
-// Language
-import 'codemirror/mode/markdown/markdown.js'
-import 'codemirror/mode/htmlmixed/htmlmixed.js'
-
-// Addons
-import 'codemirror/addon/selection/active-line.js'
-import 'codemirror/addon/merge/merge.js'
-import 'codemirror/addon/merge/merge.css'
-
-export default {
-  data() {
-    return {
-      cm: null,
-      latest: {
-        title: '',
-        description: '',
-        updatedAt: '',
-        authorName: ''
-      },
-      isRemoteConfirmDiagShown: false
-    }
-  },
-  computed: {
-    editorKey: get('editor/editorKey'),
-    activeModal: sync('editor/activeModal'),
-    pageId: get('page/id'),
-    title: get('page/title'),
-    description: get('page/description'),
-    updatedAt: get('page/updatedAt'),
-    checkoutDateActive: sync('editor/checkoutDateActive')
-  },
-  methods: {
-    close () {
-      this.isRemoteConfirmDiagShown = false
-      this.activeModal = ''
-    },
-    overwriteAndClose() {
-      this.checkoutDateActive = this.latest.updatedAt
-      this.$root.$emit('overwriteEditorContent')
-      this.$root.$emit('resetEditorConflict')
-      this.close()
-    },
-    useLocal () {
-      this.$store.set('editor/content', this.cm.edit.getValue())
-      this.overwriteAndClose()
-    },
-    useRemote () {
-      this.$store.set('editor/content', this.latest.content)
-      this.overwriteAndClose()
-    }
-  },
-  async mounted () {
-    let textMode = 'text/html'
-
-    switch (this.editorKey) {
-      case 'markdown':
-        textMode = 'text/markdown'
-        break
-    }
-
-    let resp = await this.$apollo.query({
-      query: gql`
-        query ($id: Int!) {
-          pages {
-            conflictLatest(id: $id) {
-              id
-              authorId
-              authorName
-              content
-              createdAt
-              description
-              isPublished
-              locale
-              path
-              tags
-              title
-              updatedAt
-            }
-          }
-        }
-      `,
-      fetchPolicy: 'network-only',
-      variables: {
-        id: this.$store.get('page/id')
-      }
-    })
-    resp = _.get(resp, 'data.pages.conflictLatest', false)
-
-    if (!resp) {
-      return this.$store.commit('showNotification', {
-        message: 'Failed to fetch latest version.',
-        style: 'warning',
-        icon: 'warning'
-      })
-    }
-    this.latest = resp
-
-    this.cm = CodeMirror.MergeView(this.$refs.cm, {
-      value: this.$store.get('editor/content'),
-      orig: resp.content,
-      tabSize: 2,
-      mode: textMode,
-      lineNumbers: true,
-      lineWrapping: true,
-      connect: null,
-      highlightDifferences: true,
-      styleActiveLine: true,
-      collapseIdentical: true,
-      direction: siteConfig.rtl ? 'rtl' : 'ltr'
-    })
-    this.cm.rightOriginal().setSize(null, 'calc(100vh - 265px)')
-    this.cm.editor().setSize(null, 'calc(100vh - 265px)')
-    this.cm.wrap.style.height = 'calc(100vh - 265px)'
-  }
-}
-</script>
-
-<style lang='scss'>
-.editor-modal-conflict {
-  position: fixed !important;
-  top: 0;
-  left: 0;
-  z-index: 10;
-  width: 100%;
-  height: 100vh;
-  background-color: rgba(0, 0, 0, .9) !important;
-  overflow: auto;
-}
-</style>

+ 0 - 126
client/components/editor/editor-modal-drawio.vue

@@ -1,126 +0,0 @@
-<template lang='pug'>
-  v-card.editor-modal-drawio.animated.fadeIn(flat, tile)
-    iframe(
-      ref='drawio'
-      src='https://embed.diagrams.net/?embed=1&proto=json&spin=1&saveAndExit=1&noSaveBtn=1&noExitBtn=0'
-      frameborder='0'
-    )
-</template>
-
-<script>
-import { sync, get } from 'vuex-pathify'
-
-// const xmlTest = `<?xml version="1.0" encoding="UTF-8"?>
-// <mxfile version="13.4.2">
-//   <diagram id="SgbkCjxR32CZT1FvBvkp" name="Page-1">
-//     <mxGraphModel dx="2062" dy="1123" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
-//       <root>
-//         <mxCell id="0" />
-//         <mxCell id="1" parent="0" />
-//         <mxCell id="5gE3BTvRYS_8FoJnOusC-1" value="" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="1">
-//           <mxGeometry x="380" y="530" width="80" height="80" as="geometry" />
-//         </mxCell>
-//       </root>
-//     </mxGraphModel>
-//   </diagram>
-// </mxfile>
-// `
-
-export default {
-  data() {
-    return {
-      content: ''
-    }
-  },
-  computed: {
-    editorKey: get('editor/editorKey'),
-    activeModal: sync('editor/activeModal')
-  },
-  methods: {
-    close () {
-      this.activeModal = ''
-    },
-    overwriteAndClose() {
-      this.$root.$emit('overwriteEditorContent')
-      this.$root.$emit('resetEditorConflict')
-      this.close()
-    },
-    send (msg) {
-      this.$refs.drawio.contentWindow.postMessage(JSON.stringify(msg), '*')
-    },
-    receive (evt) {
-      if (evt.frame === null || evt.source !== this.$refs.drawio.contentWindow || evt.data.length < 1) {
-        return
-      }
-      try {
-        const msg = JSON.parse(evt.data)
-        switch (msg.event) {
-          case 'init': {
-            this.send({
-              action: 'load',
-              autosave: 0,
-              modified: 'unsavedChanges',
-              xml: this.$store.get('editor/activeModalData'),
-              title: this.$store.get('page/title')
-            })
-            this.$store.set('editor/activeModalData', null)
-            break
-          }
-          case 'save': {
-            if (msg.exit) {
-              this.send({
-                action: 'export',
-                format: 'xmlsvg'
-              })
-            }
-            break
-          }
-          case 'export': {
-            const svgDataStart = msg.data.indexOf('base64,') + 7
-            this.$root.$emit('editorInsert', {
-              kind: 'DIAGRAM',
-              text: msg.data.slice(svgDataStart)
-              // text: msg.xml.replace(/ agent="(.*?)"/, '').replace(/ host="(.*?)"/, '').replace(/ etag="(.*?)"/, '')
-            })
-            this.close()
-            break
-          }
-          case 'exit': {
-            this.close()
-            break
-          }
-        }
-      } catch (err) {
-        console.error(err)
-      }
-    }
-  },
-  async mounted () {
-    window.addEventListener('message', this.receive)
-  },
-  beforeDestroy () {
-    window.removeEventListener('message', this.receive)
-  }
-}
-</script>
-
-<style lang='scss'>
-.editor-modal-drawio {
-  position: fixed !important;
-  top: 0;
-  left: 0;
-  z-index: 10;
-  width: 100%;
-  height: 100vh;
-  background-color: rgba(255,255,255, 1) !important;
-  overflow: hidden;
-
-  > iframe {
-    width: 100%;
-    height: 100vh;
-    border: 0;
-    padding: 0;
-    background-color: #FFF;
-  }
-}
-</style>

+ 0 - 90
client/components/editor/editor-modal-editorselect.vue

@@ -1,90 +0,0 @@
-<template lang='pug'>
-  v-dialog(v-model='isShown', persistent, max-width='700', no-click-animation)
-    v-btn(fab, fixed, bottom, right, color='grey darken-3', dark, @click='goBack', style='width: 50px;'): v-icon mdi-undo-variant
-    v-card.radius-7(color='blue darken-3', dark)
-      v-card-text.text-center.py-4
-        .subtitle-1.white--text {{$t('editor:select.title')}}
-        v-container(grid-list-lg, fluid)
-          v-layout(row, wrap, justify-center)
-            v-flex(xs4)
-              v-card.radius-7.animated.fadeInUp.wait-p2s(
-                hover
-                light
-                ripple
-                )
-                v-card-text.text-center(@click='selectEditor("code")')
-                  img(src='/_assets-legacy/svg/editor-icon-code.svg', alt='Code', style='width: 36px;')
-                  .body-2.primary--text.mt-2 Code
-                  .caption.grey--text Raw HTML
-            v-flex(xs4)
-              v-card.radius-7.animated.fadeInUp.wait-p1s(
-                hover
-                light
-                ripple
-                )
-                v-card-text.text-center(@click='selectEditor("markdown")')
-                  img(src='/_assets-legacy/svg/editor-icon-markdown.svg', alt='Markdown', style='width: 36px;')
-                  .body-2.primary--text.mt-2 Markdown
-                  .caption.grey--text Plain Text Formatting
-            v-flex(xs4)
-              v-card.radius-7.animated.fadeInUp.wait-p3s(
-                hover
-                light
-                ripple
-                )
-                v-card-text.text-center(@click='selectEditor("ckeditor")')
-                  img(src='/_assets-legacy/svg/editor-icon-ckeditor.svg', alt='Visual Editor', style='width: 36px;')
-                  .body-2.mt-2.primary--text Visual Editor
-                  .caption.grey--text Rich-text WYSIWYG
-</template>
-
-<script>
-import _ from 'lodash'
-import { sync, get } from 'vuex-pathify'
-
-export default {
-  props: {
-    value: {
-      type: Boolean,
-      default: false
-    }
-  },
-  data() {
-    return {
-      templateDialogIsShown: false
-    }
-  },
-  computed: {
-    isShown: {
-      get() { return this.value },
-      set(val) { this.$emit('input', val) }
-    },
-    currentEditor: sync('editor/editor'),
-    locale: get('page/locale'),
-    path: get('page/path')
-  },
-  methods: {
-    selectEditor (name) {
-      this.currentEditor = `editor${_.startCase(name)}`
-      this.isShown = false
-    },
-    goBack () {
-      window.history.go(-1)
-    },
-    fromTemplate () {
-      this.templateDialogIsShown = true
-    },
-    fromTemplateHandle ({ id }) {
-      this.templateDialogIsShown = false
-      this.isShown = false
-      this.$nextTick(() => {
-        window.location.assign(`/e/${this.locale}/${this.path}?from=${id}`)
-      })
-    }
-  }
-}
-</script>
-
-<style lang='scss'>
-
-</style>

+ 0 - 629
client/components/editor/editor-modal-media.vue

@@ -1,629 +0,0 @@
-<template lang='pug'>
-  v-card.editor-modal-media.animated.fadeInLeft(flat, tile, :class='`is-editor-` + editorKey')
-    v-container.pa-3(grid-list-lg, fluid)
-      v-layout(row, wrap)
-        v-flex(xs12, lg9)
-          v-card.radius-7.animated.fadeInLeft.wait-p1s(:light='!$vuetify.theme.dark', :dark='$vuetify.theme.dark')
-            v-card-text
-              .d-flex
-                v-toolbar.radius-7(:color='$vuetify.theme.dark ? `teal` : `teal lighten-5`', dense, flat, height='44')
-                  .body-2(:class='$vuetify.theme.dark ? `white--text` : `teal--text`') {{$t('editor:assets.title')}}
-                  v-spacer
-                  v-btn(text, icon, @click='refresh')
-                    v-icon(:color='$vuetify.theme.dark ? `white` : `teal`') mdi-refresh
-                v-dialog(v-model='newFolderDialog', max-width='550')
-                  template(v-slot:activator='{ on }')
-                    v-btn.ml-3.my-0.mr-0.radius-7(outlined, large, color='teal', :icon='$vuetify.breakpoint.xsOnly', v-on='on')
-                      v-icon(:left='$vuetify.breakpoint.mdAndUp') mdi-plus
-                      span.hidden-sm-and-down(:class='$vuetify.theme.dark ? `teal--text text--lighten-3` : ``') {{$t('editor:assets.newFolder')}}
-                  v-card
-                    .dialog-header.is-short.subtitle-1 {{$t('editor:assets.newFolder')}}
-                    v-card-text.pt-5
-                      v-text-field.md2(
-                        outlined
-                        prepend-icon='mdi-folder-outline'
-                        v-model='newFolderName'
-                        :label='$t(`editor:assets.folderName`)'
-                        counter='255'
-                        @keyup.enter='createFolder'
-                        @keyup.esc='newFolderDialog = false'
-                        ref='folderNameIpt'
-                        )
-                      i18next.caption.grey--text.text--darken-1.pl-5(path='editor:assets.folderNameNamingRules', tag='div')
-                        a(place='namingRules', href='https://docs-beta.requarks.io/guide/assets#naming-restrictions', target='_blank') {{$t('editor:assets.folderNameNamingRulesLink')}}
-                    v-card-chin
-                      v-spacer
-                      v-btn(text, @click='newFolderDialog = false') {{$t('common:actions.cancel')}}
-                      v-btn.px-3(color='primary', @click='createFolder', :disabled='!isFolderNameValid', :loading='newFolderLoading') {{$t('common:actions.create')}}
-              v-toolbar(flat, dense, :color='$vuetify.theme.dark ? `grey darken-3` : `white`')
-                template(v-if='folderTree.length > 0')
-                  .body-2
-                    span.mr-1 /
-                    template(v-for='folder of folderTree')
-                      span(:key='folder.id') {{folder.name}}
-                      span.mx-1 /
-                .body-2(v-else) / #[em root]
-              template(v-if='folders.length > 0 || currentFolderId > 0')
-                v-btn.is-icon.mx-1(:color='$vuetify.theme.dark ? `grey lighten-1` : `grey darken-2`', outlined, :dark='currentFolderId > 0', @click='upFolder()', :disabled='currentFolderId === 0')
-                  v-icon mdi-folder-upload
-                v-btn.btn-normalcase.mx-1(v-for='folder of folders', :key='folder.id', depressed,  color='grey darken-2', dark, @click='downFolder(folder)')
-                  v-icon(left) mdi-folder
-                  span.caption(style='text-transform: none;') {{ folder.name }}
-                v-divider.mt-2
-              v-data-table(
-                :items='assets'
-                :headers='headers'
-                :page.sync='pagination'
-                :items-per-page='15'
-                :loading='loading'
-                must-sort,
-                sort-by='ID',
-                sort-desc,
-                hide-default-footer,
-                dense
-              )
-                template(slot='item', slot-scope='props')
-                  tr.is-clickable(
-                    @click.left='currentFileId = props.item.id'
-                    @click.right.prevent=''
-                    :class='currentFileId === props.item.id ? ($vuetify.theme.dark ? `grey darken-3-d5` : `teal lighten-5`) : ``'
-                    )
-                    td.caption(v-if='$vuetify.breakpoint.smAndUp') {{ props.item.id }}
-                    td
-                      .body-2: strong(:class='currentFileId === props.item.id ? `teal--text` : ``') {{ props.item.filename }}
-                      .caption.grey--text {{ props.item.description }}
-                    td.text-xs-center(v-if='$vuetify.breakpoint.lgAndUp')
-                      v-chip.ma-0(x-small, :color='$vuetify.theme.dark ? `grey darken-4` : `grey lighten-4`')
-                        .overline {{props.item.ext.toUpperCase().substring(1)}}
-                    td.caption(v-if='$vuetify.breakpoint.mdAndUp') {{ props.item.fileSize | prettyBytes }}
-                    td.caption(v-if='$vuetify.breakpoint.mdAndUp') {{ props.item.createdAt | moment('from') }}
-                    td(v-if='$vuetify.breakpoint.smAndUp')
-                      v-menu(offset-x, min-width='200')
-                        template(v-slot:activator='{ on }')
-                          v-btn(icon, v-on='on', tile, small, @click.left='currentFileId = props.item.id')
-                            v-icon(color='grey darken-2') mdi-dots-horizontal
-                        v-list(nav, style='border-top: 5px solid #444;')
-                          v-list-item(@click='', disabled)
-                            v-list-item-avatar(size='24')
-                              v-icon(color='teal') mdi-text-short
-                            v-list-item-content {{$t('common:actions.properties')}}
-                          template(v-if='props.item.kind === `IMAGE`')
-                            v-list-item(@click='previewDialog = true', disabled)
-                              v-list-item-avatar(size='24')
-                                v-icon(color='green') mdi-image-search-outline
-                              v-list-item-content {{$t('common:actions.preview')}}
-                            v-list-item(@click='', disabled)
-                              v-list-item-avatar(size='24')
-                                v-icon(color='indigo') mdi-crop-rotate
-                              v-list-item-content {{$t('common:actions.edit')}}
-                            v-list-item(@click='', disabled)
-                              v-list-item-avatar(size='24')
-                                v-icon(color='purple') mdi-flash-circle
-                              v-list-item-content {{$t('common:actions.optimize')}}
-                          v-list-item(@click='openRenameDialog')
-                            v-list-item-avatar(size='24')
-                              v-icon(color='orange') mdi-keyboard-outline
-                            v-list-item-content {{$t('common:actions.rename')}}
-                          v-list-item(@click='', disabled)
-                            v-list-item-avatar(size='24')
-                              v-icon(color='blue') mdi-file-move
-                            v-list-item-content {{$t('common:actions.move')}}
-                          v-list-item(@click='deleteDialog = true')
-                            v-list-item-avatar(size='24')
-                              v-icon(color='red') mdi-file-hidden
-                            v-list-item-content {{$t('common:actions.delete')}}
-                template(slot='no-data')
-                  v-alert.mt-3.radius-7(icon='mdi-folder-open-outline', :value='true', outlined, color='teal') {{$t('editor:assets.folderEmpty')}}
-              .text-xs-center.py-2(v-if='this.pageTotal > 1')
-                v-pagination(v-model='pagination', :length='pageTotal', color='teal')
-              .d-flex.mt-3
-                v-toolbar.radius-7(flat, :color='$vuetify.theme.dark ? `grey darken-2` : `grey lighten-4`', dense, height='44')
-                  .body-2(:class='$vuetify.theme.dark ? `grey--text text--lighten-1` : `grey--text text--darken-1`') {{$t('editor:assets.fileCount', { count: assets.length })}}
-                v-btn.ml-3.mr-0.my-0.radius-7(color='red darken-2', large, @click='cancel', dark)
-                  v-icon(left) mdi-close
-                  span {{$t('common:actions.cancel')}}
-                v-btn.ml-3.mr-0.my-0.radius-7(color='teal', large, @click='insert', :disabled='!currentFileId', :dark='currentFileId !== null')
-                  v-icon(left) mdi-playlist-plus
-                  span {{$t('common:actions.insert')}}
-
-        v-flex(xs12, lg3)
-          v-card.radius-7.animated.fadeInRight.wait-p3s(:light='!$vuetify.theme.dark', :dark='$vuetify.theme.dark')
-            v-card-text
-              .d-flex
-                v-toolbar.radius-7(:color='$vuetify.theme.dark ? `teal` : `teal lighten-5`', dense, flat, height='44')
-                  v-icon.mr-3(:color='$vuetify.theme.dark ? `white` : `teal`') mdi-cloud-upload
-                  .body-2(:class='$vuetify.theme.dark ? `white--text` : `teal--text`') {{$t('editor:assets.uploadAssets')}}
-                v-btn.my-0.ml-3.mr-0.radius-7(outlined, large, color='teal', @click='browse', v-if='$vuetify.breakpoint.mdAndUp')
-                  v-icon(left) mdi-plus-box-multiple
-                  span(:class='$vuetify.theme.dark ? `teal--text text--lighten-3` : ``') {{$t('common:actions.browse')}}
-              file-pond.mt-3(
-                name='mediaUpload'
-                ref='pond'
-                :label-idle='$t(`editor:assets.uploadAssetsDropZone`)'
-                allow-multiple='true'
-                :files='files'
-                max-files='10'
-                :server='filePondServerOpts'
-                :instant-upload='false'
-                :allow-revert='false'
-                @processfile='onFileProcessed'
-              )
-            v-divider
-            v-card-actions.pa-3
-              .caption.grey--text.text-darken-2 Max 10 files, 5 MB each
-              v-spacer
-              v-btn.px-4(color='teal', dark, @click='upload') {{$t('common:actions.upload')}}
-
-          v-card.mt-3.radius-7.animated.fadeInRight.wait-p4s(:light='!$vuetify.theme.dark', :dark='$vuetify.theme.dark')
-            v-card-text.pb-0
-              v-toolbar.radius-7(:color='$vuetify.theme.dark ? `teal` : `teal lighten-5`', dense, flat)
-                v-icon.mr-3(:color='$vuetify.theme.dark ? `white` : `teal`') mdi-cloud-download
-                .body-2(:class='$vuetify.theme.dark ? `white--text` : `teal--text`') {{$t('editor:assets.fetchImage')}}
-                v-spacer
-                v-chip(label, color='white', small).teal--text coming soon
-              v-text-field.mt-3(
-                v-model='remoteImageUrl'
-                outlined
-                color='teal'
-                single-line
-                placeholder='https://example.com/image.jpg'
-              )
-            v-divider
-            v-card-actions.pa-3
-              .caption.grey--text.text-darken-2 Max 5 MB
-              v-spacer
-              v-btn.px-4(color='teal', disabled) {{$t('common:actions.fetch')}}
-
-          v-card.mt-3.radius-7.animated.fadeInRight.wait-p4s(:light='!$vuetify.theme.dark', :dark='$vuetify.theme.dark')
-            v-card-text.pb-0
-              v-toolbar.radius-7(:color='$vuetify.theme.dark ? `teal` : `teal lighten-5`', dense, flat)
-                v-icon.mr-3(:color='$vuetify.theme.dark ? `white` : `teal`') mdi-format-align-top
-                .body-2(:class='$vuetify.theme.dark ? `white--text` : `teal--text`') {{$t('editor:assets.imageAlign')}}
-              v-select.mt-3(
-                v-model='imageAlignment'
-                :items='imageAlignments'
-                outlined
-                single-line
-                color='teal'
-                placeholder='None'
-              )
-
-    //- RENAME DIALOG
-
-    v-dialog(v-model='renameDialog', max-width='550', persistent)
-      v-card
-        .dialog-header.is-short.is-orange
-          v-icon.mr-2(color='white') mdi-keyboard
-          span {{$t('editor:assets.renameAsset')}}
-        v-card-text.pt-5
-          .body-2 {{$t('editor:assets.renameAssetSubtitle')}}
-          v-text-field(
-            outlined
-            single-line
-            :counter='255'
-            v-model='renameAssetName'
-            @keyup.enter='renameAsset'
-            :disabled='renameAssetLoading'
-          )
-        v-card-chin
-          v-spacer
-          v-btn(text, @click='renameDialog = false', :disabled='renameAssetLoading') {{$t('common:actions.cancel')}}
-          v-btn.px-3(color='orange darken-3', @click='renameAsset', :loading='renameAssetLoading').white--text {{$t('common:actions.rename')}}
-
-    //- DELETE DIALOG
-
-    v-dialog(v-model='deleteDialog', max-width='550', persistent)
-      v-card
-        .dialog-header.is-short.is-red
-          v-icon.mr-2(color='white') mdi-trash-can-outline
-          span {{$t('editor:assets.deleteAsset')}}
-        v-card-text.pt-5
-          .body-2 {{$t('editor:assets.deleteAssetConfirm')}}
-          .body-2.red--text.text--darken-2 {{currentAsset.filename}}?
-          .caption.mt-3 {{$t('editor:assets.deleteAssetWarn')}}
-        v-card-chin
-          v-spacer
-          v-btn(text, @click='deleteDialog = false', :disabled='deleteAssetLoading') {{$t('common:actions.cancel')}}
-          v-btn.px-3(color='red darken-2', @click='deleteAsset', :loading='deleteAssetLoading').white--text {{$t('common:actions.delete')}}
-</template>
-
-<script>
-import _ from 'lodash'
-import { get, sync } from 'vuex-pathify'
-import Cookies from 'js-cookie'
-import vueFilePond from 'vue-filepond'
-import 'filepond/dist/filepond.min.css'
-
-import listAssetQuery from 'gql/editor/editor-media-query-list.gql'
-import listFolderAssetQuery from 'gql/editor/editor-media-query-folder-list.gql'
-import createAssetFolderMutation from 'gql/editor/editor-media-mutation-folder-create.gql'
-import renameAssetMutation from 'gql/editor/editor-media-mutation-asset-rename.gql'
-import deleteAssetMutation from 'gql/editor/editor-media-mutation-asset-delete.gql'
-
-const FilePond = vueFilePond()
-const localeSegmentRegex = /^[A-Z]{2}(-[A-Z]{2})?$/i
-const disallowedFolderChars = /[A-Z()=.!@#$%?&*+`~<>,;:\\/[\]¬{| ]/
-
-export default {
-  components: {
-    FilePond
-  },
-  props: {
-    value: {
-      type: Boolean,
-      default: false
-    }
-  },
-  data() {
-    return {
-      folders: [],
-      files: [],
-      assets: [],
-      pagination: 1,
-      remoteImageUrl: '',
-      imageAlignments: [
-        { text: 'None', value: '' },
-        { text: 'Left', value: 'left' },
-        { text: 'Centered', value: 'center' },
-        { text: 'Right', value: 'right' },
-        { text: 'Absolute Top Right', value: 'abstopright' }
-      ],
-      imageAlignment: '',
-      loading: false,
-      newFolderDialog: false,
-      newFolderName: '',
-      newFolderLoading: false,
-      previewDialog: false,
-      renameDialog: false,
-      renameAssetName: '',
-      renameAssetLoading: false,
-      deleteDialog: false,
-      deleteAssetLoading: false
-    }
-  },
-  computed: {
-    isShown: {
-      get() { return this.value },
-      set(val) { this.$emit('input', val) }
-    },
-    editorKey: get('editor/editorKey'),
-    activeModal: sync('editor/activeModal'),
-    folderTree: get('editor/media@folderTree'),
-    currentFolderId: sync('editor/media@currentFolderId'),
-    currentFileId: sync('editor/media@currentFileId'),
-    pageTotal () {
-      if (!this.assets) {
-        return 0
-      }
-
-      return Math.ceil(this.assets.length / 15)
-    },
-    headers() {
-      return _.compact([
-        this.$vuetify.breakpoint.smAndUp && { text: this.$t('editor:assets.headerId'), value: 'id', width: 80 },
-        { text: this.$t('editor:assets.headerFilename'), value: 'filename' },
-        this.$vuetify.breakpoint.lgAndUp && { text: this.$t('editor:assets.headerType'), value: 'ext', width: 90 },
-        this.$vuetify.breakpoint.mdAndUp && { text: this.$t('editor:assets.headerFileSize'), value: 'fileSize', width: 110 },
-        this.$vuetify.breakpoint.mdAndUp && { text: this.$t('editor:assets.headerAdded'), value: 'createdAt', width: 175 },
-        this.$vuetify.breakpoint.smAndUp && { text: this.$t('editor:assets.headerActions'), value: '', width: 80, sortable: false, align: 'right' }
-      ])
-    },
-    isFolderNameValid() {
-      return this.newFolderName.length > 1 && !localeSegmentRegex.test(this.newFolderName) && !disallowedFolderChars.test(this.newFolderName)
-    },
-    currentAsset () {
-      return _.find(this.assets, ['id', this.currentFileId]) || {}
-    },
-    filePondServerOpts () {
-      const jwtToken = Cookies.get('jwt')
-      return {
-        process: {
-          url: '/u',
-          headers: {
-            'Authorization': `Bearer ${jwtToken}`
-          }
-        }
-      }
-    }
-  },
-  watch: {
-    newFolderDialog(newValue, oldValue) {
-      if (newValue) {
-        this.$nextTick(() => {
-          this.$refs.folderNameIpt.focus()
-        })
-      }
-    }
-  },
-  filters: {
-    prettyBytes(num) {
-      if (typeof num !== 'number' || isNaN(num)) {
-        throw new TypeError('Expected a number')
-      }
-
-      let exponent
-      let unit
-      let neg = num < 0
-      let units = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
-
-      if (neg) {
-        num = -num
-      }
-      if (num < 1) {
-        return (neg ? '-' : '') + num + ' B'
-      }
-      exponent = Math.min(Math.floor(Math.log(num) / Math.log(1000)), units.length - 1)
-      num = (num / Math.pow(1000, exponent)).toFixed(2) * 1
-      unit = units[exponent]
-
-      return (neg ? '-' : '') + num + ' ' + unit
-    }
-  },
-  methods: {
-    async refresh() {
-      await this.$apollo.queries.assets.refetch()
-      this.$store.commit('showNotification', {
-        message: this.$t('editor:assets.refreshSuccess'),
-        style: 'success',
-        icon: 'check'
-      })
-    },
-    insert () {
-      const asset = _.find(this.assets, ['id', this.currentFileId])
-      const assetPath = this.folderTree.map(f => f.slug).join('/')
-      this.$root.$emit('editorInsert', {
-        kind: asset.kind,
-        path: this.currentFolderId > 0 ? `/${assetPath}/${asset.filename}` : `/${asset.filename}`,
-        text: asset.filename,
-        align: this.imageAlignment
-      })
-      this.activeModal = ''
-    },
-    browse () {
-      this.$refs.pond.browse()
-    },
-    async upload () {
-      const files = this.$refs.pond.getFiles()
-      if (files.length < 1) {
-        return this.$store.commit('showNotification', {
-          message: this.$t('editor:assets.noUploadError'),
-          style: 'warning',
-          icon: 'warning'
-        })
-      }
-      for (let file of files) {
-        file.setMetadata({
-          folderId: this.currentFolderId
-        })
-      }
-      await this.$refs.pond.processFiles()
-    },
-    async onFileProcessed (err, file) {
-      if (err) {
-        return this.$store.commit('showNotification', {
-          message: this.$t('editor:assets.uploadFailed'),
-          style: 'error',
-          icon: 'error'
-        })
-      }
-      _.delay(() => {
-        this.$refs.pond.removeFile(file.id)
-      }, 5000)
-
-      await this.$apollo.queries.assets.refetch()
-    },
-    downFolder(folder) {
-      this.$store.commit('editor/pushMediaFolderTree', folder)
-      this.currentFolderId = folder.id
-      this.currentFileId = null
-    },
-    upFolder() {
-      this.$store.commit('editor/popMediaFolderTree')
-      const parentFolder = _.last(this.folderTree)
-      this.currentFolderId = parentFolder ? parentFolder.id : 0
-      this.currentFileId = null
-    },
-    async createFolder() {
-      this.$store.commit(`loadingStart`, 'editor-media-createfolder')
-      this.newFolderLoading = true
-      try {
-        const resp = await this.$apollo.mutate({
-          mutation: createAssetFolderMutation,
-          variables: {
-            parentFolderId: this.currentFolderId,
-            slug: this.newFolderName
-          }
-        })
-        if (_.get(resp, 'data.assets.createFolder.responseResult.succeeded', false)) {
-          await this.$apollo.queries.folders.refetch()
-          this.$store.commit('showNotification', {
-            message: this.$t('editor:assets.folderCreateSuccess'),
-            style: 'success',
-            icon: 'check'
-          })
-          this.newFolderDialog = false
-          this.newFolderName = ''
-        } else {
-          this.$store.commit('pushGraphError', new Error(_.get(resp, 'data.assets.createFolder.responseResult.message')))
-        }
-      } catch (err) {
-        this.$store.commit('pushGraphError', err)
-      }
-      this.newFolderLoading = false
-      this.$store.commit(`loadingStop`, 'editor-media-createfolder')
-    },
-    openRenameDialog() {
-      this.renameAssetName = this.currentAsset.filename
-      this.renameDialog = true
-    },
-    async renameAsset() {
-      this.$store.commit(`loadingStart`, 'editor-media-renameasset')
-      this.renameAssetLoading = true
-      try {
-        const resp = await this.$apollo.mutate({
-          mutation: renameAssetMutation,
-          variables: {
-            id: this.currentFileId,
-            filename: this.renameAssetName
-          }
-        })
-        if (_.get(resp, 'data.assets.renameAsset.responseResult.succeeded', false)) {
-          await this.$apollo.queries.assets.refetch()
-          this.$store.commit('showNotification', {
-            message: this.$t('editor:assets.renameSuccess'),
-            style: 'success',
-            icon: 'check'
-          })
-          this.renameDialog = false
-          this.renameAssetName = ''
-        } else {
-          this.$store.commit('pushGraphError', new Error(_.get(resp, 'data.assets.renameAsset.responseResult.message')))
-        }
-      } catch (err) {
-        this.$store.commit('pushGraphError', err)
-      }
-      this.renameAssetLoading = false
-      this.$store.commit(`loadingStop`, 'editor-media-renameasset')
-    },
-    async deleteAsset() {
-      this.$store.commit(`loadingStart`, 'editor-media-deleteasset')
-      this.deleteAssetLoading = true
-      try {
-        const resp = await this.$apollo.mutate({
-          mutation: deleteAssetMutation,
-          variables: {
-            id: this.currentFileId
-          }
-        })
-        if (_.get(resp, 'data.assets.deleteAsset.responseResult.succeeded', false)) {
-          this.currentFileId = null
-          await this.$apollo.queries.assets.refetch()
-          this.$store.commit('showNotification', {
-            message: this.$t('editor:assets.deleteSuccess'),
-            style: 'success',
-            icon: 'check'
-          })
-          this.deleteDialog = false
-        } else {
-          this.$store.commit('pushGraphError', new Error(_.get(resp, 'data.assets.deleteAsset.responseResult.message')))
-        }
-      } catch (err) {
-        this.$store.commit('pushGraphError', err)
-      }
-      this.deleteAssetLoading = false
-      this.$store.commit(`loadingStop`, 'editor-media-deleteasset')
-    },
-    cancel () {
-      this.activeModal = ''
-    }
-  },
-  apollo: {
-    folders: {
-      query: listFolderAssetQuery,
-      variables() {
-        return {
-          parentFolderId: this.currentFolderId
-        }
-      },
-      fetchPolicy: 'network-only',
-      update: (data) => data.assets.folders,
-      watchLoading (isLoading) {
-        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'editor-media-folders-list-refresh')
-      }
-    },
-    assets: {
-      query: listAssetQuery,
-      variables() {
-        return {
-          folderId: this.currentFolderId,
-          kind: 'ALL'
-        }
-      },
-      throttle: 1000,
-      fetchPolicy: 'network-only',
-      update: (data) => data.assets.list,
-      watchLoading (isLoading) {
-        this.loading = isLoading
-        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'editor-media-list-refresh')
-      }
-    }
-  }
-}
-</script>
-
-<style lang='scss'>
-.editor-modal-media {
-  position: fixed !important;
-  top: 112px;
-  left: 64px;
-  z-index: 10;
-  width: calc(100vw - 64px - 17px);
-  height: calc(100vh - 112px - 24px);
-  background-color: rgba(darken(mc('grey', '900'), 3%), .9) !important;
-  overflow: auto;
-
-  @include until($tablet) {
-    left: 40px;
-    width: calc(100vw - 40px);
-    height: calc(100vh - 112px - 24px);
-  }
-
-  &.is-editor-ckeditor {
-    top: 64px;
-    left: 0;
-    width: 100%;
-    height: calc(100vh - 64px - 26px);
-
-    @include until($tablet) {
-      top: 56px;
-      left: 0;
-      width: 100%;
-      height: calc(100vh - 56px - 24px);
-    }
-  }
-
-  &.is-editor-code {
-    top: 64px;
-    height: calc(100vh - 64px - 26px);
-
-    @include until($tablet) {
-      top: 56px;
-      height: calc(100vh - 56px - 24px);
-    }
-  }
-
-  &.is-editor-common {
-    top: 64px;
-    left: 0;
-    width: 100%;
-    height: calc(100vh - 64px);
-
-    @include until($tablet) {
-      top: 56px;
-      left: 0;
-      width: 100%;
-      height: calc(100vh - 56px);
-    }
-  }
-
-  .filepond--root {
-    margin-bottom: 0;
-  }
-
-  .filepond--drop-label {
-    cursor: pointer;
-
-    > label {
-      cursor: pointer;
-    }
-  }
-
-  .filepond--file-action-button.filepond--action-process-item {
-    display: none;
-  }
-
-  .v-btn--icon {
-    padding: 0 20px;
-  }
-}
-</style>

+ 0 - 447
client/components/editor/editor-modal-properties.vue

@@ -1,447 +0,0 @@
-<template lang='pug'>
-  v-dialog(
-    v-model='isShown'
-    persistent
-    width='1000'
-    :fullscreen='$vuetify.breakpoint.smAndDown'
-    )
-    .dialog-header
-      v-icon(color='white') mdi-tag-text-outline
-      .subtitle-1.white--text.ml-3 {{$t('editor:props.pageProperties')}}
-      v-spacer
-      v-btn.mx-0(
-        outlined
-        dark
-        @click.native='close'
-        )
-        v-icon(left) mdi-check
-        span {{ $t('common:actions.ok') }}
-    v-card(tile)
-      v-tabs(color='white', background-color='blue darken-1', dark, centered, v-model='currentTab')
-        v-tab {{$t('editor:props.info')}}
-        v-tab {{$t('editor:props.scheduling')}}
-        v-tab(:disabled='!hasScriptPermission') {{$t('editor:props.scripts')}}
-        v-tab(disabled) {{$t('editor:props.social')}}
-        v-tab(:disabled='!hasStylePermission') {{$t('editor:props.styles')}}
-        v-tab-item(transition='fade-transition', reverse-transition='fade-transition')
-          v-card-text.pt-5
-            .overline.pb-5 {{$t('editor:props.pageInfo')}}
-            v-text-field(
-              ref='iptTitle'
-              outlined
-              :label='$t(`editor:props.title`)'
-              counter='255'
-              v-model='title'
-              )
-            v-text-field(
-              outlined
-              :label='$t(`editor:props.shortDescription`)'
-              counter='255'
-              v-model='description'
-              persistent-hint
-              :hint='$t(`editor:props.shortDescriptionHint`)'
-              )
-          v-divider
-          v-card-text.grey.pt-5(:class='$vuetify.theme.dark ? `darken-3-d3` : `lighten-5`')
-            .overline.pb-5 {{$t('editor:props.path')}}
-            v-container.pa-0(fluid, grid-list-lg)
-              v-layout(row, wrap)
-                v-flex(xs12, md2)
-                  v-select(
-                    outlined
-                    :label='$t(`editor:props.locale`)'
-                    suffix='/'
-                    :items='namespaces'
-                    v-model='locale'
-                    hide-details
-                  )
-                v-flex(xs12, md10)
-                  v-text-field(
-                    outlined
-                    :label='$t(`editor:props.path`)'
-                    append-icon='mdi-folder-search'
-                    v-model='path'
-                    :hint='$t(`editor:props.pathHint`)'
-                    persistent-hint
-                    @click:append='showPathSelector'
-                    :rules='[rules.required, rules.path]'
-                    )
-          v-divider
-          v-card-text.grey.pt-5(:class='$vuetify.theme.dark ? `darken-3-d5` : `lighten-4`')
-            .overline.pb-5 {{$t('editor:props.categorization')}}
-            v-chip-group.radius-5.mb-5(column, v-if='tags && tags.length > 0')
-              v-chip(
-                v-for='tag of tags'
-                :key='`tag-` + tag'
-                close
-                label
-                color='teal'
-                text-color='teal lighten-5'
-                @click:close='removeTag(tag)'
-                ) {{tag}}
-            v-combobox(
-              :label='$t(`editor:props.tags`)'
-              outlined
-              v-model='newTag'
-              :hint='$t(`editor:props.tagsHint`)'
-              :items='newTagSuggestions'
-              :loading='$apollo.queries.newTagSuggestions.loading'
-              persistent-hint
-              hide-no-data
-              :search-input.sync='newTagSearch'
-              )
-        v-tab-item(transition='fade-transition', reverse-transition='fade-transition')
-          v-card-text
-            .overline {{$t('editor:props.publishState')}}
-            v-switch(
-              :label='$t(`editor:props.publishToggle`)'
-              v-model='isPublished'
-              color='primary'
-              :hint='$t(`editor:props.publishToggleHint`)'
-              persistent-hint
-              inset
-              )
-          v-divider
-          v-card-text.grey.pt-5(:class='$vuetify.theme.dark ? `darken-3-d3` : `lighten-5`')
-            v-container.pa-0(fluid, grid-list-lg)
-              v-row
-                v-col(cols='6')
-                  v-dialog(
-                    ref='menuPublishStart'
-                    :close-on-content-click='false'
-                    v-model='isPublishStartShown'
-                    :return-value.sync='publishStartDate'
-                    width='460px'
-                    :disabled='!isPublished'
-                    )
-                    template(v-slot:activator='{ on }')
-                      v-text-field(
-                        v-on='on'
-                        :label='$t(`editor:props.publishStart`)'
-                        v-model='publishStartDate'
-                        prepend-icon='mdi-calendar-check'
-                        readonly
-                        outlined
-                        clearable
-                        :hint='$t(`editor:props.publishStartHint`)'
-                        persistent-hint
-                        :disabled='!isPublished'
-                        )
-                    v-date-picker(
-                      v-model='publishStartDate'
-                      :min='(new Date()).toISOString().substring(0, 10)'
-                      color='primary'
-                      reactive
-                      scrollable
-                      landscape
-                      )
-                      v-spacer
-                      v-btn(
-                        text
-                        color='primary'
-                        @click='isPublishStartShown = false'
-                        ) {{$t('common:actions.cancel')}}
-                      v-btn(
-                        text
-                        color='primary'
-                        @click='$refs.menuPublishStart.save(publishStartDate)'
-                        ) {{$t('common:actions.ok')}}
-                v-col(cols='6')
-                  v-dialog(
-                    ref='menuPublishEnd'
-                    :close-on-content-click='false'
-                    v-model='isPublishEndShown'
-                    :return-value.sync='publishEndDate'
-                    width='460px'
-                    :disabled='!isPublished'
-                    )
-                    template(v-slot:activator='{ on }')
-                      v-text-field(
-                        v-on='on'
-                        :label='$t(`editor:props.publishEnd`)'
-                        v-model='publishEndDate'
-                        prepend-icon='mdi-calendar-remove'
-                        readonly
-                        outlined
-                        clearable
-                        :hint='$t(`editor:props.publishEndHint`)'
-                        persistent-hint
-                        :disabled='!isPublished'
-                        )
-                    v-date-picker(
-                      v-model='publishEndDate'
-                      :min='(new Date()).toISOString().substring(0, 10)'
-                      color='primary'
-                      reactive
-                      scrollable
-                      landscape
-                      )
-                      v-spacer
-                      v-btn(
-                        text
-                        color='primary'
-                        @click='isPublishEndShown = false'
-                        ) {{$t('common:actions.cancel')}}
-                      v-btn(
-                        text
-                        color='primary'
-                        @click='$refs.menuPublishEnd.save(publishEndDate)'
-                        ) {{$t('common:actions.ok')}}
-
-        v-tab-item(:transition='false', :reverse-transition='false')
-          .editor-props-codeeditor-title
-            .overline {{$t('editor:props.html')}}
-          .editor-props-codeeditor
-            textarea(ref='codejs')
-          .editor-props-codeeditor-hint
-            .caption {{$t('editor:props.htmlHint')}}
-
-        v-tab-item(transition='fade-transition', reverse-transition='fade-transition')
-          v-card-text
-            .overline {{$t('editor:props.socialFeatures')}}
-            v-switch(
-              :label='$t(`editor:props.allowComments`)'
-              v-model='isPublished'
-              color='primary'
-              :hint='$t(`editor:props.allowCommentsHint`)'
-              persistent-hint
-              inset
-              )
-            v-switch(
-              :label='$t(`editor:props.allowRatings`)'
-              v-model='isPublished'
-              color='primary'
-              :hint='$t(`editor:props.allowRatingsHint`)'
-              persistent-hint
-              disabled
-              inset
-              )
-            v-switch(
-              :label='$t(`editor:props.displayAuthor`)'
-              v-model='isPublished'
-              color='primary'
-              :hint='$t(`editor:props.displayAuthorHint`)'
-              persistent-hint
-              inset
-              )
-            v-switch(
-              :label='$t(`editor:props.displaySharingBar`)'
-              v-model='isPublished'
-              color='primary'
-              :hint='$t(`editor:props.displaySharingBarHint`)'
-              persistent-hint
-              inset
-              )
-
-        v-tab-item(:transition='false', :reverse-transition='false')
-          .editor-props-codeeditor-title
-            .overline {{$t('editor:props.css')}}
-          .editor-props-codeeditor
-            textarea(ref='codecss')
-          .editor-props-codeeditor-hint
-            .caption {{$t('editor:props.cssHint')}}
-
-    page-selector(:mode='pageSelectorMode', v-model='pageSelectorShown', :path='path', :locale='locale', :open-handler='setPath')
-</template>
-
-<script>
-import _ from 'lodash'
-import { sync, get } from 'vuex-pathify'
-import gql from 'graphql-tag'
-
-import CodeMirror from 'codemirror'
-import 'codemirror/lib/codemirror.css'
-import 'codemirror/mode/htmlmixed/htmlmixed.js'
-import 'codemirror/mode/css/css.js'
-
-/* global siteLangs, siteConfig */
-const filenamePattern = /^(?![\#\/\.\$\^\=\*\;\:\&\?\(\)\[\]\{\}\"\'\>\<\,\@\!\%\`\~\s])(?!.*[\#\/\.\$\^\=\*\;\:\&\?\(\)\[\]\{\}\"\'\>\<\,\@\!\%\`\~\s]$)[^\#\.\$\^\=\*\;\:\&\?\(\)\[\]\{\}\"\'\>\<\,\@\!\%\`\~\s]*$/
-
-export default {
-  props: {
-    value: {
-      type: Boolean,
-      default: false
-    }
-  },
-  data () {
-    return {
-      isPublishStartShown: false,
-      isPublishEndShown: false,
-      pageSelectorShown: false,
-      namespaces: siteLangs.length ? siteLangs.map(ns => ns.code) : [siteConfig.lang],
-      newTag: '',
-      newTagSuggestions: [],
-      newTagSearch: '',
-      currentTab: 0,
-      cm: null,
-      rules: {
-          required: value => !!value || 'This field is required.',
-          path: value => {
-            return filenamePattern.test(value) || 'Invalid path. Please ensure it does not contain special characters, or begin/end in a slash or hashtag string.'
-          }
-      }
-    }
-  },
-  computed: {
-    isShown: {
-      get() { return this.value },
-      set(val) { this.$emit('input', val) }
-    },
-    mode: get('editor/mode'),
-    title: sync('page/title'),
-    description: sync('page/description'),
-    locale: sync('page/locale'),
-    tags: sync('page/tags'),
-    path: sync('page/path'),
-    isPublished: sync('page/isPublished'),
-    publishStartDate: sync('page/publishStartDate'),
-    publishEndDate: sync('page/publishEndDate'),
-    scriptJs: sync('page/scriptJs'),
-    scriptCss: sync('page/scriptCss'),
-    hasScriptPermission: get('page/effectivePermissions@pages.script'),
-    hasStylePermission: get('page/effectivePermissions@pages.style'),
-    pageSelectorMode () {
-      return (this.mode === 'create') ? 'create' : 'move'
-    }
-  },
-  watch: {
-    value (newValue, oldValue) {
-      if (newValue) {
-        _.delay(() => {
-          this.$refs.iptTitle.focus()
-        }, 500)
-      }
-    },
-    newTag (newValue, oldValue) {
-      const tagClean = _.trim(newValue || '').toLowerCase()
-      if (tagClean && tagClean.length > 0) {
-        if (!_.includes(this.tags, tagClean)) {
-          this.tags = [...this.tags, tagClean]
-        }
-        this.$nextTick(() => {
-          this.newTag = null
-        })
-      }
-    },
-    currentTab (newValue, oldValue) {
-      if (this.cm) {
-        this.cm.toTextArea()
-      }
-      if (newValue === 2) {
-        this.$nextTick(() => {
-          setTimeout(() => {
-            this.loadEditor(this.$refs.codejs, 'html')
-          }, 100)
-        })
-      } else if (newValue === 4) {
-        this.$nextTick(() => {
-          setTimeout(() => {
-            this.loadEditor(this.$refs.codecss, 'css')
-          }, 100)
-        })
-      }
-    }
-  },
-  methods: {
-    removeTag (tag) {
-      this.tags = _.without(this.tags, tag)
-    },
-    close() {
-      this.isShown = false
-    },
-    showPathSelector() {
-      this.pageSelectorShown = true
-    },
-    setPath({ path, locale }) {
-      this.locale = locale
-      this.path = path
-    },
-    loadEditor(ref, mode) {
-      this.cm = CodeMirror.fromTextArea(ref, {
-        tabSize: 2,
-        mode: `text/${mode}`,
-        theme: 'wikijs-dark',
-        lineNumbers: true,
-        lineWrapping: true,
-        line: true,
-        styleActiveLine: true,
-        viewportMargin: 50,
-        inputStyle: 'contenteditable',
-        direction: 'ltr'
-      })
-      switch (mode) {
-        case 'html':
-          this.cm.setValue(this.scriptJs)
-          this.cm.on('change', c => {
-            this.scriptJs = c.getValue()
-          })
-          break
-        case 'css':
-          this.cm.setValue(this.scriptCss)
-          this.cm.on('change', c => {
-            this.scriptCss = c.getValue()
-          })
-          break
-        default:
-          console.warn('Invalid Editor Mode')
-          break
-      }
-      this.cm.setSize(null, '500px')
-      this.$nextTick(() => {
-        this.cm.refresh()
-        this.cm.focus()
-      })
-    }
-  },
-  apollo: {
-    newTagSuggestions: {
-      query: gql`
-        query ($query: String!) {
-          pages {
-            searchTags (query: $query)
-          }
-        }
-      `,
-      variables () {
-        return {
-          query: this.newTagSearch
-        }
-      },
-      fetchPolicy: 'cache-first',
-      update: (data) => _.get(data, 'pages.searchTags', []),
-      skip () {
-        return !this.value || _.isEmpty(this.newTagSearch)
-      },
-      throttle: 500
-    }
-  }
-}
-</script>
-
-<style lang='scss'>
-
-.editor-props-codeeditor {
-  background-color: mc('grey', '900');
-  min-height: 500px;
-
-  > textarea {
-    visibility: hidden;
-  }
-
-  &-title {
-    background-color: mc('grey', '900');
-    border-bottom: 1px solid lighten(mc('grey', '900'), 10%);
-    color: #FFF;
-    padding: 10px;
-  }
-
-  &-hint {
-    background-color: mc('grey', '900');
-    border-top: 1px solid lighten(mc('grey', '900'), 5%);
-    color: mc('grey', '500');
-    padding: 5px 10px;
-  }
-}
-
-</style>

+ 0 - 40
client/components/editor/editor-modal-unsaved.vue

@@ -1,40 +0,0 @@
-<template lang="pug">
-  v-dialog(v-model='isShown', max-width='550')
-    v-card
-      .dialog-header.is-short.is-red
-        v-icon.mr-2(color='white') mdi-alert
-        span {{$t('editor:unsaved.title')}}
-      v-card-text.pt-4
-        .body-2 {{$t('editor:unsaved.body')}}
-      v-card-chin
-        v-spacer
-        v-btn(text, @click='isShown = false') {{$t('common:actions.cancel')}}
-        v-btn.px-4(color='red', @click='discard', dark) {{$t('common:actions.discardChanges')}}
-</template>
-
-<script>
-
-export default {
-  props: {
-    value: {
-      type: Boolean,
-      default: false
-    }
-  },
-  data() {
-    return { }
-  },
-  computed: {
-    isShown: {
-      get() { return this.value },
-      set(val) { this.$emit('input', val) }
-    }
-  },
-  methods: {
-    async discard() {
-      this.isShown = false
-      this.$emit('discard', true)
-    }
-  }
-}
-</script>

+ 0 - 224
client/components/editor/editor-redirect.vue

@@ -1,224 +0,0 @@
-<template lang='pug'>
-  .editor-redirect
-    .editor-redirect-main
-      .editor-redirect-editor
-        v-container.px-2.pt-1(fluid)
-          v-row(dense)
-            v-col(
-              cols='12'
-              lg='8'
-              offset-lg='2'
-              xl='6'
-              offset-xl='3'
-              )
-              v-card.pt-2
-                v-card-text
-                  .pb-1
-                    .subtitle-2.primary--text When a user reaches this page
-                    .caption.grey--text.text--darken-1 and matches one of these rules...
-                  v-timeline(dense)
-                    v-slide-x-reverse-transition(group, hide-on-leave)
-                      v-timeline-item(
-                        key='cond-add-new'
-                        hide-dot
-                        )
-                        v-btn(
-                          color='primary'
-                          @click=''
-                          )
-                          v-icon(left) mdi-plus
-                          span Add Conditional Rule
-                      v-timeline-item(
-                        key='cond-none'
-                        small
-                        color='grey'
-                        )
-                        v-card.grey.lighten-5(flat)
-                          v-card-text
-                            .body-2: strong No conditional rule
-                            em Add conditional rules to direct users to a different page based on their group.
-                      v-timeline-item(
-                        key='cond-rule-1'
-                        small
-                        color='primary'
-                        )
-                        v-card.blue-grey.lighten-5(flat)
-                          v-card-text
-                            .d-flex.align-center
-                              .body-2: strong User is a member of any of these groups:
-                              v-select.ml-3(
-                                color='primary'
-                                :items='groups'
-                                item-text='name'
-                                item-value='id'
-                                multiple
-                                solo
-                                flat
-                                hide-details
-                                dense
-                                chips
-                                small-chips
-                                )
-                            v-divider.my-3
-                            .d-flex.align-center
-                              .body-2.mr-3 then redirect to
-                              v-btn-toggle.mr-3(
-                                v-model='fallbackMode'
-                                mandatory
-                                color='primary'
-                                borderless
-                                dense
-                                )
-                                v-btn.text-none(value='page') Page
-                                v-btn.text-none(value='url') External URL
-                              v-btn.mr-3(
-                                v-if='fallbackMode === `page`'
-                                color='primary'
-                                )
-                                v-icon(left) mdi-magnify
-                                span Select Page...
-                              v-text-field(
-                                v-if='fallbackMode === `url`'
-                                label='External URL'
-                                outlined
-                                hint='Required - Title of the API'
-                                hide-details
-                                v-model='fallbackUrl'
-                                dense
-                                single-line
-                              )
-                  v-divider.mb-5
-                  .subtitle-2.primary--text Otherwise, redirect to...
-                  .caption.grey--text.text--darken-1.pb-2 This fallback rule is mandatory and used if none of the conditional rules above applies.
-                  .d-flex.align-center
-                    v-btn-toggle.mr-3(
-                      v-model='fallbackMode'
-                      mandatory
-                      color='primary'
-                      borderless
-                      dense
-                      )
-                      v-btn.text-none(value='page') Page
-                      v-btn.text-none(value='url') External URL
-                    v-btn.mr-3(
-                      v-if='fallbackMode === `page`'
-                      color='primary'
-                      )
-                      v-icon(left) mdi-magnify
-                      span Select Page...
-                    v-text-field(
-                      v-if='fallbackMode === `url`'
-                      label='External URL'
-                      outlined
-                      hint='Required - Title of the API'
-                      hide-details
-                      v-model='fallbackUrl'
-                      dense
-                      single-line
-                    )
-
-    v-system-bar.editor-redirect-sysbar(dark, status, color='grey darken-3')
-      .caption.editor-redirect-sysbar-locale {{locale.toUpperCase()}}
-      .caption.px-3 /{{path}}
-      template(v-if='$vuetify.breakpoint.mdAndUp')
-        v-spacer
-        .caption Redirect
-        v-spacer
-        .caption 0 rules
-</template>
-
-<script>
-import _ from 'lodash'
-import gql from 'graphql-tag'
-import { v4 as uuid } from 'uuid'
-import { get, sync } from 'vuex-pathify'
-
-export default {
-  data() {
-    return {
-      fallbackMode: 'page',
-      fallbackUrl: 'https://'
-    }
-  },
-  computed: {
-    isMobile() {
-      return this.$vuetify.breakpoint.smAndDown
-    },
-    locale: get('page/locale'),
-    path: get('page/path'),
-    mode: get('editor/mode'),
-    activeModal: sync('editor/activeModal')
-  },
-  methods: {
-  },
-  mounted() {
-    this.$store.set('editor/editorKey', 'redirect')
-
-    if (this.mode === 'create') {
-      this.$store.set('editor/content', '<h1>Title</h1>\n\n<p>Some text here</p>')
-    }
-  },
-  apollo: {
-    groups: {
-      query: gql`
-        {
-          groups {
-            list {
-              id
-              name
-            }
-          }
-        }
-      `,
-      fetchPolicy: 'network-only',
-      update: (data) => data.groups.list,
-      watchLoading (isLoading) {
-        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'editor-redirect-groups')
-      }
-    }
-  }
-}
-</script>
-
-<style lang='scss'>
-$editor-height: calc(100vh - 64px - 24px);
-$editor-height-mobile: calc(100vh - 56px - 16px);
-
-.editor-redirect {
-  &-main {
-    display: flex;
-    width: 100%;
-  }
-
-  &-editor {
-    background-color: darken(mc('grey', '100'), 4.5%);
-    flex: 1 1 50%;
-    display: block;
-    height: $editor-height;
-    position: relative;
-
-    @at-root .theme--dark & {
-      background-color: darken(mc('grey', '900'), 4.5%);
-    }
-  }
-
-  &-sidebar {
-    width: 200px;
-  }
-
-  &-sysbar {
-    padding-left: 0 !important;
-
-    &-locale {
-      background-color: rgba(255,255,255,.25);
-      display:inline-flex;
-      padding: 0 12px;
-      height: 24px;
-      width: 63px;
-      justify-content: center;
-      align-items: center;
-    }
-  }
-
-}
-</style>

+ 0 - 62
client/components/editor/markdown/fold.js

@@ -1,62 +0,0 @@
-// Header matching code by CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/LICENSE
-
-import CodeMirror from 'codemirror'
-
-const maxDepth = 100
-const codeBlockStartMatch = /^`{3}[a-zA-Z0-9]+$/
-const codeBlockEndMatch = /^`{3}$/
-
-CodeMirror.registerHelper('fold', 'markdown', function (cm, start) {
-  const firstLine = cm.getLine(start.line)
-  const lastLineNo = cm.lastLine()
-  let end
-
-  function isHeader(lineNo) {
-    const tokentype = cm.getTokenTypeAt(CodeMirror.Pos(lineNo, 0))
-    return tokentype && /\bheader\b/.test(tokentype)
-  }
-
-  function headerLevel(lineNo, line, nextLine) {
-    let match = line && line.match(/^#+/)
-    if (match && isHeader(lineNo)) return match[0].length
-    match = nextLine && nextLine.match(/^[=-]+\s*$/)
-    if (match && isHeader(lineNo + 1)) return nextLine[0] === '=' ? 1 : 2
-    return maxDepth
-  }
-
-  // -> CODE BLOCK
-
-  if (codeBlockStartMatch.test(cm.getLine(start.line))) {
-    end = start.line
-    let nextNextLine = cm.getLine(end + 1)
-    while (end < lastLineNo) {
-      if (codeBlockEndMatch.test(nextNextLine)) {
-        end++
-        break
-      }
-      end++
-      nextNextLine = cm.getLine(end + 1)
-    }
-  } else {
-    // -> HEADER
-
-    let nextLine = cm.getLine(start.line + 1)
-    const level = headerLevel(start.line, firstLine, nextLine)
-    if (level === maxDepth) return undefined
-
-    end = start.line
-    let nextNextLine = cm.getLine(end + 2)
-    while (end < lastLineNo) {
-      if (headerLevel(end + 1, nextLine, nextNextLine) <= level) break
-      ++end
-      nextLine = nextNextLine
-      nextNextLine = cm.getLine(end + 2)
-    }
-  }
-
-  return {
-    from: CodeMirror.Pos(start.line, firstLine.length),
-    to: CodeMirror.Pos(end, cm.getLine(end).length)
-  }
-})

+ 0 - 352
client/components/editor/markdown/help.vue

@@ -1,352 +0,0 @@
-<template lang='pug'>
-  v-card.editor-markdown-help.animated.fadeInLeft(flat, tile)
-    v-container.pa-3(grid-list-lg, fluid)
-      v-layout(row, wrap)
-        v-flex(xs12, lg6, xl4)
-          v-card.radius-7.animated.fadeInUp(light)
-            v-card-text
-              .d-flex
-                v-toolbar.radius-7(color='teal lighten-5', dense, flat, height='44')
-                  v-icon.mr-3(color='teal') mdi-information-variant
-                  .body-2.teal--text Markdown Reference
-              .body-2.mt-3 Bold
-              v-layout(row)
-                v-flex(xs6)
-                  v-card.editor-markdown-help-source(flat)
-                    v-card-text
-                      div **Lorem ipsum**
-                v-icon mdi-chevron-right
-                v-flex
-                  v-card.editor-markdown-help-result(flat)
-                    v-card-text
-                      .caption: strong Lorem ipsum
-              .body-2.mt-3 Italic
-              v-layout(row)
-                v-flex(xs6)
-                  v-card.editor-markdown-help-source(flat)
-                    v-card-text
-                      div *Lorem ipsum*
-                v-icon mdi-chevron-right
-                v-flex
-                  v-card.editor-markdown-help-result(flat)
-                    v-card-text
-                      .caption: em Lorem ipsum
-              .body-2.mt-3 Strikethrough
-              v-layout(row)
-                v-flex(xs6)
-                  v-card.editor-markdown-help-source(flat)
-                    v-card-text
-                      div ~~Lorem ipsum~~
-                v-icon mdi-chevron-right
-                v-flex
-                  v-card.editor-markdown-help-result(flat)
-                    v-card-text
-                      .caption(style='text-decoration: line-through;') Lorem ipsum
-              .body-2.mt-3 Headers
-              v-layout(row)
-                v-flex(xs6)
-                  v-card.editor-markdown-help-source(flat)
-                    v-card-text
-                      div # Header 1
-                      div ## Header 2
-                      div ### Header 3
-                      div #### Header 4
-                      div ##### Header 5
-                      div ###### Header 6
-                v-icon mdi-chevron-right
-                v-flex
-                  v-card.editor-markdown-help-result(flat)
-                    v-card-text
-                      div(style='font-weight: 700; font-size: 24px;') Header 1
-                      div(style='font-weight: 700; font-size: 22px;') Header 2
-                      div(style='font-weight: 700; font-size: 20px;') Header 3
-                      div(style='font-weight: 700; font-size: 18px;') Header 4
-                      div(style='font-weight: 700; font-size: 16px;') Header 5
-                      div(style='font-weight: 700; font-size: 14px;') Header 6
-              .body-2.mt-3 Unordered Lists
-              .caption.grey--text.text--darken-1: em You can also use the asterisk symbol instead of the dash.
-              v-layout(row)
-                v-flex(xs6)
-                  v-card.editor-markdown-help-source(flat)
-                    v-card-text
-                      div - Unordered List Item 1
-                      div - Unordered List Item 2
-                      div - Unordered List Item 3
-                v-icon mdi-chevron-right
-                v-flex
-                  v-card.editor-markdown-help-result(flat)
-                    v-card-text
-                      ul
-                        li Unordered List Item 1
-                        li Unordered List Item 2
-                        li Unordered List Item 3
-              .body-2.mt-3 Ordered Lists
-              .caption.grey--text.text--darken-1: em Even though we prefix all lines with #[strong 1.], the output will be correctly numbered automatically.
-              v-layout(row)
-                v-flex(xs6)
-                  v-card.editor-markdown-help-source(flat)
-                    v-card-text
-                      div 1. Ordered List Item 1
-                      div 1. Ordered List Item 2
-                      div 1. Ordered List Item 3
-                v-icon mdi-chevron-right
-                v-flex
-                  v-card.editor-markdown-help-result(flat)
-                    v-card-text
-                      ol
-                        li Unordered List Item 1
-                        li Unordered List Item 2
-                        li Unordered List Item 3
-              .body-2.mt-3 Images
-              v-layout(row)
-                v-flex(xs6)
-                  v-card.editor-markdown-help-source(flat)
-                    v-card-text
-                      div ![Caption Text](/path/to/image.jpg)
-                v-icon mdi-chevron-right
-                v-flex
-                  v-card.editor-markdown-help-result(flat)
-                    v-card-text
-                      img(src='https://via.placeholder.com/150x50.png')
-        v-flex(xs12, lg6, xl4)
-          v-card.radius-7.animated.fadeInUp.wait-p1s(light)
-            v-card-text
-              .d-flex
-                v-toolbar.radius-7(color='teal lighten-5', dense, flat, height='44')
-                  v-icon.mr-3(color='teal') mdi-information-variant
-                  .body-2.teal--text Markdown Reference (continued)
-              .body-2.mt-3 Links
-              v-layout(row)
-                v-flex(xs6)
-                  v-card.editor-markdown-help-source(flat)
-                    v-card-text
-                      div [Link Text](https://wiki.js.org)
-                v-icon mdi-chevron-right
-                v-flex
-                  v-card.editor-markdown-help-result(flat)
-                    v-card-text
-                      .caption: a(href='https://wiki.js.org', target='_blank') Link Text
-              .body-2.mt-3 Superscript
-              v-layout(row)
-                v-flex(xs6)
-                  v-card.editor-markdown-help-source(flat)
-                    v-card-text
-                      div Lorem ^ipsum^
-                v-icon mdi-chevron-right
-                v-flex
-                  v-card.editor-markdown-help-result(flat)
-                    v-card-text
-                      .caption Lorem #[sup ipsum]
-              .body-2.mt-3 Subscript
-              v-layout(row)
-                v-flex(xs6)
-                  v-card.editor-markdown-help-source(flat)
-                    v-card-text
-                      div Lorem ~ipsum~
-                v-icon mdi-chevron-right
-                v-flex
-                  v-card.editor-markdown-help-result(flat)
-                    v-card-text
-                      .caption: em Lorem #[sub ipsum]
-              .body-2.mt-3 Horizontal Line
-              v-layout(row)
-                v-flex(xs6)
-                  v-card.editor-markdown-help-source(flat)
-                    v-card-text
-                      div Lorem ipsum
-                      div ---
-                      div Dolor sit amet
-                v-icon mdi-chevron-right
-                v-flex
-                  v-card.editor-markdown-help-result(flat)
-                    v-card-text
-                      .caption Lorem ipsum
-                      v-divider.my-2
-                      .caption Dolor sit amet
-              .body-2.mt-3 Inline Code
-              v-layout(row)
-                v-flex(xs6)
-                  v-card.editor-markdown-help-source(flat)
-                    v-card-text
-                      div Lorem `ipsum dolor sit` amet
-                v-icon mdi-chevron-right
-                v-flex
-                  v-card.editor-markdown-help-result(flat)
-                    v-card-text
-                      .caption Lorem #[code ipsum dolor sit] amet
-              .body-2.mt-3 Code Blocks
-              .caption.grey--text.text--darken-1: em In the example below, #[strong js] defines the syntax highlighting language to use. It can be omitted.
-              v-layout(row)
-                v-flex(xs6)
-                  v-card.editor-markdown-help-source(flat)
-                    v-card-text
-                      div ```js
-                      div function main () {
-                      div.pl-3 echo 'Lorem ipsum'
-                      div }
-                      div ```
-                v-icon mdi-chevron-right
-                v-flex
-                  v-card.editor-markdown-help-result(flat)
-                    v-card-text.contents
-                      pre.prismjs.line-numbers.language-js
-                        code.language-js
-                          span.token.keyword function
-                          span.token.function  main
-                          span.token.punctuation  (
-                          span.token.punctuation )
-                          span.token.punctuation  {#[br]
-                          |   echo
-                          span.token.string  'Lorem ipsum'#[br]
-                          span.token.punctuation }
-                          span.line-numbers-rows(aria-hidden='true')
-                            span
-                            span
-                            span
-              .body-2.mt-3 Blockquotes
-              v-layout(row)
-                v-flex(xs6)
-                  v-card.editor-markdown-help-source(flat)
-                    v-card-text
-                      div &gt; Lorem ipsum
-                      div &gt; dolor sit amet
-                      div &gt; consectetur adipiscing elit
-                v-icon mdi-chevron-right
-                v-flex
-                  v-card.editor-markdown-help-result(flat)
-                    v-card-text
-                      blockquote(style='border: 1px solid #263238; border-radius: .5rem; padding: 1rem 24px;') Lorem ipsum#[br]dolor sit amet#[br]consectetur adipiscing elit
-
-        v-flex(xs12, xl4)
-          v-card.radius-7.animated.fadeInUp.wait-p2s(light)
-            v-card-text
-              v-toolbar.radius-7(color='teal lighten-5', dense, flat)
-                v-icon.mr-3(color='teal') mdi-keyboard
-                .body-2.teal--text Keyboard Shortcuts
-              v-list.editor-markdown-help-kbd(two-line, dense)
-                v-list-item
-                  v-list-item-content.body-2 Bold
-                  v-list-item-action #[kbd {{ctrlKey}}] + #[kbd B]
-                v-divider
-                v-list-item
-                  v-list-item-content.body-2 Italic
-                  v-list-item-action #[kbd {{ctrlKey}}] + #[kbd I]
-                v-divider
-                v-list-item
-                  v-list-item-content.body-2 Increase Header Level
-                  v-list-item-action #[kbd {{ctrlKey}}] + #[kbd {{altKey}}] + #[kbd Right]
-                v-divider
-                v-list-item
-                  v-list-item-content.body-2 Decrease Header Level
-                  v-list-item-action #[kbd {{ctrlKey}}] + #[kbd {{altKey}}] + #[kbd Left]
-                v-divider
-                v-list-item
-                  v-list-item-content.body-2 Save
-                  v-list-item-action #[kbd {{ctrlKey}}] + #[kbd S]
-                v-divider
-                v-list-item
-                  v-list-item-content.body-2 Undo
-                  v-list-item-action #[kbd {{ctrlKey}}] + #[kbd Z]
-                v-divider
-                v-list-item
-                  v-list-item-content.body-2 Redo
-                  v-list-item-action #[kbd {{ctrlKey}}] + #[kbd Y]
-                v-divider
-                v-list-item
-                  v-list-item-content
-                    v-list-item-title.body-2 Distraction Free Mode
-                    v-list-item-subtitle Press <kbd>Esc</kbd> to exit.
-                  v-list-item-action #[kbd F11]
-
-          v-card.radius-7.animated.fadeInUp.wait-p3s.mt-3(light)
-            v-card-text
-              v-toolbar.radius-7(color='teal lighten-5', dense, flat)
-                v-icon.mr-3(color='teal') mdi-mouse
-                .body-2.teal--text Multi-Selection
-              v-list.editor-markdown-help-kbd(two-line, dense)
-                v-list-item
-                  v-list-item-content.body-2 Multiple Cursors
-                  v-list-item-action #[kbd {{ctrlKey}}] + Left Click
-                v-divider
-                v-list-item
-                  v-list-item-content.body-2 Select Region
-                  v-list-item-action #[kbd {{ctrlKey}}] + #[kbd {{altKey}}] + Left Click
-                v-divider
-                v-list-item
-                  v-list-item-content.body-2 Deselect
-                  v-list-item-action #[kbd Esc]
-</template>
-
-<script>
-export default {
-  computed: {
-    ctrlKey() { return /Mac/.test(navigator.platform) ? 'Cmd' : 'Ctrl' },
-    altKey() { return /Mac/.test(navigator.platform) ? 'Option' : 'Alt' }
-  }
-}
-</script>
-
-<style lang='scss'>
-.editor-markdown-help {
-  position: fixed !important;
-  top: 112px;
-  left: 64px;
-  z-index: 10;
-  width: calc(100vw - 64px - 17px);
-  height: calc(100vh - 112px - 24px);
-  background-color: rgba(darken(mc('grey', '900'), 3%), .9) !important;
-  overflow: auto;
-
-  &-source {
-    background-color: mc('blue-grey', '900') !important;
-    border-radius: 7px;
-    font-family: 'Roboto Mono', monospace;
-    font-size: 14px;
-    color: #FFF !important;
-
-    .v-card__text {
-      color: #FFF !important;
-    }
-  }
-
-  &-result {
-    background-color: mc('blue-grey', '50') !important;
-    border-radius: 7px;
-    font-size: 14px;
-
-    code {
-      display: inline-block;
-      background-color: mc('pink', '50');
-      box-shadow: none;
-      font-size: inherit;
-    }
-
-    .contents {
-      padding-bottom: 16px;
-    }
-
-    .prismjs {
-      margin: 0;
-    }
-  }
-
-  &-kbd {
-    .v-list-item__action {
-      flex-direction: row;
-      align-items: center;
-
-      kbd {
-        display: inline-block;
-        border: 1px solid #ccc;
-        border-radius: 4px;
-        padding: 0.1em 0.5em;
-        margin: 0 0.2em;
-        box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2), 0 0 0 2px #fff inset;
-        background-color: #f7f7f7;
-        color: mc('grey', '700');
-        font-size: 12px;
-      }
-    }
-  }
-}
-</style>

+ 0 - 190
client/components/editor/markdown/plantuml.js

@@ -1,190 +0,0 @@
-const pako = require('pako')
-
-// ------------------------------------
-// Markdown - PlantUML Preprocessor
-// ------------------------------------
-
-module.exports = {
-  init (mdinst, conf) {
-    mdinst.use((md, opts) => {
-      const openMarker = opts.openMarker || '```plantuml'
-      const openChar = openMarker.charCodeAt(0)
-      const closeMarker = opts.closeMarker || '```'
-      const closeChar = closeMarker.charCodeAt(0)
-      const imageFormat = opts.imageFormat || 'svg'
-      const server = opts.server || 'https://plantuml.requarks.io'
-
-      md.block.ruler.before('fence', 'uml_diagram', (state, startLine, endLine, silent) => {
-        let nextLine
-        let markup
-        let params
-        let token
-        let i
-        let autoClosed = false
-        let start = state.bMarks[startLine] + state.tShift[startLine]
-        let max = state.eMarks[startLine]
-
-        // Check out the first character quickly,
-        // this should filter out most of non-uml blocks
-        //
-        if (openChar !== state.src.charCodeAt(start)) { return false }
-
-        // Check out the rest of the marker string
-        //
-        for (i = 0; i < openMarker.length; ++i) {
-          if (openMarker[i] !== state.src[start + i]) { return false }
-        }
-
-        markup = state.src.slice(start, start + i)
-        params = state.src.slice(start + i, max)
-
-        // Since start is found, we can report success here in validation mode
-        //
-        if (silent) { return true }
-
-        // Search for the end of the block
-        //
-        nextLine = startLine
-
-        for (;;) {
-          nextLine++
-          if (nextLine >= endLine) {
-            // unclosed block should be autoclosed by end of document.
-            // also block seems to be autoclosed by end of parent
-            break
-          }
-
-          start = state.bMarks[nextLine] + state.tShift[nextLine]
-          max = state.eMarks[nextLine]
-
-          if (start < max && state.sCount[nextLine] < state.blkIndent) {
-            // non-empty line with negative indent should stop the list:
-            // - ```
-            //  test
-            break
-          }
-
-          if (closeChar !== state.src.charCodeAt(start)) {
-            // didn't find the closing fence
-            continue
-          }
-
-          if (state.sCount[nextLine] > state.sCount[startLine]) {
-            // closing fence should not be indented with respect of opening fence
-            continue
-          }
-
-          var closeMarkerMatched = true
-          for (i = 0; i < closeMarker.length; ++i) {
-            if (closeMarker[i] !== state.src[start + i]) {
-              closeMarkerMatched = false
-              break
-            }
-          }
-
-          if (!closeMarkerMatched) {
-            continue
-          }
-
-          // make sure tail has spaces only
-          if (state.skipSpaces(start + i) < max) {
-            continue
-          }
-
-          // found!
-          autoClosed = true
-          break
-        }
-
-        const contents = state.src
-          .split('\n')
-          .slice(startLine + 1, nextLine)
-          .join('\n')
-
-        // We generate a token list for the alt property, to mimic what the image parser does.
-        let altToken = []
-        // Remove leading space if any.
-        let alt = params ? params.slice(1) : 'uml diagram'
-        state.md.inline.parse(
-          alt,
-          state.md,
-          state.env,
-          altToken
-        )
-
-        var zippedCode = encode64(pako.deflate('@startuml\n' + contents + '\n@enduml', { to: 'string' }))
-
-        token = state.push('uml_diagram', 'img', 0)
-        // alt is constructed from children. No point in populating it here.
-        token.attrs = [ [ 'src', `${server}/${imageFormat}/${zippedCode}` ], [ 'alt', '' ], ['class', 'uml-diagram'] ]
-        token.block = true
-        token.children = altToken
-        token.info = params
-        token.map = [ startLine, nextLine ]
-        token.markup = markup
-
-        state.line = nextLine + (autoClosed ? 1 : 0)
-
-        return true
-      }, {
-        alt: [ 'paragraph', 'reference', 'blockquote', 'list' ]
-      })
-      md.renderer.rules.uml_diagram = md.renderer.rules.image
-    }, {
-      openMarker: conf.openMarker,
-      closeMarker: conf.closeMarker,
-      imageFormat: conf.imageFormat,
-      server: conf.server
-    })
-  }
-}
-
-function encode64 (data) {
-  let r = ''
-  for (let i = 0; i < data.length; i += 3) {
-    if (i + 2 === data.length) {
-      r += append3bytes(data.charCodeAt(i), data.charCodeAt(i + 1), 0)
-    } else if (i + 1 === data.length) {
-      r += append3bytes(data.charCodeAt(i), 0, 0)
-    } else {
-      r += append3bytes(data.charCodeAt(i), data.charCodeAt(i + 1), data.charCodeAt(i + 2))
-    }
-  }
-  return r
-}
-
-function append3bytes (b1, b2, b3) {
-  let c1 = b1 >> 2
-  let c2 = ((b1 & 0x3) << 4) | (b2 >> 4)
-  let c3 = ((b2 & 0xF) << 2) | (b3 >> 6)
-  let c4 = b3 & 0x3F
-  let r = ''
-  r += encode6bit(c1 & 0x3F)
-  r += encode6bit(c2 & 0x3F)
-  r += encode6bit(c3 & 0x3F)
-  r += encode6bit(c4 & 0x3F)
-  return r
-}
-
-function encode6bit(raw) {
-  let b = raw
-  if (b < 10) {
-    return String.fromCharCode(48 + b)
-  }
-  b -= 10
-  if (b < 26) {
-    return String.fromCharCode(65 + b)
-  }
-  b -= 26
-  if (b < 26) {
-    return String.fromCharCode(97 + b)
-  }
-  b -= 26
-  if (b === 0) {
-    return '-'
-  }
-  if (b === 1) {
-    return '_'
-  }
-  return '?'
-}

+ 0 - 16
client/components/editor/markdown/tabset.js

@@ -1,16 +0,0 @@
-import cash from 'cash-dom'
-import _ from 'lodash'
-
-export default {
-  format () {
-    for (let i = 1; i < 6; i++) {
-      cash(`.editor-markdown-preview-content h${i}.tabset`).each((idx, elm) => {
-        elm.innerHTML = 'Tabset ( rendered upon saving )'
-        cash(elm).nextUntil(_.times(i, t => `h${t + 1}`).join(', '), `h${i + 1}`).each((hidx, hd) => {
-          hd.classList.add('tabset-header')
-          cash(hd).nextUntil(_.times(i + 1, t => `h${t + 1}`).join(', ')).wrapAll('<div class="tabset-content"></div>')
-        })
-      })
-    }
-  }
-}

+ 0 - 577
client/components/history.vue

@@ -1,577 +0,0 @@
-<template lang='pug'>
-  v-app(:dark='$vuetify.theme.dark').history
-    nav-header
-    v-content
-      v-toolbar(color='primary', dark)
-        .subheading Viewing history of #[strong /{{path}}]
-        template(v-if='$vuetify.breakpoint.mdAndUp')
-          v-spacer
-          .caption.blue--text.text--lighten-3.mr-4 Trail Length: {{total}}
-          .caption.blue--text.text--lighten-3 ID: {{pageId}}
-          v-btn.ml-4(depressed, color='blue darken-1', @click='goLive') Return to Live Version
-      v-container(fluid, grid-list-xl)
-        v-layout(row, wrap)
-          v-flex(xs12, md4)
-            v-chip.my-0.ml-6(
-              label
-              small
-              :color='$vuetify.theme.dark ? `grey darken-2` : `grey lighten-2`'
-              :class='$vuetify.theme.dark ? `grey--text text--lighten-2` : `grey--text text--darken-2`'
-              )
-              span Live
-            v-timeline(
-              dense
-              )
-              v-timeline-item.pb-2(
-                v-for='(ph, idx) in fullTrail'
-                :key='ph.versionId'
-                :small='ph.actionType === `edit`'
-                :color='trailColor(ph.actionType)'
-                :icon='trailIcon(ph.actionType)'
-                )
-                v-card.radius-7(flat, :class='trailBgColor(ph.actionType)')
-                  v-toolbar(flat, :color='trailBgColor(ph.actionType)', height='40')
-                    .caption(:title='$options.filters.moment(ph.versionDate, `LLL`)') {{ ph.versionDate | moment('ll') }}
-                    v-divider.mx-3(vertical)
-                    .caption(v-if='ph.actionType === `edit`') Edited by #[strong {{ ph.authorName }}]
-                    .caption(v-else-if='ph.actionType === `move`') Moved from #[strong {{ph.valueBefore}}] to #[strong {{ph.valueAfter}}] by #[strong {{ ph.authorName }}]
-                    .caption(v-else-if='ph.actionType === `initial`') Created by #[strong {{ ph.authorName }}]
-                    .caption(v-else-if='ph.actionType === `live`') Last Edited by #[strong {{ ph.authorName }}]
-                    .caption(v-else) Unknown Action by #[strong {{ ph.authorName }}]
-                    v-spacer
-                    v-menu(offset-x, left)
-                      template(v-slot:activator='{ on }')
-                        v-btn.mr-2.radius-4(icon, v-on='on', small, tile): v-icon mdi-dots-horizontal
-                      v-list(dense, nav).history-promptmenu
-                        v-list-item(@click='setDiffSource(ph.versionId)', :disabled='(ph.versionId >= diffTarget && diffTarget !== 0) || ph.versionId === 0')
-                          v-list-item-avatar(size='24'): v-avatar A
-                          v-list-item-title Set as Differencing Source
-                        v-list-item(@click='setDiffTarget(ph.versionId)', :disabled='ph.versionId <= diffSource && ph.versionId !== 0')
-                          v-list-item-avatar(size='24'): v-avatar B
-                          v-list-item-title Set as Differencing Target
-                        v-list-item(@click='viewSource(ph.versionId)')
-                          v-list-item-avatar(size='24'): v-icon mdi-code-tags
-                          v-list-item-title View Source
-                        v-list-item(@click='download(ph.versionId)')
-                          v-list-item-avatar(size='24'): v-icon mdi-cloud-download-outline
-                          v-list-item-title Download Version
-                        v-list-item(@click='restore(ph.versionId, ph.versionDate)', :disabled='ph.versionId === 0')
-                          v-list-item-avatar(size='24'): v-icon(:disabled='ph.versionId === 0') mdi-history
-                          v-list-item-title Restore
-                        v-list-item(@click='branchOff(ph.versionId)')
-                          v-list-item-avatar(size='24'): v-icon mdi-source-branch
-                          v-list-item-title Branch off from here
-                    v-btn.mr-2.radius-4(
-                      @click='setDiffSource(ph.versionId)'
-                      icon
-                      small
-                      depressed
-                      tile
-                      :class='diffSource === ph.versionId ? `pink white--text` : ($vuetify.theme.dark ? `grey darken-2` : `grey lighten-2`)'
-                      :disabled='(ph.versionId >= diffTarget && diffTarget !== 0) || ph.versionId === 0'
-                      ): strong A
-                    v-btn.mr-0.radius-4(
-                      @click='setDiffTarget(ph.versionId)'
-                      icon
-                      small
-                      depressed
-                      tile
-                      :class='diffTarget === ph.versionId ? `pink white--text` : ($vuetify.theme.dark ? `grey darken-2` : `grey lighten-2`)'
-                      :disabled='ph.versionId <= diffSource && ph.versionId !== 0'
-                      ): strong B
-
-            v-btn.ma-0.radius-7(
-              v-if='total > trail.length'
-              block
-              color='primary'
-              @click='loadMore'
-              )
-              .caption.white--text Load More...
-
-            v-chip.ma-0(
-              v-else
-              label
-              small
-              :color='$vuetify.theme.dark ? `grey darken-2` : `grey lighten-2`'
-              :class='$vuetify.theme.dark ? `grey--text text--lighten-2` : `grey--text text--darken-2`'
-              ) End of history trail
-
-          v-flex(xs12, md8)
-            v-card.radius-7(:class='$vuetify.breakpoint.mdAndUp ? `mt-8` : ``')
-              v-card-text
-                v-card.grey.radius-7(flat, :class='$vuetify.theme.dark ? `darken-2` : `lighten-4`')
-                  v-row(no-gutters, align='center')
-                    v-col
-                      v-card-text
-                        .subheading {{target.title}}
-                        .caption {{target.description}}
-                    v-col.text-right.py-3(cols='2', v-if='$vuetify.breakpoint.mdAndUp')
-                      v-btn.mr-3(:color='$vuetify.theme.dark ? `white` : `grey darken-3`', small, dark, outlined, @click='toggleViewMode')
-                        v-icon(left) mdi-eye
-                        .overline View Mode
-                v-card.mt-3(light, v-html='diffHTML', flat)
-
-    v-dialog(v-model='isRestoreConfirmDialogShown', max-width='650', persistent)
-      v-card
-        .dialog-header.is-orange {{$t('history:restore.confirmTitle')}}
-        v-card-text.pa-4
-          i18next(tag='span', path='history:restore.confirmText')
-            strong(place='date') {{ restoreTarget.versionDate | moment('LLL') }}
-        v-card-actions
-          v-spacer
-          v-btn(text, @click='isRestoreConfirmDialogShown = false', :disabled='restoreLoading') {{$t('common:actions.cancel')}}
-          v-btn(color='orange darken-2', dark, @click='restoreConfirm', :loading='restoreLoading') {{$t('history:restore.confirmButton')}}
-
-    page-selector(mode='create', v-model='branchOffOpts.modal', :open-handler='branchOffHandle', :path='branchOffOpts.path', :locale='branchOffOpts.locale')
-
-    nav-footer
-    notify
-    search-results
-</template>
-
-<script>
-import * as Diff2Html from 'diff2html'
-import { createPatch } from 'diff'
-import _ from 'lodash'
-import gql from 'graphql-tag'
-
-export default {
-  i18nOptions: { namespaces: 'history' },
-  props: {
-    pageId: {
-      type: Number,
-      default: 0
-    },
-    locale: {
-      type: String,
-      default: 'en'
-    },
-    path: {
-      type: String,
-      default: 'home'
-    },
-    title: {
-      type: String,
-      default: 'Untitled Page'
-    },
-    description: {
-      type: String,
-      default: ''
-    },
-    createdAt: {
-      type: String,
-      default: ''
-    },
-    updatedAt: {
-      type: String,
-      default: ''
-    },
-    tags: {
-      type: Array,
-      default: () => ([])
-    },
-    authorName: {
-      type: String,
-      default: 'Unknown'
-    },
-    authorId: {
-      type: Number,
-      default: 0
-    },
-    isPublished: {
-      type: Boolean,
-      default: false
-    },
-    liveContent: {
-      type: String,
-      default: ''
-    },
-    effectivePermissions: {
-      type: String,
-      default: ''
-    }
-  },
-  data () {
-    return {
-      source: {
-        versionId: 0,
-        content: '',
-        title: '',
-        description: ''
-      },
-      target: {
-        versionId: 0,
-        content: '',
-        title: '',
-        description: ''
-      },
-      trail: [],
-      diffSource: 0,
-      diffTarget: 0,
-      offsetPage: 0,
-      total: 0,
-      viewMode: 'line-by-line',
-      cache: [],
-      restoreTarget: {
-        versionId: 0,
-        versionDate: ''
-      },
-      branchOffOpts: {
-        versionId: 0,
-        locale: 'en',
-        path: 'new-page',
-        modal: false
-      },
-      isRestoreConfirmDialogShown: false,
-      restoreLoading: false
-    }
-  },
-  computed: {
-    fullTrail () {
-      const liveTrailItem = {
-        versionId: 0,
-        authorId: this.authorId,
-        authorName: this.authorName,
-        actionType: 'live',
-        valueBefore: null,
-        valueAfter: null,
-        versionDate: this.updatedAt
-      }
-      // -> Check for move between latest and live
-      const prevPage = _.find(this.cache, ['versionId', _.get(this.trail, '[0].versionId', -1)])
-      if (prevPage && this.path !== prevPage.path) {
-        liveTrailItem.actionType = 'move'
-        liveTrailItem.valueBefore = prevPage.path
-        liveTrailItem.valueAfter = this.path
-      }
-      // -> Combine trail with live
-      return [
-        liveTrailItem,
-        ...this.trail
-      ]
-    },
-    diffs () {
-      return createPatch(`/${this.path}`, this.source.content, this.target.content)
-    },
-    diffHTML () {
-      return Diff2Html.html(this.diffs, {
-        inputFormat: 'diff',
-        drawFileList: false,
-        matching: 'lines',
-        outputFormat: this.viewMode
-      })
-    }
-  },
-  watch: {
-    trail (newValue, oldValue) {
-      if (newValue && newValue.length > 0) {
-        this.diffTarget = 0
-        this.diffSource = _.get(_.head(newValue), 'versionId', 0)
-      }
-    },
-    async diffSource (newValue, oldValue) {
-      if (this.diffSource !== this.source.versionId) {
-        const page = _.find(this.cache, { versionId: newValue })
-        if (page) {
-          this.source = page
-        } else {
-          this.source = await this.loadVersion(newValue)
-        }
-      }
-    },
-    async diffTarget (newValue, oldValue) {
-      if (this.diffTarget !== this.target.versionId) {
-        const page = _.find(this.cache, { versionId: newValue })
-        if (page) {
-          this.target = page
-        } else {
-          this.target = await this.loadVersion(newValue)
-        }
-      }
-    }
-  },
-  created () {
-    this.$store.commit('page/SET_ID', this.id)
-    this.$store.commit('page/SET_LOCALE', this.locale)
-    this.$store.commit('page/SET_PATH', this.path)
-
-    this.$store.commit('page/SET_MODE', 'history')
-
-    this.cache.push({
-      action: 'live',
-      authorId: this.authorId,
-      authorName: this.authorName,
-      content: this.liveContent,
-      contentType: '',
-      createdAt: this.createdAt,
-      description: this.description,
-      editor: '',
-      isPrivate: false,
-      isPublished: this.isPublished,
-      locale: this.locale,
-      pageId: this.pageId,
-      path: this.path,
-      publishEndDate: '',
-      publishStartDate: '',
-      tags: this.tags,
-      title: this.title,
-      versionId: 0,
-      versionDate: this.updatedAt
-    })
-
-    this.target = this.cache[0]
-
-    if (this.effectivePermissions) {
-      this.$store.set('page/effectivePermissions', JSON.parse(Buffer.from(this.effectivePermissions, 'base64').toString()))
-    }
-  },
-  methods: {
-    async loadVersion (versionId) {
-      this.$store.commit(`loadingStart`, 'history-version-' + versionId)
-      const resp = await this.$apollo.query({
-        query: gql`
-          query ($pageId: Int!, $versionId: Int!) {
-            pages {
-              version (pageId: $pageId, versionId: $versionId) {
-                action
-                authorId
-                authorName
-                content
-                contentType
-                createdAt
-                versionDate
-                description
-                editor
-                isPrivate
-                isPublished
-                locale
-                pageId
-                path
-                publishEndDate
-                publishStartDate
-                tags
-                title
-                versionId
-              }
-            }
-          }
-        `,
-        variables: {
-          versionId,
-          pageId: this.pageId
-        }
-      })
-      this.$store.commit(`loadingStop`, 'history-version-' + versionId)
-      const page = _.get(resp, 'data.pages.version', null)
-      if (page) {
-        this.cache.push(page)
-        return page
-      } else {
-        return { content: '' }
-      }
-    },
-    viewSource (versionId) {
-      window.location.assign(`/s/${this.locale}/${this.path}?v=${versionId}`)
-    },
-    download (versionId) {
-      window.location.assign(`/d/${this.locale}/${this.path}?v=${versionId}`)
-    },
-    restore (versionId, versionDate) {
-      this.restoreTarget = {
-        versionId,
-        versionDate
-      }
-      this.isRestoreConfirmDialogShown = true
-    },
-    async restoreConfirm () {
-      this.restoreLoading = true
-      this.$store.commit(`loadingStart`, 'history-restore')
-      try {
-        const resp = await this.$apollo.mutate({
-          mutation: gql`
-            mutation ($pageId: Int!, $versionId: Int!) {
-              pages {
-                restore (pageId: $pageId, versionId: $versionId) {
-                  responseResult {
-                    succeeded
-                    errorCode
-                    slug
-                    message
-                  }
-                }
-              }
-            }
-          `,
-          variables: {
-            versionId: this.restoreTarget.versionId,
-            pageId: this.pageId
-          }
-        })
-        if (_.get(resp, 'data.pages.restore.responseResult.succeeded', false) === true) {
-          this.$store.commit('showNotification', {
-            style: 'success',
-            message: this.$t('history:restore.success'),
-            icon: 'check'
-          })
-          this.isRestoreConfirmDialogShown = false
-          setTimeout(() => {
-            window.location.assign(`/${this.locale}/${this.path}`)
-          }, 1000)
-        } else {
-          throw new Error(_.get(resp, 'data.pages.restore.responseResult.message', 'An unexpected error occurred'))
-        }
-      } catch (err) {
-        this.$store.commit('showNotification', {
-          style: 'red',
-          message: err.message,
-          icon: 'alert'
-        })
-      }
-      this.$store.commit(`loadingStop`, 'history-restore')
-      this.restoreLoading = false
-    },
-    branchOff (versionId) {
-      const pathParts = this.path.split('/')
-      this.branchOffOpts = {
-        versionId: versionId,
-        locale: this.locale,
-        path: (pathParts.length > 1) ? _.initial(pathParts).join('/') + `/new-page` : `new-page`,
-        modal: true
-      }
-    },
-    branchOffHandle ({ locale, path }) {
-      window.location.assign(`/e/${locale}/${path}?from=${this.pageId},${this.branchOffOpts.versionId}`)
-    },
-    toggleViewMode () {
-      this.viewMode = (this.viewMode === 'line-by-line') ? 'side-by-side' : 'line-by-line'
-    },
-    goLive () {
-      window.location.assign(`/${this.path}`)
-    },
-    setDiffSource (versionId) {
-      this.diffSource = versionId
-    },
-    setDiffTarget (versionId) {
-      this.diffTarget = versionId
-    },
-    loadMore () {
-      this.offsetPage++
-      this.$apollo.queries.trail.fetchMore({
-        variables: {
-          id: this.pageId,
-          offsetPage: this.offsetPage,
-          offsetSize: this.$vuetify.breakpoint.mdAndUp ? 25 : 5
-        },
-        updateQuery: (previousResult, { fetchMoreResult }) => {
-          return {
-            pages: {
-              history: {
-                total: previousResult.pages.history.total,
-                trail: [...previousResult.pages.history.trail, ...fetchMoreResult.pages.history.trail],
-                __typename: previousResult.pages.history.__typename
-              },
-              __typename: previousResult.pages.__typename
-            }
-          }
-        }
-      })
-    },
-    trailColor (actionType) {
-      switch (actionType) {
-        case 'edit':
-          return 'primary'
-        case 'move':
-          return 'purple'
-        case 'initial':
-          return 'teal'
-        case 'live':
-          return 'orange'
-        default:
-          return 'grey'
-      }
-    },
-    trailIcon (actionType) {
-      switch (actionType) {
-        case 'edit':
-          return '' // 'mdi-pencil'
-        case 'move':
-          return 'mdi-forward'
-        case 'initial':
-          return 'mdi-plus'
-        case 'live':
-          return 'mdi-atom-variant'
-        default:
-          return 'mdi-alert'
-      }
-    },
-    trailBgColor (actionType) {
-      switch (actionType) {
-        case 'move':
-          return this.$vuetify.theme.dark ? 'purple' : 'purple lighten-5'
-        case 'initial':
-          return this.$vuetify.theme.dark ? 'teal darken-3' : 'teal lighten-5'
-        case 'live':
-          return this.$vuetify.theme.dark ? 'orange darken-3' : 'orange lighten-5'
-        default:
-          return this.$vuetify.theme.dark ? 'grey darken-3' : 'grey lighten-4'
-      }
-    }
-  },
-  apollo: {
-    trail: {
-      query: gql`
-        query($id: Int!, $offsetPage: Int, $offsetSize: Int) {
-          pages {
-            history(id:$id, offsetPage:$offsetPage, offsetSize:$offsetSize) {
-              trail {
-                versionId
-                authorId
-                authorName
-                actionType
-                valueBefore
-                valueAfter
-                versionDate
-              }
-              total
-            }
-          }
-        }
-      `,
-      variables () {
-        return {
-          id: this.pageId,
-          offsetPage: 0,
-          offsetSize: this.$vuetify.breakpoint.mdAndUp ? 25 : 5
-        }
-      },
-      manual: true,
-      result ({ data, loading, networkStatus }) {
-        this.total = data.pages.history.total
-        this.trail = data.pages.history.trail
-      },
-      watchLoading (isLoading) {
-        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'history-trail-refresh')
-      }
-    }
-  }
-}
-</script>
-
-<style lang='scss'>
-
-.history {
-  &-promptmenu {
-    border-top: 5px solid mc('blue', '700');
-  }
-
-  .d2h-file-wrapper {
-    border: 1px solid #EEE;
-    border-left: none;
-  }
-
-  .d2h-file-header {
-    display: none;
-  }
-}
-
-</style>

+ 0 - 37
client/components/new-page.vue

@@ -1,37 +0,0 @@
-<template lang='pug'>
-  v-app
-    .newpage
-      .newpage-content
-        img.animated.fadeIn(src='/_assets-legacy/svg/icon-delete-file.svg', alt='Not Found')
-        .headline {{ $t('newpage.title') }}
-        .subtitle-1.mt-3 {{ $t('newpage.subtitle') }}
-        v-btn.mt-5(:href='`/e/` + locale + `/` + path', x-large)
-          v-icon(left) mdi-plus
-          span {{ $t('newpage.create') }}
-        v-btn.mt-5(color='purple lighten-3', href='javascript:window.history.go(-1);', outlined)
-          v-icon(left) mdi-arrow-left
-          span {{ $t('newpage.goback') }}
-</template>
-
-<script>
-
-export default {
-  props: {
-    locale: {
-      type: String,
-      default: 'en'
-    },
-    path: {
-      type: String,
-      default: 'home'
-    }
-  },
-  data() {
-    return { }
-  }
-}
-</script>
-
-<style lang='scss'>
-
-</style>

+ 0 - 24
client/components/not-found.vue

@@ -1,24 +0,0 @@
-<template lang='pug'>
-  v-app
-    .notfound
-      .notfound-content
-        img.animated.fadeIn(src='/_assets-legacy/svg/icon-delete-file.svg', alt='Not Found')
-        .headline {{$t('notfound.title')}}
-        .subheading.mt-3 {{$t('notfound.subtitle')}}
-        v-btn.mt-5(color='red lighten-4', href='/', large, outlined)
-          v-icon(left) mdi-home
-          span {{$t('notfound.gohome')}}
-</template>
-
-<script>
-
-export default {
-  data() {
-    return { }
-  }
-}
-</script>
-
-<style lang='scss'>
-
-</style>

+ 0 - 106
client/components/source.vue

@@ -1,106 +0,0 @@
-<template lang='pug'>
-  v-app(:dark='$vuetify.theme.dark').source
-    nav-header
-    v-content
-      v-toolbar(color='primary', dark)
-        i18next.subheading(v-if='versionId > 0', path='common:page.viewingSourceVersion', tag='div')
-          strong(place='date', :title='$options.filters.moment(versionDate, `LLL`)') {{versionDate | moment('lll')}}
-          strong(place='path') /{{path}}
-        i18next.subheading(v-else, path='common:page.viewingSource', tag='div')
-          strong(place='path') /{{path}}
-        template(v-if='$vuetify.breakpoint.mdAndUp')
-          v-spacer
-          .caption.blue--text.text--lighten-3 {{$t('common:page.id', { id: pageId })}}
-          .caption.blue--text.text--lighten-3.ml-4(v-if='versionId > 0') {{$t('common:page.versionId', { id: versionId })}}
-          v-btn.ml-4(v-if='versionId > 0', depressed, color='blue darken-1', @click='goHistory')
-            v-icon mdi-history
-          v-btn.ml-4(depressed, color='blue darken-1', @click='goLive') {{$t('common:page.returnNormalView')}}
-      v-card(tile)
-        v-card-text
-          v-card.grey.radius-7(flat, :class='$vuetify.theme.dark ? `darken-4` : `lighten-4`')
-            v-card-text
-              pre
-                code
-                  slot
-
-    nav-footer
-    notify
-    search-results
-</template>
-
-<script>
-export default {
-  props: {
-    pageId: {
-      type: Number,
-      default: 0
-    },
-    locale: {
-      type: String,
-      default: 'en'
-    },
-    path: {
-      type: String,
-      default: 'home'
-    },
-    versionId: {
-      type: Number,
-      default: 0
-    },
-    versionDate: {
-      type: String,
-      default: ''
-    },
-    effectivePermissions: {
-      type: String,
-      default: ''
-    }
-  },
-  data() {
-    return {}
-  },
-  created () {
-    this.$store.commit('page/SET_ID', this.id)
-    this.$store.commit('page/SET_LOCALE', this.locale)
-    this.$store.commit('page/SET_PATH', this.path)
-
-    this.$store.commit('page/SET_MODE', 'source')
-
-    if (this.effectivePermissions) {
-      this.$store.set('page/effectivePermissions', JSON.parse(Buffer.from(this.effectivePermissions, 'base64').toString()))
-    }
-  },
-  methods: {
-    goLive() {
-      window.location.assign(`/${this.locale}/${this.path}`)
-    },
-    goHistory () {
-      window.location.assign(`/h/${this.locale}/${this.path}`)
-    }
-  }
-}
-</script>
-
-<style lang='scss'>
-
-.source {
-  pre > code {
-    box-shadow: none;
-    background-color: transparent;
-    color: mc('grey', '800');
-    font-family: 'Roboto Mono', sans-serif;
-    font-weight: 400;
-    font-size: 1rem;
-
-    @at-root .theme--dark.source pre > code {
-      background-color: mc('grey', '900');
-      color: mc('grey', '400');
-    }
-
-    &::before {
-      display: none;
-    }
-  }
-}
-
-</style>

+ 0 - 341
client/components/tags.vue

@@ -1,341 +0,0 @@
-<template lang='pug'>
-  v-app(:dark='$vuetify.theme.dark').tags
-    nav-header
-    v-navigation-drawer.pb-0.elevation-1(app, fixed, clipped, :right='$vuetify.rtl', permanent, width='300')
-      vue-scroll(:ops='scrollStyle')
-        v-list(dense, nav)
-          v-list-item(href='/')
-            v-list-item-icon: v-icon mdi-home
-            v-list-item-title {{$t('common:header.home')}}
-          template(v-for='(tags, groupName) in tagsGrouped')
-            v-divider.my-2
-            v-subheader.pl-4(:key='`tagGroup-` + groupName') {{groupName}}
-            v-list-item(v-for='tag of tags', @click='toggleTag(tag.tag)', :key='`tag-` + tag.tag')
-              v-list-item-icon
-                v-icon(v-if='isSelected(tag.tag)', color='primary') mdi-checkbox-intermediate
-                v-icon(v-else) mdi-checkbox-blank-outline
-              v-list-item-title {{tag.title}}
-    v-content.grey(:class='$vuetify.theme.dark ? `darken-4-d5` : `lighten-3`')
-      v-toolbar(color='primary', dark, flat, height='58')
-        template(v-if='selection.length > 0')
-          .overline.mr-3.animated.fadeInLeft {{$t('tags:currentSelection')}}
-          v-chip.mr-3.primary--text(
-            v-for='tag of tagsSelected'
-            :key='`tagSelected-` + tag.tag'
-            color='white'
-            close
-            @click:close='toggleTag(tag.tag)'
-            ) {{tag.title}}
-          v-spacer
-          v-btn.animated.fadeIn(
-            small
-            outlined
-            color='blue lighten-4'
-            rounded
-            @click='selection = []'
-            )
-            v-icon(left) mdi-close
-            span {{$t('tags:clearSelection')}}
-        template(v-else)
-          v-icon.mr-3.animated.fadeInRight mdi-arrow-left
-          .overline.animated.fadeInRight {{$t('tags:selectOneMoreTags')}}
-      v-toolbar(:color='$vuetify.theme.dark ? `grey darken-4-l5` : `grey lighten-4`', flat, height='58')
-        v-text-field.tags-search(
-          v-model='innerSearch'
-          :label='$t(`tags:searchWithinResultsPlaceholder`)'
-          solo
-          hide-details
-          flat
-          rounded
-          single-line
-          height='40'
-          prepend-icon='mdi-text-box-search-outline'
-          append-icon='mdi-arrow-right'
-          clearable
-        )
-        template(v-if='locales.length > 1')
-          v-divider.mx-3(vertical)
-          .overline {{$t('tags:locale')}}
-          v-select.ml-2(
-            :items='locales'
-            v-model='locale'
-            :background-color='$vuetify.theme.dark ? `grey darken-3` : `white`'
-            hide-details
-            :label='$t(`tags:locale`)'
-            item-text='name'
-            item-value='code'
-            rounded
-            single-line
-            dense
-            height='40'
-            style='max-width: 170px;'
-          )
-        v-divider.mx-3(vertical)
-        .overline {{$t('tags:orderBy')}}
-        v-select.ml-2(
-          :items='orderByItems'
-          v-model='orderBy'
-          :background-color='$vuetify.theme.dark ? `grey darken-3` : `white`'
-          hide-details
-          :label='$t(`tags:orderBy`)'
-          rounded
-          single-line
-          dense
-          height='40'
-          style='max-width: 250px;'
-        )
-        v-btn-toggle.ml-2(v-model='orderByDirection', rounded, mandatory)
-          v-btn(text, height='40'): v-icon(size='20') mdi-chevron-double-up
-          v-btn(text, height='40'): v-icon(size='20') mdi-chevron-double-down
-      v-divider
-      .text-center.pt-10(v-if='selection.length < 1')
-        img(src='/_assets-legacy/svg/icon-price-tag.svg')
-        .subtitle-2.grey--text {{$t('tags:selectOneMoreTagsHint')}}
-      .px-5.py-2(v-else)
-        v-data-iterator(
-          :items='pages'
-          :items-per-page='4'
-          :search='innerSearch'
-          :loading='isLoading'
-          :options.sync='pagination'
-          hide-default-footer
-          ref='dude'
-          )
-          template(v-slot:loading)
-            .text-center.pt-10
-              v-progress-circular(
-                indeterminate
-                color='primary'
-                size='96'
-                width='2'
-                )
-              .subtitle-2.grey--text.mt-5 {{$t('tags:retrievingResultsLoading')}}
-          template(v-slot:no-data)
-            .text-center.pt-10
-              img(src='/_assets-legacy/svg/icon-info.svg')
-              .subtitle-2.grey--text {{$t('tags:noResults')}}
-          template(v-slot:no-results)
-            .text-center.pt-10
-              img(src='/_assets-legacy/svg/icon-info.svg')
-              .subtitle-2.grey--text {{$t('tags:noResultsWithFilter')}}
-          template(v-slot:default='props')
-            v-row(align='stretch')
-              v-col(
-                v-for='item of props.items'
-                :key='`page-` + item.id'
-                cols='12'
-                lg='6'
-                )
-                v-card.radius-7(
-                  @click='goTo(item)'
-                  style='height:100%;'
-                  :class='$vuetify.theme.dark ? `grey darken-4` : ``'
-                  )
-                  v-card-text
-                    .d-flex.flex-row.align-center
-                      .body-1: strong.primary--text {{item.title}}
-                      v-spacer
-                      i18next.caption(tag='div', path='tags:pageLastUpdated')
-                        span(place='date') {{item.updatedAt | moment('from')}}
-                    .body-2.grey--text {{item.description || '---'}}
-                    v-divider.my-2
-                    .d-flex.flex-row.align-center
-                      v-chip(small, label, :color='$vuetify.theme.dark ? `grey darken-3-l5` : `grey lighten-4`').overline {{item.locale}}
-                      .caption.ml-1 / {{item.path}}
-        .text-center.py-2.animated.fadeInDown(v-if='this.pageTotal > 1')
-          v-pagination(v-model='pagination.page', :length='pageTotal')
-
-    nav-footer
-    notify
-    search-results
-</template>
-
-<script>
-import VueRouter from 'vue-router'
-import _ from 'lodash'
-
-import tagsQuery from 'gql/common/common-pages-query-tags.gql'
-import pagesQuery from 'gql/common/common-pages-query-list.gql'
-
-/* global siteLangs */
-
-const router = new VueRouter({
-  mode: 'history',
-  base: '/t'
-})
-
-export default {
-  i18nOptions: { namespaces: 'tags' },
-  data() {
-    return {
-      tags: [],
-      selection: [],
-      innerSearch: '',
-      locale: 'any',
-      locales: [],
-      orderBy: 'title',
-      orderByDirection: 0,
-      pagination: {
-        page: 1,
-        itemsPerPage: 12,
-        mustSort: true,
-        sortBy: ['title'],
-        sortDesc: [false]
-      },
-      pages: [],
-      isLoading: true,
-      scrollStyle: {
-        vuescroll: {},
-        scrollPanel: {
-          initialScrollY: 0,
-          initialScrollX: 0,
-          scrollingX: false,
-          easing: 'easeOutQuad',
-          speed: 1000,
-          verticalNativeBarPos: this.$vuetify.rtl ? `left` : `right`
-        },
-        rail: {
-          gutterOfEnds: '2px'
-        },
-        bar: {
-          onlyShowBarOnScroll: false,
-          background: '#CCC',
-          hoverStyle: {
-            background: '#999'
-          }
-        }
-      }
-    }
-  },
-  computed: {
-    tagsGrouped () {
-      return _.groupBy(this.tags, t => t.title.charAt(0).toUpperCase())
-    },
-    tagsSelected () {
-      return _.filter(this.tags, t => _.includes(this.selection, t.tag))
-    },
-    pageTotal () {
-      return Math.ceil(this.pages.length / this.pagination.itemsPerPage)
-    },
-    orderByItems () {
-      return [
-        { text: this.$t('tags:orderByField.creationDate'), value: 'createdAt' },
-        { text: this.$t('tags:orderByField.ID'), value: 'id' },
-        { text: this.$t('tags:orderByField.lastModified'), value: 'updatedAt' },
-        { text: this.$t('tags:orderByField.path'), value: 'path' },
-        { text: this.$t('tags:orderByField.title'), value: 'title' }
-      ]
-    }
-  },
-  watch: {
-    locale (newValue, oldValue) {
-      this.rebuildURL()
-    },
-    orderBy (newValue, oldValue) {
-      this.rebuildURL()
-      this.pagination.sortBy = [newValue]
-    },
-    orderByDirection (newValue, oldValue) {
-      this.rebuildURL()
-      this.pagination.sortDesc = [newValue === 1]
-    }
-  },
-  router,
-  created () {
-    this.$store.commit('page/SET_MODE', 'tags')
-    this.selection = _.compact(decodeURI(this.$route.path).split('/'))
-  },
-  mounted () {
-    this.locales = _.concat(
-      [{name: this.$t('tags:localeAny'), code: 'any'}],
-      (siteLangs.length > 0 ? siteLangs : [])
-    )
-    if (this.$route.query.lang) {
-      this.locale = this.$route.query.lang
-    }
-    if (this.$route.query.sort) {
-      this.orderBy = this.$route.query.sort.toLowerCase()
-      switch (this.orderBy) {
-        case 'updatedat':
-          this.orderBy = 'updatedAt'
-          break
-      }
-      this.pagination.sortBy = [this.orderBy]
-    }
-    if (this.$route.query.dir) {
-      this.orderByDirection = this.$route.query.dir === 'asc' ? 0 : 1
-      this.pagination.sortDesc = [this.orderByDirection === 1]
-    }
-  },
-  methods: {
-    toggleTag (tag) {
-      if (_.includes(this.selection, tag)) {
-        this.selection = _.without(this.selection, tag)
-      } else {
-        this.selection.push(tag)
-      }
-      this.rebuildURL()
-    },
-    isSelected (tag) {
-      return _.includes(this.selection, tag)
-    },
-    rebuildURL () {
-      let urlObj = {
-        path: '/' + this.selection.join('/')
-      }
-      if (this.locale !== `any`) {
-        _.set(urlObj, 'query.lang', this.locale)
-      }
-      if (this.orderBy !== `TITLE`) {
-        _.set(urlObj, 'query.sort', this.orderBy.toLowerCase())
-      }
-      if (this.orderByDirection !== 0) {
-        _.set(urlObj, 'query.dir', this.orderByDirection === 0 ? `asc` : `desc`)
-      }
-      this.$router.push(urlObj)
-    },
-    goTo (page) {
-      window.location.assign(`/${page.locale}/${page.path}`)
-    }
-  },
-  apollo: {
-    tags: {
-      query: tagsQuery,
-      fetchPolicy: 'cache-and-network',
-      update: (data) => _.cloneDeep(data.pages.tags),
-      watchLoading (isLoading) {
-        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'tags-refresh')
-      }
-    },
-    pages: {
-      query: pagesQuery,
-      fetchPolicy: 'cache-and-network',
-      update: (data) => _.cloneDeep(data.pages.list),
-      watchLoading (isLoading) {
-        this.isLoading = isLoading
-        this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'pages-refresh')
-      },
-      variables () {
-        return {
-          locale: this.locale === 'any' ? null : this.locale,
-          tags: this.selection
-        }
-      },
-      skip () {
-        return this.selection.length < 1
-      }
-    }
-  }
-}
-</script>
-
-<style lang='scss'>
-.tags-search {
-  .v-input__control {
-    min-height: initial !important;
-  }
-  .v-input__prepend-outer {
-    margin-top: 8px !important;
-  }
-}
-</style>

+ 0 - 33
client/components/unauthorized.vue

@@ -1,33 +0,0 @@
-<template lang='pug'>
-  v-app
-    .unauthorized
-      .unauthorized-content
-        img.animated.fadeIn(src='/_assets-legacy/svg/icon-delete-shield.svg', alt='Unauthorized')
-        .headline {{$t('unauthorized.title')}}
-        .subtitle-1.mt-3 {{$t('unauthorized.action.' + action)}}
-        v-btn.mt-5(href='/login', x-large)
-          v-icon(left) mdi-login
-          span {{$t('unauthorized.login')}}
-        v-btn.mt-5(color='red lighten-4', href='javascript:window.history.go(-1);', outlined)
-          v-icon(left) mdi-arrow-left
-          span {{$t('unauthorized.goback')}}
-</template>
-
-<script>
-
-export default {
-  props: {
-    action: {
-      type: String,
-      default: 'view'
-    }
-  },
-  data() {
-    return { }
-  }
-}
-</script>
-
-<style lang='scss'>
-
-</style>

BIN
client/fonts/arabic/BalooBhaijaan-Regular.woff


BIN
client/fonts/arabic/BalooBhaijaan-Regular.woff2


BIN
client/fonts/arabic/Tajawal-Bold.woff


BIN
client/fonts/arabic/Tajawal-Bold.woff2


BIN
client/fonts/arabic/Tajawal-Medium.woff


BIN
client/fonts/arabic/Tajawal-Medium.woff2


BIN
client/fonts/arabic/Tajawal-Regular.woff


BIN
client/fonts/arabic/Tajawal-Regular.woff2


BIN
client/fonts/default/Roboto-Bold.woff


BIN
client/fonts/default/Roboto-Bold.woff2


BIN
client/fonts/default/Roboto-BoldItalic.woff


BIN
client/fonts/default/Roboto-BoldItalic.woff2


BIN
client/fonts/default/Roboto-Italic.woff


BIN
client/fonts/default/Roboto-Italic.woff2


BIN
client/fonts/default/Roboto-Medium.woff


BIN
client/fonts/default/Roboto-Medium.woff2


BIN
client/fonts/default/Roboto-MediumItalic.woff


BIN
client/fonts/default/Roboto-MediumItalic.woff2


BIN
client/fonts/default/Roboto-Regular.woff


BIN
client/fonts/default/Roboto-Regular.woff2


BIN
client/fonts/default/RobotoMono-Regular.woff


BIN
client/fonts/default/RobotoMono-Regular.woff2


+ 0 - 8
client/graph/common/common-localization-query-translations.gql

@@ -1,8 +0,0 @@
-query($locale: String!, $namespace: String!) {
-  localization {
-    translations(locale:$locale, namespace:$namespace) {
-      key
-      value
-    }
-  }
-}

+ 0 - 12
client/graph/common/common-pages-mutation-delete.gql

@@ -1,12 +0,0 @@
-mutation($id: Int!) {
-  pages {
-    delete(id: $id) {
-      responseResult {
-        succeeded
-        errorCode
-        slug
-        message
-      }
-    }
-  }
-}

+ 0 - 12
client/graph/common/common-pages-mutation-move.gql

@@ -1,12 +0,0 @@
-mutation($id: Int!, $destinationPath: String!, $destinationLocale: String!) {
-  pages {
-    move(id: $id, destinationPath: $destinationPath, destinationLocale: $destinationLocale) {
-      responseResult {
-        succeeded
-        errorCode
-        slug
-        message
-      }
-    }
-  }
-}

+ 0 - 14
client/graph/common/common-pages-query-list.gql

@@ -1,14 +0,0 @@
-query ($limit: Int, $orderBy: PageOrderBy, $orderByDirection: PageOrderByDirection, $tags: [String!], $locale: String) {
-  pages {
-    list(limit: $limit, orderBy: $orderBy, orderByDirection: $orderByDirection, tags: $tags, locale: $locale) {
-      id
-      locale
-      path
-      title
-      description
-      createdAt
-      updatedAt
-      tags
-    }
-  }
-}

+ 0 - 15
client/graph/common/common-pages-query-search.gql

@@ -1,15 +0,0 @@
-query ($query: String!) {
-  pages {
-    search(query:$query) {
-      results {
-        id
-        title
-        description
-        path
-        locale
-      }
-      suggestions
-      totalHits
-    }
-  }
-}

+ 0 - 8
client/graph/common/common-pages-query-tags.gql

@@ -1,8 +0,0 @@
-query {
-  pages {
-    tags {
-      tag
-      title
-    }
-  }
-}

+ 0 - 12
client/graph/common/common-pages-query-tree.gql

@@ -1,12 +0,0 @@
-query ($parent: Int!, $mode: PageTreeMode!, $locale: String!) {
-  pages {
-    tree(parent: $parent, mode: $mode, locale: $locale) {
-      id
-      path
-      title
-      isFolder
-      pageId
-      parent
-    }
-  }
-}

+ 0 - 12
client/graph/editor/editor-media-mutation-asset-delete.gql

@@ -1,12 +0,0 @@
-mutation ($id: Int!) {
-  assets {
-    deleteAsset(id: $id) {
-      responseResult {
-        succeeded
-        errorCode
-        slug
-        message
-      }
-    }
-  }
-}

+ 0 - 12
client/graph/editor/editor-media-mutation-asset-rename.gql

@@ -1,12 +0,0 @@
-mutation ($id: Int!, $filename: String!) {
-  assets {
-    renameAsset(id:$id, filename: $filename) {
-      responseResult {
-        succeeded
-        errorCode
-        slug
-        message
-      }
-    }
-  }
-}

+ 0 - 12
client/graph/editor/editor-media-mutation-folder-create.gql

@@ -1,12 +0,0 @@
-mutation ($parentFolderId: Int!, $slug: String!) {
-  assets {
-    createFolder(parentFolderId:$parentFolderId, slug: $slug) {
-      responseResult {
-        succeeded
-        errorCode
-        slug
-        message
-      }
-    }
-  }
-}

+ 0 - 9
client/graph/editor/editor-media-query-folder-list.gql

@@ -1,9 +0,0 @@
-query ($parentFolderId: Int!) {
-  assets {
-    folders(parentFolderId:$parentFolderId) {
-      id
-      name
-      slug
-    }
-  }
-}

+ 0 - 14
client/graph/editor/editor-media-query-list.gql

@@ -1,14 +0,0 @@
-query ($folderId: Int!, $kind: AssetKind!) {
-  assets {
-    list(folderId:$folderId, kind: $kind) {
-      id
-      filename
-      ext
-      kind
-      mime
-      fileSize
-      createdAt
-      updatedAt
-    }
-  }
-}

+ 0 - 8
client/helpers/compatibility.js

@@ -1,8 +0,0 @@
-// =======================================
-// Fetch polyfill
-// =======================================
-// Requirement: Safari 9 and below, IE 11 and below
-
-if (!window.fetch) {
-  require('whatwg-fetch')
-}

+ 0 - 66
client/helpers/index.js

@@ -1,66 +0,0 @@
-import filesize from 'filesize.js'
-import _ from 'lodash'
-
-/* global siteConfig */
-
-const helpers = {
-  /**
-   * Convert bytes to humanized form
-   * @param {number} rawSize Size in bytes
-   * @returns {string} Humanized file size
-   */
-  filesize (rawSize) {
-    return _.toUpper(filesize(rawSize))
-  },
-  /**
-   * Convert raw path to safe path
-   * @param {string} rawPath Raw path
-   * @returns {string} Safe path
-   */
-  makeSafePath (rawPath) {
-    let rawParts = _.split(_.trim(rawPath), '/')
-    rawParts = _.map(rawParts, (r) => {
-      return _.kebabCase(_.deburr(_.trim(r)))
-    })
-
-    return _.join(_.filter(rawParts, (r) => { return !_.isEmpty(r) }), '/')
-  },
-  resolvePath (path) {
-    if (_.startsWith(path, '/')) { path = path.substring(1) }
-    return `${siteConfig.path}${path}`
-  },
-  /**
-   * Set Input Selection
-   * @param {DOMElement} input The input element
-   * @param {number} startPos The starting position
-   * @param {nunber} endPos The ending position
-   */
-  setInputSelection (input, startPos, endPos) {
-    input.focus()
-    if (typeof input.selectionStart !== 'undefined') {
-      input.selectionStart = startPos
-      input.selectionEnd = endPos
-    } else if (document.selection && document.selection.createRange) {
-      // IE branch
-      input.select()
-      var range = document.selection.createRange()
-      range.collapse(true)
-      range.moveEnd('character', endPos)
-      range.moveStart('character', startPos)
-      range.select()
-    }
-  }
-}
-
-export default {
-  install(Vue) {
-    Vue.$helpers = helpers
-    Object.defineProperties(Vue.prototype, {
-      $helpers: {
-        get() {
-          return helpers
-        }
-      }
-    })
-  }
-}

+ 0 - 25
client/index-app.js

@@ -1,25 +0,0 @@
-require('core-js/stable')
-require('regenerator-runtime/runtime')
-
-/* eslint-disable no-unused-expressions */
-
-switch (window.document.documentElement.lang) {
-  case 'ar':
-  case 'fa':
-    import(/* webpackChunkName: "fonts-arabic" */ './scss/fonts/arabic.scss')
-    break
-  default:
-    import(/* webpackChunkName: "fonts-default" */ './scss/fonts/default.scss')
-    break
-}
-
-require('modernizr')
-
-require('./scss/app.scss')
-import(/* webpackChunkName: "theme" */ './themes/default/scss/app.scss')
-
-import(/* webpackChunkName: "mdi" */ '@mdi/font/css/materialdesignicons.css')
-
-require('./helpers/compatibility.js')
-require('./client-app.js')
-import(/* webpackChunkName: "theme" */ './themes/default/js/app.js')

+ 0 - 3508
client/libs/animate/animate.scss

@@ -1,3508 +0,0 @@
-@charset "UTF-8";
-
-/*!
- * animate.css -http://daneden.me/animate
- * Version - 3.5.1
- * Licensed under the MIT license - http://opensource.org/licenses/MIT
- *
- * Copyright (c) 2016 Daniel Eden
- */
-
-.animated {
-  -webkit-animation-duration: 1s;
-  animation-duration: 1s;
-  -webkit-animation-fill-mode: both;
-  animation-fill-mode: both;
-  &.infinite {
-    -webkit-animation-iteration-count: infinite;
-    animation-iteration-count: infinite;
-  }
-  &.hinge {
-    -webkit-animation-duration: 2s;
-    animation-duration: 2s;
-  }
-  &.flipOutX, &.flipOutY, &.bounceIn, &.bounceOut {
-    -webkit-animation-duration: .75s;
-    animation-duration: .75s;
-  }
-}
-
-@-webkit-keyframes bounce {
-  from, 20%, 53%, 80%, to {
-    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-    -webkit-transform: translate3d(0, 0, 0);
-    transform: translate3d(0, 0, 0);
-  }
-
-  40%, 43% {
-    -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
-    animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
-    -webkit-transform: translate3d(0, -30px, 0);
-    transform: translate3d(0, -30px, 0);
-  }
-
-  70% {
-    -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
-    animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
-    -webkit-transform: translate3d(0, -15px, 0);
-    transform: translate3d(0, -15px, 0);
-  }
-
-  90% {
-    -webkit-transform: translate3d(0, -4px, 0);
-    transform: translate3d(0, -4px, 0);
-  }
-}
-
-
-@keyframes bounce {
-  from, 20%, 53%, 80%, to {
-    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-    -webkit-transform: translate3d(0, 0, 0);
-    transform: translate3d(0, 0, 0);
-  }
-
-  40%, 43% {
-    -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
-    animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
-    -webkit-transform: translate3d(0, -30px, 0);
-    transform: translate3d(0, -30px, 0);
-  }
-
-  70% {
-    -webkit-animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
-    animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
-    -webkit-transform: translate3d(0, -15px, 0);
-    transform: translate3d(0, -15px, 0);
-  }
-
-  90% {
-    -webkit-transform: translate3d(0, -4px, 0);
-    transform: translate3d(0, -4px, 0);
-  }
-}
-
-
-.bounce {
-  -webkit-animation-name: bounce;
-  animation-name: bounce;
-  -webkit-transform-origin: center bottom;
-  transform-origin: center bottom;
-}
-
-@-webkit-keyframes flash {
-  from, 50%, to {
-    opacity: 1;
-  }
-
-  25%, 75% {
-    opacity: 0;
-  }
-}
-
-
-@keyframes flash {
-  from, 50%, to {
-    opacity: 1;
-  }
-
-  25%, 75% {
-    opacity: 0;
-  }
-}
-
-
-.flash {
-  -webkit-animation-name: flash;
-  animation-name: flash;
-}
-
-/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */
-
-@-webkit-keyframes pulse {
-  from {
-    -webkit-transform: scale3d(1, 1, 1);
-    transform: scale3d(1, 1, 1);
-  }
-
-  50% {
-    -webkit-transform: scale3d(1.05, 1.05, 1.05);
-    transform: scale3d(1.05, 1.05, 1.05);
-  }
-
-  to {
-    -webkit-transform: scale3d(1, 1, 1);
-    transform: scale3d(1, 1, 1);
-  }
-}
-
-
-@keyframes pulse {
-  from {
-    -webkit-transform: scale3d(1, 1, 1);
-    transform: scale3d(1, 1, 1);
-  }
-
-  50% {
-    -webkit-transform: scale3d(1.05, 1.05, 1.05);
-    transform: scale3d(1.05, 1.05, 1.05);
-  }
-
-  to {
-    -webkit-transform: scale3d(1, 1, 1);
-    transform: scale3d(1, 1, 1);
-  }
-}
-
-
-.pulse {
-  -webkit-animation-name: pulse;
-  animation-name: pulse;
-}
-
-@-webkit-keyframes rubberBand {
-  from {
-    -webkit-transform: scale3d(1, 1, 1);
-    transform: scale3d(1, 1, 1);
-  }
-
-  30% {
-    -webkit-transform: scale3d(1.25, 0.75, 1);
-    transform: scale3d(1.25, 0.75, 1);
-  }
-
-  40% {
-    -webkit-transform: scale3d(0.75, 1.25, 1);
-    transform: scale3d(0.75, 1.25, 1);
-  }
-
-  50% {
-    -webkit-transform: scale3d(1.15, 0.85, 1);
-    transform: scale3d(1.15, 0.85, 1);
-  }
-
-  65% {
-    -webkit-transform: scale3d(0.95, 1.05, 1);
-    transform: scale3d(0.95, 1.05, 1);
-  }
-
-  75% {
-    -webkit-transform: scale3d(1.05, 0.95, 1);
-    transform: scale3d(1.05, 0.95, 1);
-  }
-
-  to {
-    -webkit-transform: scale3d(1, 1, 1);
-    transform: scale3d(1, 1, 1);
-  }
-}
-
-
-@keyframes rubberBand {
-  from {
-    -webkit-transform: scale3d(1, 1, 1);
-    transform: scale3d(1, 1, 1);
-  }
-
-  30% {
-    -webkit-transform: scale3d(1.25, 0.75, 1);
-    transform: scale3d(1.25, 0.75, 1);
-  }
-
-  40% {
-    -webkit-transform: scale3d(0.75, 1.25, 1);
-    transform: scale3d(0.75, 1.25, 1);
-  }
-
-  50% {
-    -webkit-transform: scale3d(1.15, 0.85, 1);
-    transform: scale3d(1.15, 0.85, 1);
-  }
-
-  65% {
-    -webkit-transform: scale3d(0.95, 1.05, 1);
-    transform: scale3d(0.95, 1.05, 1);
-  }
-
-  75% {
-    -webkit-transform: scale3d(1.05, 0.95, 1);
-    transform: scale3d(1.05, 0.95, 1);
-  }
-
-  to {
-    -webkit-transform: scale3d(1, 1, 1);
-    transform: scale3d(1, 1, 1);
-  }
-}
-
-
-.rubberBand {
-  -webkit-animation-name: rubberBand;
-  animation-name: rubberBand;
-}
-
-@-webkit-keyframes shake {
-  from, to {
-    -webkit-transform: translate3d(0, 0, 0);
-    transform: translate3d(0, 0, 0);
-  }
-
-  10%, 30%, 50%, 70%, 90% {
-    -webkit-transform: translate3d(-10px, 0, 0);
-    transform: translate3d(-10px, 0, 0);
-  }
-
-  20%, 40%, 60%, 80% {
-    -webkit-transform: translate3d(10px, 0, 0);
-    transform: translate3d(10px, 0, 0);
-  }
-}
-
-
-@keyframes shake {
-  from, to {
-    -webkit-transform: translate3d(0, 0, 0);
-    transform: translate3d(0, 0, 0);
-  }
-
-  10%, 30%, 50%, 70%, 90% {
-    -webkit-transform: translate3d(-10px, 0, 0);
-    transform: translate3d(-10px, 0, 0);
-  }
-
-  20%, 40%, 60%, 80% {
-    -webkit-transform: translate3d(10px, 0, 0);
-    transform: translate3d(10px, 0, 0);
-  }
-}
-
-
-.shake {
-  -webkit-animation-name: shake;
-  animation-name: shake;
-}
-
-@-webkit-keyframes headShake {
-  0% {
-    -webkit-transform: translateX(0);
-    transform: translateX(0);
-  }
-
-  6.5% {
-    -webkit-transform: translateX(-6px) rotateY(-9deg);
-    transform: translateX(-6px) rotateY(-9deg);
-  }
-
-  18.5% {
-    -webkit-transform: translateX(5px) rotateY(7deg);
-    transform: translateX(5px) rotateY(7deg);
-  }
-
-  31.5% {
-    -webkit-transform: translateX(-3px) rotateY(-5deg);
-    transform: translateX(-3px) rotateY(-5deg);
-  }
-
-  43.5% {
-    -webkit-transform: translateX(2px) rotateY(3deg);
-    transform: translateX(2px) rotateY(3deg);
-  }
-
-  50% {
-    -webkit-transform: translateX(0);
-    transform: translateX(0);
-  }
-}
-
-
-@keyframes headShake {
-  0% {
-    -webkit-transform: translateX(0);
-    transform: translateX(0);
-  }
-
-  6.5% {
-    -webkit-transform: translateX(-6px) rotateY(-9deg);
-    transform: translateX(-6px) rotateY(-9deg);
-  }
-
-  18.5% {
-    -webkit-transform: translateX(5px) rotateY(7deg);
-    transform: translateX(5px) rotateY(7deg);
-  }
-
-  31.5% {
-    -webkit-transform: translateX(-3px) rotateY(-5deg);
-    transform: translateX(-3px) rotateY(-5deg);
-  }
-
-  43.5% {
-    -webkit-transform: translateX(2px) rotateY(3deg);
-    transform: translateX(2px) rotateY(3deg);
-  }
-
-  50% {
-    -webkit-transform: translateX(0);
-    transform: translateX(0);
-  }
-}
-
-
-.headShake {
-  -webkit-animation-timing-function: ease-in-out;
-  animation-timing-function: ease-in-out;
-  -webkit-animation-name: headShake;
-  animation-name: headShake;
-}
-
-@-webkit-keyframes swing {
-  20% {
-    -webkit-transform: rotate3d(0, 0, 1, 15deg);
-    transform: rotate3d(0, 0, 1, 15deg);
-  }
-
-  40% {
-    -webkit-transform: rotate3d(0, 0, 1, -10deg);
-    transform: rotate3d(0, 0, 1, -10deg);
-  }
-
-  60% {
-    -webkit-transform: rotate3d(0, 0, 1, 5deg);
-    transform: rotate3d(0, 0, 1, 5deg);
-  }
-
-  80% {
-    -webkit-transform: rotate3d(0, 0, 1, -5deg);
-    transform: rotate3d(0, 0, 1, -5deg);
-  }
-
-  to {
-    -webkit-transform: rotate3d(0, 0, 1, 0deg);
-    transform: rotate3d(0, 0, 1, 0deg);
-  }
-}
-
-
-@keyframes swing {
-  20% {
-    -webkit-transform: rotate3d(0, 0, 1, 15deg);
-    transform: rotate3d(0, 0, 1, 15deg);
-  }
-
-  40% {
-    -webkit-transform: rotate3d(0, 0, 1, -10deg);
-    transform: rotate3d(0, 0, 1, -10deg);
-  }
-
-  60% {
-    -webkit-transform: rotate3d(0, 0, 1, 5deg);
-    transform: rotate3d(0, 0, 1, 5deg);
-  }
-
-  80% {
-    -webkit-transform: rotate3d(0, 0, 1, -5deg);
-    transform: rotate3d(0, 0, 1, -5deg);
-  }
-
-  to {
-    -webkit-transform: rotate3d(0, 0, 1, 0deg);
-    transform: rotate3d(0, 0, 1, 0deg);
-  }
-}
-
-
-.swing {
-  -webkit-transform-origin: top center;
-  transform-origin: top center;
-  -webkit-animation-name: swing;
-  animation-name: swing;
-}
-
-@-webkit-keyframes tada {
-  from {
-    -webkit-transform: scale3d(1, 1, 1);
-    transform: scale3d(1, 1, 1);
-  }
-
-  10%, 20% {
-    -webkit-transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg);
-    transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg);
-  }
-
-  30%, 50%, 70%, 90% {
-    -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg);
-    transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg);
-  }
-
-  40%, 60%, 80% {
-    -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg);
-    transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg);
-  }
-
-  to {
-    -webkit-transform: scale3d(1, 1, 1);
-    transform: scale3d(1, 1, 1);
-  }
-}
-
-
-@keyframes tada {
-  from {
-    -webkit-transform: scale3d(1, 1, 1);
-    transform: scale3d(1, 1, 1);
-  }
-
-  10%, 20% {
-    -webkit-transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg);
-    transform: scale3d(0.9, 0.9, 0.9) rotate3d(0, 0, 1, -3deg);
-  }
-
-  30%, 50%, 70%, 90% {
-    -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg);
-    transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, 3deg);
-  }
-
-  40%, 60%, 80% {
-    -webkit-transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg);
-    transform: scale3d(1.1, 1.1, 1.1) rotate3d(0, 0, 1, -3deg);
-  }
-
-  to {
-    -webkit-transform: scale3d(1, 1, 1);
-    transform: scale3d(1, 1, 1);
-  }
-}
-
-
-.tada {
-  -webkit-animation-name: tada;
-  animation-name: tada;
-}
-
-/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */
-
-@-webkit-keyframes wobble {
-  from {
-    -webkit-transform: none;
-    transform: none;
-  }
-
-  15% {
-    -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);
-    transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);
-  }
-
-  30% {
-    -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);
-    transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);
-  }
-
-  45% {
-    -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);
-    transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);
-  }
-
-  60% {
-    -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);
-    transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);
-  }
-
-  75% {
-    -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);
-    transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);
-  }
-
-  to {
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-@keyframes wobble {
-  from {
-    -webkit-transform: none;
-    transform: none;
-  }
-
-  15% {
-    -webkit-transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);
-    transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg);
-  }
-
-  30% {
-    -webkit-transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);
-    transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg);
-  }
-
-  45% {
-    -webkit-transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);
-    transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg);
-  }
-
-  60% {
-    -webkit-transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);
-    transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg);
-  }
-
-  75% {
-    -webkit-transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);
-    transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg);
-  }
-
-  to {
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-.wobble {
-  -webkit-animation-name: wobble;
-  animation-name: wobble;
-}
-
-@-webkit-keyframes jello {
-  from, 11.1%, to {
-    -webkit-transform: none;
-    transform: none;
-  }
-
-  22.2% {
-    -webkit-transform: skewX(-12.5deg) skewY(-12.5deg);
-    transform: skewX(-12.5deg) skewY(-12.5deg);
-  }
-
-  33.3% {
-    -webkit-transform: skewX(6.25deg) skewY(6.25deg);
-    transform: skewX(6.25deg) skewY(6.25deg);
-  }
-
-  44.4% {
-    -webkit-transform: skewX(-3.125deg) skewY(-3.125deg);
-    transform: skewX(-3.125deg) skewY(-3.125deg);
-  }
-
-  55.5% {
-    -webkit-transform: skewX(1.5625deg) skewY(1.5625deg);
-    transform: skewX(1.5625deg) skewY(1.5625deg);
-  }
-
-  66.6% {
-    -webkit-transform: skewX(-0.78125deg) skewY(-0.78125deg);
-    transform: skewX(-0.78125deg) skewY(-0.78125deg);
-  }
-
-  77.7% {
-    -webkit-transform: skewX(0.39063deg) skewY(0.39063deg);
-    transform: skewX(0.39063deg) skewY(0.39063deg);
-  }
-
-  88.8% {
-    -webkit-transform: skewX(-0.19531deg) skewY(-0.19531deg);
-    transform: skewX(-0.19531deg) skewY(-0.19531deg);
-  }
-}
-
-
-@keyframes jello {
-  from, 11.1%, to {
-    -webkit-transform: none;
-    transform: none;
-  }
-
-  22.2% {
-    -webkit-transform: skewX(-12.5deg) skewY(-12.5deg);
-    transform: skewX(-12.5deg) skewY(-12.5deg);
-  }
-
-  33.3% {
-    -webkit-transform: skewX(6.25deg) skewY(6.25deg);
-    transform: skewX(6.25deg) skewY(6.25deg);
-  }
-
-  44.4% {
-    -webkit-transform: skewX(-3.125deg) skewY(-3.125deg);
-    transform: skewX(-3.125deg) skewY(-3.125deg);
-  }
-
-  55.5% {
-    -webkit-transform: skewX(1.5625deg) skewY(1.5625deg);
-    transform: skewX(1.5625deg) skewY(1.5625deg);
-  }
-
-  66.6% {
-    -webkit-transform: skewX(-0.78125deg) skewY(-0.78125deg);
-    transform: skewX(-0.78125deg) skewY(-0.78125deg);
-  }
-
-  77.7% {
-    -webkit-transform: skewX(0.39063deg) skewY(0.39063deg);
-    transform: skewX(0.39063deg) skewY(0.39063deg);
-  }
-
-  88.8% {
-    -webkit-transform: skewX(-0.19531deg) skewY(-0.19531deg);
-    transform: skewX(-0.19531deg) skewY(-0.19531deg);
-  }
-}
-
-
-.jello {
-  -webkit-animation-name: jello;
-  animation-name: jello;
-  -webkit-transform-origin: center;
-  transform-origin: center;
-}
-
-@-webkit-keyframes bounceIn {
-  from, 20%, 40%, 60%, 80%, to {
-    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-  }
-
-  0% {
-    opacity: 0;
-    -webkit-transform: scale3d(0.3, 0.3, 0.3);
-    transform: scale3d(0.3, 0.3, 0.3);
-  }
-
-  20% {
-    -webkit-transform: scale3d(1.1, 1.1, 1.1);
-    transform: scale3d(1.1, 1.1, 1.1);
-  }
-
-  40% {
-    -webkit-transform: scale3d(0.9, 0.9, 0.9);
-    transform: scale3d(0.9, 0.9, 0.9);
-  }
-
-  60% {
-    opacity: 1;
-    -webkit-transform: scale3d(1.03, 1.03, 1.03);
-    transform: scale3d(1.03, 1.03, 1.03);
-  }
-
-  80% {
-    -webkit-transform: scale3d(0.97, 0.97, 0.97);
-    transform: scale3d(0.97, 0.97, 0.97);
-  }
-
-  to {
-    opacity: 1;
-    -webkit-transform: scale3d(1, 1, 1);
-    transform: scale3d(1, 1, 1);
-  }
-}
-
-
-@keyframes bounceIn {
-  from, 20%, 40%, 60%, 80%, to {
-    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-  }
-
-  0% {
-    opacity: 0;
-    -webkit-transform: scale3d(0.3, 0.3, 0.3);
-    transform: scale3d(0.3, 0.3, 0.3);
-  }
-
-  20% {
-    -webkit-transform: scale3d(1.1, 1.1, 1.1);
-    transform: scale3d(1.1, 1.1, 1.1);
-  }
-
-  40% {
-    -webkit-transform: scale3d(0.9, 0.9, 0.9);
-    transform: scale3d(0.9, 0.9, 0.9);
-  }
-
-  60% {
-    opacity: 1;
-    -webkit-transform: scale3d(1.03, 1.03, 1.03);
-    transform: scale3d(1.03, 1.03, 1.03);
-  }
-
-  80% {
-    -webkit-transform: scale3d(0.97, 0.97, 0.97);
-    transform: scale3d(0.97, 0.97, 0.97);
-  }
-
-  to {
-    opacity: 1;
-    -webkit-transform: scale3d(1, 1, 1);
-    transform: scale3d(1, 1, 1);
-  }
-}
-
-
-.bounceIn {
-  -webkit-animation-name: bounceIn;
-  animation-name: bounceIn;
-}
-
-@-webkit-keyframes bounceInDown {
-  from, 60%, 75%, 90%, to {
-    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-  }
-
-  0% {
-    opacity: 0;
-    -webkit-transform: translate3d(0, -3000px, 0);
-    transform: translate3d(0, -3000px, 0);
-  }
-
-  60% {
-    opacity: 1;
-    -webkit-transform: translate3d(0, 25px, 0);
-    transform: translate3d(0, 25px, 0);
-  }
-
-  75% {
-    -webkit-transform: translate3d(0, -10px, 0);
-    transform: translate3d(0, -10px, 0);
-  }
-
-  90% {
-    -webkit-transform: translate3d(0, 5px, 0);
-    transform: translate3d(0, 5px, 0);
-  }
-
-  to {
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-@keyframes bounceInDown {
-  from, 60%, 75%, 90%, to {
-    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-  }
-
-  0% {
-    opacity: 0;
-    -webkit-transform: translate3d(0, -3000px, 0);
-    transform: translate3d(0, -3000px, 0);
-  }
-
-  60% {
-    opacity: 1;
-    -webkit-transform: translate3d(0, 25px, 0);
-    transform: translate3d(0, 25px, 0);
-  }
-
-  75% {
-    -webkit-transform: translate3d(0, -10px, 0);
-    transform: translate3d(0, -10px, 0);
-  }
-
-  90% {
-    -webkit-transform: translate3d(0, 5px, 0);
-    transform: translate3d(0, 5px, 0);
-  }
-
-  to {
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-.bounceInDown {
-  -webkit-animation-name: bounceInDown;
-  animation-name: bounceInDown;
-}
-
-@-webkit-keyframes bounceInLeft {
-  from, 60%, 75%, 90%, to {
-    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-  }
-
-  0% {
-    opacity: 0;
-    -webkit-transform: translate3d(-3000px, 0, 0);
-    transform: translate3d(-3000px, 0, 0);
-  }
-
-  60% {
-    opacity: 1;
-    -webkit-transform: translate3d(25px, 0, 0);
-    transform: translate3d(25px, 0, 0);
-  }
-
-  75% {
-    -webkit-transform: translate3d(-10px, 0, 0);
-    transform: translate3d(-10px, 0, 0);
-  }
-
-  90% {
-    -webkit-transform: translate3d(5px, 0, 0);
-    transform: translate3d(5px, 0, 0);
-  }
-
-  to {
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-@keyframes bounceInLeft {
-  from, 60%, 75%, 90%, to {
-    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-  }
-
-  0% {
-    opacity: 0;
-    -webkit-transform: translate3d(-3000px, 0, 0);
-    transform: translate3d(-3000px, 0, 0);
-  }
-
-  60% {
-    opacity: 1;
-    -webkit-transform: translate3d(25px, 0, 0);
-    transform: translate3d(25px, 0, 0);
-  }
-
-  75% {
-    -webkit-transform: translate3d(-10px, 0, 0);
-    transform: translate3d(-10px, 0, 0);
-  }
-
-  90% {
-    -webkit-transform: translate3d(5px, 0, 0);
-    transform: translate3d(5px, 0, 0);
-  }
-
-  to {
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-.bounceInLeft {
-  -webkit-animation-name: bounceInLeft;
-  animation-name: bounceInLeft;
-}
-
-@-webkit-keyframes bounceInRight {
-  from, 60%, 75%, 90%, to {
-    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-  }
-
-  from {
-    opacity: 0;
-    -webkit-transform: translate3d(3000px, 0, 0);
-    transform: translate3d(3000px, 0, 0);
-  }
-
-  60% {
-    opacity: 1;
-    -webkit-transform: translate3d(-25px, 0, 0);
-    transform: translate3d(-25px, 0, 0);
-  }
-
-  75% {
-    -webkit-transform: translate3d(10px, 0, 0);
-    transform: translate3d(10px, 0, 0);
-  }
-
-  90% {
-    -webkit-transform: translate3d(-5px, 0, 0);
-    transform: translate3d(-5px, 0, 0);
-  }
-
-  to {
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-@keyframes bounceInRight {
-  from, 60%, 75%, 90%, to {
-    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-  }
-
-  from {
-    opacity: 0;
-    -webkit-transform: translate3d(3000px, 0, 0);
-    transform: translate3d(3000px, 0, 0);
-  }
-
-  60% {
-    opacity: 1;
-    -webkit-transform: translate3d(-25px, 0, 0);
-    transform: translate3d(-25px, 0, 0);
-  }
-
-  75% {
-    -webkit-transform: translate3d(10px, 0, 0);
-    transform: translate3d(10px, 0, 0);
-  }
-
-  90% {
-    -webkit-transform: translate3d(-5px, 0, 0);
-    transform: translate3d(-5px, 0, 0);
-  }
-
-  to {
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-.bounceInRight {
-  -webkit-animation-name: bounceInRight;
-  animation-name: bounceInRight;
-}
-
-@-webkit-keyframes bounceInUp {
-  from, 60%, 75%, 90%, to {
-    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-  }
-
-  from {
-    opacity: 0;
-    -webkit-transform: translate3d(0, 3000px, 0);
-    transform: translate3d(0, 3000px, 0);
-  }
-
-  60% {
-    opacity: 1;
-    -webkit-transform: translate3d(0, -20px, 0);
-    transform: translate3d(0, -20px, 0);
-  }
-
-  75% {
-    -webkit-transform: translate3d(0, 10px, 0);
-    transform: translate3d(0, 10px, 0);
-  }
-
-  90% {
-    -webkit-transform: translate3d(0, -5px, 0);
-    transform: translate3d(0, -5px, 0);
-  }
-
-  to {
-    -webkit-transform: translate3d(0, 0, 0);
-    transform: translate3d(0, 0, 0);
-  }
-}
-
-
-@keyframes bounceInUp {
-  from, 60%, 75%, 90%, to {
-    -webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-    animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
-  }
-
-  from {
-    opacity: 0;
-    -webkit-transform: translate3d(0, 3000px, 0);
-    transform: translate3d(0, 3000px, 0);
-  }
-
-  60% {
-    opacity: 1;
-    -webkit-transform: translate3d(0, -20px, 0);
-    transform: translate3d(0, -20px, 0);
-  }
-
-  75% {
-    -webkit-transform: translate3d(0, 10px, 0);
-    transform: translate3d(0, 10px, 0);
-  }
-
-  90% {
-    -webkit-transform: translate3d(0, -5px, 0);
-    transform: translate3d(0, -5px, 0);
-  }
-
-  to {
-    -webkit-transform: translate3d(0, 0, 0);
-    transform: translate3d(0, 0, 0);
-  }
-}
-
-
-.bounceInUp {
-  -webkit-animation-name: bounceInUp;
-  animation-name: bounceInUp;
-}
-
-@-webkit-keyframes bounceOut {
-  20% {
-    -webkit-transform: scale3d(0.9, 0.9, 0.9);
-    transform: scale3d(0.9, 0.9, 0.9);
-  }
-
-  50%, 55% {
-    opacity: 1;
-    -webkit-transform: scale3d(1.1, 1.1, 1.1);
-    transform: scale3d(1.1, 1.1, 1.1);
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: scale3d(0.3, 0.3, 0.3);
-    transform: scale3d(0.3, 0.3, 0.3);
-  }
-}
-
-
-@keyframes bounceOut {
-  20% {
-    -webkit-transform: scale3d(0.9, 0.9, 0.9);
-    transform: scale3d(0.9, 0.9, 0.9);
-  }
-
-  50%, 55% {
-    opacity: 1;
-    -webkit-transform: scale3d(1.1, 1.1, 1.1);
-    transform: scale3d(1.1, 1.1, 1.1);
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: scale3d(0.3, 0.3, 0.3);
-    transform: scale3d(0.3, 0.3, 0.3);
-  }
-}
-
-
-.bounceOut {
-  -webkit-animation-name: bounceOut;
-  animation-name: bounceOut;
-}
-
-@-webkit-keyframes bounceOutDown {
-  20% {
-    -webkit-transform: translate3d(0, 10px, 0);
-    transform: translate3d(0, 10px, 0);
-  }
-
-  40%, 45% {
-    opacity: 1;
-    -webkit-transform: translate3d(0, -20px, 0);
-    transform: translate3d(0, -20px, 0);
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(0, 2000px, 0);
-    transform: translate3d(0, 2000px, 0);
-  }
-}
-
-
-@keyframes bounceOutDown {
-  20% {
-    -webkit-transform: translate3d(0, 10px, 0);
-    transform: translate3d(0, 10px, 0);
-  }
-
-  40%, 45% {
-    opacity: 1;
-    -webkit-transform: translate3d(0, -20px, 0);
-    transform: translate3d(0, -20px, 0);
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(0, 2000px, 0);
-    transform: translate3d(0, 2000px, 0);
-  }
-}
-
-
-.bounceOutDown {
-  -webkit-animation-name: bounceOutDown;
-  animation-name: bounceOutDown;
-}
-
-@-webkit-keyframes bounceOutLeft {
-  20% {
-    opacity: 1;
-    -webkit-transform: translate3d(20px, 0, 0);
-    transform: translate3d(20px, 0, 0);
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(-2000px, 0, 0);
-    transform: translate3d(-2000px, 0, 0);
-  }
-}
-
-
-@keyframes bounceOutLeft {
-  20% {
-    opacity: 1;
-    -webkit-transform: translate3d(20px, 0, 0);
-    transform: translate3d(20px, 0, 0);
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(-2000px, 0, 0);
-    transform: translate3d(-2000px, 0, 0);
-  }
-}
-
-
-.bounceOutLeft {
-  -webkit-animation-name: bounceOutLeft;
-  animation-name: bounceOutLeft;
-}
-
-@-webkit-keyframes bounceOutRight {
-  20% {
-    opacity: 1;
-    -webkit-transform: translate3d(-20px, 0, 0);
-    transform: translate3d(-20px, 0, 0);
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(2000px, 0, 0);
-    transform: translate3d(2000px, 0, 0);
-  }
-}
-
-
-@keyframes bounceOutRight {
-  20% {
-    opacity: 1;
-    -webkit-transform: translate3d(-20px, 0, 0);
-    transform: translate3d(-20px, 0, 0);
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(2000px, 0, 0);
-    transform: translate3d(2000px, 0, 0);
-  }
-}
-
-
-.bounceOutRight {
-  -webkit-animation-name: bounceOutRight;
-  animation-name: bounceOutRight;
-}
-
-@-webkit-keyframes bounceOutUp {
-  20% {
-    -webkit-transform: translate3d(0, -10px, 0);
-    transform: translate3d(0, -10px, 0);
-  }
-
-  40%, 45% {
-    opacity: 1;
-    -webkit-transform: translate3d(0, 20px, 0);
-    transform: translate3d(0, 20px, 0);
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(0, -2000px, 0);
-    transform: translate3d(0, -2000px, 0);
-  }
-}
-
-
-@keyframes bounceOutUp {
-  20% {
-    -webkit-transform: translate3d(0, -10px, 0);
-    transform: translate3d(0, -10px, 0);
-  }
-
-  40%, 45% {
-    opacity: 1;
-    -webkit-transform: translate3d(0, 20px, 0);
-    transform: translate3d(0, 20px, 0);
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(0, -2000px, 0);
-    transform: translate3d(0, -2000px, 0);
-  }
-}
-
-
-.bounceOutUp {
-  -webkit-animation-name: bounceOutUp;
-  animation-name: bounceOutUp;
-}
-
-@-webkit-keyframes fadeIn {
-  from {
-    opacity: 0;
-  }
-
-  to {
-    opacity: 1;
-  }
-}
-
-
-@keyframes fadeIn {
-  from {
-    opacity: 0;
-  }
-
-  to {
-    opacity: 1;
-  }
-}
-
-
-.fadeIn {
-  -webkit-animation-name: fadeIn;
-  animation-name: fadeIn;
-}
-
-@-webkit-keyframes fadeInDown {
-  from {
-    opacity: 0;
-    -webkit-transform: translate3d(0, -100%, 0);
-    transform: translate3d(0, -100%, 0);
-  }
-
-  to {
-    opacity: 1;
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-@keyframes fadeInDown {
-  from {
-    opacity: 0;
-    -webkit-transform: translate3d(0, -100%, 0);
-    transform: translate3d(0, -100%, 0);
-  }
-
-  to {
-    opacity: 1;
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-.fadeInDown {
-  -webkit-animation-name: fadeInDown;
-  animation-name: fadeInDown;
-}
-
-@-webkit-keyframes fadeInDownBig {
-  from {
-    opacity: 0;
-    -webkit-transform: translate3d(0, -2000px, 0);
-    transform: translate3d(0, -2000px, 0);
-  }
-
-  to {
-    opacity: 1;
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-@keyframes fadeInDownBig {
-  from {
-    opacity: 0;
-    -webkit-transform: translate3d(0, -2000px, 0);
-    transform: translate3d(0, -2000px, 0);
-  }
-
-  to {
-    opacity: 1;
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-.fadeInDownBig {
-  -webkit-animation-name: fadeInDownBig;
-  animation-name: fadeInDownBig;
-}
-
-@-webkit-keyframes fadeInLeft {
-  from {
-    opacity: 0;
-    -webkit-transform: translate3d(-100%, 0, 0);
-    transform: translate3d(-100%, 0, 0);
-  }
-
-  to {
-    opacity: 1;
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-@keyframes fadeInLeft {
-  from {
-    opacity: 0;
-    -webkit-transform: translate3d(-100%, 0, 0);
-    transform: translate3d(-100%, 0, 0);
-  }
-
-  to {
-    opacity: 1;
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-.fadeInLeft {
-  -webkit-animation-name: fadeInLeft;
-  animation-name: fadeInLeft;
-}
-
-@-webkit-keyframes fadeInLeftBig {
-  from {
-    opacity: 0;
-    -webkit-transform: translate3d(-2000px, 0, 0);
-    transform: translate3d(-2000px, 0, 0);
-  }
-
-  to {
-    opacity: 1;
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-@keyframes fadeInLeftBig {
-  from {
-    opacity: 0;
-    -webkit-transform: translate3d(-2000px, 0, 0);
-    transform: translate3d(-2000px, 0, 0);
-  }
-
-  to {
-    opacity: 1;
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-.fadeInLeftBig {
-  -webkit-animation-name: fadeInLeftBig;
-  animation-name: fadeInLeftBig;
-}
-
-@-webkit-keyframes fadeInRight {
-  from {
-    opacity: 0;
-    -webkit-transform: translate3d(100%, 0, 0);
-    transform: translate3d(100%, 0, 0);
-  }
-
-  to {
-    opacity: 1;
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-@keyframes fadeInRight {
-  from {
-    opacity: 0;
-    -webkit-transform: translate3d(100%, 0, 0);
-    transform: translate3d(100%, 0, 0);
-  }
-
-  to {
-    opacity: 1;
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-.fadeInRight {
-  -webkit-animation-name: fadeInRight;
-  animation-name: fadeInRight;
-}
-
-@-webkit-keyframes fadeInRightBig {
-  from {
-    opacity: 0;
-    -webkit-transform: translate3d(2000px, 0, 0);
-    transform: translate3d(2000px, 0, 0);
-  }
-
-  to {
-    opacity: 1;
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-@keyframes fadeInRightBig {
-  from {
-    opacity: 0;
-    -webkit-transform: translate3d(2000px, 0, 0);
-    transform: translate3d(2000px, 0, 0);
-  }
-
-  to {
-    opacity: 1;
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-.fadeInRightBig {
-  -webkit-animation-name: fadeInRightBig;
-  animation-name: fadeInRightBig;
-}
-
-@-webkit-keyframes fadeInUp {
-  from {
-    opacity: 0;
-    -webkit-transform: translate3d(0, 100%, 0);
-    transform: translate3d(0, 100%, 0);
-  }
-
-  to {
-    opacity: 1;
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-@keyframes fadeInUp {
-  from {
-    opacity: 0;
-    -webkit-transform: translate3d(0, 100%, 0);
-    transform: translate3d(0, 100%, 0);
-  }
-
-  to {
-    opacity: 1;
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-.fadeInUp {
-  -webkit-animation-name: fadeInUp;
-  animation-name: fadeInUp;
-}
-
-@-webkit-keyframes fadeInUpBig {
-  from {
-    opacity: 0;
-    -webkit-transform: translate3d(0, 2000px, 0);
-    transform: translate3d(0, 2000px, 0);
-  }
-
-  to {
-    opacity: 1;
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-@keyframes fadeInUpBig {
-  from {
-    opacity: 0;
-    -webkit-transform: translate3d(0, 2000px, 0);
-    transform: translate3d(0, 2000px, 0);
-  }
-
-  to {
-    opacity: 1;
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-.fadeInUpBig {
-  -webkit-animation-name: fadeInUpBig;
-  animation-name: fadeInUpBig;
-}
-
-@-webkit-keyframes fadeOut {
-  from {
-    opacity: 1;
-  }
-
-  to {
-    opacity: 0;
-  }
-}
-
-
-@keyframes fadeOut {
-  from {
-    opacity: 1;
-  }
-
-  to {
-    opacity: 0;
-  }
-}
-
-
-.fadeOut {
-  -webkit-animation-name: fadeOut;
-  animation-name: fadeOut;
-}
-
-@-webkit-keyframes fadeOutDown {
-  from {
-    opacity: 1;
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(0, 100%, 0);
-    transform: translate3d(0, 100%, 0);
-  }
-}
-
-
-@keyframes fadeOutDown {
-  from {
-    opacity: 1;
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(0, 100%, 0);
-    transform: translate3d(0, 100%, 0);
-  }
-}
-
-
-.fadeOutDown {
-  -webkit-animation-name: fadeOutDown;
-  animation-name: fadeOutDown;
-}
-
-@-webkit-keyframes fadeOutDownBig {
-  from {
-    opacity: 1;
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(0, 2000px, 0);
-    transform: translate3d(0, 2000px, 0);
-  }
-}
-
-
-@keyframes fadeOutDownBig {
-  from {
-    opacity: 1;
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(0, 2000px, 0);
-    transform: translate3d(0, 2000px, 0);
-  }
-}
-
-
-.fadeOutDownBig {
-  -webkit-animation-name: fadeOutDownBig;
-  animation-name: fadeOutDownBig;
-}
-
-@-webkit-keyframes fadeOutLeft {
-  from {
-    opacity: 1;
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(-100%, 0, 0);
-    transform: translate3d(-100%, 0, 0);
-  }
-}
-
-
-@keyframes fadeOutLeft {
-  from {
-    opacity: 1;
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(-100%, 0, 0);
-    transform: translate3d(-100%, 0, 0);
-  }
-}
-
-
-.fadeOutLeft {
-  -webkit-animation-name: fadeOutLeft;
-  animation-name: fadeOutLeft;
-}
-
-@-webkit-keyframes fadeOutLeftBig {
-  from {
-    opacity: 1;
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(-2000px, 0, 0);
-    transform: translate3d(-2000px, 0, 0);
-  }
-}
-
-
-@keyframes fadeOutLeftBig {
-  from {
-    opacity: 1;
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(-2000px, 0, 0);
-    transform: translate3d(-2000px, 0, 0);
-  }
-}
-
-
-.fadeOutLeftBig {
-  -webkit-animation-name: fadeOutLeftBig;
-  animation-name: fadeOutLeftBig;
-}
-
-@-webkit-keyframes fadeOutRight {
-  from {
-    opacity: 1;
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(100%, 0, 0);
-    transform: translate3d(100%, 0, 0);
-  }
-}
-
-
-@keyframes fadeOutRight {
-  from {
-    opacity: 1;
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(100%, 0, 0);
-    transform: translate3d(100%, 0, 0);
-  }
-}
-
-
-.fadeOutRight {
-  -webkit-animation-name: fadeOutRight;
-  animation-name: fadeOutRight;
-}
-
-@-webkit-keyframes fadeOutRightBig {
-  from {
-    opacity: 1;
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(2000px, 0, 0);
-    transform: translate3d(2000px, 0, 0);
-  }
-}
-
-
-@keyframes fadeOutRightBig {
-  from {
-    opacity: 1;
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(2000px, 0, 0);
-    transform: translate3d(2000px, 0, 0);
-  }
-}
-
-
-.fadeOutRightBig {
-  -webkit-animation-name: fadeOutRightBig;
-  animation-name: fadeOutRightBig;
-}
-
-@-webkit-keyframes fadeOutUp {
-  from {
-    opacity: 1;
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(0, -100%, 0);
-    transform: translate3d(0, -100%, 0);
-  }
-}
-
-
-@keyframes fadeOutUp {
-  from {
-    opacity: 1;
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(0, -100%, 0);
-    transform: translate3d(0, -100%, 0);
-  }
-}
-
-
-.fadeOutUp {
-  -webkit-animation-name: fadeOutUp;
-  animation-name: fadeOutUp;
-}
-
-@-webkit-keyframes fadeOutUpBig {
-  from {
-    opacity: 1;
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(0, -2000px, 0);
-    transform: translate3d(0, -2000px, 0);
-  }
-}
-
-
-@keyframes fadeOutUpBig {
-  from {
-    opacity: 1;
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(0, -2000px, 0);
-    transform: translate3d(0, -2000px, 0);
-  }
-}
-
-
-.fadeOutUpBig {
-  -webkit-animation-name: fadeOutUpBig;
-  animation-name: fadeOutUpBig;
-}
-
-@-webkit-keyframes flip {
-  from {
-    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -360deg);
-    transform: perspective(400px) rotate3d(0, 1, 0, -360deg);
-    -webkit-animation-timing-function: ease-out;
-    animation-timing-function: ease-out;
-  }
-
-  40% {
-    -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg);
-    transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg);
-    -webkit-animation-timing-function: ease-out;
-    animation-timing-function: ease-out;
-  }
-
-  50% {
-    -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg);
-    transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg);
-    -webkit-animation-timing-function: ease-in;
-    animation-timing-function: ease-in;
-  }
-
-  80% {
-    -webkit-transform: perspective(400px) scale3d(0.95, 0.95, 0.95);
-    transform: perspective(400px) scale3d(0.95, 0.95, 0.95);
-    -webkit-animation-timing-function: ease-in;
-    animation-timing-function: ease-in;
-  }
-
-  to {
-    -webkit-transform: perspective(400px);
-    transform: perspective(400px);
-    -webkit-animation-timing-function: ease-in;
-    animation-timing-function: ease-in;
-  }
-}
-
-
-@keyframes flip {
-  from {
-    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -360deg);
-    transform: perspective(400px) rotate3d(0, 1, 0, -360deg);
-    -webkit-animation-timing-function: ease-out;
-    animation-timing-function: ease-out;
-  }
-
-  40% {
-    -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg);
-    transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -190deg);
-    -webkit-animation-timing-function: ease-out;
-    animation-timing-function: ease-out;
-  }
-
-  50% {
-    -webkit-transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg);
-    transform: perspective(400px) translate3d(0, 0, 150px) rotate3d(0, 1, 0, -170deg);
-    -webkit-animation-timing-function: ease-in;
-    animation-timing-function: ease-in;
-  }
-
-  80% {
-    -webkit-transform: perspective(400px) scale3d(0.95, 0.95, 0.95);
-    transform: perspective(400px) scale3d(0.95, 0.95, 0.95);
-    -webkit-animation-timing-function: ease-in;
-    animation-timing-function: ease-in;
-  }
-
-  to {
-    -webkit-transform: perspective(400px);
-    transform: perspective(400px);
-    -webkit-animation-timing-function: ease-in;
-    animation-timing-function: ease-in;
-  }
-}
-
-
-.animated.flip {
-  -webkit-backface-visibility: visible;
-  backface-visibility: visible;
-  -webkit-animation-name: flip;
-  animation-name: flip;
-}
-
-@-webkit-keyframes flipInX {
-  from {
-    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
-    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
-    -webkit-animation-timing-function: ease-in;
-    animation-timing-function: ease-in;
-    opacity: 0;
-  }
-
-  40% {
-    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
-    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
-    -webkit-animation-timing-function: ease-in;
-    animation-timing-function: ease-in;
-  }
-
-  60% {
-    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
-    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
-    opacity: 1;
-  }
-
-  80% {
-    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
-    transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
-  }
-
-  to {
-    -webkit-transform: perspective(400px);
-    transform: perspective(400px);
-  }
-}
-
-
-@keyframes flipInX {
-  from {
-    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
-    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
-    -webkit-animation-timing-function: ease-in;
-    animation-timing-function: ease-in;
-    opacity: 0;
-  }
-
-  40% {
-    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
-    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
-    -webkit-animation-timing-function: ease-in;
-    animation-timing-function: ease-in;
-  }
-
-  60% {
-    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
-    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
-    opacity: 1;
-  }
-
-  80% {
-    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
-    transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
-  }
-
-  to {
-    -webkit-transform: perspective(400px);
-    transform: perspective(400px);
-  }
-}
-
-
-.flipInX {
-  -webkit-backface-visibility: visible !important;
-  backface-visibility: visible !important;
-  -webkit-animation-name: flipInX;
-  animation-name: flipInX;
-}
-
-@-webkit-keyframes flipInY {
-  from {
-    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
-    transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
-    -webkit-animation-timing-function: ease-in;
-    animation-timing-function: ease-in;
-    opacity: 0;
-  }
-
-  40% {
-    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -20deg);
-    transform: perspective(400px) rotate3d(0, 1, 0, -20deg);
-    -webkit-animation-timing-function: ease-in;
-    animation-timing-function: ease-in;
-  }
-
-  60% {
-    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 10deg);
-    transform: perspective(400px) rotate3d(0, 1, 0, 10deg);
-    opacity: 1;
-  }
-
-  80% {
-    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -5deg);
-    transform: perspective(400px) rotate3d(0, 1, 0, -5deg);
-  }
-
-  to {
-    -webkit-transform: perspective(400px);
-    transform: perspective(400px);
-  }
-}
-
-
-@keyframes flipInY {
-  from {
-    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
-    transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
-    -webkit-animation-timing-function: ease-in;
-    animation-timing-function: ease-in;
-    opacity: 0;
-  }
-
-  40% {
-    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -20deg);
-    transform: perspective(400px) rotate3d(0, 1, 0, -20deg);
-    -webkit-animation-timing-function: ease-in;
-    animation-timing-function: ease-in;
-  }
-
-  60% {
-    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 10deg);
-    transform: perspective(400px) rotate3d(0, 1, 0, 10deg);
-    opacity: 1;
-  }
-
-  80% {
-    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -5deg);
-    transform: perspective(400px) rotate3d(0, 1, 0, -5deg);
-  }
-
-  to {
-    -webkit-transform: perspective(400px);
-    transform: perspective(400px);
-  }
-}
-
-
-.flipInY {
-  -webkit-backface-visibility: visible !important;
-  backface-visibility: visible !important;
-  -webkit-animation-name: flipInY;
-  animation-name: flipInY;
-}
-
-@-webkit-keyframes flipOutX {
-  from {
-    -webkit-transform: perspective(400px);
-    transform: perspective(400px);
-  }
-
-  30% {
-    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
-    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
-    opacity: 1;
-  }
-
-  to {
-    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
-    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
-    opacity: 0;
-  }
-}
-
-
-@keyframes flipOutX {
-  from {
-    -webkit-transform: perspective(400px);
-    transform: perspective(400px);
-  }
-
-  30% {
-    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
-    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
-    opacity: 1;
-  }
-
-  to {
-    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
-    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
-    opacity: 0;
-  }
-}
-
-
-.flipOutX {
-  -webkit-animation-name: flipOutX;
-  animation-name: flipOutX;
-  -webkit-backface-visibility: visible !important;
-  backface-visibility: visible !important;
-}
-
-@-webkit-keyframes flipOutY {
-  from {
-    -webkit-transform: perspective(400px);
-    transform: perspective(400px);
-  }
-
-  30% {
-    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -15deg);
-    transform: perspective(400px) rotate3d(0, 1, 0, -15deg);
-    opacity: 1;
-  }
-
-  to {
-    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
-    transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
-    opacity: 0;
-  }
-}
-
-
-@keyframes flipOutY {
-  from {
-    -webkit-transform: perspective(400px);
-    transform: perspective(400px);
-  }
-
-  30% {
-    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, -15deg);
-    transform: perspective(400px) rotate3d(0, 1, 0, -15deg);
-    opacity: 1;
-  }
-
-  to {
-    -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
-    transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
-    opacity: 0;
-  }
-}
-
-
-.flipOutY {
-  -webkit-backface-visibility: visible !important;
-  backface-visibility: visible !important;
-  -webkit-animation-name: flipOutY;
-  animation-name: flipOutY;
-}
-
-@-webkit-keyframes lightSpeedIn {
-  from {
-    -webkit-transform: translate3d(100%, 0, 0) skewX(-30deg);
-    transform: translate3d(100%, 0, 0) skewX(-30deg);
-    opacity: 0;
-  }
-
-  60% {
-    -webkit-transform: skewX(20deg);
-    transform: skewX(20deg);
-    opacity: 1;
-  }
-
-  80% {
-    -webkit-transform: skewX(-5deg);
-    transform: skewX(-5deg);
-    opacity: 1;
-  }
-
-  to {
-    -webkit-transform: none;
-    transform: none;
-    opacity: 1;
-  }
-}
-
-
-@keyframes lightSpeedIn {
-  from {
-    -webkit-transform: translate3d(100%, 0, 0) skewX(-30deg);
-    transform: translate3d(100%, 0, 0) skewX(-30deg);
-    opacity: 0;
-  }
-
-  60% {
-    -webkit-transform: skewX(20deg);
-    transform: skewX(20deg);
-    opacity: 1;
-  }
-
-  80% {
-    -webkit-transform: skewX(-5deg);
-    transform: skewX(-5deg);
-    opacity: 1;
-  }
-
-  to {
-    -webkit-transform: none;
-    transform: none;
-    opacity: 1;
-  }
-}
-
-
-.lightSpeedIn {
-  -webkit-animation-name: lightSpeedIn;
-  animation-name: lightSpeedIn;
-  -webkit-animation-timing-function: ease-out;
-  animation-timing-function: ease-out;
-}
-
-@-webkit-keyframes lightSpeedOut {
-  from {
-    opacity: 1;
-  }
-
-  to {
-    -webkit-transform: translate3d(100%, 0, 0) skewX(30deg);
-    transform: translate3d(100%, 0, 0) skewX(30deg);
-    opacity: 0;
-  }
-}
-
-
-@keyframes lightSpeedOut {
-  from {
-    opacity: 1;
-  }
-
-  to {
-    -webkit-transform: translate3d(100%, 0, 0) skewX(30deg);
-    transform: translate3d(100%, 0, 0) skewX(30deg);
-    opacity: 0;
-  }
-}
-
-
-.lightSpeedOut {
-  -webkit-animation-name: lightSpeedOut;
-  animation-name: lightSpeedOut;
-  -webkit-animation-timing-function: ease-in;
-  animation-timing-function: ease-in;
-}
-
-@-webkit-keyframes rotateIn {
-  from {
-    -webkit-transform-origin: center;
-    transform-origin: center;
-    -webkit-transform: rotate3d(0, 0, 1, -200deg);
-    transform: rotate3d(0, 0, 1, -200deg);
-    opacity: 0;
-  }
-
-  to {
-    -webkit-transform-origin: center;
-    transform-origin: center;
-    -webkit-transform: none;
-    transform: none;
-    opacity: 1;
-  }
-}
-
-
-@keyframes rotateIn {
-  from {
-    -webkit-transform-origin: center;
-    transform-origin: center;
-    -webkit-transform: rotate3d(0, 0, 1, -200deg);
-    transform: rotate3d(0, 0, 1, -200deg);
-    opacity: 0;
-  }
-
-  to {
-    -webkit-transform-origin: center;
-    transform-origin: center;
-    -webkit-transform: none;
-    transform: none;
-    opacity: 1;
-  }
-}
-
-
-.rotateIn {
-  -webkit-animation-name: rotateIn;
-  animation-name: rotateIn;
-}
-
-@-webkit-keyframes rotateInDownLeft {
-  from {
-    -webkit-transform-origin: left bottom;
-    transform-origin: left bottom;
-    -webkit-transform: rotate3d(0, 0, 1, -45deg);
-    transform: rotate3d(0, 0, 1, -45deg);
-    opacity: 0;
-  }
-
-  to {
-    -webkit-transform-origin: left bottom;
-    transform-origin: left bottom;
-    -webkit-transform: none;
-    transform: none;
-    opacity: 1;
-  }
-}
-
-
-@keyframes rotateInDownLeft {
-  from {
-    -webkit-transform-origin: left bottom;
-    transform-origin: left bottom;
-    -webkit-transform: rotate3d(0, 0, 1, -45deg);
-    transform: rotate3d(0, 0, 1, -45deg);
-    opacity: 0;
-  }
-
-  to {
-    -webkit-transform-origin: left bottom;
-    transform-origin: left bottom;
-    -webkit-transform: none;
-    transform: none;
-    opacity: 1;
-  }
-}
-
-
-.rotateInDownLeft {
-  -webkit-animation-name: rotateInDownLeft;
-  animation-name: rotateInDownLeft;
-}
-
-@-webkit-keyframes rotateInDownRight {
-  from {
-    -webkit-transform-origin: right bottom;
-    transform-origin: right bottom;
-    -webkit-transform: rotate3d(0, 0, 1, 45deg);
-    transform: rotate3d(0, 0, 1, 45deg);
-    opacity: 0;
-  }
-
-  to {
-    -webkit-transform-origin: right bottom;
-    transform-origin: right bottom;
-    -webkit-transform: none;
-    transform: none;
-    opacity: 1;
-  }
-}
-
-
-@keyframes rotateInDownRight {
-  from {
-    -webkit-transform-origin: right bottom;
-    transform-origin: right bottom;
-    -webkit-transform: rotate3d(0, 0, 1, 45deg);
-    transform: rotate3d(0, 0, 1, 45deg);
-    opacity: 0;
-  }
-
-  to {
-    -webkit-transform-origin: right bottom;
-    transform-origin: right bottom;
-    -webkit-transform: none;
-    transform: none;
-    opacity: 1;
-  }
-}
-
-
-.rotateInDownRight {
-  -webkit-animation-name: rotateInDownRight;
-  animation-name: rotateInDownRight;
-}
-
-@-webkit-keyframes rotateInUpLeft {
-  from {
-    -webkit-transform-origin: left bottom;
-    transform-origin: left bottom;
-    -webkit-transform: rotate3d(0, 0, 1, 45deg);
-    transform: rotate3d(0, 0, 1, 45deg);
-    opacity: 0;
-  }
-
-  to {
-    -webkit-transform-origin: left bottom;
-    transform-origin: left bottom;
-    -webkit-transform: none;
-    transform: none;
-    opacity: 1;
-  }
-}
-
-
-@keyframes rotateInUpLeft {
-  from {
-    -webkit-transform-origin: left bottom;
-    transform-origin: left bottom;
-    -webkit-transform: rotate3d(0, 0, 1, 45deg);
-    transform: rotate3d(0, 0, 1, 45deg);
-    opacity: 0;
-  }
-
-  to {
-    -webkit-transform-origin: left bottom;
-    transform-origin: left bottom;
-    -webkit-transform: none;
-    transform: none;
-    opacity: 1;
-  }
-}
-
-
-.rotateInUpLeft {
-  -webkit-animation-name: rotateInUpLeft;
-  animation-name: rotateInUpLeft;
-}
-
-@-webkit-keyframes rotateInUpRight {
-  from {
-    -webkit-transform-origin: right bottom;
-    transform-origin: right bottom;
-    -webkit-transform: rotate3d(0, 0, 1, -90deg);
-    transform: rotate3d(0, 0, 1, -90deg);
-    opacity: 0;
-  }
-
-  to {
-    -webkit-transform-origin: right bottom;
-    transform-origin: right bottom;
-    -webkit-transform: none;
-    transform: none;
-    opacity: 1;
-  }
-}
-
-
-@keyframes rotateInUpRight {
-  from {
-    -webkit-transform-origin: right bottom;
-    transform-origin: right bottom;
-    -webkit-transform: rotate3d(0, 0, 1, -90deg);
-    transform: rotate3d(0, 0, 1, -90deg);
-    opacity: 0;
-  }
-
-  to {
-    -webkit-transform-origin: right bottom;
-    transform-origin: right bottom;
-    -webkit-transform: none;
-    transform: none;
-    opacity: 1;
-  }
-}
-
-
-.rotateInUpRight {
-  -webkit-animation-name: rotateInUpRight;
-  animation-name: rotateInUpRight;
-}
-
-@-webkit-keyframes rotateOut {
-  from {
-    -webkit-transform-origin: center;
-    transform-origin: center;
-    opacity: 1;
-  }
-
-  to {
-    -webkit-transform-origin: center;
-    transform-origin: center;
-    -webkit-transform: rotate3d(0, 0, 1, 200deg);
-    transform: rotate3d(0, 0, 1, 200deg);
-    opacity: 0;
-  }
-}
-
-
-@keyframes rotateOut {
-  from {
-    -webkit-transform-origin: center;
-    transform-origin: center;
-    opacity: 1;
-  }
-
-  to {
-    -webkit-transform-origin: center;
-    transform-origin: center;
-    -webkit-transform: rotate3d(0, 0, 1, 200deg);
-    transform: rotate3d(0, 0, 1, 200deg);
-    opacity: 0;
-  }
-}
-
-
-.rotateOut {
-  -webkit-animation-name: rotateOut;
-  animation-name: rotateOut;
-}
-
-@-webkit-keyframes rotateOutDownLeft {
-  from {
-    -webkit-transform-origin: left bottom;
-    transform-origin: left bottom;
-    opacity: 1;
-  }
-
-  to {
-    -webkit-transform-origin: left bottom;
-    transform-origin: left bottom;
-    -webkit-transform: rotate3d(0, 0, 1, 45deg);
-    transform: rotate3d(0, 0, 1, 45deg);
-    opacity: 0;
-  }
-}
-
-
-@keyframes rotateOutDownLeft {
-  from {
-    -webkit-transform-origin: left bottom;
-    transform-origin: left bottom;
-    opacity: 1;
-  }
-
-  to {
-    -webkit-transform-origin: left bottom;
-    transform-origin: left bottom;
-    -webkit-transform: rotate3d(0, 0, 1, 45deg);
-    transform: rotate3d(0, 0, 1, 45deg);
-    opacity: 0;
-  }
-}
-
-
-.rotateOutDownLeft {
-  -webkit-animation-name: rotateOutDownLeft;
-  animation-name: rotateOutDownLeft;
-}
-
-@-webkit-keyframes rotateOutDownRight {
-  from {
-    -webkit-transform-origin: right bottom;
-    transform-origin: right bottom;
-    opacity: 1;
-  }
-
-  to {
-    -webkit-transform-origin: right bottom;
-    transform-origin: right bottom;
-    -webkit-transform: rotate3d(0, 0, 1, -45deg);
-    transform: rotate3d(0, 0, 1, -45deg);
-    opacity: 0;
-  }
-}
-
-
-@keyframes rotateOutDownRight {
-  from {
-    -webkit-transform-origin: right bottom;
-    transform-origin: right bottom;
-    opacity: 1;
-  }
-
-  to {
-    -webkit-transform-origin: right bottom;
-    transform-origin: right bottom;
-    -webkit-transform: rotate3d(0, 0, 1, -45deg);
-    transform: rotate3d(0, 0, 1, -45deg);
-    opacity: 0;
-  }
-}
-
-
-.rotateOutDownRight {
-  -webkit-animation-name: rotateOutDownRight;
-  animation-name: rotateOutDownRight;
-}
-
-@-webkit-keyframes rotateOutUpLeft {
-  from {
-    -webkit-transform-origin: left bottom;
-    transform-origin: left bottom;
-    opacity: 1;
-  }
-
-  to {
-    -webkit-transform-origin: left bottom;
-    transform-origin: left bottom;
-    -webkit-transform: rotate3d(0, 0, 1, -45deg);
-    transform: rotate3d(0, 0, 1, -45deg);
-    opacity: 0;
-  }
-}
-
-
-@keyframes rotateOutUpLeft {
-  from {
-    -webkit-transform-origin: left bottom;
-    transform-origin: left bottom;
-    opacity: 1;
-  }
-
-  to {
-    -webkit-transform-origin: left bottom;
-    transform-origin: left bottom;
-    -webkit-transform: rotate3d(0, 0, 1, -45deg);
-    transform: rotate3d(0, 0, 1, -45deg);
-    opacity: 0;
-  }
-}
-
-
-.rotateOutUpLeft {
-  -webkit-animation-name: rotateOutUpLeft;
-  animation-name: rotateOutUpLeft;
-}
-
-@-webkit-keyframes rotateOutUpRight {
-  from {
-    -webkit-transform-origin: right bottom;
-    transform-origin: right bottom;
-    opacity: 1;
-  }
-
-  to {
-    -webkit-transform-origin: right bottom;
-    transform-origin: right bottom;
-    -webkit-transform: rotate3d(0, 0, 1, 90deg);
-    transform: rotate3d(0, 0, 1, 90deg);
-    opacity: 0;
-  }
-}
-
-
-@keyframes rotateOutUpRight {
-  from {
-    -webkit-transform-origin: right bottom;
-    transform-origin: right bottom;
-    opacity: 1;
-  }
-
-  to {
-    -webkit-transform-origin: right bottom;
-    transform-origin: right bottom;
-    -webkit-transform: rotate3d(0, 0, 1, 90deg);
-    transform: rotate3d(0, 0, 1, 90deg);
-    opacity: 0;
-  }
-}
-
-
-.rotateOutUpRight {
-  -webkit-animation-name: rotateOutUpRight;
-  animation-name: rotateOutUpRight;
-}
-
-@-webkit-keyframes hinge {
-  0% {
-    -webkit-transform-origin: top left;
-    transform-origin: top left;
-    -webkit-animation-timing-function: ease-in-out;
-    animation-timing-function: ease-in-out;
-  }
-
-  20%, 60% {
-    -webkit-transform: rotate3d(0, 0, 1, 80deg);
-    transform: rotate3d(0, 0, 1, 80deg);
-    -webkit-transform-origin: top left;
-    transform-origin: top left;
-    -webkit-animation-timing-function: ease-in-out;
-    animation-timing-function: ease-in-out;
-  }
-
-  40%, 80% {
-    -webkit-transform: rotate3d(0, 0, 1, 60deg);
-    transform: rotate3d(0, 0, 1, 60deg);
-    -webkit-transform-origin: top left;
-    transform-origin: top left;
-    -webkit-animation-timing-function: ease-in-out;
-    animation-timing-function: ease-in-out;
-    opacity: 1;
-  }
-
-  to {
-    -webkit-transform: translate3d(0, 700px, 0);
-    transform: translate3d(0, 700px, 0);
-    opacity: 0;
-  }
-}
-
-
-@keyframes hinge {
-  0% {
-    -webkit-transform-origin: top left;
-    transform-origin: top left;
-    -webkit-animation-timing-function: ease-in-out;
-    animation-timing-function: ease-in-out;
-  }
-
-  20%, 60% {
-    -webkit-transform: rotate3d(0, 0, 1, 80deg);
-    transform: rotate3d(0, 0, 1, 80deg);
-    -webkit-transform-origin: top left;
-    transform-origin: top left;
-    -webkit-animation-timing-function: ease-in-out;
-    animation-timing-function: ease-in-out;
-  }
-
-  40%, 80% {
-    -webkit-transform: rotate3d(0, 0, 1, 60deg);
-    transform: rotate3d(0, 0, 1, 60deg);
-    -webkit-transform-origin: top left;
-    transform-origin: top left;
-    -webkit-animation-timing-function: ease-in-out;
-    animation-timing-function: ease-in-out;
-    opacity: 1;
-  }
-
-  to {
-    -webkit-transform: translate3d(0, 700px, 0);
-    transform: translate3d(0, 700px, 0);
-    opacity: 0;
-  }
-}
-
-
-.hinge {
-  -webkit-animation-name: hinge;
-  animation-name: hinge;
-}
-
-/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */
-
-@-webkit-keyframes rollIn {
-  from {
-    opacity: 0;
-    -webkit-transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg);
-    transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg);
-  }
-
-  to {
-    opacity: 1;
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-@keyframes rollIn {
-  from {
-    opacity: 0;
-    -webkit-transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg);
-    transform: translate3d(-100%, 0, 0) rotate3d(0, 0, 1, -120deg);
-  }
-
-  to {
-    opacity: 1;
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-.rollIn {
-  -webkit-animation-name: rollIn;
-  animation-name: rollIn;
-}
-
-/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */
-
-@-webkit-keyframes rollOut {
-  from {
-    opacity: 1;
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg);
-    transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg);
-  }
-}
-
-
-@keyframes rollOut {
-  from {
-    opacity: 1;
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg);
-    transform: translate3d(100%, 0, 0) rotate3d(0, 0, 1, 120deg);
-  }
-}
-
-
-.rollOut {
-  -webkit-animation-name: rollOut;
-  animation-name: rollOut;
-}
-
-@-webkit-keyframes zoomIn {
-  from {
-    opacity: 0;
-    -webkit-transform: scale3d(0.3, 0.3, 0.3);
-    transform: scale3d(0.3, 0.3, 0.3);
-  }
-
-  50% {
-    opacity: 1;
-  }
-}
-
-
-@keyframes zoomIn {
-  from {
-    opacity: 0;
-    -webkit-transform: scale3d(0.3, 0.3, 0.3);
-    transform: scale3d(0.3, 0.3, 0.3);
-  }
-
-  50% {
-    opacity: 1;
-  }
-}
-
-
-.zoomIn {
-  -webkit-animation-name: zoomIn;
-  animation-name: zoomIn;
-}
-
-@-webkit-keyframes zoomInDown {
-  from {
-    opacity: 0;
-    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -1000px, 0);
-    transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -1000px, 0);
-    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-  }
-
-  60% {
-    opacity: 1;
-    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0);
-    transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0);
-    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-  }
-}
-
-
-@keyframes zoomInDown {
-  from {
-    opacity: 0;
-    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -1000px, 0);
-    transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -1000px, 0);
-    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-  }
-
-  60% {
-    opacity: 1;
-    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0);
-    transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0);
-    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-  }
-}
-
-
-.zoomInDown {
-  -webkit-animation-name: zoomInDown;
-  animation-name: zoomInDown;
-}
-
-@-webkit-keyframes zoomInLeft {
-  from {
-    opacity: 0;
-    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(-1000px, 0, 0);
-    transform: scale3d(0.1, 0.1, 0.1) translate3d(-1000px, 0, 0);
-    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-  }
-
-  60% {
-    opacity: 1;
-    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(10px, 0, 0);
-    transform: scale3d(0.475, 0.475, 0.475) translate3d(10px, 0, 0);
-    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-  }
-}
-
-
-@keyframes zoomInLeft {
-  from {
-    opacity: 0;
-    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(-1000px, 0, 0);
-    transform: scale3d(0.1, 0.1, 0.1) translate3d(-1000px, 0, 0);
-    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-  }
-
-  60% {
-    opacity: 1;
-    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(10px, 0, 0);
-    transform: scale3d(0.475, 0.475, 0.475) translate3d(10px, 0, 0);
-    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-  }
-}
-
-
-.zoomInLeft {
-  -webkit-animation-name: zoomInLeft;
-  animation-name: zoomInLeft;
-}
-
-@-webkit-keyframes zoomInRight {
-  from {
-    opacity: 0;
-    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(1000px, 0, 0);
-    transform: scale3d(0.1, 0.1, 0.1) translate3d(1000px, 0, 0);
-    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-  }
-
-  60% {
-    opacity: 1;
-    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(-10px, 0, 0);
-    transform: scale3d(0.475, 0.475, 0.475) translate3d(-10px, 0, 0);
-    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-  }
-}
-
-
-@keyframes zoomInRight {
-  from {
-    opacity: 0;
-    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(1000px, 0, 0);
-    transform: scale3d(0.1, 0.1, 0.1) translate3d(1000px, 0, 0);
-    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-  }
-
-  60% {
-    opacity: 1;
-    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(-10px, 0, 0);
-    transform: scale3d(0.475, 0.475, 0.475) translate3d(-10px, 0, 0);
-    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-  }
-}
-
-
-.zoomInRight {
-  -webkit-animation-name: zoomInRight;
-  animation-name: zoomInRight;
-}
-
-@-webkit-keyframes zoomInUp {
-  from {
-    opacity: 0;
-    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 1000px, 0);
-    transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 1000px, 0);
-    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-  }
-
-  60% {
-    opacity: 1;
-    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0);
-    transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0);
-    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-  }
-}
-
-
-@keyframes zoomInUp {
-  from {
-    opacity: 0;
-    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 1000px, 0);
-    transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 1000px, 0);
-    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-  }
-
-  60% {
-    opacity: 1;
-    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0);
-    transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0);
-    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-  }
-}
-
-
-.zoomInUp {
-  -webkit-animation-name: zoomInUp;
-  animation-name: zoomInUp;
-}
-
-@-webkit-keyframes zoomOut {
-  from {
-    opacity: 1;
-  }
-
-  50% {
-    opacity: 0;
-    -webkit-transform: scale3d(0.3, 0.3, 0.3);
-    transform: scale3d(0.3, 0.3, 0.3);
-  }
-
-  to {
-    opacity: 0;
-  }
-}
-
-
-@keyframes zoomOut {
-  from {
-    opacity: 1;
-  }
-
-  50% {
-    opacity: 0;
-    -webkit-transform: scale3d(0.3, 0.3, 0.3);
-    transform: scale3d(0.3, 0.3, 0.3);
-  }
-
-  to {
-    opacity: 0;
-  }
-}
-
-
-.zoomOut {
-  -webkit-animation-name: zoomOut;
-  animation-name: zoomOut;
-}
-
-@-webkit-keyframes zoomOutDown {
-  40% {
-    opacity: 1;
-    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0);
-    transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0);
-    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 2000px, 0);
-    transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 2000px, 0);
-    -webkit-transform-origin: center bottom;
-    transform-origin: center bottom;
-    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-  }
-}
-
-
-@keyframes zoomOutDown {
-  40% {
-    opacity: 1;
-    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0);
-    transform: scale3d(0.475, 0.475, 0.475) translate3d(0, -60px, 0);
-    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 2000px, 0);
-    transform: scale3d(0.1, 0.1, 0.1) translate3d(0, 2000px, 0);
-    -webkit-transform-origin: center bottom;
-    transform-origin: center bottom;
-    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-  }
-}
-
-
-.zoomOutDown {
-  -webkit-animation-name: zoomOutDown;
-  animation-name: zoomOutDown;
-}
-
-@-webkit-keyframes zoomOutLeft {
-  40% {
-    opacity: 1;
-    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(42px, 0, 0);
-    transform: scale3d(0.475, 0.475, 0.475) translate3d(42px, 0, 0);
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: scale(0.1) translate3d(-2000px, 0, 0);
-    transform: scale(0.1) translate3d(-2000px, 0, 0);
-    -webkit-transform-origin: left center;
-    transform-origin: left center;
-  }
-}
-
-
-@keyframes zoomOutLeft {
-  40% {
-    opacity: 1;
-    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(42px, 0, 0);
-    transform: scale3d(0.475, 0.475, 0.475) translate3d(42px, 0, 0);
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: scale(0.1) translate3d(-2000px, 0, 0);
-    transform: scale(0.1) translate3d(-2000px, 0, 0);
-    -webkit-transform-origin: left center;
-    transform-origin: left center;
-  }
-}
-
-
-.zoomOutLeft {
-  -webkit-animation-name: zoomOutLeft;
-  animation-name: zoomOutLeft;
-}
-
-@-webkit-keyframes zoomOutRight {
-  40% {
-    opacity: 1;
-    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(-42px, 0, 0);
-    transform: scale3d(0.475, 0.475, 0.475) translate3d(-42px, 0, 0);
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: scale(0.1) translate3d(2000px, 0, 0);
-    transform: scale(0.1) translate3d(2000px, 0, 0);
-    -webkit-transform-origin: right center;
-    transform-origin: right center;
-  }
-}
-
-
-@keyframes zoomOutRight {
-  40% {
-    opacity: 1;
-    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(-42px, 0, 0);
-    transform: scale3d(0.475, 0.475, 0.475) translate3d(-42px, 0, 0);
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: scale(0.1) translate3d(2000px, 0, 0);
-    transform: scale(0.1) translate3d(2000px, 0, 0);
-    -webkit-transform-origin: right center;
-    transform-origin: right center;
-  }
-}
-
-
-.zoomOutRight {
-  -webkit-animation-name: zoomOutRight;
-  animation-name: zoomOutRight;
-}
-
-@-webkit-keyframes zoomOutUp {
-  40% {
-    opacity: 1;
-    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0);
-    transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0);
-    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -2000px, 0);
-    transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -2000px, 0);
-    -webkit-transform-origin: center bottom;
-    transform-origin: center bottom;
-    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-  }
-}
-
-
-@keyframes zoomOutUp {
-  40% {
-    opacity: 1;
-    -webkit-transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0);
-    transform: scale3d(0.475, 0.475, 0.475) translate3d(0, 60px, 0);
-    -webkit-animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-    animation-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
-  }
-
-  to {
-    opacity: 0;
-    -webkit-transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -2000px, 0);
-    transform: scale3d(0.1, 0.1, 0.1) translate3d(0, -2000px, 0);
-    -webkit-transform-origin: center bottom;
-    transform-origin: center bottom;
-    -webkit-animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-    animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1);
-  }
-}
-
-
-.zoomOutUp {
-  -webkit-animation-name: zoomOutUp;
-  animation-name: zoomOutUp;
-}
-
-@-webkit-keyframes slideInDown {
-  from {
-    -webkit-transform: translate3d(0, -100%, 0);
-    transform: translate3d(0, -100%, 0);
-    visibility: visible;
-  }
-
-  to {
-    -webkit-transform: translate3d(0, 0, 0);
-    transform: translate3d(0, 0, 0);
-  }
-}
-
-
-@keyframes slideInDown {
-  from {
-    -webkit-transform: translate3d(0, -100%, 0);
-    transform: translate3d(0, -100%, 0);
-    visibility: visible;
-  }
-
-  to {
-    -webkit-transform: translate3d(0, 0, 0);
-    transform: translate3d(0, 0, 0);
-  }
-}
-
-
-.slideInDown {
-  -webkit-animation-name: slideInDown;
-  animation-name: slideInDown;
-}
-
-@-webkit-keyframes slideInLeft {
-  from {
-    -webkit-transform: translate3d(-100%, 0, 0);
-    transform: translate3d(-100%, 0, 0);
-    visibility: visible;
-  }
-
-  to {
-    -webkit-transform: translate3d(0, 0, 0);
-    transform: translate3d(0, 0, 0);
-  }
-}
-
-
-@keyframes slideInLeft {
-  from {
-    -webkit-transform: translate3d(-100%, 0, 0);
-    transform: translate3d(-100%, 0, 0);
-    visibility: visible;
-  }
-
-  to {
-    -webkit-transform: translate3d(0, 0, 0);
-    transform: translate3d(0, 0, 0);
-  }
-}
-
-
-.slideInLeft {
-  -webkit-animation-name: slideInLeft;
-  animation-name: slideInLeft;
-}
-
-@-webkit-keyframes slideInRight {
-  from {
-    -webkit-transform: translate3d(100%, 0, 0);
-    transform: translate3d(100%, 0, 0);
-    visibility: visible;
-  }
-
-  to {
-    -webkit-transform: translate3d(0, 0, 0);
-    transform: translate3d(0, 0, 0);
-  }
-}
-
-
-@keyframes slideInRight {
-  from {
-    -webkit-transform: translate3d(100%, 0, 0);
-    transform: translate3d(100%, 0, 0);
-    visibility: visible;
-  }
-
-  to {
-    -webkit-transform: translate3d(0, 0, 0);
-    transform: translate3d(0, 0, 0);
-  }
-}
-
-
-.slideInRight {
-  -webkit-animation-name: slideInRight;
-  animation-name: slideInRight;
-}
-
-@-webkit-keyframes slideInUp {
-  from {
-    -webkit-transform: translate3d(0, 100%, 0);
-    transform: translate3d(0, 100%, 0);
-    visibility: visible;
-  }
-
-  to {
-    -webkit-transform: translate3d(0, 0, 0);
-    transform: translate3d(0, 0, 0);
-  }
-}
-
-
-@keyframes slideInUp {
-  from {
-    -webkit-transform: translate3d(0, 100%, 0);
-    transform: translate3d(0, 100%, 0);
-    visibility: visible;
-  }
-
-  to {
-    -webkit-transform: translate3d(0, 0, 0);
-    transform: translate3d(0, 0, 0);
-  }
-}
-
-
-.slideInUp {
-  -webkit-animation-name: slideInUp;
-  animation-name: slideInUp;
-}
-
-@-webkit-keyframes slideOutDown {
-  from {
-    -webkit-transform: translate3d(0, 0, 0);
-    transform: translate3d(0, 0, 0);
-  }
-
-  to {
-    visibility: hidden;
-    -webkit-transform: translate3d(0, 100%, 0);
-    transform: translate3d(0, 100%, 0);
-  }
-}
-
-
-@keyframes slideOutDown {
-  from {
-    -webkit-transform: translate3d(0, 0, 0);
-    transform: translate3d(0, 0, 0);
-  }
-
-  to {
-    visibility: hidden;
-    -webkit-transform: translate3d(0, 100%, 0);
-    transform: translate3d(0, 100%, 0);
-  }
-}
-
-
-.slideOutDown {
-  -webkit-animation-name: slideOutDown;
-  animation-name: slideOutDown;
-}
-
-@-webkit-keyframes slideOutLeft {
-  from {
-    -webkit-transform: translate3d(0, 0, 0);
-    transform: translate3d(0, 0, 0);
-  }
-
-  to {
-    visibility: hidden;
-    -webkit-transform: translate3d(-100%, 0, 0);
-    transform: translate3d(-100%, 0, 0);
-  }
-}
-
-
-@keyframes slideOutLeft {
-  from {
-    -webkit-transform: translate3d(0, 0, 0);
-    transform: translate3d(0, 0, 0);
-  }
-
-  to {
-    visibility: hidden;
-    -webkit-transform: translate3d(-100%, 0, 0);
-    transform: translate3d(-100%, 0, 0);
-  }
-}
-
-
-.slideOutLeft {
-  -webkit-animation-name: slideOutLeft;
-  animation-name: slideOutLeft;
-}
-
-@-webkit-keyframes slideOutRight {
-  from {
-    -webkit-transform: translate3d(0, 0, 0);
-    transform: translate3d(0, 0, 0);
-  }
-
-  to {
-    visibility: hidden;
-    -webkit-transform: translate3d(100%, 0, 0);
-    transform: translate3d(100%, 0, 0);
-  }
-}
-
-
-@keyframes slideOutRight {
-  from {
-    -webkit-transform: translate3d(0, 0, 0);
-    transform: translate3d(0, 0, 0);
-  }
-
-  to {
-    visibility: hidden;
-    -webkit-transform: translate3d(100%, 0, 0);
-    transform: translate3d(100%, 0, 0);
-  }
-}
-
-
-.slideOutRight {
-  -webkit-animation-name: slideOutRight;
-  animation-name: slideOutRight;
-}
-
-@-webkit-keyframes slideOutUp {
-  from {
-    -webkit-transform: translate3d(0, 0, 0);
-    transform: translate3d(0, 0, 0);
-  }
-
-  to {
-    visibility: hidden;
-    -webkit-transform: translate3d(0, -100%, 0);
-    transform: translate3d(0, -100%, 0);
-  }
-}
-
-
-@keyframes slideOutUp {
-  from {
-    -webkit-transform: translate3d(0, 0, 0);
-    transform: translate3d(0, 0, 0);
-  }
-
-  to {
-    visibility: hidden;
-    -webkit-transform: translate3d(0, -100%, 0);
-    transform: translate3d(0, -100%, 0);
-  }
-}
-
-
-.slideOutUp {
-  -webkit-animation-name: slideOutUp;
-  animation-name: slideOutUp;
-}
-
-@keyframes spin {
-  from {
-    -webkit-transform-origin: center;
-    transform-origin: center;
-    -webkit-transform: rotate3d(0, 0, 1, -360deg);
-    transform: rotate3d(0, 0, 1, -360deg);
-  }
-
-  to {
-    -webkit-transform-origin: center;
-    transform-origin: center;
-    -webkit-transform: none;
-    transform: none;
-  }
-}
-
-
-.spin {
-  -webkit-animation-name: spin;
-  animation-name: spin;
-}

+ 0 - 60
client/libs/codemirror-merge/diff-match-patch.js

@@ -1,60 +0,0 @@
-var diff_match_patch=function(){this.Diff_Timeout=1;this.Diff_EditCost=4;this.Match_Threshold=.5;this.Match_Distance=1E3;this.Patch_DeleteThreshold=.5;this.Patch_Margin=4;this.Match_MaxBits=32},DIFF_DELETE=-1,DIFF_INSERT=1,DIFF_EQUAL=0;diff_match_patch.Diff=function(a,b){this[0]=a;this[1]=b};diff_match_patch.Diff.prototype.length=2;diff_match_patch.Diff.prototype.toString=function(){return this[0]+","+this[1]};
-diff_match_patch.prototype.diff_main=function(a,b,c,d){"undefined"==typeof d&&(d=0>=this.Diff_Timeout?Number.MAX_VALUE:(new Date).getTime()+1E3*this.Diff_Timeout);if(null==a||null==b)throw Error("Null input. (diff_main)");if(a==b)return a?[new diff_match_patch.Diff(DIFF_EQUAL,a)]:[];"undefined"==typeof c&&(c=!0);var e=c,f=this.diff_commonPrefix(a,b);c=a.substring(0,f);a=a.substring(f);b=b.substring(f);f=this.diff_commonSuffix(a,b);var g=a.substring(a.length-f);a=a.substring(0,a.length-f);b=b.substring(0,
-b.length-f);a=this.diff_compute_(a,b,e,d);c&&a.unshift(new diff_match_patch.Diff(DIFF_EQUAL,c));g&&a.push(new diff_match_patch.Diff(DIFF_EQUAL,g));this.diff_cleanupMerge(a);return a};
-diff_match_patch.prototype.diff_compute_=function(a,b,c,d){if(!a)return[new diff_match_patch.Diff(DIFF_INSERT,b)];if(!b)return[new diff_match_patch.Diff(DIFF_DELETE,a)];var e=a.length>b.length?a:b,f=a.length>b.length?b:a,g=e.indexOf(f);return-1!=g?(c=[new diff_match_patch.Diff(DIFF_INSERT,e.substring(0,g)),new diff_match_patch.Diff(DIFF_EQUAL,f),new diff_match_patch.Diff(DIFF_INSERT,e.substring(g+f.length))],a.length>b.length&&(c[0][0]=c[2][0]=DIFF_DELETE),c):1==f.length?[new diff_match_patch.Diff(DIFF_DELETE,
-a),new diff_match_patch.Diff(DIFF_INSERT,b)]:(e=this.diff_halfMatch_(a,b))?(b=e[1],f=e[3],a=e[4],e=this.diff_main(e[0],e[2],c,d),c=this.diff_main(b,f,c,d),e.concat([new diff_match_patch.Diff(DIFF_EQUAL,a)],c)):c&&100<a.length&&100<b.length?this.diff_lineMode_(a,b,d):this.diff_bisect_(a,b,d)};
-diff_match_patch.prototype.diff_lineMode_=function(a,b,c){var d=this.diff_linesToChars_(a,b);a=d.chars1;b=d.chars2;d=d.lineArray;a=this.diff_main(a,b,!1,c);this.diff_charsToLines_(a,d);this.diff_cleanupSemantic(a);a.push(new diff_match_patch.Diff(DIFF_EQUAL,""));for(var e=d=b=0,f="",g="";b<a.length;){switch(a[b][0]){case DIFF_INSERT:e++;g+=a[b][1];break;case DIFF_DELETE:d++;f+=a[b][1];break;case DIFF_EQUAL:if(1<=d&&1<=e){a.splice(b-d-e,d+e);b=b-d-e;d=this.diff_main(f,g,!1,c);for(e=d.length-1;0<=e;e--)a.splice(b,
-0,d[e]);b+=d.length}d=e=0;g=f=""}b++}a.pop();return a};
-diff_match_patch.prototype.diff_bisect_=function(a,b,c){for(var d=a.length,e=b.length,f=Math.ceil((d+e)/2),g=2*f,h=Array(g),l=Array(g),k=0;k<g;k++)h[k]=-1,l[k]=-1;h[f+1]=0;l[f+1]=0;k=d-e;for(var m=0!=k%2,p=0,x=0,w=0,q=0,t=0;t<f&&!((new Date).getTime()>c);t++){for(var v=-t+p;v<=t-x;v+=2){var n=f+v;var r=v==-t||v!=t&&h[n-1]<h[n+1]?h[n+1]:h[n-1]+1;for(var y=r-v;r<d&&y<e&&a.charAt(r)==b.charAt(y);)r++,y++;h[n]=r;if(r>d)x+=2;else if(y>e)p+=2;else if(m&&(n=f+k-v,0<=n&&n<g&&-1!=l[n])){var u=d-l[n];if(r>=
-u)return this.diff_bisectSplit_(a,b,r,y,c)}}for(v=-t+w;v<=t-q;v+=2){n=f+v;u=v==-t||v!=t&&l[n-1]<l[n+1]?l[n+1]:l[n-1]+1;for(r=u-v;u<d&&r<e&&a.charAt(d-u-1)==b.charAt(e-r-1);)u++,r++;l[n]=u;if(u>d)q+=2;else if(r>e)w+=2;else if(!m&&(n=f+k-v,0<=n&&n<g&&-1!=h[n]&&(r=h[n],y=f+r-n,u=d-u,r>=u)))return this.diff_bisectSplit_(a,b,r,y,c)}}return[new diff_match_patch.Diff(DIFF_DELETE,a),new diff_match_patch.Diff(DIFF_INSERT,b)]};
-diff_match_patch.prototype.diff_bisectSplit_=function(a,b,c,d,e){var f=a.substring(0,c),g=b.substring(0,d);a=a.substring(c);b=b.substring(d);f=this.diff_main(f,g,!1,e);e=this.diff_main(a,b,!1,e);return f.concat(e)};
-diff_match_patch.prototype.diff_linesToChars_=function(a,b){function c(a){for(var b="",c=0,g=-1,h=d.length;g<a.length-1;){g=a.indexOf("\n",c);-1==g&&(g=a.length-1);var l=a.substring(c,g+1);(e.hasOwnProperty?e.hasOwnProperty(l):void 0!==e[l])?b+=String.fromCharCode(e[l]):(h==f&&(l=a.substring(c),g=a.length),b+=String.fromCharCode(h),e[l]=h,d[h++]=l);c=g+1}return b}var d=[],e={};d[0]="";var f=4E4,g=c(a);f=65535;var h=c(b);return{chars1:g,chars2:h,lineArray:d}};
-diff_match_patch.prototype.diff_charsToLines_=function(a,b){for(var c=0;c<a.length;c++){for(var d=a[c][1],e=[],f=0;f<d.length;f++)e[f]=b[d.charCodeAt(f)];a[c][1]=e.join("")}};diff_match_patch.prototype.diff_commonPrefix=function(a,b){if(!a||!b||a.charAt(0)!=b.charAt(0))return 0;for(var c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(f,e)==b.substring(f,e)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return e};
-diff_match_patch.prototype.diff_commonSuffix=function(a,b){if(!a||!b||a.charAt(a.length-1)!=b.charAt(b.length-1))return 0;for(var c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(a.length-e,a.length-f)==b.substring(b.length-e,b.length-f)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return e};
-diff_match_patch.prototype.diff_commonOverlap_=function(a,b){var c=a.length,d=b.length;if(0==c||0==d)return 0;c>d?a=a.substring(c-d):c<d&&(b=b.substring(0,c));c=Math.min(c,d);if(a==b)return c;d=0;for(var e=1;;){var f=a.substring(c-e);f=b.indexOf(f);if(-1==f)return d;e+=f;if(0==f||a.substring(c-e)==b.substring(0,e))d=e,e++}};
-diff_match_patch.prototype.diff_halfMatch_=function(a,b){function c(a,b,c){for(var d=a.substring(c,c+Math.floor(a.length/4)),e=-1,g="",h,k,l,m;-1!=(e=b.indexOf(d,e+1));){var p=f.diff_commonPrefix(a.substring(c),b.substring(e)),u=f.diff_commonSuffix(a.substring(0,c),b.substring(0,e));g.length<u+p&&(g=b.substring(e-u,e)+b.substring(e,e+p),h=a.substring(0,c-u),k=a.substring(c+p),l=b.substring(0,e-u),m=b.substring(e+p))}return 2*g.length>=a.length?[h,k,l,m,g]:null}if(0>=this.Diff_Timeout)return null;
-var d=a.length>b.length?a:b,e=a.length>b.length?b:a;if(4>d.length||2*e.length<d.length)return null;var f=this,g=c(d,e,Math.ceil(d.length/4));d=c(d,e,Math.ceil(d.length/2));if(g||d)g=d?g?g[4].length>d[4].length?g:d:d:g;else return null;if(a.length>b.length){d=g[0];e=g[1];var h=g[2];var l=g[3]}else h=g[0],l=g[1],d=g[2],e=g[3];return[d,e,h,l,g[4]]};
-diff_match_patch.prototype.diff_cleanupSemantic=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=0,h=0,l=0,k=0;f<a.length;)a[f][0]==DIFF_EQUAL?(c[d++]=f,g=l,h=k,k=l=0,e=a[f][1]):(a[f][0]==DIFF_INSERT?l+=a[f][1].length:k+=a[f][1].length,e&&e.length<=Math.max(g,h)&&e.length<=Math.max(l,k)&&(a.splice(c[d-1],0,new diff_match_patch.Diff(DIFF_DELETE,e)),a[c[d-1]+1][0]=DIFF_INSERT,d--,d--,f=0<d?c[d-1]:-1,k=l=h=g=0,e=null,b=!0)),f++;b&&this.diff_cleanupMerge(a);this.diff_cleanupSemanticLossless(a);for(f=1;f<
-a.length;){if(a[f-1][0]==DIFF_DELETE&&a[f][0]==DIFF_INSERT){b=a[f-1][1];c=a[f][1];d=this.diff_commonOverlap_(b,c);e=this.diff_commonOverlap_(c,b);if(d>=e){if(d>=b.length/2||d>=c.length/2)a.splice(f,0,new diff_match_patch.Diff(DIFF_EQUAL,c.substring(0,d))),a[f-1][1]=b.substring(0,b.length-d),a[f+1][1]=c.substring(d),f++}else if(e>=b.length/2||e>=c.length/2)a.splice(f,0,new diff_match_patch.Diff(DIFF_EQUAL,b.substring(0,e))),a[f-1][0]=DIFF_INSERT,a[f-1][1]=c.substring(0,c.length-e),a[f+1][0]=DIFF_DELETE,
-a[f+1][1]=b.substring(e),f++;f++}f++}};
-diff_match_patch.prototype.diff_cleanupSemanticLossless=function(a){function b(a,b){if(!a||!b)return 6;var c=a.charAt(a.length-1),d=b.charAt(0),e=c.match(diff_match_patch.nonAlphaNumericRegex_),f=d.match(diff_match_patch.nonAlphaNumericRegex_),g=e&&c.match(diff_match_patch.whitespaceRegex_),h=f&&d.match(diff_match_patch.whitespaceRegex_);c=g&&c.match(diff_match_patch.linebreakRegex_);d=h&&d.match(diff_match_patch.linebreakRegex_);var k=c&&a.match(diff_match_patch.blanklineEndRegex_),l=d&&b.match(diff_match_patch.blanklineStartRegex_);
-return k||l?5:c||d?4:e&&!g&&h?3:g||h?2:e||f?1:0}for(var c=1;c<a.length-1;){if(a[c-1][0]==DIFF_EQUAL&&a[c+1][0]==DIFF_EQUAL){var d=a[c-1][1],e=a[c][1],f=a[c+1][1],g=this.diff_commonSuffix(d,e);if(g){var h=e.substring(e.length-g);d=d.substring(0,d.length-g);e=h+e.substring(0,e.length-g);f=h+f}g=d;h=e;for(var l=f,k=b(d,e)+b(e,f);e.charAt(0)===f.charAt(0);){d+=e.charAt(0);e=e.substring(1)+f.charAt(0);f=f.substring(1);var m=b(d,e)+b(e,f);m>=k&&(k=m,g=d,h=e,l=f)}a[c-1][1]!=g&&(g?a[c-1][1]=g:(a.splice(c-
-1,1),c--),a[c][1]=h,l?a[c+1][1]=l:(a.splice(c+1,1),c--))}c++}};diff_match_patch.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/;diff_match_patch.whitespaceRegex_=/\s/;diff_match_patch.linebreakRegex_=/[\r\n]/;diff_match_patch.blanklineEndRegex_=/\n\r?\n$/;diff_match_patch.blanklineStartRegex_=/^\r?\n\r?\n/;
-diff_match_patch.prototype.diff_cleanupEfficiency=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=!1,h=!1,l=!1,k=!1;f<a.length;)a[f][0]==DIFF_EQUAL?(a[f][1].length<this.Diff_EditCost&&(l||k)?(c[d++]=f,g=l,h=k,e=a[f][1]):(d=0,e=null),l=k=!1):(a[f][0]==DIFF_DELETE?k=!0:l=!0,e&&(g&&h&&l&&k||e.length<this.Diff_EditCost/2&&3==g+h+l+k)&&(a.splice(c[d-1],0,new diff_match_patch.Diff(DIFF_DELETE,e)),a[c[d-1]+1][0]=DIFF_INSERT,d--,e=null,g&&h?(l=k=!0,d=0):(d--,f=0<d?c[d-1]:-1,l=k=!1),b=!0)),f++;b&&this.diff_cleanupMerge(a)};
-diff_match_patch.prototype.diff_cleanupMerge=function(a){a.push(new diff_match_patch.Diff(DIFF_EQUAL,""));for(var b=0,c=0,d=0,e="",f="",g;b<a.length;)switch(a[b][0]){case DIFF_INSERT:d++;f+=a[b][1];b++;break;case DIFF_DELETE:c++;e+=a[b][1];b++;break;case DIFF_EQUAL:1<c+d?(0!==c&&0!==d&&(g=this.diff_commonPrefix(f,e),0!==g&&(0<b-c-d&&a[b-c-d-1][0]==DIFF_EQUAL?a[b-c-d-1][1]+=f.substring(0,g):(a.splice(0,0,new diff_match_patch.Diff(DIFF_EQUAL,f.substring(0,g))),b++),f=f.substring(g),e=e.substring(g)),
-g=this.diff_commonSuffix(f,e),0!==g&&(a[b][1]=f.substring(f.length-g)+a[b][1],f=f.substring(0,f.length-g),e=e.substring(0,e.length-g))),b-=c+d,a.splice(b,c+d),e.length&&(a.splice(b,0,new diff_match_patch.Diff(DIFF_DELETE,e)),b++),f.length&&(a.splice(b,0,new diff_match_patch.Diff(DIFF_INSERT,f)),b++),b++):0!==b&&a[b-1][0]==DIFF_EQUAL?(a[b-1][1]+=a[b][1],a.splice(b,1)):b++,c=d=0,f=e=""}""===a[a.length-1][1]&&a.pop();c=!1;for(b=1;b<a.length-1;)a[b-1][0]==DIFF_EQUAL&&a[b+1][0]==DIFF_EQUAL&&(a[b][1].substring(a[b][1].length-
-a[b-1][1].length)==a[b-1][1]?(a[b][1]=a[b-1][1]+a[b][1].substring(0,a[b][1].length-a[b-1][1].length),a[b+1][1]=a[b-1][1]+a[b+1][1],a.splice(b-1,1),c=!0):a[b][1].substring(0,a[b+1][1].length)==a[b+1][1]&&(a[b-1][1]+=a[b+1][1],a[b][1]=a[b][1].substring(a[b+1][1].length)+a[b+1][1],a.splice(b+1,1),c=!0)),b++;c&&this.diff_cleanupMerge(a)};
-diff_match_patch.prototype.diff_xIndex=function(a,b){var c=0,d=0,e=0,f=0,g;for(g=0;g<a.length;g++){a[g][0]!==DIFF_INSERT&&(c+=a[g][1].length);a[g][0]!==DIFF_DELETE&&(d+=a[g][1].length);if(c>b)break;e=c;f=d}return a.length!=g&&a[g][0]===DIFF_DELETE?f:f+(b-e)};
-diff_match_patch.prototype.diff_prettyHtml=function(a){for(var b=[],c=/&/g,d=/</g,e=/>/g,f=/\n/g,g=0;g<a.length;g++){var h=a[g][0],l=a[g][1].replace(c,"&amp;").replace(d,"&lt;").replace(e,"&gt;").replace(f,"&para;<br>");switch(h){case DIFF_INSERT:b[g]='<ins style="background:#e6ffe6;">'+l+"</ins>";break;case DIFF_DELETE:b[g]='<del style="background:#ffe6e6;">'+l+"</del>";break;case DIFF_EQUAL:b[g]="<span>"+l+"</span>"}}return b.join("")};
-diff_match_patch.prototype.diff_text1=function(a){for(var b=[],c=0;c<a.length;c++)a[c][0]!==DIFF_INSERT&&(b[c]=a[c][1]);return b.join("")};diff_match_patch.prototype.diff_text2=function(a){for(var b=[],c=0;c<a.length;c++)a[c][0]!==DIFF_DELETE&&(b[c]=a[c][1]);return b.join("")};
-diff_match_patch.prototype.diff_levenshtein=function(a){for(var b=0,c=0,d=0,e=0;e<a.length;e++){var f=a[e][1];switch(a[e][0]){case DIFF_INSERT:c+=f.length;break;case DIFF_DELETE:d+=f.length;break;case DIFF_EQUAL:b+=Math.max(c,d),d=c=0}}return b+=Math.max(c,d)};
-diff_match_patch.prototype.diff_toDelta=function(a){for(var b=[],c=0;c<a.length;c++)switch(a[c][0]){case DIFF_INSERT:b[c]="+"+encodeURI(a[c][1]);break;case DIFF_DELETE:b[c]="-"+a[c][1].length;break;case DIFF_EQUAL:b[c]="="+a[c][1].length}return b.join("\t").replace(/%20/g," ")};
-diff_match_patch.prototype.diff_fromDelta=function(a,b){for(var c=[],d=0,e=0,f=b.split(/\t/g),g=0;g<f.length;g++){var h=f[g].substring(1);switch(f[g].charAt(0)){case "+":try{c[d++]=new diff_match_patch.Diff(DIFF_INSERT,decodeURI(h))}catch(k){throw Error("Illegal escape in diff_fromDelta: "+h);}break;case "-":case "=":var l=parseInt(h,10);if(isNaN(l)||0>l)throw Error("Invalid number in diff_fromDelta: "+h);h=a.substring(e,e+=l);"="==f[g].charAt(0)?c[d++]=new diff_match_patch.Diff(DIFF_EQUAL,h):c[d++]=
-new diff_match_patch.Diff(DIFF_DELETE,h);break;default:if(f[g])throw Error("Invalid diff operation in diff_fromDelta: "+f[g]);}}if(e!=a.length)throw Error("Delta length ("+e+") does not equal source text length ("+a.length+").");return c};diff_match_patch.prototype.match_main=function(a,b,c){if(null==a||null==b||null==c)throw Error("Null input. (match_main)");c=Math.max(0,Math.min(c,a.length));return a==b?0:a.length?a.substring(c,c+b.length)==b?c:this.match_bitap_(a,b,c):-1};
-diff_match_patch.prototype.match_bitap_=function(a,b,c){function d(a,d){var e=a/b.length,g=Math.abs(c-d);return f.Match_Distance?e+g/f.Match_Distance:g?1:e}if(b.length>this.Match_MaxBits)throw Error("Pattern too long for this browser.");var e=this.match_alphabet_(b),f=this,g=this.Match_Threshold,h=a.indexOf(b,c);-1!=h&&(g=Math.min(d(0,h),g),h=a.lastIndexOf(b,c+b.length),-1!=h&&(g=Math.min(d(0,h),g)));var l=1<<b.length-1;h=-1;for(var k,m,p=b.length+a.length,x,w=0;w<b.length;w++){k=0;for(m=p;k<m;)d(w,
-c+m)<=g?k=m:p=m,m=Math.floor((p-k)/2+k);p=m;k=Math.max(1,c-m+1);var q=Math.min(c+m,a.length)+b.length;m=Array(q+2);for(m[q+1]=(1<<w)-1;q>=k;q--){var t=e[a.charAt(q-1)];m[q]=0===w?(m[q+1]<<1|1)&t:(m[q+1]<<1|1)&t|(x[q+1]|x[q])<<1|1|x[q+1];if(m[q]&l&&(t=d(w,q-1),t<=g))if(g=t,h=q-1,h>c)k=Math.max(1,2*c-h);else break}if(d(w+1,c)>g)break;x=m}return h};
-diff_match_patch.prototype.match_alphabet_=function(a){for(var b={},c=0;c<a.length;c++)b[a.charAt(c)]=0;for(c=0;c<a.length;c++)b[a.charAt(c)]|=1<<a.length-c-1;return b};
-diff_match_patch.prototype.patch_addContext_=function(a,b){if(0!=b.length){if(null===a.start2)throw Error("patch not initialized");for(var c=b.substring(a.start2,a.start2+a.length1),d=0;b.indexOf(c)!=b.lastIndexOf(c)&&c.length<this.Match_MaxBits-this.Patch_Margin-this.Patch_Margin;)d+=this.Patch_Margin,c=b.substring(a.start2-d,a.start2+a.length1+d);d+=this.Patch_Margin;(c=b.substring(a.start2-d,a.start2))&&a.diffs.unshift(new diff_match_patch.Diff(DIFF_EQUAL,c));(d=b.substring(a.start2+a.length1,
-a.start2+a.length1+d))&&a.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL,d));a.start1-=c.length;a.start2-=c.length;a.length1+=c.length+d.length;a.length2+=c.length+d.length}};
-diff_match_patch.prototype.patch_make=function(a,b,c){if("string"==typeof a&&"string"==typeof b&&"undefined"==typeof c){var d=a;b=this.diff_main(d,b,!0);2<b.length&&(this.diff_cleanupSemantic(b),this.diff_cleanupEfficiency(b))}else if(a&&"object"==typeof a&&"undefined"==typeof b&&"undefined"==typeof c)b=a,d=this.diff_text1(b);else if("string"==typeof a&&b&&"object"==typeof b&&"undefined"==typeof c)d=a;else if("string"==typeof a&&"string"==typeof b&&c&&"object"==typeof c)d=a,b=c;else throw Error("Unknown call format to patch_make.");
-if(0===b.length)return[];c=[];a=new diff_match_patch.patch_obj;for(var e=0,f=0,g=0,h=d,l=0;l<b.length;l++){var k=b[l][0],m=b[l][1];e||k===DIFF_EQUAL||(a.start1=f,a.start2=g);switch(k){case DIFF_INSERT:a.diffs[e++]=b[l];a.length2+=m.length;d=d.substring(0,g)+m+d.substring(g);break;case DIFF_DELETE:a.length1+=m.length;a.diffs[e++]=b[l];d=d.substring(0,g)+d.substring(g+m.length);break;case DIFF_EQUAL:m.length<=2*this.Patch_Margin&&e&&b.length!=l+1?(a.diffs[e++]=b[l],a.length1+=m.length,a.length2+=m.length):
-m.length>=2*this.Patch_Margin&&e&&(this.patch_addContext_(a,h),c.push(a),a=new diff_match_patch.patch_obj,e=0,h=d,f=g)}k!==DIFF_INSERT&&(f+=m.length);k!==DIFF_DELETE&&(g+=m.length)}e&&(this.patch_addContext_(a,h),c.push(a));return c};
-diff_match_patch.prototype.patch_deepCopy=function(a){for(var b=[],c=0;c<a.length;c++){var d=a[c],e=new diff_match_patch.patch_obj;e.diffs=[];for(var f=0;f<d.diffs.length;f++)e.diffs[f]=new diff_match_patch.Diff(d.diffs[f][0],d.diffs[f][1]);e.start1=d.start1;e.start2=d.start2;e.length1=d.length1;e.length2=d.length2;b[c]=e}return b};
-diff_match_patch.prototype.patch_apply=function(a,b){if(0==a.length)return[b,[]];a=this.patch_deepCopy(a);var c=this.patch_addPadding(a);b=c+b+c;this.patch_splitMax(a);for(var d=0,e=[],f=0;f<a.length;f++){var g=a[f].start2+d,h=this.diff_text1(a[f].diffs),l=-1;if(h.length>this.Match_MaxBits){var k=this.match_main(b,h.substring(0,this.Match_MaxBits),g);-1!=k&&(l=this.match_main(b,h.substring(h.length-this.Match_MaxBits),g+h.length-this.Match_MaxBits),-1==l||k>=l)&&(k=-1)}else k=this.match_main(b,h,
-g);if(-1==k)e[f]=!1,d-=a[f].length2-a[f].length1;else if(e[f]=!0,d=k-g,g=-1==l?b.substring(k,k+h.length):b.substring(k,l+this.Match_MaxBits),h==g)b=b.substring(0,k)+this.diff_text2(a[f].diffs)+b.substring(k+h.length);else if(g=this.diff_main(h,g,!1),h.length>this.Match_MaxBits&&this.diff_levenshtein(g)/h.length>this.Patch_DeleteThreshold)e[f]=!1;else{this.diff_cleanupSemanticLossless(g);h=0;var m;for(l=0;l<a[f].diffs.length;l++){var p=a[f].diffs[l];p[0]!==DIFF_EQUAL&&(m=this.diff_xIndex(g,h));p[0]===
-DIFF_INSERT?b=b.substring(0,k+m)+p[1]+b.substring(k+m):p[0]===DIFF_DELETE&&(b=b.substring(0,k+m)+b.substring(k+this.diff_xIndex(g,h+p[1].length)));p[0]!==DIFF_DELETE&&(h+=p[1].length)}}}b=b.substring(c.length,b.length-c.length);return[b,e]};
-diff_match_patch.prototype.patch_addPadding=function(a){for(var b=this.Patch_Margin,c="",d=1;d<=b;d++)c+=String.fromCharCode(d);for(d=0;d<a.length;d++)a[d].start1+=b,a[d].start2+=b;d=a[0];var e=d.diffs;if(0==e.length||e[0][0]!=DIFF_EQUAL)e.unshift(new diff_match_patch.Diff(DIFF_EQUAL,c)),d.start1-=b,d.start2-=b,d.length1+=b,d.length2+=b;else if(b>e[0][1].length){var f=b-e[0][1].length;e[0][1]=c.substring(e[0][1].length)+e[0][1];d.start1-=f;d.start2-=f;d.length1+=f;d.length2+=f}d=a[a.length-1];e=d.diffs;
-0==e.length||e[e.length-1][0]!=DIFF_EQUAL?(e.push(new diff_match_patch.Diff(DIFF_EQUAL,c)),d.length1+=b,d.length2+=b):b>e[e.length-1][1].length&&(f=b-e[e.length-1][1].length,e[e.length-1][1]+=c.substring(0,f),d.length1+=f,d.length2+=f);return c};
-diff_match_patch.prototype.patch_splitMax=function(a){for(var b=this.Match_MaxBits,c=0;c<a.length;c++)if(!(a[c].length1<=b)){var d=a[c];a.splice(c--,1);for(var e=d.start1,f=d.start2,g="";0!==d.diffs.length;){var h=new diff_match_patch.patch_obj,l=!0;h.start1=e-g.length;h.start2=f-g.length;""!==g&&(h.length1=h.length2=g.length,h.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL,g)));for(;0!==d.diffs.length&&h.length1<b-this.Patch_Margin;){g=d.diffs[0][0];var k=d.diffs[0][1];g===DIFF_INSERT?(h.length2+=
-k.length,f+=k.length,h.diffs.push(d.diffs.shift()),l=!1):g===DIFF_DELETE&&1==h.diffs.length&&h.diffs[0][0]==DIFF_EQUAL&&k.length>2*b?(h.length1+=k.length,e+=k.length,l=!1,h.diffs.push(new diff_match_patch.Diff(g,k)),d.diffs.shift()):(k=k.substring(0,b-h.length1-this.Patch_Margin),h.length1+=k.length,e+=k.length,g===DIFF_EQUAL?(h.length2+=k.length,f+=k.length):l=!1,h.diffs.push(new diff_match_patch.Diff(g,k)),k==d.diffs[0][1]?d.diffs.shift():d.diffs[0][1]=d.diffs[0][1].substring(k.length))}g=this.diff_text2(h.diffs);
-g=g.substring(g.length-this.Patch_Margin);k=this.diff_text1(d.diffs).substring(0,this.Patch_Margin);""!==k&&(h.length1+=k.length,h.length2+=k.length,0!==h.diffs.length&&h.diffs[h.diffs.length-1][0]===DIFF_EQUAL?h.diffs[h.diffs.length-1][1]+=k:h.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL,k)));l||a.splice(++c,0,h)}}};diff_match_patch.prototype.patch_toText=function(a){for(var b=[],c=0;c<a.length;c++)b[c]=a[c];return b.join("")};
-diff_match_patch.prototype.patch_fromText=function(a){var b=[];if(!a)return b;a=a.split("\n");for(var c=0,d=/^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/;c<a.length;){var e=a[c].match(d);if(!e)throw Error("Invalid patch string: "+a[c]);var f=new diff_match_patch.patch_obj;b.push(f);f.start1=parseInt(e[1],10);""===e[2]?(f.start1--,f.length1=1):"0"==e[2]?f.length1=0:(f.start1--,f.length1=parseInt(e[2],10));f.start2=parseInt(e[3],10);""===e[4]?(f.start2--,f.length2=1):"0"==e[4]?f.length2=0:(f.start2--,f.length2=
-parseInt(e[4],10));for(c++;c<a.length;){e=a[c].charAt(0);try{var g=decodeURI(a[c].substring(1))}catch(h){throw Error("Illegal escape in patch_fromText: "+g);}if("-"==e)f.diffs.push(new diff_match_patch.Diff(DIFF_DELETE,g));else if("+"==e)f.diffs.push(new diff_match_patch.Diff(DIFF_INSERT,g));else if(" "==e)f.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL,g));else if("@"==e)break;else if(""!==e)throw Error('Invalid patch mode "'+e+'" in: '+g);c++}}return b};
-diff_match_patch.patch_obj=function(){this.diffs=[];this.start2=this.start1=null;this.length2=this.length1=0};
-diff_match_patch.patch_obj.prototype.toString=function(){for(var a=["@@ -"+(0===this.length1?this.start1+",0":1==this.length1?this.start1+1:this.start1+1+","+this.length1)+" +"+(0===this.length2?this.start2+",0":1==this.length2?this.start2+1:this.start2+1+","+this.length2)+" @@\n"],b,c=0;c<this.diffs.length;c++){switch(this.diffs[c][0]){case DIFF_INSERT:b="+";break;case DIFF_DELETE:b="-";break;case DIFF_EQUAL:b=" "}a[c+1]=b+encodeURI(this.diffs[c][1])+"\n"}return a.join("").replace(/%20/g," ")};
-this.diff_match_patch=diff_match_patch;this.DIFF_DELETE=DIFF_DELETE;this.DIFF_INSERT=DIFF_INSERT;this.DIFF_EQUAL=DIFF_EQUAL;
-
-window.diff_match_patch = diff_match_patch
-window.DIFF_INSERT = DIFF_INSERT
-window.DIFF_DELETE = DIFF_DELETE
-window.DIFF_EQUAL = DIFF_EQUAL

+ 0 - 12
client/libs/markdown-it-underline/index.js

@@ -1,12 +0,0 @@
-const renderEm = (tokens, idx, opts, env, slf) => {
-  const token = tokens[idx];
-  if (token.markup === '_') {
-    token.tag = 'u';
-  }
-  return slf.renderToken(tokens, idx, opts);
-}
-
-module.exports = (md) => {
-  md.renderer.rules.em_open = renderEm;
-  md.renderer.rules.em_close = renderEm;
-}

+ 0 - 3
client/libs/modernizr/modernizr.js

@@ -1,3 +0,0 @@
-/*! modernizr 3.6.0 (Custom Build) | MIT *
- * https://modernizr.com/download/?-setclasses !*/
-!function(n,e,s){function o(n){var e=r.className,s=Modernizr._config.classPrefix||"";if(c&&(e=e.baseVal),Modernizr._config.enableJSClass){var o=new RegExp("(^|\\s)"+s+"no-js(\\s|$)");e=e.replace(o,"$1"+s+"js$2")}Modernizr._config.enableClasses&&(e+=" "+s+n.join(" "+s),c?r.className.baseVal=e:r.className=e)}function a(n,e){return typeof n===e}function i(){var n,e,s,o,i,l,r;for(var c in f)if(f.hasOwnProperty(c)){if(n=[],e=f[c],e.name&&(n.push(e.name.toLowerCase()),e.options&&e.options.aliases&&e.options.aliases.length))for(s=0;s<e.options.aliases.length;s++)n.push(e.options.aliases[s].toLowerCase());for(o=a(e.fn,"function")?e.fn():e.fn,i=0;i<n.length;i++)l=n[i],r=l.split("."),1===r.length?Modernizr[r[0]]=o:(!Modernizr[r[0]]||Modernizr[r[0]]instanceof Boolean||(Modernizr[r[0]]=new Boolean(Modernizr[r[0]])),Modernizr[r[0]][r[1]]=o),t.push((o?"":"no-")+r.join("-"))}}var t=[],f=[],l={_version:"3.6.0",_config:{classPrefix:"",enableClasses:!0,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(n,e){var s=this;setTimeout(function(){e(s[n])},0)},addTest:function(n,e,s){f.push({name:n,fn:e,options:s})},addAsyncTest:function(n){f.push({name:null,fn:n})}},Modernizr=function(){};Modernizr.prototype=l,Modernizr=new Modernizr;var r=e.documentElement,c="svg"===r.nodeName.toLowerCase();i(),o(t),delete l.addTest,delete l.addAsyncTest;for(var u=0;u<Modernizr._q.length;u++)Modernizr._q[u]();n.Modernizr=Modernizr}(window,document);

+ 0 - 172
client/libs/prism/prism.css

@@ -1,172 +0,0 @@
-/* PrismJS 1.11.0
-http://prismjs.com/download.html?themes=prism-dark&languages=markup+css+clike+javascript+c+bash+basic+cpp+csharp+arduino+ruby+elixir+fsharp+go+graphql+handlebars+haskell+ini+java+json+kotlin+latex+less+makefile+markdown+matlab+nginx+objectivec+perl+php+powershell+pug+python+typescript+rust+scss+scala+smalltalk+sql+stylus+swift+vbnet+yaml&plugins=line-numbers */
-/**
- * prism.js Dark theme for JavaScript, CSS and HTML
- * Based on the slides of the talk “/Reg(exp){2}lained/”
- * @author Lea Verou
- */
-
-code[class*="language-"],
-pre[class*="language-"] {
-	color: white;
-	background: none;
-	text-shadow: 0 -.1em .2em black;
-	font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
-	text-align: left;
-	white-space: pre;
-	word-spacing: normal;
-	word-break: normal;
-	word-wrap: normal;
-	line-height: 1.5;
-
-	-moz-tab-size: 4;
-	-o-tab-size: 4;
-	tab-size: 4;
-
-	-webkit-hyphens: none;
-	-moz-hyphens: none;
-	-ms-hyphens: none;
-	hyphens: none;
-}
-
-@media print {
-	code[class*="language-"],
-	pre[class*="language-"] {
-		text-shadow: none;
-	}
-}
-
-pre[class*="language-"],
-:not(pre) > code[class*="language-"] {
-	background: hsl(30, 20%, 25%);
-}
-
-/* Code blocks */
-pre[class*="language-"] {
-	padding: 1em;
-	margin: .5em 0;
-	overflow: auto;
-	border: .3em solid hsl(30, 20%, 40%);
-	border-radius: .5em;
-	box-shadow: 1px 1px .5em black inset;
-}
-
-/* Inline code */
-:not(pre) > code[class*="language-"] {
-	padding: .15em .2em .05em;
-	border-radius: .3em;
-	border: .13em solid hsl(30, 20%, 40%);
-	box-shadow: 1px 1px .3em -.1em black inset;
-	white-space: normal;
-}
-
-.token.comment,
-.token.prolog,
-.token.doctype,
-.token.cdata {
-	color: hsl(30, 20%, 50%);
-}
-
-.token.punctuation {
-	opacity: .7;
-}
-
-.namespace {
-	opacity: .7;
-}
-
-.token.property,
-.token.tag,
-.token.boolean,
-.token.number,
-.token.constant,
-.token.symbol {
-	color: hsl(350, 40%, 70%);
-}
-
-.token.selector,
-.token.attr-name,
-.token.string,
-.token.char,
-.token.builtin,
-.token.inserted {
-	color: hsl(75, 70%, 60%);
-}
-
-.token.operator,
-.token.entity,
-.token.url,
-.language-css .token.string,
-.style .token.string,
-.token.variable {
-	color: hsl(40, 90%, 60%);
-}
-
-.token.atrule,
-.token.attr-value,
-.token.keyword {
-	color: hsl(350, 40%, 70%);
-}
-
-.token.regex,
-.token.important {
-	color: #e90;
-}
-
-.token.important,
-.token.bold {
-	font-weight: bold;
-}
-.token.italic {
-	font-style: italic;
-}
-
-.token.entity {
-	cursor: help;
-}
-
-.token.deleted {
-	color: red;
-}
-
-pre.line-numbers {
-	position: relative;
-	padding-left: 3.8em;
-	counter-reset: linenumber;
-}
-
-pre.line-numbers > code {
-	position: relative;
-    white-space: inherit;
-}
-
-.line-numbers .line-numbers-rows {
-	position: absolute;
-	pointer-events: none;
-	top: 0;
-	font-size: 100%;
-	left: -3.8em;
-	width: 3em; /* works for line-numbers below 1000 lines */
-	letter-spacing: -1px;
-	border-right: 1px solid #999;
-
-	-webkit-user-select: none;
-	-moz-user-select: none;
-	-ms-user-select: none;
-	user-select: none;
-
-}
-
-	.line-numbers-rows > span {
-		pointer-events: none;
-		display: block;
-		counter-increment: linenumber;
-	}
-
-		.line-numbers-rows > span:before {
-			content: counter(linenumber);
-			color: #999;
-			display: block;
-			padding-right: 0.8em;
-			text-align: right;
-		}

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 2
client/libs/prism/prism.js


+ 0 - 1106
client/libs/twemoji/twemoji-awesome.scss

@@ -1,1106 +0,0 @@
-.twa {
-  display: inline-block;
-  height: 1em;
-  width: 1em;
-  margin: 0 .05em 0 .1em;
-  vertical-align: -0.1em;
-  background-repeat: no-repeat;
-  background-position: center center;
-  background-size: 1em 1em;
-}
-
-$size-map: (
-  "lg": 1.33,
-  "2x": 2,
-  "3x": 3,
-  "4x": 4,
-  "5x": 5
-);
-
-@each $name, $size in $size-map {
-  .twa-#{$name} {
-    height: 1em * $size;
-    width: 1em * $size;
-    margin: 0 .05em * $size 0 .1em * $size;
-    vertical-align: -0.1em * $size;
-    background-size: 1em * $size 1em * $size;
-  }
-}
-
-$emoji-map: (
-  "1f604": "smile",
-  "1f606": "laughing",
-  "1f60a": "blush",
-  "1f603": "smiley",
-  "263a": "relaxed",
-  "1f60f": "smirk",
-  "1f60d": "heart-eyes",
-  "1f618": "kissing-heart",
-  "1f61a": "kissing-closed-eyes",
-  "1f633": "flushed",
-  "1f625": "relieved",
-  "1f60c": "satisfied",
-  "1f601": "grin",
-  "1f609": "wink",
-  "1f61c": "stuck-out-tongue-winking-eye",
-  "1f61d": "stuck-out-tongue-closed-eyes",
-  "1f600": "grinning",
-  "1f617": "kissing",
-  "1f619": "kissing-smiling-eyes",
-  "1f61b": "stuck-out-tongue",
-  "1f634": "sleeping",
-  "1f61f": "worried",
-  "1f626": "frowning",
-  "1f627": "anguished",
-  "1f62e": "open-mouth",
-  "1f62c": "grimacing",
-  "1f615": "confused",
-  "1f62f": "hushed",
-  "1f611": "expressionless",
-  "1f612": "unamused",
-  "1f605": "sweat-smile",
-  "1f613": "sweat",
-  "1f629": "weary",
-  "1f614": "pensive",
-  "1f61e": "disappointed",
-  "1f616": "confounded",
-  "1f628": "fearful",
-  "1f630": "cold-sweat",
-  "1f623": "persevere",
-  "1f622": "cry",
-  "1f62d": "sob",
-  "1f602": "joy",
-  "1f632": "astonished",
-  "1f631": "scream",
-  "1f62b": "tired-face",
-  "1f620": "angry",
-  "1f621": "rage",
-  "1f624": "triumph",
-  "1f62a": "sleepy",
-  "1f60b": "yum",
-  "1f637": "mask",
-  "1f60e": "sunglasses",
-  "1f635": "dizzy-face",
-  "1f47f": "imp",
-  "1f608": "smiling-imp",
-  "1f610": "neutral-face",
-  "1f636": "no-mouth",
-  "1f607": "innocent",
-  "1f47d": "alien",
-  "1f49b": "yellow-heart",
-  "1f499": "blue-heart",
-  "1f49c": "purple-heart",
-  "2764": "heart",
-  "1f49a": "green-heart",
-  "1f494": "broken-heart",
-  "1f493": "heartbeat",
-  "1f497": "heartpulse",
-  "1f495": "two-hearts",
-  "1f49e": "revolving-hearts",
-  "1f498": "cupid",
-  "1f496": "sparkling-heart",
-  "2728": "sparkles",
-  "2b50": "star",
-  "1f31f": "star2",
-  "1f4ab": "dizzy",
-  "1f4a5": "boom",
-  "1f4a2": "anger",
-  "2757": "exclamation",
-  "2753": "question",
-  "2755": "grey-exclamation",
-  "2754": "grey-question",
-  "1f4a4": "zzz",
-  "1f4a8": "dash",
-  "1f4a6": "sweat-drops",
-  "1f3b6": "notes",
-  "1f3b5": "musical-note",
-  "1f525": "fire",
-  "1f4a9": "poop",
-  "1f44d": "thumbsup",
-  "1f44e": "thumbsdown",
-  "1f44c": "ok-hand",
-  "1f44a": "punch",
-  "270a": "fist",
-  "270c": "v",
-  "1f44b": "wave",
-  "270b": "hand",
-  "1f450": "open-hands",
-  "261d": "point-up",
-  "1f447": "point-down",
-  "1f448": "point-left",
-  "1f449": "point-right",
-  "1f64c": "raised-hands",
-  "1f64f": "pray",
-  "1f446": "point-up-2",
-  "1f44f": "clap",
-  "1f4aa": "muscle",
-  "1f6b6": "walking",
-  "1f3c3": "runner",
-  "1f46b": "couple",
-  "1f46a": "family",
-  "1f46c": "two-men-holding-hands",
-  "1f46d": "two-women-holding-hands",
-  "1f483": "dancer",
-  "1f46f": "dancers",
-  "1f646": "ok-woman",
-  "1f645": "no-good",
-  "1f481": "information-desk-person",
-  "1f64b": "raised-hand",
-  "1f470": "bride-with-veil",
-  "1f64e": "person-with-pouting-face",
-  "1f64d": "person-frowning",
-  "1f647": "bow",
-  "1f48f": "couplekiss",
-  "1f491": "couple-with-heart",
-  "1f486": "massage",
-  "1f487": "haircut",
-  "1f485": "nail-care",
-  "1f466": "boy",
-  "1f467": "girl",
-  "1f469": "woman",
-  "1f468": "man",
-  "1f476": "baby",
-  "1f475": "older-woman",
-  "1f474": "older-man",
-  "1f471": "person-with-blond-hair",
-  "1f472": "man-with-gua-pi-mao",
-  "1f473": "man-with-turban",
-  "1f477": "construction-worker",
-  "1f46e": "cop",
-  "1f47c": "angel",
-  "1f478": "princess",
-  "1f63a": "smiley-cat",
-  "1f638": "smile-cat",
-  "1f63b": "heart-eyes-cat",
-  "1f63d": "kissing-cat",
-  "1f63c": "smirk-cat",
-  "1f640": "scream-cat",
-  "1f63f": "crying-cat-face",
-  "1f639": "joy-cat",
-  "1f63e": "pouting-cat",
-  "1f479": "japanese-ogre",
-  "1f47a": "japanese-goblin",
-  "1f648": "see-no-evil",
-  "1f649": "hear-no-evil",
-  "1f64a": "speak-no-evil",
-  "1f482": "guardsman",
-  "1f480": "skull",
-  "1f463": "feet",
-  "1f444": "lips",
-  "1f48b": "kiss",
-  "1f4a7": "droplet",
-  "1f442": "ear",
-  "1f440": "eyes",
-  "1f443": "nose",
-  "1f445": "tongue",
-  "1f48c": "love-letter",
-  "1f464": "bust-in-silhouette",
-  "1f465": "busts-in-silhouette",
-  "1f4ac": "speech-balloon",
-  "1f4ad": "thought-balloon",
-  "2600": "sunny",
-  "2614": "umbrella",
-  "2601": "cloud",
-  "2744": "snowflake",
-  "26c4": "snowman",
-  "26a1": "zap",
-  "1f300": "cyclone",
-  "1f301": "foggy",
-  "1f30a": "ocean",
-  "1f431": "cat",
-  "1f436": "dog",
-  "1f42d": "mouse",
-  "1f439": "hamster",
-  "1f430": "rabbit",
-  "1f43a": "wolf",
-  "1f438": "frog",
-  "1f42f": "tiger",
-  "1f428": "koala",
-  "1f43b": "bear",
-  "1f437": "pig",
-  "1f43d": "pig-nose",
-  "1f42e": "cow",
-  "1f417": "boar",
-  "1f435": "monkey-face",
-  "1f412": "monkey",
-  "1f434": "horse",
-  "1f40e": "racehorse",
-  "1f42b": "camel",
-  "1f411": "sheep",
-  "1f418": "elephant",
-  "1f43c": "panda-face",
-  "1f40d": "snake",
-  "1f426": "bird",
-  "1f424": "baby-chick",
-  "1f425": "hatched-chick",
-  "1f423": "hatching-chick",
-  "1f414": "chicken",
-  "1f427": "penguin",
-  "1f422": "turtle",
-  "1f41b": "bug",
-  "1f41d": "honeybee",
-  "1f41c": "ant",
-  "1f41e": "beetle",
-  "1f40c": "snail",
-  "1f419": "octopus",
-  "1f420": "tropical-fish",
-  "1f41f": "fish",
-  "1f433": "whale",
-  "1f40b": "whale2",
-  "1f42c": "dolphin",
-  "1f404": "cow2",
-  "1f40f": "ram",
-  "1f400": "rat",
-  "1f403": "water-buffalo",
-  "1f405": "tiger2",
-  "1f407": "rabbit2",
-  "1f409": "dragon",
-  "1f410": "goat",
-  "1f413": "rooster",
-  "1f415": "dog2",
-  "1f416": "pig2",
-  "1f401": "mouse2",
-  "1f402": "ox",
-  "1f432": "dragon-face",
-  "1f421": "blowfish",
-  "1f40a": "crocodile",
-  "1f42a": "dromedary-camel",
-  "1f406": "leopard",
-  "1f408": "cat2",
-  "1f429": "poodle",
-  "1f43e": "paw-prints",
-  "1f490": "bouquet",
-  "1f338": "cherry-blossom",
-  "1f337": "tulip",
-  "1f340": "four-leaf-clover",
-  "1f339": "rose",
-  "1f33b": "sunflower",
-  "1f33a": "hibiscus",
-  "1f341": "maple-leaf",
-  "1f343": "leaves",
-  "1f342": "fallen-leaf",
-  "1f33f": "herb",
-  "1f344": "mushroom",
-  "1f335": "cactus",
-  "1f334": "palm-tree",
-  "1f332": "evergreen-tree",
-  "1f333": "deciduous-tree",
-  "1f330": "chestnut",
-  "1f331": "seedling",
-  "1f33c": "blossom",
-  "1f33e": "ear-of-rice",
-  "1f41a": "shell",
-  "1f310": "globe-with-meridians",
-  "1f31e": "sun-with-face",
-  "1f31d": "full-moon-with-face",
-  "1f31a": "new-moon-with-face",
-  "1f311": "new-moon",
-  "1f312": "waxing-crescent-moon",
-  "1f313": "first-quarter-moon",
-  "1f314": "waxing-gibbous-moon",
-  "1f315": "full-moon",
-  "1f316": "waning-gibbous-moon",
-  "1f317": "last-quarter-moon",
-  "1f318": "waning-crescent-moon",
-  "1f31c": "last-quarter-moon-with-face",
-  "1f31b": "first-quarter-moon-with-face",
-  "1f319": "moon",
-  "1f30d": "earth-africa",
-  "1f30e": "earth-americas",
-  "1f30f": "earth-asia",
-  "1f30b": "volcano",
-  "1f30c": "milky-way",
-  "26c5": "partly-sunny",
-  "1f38d": "bamboo",
-  "1f49d": "gift-heart",
-  "1f38e": "dolls",
-  "1f392": "school-satchel",
-  "1f393": "mortar-board",
-  "1f38f": "flags",
-  "1f386": "fireworks",
-  "1f387": "sparkler",
-  "1f390": "wind-chime",
-  "1f391": "rice-scene",
-  "1f383": "jack-o-lantern",
-  "1f47b": "ghost",
-  "1f385": "santa",
-  "1f3b1": "8ball",
-  "23f0": "alarm-clock",
-  "1f34e": "apple",
-  "1f3a8": "art",
-  "1f37c": "baby-bottle",
-  "1f388": "balloon",
-  "1f34c": "banana",
-  "1f4ca": "bar-chart",
-  "26be": "baseball",
-  "1f3c0": "basketball",
-  "1f6c0": "bath",
-  "1f6c1": "bathtub",
-  "1f50b": "battery",
-  "1f37a": "beer",
-  "1f37b": "beers",
-  "1f514": "bell",
-  "1f371": "bento",
-  "1f6b4": "bicyclist",
-  "1f459": "bikini",
-  "1f382": "birthday",
-  "1f0cf": "black-joker",
-  "2712": "black-nib",
-  "1f4d8": "blue-book",
-  "1f4a3": "bomb",
-  "1f516": "bookmark",
-  "1f4d1": "bookmark-tabs",
-  "1f4da": "books",
-  "1f462": "boot",
-  "1f3b3": "bowling",
-  "1f35e": "bread",
-  "1f4bc": "briefcase",
-  "1f4a1": "bulb",
-  "1f370": "cake",
-  "1f4c6": "calendar",
-  "1f4f2": "calling",
-  "1f4f7": "camera",
-  "1f36c": "candy",
-  "1f4c7": "card-index",
-  "1f4bf": "cd",
-  "1f4c9": "chart-with-downwards-trend",
-  "1f4c8": "chart-with-upwards-trend",
-  "1f352": "cherries",
-  "1f36b": "chocolate-bar",
-  "1f384": "christmas-tree",
-  "1f3ac": "clapper",
-  "1f4cb": "clipboard",
-  "1f4d5": "closed-book",
-  "1f510": "closed-lock-with-key",
-  "1f302": "closed-umbrella",
-  "2663": "clubs",
-  "1f378": "cocktail",
-  "2615": "coffee",
-  "1f4bb": "computer",
-  "1f38a": "confetti-ball",
-  "1f36a": "cookie",
-  "1f33d": "corn",
-  "1f4b3": "credit-card",
-  "1f451": "crown",
-  "1f52e": "crystal-ball",
-  "1f35b": "curry",
-  "1f36e": "custard",
-  "1f361": "dango",
-  "1f3af": "dart",
-  "1f4c5": "date",
-  "2666": "diamonds",
-  "1f4b5": "dollar",
-  "1f6aa": "door",
-  "1f369": "doughnut",
-  "1f457": "dress",
-  "1f4c0": "dvd",
-  "1f4e7": "e-mail",
-  "1f373": "egg",
-  "1f346": "eggplant",
-  "1f50c": "electric-plug",
-  "2709": "email",
-  "1f4b6": "euro",
-  "1f453": "eyeglasses",
-  "1f4e0": "fax",
-  "1f4c1": "file-folder",
-  "1f365": "fish-cake",
-  "1f3a3": "fishing-pole-and-fish",
-  "1f526": "flashlight",
-  "1f4be": "floppy-disk",
-  "1f3b4": "flower-playing-cards",
-  "1f3c8": "football",
-  "1f374": "fork-and-knife",
-  "1f364": "fried-shrimp",
-  "1f35f": "fries",
-  "1f3b2": "game-die",
-  "1f48e": "gem",
-  "1f381": "gift",
-  "26f3": "golf",
-  "1f347": "grapes",
-  "1f34f": "green-apple",
-  "1f4d7": "green-book",
-  "1f3b8": "guitar",
-  "1f52b": "gun",
-  "1f354": "hamburger",
-  "1f528": "hammer",
-  "1f45c": "handbag",
-  "1f3a7": "headphones",
-  "2665": "hearts",
-  "1f506": "high-brightness",
-  "1f460": "high-heel",
-  "1f52a": "hocho",
-  "1f36f": "honey-pot",
-  "1f3c7": "horse-racing",
-  "231b": "hourglass",
-  "23f3": "hourglass-flowing-sand",
-  "1f368": "ice-cream",
-  "1f366": "icecream",
-  "1f4e5": "inbox-tray",
-  "1f4e8": "incoming-envelope",
-  "1f4f1": "iphone",
-  "1f456": "jeans",
-  "1f511": "key",
-  "1f458": "kimono",
-  "1f4d2": "ledger",
-  "1f34b": "lemon",
-  "1f484": "lipstick",
-  "1f512": "lock",
-  "1f50f": "lock-with-ink-pen",
-  "1f36d": "lollipop",
-  "27bf": "loop",
-  "1f4e2": "loudspeaker",
-  "1f505": "low-brightness",
-  "1f50d": "mag",
-  "1f50e": "mag-right",
-  "1f004": "mahjong",
-  "1f4eb": "mailbox",
-  "1f4ea": "mailbox-closed",
-  "1f4ec": "mailbox-with-mail",
-  "1f4ed": "mailbox-with-no-mail",
-  "1f45e": "mans-shoe",
-  "1f356": "meat-on-bone",
-  "1f4e3": "mega",
-  "1f348": "melon",
-  "1f4dd": "memo",
-  "1f3a4": "microphone",
-  "1f52c": "microscope",
-  "1f4bd": "minidisc",
-  "1f4b8": "money-with-wings",
-  "1f4b0": "moneybag",
-  "1f6b5": "mountain-bicyclist",
-  "1f3a5": "movie-camera",
-  "1f3b9": "musical-keyboard",
-  "1f3bc": "musical-score",
-  "1f507": "mute",
-  "1f4db": "name-badge",
-  "1f454": "necktie",
-  "1f4f0": "newspaper",
-  "1f515": "no-bell",
-  "1f4d3": "notebook",
-  "1f4d4": "notebook-with-decorative-cover",
-  "1f529": "nut-and-bolt",
-  "1f362": "oden",
-  "1f4c2": "open-file-folder",
-  "1f4d9": "orange-book",
-  "1f4e4": "outbox-tray",
-  "1f4c4": "page-facing-up",
-  "1f4c3": "page-with-curl",
-  "1f4df": "pager",
-  "1f4ce": "paperclip",
-  "1f351": "peach",
-  "1f350": "pear",
-  "270f": "pencil2",
-  "260e": "phone",
-  "1f48a": "pill",
-  "1f34d": "pineapple",
-  "1f355": "pizza",
-  "1f4ef": "postal-horn",
-  "1f4ee": "postbox",
-  "1f45d": "pouch",
-  "1f357": "poultry-leg",
-  "1f4b7": "pound",
-  "1f45b": "purse",
-  "1f4cc": "pushpin",
-  "1f4fb": "radio",
-  "1f35c": "ramen",
-  "1f380": "ribbon",
-  "1f35a": "rice",
-  "1f359": "rice-ball",
-  "1f358": "rice-cracker",
-  "1f48d": "ring",
-  "1f3c9": "rugby-football",
-  "1f3bd": "running-shirt-with-sash",
-  "1f376": "sake",
-  "1f461": "sandal",
-  "1f4e1": "satellite",
-  "1f3b7": "saxophone",
-  "2702": "scissors",
-  "1f4dc": "scroll",
-  "1f4ba": "seat",
-  "1f367": "shaved-ice",
-  "1f455": "shirt",
-  "1f6bf": "shower",
-  "1f3bf": "ski",
-  "1f6ac": "smoking",
-  "1f3c2": "snowboarder",
-  "26bd": "soccer",
-  "1f509": "sound",
-  "1f47e": "space-invader",
-  "2660": "spades",
-  "1f35d": "spaghetti",
-  "1f50a": "speaker",
-  "1f372": "stew",
-  "1f4cf": "straight-ruler",
-  "1f353": "strawberry",
-  "1f3c4": "surfer",
-  "1f363": "sushi",
-  "1f360": "sweet-potato",
-  "1f3ca": "swimmer",
-  "1f489": "syringe",
-  "1f389": "tada",
-  "1f38b": "tanabata-tree",
-  "1f34a": "tangerine",
-  "1f375": "tea",
-  "1f4de": "telephone-receiver",
-  "1f52d": "telescope",
-  "1f3be": "tennis",
-  "1f6bd": "toilet",
-  "1f345": "tomato",
-  "1f3a9": "tophat",
-  "1f4d0": "triangular-ruler",
-  "1f3c6": "trophy",
-  "1f379": "tropical-drink",
-  "1f3ba": "trumpet",
-  "1f4fa": "tv",
-  "1f513": "unlock",
-  "1f4fc": "vhs",
-  "1f4f9": "video-camera",
-  "1f3ae": "video-game",
-  "1f3bb": "violin",
-  "231a": "watch",
-  "1f349": "watermelon",
-  "1f377": "wine-glass",
-  "1f45a": "womans-clothes",
-  "1f452": "womans-hat",
-  "1f527": "wrench",
-  "1f4b4": "yen",
-  "1f6a1": "aerial-tramway",
-  "2708": "airplane",
-  "1f691": "ambulance",
-  "2693": "anchor",
-  "1f69b": "articulated-lorry",
-  "1f3e7": "atm",
-  "1f3e6": "bank",
-  "1f488": "barber",
-  "1f530": "beginner",
-  "1f6b2": "bike",
-  "1f699": "blue-car",
-  "26f5": "boat",
-  "1f309": "bridge-at-night",
-  "1f685": "bullettrain-front",
-  "1f684": "bullettrain-side",
-  "1f68c": "bus",
-  "1f68f": "busstop",
-  "1f697": "car",
-  "1f3a0": "carousel-horse",
-  "1f3c1": "checkered-flag",
-  "26ea": "church",
-  "1f3aa": "circus-tent",
-  "1f307": "city-sunrise",
-  "1f306": "city-sunset",
-  "1f6a7": "construction",
-  "1f3ea": "convenience-store",
-  "1f38c": "crossed-flags",
-  "1f3ec": "department-store",
-  "1f3f0": "european-castle",
-  "1f3e4": "european-post-office",
-  "1f3ed": "factory",
-  "1f3a1": "ferris-wheel",
-  "1f692": "fire-engine",
-  "26f2": "fountain",
-  "26fd": "fuelpump",
-  "1f681": "helicopter",
-  "1f3e5": "hospital",
-  "1f3e8": "hotel",
-  "2668": "hotsprings",
-  "1f3e0": "house",
-  "1f3e1": "house-with-garden",
-  "1f5fe": "japan",
-  "1f3ef": "japanese-castle",
-  "1f688": "light-rail",
-  "1f3e9": "love-hotel",
-  "1f690": "minibus",
-  "1f69d": "monorail",
-  "1f5fb": "mount-fuji",
-  "1f6a0": "mountain-cableway",
-  "1f69e": "mountain-railway",
-  "1f5ff": "moyai",
-  "1f3e2": "office",
-  "1f698": "oncoming-automobile",
-  "1f68d": "oncoming-bus",
-  "1f694": "oncoming-police-car",
-  "1f696": "oncoming-taxi",
-  "1f3ad": "performing-arts",
-  "1f693": "police-car",
-  "1f3e3": "post-office",
-  "1f683": "railway-car",
-  "1f308": "rainbow",
-  "1f680": "rocket",
-  "1f3a2": "roller-coaster",
-  "1f6a8": "rotating-light",
-  "1f4cd": "round-pushpin",
-  "1f6a3": "rowboat",
-  "1f3eb": "school",
-  "1f6a2": "ship",
-  "1f3b0": "slot-machine",
-  "1f6a4": "speedboat",
-  "1f303": "stars",
-  "1f689": "station",
-  "1f5fd": "statue-of-liberty",
-  "1f682": "steam-locomotive",
-  "1f305": "sunrise",
-  "1f304": "sunrise-over-mountains",
-  "1f69f": "suspension-railway",
-  "1f695": "taxi",
-  "26fa": "tent",
-  "1f3ab": "ticket",
-  "1f5fc": "tokyo-tower",
-  "1f69c": "tractor",
-  "1f6a5": "traffic-light",
-  "1f686": "train2",
-  "1f68a": "tram",
-  "1f6a9": "triangular-flag-on-post",
-  "1f68e": "trolleybus",
-  "1f69a": "truck",
-  "1f6a6": "vertical-traffic-light",
-  "26a0": "warning",
-  "1f492": "wedding",
-  "1f1e6-1f1e9": "flag-ad",
-  "1f1e6-1f1ea": "flag-ae",
-  "1f1e6-1f1eb": "flag-af",
-  "1f1e6-1f1ec": "flag-ag",
-  "1f1e6-1f1ee": "flag-ai",
-  "1f1e6-1f1f1": "flag-al",
-  "1f1e6-1f1f2": "flag-am",
-  "1f1e6-1f1f4": "flag-ao",
-  "1f1e6-1f1f6": "flag-aq",
-  "1f1e6-1f1f7": "flag-ar",
-  "1f1e6-1f1f8": "flag-as",
-  "1f1e6-1f1f9": "flag-at",
-  "1f1e6-1f1fa": "flag-au",
-  "1f1e6-1f1fc": "flag-aw",
-  "1f1e6-1f1fd": "flag-ax",
-  "1f1e6-1f1ff": "flag-az",
-  "1f1e7-1f1e6": "flag-ba",
-  "1f1e7-1f1e7": "flag-bb",
-  "1f1e7-1f1e9": "flag-bd",
-  "1f1e7-1f1ea": "flag-be",
-  "1f1e7-1f1eb": "flag-bf",
-  "1f1e7-1f1ec": "flag-bg",
-  "1f1e7-1f1ed": "flag-bh",
-  "1f1e7-1f1ee": "flag-bi",
-  "1f1e7-1f1ef": "flag-bj",
-  "1f1e7-1f1f1": "flag-bl",
-  "1f1e7-1f1f2": "flag-bm",
-  "1f1e7-1f1f3": "flag-bn",
-  "1f1e7-1f1f4": "flag-bo",
-  "1f1e7-1f1f6": "flag-bq",
-  "1f1e7-1f1f7": "flag-br",
-  "1f1e7-1f1f8": "flag-bs",
-  "1f1e7-1f1f9": "flag-bt",
-  "1f1e7-1f1fb": "flag-bv",
-  "1f1e7-1f1fc": "flag-bw",
-  "1f1e7-1f1fe": "flag-by",
-  "1f1e7-1f1ff": "flag-bz",
-  "1f1e8-1f1e6": "flag-ca",
-  "1f1e8-1f1e8": "flag-cc",
-  "1f1e8-1f1e9": "flag-cd",
-  "1f1e8-1f1eb": "flag-cf",
-  "1f1e8-1f1ec": "flag-cg",
-  "1f1e8-1f1ed": "flag-ch",
-  "1f1e8-1f1ee": "flag-ci",
-  "1f1e8-1f1f0": "flag-ck",
-  "1f1e8-1f1f1": "flag-cl",
-  "1f1e8-1f1f2": "flag-cm",
-  "1f1e8-1f1f3": "flag-cn",
-  "1f1e8-1f1f4": "flag-co",
-  "1f1e8-1f1f7": "flag-cr",
-  "1f1e8-1f1fa": "flag-cu",
-  "1f1e8-1f1fb": "flag-cv",
-  "1f1e8-1f1fc": "flag-cw",
-  "1f1e8-1f1fd": "flag-cx",
-  "1f1e8-1f1fe": "flag-cy",
-  "1f1e8-1f1ff": "flag-cz",
-  "1f1e9-1f1ea": "flag-de",
-  "1f1e9-1f1ec": "flag-dg",
-  "1f1e9-1f1ef": "flag-dj",
-  "1f1e9-1f1f0": "flag-dk",
-  "1f1e9-1f1f2": "flag-dm",
-  "1f1e9-1f1f4": "flag-do",
-  "1f1e9-1f1ff": "flag-dz",
-  "1f1ea-1f1e8": "flag-ec",
-  "1f1ea-1f1ea": "flag-ee",
-  "1f1ea-1f1ec": "flag-eg",
-  "1f1ea-1f1ed": "flag-eh",
-  "1f1ea-1f1f7": "flag-er",
-  "1f1ea-1f1f8": "flag-es",
-  "1f1ea-1f1fa": "flag-eu",
-  "1f1ea-1f1f9": "flag-et",
-  "1f1eb-1f1ee": "flag-fi",
-  "1f1eb-1f1ef": "flag-fj",
-  "1f1eb-1f1f0": "flag-fk",
-  "1f1eb-1f1f2": "flag-fm",
-  "1f1eb-1f1f4": "flag-fo",
-  "1f1eb-1f1f7": "flag-fr",
-  "1f1ec-1f1e6": "flag-ga",
-  "1f1ec-1f1e7": "flag-gb",
-  "1f1ec-1f1e9": "flag-gd",
-  "1f1ec-1f1ea": "flag-ge",
-  "1f1ec-1f1eb": "flag-gf",
-  "1f1ec-1f1ec": "flag-gg",
-  "1f1ec-1f1ed": "flag-gh",
-  "1f1ec-1f1ee": "flag-gi",
-  "1f1ec-1f1f1": "flag-gl",
-  "1f1ec-1f1f2": "flag-gm",
-  "1f1ec-1f1f3": "flag-gn",
-  "1f1ec-1f1f5": "flag-gp",
-  "1f1ec-1f1f6": "flag-gq",
-  "1f1ec-1f1f7": "flag-gr",
-  "1f1ec-1f1f8": "flag-gs",
-  "1f1ec-1f1f9": "flag-gt",
-  "1f1ec-1f1fa": "flag-gu",
-  "1f1ec-1f1fc": "flag-gw",
-  "1f1ec-1f1fe": "flag-gy",
-  "1f1ed-1f1f0": "flag-hk",
-  "1f1ed-1f1f2": "flag-hm",
-  "1f1ed-1f1f3": "flag-hn",
-  "1f1ed-1f1f7": "flag-hr",
-  "1f1ed-1f1f9": "flag-ht",
-  "1f1ed-1f1fa": "flag-hu",
-  "1f1ee-1f1e9": "flag-id",
-  "1f1ee-1f1ea": "flag-ie",
-  "1f1ee-1f1f1": "flag-il",
-  "1f1ee-1f1f2": "flag-im",
-  "1f1ee-1f1f3": "flag-in",
-  "1f1ee-1f1f4": "flag-io",
-  "1f1ee-1f1f6": "flag-iq",
-  "1f1ee-1f1f7": "flag-ir",
-  "1f1ee-1f1f8": "flag-is",
-  "1f1ee-1f1f9": "flag-it",
-  "1f1ef-1f1ea": "flag-je",
-  "1f1ef-1f1f2": "flag-jm",
-  "1f1ef-1f1f4": "flag-jo",
-  "1f1ef-1f1f5": "flag-jp",
-  "1f1f0-1f1ea": "flag-ke",
-  "1f1f0-1f1ec": "flag-kg",
-  "1f1f0-1f1ed": "flag-kh",
-  "1f1f0-1f1ee": "flag-ki",
-  "1f1f0-1f1f2": "flag-km",
-  "1f1f0-1f1f3": "flag-kn",
-  "1f1f0-1f1f5": "flag-kp",
-  "1f1f0-1f1f7": "flag-kr",
-  "1f1f0-1f1fc": "flag-kw",
-  "1f1f0-1f1fe": "flag-ky",
-  "1f1f0-1f1ff": "flag-kz",
-  "1f1f1-1f1e6": "flag-la",
-  "1f1f1-1f1e7": "flag-lb",
-  "1f1f1-1f1e8": "flag-lc",
-  "1f1f1-1f1ee": "flag-li",
-  "1f1f1-1f1f0": "flag-lk",
-  "1f1f1-1f1f7": "flag-lr",
-  "1f1f1-1f1f8": "flag-ls",
-  "1f1f1-1f1f9": "flag-lt",
-  "1f1f1-1f1fa": "flag-lu",
-  "1f1f1-1f1fb": "flag-lv",
-  "1f1f1-1f1fe": "flag-ly",
-  "1f1f2-1f1e6": "flag-ma",
-  "1f1f2-1f1e8": "flag-mc",
-  "1f1f2-1f1e9": "flag-md",
-  "1f1f2-1f1ea": "flag-me",
-  "1f1f2-1f1eb": "flag-mf",
-  "1f1f2-1f1ec": "flag-mg",
-  "1f1f2-1f1ed": "flag-mh",
-  "1f1f2-1f1f0": "flag-mk",
-  "1f1f2-1f1f1": "flag-ml",
-  "1f1f2-1f1f2": "flag-mm",
-  "1f1f2-1f1f3": "flag-mn",
-  "1f1f2-1f1f4": "flag-mo",
-  "1f1f2-1f1f5": "flag-mp",
-  "1f1f2-1f1f6": "flag-mq",
-  "1f1f2-1f1f7": "flag-mr",
-  "1f1f2-1f1f8": "flag-ms",
-  "1f1f2-1f1f9": "flag-mt",
-  "1f1f2-1f1fa": "flag-mu",
-  "1f1f2-1f1fb": "flag-mv",
-  "1f1f2-1f1fc": "flag-mw",
-  "1f1f2-1f1fd": "flag-mx",
-  "1f1f2-1f1fe": "flag-my",
-  "1f1f2-1f1ff": "flag-mz",
-  "1f1f3-1f1e6": "flag-na",
-  "1f1f3-1f1e8": "flag-nc",
-  "1f1f3-1f1ea": "flag-ne",
-  "1f1f3-1f1eb": "flag-nf",
-  "1f1f3-1f1ec": "flag-ng",
-  "1f1f3-1f1ee": "flag-ni",
-  "1f1f3-1f1f1": "flag-nl",
-  "1f1f3-1f1f4": "flag-no",
-  "1f1f3-1f1f5": "flag-np",
-  "1f1f3-1f1f7": "flag-nr",
-  "1f1f3-1f1fa": "flag-nu",
-  "1f1f3-1f1ff": "flag-nz",
-  "1f1f4-1f1f2": "flag-om",
-  "1f1f5-1f1e6": "flag-pa",
-  "1f1f5-1f1ea": "flag-pe",
-  "1f1f5-1f1eb": "flag-pf",
-  "1f1f5-1f1ec": "flag-pg",
-  "1f1f5-1f1ed": "flag-ph",
-  "1f1f5-1f1f0": "flag-pk",
-  "1f1f5-1f1f1": "flag-pl",
-  "1f1f5-1f1f2": "flag-pm",
-  "1f1f5-1f1f3": "flag-pn",
-  "1f1f5-1f1f7": "flag-pr",
-  "1f1f5-1f1f8": "flag-ps",
-  "1f1f5-1f1f9": "flag-pt",
-  "1f1f5-1f1fc": "flag-pw",
-  "1f1f5-1f1fe": "flag-py",
-  "1f1f6-1f1e6": "flag-qa",
-  "1f1f7-1f1ea": "flag-re",
-  "1f1f7-1f1f4": "flag-ro",
-  "1f1f7-1f1f8": "flag-rs",
-  "1f1f7-1f1fa": "flag-ru",
-  "1f1f7-1f1fc": "flag-rw",
-  "1f1f8-1f1e6": "flag-sa",
-  "1f1f8-1f1e7": "flag-sb",
-  "1f1f8-1f1e8": "flag-sc",
-  "1f1f8-1f1e9": "flag-sd",
-  "1f1f8-1f1ea": "flag-se",
-  "1f1f8-1f1ec": "flag-sg",
-  "1f1f8-1f1ed": "flag-sh",
-  "1f1f8-1f1ee": "flag-si",
-  "1f1f8-1f1ef": "flag-sj",
-  "1f1f8-1f1f0": "flag-sk",
-  "1f1f8-1f1f1": "flag-sl",
-  "1f1f8-1f1f2": "flag-sm",
-  "1f1f8-1f1f3": "flag-sn",
-  "1f1f8-1f1f4": "flag-so",
-  "1f1f8-1f1f7": "flag-sr",
-  "1f1f8-1f1f8": "flag-ss",
-  "1f1f8-1f1f9": "flag-st",
-  "1f1f8-1f1fb": "flag-sv",
-  "1f1f8-1f1fd": "flag-sx",
-  "1f1f8-1f1fe": "flag-sy",
-  "1f1f8-1f1ff": "flag-sz",
-  "1f1f9-1f1e8": "flag-tc",
-  "1f1f9-1f1e9": "flag-td",
-  "1f1f9-1f1eb": "flag-tf",
-  "1f1f9-1f1ec": "flag-tg",
-  "1f1f9-1f1ed": "flag-th",
-  "1f1f9-1f1ef": "flag-tj",
-  "1f1f9-1f1f0": "flag-tk",
-  "1f1f9-1f1f1": "flag-tl",
-  "1f1f9-1f1f2": "flag-tm",
-  "1f1f9-1f1f3": "flag-tn",
-  "1f1f9-1f1f4": "flag-to",
-  "1f1f9-1f1f7": "flag-tr",
-  "1f1f9-1f1f9": "flag-tt",
-  "1f1f9-1f1fb": "flag-tv",
-  "1f1f9-1f1fc": "flag-tw",
-  "1f1f9-1f1ff": "flag-tz",
-  "1f1fa-1f1e6": "flag-ua",
-  "1f1fa-1f1ec": "flag-ug",
-  "1f1fa-1f1f2": "flag-um",
-  "1f1fa-1f1f8": "flag-us",
-  "1f1fa-1f1fe": "flag-uy",
-  "1f1fa-1f1ff": "flag-uz",
-  "1f1fb-1f1e6": "flag-va",
-  "1f1fb-1f1e8": "flag-vc",
-  "1f1fb-1f1ea": "flag-ve",
-  "1f1fb-1f1ec": "flag-vg",
-  "1f1fb-1f1ee": "flag-vi",
-  "1f1fb-1f1f3": "flag-vn",
-  "1f1fb-1f1fa": "flag-vu",
-  "1f1fc-1f1eb": "flag-wf",
-  "1f1fc-1f1f8": "flag-ws",
-  "1f1fd-1f1f0": "flag-xk",
-  "1f1fe-1f1ea": "flag-ye",
-  "1f1fe-1f1f9": "flag-yt",
-  "1f1ff-1f1e6": "flag-za",
-  "1f1ff-1f1f2": "flag-zm",
-  "1f1ff-1f1fc": "flag-zw",
-  "1f4af": "100",
-  "1f522": "1234",
-  "1f170": "a",
-  "1f18e": "ab",
-  "1f524": "abc",
-  "1f521": "abcd",
-  "1f251": "accept",
-  "2652": "aquarius",
-  "2648": "aries",
-  "25c0": "arrow-backward",
-  "23ec": "arrow-double-down",
-  "23eb": "arrow-double-up",
-  "2b07": "arrow-down",
-  "1f53d": "arrow-down-small",
-  "25b6": "arrow-forward",
-  "2935": "arrow-heading-down",
-  "2934": "arrow-heading-up",
-  "2b05": "arrow-left",
-  "2199": "arrow-lower-left",
-  "2198": "arrow-lower-right",
-  "27a1": "arrow-right",
-  "21aa": "arrow-right-hook",
-  "2b06": "arrow-up",
-  "2195": "arrow-up-down",
-  "1f53c": "arrow-up-small",
-  "2196": "arrow-upper-left",
-  "2197": "arrow-upper-right",
-  "1f503": "arrows-clockwise",
-  "1f504": "arrows-counterclockwise",
-  "1f171": "b",
-  "1f6bc": "baby-symbol",
-  "1f6c4": "baggage-claim",
-  "2611": "ballot-box-with-check",
-  "203c": "bangbang",
-  "26ab": "black-circle",
-  "1f532": "black-square-button",
-  "264b": "cancer",
-  "1f520": "capital-abcd",
-  "2651": "capricorn",
-  "1f4b9": "chart",
-  "1f6b8": "children-crossing",
-  "1f3a6": "cinema",
-  "1f191": "cl",
-  "1f550": "clock1",
-  "1f559": "clock10",
-  "1f565": "clock1030",
-  "1f55a": "clock11",
-  "1f566": "clock1130",
-  "1f55b": "clock12",
-  "1f567": "clock1230",
-  "1f55c": "clock130",
-  "1f551": "clock2",
-  "1f55d": "clock230",
-  "1f552": "clock3",
-  "1f55e": "clock330",
-  "1f553": "clock4",
-  "1f55f": "clock430",
-  "1f554": "clock5",
-  "1f560": "clock530",
-  "1f555": "clock6",
-  "1f561": "clock630",
-  "1f556": "clock7",
-  "1f562": "clock730",
-  "1f557": "clock8",
-  "1f563": "clock830",
-  "1f558": "clock9",
-  "1f564": "clock930",
-  "3297": "congratulations",
-  "1f192": "cool",
-  "a9": "copyright",
-  "27b0": "curly-loop",
-  "1f4b1": "currency-exchange",
-  "1f6c3": "customs",
-  "1f4a0": "diamond-shape-with-a-dot-inside",
-  "1f6af": "do-not-litter",
-  "38-20e3": "eight",
-  "2734": "eight-pointed-black-star",
-  "2733": "eight-spoked-asterisk",
-  "1f51a": "end",
-  "23e9": "fast-forward",
-  "35-20e3": "five",
-  "34-20e3": "four",
-  "1f193": "free",
-  "264a": "gemini",
-  "23-20e3": "hash",
-  "1f49f": "heart-decoration",
-  "2714": "heavy-check-mark",
-  "2797": "heavy-division-sign",
-  "1f4b2": "heavy-dollar-sign",
-  "2796": "heavy-minus-sign",
-  "2716": "heavy-multiplication-x",
-  "2795": "heavy-plus-sign",
-  "1f194": "id",
-  "1f250": "ideograph-advantage",
-  "2139": "information-source",
-  "2049": "interrobang",
-  "1f51f": "keycap-ten",
-  "1f201": "koko",
-  "1f535": "large-blue-circle",
-  "1f537": "large-blue-diamond",
-  "1f536": "large-orange-diamond",
-  "1f6c5": "left-luggage",
-  "2194": "left-right-arrow",
-  "21a9": "leftwards-arrow-with-hook",
-  "264c": "leo",
-  "264e": "libra",
-  "1f517": "link",
-  "24c2": "m",
-  "1f6b9": "mens",
-  "1f687": "metro",
-  "1f4f4": "mobile-phone-off",
-  "274e": "negative-squared-cross-mark",
-  "1f195": "new",
-  "1f196": "ng",
-  "39-20e3": "nine",
-  "1f6b3": "no-bicycles",
-  "26d4": "no-entry",
-  "1f6ab": "no-entry-sign",
-  "1f4f5": "no-mobile-phones",
-  "1f6b7": "no-pedestrians",
-  "1f6ad": "no-smoking",
-  "1f6b1": "non-potable-water",
-  "2b55": "o",
-  "1f17e": "o2",
-  "1f197": "ok",
-  "1f51b": "on",
-  "31-20e3": "one",
-  "26ce": "ophiuchus",
-  "1f17f": "parking",
-  "303d": "part-alternation-mark",
-  "1f6c2": "passport-control",
-  "2653": "pisces",
-  "1f6b0": "potable-water",
-  "1f6ae": "put-litter-in-its-place",
-  "1f518": "radio-button",
-  "267b": "recycle",
-  "1f534": "red-circle",
-  "ae": "registered",
-  "1f501": "repeat",
-  "1f502": "repeat-one",
-  "1f6bb": "restroom",
-  "23ea": "rewind",
-  "1f202": "sa",
-  "2650": "sagittarius",
-  "264f": "scorpius",
-  "3299": "secret",
-  "37-20e3": "seven",
-  "1f4f6": "signal-strength",
-  "36-20e3": "six",
-  "1f52f": "six-pointed-star",
-  "1f539": "small-blue-diamond",
-  "1f538": "small-orange-diamond",
-  "1f53a": "small-red-triangle",
-  "1f53b": "small-red-triangle-down",
-  "1f51c": "soon",
-  "1f198": "sos",
-  "1f523": "symbols",
-  "2649": "taurus",
-  "33-20e3": "three",
-  "2122": "tm",
-  "1f51d": "top",
-  "1f531": "trident",
-  "1f500": "twisted-rightwards-arrows",
-  "32-20e3": "two",
-  "1f239": "u5272",
-  "1f234": "u5408",
-  "1f23a": "u55b6",
-  "1f22f": "u6307",
-  "1f237": "u6708",
-  "1f236": "u6709",
-  "1f235": "u6e80",
-  "1f21a": "u7121",
-  "1f238": "u7533",
-  "1f232": "u7981",
-  "1f233": "u7a7a",
-  "1f51e": "underage",
-  "1f199": "up",
-  "1f4f3": "vibration-mode",
-  "264d": "virgo",
-  "1f19a": "vs",
-  "3030": "wavy-dash",
-  "1f6be": "wc",
-  "267f": "wheelchair",
-  "2705": "white-check-mark",
-  "26aa": "white-circle",
-  "1f4ae": "white-flower",
-  "1f533": "white-square-button",
-  "1f6ba": "womens",
-  "274c": "x",
-  "30-20e3": "zero"
-);
-
-@each $code, $name in $emoji-map {
-  .twa-#{$name} {
-    background-image: url("https://twemoji.maxcdn.com/svg/#{$code}.svg");
-  }
-}

+ 0 - 68
client/modules/boot.js

@@ -1,68 +0,0 @@
-export default {
-  readyStates: [],
-  callbacks: [],
-  /**
-   * Check if event has been sent
-   *
-   * @param {String} evt Event name
-   * @returns {Boolean} True if fired
-   */
-  isReady (evt) {
-    return this.readyStates.indexOf(evt) >= 0
-  },
-  /**
-   * Register a callback to be executed when event is sent
-   *
-   * @param {String} evt Event name to register to
-   * @param {Function} clb Callback function
-   * @param {Boolean} once If the callback should be called only once
-   */
-  register (evt, clb, once) {
-    if (this.isReady(evt)) {
-      clb()
-    } else {
-      this.callbacks.push({
-        event: evt,
-        callback: clb,
-        once: false,
-        called: false
-      })
-    }
-  },
-  /**
-   * Register a callback to be executed only once when event is sent
-   *
-   * @param {String} evt Event name to register to
-   * @param {Function} clb Callback function
-   */
-  registerOnce (evt, clb) {
-    this.register(evt, clb, true)
-  },
-  /**
-   * Set ready state and execute callbacks
-   */
-  notify (evt) {
-    this.readyStates.push(evt)
-    this.callbacks.forEach(clb => {
-      if (clb.event === evt) {
-        if (clb.once && clb.called) {
-          return
-        }
-        clb.called = true
-        clb.callback()
-      }
-    })
-  },
-  /**
-   * Execute callback on DOM ready
-   *
-   * @param {Function} clb Callback function
-   */
-  onDOMReady (clb) {
-    if (document.readyState === 'interactive' || document.readyState === 'complete' || document.readyState === 'loaded') {
-      clb()
-    } else {
-      document.addEventListener('DOMContentLoaded', clb)
-    }
-  }
-}

+ 0 - 65
client/modules/localization.js

@@ -1,65 +0,0 @@
-import i18next from 'i18next'
-import Backend from 'i18next-chained-backend'
-import LocalStorageBackend from 'i18next-localstorage-backend'
-import i18nextXHR from 'i18next-xhr-backend'
-import VueI18Next from '@panter/vue-i18next'
-import _ from 'lodash'
-
-/* global siteConfig, graphQL */
-
-import localeQuery from 'gql/common/common-localization-query-translations.gql'
-
-export default {
-  VueI18Next,
-  init() {
-    i18next
-      .use(Backend)
-      .init({
-        backend: {
-          backends: [
-            LocalStorageBackend,
-            i18nextXHR
-          ],
-          backendOptions: [
-            {
-              expirationTime: 1000 * 60 * 60 * 24 // 24h
-            },
-            {
-              loadPath: '{{lng}}/{{ns}}',
-              parse: (data) => data,
-              ajax: (url, opts, cb, data) => {
-                let ns = {}
-                return cb(ns, {status: '200'})
-                // let langParams = url.split('/')
-                // graphQL.query({
-                //   query: localeQuery,
-                //   variables: {
-                //     locale: langParams[0],
-                //     namespace: langParams[1]
-                //   }
-                // }).then(resp => {
-                //   let ns = {}
-                //   if (_.get(resp, 'data.localization.translations', []).length > 0) {
-                //     resp.data.localization.translations.forEach(entry => {
-                //       _.set(ns, entry.key, entry.value)
-                //     })
-                //   }
-                //   return cb(ns, {status: '200'})
-                // }).catch(err => {
-                //   console.error(err)
-                //   return cb(null, {status: '404'})
-                // })
-              }
-            }
-          ]
-        },
-        defaultNS: 'common',
-        lng: siteConfig.lang,
-        load: 'currentOnly',
-        lowerCaseLng: true,
-        fallbackLng: siteConfig.lang,
-        ns: ['common', 'auth']
-      })
-    return new VueI18Next(i18next)
-  }
-}

+ 0 - 78
client/polyfills/array-from.js

@@ -1,78 +0,0 @@
-// Production steps of ECMA-262, Edition 6, 22.1.2.1
-if (!Array.from) {
-  Array.from = (function () {
-    var toStr = Object.prototype.toString
-    var isCallable = function (fn) {
-      return typeof fn === 'function' || toStr.call(fn) === '[object Function]'
-    }
-    var toInteger = function (value) {
-      var number = Number(value)
-      if (isNaN(number)) { return 0 }
-      if (number === 0 || !isFinite(number)) { return number }
-      return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number))
-    }
-    var maxSafeInteger = Math.pow(2, 53) - 1
-    var toLength = function (value) {
-      var len = toInteger(value)
-      return Math.min(Math.max(len, 0), maxSafeInteger)
-    }
-
-    // The length property of the from method is 1.
-    return function from (arrayLike/*, mapFn, thisArg */) {
-      // 1. Let C be the this value.
-      var C = this
-
-      // 2. Let items be ToObject(arrayLike).
-      var items = Object(arrayLike)
-
-      // 3. ReturnIfAbrupt(items).
-      if (arrayLike == null) {
-        throw new TypeError('Array.from requires an array-like object - not null or undefined')
-      }
-
-      // 4. If mapfn is undefined, then let mapping be false.
-      var mapFn = arguments.length > 1 ? arguments[1] : void undefined
-      var T
-      if (typeof mapFn !== 'undefined') {
-        // 5. else
-        // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
-        if (!isCallable(mapFn)) {
-          throw new TypeError('Array.from: when provided, the second argument must be a function')
-        }
-
-        // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
-        if (arguments.length > 2) {
-          T = arguments[2]
-        }
-      }
-
-      // 10. Let lenValue be Get(items, "length").
-      // 11. Let len be ToLength(lenValue).
-      var len = toLength(items.length)
-
-      // 13. If IsConstructor(C) is true, then
-      // 13. a. Let A be the result of calling the [[Construct]] internal method
-      // of C with an argument list containing the single item len.
-      // 14. a. Else, Let A be ArrayCreate(len).
-      var A = isCallable(C) ? Object(new C(len)) : new Array(len)
-
-      // 16. Let k be 0.
-      var k = 0
-      // 17. Repeat, while k < len… (also steps a - h)
-      var kValue
-      while (k < len) {
-        kValue = items[k]
-        if (mapFn) {
-          A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k)
-        } else {
-          A[k] = kValue
-        }
-        k += 1
-      }
-      // 18. Let putStatus be Put(A, "length", len, true).
-      A.length = len
-      // 20. Return A.
-      return A
-    }
-  }())
-}

+ 0 - 31
client/scss/app.scss

@@ -1,31 +0,0 @@
-@import "global";
-
-@import "base/base";
-@import "base/icons";
-@import "base/animation";
-
-@import '~vuescroll/dist/vuescroll.css';
-@import '~katex/dist/katex.min.css';
-@import '~diff2html/bundles/css/diff2html.min.css';
-
-@import 'components/codemirror';
-@import 'components/katex';
-@import 'components/v-btn';
-@import 'components/v-data-table';
-@import 'components/v-dialog';
-@import 'components/v-form';
-@import 'components/v-tabs';
-
-// @import '../libs/twemoji/twemoji-awesome';
-// @import '../libs/prism/prism.css';
-// @import '~vue-tour/dist/vue-tour.css';
-// @import '~xterm/dist/xterm.css';
-// @import 'node_modules/diff2html/dist/diff2html.min';
-
-@import 'pages/new';
-@import 'pages/notfound';
-@import 'pages/unauthorized';
-@import 'pages/welcome';
-@import 'pages/error';
-
-@import 'layout/_rtl';

+ 0 - 12
client/scss/base/animation.scss

@@ -1,12 +0,0 @@
-$use-fade: true;
-$use-zoom: true;
-$use-bounce: true;
-
-@import "~animate-sass/animate";
-
-
-@for $i from 1 to 12 {
-  .wait-p#{$i}s {
-    animation-delay: $i * .1s !important;
-  }
-}

+ 0 - 86
client/scss/base/base.scss

@@ -1,86 +0,0 @@
-html {
-  box-sizing: border-box;
-  height: 100%;
-  overflow-y: auto !important;
-}
-*, *:before, *:after {
-  box-sizing: inherit;
-}
-
-[v-cloak], .is-hidden {
-  display: none;
-}
-
-#root {
-  position: relative;
-  min-height: 100%;
-
-  &.is-fullscreen {
-    height: 100vh;
-  }
-}
-
-.v-application--wrap {
-  transition: all 1.2s ease;
-  transform-origin: 50% 50%;
-  // background-color: #FFF;
-
-  @at-root .theme--dark & {
-    background-color: mc('grey', '900');
-  }
-}
-
-#root .v-application {
-  .overline {
-    line-height: 1rem;
-    font-size: .625rem!important;
-    font-weight: 400;
-    letter-spacing: .1666666667em!important;
-  }
-
-  @for $i from 0 through 25 {
-    .radius-#{$i} {
-      border-radius: #{$i}px;
-    }
-  }
-
-  @for $i from 1 through 5 {
-    .grey.darken-2-d#{$i} {
-      background-color: darken(mc('grey', '700'), percentage($i/100)) !important;
-      border-color: darken(mc('grey', '700'), percentage($i/100)) !important;
-    }
-    .grey.darken-2-l#{$i} {
-      background-color: lighten(mc('grey', '700'), percentage($i/100)) !important;
-      border-color: lighten(mc('grey', '700'), percentage($i/100)) !important;
-    }
-    .grey.darken-3-d#{$i} {
-      background-color: darken(mc('grey', '800'), percentage($i/100)) !important;
-      border-color: darken(mc('grey', '800'), percentage($i/100)) !important;
-    }
-    .grey.darken-3-l#{$i} {
-      background-color: lighten(mc('grey', '800'), percentage($i/100)) !important;
-      border-color: lighten(mc('grey', '800'), percentage($i/100)) !important;
-    }
-    .grey.darken-4-d#{$i} {
-      background-color: darken(mc('grey', '900'), percentage($i/100)) !important;
-      border-color: darken(mc('grey', '900'), percentage($i/100)) !important;
-    }
-    .grey.darken-4-l#{$i} {
-      background-color: lighten(mc('grey', '900'), percentage($i/100)) !important;
-      border-color: lighten(mc('grey', '900'), percentage($i/100)) !important;
-    }
-  }
-  .grey.darken-5 {
-    background-color: #0C0C0C !important;
-    border-color: #0C0C0C !important;
-  }
-
-  .blue.darken-5 {
-    background-color: darken(mc('blue', '900'), 20%) !important;
-    border-color: darken(mc('blue', '900'), 20%) !important;
-  }
-  .indigo.darken-5 {
-    background-color: darken(mc('indigo', '900'), 10%) !important;
-    border-color: darken(mc('indigo', '900'), 10%) !important;
-  }
-}

+ 0 - 68
client/scss/base/icons.scss

@@ -1,68 +0,0 @@
-// @font-face {
-//   font-family: 'Material Icons';
-//   font-style: normal;
-//   font-weight: 400;
-//   src: local('Material Icons'),
-//     local('MaterialIcons-Regular'),
-//     url(/fonts/MaterialIcons-Regular.woff2) format('woff2'),
-//     url(/fonts/MaterialIcons-Regular.woff) format('woff');
-// }
-
-// .material-icons {
-//   font-family: 'Material Icons', sans-serif;
-//   font-weight: normal;
-//   font-style: normal;
-//   font-size: 24px;  /* Preferred icon size */
-//   display: inline-flex;
-//   line-height: 1;
-//   text-transform: none;
-//   letter-spacing: normal;
-//   word-wrap: normal;
-//   white-space: nowrap;
-//   direction: ltr;
-
-//   /* Support for all WebKit browsers. */
-//   -webkit-font-smoothing: antialiased;
-//   /* Support for Safari and Chrome. */
-//   text-rendering: optimizeLegibility;
-
-//   /* Support for Firefox. */
-//   -moz-osx-font-smoothing: grayscale;
-
-//   /* Support for IE. */
-//   font-feature-settings: 'liga';
-// }
-
-.icons {
-  display: inline-block;
-  color: mc('grey', '800');
-  &.is-text {
-    display: inline-block;
-    width: 1em;
-    height: 1em;
-    vertical-align: middle;
-    position: relative;
-    top: -0.0625em;
-    stroke: none;
-    fill: none;
-  }
-  @each $size in 16,18,20,24,32,48,64,96,128 {
-    &.is-#{$size} {
-      width: #{$size}px;
-      height: #{$size}px;
-    }
-  }
-  &.has-right-pad {
-    margin-right: .5rem;
-  }
-  &.is-outlined {
-    stroke-width: 2px;
-    use {
-      fill: inherit;
-      stroke: mc('grey', '800');
-    }
-  }
-}
-.material-design-icon {
-  display: inline-flex;
-}

+ 0 - 333
client/scss/base/material.scss

@@ -1,333 +0,0 @@
-$material-colors: (
-  'red': (
-	'50': #ffebee,
-	'100': #ffcdd2,
-	'200': #ef9a9a,
-	'300': #e57373,
-	'400': #ef5350,
-	'500': #f44336,
-	'600': #e53935,
-	'700': #d32f2f,
-	'800': #c62828,
-	'900': #b71c1c,
-	'a100': #ff8a80,
-	'a200': #ff5252,
-	'a400': #ff1744,
-	'a700': #d50000
-  ),
-
-  'pink': (
-	'50': #fce4ec,
-	'100': #f8bbd0,
-	'200': #f48fb1,
-	'300': #f06292,
-	'400': #ec407a,
-	'500': #e91e63,
-	'600': #d81b60,
-	'700': #c2185b,
-	'800': #ad1457,
-	'900': #880e4f,
-	'a100': #ff80ab,
-	'a200': #ff4081,
-	'a400': #f50057,
-	'a700': #c51162
-  ),
-
-  'purple': (
-	'50': #f3e5f5,
-	'100': #e1bee7,
-	'200': #ce93d8,
-	'300': #ba68c8,
-	'400': #ab47bc,
-	'500': #9c27b0,
-	'600': #8e24aa,
-	'700': #7b1fa2,
-	'800': #6a1b9a,
-	'900': #4a148c,
-	'a100': #ea80fc,
-	'a200': #e040fb,
-	'a400': #d500f9,
-	'a700': #aa00ff
-  ),
-
-  'deep-purple': (
-	'50': #ede7f6,
-	'100': #d1c4e9,
-	'200': #b39ddb,
-	'300': #9575cd,
-	'400': #7e57c2,
-	'500': #673ab7,
-	'600': #5e35b1,
-	'700': #512da8,
-	'800': #4527a0,
-	'900': #311b92,
-	'a100': #b388ff,
-	'a200': #7c4dff,
-	'a400': #651fff,
-	'a700': #6200ea
-  ),
-
-  'indigo': (
-	'50': #e8eaf6,
-	'100': #c5cae9,
-	'200': #9fa8da,
-	'300': #7986cb,
-	'400': #5c6bc0,
-	'500': #3f51b5,
-	'600': #3949ab,
-	'700': #303f9f,
-	'800': #283593,
-	'900': #1a237e,
-	'a100': #8c9eff,
-	'a200': #536dfe,
-	'a400': #3d5afe,
-	'a700': #304ffe
-  ),
-
-  'blue': (
-	'50': #e3f2fd,
-	'100': #bbdefb,
-	'200': #90caf9,
-	'300': #64b5f6,
-	'400': #42a5f5,
-	'500': #2196f3,
-	'600': #1e88e5,
-	'700': #1976d2,
-	'800': #1565c0,
-	'900': #0d47a1,
-	'a100': #82b1ff,
-	'a200': #448aff,
-	'a400': #2979ff,
-	'a700': #2962ff
-  ),
-
-  'light-blue': (
-	'50': #e1f5fe,
-	'100': #b3e5fc,
-	'200': #81d4fa,
-	'300': #4fc3f7,
-	'400': #29b6f6,
-	'500': #03a9f4,
-	'600': #039be5,
-	'700': #0288d1,
-	'800': #0277bd,
-	'900': #01579b,
-	'a100': #80d8ff,
-	'a200': #40c4ff,
-	'a400': #00b0ff,
-	'a700': #0091ea
-  ),
-
-  'cyan': (
-	'50': #e0f7fa,
-	'100': #b2ebf2,
-	'200': #80deea,
-	'300': #4dd0e1,
-	'400': #26c6da,
-	'500': #00bcd4,
-	'600': #00acc1,
-	'700': #0097a7,
-	'800': #00838f,
-	'900': #006064,
-	'a100': #84ffff,
-	'a200': #18ffff,
-	'a400': #00e5ff,
-	'a700': #00b8d4
-  ),
-
-  'teal': (
-	'50': #e0f2f1,
-	'100': #b2dfdb,
-	'200': #80cbc4,
-	'300': #4db6ac,
-	'400': #26a69a,
-	'500': #009688,
-	'600': #00897b,
-	'700': #00796b,
-	'800': #00695c,
-	'900': #004d40,
-	'a100': #a7ffeb,
-	'a200': #64ffda,
-	'a400': #1de9b6,
-	'a700': #00bfa5
-  ),
-
-  'green': (
-	'50': #e8f5e9,
-	'100': #c8e6c9,
-	'200': #a5d6a7,
-	'300': #81c784,
-	'400': #66bb6a,
-	'500': #4caf50,
-	'600': #43a047,
-	'700': #388e3c,
-	'800': #2e7d32,
-	'900': #1b5e20,
-	'a100': #b9f6ca,
-	'a200': #69f0ae,
-	'a400': #00e676,
-	'a700': #00c853
-  ),
-
-  'light-green': (
-	'50': #f1f8e9,
-	'100': #dcedc8,
-	'200': #c5e1a5,
-	'300': #aed581,
-	'400': #9ccc65,
-	'500': #8bc34a,
-	'600': #7cb342,
-	'700': #689f38,
-	'800': #558b2f,
-	'900': #33691e,
-	'a100': #ccff90,
-	'a200': #b2ff59,
-	'a400': #76ff03,
-	'a700': #64dd17
-  ),
-
-  'lime': (
-	'50': #f9fbe7,
-	'100': #f0f4c3,
-	'200': #e6ee9c,
-	'300': #dce775,
-	'400': #d4e157,
-	'500': #cddc39,
-	'600': #c0ca33,
-	'700': #afb42b,
-	'800': #9e9d24,
-	'900': #827717,
-	'a100': #f4ff81,
-	'a200': #eeff41,
-	'a400': #c6ff00,
-	'a700': #aeea00
-  ),
-
-  'yellow': (
-	'50': #fffde7,
-	'100': #fff9c4,
-	'200': #fff59d,
-	'300': #fff176,
-	'400': #ffee58,
-	'500': #ffeb3b,
-	'600': #fdd835,
-	'700': #fbc02d,
-	'800': #f9a825,
-	'900': #f57f17,
-	'a100': #ffff8d,
-	'a200': #ffff00,
-	'a400': #ffea00,
-	'a700': #ffd600
-  ),
-
-  'amber': (
-	'50': #fff8e1,
-	'100': #ffecb3,
-	'200': #ffe082,
-	'300': #ffd54f,
-	'400': #ffca28,
-	'500': #ffc107,
-	'600': #ffb300,
-	'700': #ffa000,
-	'800': #ff8f00,
-	'900': #ff6f00,
-	'a100': #ffe57f,
-	'a200': #ffd740,
-	'a400': #ffc400,
-	'a700': #ffab00
-  ),
-
-  'orange': (
-	'50': #fff3e0,
-	'100': #ffe0b2,
-	'200': #ffcc80,
-	'300': #ffb74d,
-	'400': #ffa726,
-	'500': #ff9800,
-	'600': #fb8c00,
-	'700': #f57c00,
-	'800': #ef6c00,
-	'900': #e65100,
-	'a100': #ffd180,
-	'a200': #ffab40,
-	'a400': #ff9100,
-	'a700': #ff6d00
-  ),
-
-  'deep-orange': (
-	'50': #fbe9e7,
-	'100': #ffccbc,
-	'200': #ffab91,
-	'300': #ff8a65,
-	'400': #ff7043,
-	'500': #ff5722,
-	'600': #f4511e,
-	'700': #e64a19,
-	'800': #d84315,
-	'900': #bf360c,
-	'a100': #ff9e80,
-	'a200': #ff6e40,
-	'a400': #ff3d00,
-	'a700': #dd2c00
-  ),
-
-  'brown': (
-	'50': #efebe9,
-	'100': #d7ccc8,
-	'200': #bcaaa4,
-	'300': #a1887f,
-	'400': #8d6e63,
-	'500': #795548,
-	'600': #6d4c41,
-	'700': #5d4037,
-	'800': #4e342e,
-	'900': #3e2723
-  ),
-
-  'grey': (
-	'50': #fafafa,
-	'100': #f5f5f5,
-	'200': #eeeeee,
-	'300': #e0e0e0,
-	'400': #bdbdbd,
-	'500': #9e9e9e,
-	'600': #757575,
-	'700': #616161,
-	'800': #424242,
-	'900': #212121
-  ),
-
-  'blue-grey': (
-	'50': #eceff1,
-	'100': #cfd8dc,
-	'200': #b0bec5,
-	'300': #90a4ae,
-	'400': #78909c,
-	'500': #607d8b,
-	'600': #546e7a,
-	'700': #455a64,
-	'800': #37474f,
-	'900': #263238,
-	'1000': #11171a
-  ),
-
-  'theme': (
-    'primary': #1976D2,
-    'secondary': #424242,
-    'accent': #82B1FF,
-    'error': #FF5252,
-    'info': #2196F3,
-    'success': #4CAF50,
-    'warning': #FFC107
-  )
-);
-
-@function mc($color-name, $color-variant: '500') {
-  $color: map-get(map-get($material-colors, $color-name),$color-variant);
-  @if $color {
-	@return $color;
-  } @else {
-	// Libsass still doesn't seem to support @error
-	@warn "=> ERROR: COLOR NOT FOUND! <= | Your $color-name, $color-variant combination did not match any of the values in the $material-colors map.";
-  }
-}

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است