mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-05-24 01:13:00 -04:00
removing .nuxt
This commit is contained in:
parent
7532b756a7
commit
53e46ff54d
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,4 +3,5 @@ node_modules/
|
|||||||
/config/
|
/config/
|
||||||
/audiobooks/
|
/audiobooks/
|
||||||
/metadata/
|
/metadata/
|
||||||
/test/
|
/test/
|
||||||
|
/client/.nuxt/
|
@ -1,213 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
import { decode, parsePath, withoutBase, withoutTrailingSlash, normalizeURL } from 'ufo'
|
|
||||||
|
|
||||||
import { getMatchedComponentsInstances, getChildrenComponentInstancesUsingFetch, promisify, globalHandleError, urlJoin, sanitizeComponent } from './utils'
|
|
||||||
import NuxtError from './components/nuxt-error.vue'
|
|
||||||
import NuxtLoading from './components/nuxt-loading.vue'
|
|
||||||
import NuxtBuildIndicator from './components/nuxt-build-indicator'
|
|
||||||
|
|
||||||
import '..\\node_modules\\@nuxtjs\\tailwindcss\\dist\\runtime\\tailwind.css'
|
|
||||||
|
|
||||||
import '..\\assets\\app.css'
|
|
||||||
|
|
||||||
import _77180f1e from '..\\layouts\\blank.vue'
|
|
||||||
import _6f6c098b from '..\\layouts\\default.vue'
|
|
||||||
|
|
||||||
const layouts = { "_blank": sanitizeComponent(_77180f1e),"_default": sanitizeComponent(_6f6c098b) }
|
|
||||||
|
|
||||||
export default {
|
|
||||||
render (h, props) {
|
|
||||||
const loadingEl = h('NuxtLoading', { ref: 'loading' })
|
|
||||||
|
|
||||||
const layoutEl = h(this.layout || 'nuxt')
|
|
||||||
const templateEl = h('div', {
|
|
||||||
domProps: {
|
|
||||||
id: '__layout'
|
|
||||||
},
|
|
||||||
key: this.layoutName
|
|
||||||
}, [layoutEl])
|
|
||||||
|
|
||||||
const transitionEl = h('transition', {
|
|
||||||
props: {
|
|
||||||
name: 'layout',
|
|
||||||
mode: 'out-in'
|
|
||||||
},
|
|
||||||
on: {
|
|
||||||
beforeEnter (el) {
|
|
||||||
// Ensure to trigger scroll event after calling scrollBehavior
|
|
||||||
window.$nuxt.$nextTick(() => {
|
|
||||||
window.$nuxt.$emit('triggerScroll')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [templateEl])
|
|
||||||
|
|
||||||
return h('div', {
|
|
||||||
domProps: {
|
|
||||||
id: '__nuxt'
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
loadingEl,
|
|
||||||
h(NuxtBuildIndicator),
|
|
||||||
transitionEl
|
|
||||||
])
|
|
||||||
},
|
|
||||||
|
|
||||||
data: () => ({
|
|
||||||
isOnline: true,
|
|
||||||
|
|
||||||
layout: null,
|
|
||||||
layoutName: '',
|
|
||||||
|
|
||||||
nbFetching: 0
|
|
||||||
}),
|
|
||||||
|
|
||||||
beforeCreate () {
|
|
||||||
Vue.util.defineReactive(this, 'nuxt', this.$options.nuxt)
|
|
||||||
},
|
|
||||||
created () {
|
|
||||||
// Add this.$nuxt in child instances
|
|
||||||
this.$root.$options.$nuxt = this
|
|
||||||
|
|
||||||
if (process.client) {
|
|
||||||
// add to window so we can listen when ready
|
|
||||||
window.$nuxt = this
|
|
||||||
|
|
||||||
this.refreshOnlineStatus()
|
|
||||||
// Setup the listeners
|
|
||||||
window.addEventListener('online', this.refreshOnlineStatus)
|
|
||||||
window.addEventListener('offline', this.refreshOnlineStatus)
|
|
||||||
}
|
|
||||||
// Add $nuxt.error()
|
|
||||||
this.error = this.nuxt.error
|
|
||||||
// Add $nuxt.context
|
|
||||||
this.context = this.$options.context
|
|
||||||
},
|
|
||||||
|
|
||||||
async mounted () {
|
|
||||||
this.$loading = this.$refs.loading
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
'nuxt.err': 'errorChanged'
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
isOffline () {
|
|
||||||
return !this.isOnline
|
|
||||||
},
|
|
||||||
|
|
||||||
isFetching () {
|
|
||||||
return this.nbFetching > 0
|
|
||||||
},
|
|
||||||
|
|
||||||
isPreview () {
|
|
||||||
return Boolean(this.$options.previewData)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
refreshOnlineStatus () {
|
|
||||||
if (process.client) {
|
|
||||||
if (typeof window.navigator.onLine === 'undefined') {
|
|
||||||
// If the browser doesn't support connection status reports
|
|
||||||
// assume that we are online because most apps' only react
|
|
||||||
// when they now that the connection has been interrupted
|
|
||||||
this.isOnline = true
|
|
||||||
} else {
|
|
||||||
this.isOnline = window.navigator.onLine
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async refresh () {
|
|
||||||
const pages = getMatchedComponentsInstances(this.$route)
|
|
||||||
|
|
||||||
if (!pages.length) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.$loading.start()
|
|
||||||
|
|
||||||
const promises = pages.map((page) => {
|
|
||||||
const p = []
|
|
||||||
|
|
||||||
// Old fetch
|
|
||||||
if (page.$options.fetch && page.$options.fetch.length) {
|
|
||||||
p.push(promisify(page.$options.fetch, this.context))
|
|
||||||
}
|
|
||||||
if (page.$fetch) {
|
|
||||||
p.push(page.$fetch())
|
|
||||||
} else {
|
|
||||||
// Get all component instance to call $fetch
|
|
||||||
for (const component of getChildrenComponentInstancesUsingFetch(page.$vnode.componentInstance)) {
|
|
||||||
p.push(component.$fetch())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page.$options.asyncData) {
|
|
||||||
p.push(
|
|
||||||
promisify(page.$options.asyncData, this.context)
|
|
||||||
.then((newData) => {
|
|
||||||
for (const key in newData) {
|
|
||||||
Vue.set(page.$data, key, newData[key])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.all(p)
|
|
||||||
})
|
|
||||||
try {
|
|
||||||
await Promise.all(promises)
|
|
||||||
} catch (error) {
|
|
||||||
this.$loading.fail(error)
|
|
||||||
globalHandleError(error)
|
|
||||||
this.error(error)
|
|
||||||
}
|
|
||||||
this.$loading.finish()
|
|
||||||
},
|
|
||||||
errorChanged () {
|
|
||||||
if (this.nuxt.err) {
|
|
||||||
if (this.$loading) {
|
|
||||||
if (this.$loading.fail) {
|
|
||||||
this.$loading.fail(this.nuxt.err)
|
|
||||||
}
|
|
||||||
if (this.$loading.finish) {
|
|
||||||
this.$loading.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let errorLayout = (NuxtError.options || NuxtError).layout;
|
|
||||||
|
|
||||||
if (typeof errorLayout === 'function') {
|
|
||||||
errorLayout = errorLayout(this.context)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setLayout(errorLayout)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setLayout (layout) {
|
|
||||||
if(layout && typeof layout !== 'string') {
|
|
||||||
throw new Error('[nuxt] Avoid using non-string value as layout property.')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!layout || !layouts['_' + layout]) {
|
|
||||||
layout = 'default'
|
|
||||||
}
|
|
||||||
this.layoutName = layout
|
|
||||||
this.layout = layouts['_' + layout]
|
|
||||||
return this.layout
|
|
||||||
},
|
|
||||||
loadLayout (layout) {
|
|
||||||
if (!layout || !layouts['_' + layout]) {
|
|
||||||
layout = 'default'
|
|
||||||
}
|
|
||||||
return Promise.resolve(layouts['_' + layout])
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
|
||||||
NuxtLoading
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,193 +0,0 @@
|
|||||||
import Axios from 'axios'
|
|
||||||
import defu from 'defu'
|
|
||||||
|
|
||||||
// Axios.prototype cannot be modified
|
|
||||||
const axiosExtra = {
|
|
||||||
setBaseURL (baseURL) {
|
|
||||||
this.defaults.baseURL = baseURL
|
|
||||||
},
|
|
||||||
setHeader (name, value, scopes = 'common') {
|
|
||||||
for (const scope of Array.isArray(scopes) ? scopes : [ scopes ]) {
|
|
||||||
if (!value) {
|
|
||||||
delete this.defaults.headers[scope][name];
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
this.defaults.headers[scope][name] = value
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setToken (token, type, scopes = 'common') {
|
|
||||||
const value = !token ? null : (type ? type + ' ' : '') + token
|
|
||||||
this.setHeader('Authorization', value, scopes)
|
|
||||||
},
|
|
||||||
onRequest(fn) {
|
|
||||||
this.interceptors.request.use(config => fn(config) || config)
|
|
||||||
},
|
|
||||||
onResponse(fn) {
|
|
||||||
this.interceptors.response.use(response => fn(response) || response)
|
|
||||||
},
|
|
||||||
onRequestError(fn) {
|
|
||||||
this.interceptors.request.use(undefined, error => fn(error) || Promise.reject(error))
|
|
||||||
},
|
|
||||||
onResponseError(fn) {
|
|
||||||
this.interceptors.response.use(undefined, error => fn(error) || Promise.reject(error))
|
|
||||||
},
|
|
||||||
onError(fn) {
|
|
||||||
this.onRequestError(fn)
|
|
||||||
this.onResponseError(fn)
|
|
||||||
},
|
|
||||||
create(options) {
|
|
||||||
return createAxiosInstance(defu(options, this.defaults))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Request helpers ($get, $post, ...)
|
|
||||||
for (const method of ['request', 'delete', 'get', 'head', 'options', 'post', 'put', 'patch']) {
|
|
||||||
axiosExtra['$' + method] = function () { return this[method].apply(this, arguments).then(res => res && res.data) }
|
|
||||||
}
|
|
||||||
|
|
||||||
const extendAxiosInstance = axios => {
|
|
||||||
for (const key in axiosExtra) {
|
|
||||||
axios[key] = axiosExtra[key].bind(axios)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const createAxiosInstance = axiosOptions => {
|
|
||||||
// Create new axios instance
|
|
||||||
const axios = Axios.create(axiosOptions)
|
|
||||||
axios.CancelToken = Axios.CancelToken
|
|
||||||
axios.isCancel = Axios.isCancel
|
|
||||||
|
|
||||||
// Extend axios proto
|
|
||||||
extendAxiosInstance(axios)
|
|
||||||
|
|
||||||
// Intercept to apply default headers
|
|
||||||
axios.onRequest((config) => {
|
|
||||||
config.headers = { ...axios.defaults.headers.common, ...config.headers }
|
|
||||||
})
|
|
||||||
|
|
||||||
// Setup interceptors
|
|
||||||
|
|
||||||
setupProgress(axios)
|
|
||||||
|
|
||||||
return axios
|
|
||||||
}
|
|
||||||
|
|
||||||
const setupProgress = (axios) => {
|
|
||||||
if (process.server) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// A noop loading inteterface for when $nuxt is not yet ready
|
|
||||||
const noopLoading = {
|
|
||||||
finish: () => { },
|
|
||||||
start: () => { },
|
|
||||||
fail: () => { },
|
|
||||||
set: () => { }
|
|
||||||
}
|
|
||||||
|
|
||||||
const $loading = () => {
|
|
||||||
const $nuxt = typeof window !== 'undefined' && window['$nuxt']
|
|
||||||
return ($nuxt && $nuxt.$loading && $nuxt.$loading.set) ? $nuxt.$loading : noopLoading
|
|
||||||
}
|
|
||||||
|
|
||||||
let currentRequests = 0
|
|
||||||
|
|
||||||
axios.onRequest(config => {
|
|
||||||
if (config && config.progress === false) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
currentRequests++
|
|
||||||
})
|
|
||||||
|
|
||||||
axios.onResponse(response => {
|
|
||||||
if (response && response.config && response.config.progress === false) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
currentRequests--
|
|
||||||
if (currentRequests <= 0) {
|
|
||||||
currentRequests = 0
|
|
||||||
$loading().finish()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
axios.onError(error => {
|
|
||||||
if (error && error.config && error.config.progress === false) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
currentRequests--
|
|
||||||
|
|
||||||
if (Axios.isCancel(error)) {
|
|
||||||
if (currentRequests <= 0) {
|
|
||||||
currentRequests = 0
|
|
||||||
$loading().finish()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
$loading().fail()
|
|
||||||
$loading().finish()
|
|
||||||
})
|
|
||||||
|
|
||||||
const onProgress = e => {
|
|
||||||
if (!currentRequests || !e.total) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const progress = ((e.loaded * 100) / (e.total * currentRequests))
|
|
||||||
$loading().set(Math.min(100, progress))
|
|
||||||
}
|
|
||||||
|
|
||||||
axios.defaults.onUploadProgress = onProgress
|
|
||||||
axios.defaults.onDownloadProgress = onProgress
|
|
||||||
}
|
|
||||||
|
|
||||||
export default (ctx, inject) => {
|
|
||||||
// runtimeConfig
|
|
||||||
const runtimeConfig = ctx.$config && ctx.$config.axios || {}
|
|
||||||
// baseURL
|
|
||||||
const baseURL = process.browser
|
|
||||||
? (runtimeConfig.browserBaseURL || runtimeConfig.browserBaseUrl || runtimeConfig.baseURL || runtimeConfig.baseUrl || '')
|
|
||||||
: (runtimeConfig.baseURL || runtimeConfig.baseUrl || process.env._AXIOS_BASE_URL_ || '')
|
|
||||||
|
|
||||||
// Create fresh objects for all default header scopes
|
|
||||||
// Axios creates only one which is shared across SSR requests!
|
|
||||||
// https://github.com/mzabriskie/axios/blob/master/lib/defaults.js
|
|
||||||
const headers = {
|
|
||||||
"common": {
|
|
||||||
"Accept": "application/json, text/plain, */*"
|
|
||||||
},
|
|
||||||
"delete": {},
|
|
||||||
"get": {},
|
|
||||||
"head": {},
|
|
||||||
"post": {},
|
|
||||||
"put": {},
|
|
||||||
"patch": {}
|
|
||||||
}
|
|
||||||
|
|
||||||
const axiosOptions = {
|
|
||||||
baseURL,
|
|
||||||
headers
|
|
||||||
}
|
|
||||||
|
|
||||||
// Proxy SSR request headers headers
|
|
||||||
if (process.server && ctx.req && ctx.req.headers) {
|
|
||||||
const reqHeaders = { ...ctx.req.headers }
|
|
||||||
for (const h of ["accept","cf-connecting-ip","cf-ray","content-length","content-md5","content-type","host","x-forwarded-host","x-forwarded-port","x-forwarded-proto"]) {
|
|
||||||
delete reqHeaders[h]
|
|
||||||
}
|
|
||||||
axiosOptions.headers.common = { ...reqHeaders, ...axiosOptions.headers.common }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.server) {
|
|
||||||
// Don't accept brotli encoding because Node can't parse it
|
|
||||||
axiosOptions.headers.common['accept-encoding'] = 'gzip, deflate'
|
|
||||||
}
|
|
||||||
|
|
||||||
const axios = createAxiosInstance(axiosOptions)
|
|
||||||
|
|
||||||
// Inject axios to the context as $axios
|
|
||||||
ctx.$axios = axios
|
|
||||||
inject('axios', axios)
|
|
||||||
}
|
|
@ -1,817 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
import fetch from 'unfetch'
|
|
||||||
import middleware from './middleware.js'
|
|
||||||
import {
|
|
||||||
applyAsyncData,
|
|
||||||
promisify,
|
|
||||||
middlewareSeries,
|
|
||||||
sanitizeComponent,
|
|
||||||
resolveRouteComponents,
|
|
||||||
getMatchedComponents,
|
|
||||||
getMatchedComponentsInstances,
|
|
||||||
flatMapComponents,
|
|
||||||
setContext,
|
|
||||||
getLocation,
|
|
||||||
compile,
|
|
||||||
getQueryDiff,
|
|
||||||
globalHandleError,
|
|
||||||
isSamePath,
|
|
||||||
urlJoin
|
|
||||||
} from './utils.js'
|
|
||||||
import { createApp, NuxtError } from './index.js'
|
|
||||||
import fetchMixin from './mixins/fetch.client'
|
|
||||||
import NuxtLink from './components/nuxt-link.client.js' // should be included after ./index.js
|
|
||||||
|
|
||||||
// Fetch mixin
|
|
||||||
if (!Vue.__nuxt__fetch__mixin__) {
|
|
||||||
Vue.mixin(fetchMixin)
|
|
||||||
Vue.__nuxt__fetch__mixin__ = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Component: <NuxtLink>
|
|
||||||
Vue.component(NuxtLink.name, NuxtLink)
|
|
||||||
Vue.component('NLink', NuxtLink)
|
|
||||||
|
|
||||||
if (!global.fetch) { global.fetch = fetch }
|
|
||||||
|
|
||||||
// Global shared references
|
|
||||||
let _lastPaths = []
|
|
||||||
let app
|
|
||||||
let router
|
|
||||||
let store
|
|
||||||
|
|
||||||
// Try to rehydrate SSR data from window
|
|
||||||
const NUXT = window.__NUXT__ || {}
|
|
||||||
|
|
||||||
const $config = NUXT.config || {}
|
|
||||||
if ($config._app) {
|
|
||||||
__webpack_public_path__ = urlJoin($config._app.cdnURL, $config._app.assetsPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.assign(Vue.config, {"silent":false,"performance":true})
|
|
||||||
|
|
||||||
const logs = NUXT.logs || []
|
|
||||||
if (logs.length > 0) {
|
|
||||||
const ssrLogStyle = 'background: #2E495E;border-radius: 0.5em;color: white;font-weight: bold;padding: 2px 0.5em;'
|
|
||||||
console.group && console.group ('%cNuxt SSR', ssrLogStyle)
|
|
||||||
logs.forEach(logObj => (console[logObj.type] || console.log)(...logObj.args))
|
|
||||||
delete NUXT.logs
|
|
||||||
console.groupEnd && console.groupEnd()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup global Vue error handler
|
|
||||||
if (!Vue.config.$nuxt) {
|
|
||||||
const defaultErrorHandler = Vue.config.errorHandler
|
|
||||||
Vue.config.errorHandler = async (err, vm, info, ...rest) => {
|
|
||||||
// Call other handler if exist
|
|
||||||
let handled = null
|
|
||||||
if (typeof defaultErrorHandler === 'function') {
|
|
||||||
handled = defaultErrorHandler(err, vm, info, ...rest)
|
|
||||||
}
|
|
||||||
if (handled === true) {
|
|
||||||
return handled
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vm && vm.$root) {
|
|
||||||
const nuxtApp = Object.keys(Vue.config.$nuxt)
|
|
||||||
.find(nuxtInstance => vm.$root[nuxtInstance])
|
|
||||||
|
|
||||||
// Show Nuxt Error Page
|
|
||||||
if (nuxtApp && vm.$root[nuxtApp].error && info !== 'render function') {
|
|
||||||
const currentApp = vm.$root[nuxtApp]
|
|
||||||
|
|
||||||
// Load error layout
|
|
||||||
let layout = (NuxtError.options || NuxtError).layout
|
|
||||||
if (typeof layout === 'function') {
|
|
||||||
layout = layout(currentApp.context)
|
|
||||||
}
|
|
||||||
if (layout) {
|
|
||||||
await currentApp.loadLayout(layout).catch(() => {})
|
|
||||||
}
|
|
||||||
currentApp.setLayout(layout)
|
|
||||||
|
|
||||||
currentApp.error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof defaultErrorHandler === 'function') {
|
|
||||||
return handled
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log to console
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
|
||||||
console.error(err)
|
|
||||||
} else {
|
|
||||||
console.error(err.message || err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Vue.config.$nuxt = {}
|
|
||||||
}
|
|
||||||
Vue.config.$nuxt.$nuxt = true
|
|
||||||
|
|
||||||
const errorHandler = Vue.config.errorHandler || console.error
|
|
||||||
|
|
||||||
// Create and mount App
|
|
||||||
createApp(null, NUXT.config).then(mountApp).catch(errorHandler)
|
|
||||||
|
|
||||||
function componentOption (component, key, ...args) {
|
|
||||||
if (!component || !component.options || !component.options[key]) {
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
const option = component.options[key]
|
|
||||||
if (typeof option === 'function') {
|
|
||||||
return option(...args)
|
|
||||||
}
|
|
||||||
return option
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapTransitions (toComponents, to, from) {
|
|
||||||
const componentTransitions = (component) => {
|
|
||||||
const transition = componentOption(component, 'transition', to, from) || {}
|
|
||||||
return (typeof transition === 'string' ? { name: transition } : transition)
|
|
||||||
}
|
|
||||||
|
|
||||||
const fromComponents = from ? getMatchedComponents(from) : []
|
|
||||||
const maxDepth = Math.max(toComponents.length, fromComponents.length)
|
|
||||||
|
|
||||||
const mergedTransitions = []
|
|
||||||
for (let i=0; i<maxDepth; i++) {
|
|
||||||
// Clone original objects to prevent overrides
|
|
||||||
const toTransitions = Object.assign({}, componentTransitions(toComponents[i]))
|
|
||||||
const transitions = Object.assign({}, componentTransitions(fromComponents[i]))
|
|
||||||
|
|
||||||
// Combine transitions & prefer `leave` properties of "from" route
|
|
||||||
Object.keys(toTransitions)
|
|
||||||
.filter(key => typeof toTransitions[key] !== 'undefined' && !key.toLowerCase().includes('leave'))
|
|
||||||
.forEach((key) => { transitions[key] = toTransitions[key] })
|
|
||||||
|
|
||||||
mergedTransitions.push(transitions)
|
|
||||||
}
|
|
||||||
return mergedTransitions
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadAsyncComponents (to, from, next) {
|
|
||||||
// Check if route changed (this._routeChanged), only if the page is not an error (for validate())
|
|
||||||
this._routeChanged = Boolean(app.nuxt.err) || from.name !== to.name
|
|
||||||
this._paramChanged = !this._routeChanged && from.path !== to.path
|
|
||||||
this._queryChanged = !this._paramChanged && from.fullPath !== to.fullPath
|
|
||||||
this._diffQuery = (this._queryChanged ? getQueryDiff(to.query, from.query) : [])
|
|
||||||
|
|
||||||
if ((this._routeChanged || this._paramChanged) && this.$loading.start && !this.$loading.manual) {
|
|
||||||
this.$loading.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (this._queryChanged) {
|
|
||||||
const Components = await resolveRouteComponents(
|
|
||||||
to,
|
|
||||||
(Component, instance) => ({ Component, instance })
|
|
||||||
)
|
|
||||||
// Add a marker on each component that it needs to refresh or not
|
|
||||||
const startLoader = Components.some(({ Component, instance }) => {
|
|
||||||
const watchQuery = Component.options.watchQuery
|
|
||||||
if (watchQuery === true) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (Array.isArray(watchQuery)) {
|
|
||||||
return watchQuery.some(key => this._diffQuery[key])
|
|
||||||
}
|
|
||||||
if (typeof watchQuery === 'function') {
|
|
||||||
return watchQuery.apply(instance, [to.query, from.query])
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
if (startLoader && this.$loading.start && !this.$loading.manual) {
|
|
||||||
this.$loading.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Call next()
|
|
||||||
next()
|
|
||||||
} catch (error) {
|
|
||||||
const err = error || {}
|
|
||||||
const statusCode = err.statusCode || err.status || (err.response && err.response.status) || 500
|
|
||||||
const message = err.message || ''
|
|
||||||
|
|
||||||
// Handle chunk loading errors
|
|
||||||
// This may be due to a new deployment or a network problem
|
|
||||||
if (/^Loading( CSS)? chunk (\d)+ failed\./.test(message)) {
|
|
||||||
window.location.reload(true /* skip cache */)
|
|
||||||
return // prevent error page blinking for user
|
|
||||||
}
|
|
||||||
|
|
||||||
this.error({ statusCode, message })
|
|
||||||
this.$nuxt.$emit('routeChanged', to, from, err)
|
|
||||||
next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function applySSRData (Component, ssrData) {
|
|
||||||
if (NUXT.serverRendered && ssrData) {
|
|
||||||
applyAsyncData(Component, ssrData)
|
|
||||||
}
|
|
||||||
|
|
||||||
Component._Ctor = Component
|
|
||||||
return Component
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get matched components
|
|
||||||
function resolveComponents (route) {
|
|
||||||
return flatMapComponents(route, async (Component, _, match, key, index) => {
|
|
||||||
// If component is not resolved yet, resolve it
|
|
||||||
if (typeof Component === 'function' && !Component.options) {
|
|
||||||
Component = await Component()
|
|
||||||
}
|
|
||||||
// Sanitize it and save it
|
|
||||||
const _Component = applySSRData(sanitizeComponent(Component), NUXT.data ? NUXT.data[index] : null)
|
|
||||||
match.components[key] = _Component
|
|
||||||
return _Component
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function callMiddleware (Components, context, layout) {
|
|
||||||
let midd = []
|
|
||||||
let unknownMiddleware = false
|
|
||||||
|
|
||||||
// If layout is undefined, only call global middleware
|
|
||||||
if (typeof layout !== 'undefined') {
|
|
||||||
midd = [] // Exclude global middleware if layout defined (already called before)
|
|
||||||
layout = sanitizeComponent(layout)
|
|
||||||
if (layout.options.middleware) {
|
|
||||||
midd = midd.concat(layout.options.middleware)
|
|
||||||
}
|
|
||||||
Components.forEach((Component) => {
|
|
||||||
if (Component.options.middleware) {
|
|
||||||
midd = midd.concat(Component.options.middleware)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
midd = midd.map((name) => {
|
|
||||||
if (typeof name === 'function') {
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
if (typeof middleware[name] !== 'function') {
|
|
||||||
unknownMiddleware = true
|
|
||||||
this.error({ statusCode: 500, message: 'Unknown middleware ' + name })
|
|
||||||
}
|
|
||||||
return middleware[name]
|
|
||||||
})
|
|
||||||
|
|
||||||
if (unknownMiddleware) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return middlewareSeries(midd, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function render (to, from, next) {
|
|
||||||
if (this._routeChanged === false && this._paramChanged === false && this._queryChanged === false) {
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
// Handle first render on SPA mode
|
|
||||||
let spaFallback = false
|
|
||||||
if (to === from) {
|
|
||||||
_lastPaths = []
|
|
||||||
spaFallback = true
|
|
||||||
} else {
|
|
||||||
const fromMatches = []
|
|
||||||
_lastPaths = getMatchedComponents(from, fromMatches).map((Component, i) => {
|
|
||||||
return compile(from.matched[fromMatches[i]].path)(from.params)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// nextCalled is true when redirected
|
|
||||||
let nextCalled = false
|
|
||||||
const _next = (path) => {
|
|
||||||
if (from.path === path.path && this.$loading.finish) {
|
|
||||||
this.$loading.finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (from.path !== path.path && this.$loading.pause) {
|
|
||||||
this.$loading.pause()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nextCalled) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
nextCalled = true
|
|
||||||
next(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update context
|
|
||||||
await setContext(app, {
|
|
||||||
route: to,
|
|
||||||
from,
|
|
||||||
next: _next.bind(this)
|
|
||||||
})
|
|
||||||
this._dateLastError = app.nuxt.dateErr
|
|
||||||
this._hadError = Boolean(app.nuxt.err)
|
|
||||||
|
|
||||||
// Get route's matched components
|
|
||||||
const matches = []
|
|
||||||
const Components = getMatchedComponents(to, matches)
|
|
||||||
|
|
||||||
// If no Components matched, generate 404
|
|
||||||
if (!Components.length) {
|
|
||||||
// Default layout
|
|
||||||
await callMiddleware.call(this, Components, app.context)
|
|
||||||
if (nextCalled) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load layout for error page
|
|
||||||
const errorLayout = (NuxtError.options || NuxtError).layout
|
|
||||||
const layout = await this.loadLayout(
|
|
||||||
typeof errorLayout === 'function'
|
|
||||||
? errorLayout.call(NuxtError, app.context)
|
|
||||||
: errorLayout
|
|
||||||
)
|
|
||||||
|
|
||||||
await callMiddleware.call(this, Components, app.context, layout)
|
|
||||||
if (nextCalled) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show error page
|
|
||||||
app.context.error({ statusCode: 404, message: 'This page could not be found' })
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update ._data and other properties if hot reloaded
|
|
||||||
Components.forEach((Component) => {
|
|
||||||
if (Component._Ctor && Component._Ctor.options) {
|
|
||||||
Component.options.asyncData = Component._Ctor.options.asyncData
|
|
||||||
Component.options.fetch = Component._Ctor.options.fetch
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Apply transitions
|
|
||||||
this.setTransitions(mapTransitions(Components, to, from))
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Call middleware
|
|
||||||
await callMiddleware.call(this, Components, app.context)
|
|
||||||
if (nextCalled) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (app.context._errored) {
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set layout
|
|
||||||
let layout = Components[0].options.layout
|
|
||||||
if (typeof layout === 'function') {
|
|
||||||
layout = layout(app.context)
|
|
||||||
}
|
|
||||||
layout = await this.loadLayout(layout)
|
|
||||||
|
|
||||||
// Call middleware for layout
|
|
||||||
await callMiddleware.call(this, Components, app.context, layout)
|
|
||||||
if (nextCalled) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (app.context._errored) {
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call .validate()
|
|
||||||
let isValid = true
|
|
||||||
try {
|
|
||||||
for (const Component of Components) {
|
|
||||||
if (typeof Component.options.validate !== 'function') {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
isValid = await Component.options.validate(app.context)
|
|
||||||
|
|
||||||
if (!isValid) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (validationError) {
|
|
||||||
// ...If .validate() threw an error
|
|
||||||
this.error({
|
|
||||||
statusCode: validationError.statusCode || '500',
|
|
||||||
message: validationError.message
|
|
||||||
})
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...If .validate() returned false
|
|
||||||
if (!isValid) {
|
|
||||||
this.error({ statusCode: 404, message: 'This page could not be found' })
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
let instances
|
|
||||||
// Call asyncData & fetch hooks on components matched by the route.
|
|
||||||
await Promise.all(Components.map(async (Component, i) => {
|
|
||||||
// Check if only children route changed
|
|
||||||
Component._path = compile(to.matched[matches[i]].path)(to.params)
|
|
||||||
Component._dataRefresh = false
|
|
||||||
const childPathChanged = Component._path !== _lastPaths[i]
|
|
||||||
// Refresh component (call asyncData & fetch) when:
|
|
||||||
// Route path changed part includes current component
|
|
||||||
// Or route param changed part includes current component and watchParam is not `false`
|
|
||||||
// Or route query is changed and watchQuery returns `true`
|
|
||||||
if (this._routeChanged && childPathChanged) {
|
|
||||||
Component._dataRefresh = true
|
|
||||||
} else if (this._paramChanged && childPathChanged) {
|
|
||||||
const watchParam = Component.options.watchParam
|
|
||||||
Component._dataRefresh = watchParam !== false
|
|
||||||
} else if (this._queryChanged) {
|
|
||||||
const watchQuery = Component.options.watchQuery
|
|
||||||
if (watchQuery === true) {
|
|
||||||
Component._dataRefresh = true
|
|
||||||
} else if (Array.isArray(watchQuery)) {
|
|
||||||
Component._dataRefresh = watchQuery.some(key => this._diffQuery[key])
|
|
||||||
} else if (typeof watchQuery === 'function') {
|
|
||||||
if (!instances) {
|
|
||||||
instances = getMatchedComponentsInstances(to)
|
|
||||||
}
|
|
||||||
Component._dataRefresh = watchQuery.apply(instances[i], [to.query, from.query])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!this._hadError && this._isMounted && !Component._dataRefresh) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const promises = []
|
|
||||||
|
|
||||||
const hasAsyncData = (
|
|
||||||
Component.options.asyncData &&
|
|
||||||
typeof Component.options.asyncData === 'function'
|
|
||||||
)
|
|
||||||
|
|
||||||
const hasFetch = Boolean(Component.options.fetch) && Component.options.fetch.length
|
|
||||||
|
|
||||||
const loadingIncrease = (hasAsyncData && hasFetch) ? 30 : 45
|
|
||||||
|
|
||||||
// Call asyncData(context)
|
|
||||||
if (hasAsyncData) {
|
|
||||||
const promise = promisify(Component.options.asyncData, app.context)
|
|
||||||
|
|
||||||
promise.then((asyncDataResult) => {
|
|
||||||
applyAsyncData(Component, asyncDataResult)
|
|
||||||
|
|
||||||
if (this.$loading.increase) {
|
|
||||||
this.$loading.increase(loadingIncrease)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
promises.push(promise)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check disabled page loading
|
|
||||||
this.$loading.manual = Component.options.loading === false
|
|
||||||
|
|
||||||
// Call fetch(context)
|
|
||||||
if (hasFetch) {
|
|
||||||
let p = Component.options.fetch(app.context)
|
|
||||||
if (!p || (!(p instanceof Promise) && (typeof p.then !== 'function'))) {
|
|
||||||
p = Promise.resolve(p)
|
|
||||||
}
|
|
||||||
p.then((fetchResult) => {
|
|
||||||
if (this.$loading.increase) {
|
|
||||||
this.$loading.increase(loadingIncrease)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
promises.push(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.all(promises)
|
|
||||||
}))
|
|
||||||
|
|
||||||
// If not redirected
|
|
||||||
if (!nextCalled) {
|
|
||||||
if (this.$loading.finish && !this.$loading.manual) {
|
|
||||||
this.$loading.finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
next()
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
const error = err || {}
|
|
||||||
if (error.message === 'ERR_REDIRECT') {
|
|
||||||
return this.$nuxt.$emit('routeChanged', to, from, error)
|
|
||||||
}
|
|
||||||
_lastPaths = []
|
|
||||||
|
|
||||||
globalHandleError(error)
|
|
||||||
|
|
||||||
// Load error layout
|
|
||||||
let layout = (NuxtError.options || NuxtError).layout
|
|
||||||
if (typeof layout === 'function') {
|
|
||||||
layout = layout(app.context)
|
|
||||||
}
|
|
||||||
await this.loadLayout(layout)
|
|
||||||
|
|
||||||
this.error(error)
|
|
||||||
this.$nuxt.$emit('routeChanged', to, from, error)
|
|
||||||
next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fix components format in matched, it's due to code-splitting of vue-router
|
|
||||||
function normalizeComponents (to, ___) {
|
|
||||||
flatMapComponents(to, (Component, _, match, key) => {
|
|
||||||
if (typeof Component === 'object' && !Component.options) {
|
|
||||||
// Updated via vue-router resolveAsyncComponents()
|
|
||||||
Component = Vue.extend(Component)
|
|
||||||
Component._Ctor = Component
|
|
||||||
match.components[key] = Component
|
|
||||||
}
|
|
||||||
return Component
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function setLayoutForNextPage (to) {
|
|
||||||
// Set layout
|
|
||||||
let hasError = Boolean(this.$options.nuxt.err)
|
|
||||||
if (this._hadError && this._dateLastError === this.$options.nuxt.dateErr) {
|
|
||||||
hasError = false
|
|
||||||
}
|
|
||||||
let layout = hasError
|
|
||||||
? (NuxtError.options || NuxtError).layout
|
|
||||||
: to.matched[0].components.default.options.layout
|
|
||||||
|
|
||||||
if (typeof layout === 'function') {
|
|
||||||
layout = layout(app.context)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setLayout(layout)
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkForErrors (app) {
|
|
||||||
// Hide error component if no error
|
|
||||||
if (app._hadError && app._dateLastError === app.$options.nuxt.dateErr) {
|
|
||||||
app.error()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// When navigating on a different route but the same component is used, Vue.js
|
|
||||||
// Will not update the instance data, so we have to update $data ourselves
|
|
||||||
function fixPrepatch (to, ___) {
|
|
||||||
if (this._routeChanged === false && this._paramChanged === false && this._queryChanged === false) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const instances = getMatchedComponentsInstances(to)
|
|
||||||
const Components = getMatchedComponents(to)
|
|
||||||
|
|
||||||
let triggerScroll = false
|
|
||||||
|
|
||||||
Vue.nextTick(() => {
|
|
||||||
instances.forEach((instance, i) => {
|
|
||||||
if (!instance || instance._isDestroyed) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
instance.constructor._dataRefresh &&
|
|
||||||
Components[i] === instance.constructor &&
|
|
||||||
instance.$vnode.data.keepAlive !== true &&
|
|
||||||
typeof instance.constructor.options.data === 'function'
|
|
||||||
) {
|
|
||||||
const newData = instance.constructor.options.data.call(instance)
|
|
||||||
for (const key in newData) {
|
|
||||||
Vue.set(instance.$data, key, newData[key])
|
|
||||||
}
|
|
||||||
|
|
||||||
triggerScroll = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (triggerScroll) {
|
|
||||||
// Ensure to trigger scroll event after calling scrollBehavior
|
|
||||||
window.$nuxt.$nextTick(() => {
|
|
||||||
window.$nuxt.$emit('triggerScroll')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
checkForErrors(this)
|
|
||||||
|
|
||||||
// Hot reloading
|
|
||||||
setTimeout(() => hotReloadAPI(this), 100)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function nuxtReady (_app) {
|
|
||||||
window.onNuxtReadyCbs.forEach((cb) => {
|
|
||||||
if (typeof cb === 'function') {
|
|
||||||
cb(_app)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
// Special JSDOM
|
|
||||||
if (typeof window._onNuxtLoaded === 'function') {
|
|
||||||
window._onNuxtLoaded(_app)
|
|
||||||
}
|
|
||||||
// Add router hooks
|
|
||||||
router.afterEach((to, from) => {
|
|
||||||
// Wait for fixPrepatch + $data updates
|
|
||||||
Vue.nextTick(() => _app.$nuxt.$emit('routeChanged', to, from))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const noopData = () => { return {} }
|
|
||||||
const noopFetch = () => {}
|
|
||||||
|
|
||||||
// Special hot reload with asyncData(context)
|
|
||||||
function getNuxtChildComponents ($parent, $components = []) {
|
|
||||||
$parent.$children.forEach(($child) => {
|
|
||||||
if ($child.$vnode && $child.$vnode.data.nuxtChild && !$components.find(c =>(c.$options.__file === $child.$options.__file))) {
|
|
||||||
$components.push($child)
|
|
||||||
}
|
|
||||||
if ($child.$children && $child.$children.length) {
|
|
||||||
getNuxtChildComponents($child, $components)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return $components
|
|
||||||
}
|
|
||||||
|
|
||||||
function hotReloadAPI(_app) {
|
|
||||||
if (!module.hot) return
|
|
||||||
|
|
||||||
let $components = getNuxtChildComponents(_app.$nuxt, [])
|
|
||||||
|
|
||||||
$components.forEach(addHotReload.bind(_app))
|
|
||||||
}
|
|
||||||
|
|
||||||
function addHotReload ($component, depth) {
|
|
||||||
if ($component.$vnode.data._hasHotReload) return
|
|
||||||
$component.$vnode.data._hasHotReload = true
|
|
||||||
|
|
||||||
var _forceUpdate = $component.$forceUpdate.bind($component.$parent)
|
|
||||||
|
|
||||||
$component.$vnode.context.$forceUpdate = async () => {
|
|
||||||
let Components = getMatchedComponents(router.currentRoute)
|
|
||||||
let Component = Components[depth]
|
|
||||||
if (!Component) {
|
|
||||||
return _forceUpdate()
|
|
||||||
}
|
|
||||||
if (typeof Component === 'object' && !Component.options) {
|
|
||||||
// Updated via vue-router resolveAsyncComponents()
|
|
||||||
Component = Vue.extend(Component)
|
|
||||||
Component._Ctor = Component
|
|
||||||
}
|
|
||||||
this.error()
|
|
||||||
let promises = []
|
|
||||||
const next = function (path) {
|
|
||||||
this.$loading.finish && this.$loading.finish()
|
|
||||||
router.push(path)
|
|
||||||
}
|
|
||||||
await setContext(app, {
|
|
||||||
route: router.currentRoute,
|
|
||||||
isHMR: true,
|
|
||||||
next: next.bind(this)
|
|
||||||
})
|
|
||||||
const context = app.context
|
|
||||||
|
|
||||||
if (this.$loading.start && !this.$loading.manual) {
|
|
||||||
this.$loading.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
callMiddleware.call(this, Components, context)
|
|
||||||
.then(() => {
|
|
||||||
// If layout changed
|
|
||||||
if (depth !== 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let layout = Component.options.layout || 'default'
|
|
||||||
if (typeof layout === 'function') {
|
|
||||||
layout = layout(context)
|
|
||||||
}
|
|
||||||
if (this.layoutName === layout) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let promise = this.loadLayout(layout)
|
|
||||||
promise.then(() => {
|
|
||||||
this.setLayout(layout)
|
|
||||||
Vue.nextTick(() => hotReloadAPI(this))
|
|
||||||
})
|
|
||||||
return promise
|
|
||||||
})
|
|
||||||
|
|
||||||
.then(() => {
|
|
||||||
return callMiddleware.call(this, Components, context, this.layout)
|
|
||||||
})
|
|
||||||
|
|
||||||
.then(() => {
|
|
||||||
// Call asyncData(context)
|
|
||||||
let pAsyncData = promisify(Component.options.asyncData || noopData, context)
|
|
||||||
pAsyncData.then((asyncDataResult) => {
|
|
||||||
applyAsyncData(Component, asyncDataResult)
|
|
||||||
this.$loading.increase && this.$loading.increase(30)
|
|
||||||
})
|
|
||||||
promises.push(pAsyncData)
|
|
||||||
|
|
||||||
// Call fetch()
|
|
||||||
Component.options.fetch = Component.options.fetch || noopFetch
|
|
||||||
let pFetch = Component.options.fetch.length && Component.options.fetch(context)
|
|
||||||
if (!pFetch || (!(pFetch instanceof Promise) && (typeof pFetch.then !== 'function'))) { pFetch = Promise.resolve(pFetch) }
|
|
||||||
pFetch.then(() => this.$loading.increase && this.$loading.increase(30))
|
|
||||||
promises.push(pFetch)
|
|
||||||
|
|
||||||
return Promise.all(promises)
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
this.$loading.finish && this.$loading.finish()
|
|
||||||
_forceUpdate()
|
|
||||||
setTimeout(() => hotReloadAPI(this), 100)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function mountApp (__app) {
|
|
||||||
// Set global variables
|
|
||||||
app = __app.app
|
|
||||||
router = __app.router
|
|
||||||
store = __app.store
|
|
||||||
|
|
||||||
// Create Vue instance
|
|
||||||
const _app = new Vue(app)
|
|
||||||
|
|
||||||
// Mounts Vue app to DOM element
|
|
||||||
const mount = () => {
|
|
||||||
_app.$mount('#__nuxt')
|
|
||||||
|
|
||||||
// Add afterEach router hooks
|
|
||||||
router.afterEach(normalizeComponents)
|
|
||||||
|
|
||||||
router.afterEach(setLayoutForNextPage.bind(_app))
|
|
||||||
|
|
||||||
router.afterEach(fixPrepatch.bind(_app))
|
|
||||||
|
|
||||||
// Listen for first Vue update
|
|
||||||
Vue.nextTick(() => {
|
|
||||||
// Call window.{{globals.readyCallback}} callbacks
|
|
||||||
nuxtReady(_app)
|
|
||||||
|
|
||||||
// Enable hot reloading
|
|
||||||
hotReloadAPI(_app)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve route components
|
|
||||||
const Components = await Promise.all(resolveComponents(app.context.route))
|
|
||||||
|
|
||||||
// Enable transitions
|
|
||||||
_app.setTransitions = _app.$options.nuxt.setTransitions.bind(_app)
|
|
||||||
if (Components.length) {
|
|
||||||
_app.setTransitions(mapTransitions(Components, router.currentRoute))
|
|
||||||
_lastPaths = router.currentRoute.matched.map(route => compile(route.path)(router.currentRoute.params))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize error handler
|
|
||||||
_app.$loading = {} // To avoid error while _app.$nuxt does not exist
|
|
||||||
if (NUXT.error) {
|
|
||||||
_app.error(NUXT.error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add beforeEach router hooks
|
|
||||||
router.beforeEach(loadAsyncComponents.bind(_app))
|
|
||||||
router.beforeEach(render.bind(_app))
|
|
||||||
|
|
||||||
// Fix in static: remove trailing slash to force hydration
|
|
||||||
// Full static, if server-rendered: hydrate, to allow custom redirect to generated page
|
|
||||||
|
|
||||||
// Fix in static: remove trailing slash to force hydration
|
|
||||||
if (NUXT.serverRendered && isSamePath(NUXT.routePath, _app.context.route.path)) {
|
|
||||||
return mount()
|
|
||||||
}
|
|
||||||
|
|
||||||
// First render on client-side
|
|
||||||
const clientFirstMount = () => {
|
|
||||||
normalizeComponents(router.currentRoute, router.currentRoute)
|
|
||||||
setLayoutForNextPage.call(_app, router.currentRoute)
|
|
||||||
checkForErrors(_app)
|
|
||||||
// Don't call fixPrepatch.call(_app, router.currentRoute, router.currentRoute) since it's first render
|
|
||||||
mount()
|
|
||||||
}
|
|
||||||
|
|
||||||
// fix: force next tick to avoid having same timestamp when an error happen on spa fallback
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 0))
|
|
||||||
render.call(_app, router.currentRoute, router.currentRoute, (path) => {
|
|
||||||
// If not redirected
|
|
||||||
if (!path) {
|
|
||||||
clientFirstMount()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a one-time afterEach hook to
|
|
||||||
// mount the app wait for redirect and route gets resolved
|
|
||||||
const unregisterHook = router.afterEach((to, from) => {
|
|
||||||
unregisterHook()
|
|
||||||
clientFirstMount()
|
|
||||||
})
|
|
||||||
|
|
||||||
// Push the path and let route to be resolved
|
|
||||||
router.push(path, undefined, (err) => {
|
|
||||||
if (err) {
|
|
||||||
errorHandler(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
import { wrapFunctional } from './utils'
|
|
||||||
|
|
||||||
export { default as AudioPlayer } from '../..\\components\\AudioPlayer.vue'
|
|
||||||
export { default as AppAppbar } from '../..\\components\\app\\Appbar.vue'
|
|
||||||
export { default as AppBookShelf } from '../..\\components\\app\\BookShelf.vue'
|
|
||||||
export { default as AppBookShelfToolbar } from '../..\\components\\app\\BookShelfToolbar.vue'
|
|
||||||
export { default as AppStreamContainer } from '../..\\components\\app\\StreamContainer.vue'
|
|
||||||
export { default as CardsBookCard } from '../..\\components\\cards\\BookCard.vue'
|
|
||||||
export { default as CardsBookCover } from '../..\\components\\cards\\BookCover.vue'
|
|
||||||
export { default as ControlsFilterSelect } from '../..\\components\\controls\\FilterSelect.vue'
|
|
||||||
export { default as ControlsOrderSelect } from '../..\\components\\controls\\OrderSelect.vue'
|
|
||||||
export { default as ControlsVolumeControl } from '../..\\components\\controls\\VolumeControl.vue'
|
|
||||||
export { default as ModalsEditModal } from '../..\\components\\modals\\EditModal.vue'
|
|
||||||
export { default as ModalsModal } from '../..\\components\\modals\\Modal.vue'
|
|
||||||
export { default as TablesAudioFilesTable } from '../..\\components\\tables\\AudioFilesTable.vue'
|
|
||||||
export { default as TablesOtherFilesTable } from '../..\\components\\tables\\OtherFilesTable.vue'
|
|
||||||
export { default as TablesTracksTable } from '../..\\components\\tables\\TracksTable.vue'
|
|
||||||
export { default as UiBtn } from '../..\\components\\ui\\Btn.vue'
|
|
||||||
export { default as UiLoadingIndicator } from '../..\\components\\ui\\LoadingIndicator.vue'
|
|
||||||
export { default as UiMenu } from '../..\\components\\ui\\Menu.vue'
|
|
||||||
export { default as UiTextareaInput } from '../..\\components\\ui\\TextareaInput.vue'
|
|
||||||
export { default as UiTextareaWithLabel } from '../..\\components\\ui\\TextareaWithLabel.vue'
|
|
||||||
export { default as UiTextInput } from '../..\\components\\ui\\TextInput.vue'
|
|
||||||
export { default as UiTextInputWithLabel } from '../..\\components\\ui\\TextInputWithLabel.vue'
|
|
||||||
export { default as UiTooltip } from '../..\\components\\ui\\Tooltip.vue'
|
|
||||||
export { default as WidgetsScanAlert } from '../..\\components\\widgets\\ScanAlert.vue'
|
|
||||||
export { default as ModalsEditTabsCover } from '../..\\components\\modals\\edit-tabs\\Cover.vue'
|
|
||||||
export { default as ModalsEditTabsDetails } from '../..\\components\\modals\\edit-tabs\\Details.vue'
|
|
||||||
export { default as ModalsEditTabsMatch } from '../..\\components\\modals\\edit-tabs\\Match.vue'
|
|
||||||
export { default as ModalsEditTabsTracks } from '../..\\components\\modals\\edit-tabs\\Tracks.vue'
|
|
||||||
|
|
||||||
export const LazyAudioPlayer = import('../..\\components\\AudioPlayer.vue' /* webpackChunkName: "components/audio-player" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyAppAppbar = import('../..\\components\\app\\Appbar.vue' /* webpackChunkName: "components/app-appbar" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyAppBookShelf = import('../..\\components\\app\\BookShelf.vue' /* webpackChunkName: "components/app-book-shelf" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyAppBookShelfToolbar = import('../..\\components\\app\\BookShelfToolbar.vue' /* webpackChunkName: "components/app-book-shelf-toolbar" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyAppStreamContainer = import('../..\\components\\app\\StreamContainer.vue' /* webpackChunkName: "components/app-stream-container" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyCardsBookCard = import('../..\\components\\cards\\BookCard.vue' /* webpackChunkName: "components/cards-book-card" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyCardsBookCover = import('../..\\components\\cards\\BookCover.vue' /* webpackChunkName: "components/cards-book-cover" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyControlsFilterSelect = import('../..\\components\\controls\\FilterSelect.vue' /* webpackChunkName: "components/controls-filter-select" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyControlsOrderSelect = import('../..\\components\\controls\\OrderSelect.vue' /* webpackChunkName: "components/controls-order-select" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyControlsVolumeControl = import('../..\\components\\controls\\VolumeControl.vue' /* webpackChunkName: "components/controls-volume-control" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyModalsEditModal = import('../..\\components\\modals\\EditModal.vue' /* webpackChunkName: "components/modals-edit-modal" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyModalsModal = import('../..\\components\\modals\\Modal.vue' /* webpackChunkName: "components/modals-modal" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyTablesAudioFilesTable = import('../..\\components\\tables\\AudioFilesTable.vue' /* webpackChunkName: "components/tables-audio-files-table" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyTablesOtherFilesTable = import('../..\\components\\tables\\OtherFilesTable.vue' /* webpackChunkName: "components/tables-other-files-table" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyTablesTracksTable = import('../..\\components\\tables\\TracksTable.vue' /* webpackChunkName: "components/tables-tracks-table" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyUiBtn = import('../..\\components\\ui\\Btn.vue' /* webpackChunkName: "components/ui-btn" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyUiLoadingIndicator = import('../..\\components\\ui\\LoadingIndicator.vue' /* webpackChunkName: "components/ui-loading-indicator" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyUiMenu = import('../..\\components\\ui\\Menu.vue' /* webpackChunkName: "components/ui-menu" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyUiTextareaInput = import('../..\\components\\ui\\TextareaInput.vue' /* webpackChunkName: "components/ui-textarea-input" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyUiTextareaWithLabel = import('../..\\components\\ui\\TextareaWithLabel.vue' /* webpackChunkName: "components/ui-textarea-with-label" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyUiTextInput = import('../..\\components\\ui\\TextInput.vue' /* webpackChunkName: "components/ui-text-input" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyUiTextInputWithLabel = import('../..\\components\\ui\\TextInputWithLabel.vue' /* webpackChunkName: "components/ui-text-input-with-label" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyUiTooltip = import('../..\\components\\ui\\Tooltip.vue' /* webpackChunkName: "components/ui-tooltip" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyWidgetsScanAlert = import('../..\\components\\widgets\\ScanAlert.vue' /* webpackChunkName: "components/widgets-scan-alert" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyModalsEditTabsCover = import('../..\\components\\modals\\edit-tabs\\Cover.vue' /* webpackChunkName: "components/modals-edit-tabs-cover" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyModalsEditTabsDetails = import('../..\\components\\modals\\edit-tabs\\Details.vue' /* webpackChunkName: "components/modals-edit-tabs-details" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyModalsEditTabsMatch = import('../..\\components\\modals\\edit-tabs\\Match.vue' /* webpackChunkName: "components/modals-edit-tabs-match" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
export const LazyModalsEditTabsTracks = import('../..\\components\\modals\\edit-tabs\\Tracks.vue' /* webpackChunkName: "components/modals-edit-tabs-tracks" */).then(c => wrapFunctional(c.default || c))
|
|
@ -1,143 +0,0 @@
|
|||||||
<template>
|
|
||||||
<transition appear>
|
|
||||||
<div v-if="building" class="nuxt__build_indicator" :style="indicatorStyle">
|
|
||||||
<svg viewBox="0 0 96 72" version="1" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<g fill="none" fill-rule="evenodd">
|
|
||||||
<path d="M6 66h23l1-3 21-37L40 6 6 66zM79 66h11L62 17l-5 9 22 37v3zM54 31L35 66h38z" />
|
|
||||||
<path d="M29 69v-1-2H6L40 6l11 20 3-6L44 3s-2-3-4-3-3 1-5 3L1 63c0 1-2 3 0 6 0 1 2 2 5 2h28c-3 0-4-1-5-2z" fill="#00C58E" />
|
|
||||||
<path d="M95 63L67 14c0-1-2-3-5-3-1 0-3 0-4 3l-4 6 3 6 5-9 28 49H79a5 5 0 0 1 0 3c-2 2-5 2-5 2h16c1 0 4 0 5-2 1-1 2-3 0-6z" fill="#00C58E" />
|
|
||||||
<path d="M79 69v-1-2-3L57 26l-3-6-3 6-21 37-1 3a5 5 0 0 0 0 3c1 1 2 2 5 2h40s3 0 5-2zM54 31l19 35H35l19-35z" fill="#FFF" fill-rule="nonzero" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
{{ animatedProgress }}%
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'NuxtBuildIndicator',
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
building: false,
|
|
||||||
progress: 0,
|
|
||||||
animatedProgress: 0,
|
|
||||||
reconnectAttempts: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
options: () => ({"position":"bottom-right","backgroundColor":"#2E495E","color":"#00C48D"}),
|
|
||||||
indicatorStyle () {
|
|
||||||
const [d1, d2] = this.options.position.split('-')
|
|
||||||
return {
|
|
||||||
[d1]: '20px',
|
|
||||||
[d2]: '20px',
|
|
||||||
'background-color': this.options.backgroundColor,
|
|
||||||
color: this.options.color
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
progress (val, oldVal) {
|
|
||||||
// Average progress may decrease but ignore it!
|
|
||||||
if (val < oldVal) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Cancel old animation
|
|
||||||
clearInterval(this._progressAnimation)
|
|
||||||
// Jump to edge immediately
|
|
||||||
if (val < 10 || val > 90) {
|
|
||||||
this.animatedProgress = val
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Animate to value
|
|
||||||
this._progressAnimation = setInterval(() => {
|
|
||||||
const diff = this.progress - this.animatedProgress
|
|
||||||
if (diff > 0) {
|
|
||||||
this.animatedProgress++
|
|
||||||
} else {
|
|
||||||
clearInterval(this._progressAnimation)
|
|
||||||
}
|
|
||||||
}, 50)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
if (EventSource === undefined) {
|
|
||||||
return // Unsupported
|
|
||||||
}
|
|
||||||
this.sseConnect()
|
|
||||||
},
|
|
||||||
beforeDestroy () {
|
|
||||||
this.sseClose()
|
|
||||||
clearInterval(this._progressAnimation)
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
sseConnect () {
|
|
||||||
if (this._connecting) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this._connecting = true
|
|
||||||
this.sse = new EventSource('/_loading/sse')
|
|
||||||
this.sse.addEventListener('message', event => this.onSseMessage(event))
|
|
||||||
},
|
|
||||||
onSseMessage (message) {
|
|
||||||
const data = JSON.parse(message.data)
|
|
||||||
if (!data.states) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.progress = Math.round(data.states.reduce((p, s) => p + s.progress, 0) / data.states.length)
|
|
||||||
|
|
||||||
if (!data.allDone) {
|
|
||||||
this.building = true
|
|
||||||
} else {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.building = false
|
|
||||||
this.animatedProgress = 0
|
|
||||||
this.progress = 0
|
|
||||||
clearInterval(this._progressAnimation)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
sseClose () {
|
|
||||||
if (this.sse) {
|
|
||||||
this.sse.close()
|
|
||||||
delete this.sse
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.nuxt__build_indicator {
|
|
||||||
box-sizing: border-box;
|
|
||||||
position: fixed;
|
|
||||||
font-family: monospace;
|
|
||||||
padding: 5px 10px;
|
|
||||||
border-radius: 5px;
|
|
||||||
box-shadow: 1px 1px 2px 0px rgba(0,0,0,0.2);
|
|
||||||
width: 88px;
|
|
||||||
z-index: 2147483647;
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 1.2rem;
|
|
||||||
}
|
|
||||||
.v-enter-active, .v-leave-active {
|
|
||||||
transition-delay: 0.2s;
|
|
||||||
transition-property: all;
|
|
||||||
transition-duration: 0.3s;
|
|
||||||
}
|
|
||||||
.v-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(20px);
|
|
||||||
}
|
|
||||||
svg {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: baseline;
|
|
||||||
width: 1.1em;
|
|
||||||
height: 0.825em;
|
|
||||||
position: relative;
|
|
||||||
top: 1px;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,122 +0,0 @@
|
|||||||
|
|
||||||
export default {
|
|
||||||
name: 'NuxtChild',
|
|
||||||
functional: true,
|
|
||||||
props: {
|
|
||||||
nuxtChildKey: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
keepAlive: Boolean,
|
|
||||||
keepAliveProps: {
|
|
||||||
type: Object,
|
|
||||||
default: undefined
|
|
||||||
}
|
|
||||||
},
|
|
||||||
render (_, { parent, data, props }) {
|
|
||||||
const h = parent.$createElement
|
|
||||||
|
|
||||||
data.nuxtChild = true
|
|
||||||
const _parent = parent
|
|
||||||
const transitions = parent.$nuxt.nuxt.transitions
|
|
||||||
const defaultTransition = parent.$nuxt.nuxt.defaultTransition
|
|
||||||
|
|
||||||
let depth = 0
|
|
||||||
while (parent) {
|
|
||||||
if (parent.$vnode && parent.$vnode.data.nuxtChild) {
|
|
||||||
depth++
|
|
||||||
}
|
|
||||||
parent = parent.$parent
|
|
||||||
}
|
|
||||||
data.nuxtChildDepth = depth
|
|
||||||
const transition = transitions[depth] || defaultTransition
|
|
||||||
const transitionProps = {}
|
|
||||||
transitionsKeys.forEach((key) => {
|
|
||||||
if (typeof transition[key] !== 'undefined') {
|
|
||||||
transitionProps[key] = transition[key]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const listeners = {}
|
|
||||||
listenersKeys.forEach((key) => {
|
|
||||||
if (typeof transition[key] === 'function') {
|
|
||||||
listeners[key] = transition[key].bind(_parent)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (process.client) {
|
|
||||||
// Add triggerScroll event on beforeEnter (fix #1376)
|
|
||||||
const beforeEnter = listeners.beforeEnter
|
|
||||||
listeners.beforeEnter = (el) => {
|
|
||||||
// Ensure to trigger scroll event after calling scrollBehavior
|
|
||||||
window.$nuxt.$nextTick(() => {
|
|
||||||
window.$nuxt.$emit('triggerScroll')
|
|
||||||
})
|
|
||||||
if (beforeEnter) {
|
|
||||||
return beforeEnter.call(_parent, el)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure that leave is called asynchronous (fix #5703)
|
|
||||||
if (transition.css === false) {
|
|
||||||
const leave = listeners.leave
|
|
||||||
|
|
||||||
// only add leave listener when user didnt provide one
|
|
||||||
// or when it misses the done argument
|
|
||||||
if (!leave || leave.length < 2) {
|
|
||||||
listeners.leave = (el, done) => {
|
|
||||||
if (leave) {
|
|
||||||
leave.call(_parent, el)
|
|
||||||
}
|
|
||||||
|
|
||||||
_parent.$nextTick(done)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let routerView = h('routerView', data)
|
|
||||||
|
|
||||||
if (props.keepAlive) {
|
|
||||||
routerView = h('keep-alive', { props: props.keepAliveProps }, [routerView])
|
|
||||||
}
|
|
||||||
|
|
||||||
return h('transition', {
|
|
||||||
props: transitionProps,
|
|
||||||
on: listeners
|
|
||||||
}, [routerView])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const transitionsKeys = [
|
|
||||||
'name',
|
|
||||||
'mode',
|
|
||||||
'appear',
|
|
||||||
'css',
|
|
||||||
'type',
|
|
||||||
'duration',
|
|
||||||
'enterClass',
|
|
||||||
'leaveClass',
|
|
||||||
'appearClass',
|
|
||||||
'enterActiveClass',
|
|
||||||
'enterActiveClass',
|
|
||||||
'leaveActiveClass',
|
|
||||||
'appearActiveClass',
|
|
||||||
'enterToClass',
|
|
||||||
'leaveToClass',
|
|
||||||
'appearToClass'
|
|
||||||
]
|
|
||||||
|
|
||||||
const listenersKeys = [
|
|
||||||
'beforeEnter',
|
|
||||||
'enter',
|
|
||||||
'afterEnter',
|
|
||||||
'enterCancelled',
|
|
||||||
'beforeLeave',
|
|
||||||
'leave',
|
|
||||||
'afterLeave',
|
|
||||||
'leaveCancelled',
|
|
||||||
'beforeAppear',
|
|
||||||
'appear',
|
|
||||||
'afterAppear',
|
|
||||||
'appearCancelled'
|
|
||||||
]
|
|
@ -1,98 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="__nuxt-error-page">
|
|
||||||
<div class="error">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="90" height="90" fill="#DBE1EC" viewBox="0 0 48 48">
|
|
||||||
<path d="M22 30h4v4h-4zm0-16h4v12h-4zm1.99-10C12.94 4 4 12.95 4 24s8.94 20 19.99 20S44 35.05 44 24 35.04 4 23.99 4zM24 40c-8.84 0-16-7.16-16-16S15.16 8 24 8s16 7.16 16 16-7.16 16-16 16z" />
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<div class="title">{{ message }}</div>
|
|
||||||
<p v-if="statusCode === 404" class="description">
|
|
||||||
<a v-if="typeof $route === 'undefined'" class="error-link" href="/"></a>
|
|
||||||
<NuxtLink v-else class="error-link" to="/">Back to the home page</NuxtLink>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="description" v-else>An error occurred while rendering the page. Check developer tools console for details.</p>
|
|
||||||
|
|
||||||
<div class="logo">
|
|
||||||
<a href="https://nuxtjs.org" target="_blank" rel="noopener">Nuxt</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'NuxtError',
|
|
||||||
props: {
|
|
||||||
error: {
|
|
||||||
type: Object,
|
|
||||||
default: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
statusCode () {
|
|
||||||
return (this.error && this.error.statusCode) || 500
|
|
||||||
},
|
|
||||||
message () {
|
|
||||||
return this.error.message || 'Error'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
head () {
|
|
||||||
return {
|
|
||||||
title: this.message,
|
|
||||||
meta: [
|
|
||||||
{
|
|
||||||
name: 'viewport',
|
|
||||||
content: 'width=device-width,initial-scale=1.0,minimum-scale=1.0'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.__nuxt-error-page {
|
|
||||||
padding: 1rem;
|
|
||||||
background: #F7F8FB;
|
|
||||||
color: #47494E;
|
|
||||||
text-align: center;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: column;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-weight: 100 !important;
|
|
||||||
-ms-text-size-adjust: 100%;
|
|
||||||
-webkit-text-size-adjust: 100%;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
.__nuxt-error-page .error {
|
|
||||||
max-width: 450px;
|
|
||||||
}
|
|
||||||
.__nuxt-error-page .title {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
margin-top: 15px;
|
|
||||||
color: #47494E;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
.__nuxt-error-page .description {
|
|
||||||
color: #7F828B;
|
|
||||||
line-height: 21px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
.__nuxt-error-page a {
|
|
||||||
color: #7F828B !important;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
.__nuxt-error-page .logo {
|
|
||||||
position: fixed;
|
|
||||||
left: 12px;
|
|
||||||
bottom: 12px;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,98 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
|
|
||||||
const requestIdleCallback = window.requestIdleCallback ||
|
|
||||||
function (cb) {
|
|
||||||
const start = Date.now()
|
|
||||||
return setTimeout(function () {
|
|
||||||
cb({
|
|
||||||
didTimeout: false,
|
|
||||||
timeRemaining: () => Math.max(0, 50 - (Date.now() - start))
|
|
||||||
})
|
|
||||||
}, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
const cancelIdleCallback = window.cancelIdleCallback || function (id) {
|
|
||||||
clearTimeout(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
const observer = window.IntersectionObserver && new window.IntersectionObserver((entries) => {
|
|
||||||
entries.forEach(({ intersectionRatio, target: link }) => {
|
|
||||||
if (intersectionRatio <= 0 || !link.__prefetch) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
link.__prefetch()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'NuxtLink',
|
|
||||||
extends: Vue.component('RouterLink'),
|
|
||||||
props: {
|
|
||||||
prefetch: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
noPrefetch: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted () {
|
|
||||||
if (this.prefetch && !this.noPrefetch) {
|
|
||||||
this.handleId = requestIdleCallback(this.observe, { timeout: 2e3 })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeDestroy () {
|
|
||||||
cancelIdleCallback(this.handleId)
|
|
||||||
|
|
||||||
if (this.__observed) {
|
|
||||||
observer.unobserve(this.$el)
|
|
||||||
delete this.$el.__prefetch
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
observe () {
|
|
||||||
// If no IntersectionObserver, avoid prefetching
|
|
||||||
if (!observer) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Add to observer
|
|
||||||
if (this.shouldPrefetch()) {
|
|
||||||
this.$el.__prefetch = this.prefetchLink.bind(this)
|
|
||||||
observer.observe(this.$el)
|
|
||||||
this.__observed = true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
shouldPrefetch () {
|
|
||||||
return this.getPrefetchComponents().length > 0
|
|
||||||
},
|
|
||||||
canPrefetch () {
|
|
||||||
const conn = navigator.connection
|
|
||||||
const hasBadConnection = this.$nuxt.isOffline || (conn && ((conn.effectiveType || '').includes('2g') || conn.saveData))
|
|
||||||
|
|
||||||
return !hasBadConnection
|
|
||||||
},
|
|
||||||
getPrefetchComponents () {
|
|
||||||
const ref = this.$router.resolve(this.to, this.$route, this.append)
|
|
||||||
const Components = ref.resolved.matched.map(r => r.components.default)
|
|
||||||
|
|
||||||
return Components.filter(Component => typeof Component === 'function' && !Component.options && !Component.__prefetched)
|
|
||||||
},
|
|
||||||
prefetchLink () {
|
|
||||||
if (!this.canPrefetch()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Stop observing this link (in case of internet connection changes)
|
|
||||||
observer.unobserve(this.$el)
|
|
||||||
const Components = this.getPrefetchComponents()
|
|
||||||
|
|
||||||
for (const Component of Components) {
|
|
||||||
const componentOrPromise = Component()
|
|
||||||
if (componentOrPromise instanceof Promise) {
|
|
||||||
componentOrPromise.catch(() => {})
|
|
||||||
}
|
|
||||||
Component.__prefetched = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'NuxtLink',
|
|
||||||
extends: Vue.component('RouterLink'),
|
|
||||||
props: {
|
|
||||||
prefetch: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
noPrefetch: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,177 +0,0 @@
|
|||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'NuxtLoading',
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
percent: 0,
|
|
||||||
show: false,
|
|
||||||
canSucceed: true,
|
|
||||||
reversed: false,
|
|
||||||
skipTimerCount: 0,
|
|
||||||
rtl: false,
|
|
||||||
throttle: 200,
|
|
||||||
duration: 5000,
|
|
||||||
continuous: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
left () {
|
|
||||||
if (!this.continuous && !this.rtl) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return this.rtl
|
|
||||||
? (this.reversed ? '0px' : 'auto')
|
|
||||||
: (!this.reversed ? '0px' : 'auto')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeDestroy () {
|
|
||||||
this.clear()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
clear () {
|
|
||||||
clearInterval(this._timer)
|
|
||||||
clearTimeout(this._throttle)
|
|
||||||
this._timer = null
|
|
||||||
},
|
|
||||||
start () {
|
|
||||||
this.clear()
|
|
||||||
this.percent = 0
|
|
||||||
this.reversed = false
|
|
||||||
this.skipTimerCount = 0
|
|
||||||
this.canSucceed = true
|
|
||||||
|
|
||||||
if (this.throttle) {
|
|
||||||
this._throttle = setTimeout(() => this.startTimer(), this.throttle)
|
|
||||||
} else {
|
|
||||||
this.startTimer()
|
|
||||||
}
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
set (num) {
|
|
||||||
this.show = true
|
|
||||||
this.canSucceed = true
|
|
||||||
this.percent = Math.min(100, Math.max(0, Math.floor(num)))
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
get () {
|
|
||||||
return this.percent
|
|
||||||
},
|
|
||||||
increase (num) {
|
|
||||||
this.percent = Math.min(100, Math.floor(this.percent + num))
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
decrease (num) {
|
|
||||||
this.percent = Math.max(0, Math.floor(this.percent - num))
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
pause () {
|
|
||||||
clearInterval(this._timer)
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
resume () {
|
|
||||||
this.startTimer()
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
finish () {
|
|
||||||
this.percent = this.reversed ? 0 : 100
|
|
||||||
this.hide()
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
hide () {
|
|
||||||
this.clear()
|
|
||||||
setTimeout(() => {
|
|
||||||
this.show = false
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.percent = 0
|
|
||||||
this.reversed = false
|
|
||||||
})
|
|
||||||
}, 500)
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
fail (error) {
|
|
||||||
this.canSucceed = false
|
|
||||||
return this
|
|
||||||
},
|
|
||||||
startTimer () {
|
|
||||||
if (!this.show) {
|
|
||||||
this.show = true
|
|
||||||
}
|
|
||||||
if (typeof this._cut === 'undefined') {
|
|
||||||
this._cut = 10000 / Math.floor(this.duration)
|
|
||||||
}
|
|
||||||
|
|
||||||
this._timer = setInterval(() => {
|
|
||||||
/**
|
|
||||||
* When reversing direction skip one timers
|
|
||||||
* so 0, 100 are displayed for two iterations
|
|
||||||
* also disable css width transitioning
|
|
||||||
* which otherwise interferes and shows
|
|
||||||
* a jojo effect
|
|
||||||
*/
|
|
||||||
if (this.skipTimerCount > 0) {
|
|
||||||
this.skipTimerCount--
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.reversed) {
|
|
||||||
this.decrease(this._cut)
|
|
||||||
} else {
|
|
||||||
this.increase(this._cut)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.continuous) {
|
|
||||||
if (this.percent >= 100) {
|
|
||||||
this.skipTimerCount = 1
|
|
||||||
|
|
||||||
this.reversed = !this.reversed
|
|
||||||
} else if (this.percent <= 0) {
|
|
||||||
this.skipTimerCount = 1
|
|
||||||
|
|
||||||
this.reversed = !this.reversed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 100)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
render (h) {
|
|
||||||
let el = h(false)
|
|
||||||
if (this.show) {
|
|
||||||
el = h('div', {
|
|
||||||
staticClass: 'nuxt-progress',
|
|
||||||
class: {
|
|
||||||
'nuxt-progress-notransition': this.skipTimerCount > 0,
|
|
||||||
'nuxt-progress-failed': !this.canSucceed
|
|
||||||
},
|
|
||||||
style: {
|
|
||||||
width: this.percent + '%',
|
|
||||||
left: this.left
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return el
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.nuxt-progress {
|
|
||||||
position: fixed;
|
|
||||||
top: 0px;
|
|
||||||
left: 0px;
|
|
||||||
right: 0px;
|
|
||||||
height: 2px;
|
|
||||||
width: 0%;
|
|
||||||
opacity: 1;
|
|
||||||
transition: width 0.1s, opacity 0.4s;
|
|
||||||
background-color: black;
|
|
||||||
z-index: 999999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nuxt-progress.nuxt-progress-notransition {
|
|
||||||
transition: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nuxt-progress-failed {
|
|
||||||
background-color: red;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,101 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
import { compile } from '../utils'
|
|
||||||
|
|
||||||
import NuxtError from './nuxt-error.vue'
|
|
||||||
|
|
||||||
import NuxtChild from './nuxt-child'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'Nuxt',
|
|
||||||
components: {
|
|
||||||
NuxtChild,
|
|
||||||
NuxtError
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
nuxtChildKey: {
|
|
||||||
type: String,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
keepAlive: Boolean,
|
|
||||||
keepAliveProps: {
|
|
||||||
type: Object,
|
|
||||||
default: undefined
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: String,
|
|
||||||
default: 'default'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
errorCaptured (error) {
|
|
||||||
// if we receive and error while showing the NuxtError component
|
|
||||||
// capture the error and force an immediate update so we re-render
|
|
||||||
// without the NuxtError component
|
|
||||||
if (this.displayingNuxtError) {
|
|
||||||
this.errorFromNuxtError = error
|
|
||||||
this.$forceUpdate()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
routerViewKey () {
|
|
||||||
// If nuxtChildKey prop is given or current route has children
|
|
||||||
if (typeof this.nuxtChildKey !== 'undefined' || this.$route.matched.length > 1) {
|
|
||||||
return this.nuxtChildKey || compile(this.$route.matched[0].path)(this.$route.params)
|
|
||||||
}
|
|
||||||
|
|
||||||
const [matchedRoute] = this.$route.matched
|
|
||||||
|
|
||||||
if (!matchedRoute) {
|
|
||||||
return this.$route.path
|
|
||||||
}
|
|
||||||
|
|
||||||
const Component = matchedRoute.components.default
|
|
||||||
|
|
||||||
if (Component && Component.options) {
|
|
||||||
const { options } = Component
|
|
||||||
|
|
||||||
if (options.key) {
|
|
||||||
return (typeof options.key === 'function' ? options.key(this.$route) : options.key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const strict = /\/$/.test(matchedRoute.path)
|
|
||||||
return strict ? this.$route.path : this.$route.path.replace(/\/$/, '')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeCreate () {
|
|
||||||
Vue.util.defineReactive(this, 'nuxt', this.$root.$options.nuxt)
|
|
||||||
},
|
|
||||||
render (h) {
|
|
||||||
// if there is no error
|
|
||||||
if (!this.nuxt.err) {
|
|
||||||
// Directly return nuxt child
|
|
||||||
return h('NuxtChild', {
|
|
||||||
key: this.routerViewKey,
|
|
||||||
props: this.$props
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// if an error occurred within NuxtError show a simple
|
|
||||||
// error message instead to prevent looping
|
|
||||||
if (this.errorFromNuxtError) {
|
|
||||||
this.$nextTick(() => (this.errorFromNuxtError = false))
|
|
||||||
|
|
||||||
return h('div', {}, [
|
|
||||||
h('h2', 'An error occurred while showing the error page'),
|
|
||||||
h('p', 'Unfortunately an error occurred and while showing the error page another error occurred'),
|
|
||||||
h('p', `Error details: ${this.errorFromNuxtError.toString()}`),
|
|
||||||
h('nuxt-link', { props: { to: '/' } }, 'Go back to home')
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
// track if we are showing the NuxtError component
|
|
||||||
this.displayingNuxtError = true
|
|
||||||
this.$nextTick(() => (this.displayingNuxtError = false))
|
|
||||||
|
|
||||||
return h(NuxtError, {
|
|
||||||
props: {
|
|
||||||
error: this.nuxt.err
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
import { wrapFunctional } from './utils'
|
|
||||||
|
|
||||||
const components = {
|
|
||||||
AudioPlayer: () => import('../..\\components\\AudioPlayer.vue' /* webpackChunkName: "components/audio-player" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
AppAppbar: () => import('../..\\components\\app\\Appbar.vue' /* webpackChunkName: "components/app-appbar" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
AppBookShelf: () => import('../..\\components\\app\\BookShelf.vue' /* webpackChunkName: "components/app-book-shelf" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
AppBookShelfToolbar: () => import('../..\\components\\app\\BookShelfToolbar.vue' /* webpackChunkName: "components/app-book-shelf-toolbar" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
AppStreamContainer: () => import('../..\\components\\app\\StreamContainer.vue' /* webpackChunkName: "components/app-stream-container" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
CardsBookCard: () => import('../..\\components\\cards\\BookCard.vue' /* webpackChunkName: "components/cards-book-card" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
CardsBookCover: () => import('../..\\components\\cards\\BookCover.vue' /* webpackChunkName: "components/cards-book-cover" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
ControlsFilterSelect: () => import('../..\\components\\controls\\FilterSelect.vue' /* webpackChunkName: "components/controls-filter-select" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
ControlsOrderSelect: () => import('../..\\components\\controls\\OrderSelect.vue' /* webpackChunkName: "components/controls-order-select" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
ControlsVolumeControl: () => import('../..\\components\\controls\\VolumeControl.vue' /* webpackChunkName: "components/controls-volume-control" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
ModalsEditModal: () => import('../..\\components\\modals\\EditModal.vue' /* webpackChunkName: "components/modals-edit-modal" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
ModalsModal: () => import('../..\\components\\modals\\Modal.vue' /* webpackChunkName: "components/modals-modal" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
TablesAudioFilesTable: () => import('../..\\components\\tables\\AudioFilesTable.vue' /* webpackChunkName: "components/tables-audio-files-table" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
TablesOtherFilesTable: () => import('../..\\components\\tables\\OtherFilesTable.vue' /* webpackChunkName: "components/tables-other-files-table" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
TablesTracksTable: () => import('../..\\components\\tables\\TracksTable.vue' /* webpackChunkName: "components/tables-tracks-table" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
UiBtn: () => import('../..\\components\\ui\\Btn.vue' /* webpackChunkName: "components/ui-btn" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
UiLoadingIndicator: () => import('../..\\components\\ui\\LoadingIndicator.vue' /* webpackChunkName: "components/ui-loading-indicator" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
UiMenu: () => import('../..\\components\\ui\\Menu.vue' /* webpackChunkName: "components/ui-menu" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
UiTextareaInput: () => import('../..\\components\\ui\\TextareaInput.vue' /* webpackChunkName: "components/ui-textarea-input" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
UiTextareaWithLabel: () => import('../..\\components\\ui\\TextareaWithLabel.vue' /* webpackChunkName: "components/ui-textarea-with-label" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
UiTextInput: () => import('../..\\components\\ui\\TextInput.vue' /* webpackChunkName: "components/ui-text-input" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
UiTextInputWithLabel: () => import('../..\\components\\ui\\TextInputWithLabel.vue' /* webpackChunkName: "components/ui-text-input-with-label" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
UiTooltip: () => import('../..\\components\\ui\\Tooltip.vue' /* webpackChunkName: "components/ui-tooltip" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
WidgetsScanAlert: () => import('../..\\components\\widgets\\ScanAlert.vue' /* webpackChunkName: "components/widgets-scan-alert" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
ModalsEditTabsCover: () => import('../..\\components\\modals\\edit-tabs\\Cover.vue' /* webpackChunkName: "components/modals-edit-tabs-cover" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
ModalsEditTabsDetails: () => import('../..\\components\\modals\\edit-tabs\\Details.vue' /* webpackChunkName: "components/modals-edit-tabs-details" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
ModalsEditTabsMatch: () => import('../..\\components\\modals\\edit-tabs\\Match.vue' /* webpackChunkName: "components/modals-edit-tabs-match" */).then(c => wrapFunctional(c.default || c)),
|
|
||||||
ModalsEditTabsTracks: () => import('../..\\components\\modals\\edit-tabs\\Tracks.vue' /* webpackChunkName: "components/modals-edit-tabs-tracks" */).then(c => wrapFunctional(c.default || c))
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const name in components) {
|
|
||||||
Vue.component(name, components[name])
|
|
||||||
Vue.component('Lazy' + name, components[name])
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
# Discovered Components
|
|
||||||
|
|
||||||
This is an auto-generated list of components discovered by [nuxt/components](https://github.com/nuxt/components).
|
|
||||||
|
|
||||||
You can directly use them in pages and other components without the need to import them.
|
|
||||||
|
|
||||||
**Tip:** If a component is conditionally rendered with `v-if` and is big, it is better to use `Lazy` or `lazy-` prefix to lazy load.
|
|
||||||
|
|
||||||
- `<AudioPlayer>` | `<audio-player>` (components/AudioPlayer.vue)
|
|
||||||
- `<AppAppbar>` | `<app-appbar>` (components/app/Appbar.vue)
|
|
||||||
- `<AppBookShelf>` | `<app-book-shelf>` (components/app/BookShelf.vue)
|
|
||||||
- `<AppBookShelfToolbar>` | `<app-book-shelf-toolbar>` (components/app/BookShelfToolbar.vue)
|
|
||||||
- `<AppStreamContainer>` | `<app-stream-container>` (components/app/StreamContainer.vue)
|
|
||||||
- `<CardsBookCard>` | `<cards-book-card>` (components/cards/BookCard.vue)
|
|
||||||
- `<CardsBookCover>` | `<cards-book-cover>` (components/cards/BookCover.vue)
|
|
||||||
- `<ControlsFilterSelect>` | `<controls-filter-select>` (components/controls/FilterSelect.vue)
|
|
||||||
- `<ControlsOrderSelect>` | `<controls-order-select>` (components/controls/OrderSelect.vue)
|
|
||||||
- `<ControlsVolumeControl>` | `<controls-volume-control>` (components/controls/VolumeControl.vue)
|
|
||||||
- `<ModalsEditModal>` | `<modals-edit-modal>` (components/modals/EditModal.vue)
|
|
||||||
- `<ModalsModal>` | `<modals-modal>` (components/modals/Modal.vue)
|
|
||||||
- `<TablesAudioFilesTable>` | `<tables-audio-files-table>` (components/tables/AudioFilesTable.vue)
|
|
||||||
- `<TablesOtherFilesTable>` | `<tables-other-files-table>` (components/tables/OtherFilesTable.vue)
|
|
||||||
- `<TablesTracksTable>` | `<tables-tracks-table>` (components/tables/TracksTable.vue)
|
|
||||||
- `<UiBtn>` | `<ui-btn>` (components/ui/Btn.vue)
|
|
||||||
- `<UiLoadingIndicator>` | `<ui-loading-indicator>` (components/ui/LoadingIndicator.vue)
|
|
||||||
- `<UiMenu>` | `<ui-menu>` (components/ui/Menu.vue)
|
|
||||||
- `<UiTextareaInput>` | `<ui-textarea-input>` (components/ui/TextareaInput.vue)
|
|
||||||
- `<UiTextareaWithLabel>` | `<ui-textarea-with-label>` (components/ui/TextareaWithLabel.vue)
|
|
||||||
- `<UiTextInput>` | `<ui-text-input>` (components/ui/TextInput.vue)
|
|
||||||
- `<UiTextInputWithLabel>` | `<ui-text-input-with-label>` (components/ui/TextInputWithLabel.vue)
|
|
||||||
- `<UiTooltip>` | `<ui-tooltip>` (components/ui/Tooltip.vue)
|
|
||||||
- `<WidgetsScanAlert>` | `<widgets-scan-alert>` (components/widgets/ScanAlert.vue)
|
|
||||||
- `<ModalsEditTabsCover>` | `<modals-edit-tabs-cover>` (components/modals/edit-tabs/Cover.vue)
|
|
||||||
- `<ModalsEditTabsDetails>` | `<modals-edit-tabs-details>` (components/modals/edit-tabs/Details.vue)
|
|
||||||
- `<ModalsEditTabsMatch>` | `<modals-edit-tabs-match>` (components/modals/edit-tabs/Match.vue)
|
|
||||||
- `<ModalsEditTabsTracks>` | `<modals-edit-tabs-tracks>` (components/modals/edit-tabs/Tracks.vue)
|
|
@ -1,30 +0,0 @@
|
|||||||
// nuxt/nuxt.js#8607
|
|
||||||
export function wrapFunctional(options) {
|
|
||||||
if (!options || !options.functional) {
|
|
||||||
return options
|
|
||||||
}
|
|
||||||
|
|
||||||
const propKeys = Array.isArray(options.props) ? options.props : Object.keys(options.props || {})
|
|
||||||
|
|
||||||
return {
|
|
||||||
render(h) {
|
|
||||||
const attrs = {}
|
|
||||||
const props = {}
|
|
||||||
|
|
||||||
for (const key in this.$attrs) {
|
|
||||||
if (propKeys.includes(key)) {
|
|
||||||
props[key] = this.$attrs[key]
|
|
||||||
} else {
|
|
||||||
attrs[key] = this.$attrs[key]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return h(options, {
|
|
||||||
on: this.$listeners,
|
|
||||||
attrs,
|
|
||||||
props,
|
|
||||||
scopedSlots: this.$scopedSlots,
|
|
||||||
}, this.$slots.default)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
// This file is intentionally left empty for noop aliases
|
|
@ -1,279 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
import Vuex from 'vuex'
|
|
||||||
import Meta from 'vue-meta'
|
|
||||||
import ClientOnly from 'vue-client-only'
|
|
||||||
import NoSsr from 'vue-no-ssr'
|
|
||||||
import { createRouter } from './router.js'
|
|
||||||
import NuxtChild from './components/nuxt-child.js'
|
|
||||||
import NuxtError from './components/nuxt-error.vue'
|
|
||||||
import Nuxt from './components/nuxt.js'
|
|
||||||
import App from './App.js'
|
|
||||||
import { setContext, getLocation, getRouteData, normalizeError } from './utils'
|
|
||||||
import { createStore } from './store.js'
|
|
||||||
|
|
||||||
/* Plugins */
|
|
||||||
|
|
||||||
import nuxt_plugin_plugin_30872e99 from 'nuxt_plugin_plugin_30872e99' // Source: .\\components\\plugin.js (mode: 'all')
|
|
||||||
import nuxt_plugin_axios_65e5003c from 'nuxt_plugin_axios_65e5003c' // Source: .\\axios.js (mode: 'all')
|
|
||||||
import nuxt_plugin_nuxtsocketio_e6f92ab4 from 'nuxt_plugin_nuxtsocketio_e6f92ab4' // Source: .\\nuxt-socket-io.js (mode: 'all')
|
|
||||||
import nuxt_plugin_initclient_bdf7e43c from 'nuxt_plugin_initclient_bdf7e43c' // Source: ..\\plugins\\init.client.js (mode: 'client')
|
|
||||||
import nuxt_plugin_axios_397e53b5 from 'nuxt_plugin_axios_397e53b5' // Source: ..\\plugins\\axios.js (mode: 'all')
|
|
||||||
import nuxt_plugin_toast_05aea064 from 'nuxt_plugin_toast_05aea064' // Source: ..\\plugins\\toast.js (mode: 'all')
|
|
||||||
|
|
||||||
// Component: <ClientOnly>
|
|
||||||
Vue.component(ClientOnly.name, ClientOnly)
|
|
||||||
|
|
||||||
// TODO: Remove in Nuxt 3: <NoSsr>
|
|
||||||
Vue.component(NoSsr.name, {
|
|
||||||
...NoSsr,
|
|
||||||
render (h, ctx) {
|
|
||||||
if (process.client && !NoSsr._warned) {
|
|
||||||
NoSsr._warned = true
|
|
||||||
|
|
||||||
console.warn('<no-ssr> has been deprecated and will be removed in Nuxt 3, please use <client-only> instead')
|
|
||||||
}
|
|
||||||
return NoSsr.render(h, ctx)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Component: <NuxtChild>
|
|
||||||
Vue.component(NuxtChild.name, NuxtChild)
|
|
||||||
Vue.component('NChild', NuxtChild)
|
|
||||||
|
|
||||||
// Component NuxtLink is imported in server.js or client.js
|
|
||||||
|
|
||||||
// Component: <Nuxt>
|
|
||||||
Vue.component(Nuxt.name, Nuxt)
|
|
||||||
|
|
||||||
Object.defineProperty(Vue.prototype, '$nuxt', {
|
|
||||||
get() {
|
|
||||||
const globalNuxt = this.$root.$options.$nuxt
|
|
||||||
if (process.client && !globalNuxt && typeof window !== 'undefined') {
|
|
||||||
return window.$nuxt
|
|
||||||
}
|
|
||||||
return globalNuxt
|
|
||||||
},
|
|
||||||
configurable: true
|
|
||||||
})
|
|
||||||
|
|
||||||
Vue.use(Meta, {"keyName":"head","attribute":"data-n-head","ssrAttribute":"data-n-head-ssr","tagIDKeyName":"hid"})
|
|
||||||
|
|
||||||
const defaultTransition = {"name":"page","mode":"out-in","appear":true,"appearClass":"appear","appearActiveClass":"appear-active","appearToClass":"appear-to"}
|
|
||||||
|
|
||||||
const originalRegisterModule = Vuex.Store.prototype.registerModule
|
|
||||||
|
|
||||||
function registerModule (path, rawModule, options = {}) {
|
|
||||||
const preserveState = process.client && (
|
|
||||||
Array.isArray(path)
|
|
||||||
? !!path.reduce((namespacedState, path) => namespacedState && namespacedState[path], this.state)
|
|
||||||
: path in this.state
|
|
||||||
)
|
|
||||||
return originalRegisterModule.call(this, path, rawModule, { preserveState, ...options })
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createApp(ssrContext, config = {}) {
|
|
||||||
const router = await createRouter(ssrContext, config)
|
|
||||||
|
|
||||||
const store = createStore(ssrContext)
|
|
||||||
// Add this.$router into store actions/mutations
|
|
||||||
store.$router = router
|
|
||||||
|
|
||||||
// Create Root instance
|
|
||||||
|
|
||||||
// here we inject the router and store to all child components,
|
|
||||||
// making them available everywhere as `this.$router` and `this.$store`.
|
|
||||||
const app = {
|
|
||||||
head: {"title":"AudioBookshelf","htmlAttrs":{"lang":"en"},"meta":[{"charset":"utf-8"},{"name":"viewport","content":"width=device-width, initial-scale=1"},{"hid":"description","name":"description","content":""}],"script":[{"src":"\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fsortablejs@1.8.4\u002FSortable.min.js"}],"link":[{"rel":"icon","type":"image\u002Fx-icon","href":"\u002Ffavicon.ico"},{"rel":"stylesheet","href":"https:\u002F\u002Ffonts.googleapis.com\u002Fcss2?family=Fira+Mono&family=Ubuntu+Mono&family=Open+Sans:wght@600&family=Gentium+Book+Basic"},{"rel":"stylesheet","href":"https:\u002F\u002Ffonts.googleapis.com\u002Ficon?family=Material+Icons"}],"style":[]},
|
|
||||||
|
|
||||||
store,
|
|
||||||
router,
|
|
||||||
nuxt: {
|
|
||||||
defaultTransition,
|
|
||||||
transitions: [defaultTransition],
|
|
||||||
setTransitions (transitions) {
|
|
||||||
if (!Array.isArray(transitions)) {
|
|
||||||
transitions = [transitions]
|
|
||||||
}
|
|
||||||
transitions = transitions.map((transition) => {
|
|
||||||
if (!transition) {
|
|
||||||
transition = defaultTransition
|
|
||||||
} else if (typeof transition === 'string') {
|
|
||||||
transition = Object.assign({}, defaultTransition, { name: transition })
|
|
||||||
} else {
|
|
||||||
transition = Object.assign({}, defaultTransition, transition)
|
|
||||||
}
|
|
||||||
return transition
|
|
||||||
})
|
|
||||||
this.$options.nuxt.transitions = transitions
|
|
||||||
return transitions
|
|
||||||
},
|
|
||||||
|
|
||||||
err: null,
|
|
||||||
dateErr: null,
|
|
||||||
error (err) {
|
|
||||||
err = err || null
|
|
||||||
app.context._errored = Boolean(err)
|
|
||||||
err = err ? normalizeError(err) : null
|
|
||||||
let nuxt = app.nuxt // to work with @vue/composition-api, see https://github.com/nuxt/nuxt.js/issues/6517#issuecomment-573280207
|
|
||||||
if (this) {
|
|
||||||
nuxt = this.nuxt || this.$options.nuxt
|
|
||||||
}
|
|
||||||
nuxt.dateErr = Date.now()
|
|
||||||
nuxt.err = err
|
|
||||||
// Used in src/server.js
|
|
||||||
if (ssrContext) {
|
|
||||||
ssrContext.nuxt.error = err
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
},
|
|
||||||
...App
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make app available into store via this.app
|
|
||||||
store.app = app
|
|
||||||
|
|
||||||
const next = ssrContext ? ssrContext.next : location => app.router.push(location)
|
|
||||||
// Resolve route
|
|
||||||
let route
|
|
||||||
if (ssrContext) {
|
|
||||||
route = router.resolve(ssrContext.url).route
|
|
||||||
} else {
|
|
||||||
const path = getLocation(router.options.base, router.options.mode)
|
|
||||||
route = router.resolve(path).route
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set context to app.context
|
|
||||||
await setContext(app, {
|
|
||||||
store,
|
|
||||||
route,
|
|
||||||
next,
|
|
||||||
error: app.nuxt.error.bind(app),
|
|
||||||
payload: ssrContext ? ssrContext.payload : undefined,
|
|
||||||
req: ssrContext ? ssrContext.req : undefined,
|
|
||||||
res: ssrContext ? ssrContext.res : undefined,
|
|
||||||
beforeRenderFns: ssrContext ? ssrContext.beforeRenderFns : undefined,
|
|
||||||
ssrContext
|
|
||||||
})
|
|
||||||
|
|
||||||
function inject(key, value) {
|
|
||||||
if (!key) {
|
|
||||||
throw new Error('inject(key, value) has no key provided')
|
|
||||||
}
|
|
||||||
if (value === undefined) {
|
|
||||||
throw new Error(`inject('${key}', value) has no value provided`)
|
|
||||||
}
|
|
||||||
|
|
||||||
key = '$' + key
|
|
||||||
// Add into app
|
|
||||||
app[key] = value
|
|
||||||
// Add into context
|
|
||||||
if (!app.context[key]) {
|
|
||||||
app.context[key] = value
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add into store
|
|
||||||
store[key] = app[key]
|
|
||||||
|
|
||||||
// Check if plugin not already installed
|
|
||||||
const installKey = '__nuxt_' + key + '_installed__'
|
|
||||||
if (Vue[installKey]) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Vue[installKey] = true
|
|
||||||
// Call Vue.use() to install the plugin into vm
|
|
||||||
Vue.use(() => {
|
|
||||||
if (!Object.prototype.hasOwnProperty.call(Vue.prototype, key)) {
|
|
||||||
Object.defineProperty(Vue.prototype, key, {
|
|
||||||
get () {
|
|
||||||
return this.$root.$options[key]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inject runtime config as $config
|
|
||||||
inject('config', config)
|
|
||||||
|
|
||||||
if (process.client) {
|
|
||||||
// Replace store state before plugins execution
|
|
||||||
if (window.__NUXT__ && window.__NUXT__.state) {
|
|
||||||
store.replaceState(window.__NUXT__.state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add enablePreview(previewData = {}) in context for plugins
|
|
||||||
if (process.static && process.client) {
|
|
||||||
app.context.enablePreview = function (previewData = {}) {
|
|
||||||
app.previewData = Object.assign({}, previewData)
|
|
||||||
inject('preview', previewData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Plugin execution
|
|
||||||
|
|
||||||
if (typeof nuxt_plugin_plugin_30872e99 === 'function') {
|
|
||||||
await nuxt_plugin_plugin_30872e99(app.context, inject)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof nuxt_plugin_axios_65e5003c === 'function') {
|
|
||||||
await nuxt_plugin_axios_65e5003c(app.context, inject)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof nuxt_plugin_nuxtsocketio_e6f92ab4 === 'function') {
|
|
||||||
await nuxt_plugin_nuxtsocketio_e6f92ab4(app.context, inject)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.client && typeof nuxt_plugin_initclient_bdf7e43c === 'function') {
|
|
||||||
await nuxt_plugin_initclient_bdf7e43c(app.context, inject)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof nuxt_plugin_axios_397e53b5 === 'function') {
|
|
||||||
await nuxt_plugin_axios_397e53b5(app.context, inject)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof nuxt_plugin_toast_05aea064 === 'function') {
|
|
||||||
await nuxt_plugin_toast_05aea064(app.context, inject)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lock enablePreview in context
|
|
||||||
if (process.static && process.client) {
|
|
||||||
app.context.enablePreview = function () {
|
|
||||||
console.warn('You cannot call enablePreview() outside a plugin.')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for async component to be resolved first
|
|
||||||
await new Promise((resolve, reject) => {
|
|
||||||
const { route } = router.resolve(app.context.route.fullPath)
|
|
||||||
// Ignore 404s rather than blindly replacing URL
|
|
||||||
if (!route.matched.length && process.client) {
|
|
||||||
return resolve()
|
|
||||||
}
|
|
||||||
router.replace(route, resolve, (err) => {
|
|
||||||
// https://github.com/vuejs/vue-router/blob/v3.4.3/src/util/errors.js
|
|
||||||
if (!err._isRouter) return reject(err)
|
|
||||||
if (err.type !== 2 /* NavigationFailureType.redirected */) return resolve()
|
|
||||||
|
|
||||||
// navigated to a different route in router guard
|
|
||||||
const unregister = router.afterEach(async (to, from) => {
|
|
||||||
if (process.server && ssrContext && ssrContext.url) {
|
|
||||||
ssrContext.url = to.fullPath
|
|
||||||
}
|
|
||||||
app.context.route = await getRouteData(to)
|
|
||||||
app.context.params = to.params || {}
|
|
||||||
app.context.query = to.query || {}
|
|
||||||
unregister()
|
|
||||||
resolve()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
store,
|
|
||||||
app,
|
|
||||||
router
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { createApp, NuxtError }
|
|
@ -1,82 +0,0 @@
|
|||||||
const chunks = {} // chunkId => exports
|
|
||||||
const chunksInstalling = {} // chunkId => Promise
|
|
||||||
const failedChunks = {}
|
|
||||||
|
|
||||||
function importChunk(chunkId, src) {
|
|
||||||
// Already installed
|
|
||||||
if (chunks[chunkId]) {
|
|
||||||
return Promise.resolve(chunks[chunkId])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Failed loading
|
|
||||||
if (failedChunks[chunkId]) {
|
|
||||||
return Promise.reject(failedChunks[chunkId])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Installing
|
|
||||||
if (chunksInstalling[chunkId]) {
|
|
||||||
return chunksInstalling[chunkId]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set a promise in chunk cache
|
|
||||||
let resolve, reject
|
|
||||||
const promise = chunksInstalling[chunkId] = new Promise((_resolve, _reject) => {
|
|
||||||
resolve = _resolve
|
|
||||||
reject = _reject
|
|
||||||
})
|
|
||||||
|
|
||||||
// Clear chunk data from cache
|
|
||||||
delete chunks[chunkId]
|
|
||||||
|
|
||||||
// Start chunk loading
|
|
||||||
const script = document.createElement('script')
|
|
||||||
script.charset = 'utf-8'
|
|
||||||
script.timeout = 120
|
|
||||||
script.src = src
|
|
||||||
let timeout
|
|
||||||
|
|
||||||
// Create error before stack unwound to get useful stacktrace later
|
|
||||||
const error = new Error()
|
|
||||||
|
|
||||||
// Complete handlers
|
|
||||||
const onScriptComplete = script.onerror = script.onload = (event) => {
|
|
||||||
// Cleanups
|
|
||||||
clearTimeout(timeout)
|
|
||||||
delete chunksInstalling[chunkId]
|
|
||||||
|
|
||||||
// Avoid mem leaks in IE
|
|
||||||
script.onerror = script.onload = null
|
|
||||||
|
|
||||||
// Verify chunk is loaded
|
|
||||||
if (chunks[chunkId]) {
|
|
||||||
return resolve(chunks[chunkId])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Something bad happened
|
|
||||||
const errorType = event && (event.type === 'load' ? 'missing' : event.type)
|
|
||||||
const realSrc = event && event.target && event.target.src
|
|
||||||
error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')'
|
|
||||||
error.name = 'ChunkLoadError'
|
|
||||||
error.type = errorType
|
|
||||||
error.request = realSrc
|
|
||||||
failedChunks[chunkId] = error
|
|
||||||
reject(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Timeout
|
|
||||||
timeout = setTimeout(() => {
|
|
||||||
onScriptComplete({ type: 'timeout', target: script })
|
|
||||||
}, 120000)
|
|
||||||
|
|
||||||
// Append script
|
|
||||||
document.head.appendChild(script)
|
|
||||||
|
|
||||||
// Return promise
|
|
||||||
return promise
|
|
||||||
}
|
|
||||||
|
|
||||||
export function installJsonp() {
|
|
||||||
window.__NUXT_JSONP__ = function (chunkId, exports) { chunks[chunkId] = exports }
|
|
||||||
window.__NUXT_JSONP_CACHE__ = chunks
|
|
||||||
window.__NUXT_IMPORT__ = importChunk
|
|
||||||
}
|
|
@ -1,110 +0,0 @@
|
|||||||
<style>
|
|
||||||
#nuxt-loading {
|
|
||||||
background: white;
|
|
||||||
visibility: hidden;
|
|
||||||
opacity: 0;
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: column;
|
|
||||||
animation: nuxtLoadingIn 10s ease;
|
|
||||||
-webkit-animation: nuxtLoadingIn 10s ease;
|
|
||||||
animation-fill-mode: forwards;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes nuxtLoadingIn {
|
|
||||||
0% {
|
|
||||||
visibility: hidden;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
20% {
|
|
||||||
visibility: visible;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
visibility: visible;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@-webkit-keyframes nuxtLoadingIn {
|
|
||||||
0% {
|
|
||||||
visibility: hidden;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
20% {
|
|
||||||
visibility: visible;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
visibility: visible;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#nuxt-loading>div,
|
|
||||||
#nuxt-loading>div:after {
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 5rem;
|
|
||||||
height: 5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#nuxt-loading>div {
|
|
||||||
font-size: 10px;
|
|
||||||
position: relative;
|
|
||||||
text-indent: -9999em;
|
|
||||||
border: .5rem solid #F5F5F5;
|
|
||||||
border-left: .5rem solid black;
|
|
||||||
-webkit-transform: translateZ(0);
|
|
||||||
-ms-transform: translateZ(0);
|
|
||||||
transform: translateZ(0);
|
|
||||||
-webkit-animation: nuxtLoading 1.1s infinite linear;
|
|
||||||
animation: nuxtLoading 1.1s infinite linear;
|
|
||||||
}
|
|
||||||
|
|
||||||
#nuxt-loading.error>div {
|
|
||||||
border-left: .5rem solid #ff4500;
|
|
||||||
animation-duration: 5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
@-webkit-keyframes nuxtLoading {
|
|
||||||
0% {
|
|
||||||
-webkit-transform: rotate(0deg);
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
-webkit-transform: rotate(360deg);
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes nuxtLoading {
|
|
||||||
0% {
|
|
||||||
-webkit-transform: rotate(0deg);
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
-webkit-transform: rotate(360deg);
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
window.addEventListener('error', function () {
|
|
||||||
var e = document.getElementById('nuxt-loading');
|
|
||||||
if (e) {
|
|
||||||
e.className += ' error';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div id="nuxt-loading" aria-live="polite" role="status"><div>Loading...</div></div>
|
|
||||||
|
|
||||||
<!-- https://projects.lukehaas.me/css-loaders -->
|
|
@ -1,3 +0,0 @@
|
|||||||
const middleware = {}
|
|
||||||
|
|
||||||
export default middleware
|
|
@ -1,90 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
import { hasFetch, normalizeError, addLifecycleHook, createGetCounter } from '../utils'
|
|
||||||
|
|
||||||
const isSsrHydration = (vm) => vm.$vnode && vm.$vnode.elm && vm.$vnode.elm.dataset && vm.$vnode.elm.dataset.fetchKey
|
|
||||||
const nuxtState = window.__NUXT__
|
|
||||||
|
|
||||||
export default {
|
|
||||||
beforeCreate () {
|
|
||||||
if (!hasFetch(this)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this._fetchDelay = typeof this.$options.fetchDelay === 'number' ? this.$options.fetchDelay : 200
|
|
||||||
|
|
||||||
Vue.util.defineReactive(this, '$fetchState', {
|
|
||||||
pending: false,
|
|
||||||
error: null,
|
|
||||||
timestamp: Date.now()
|
|
||||||
})
|
|
||||||
|
|
||||||
this.$fetch = $fetch.bind(this)
|
|
||||||
addLifecycleHook(this, 'created', created)
|
|
||||||
addLifecycleHook(this, 'beforeMount', beforeMount)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function beforeMount() {
|
|
||||||
if (!this._hydrated) {
|
|
||||||
return this.$fetch()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function created() {
|
|
||||||
if (!isSsrHydration(this)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hydrate component
|
|
||||||
this._hydrated = true
|
|
||||||
this._fetchKey = this.$vnode.elm.dataset.fetchKey
|
|
||||||
const data = nuxtState.fetch[this._fetchKey]
|
|
||||||
|
|
||||||
// If fetch error
|
|
||||||
if (data && data._error) {
|
|
||||||
this.$fetchState.error = data._error
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge data
|
|
||||||
for (const key in data) {
|
|
||||||
Vue.set(this.$data, key, data[key])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function $fetch() {
|
|
||||||
if (!this._fetchPromise) {
|
|
||||||
this._fetchPromise = $_fetch.call(this)
|
|
||||||
.then(() => { delete this._fetchPromise })
|
|
||||||
}
|
|
||||||
return this._fetchPromise
|
|
||||||
}
|
|
||||||
|
|
||||||
async function $_fetch() {
|
|
||||||
this.$nuxt.nbFetching++
|
|
||||||
this.$fetchState.pending = true
|
|
||||||
this.$fetchState.error = null
|
|
||||||
this._hydrated = false
|
|
||||||
let error = null
|
|
||||||
const startTime = Date.now()
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.$options.fetch.call(this)
|
|
||||||
} catch (err) {
|
|
||||||
if (process.dev) {
|
|
||||||
console.error('Error in fetch():', err)
|
|
||||||
}
|
|
||||||
error = normalizeError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
const delayLeft = this._fetchDelay - (Date.now() - startTime)
|
|
||||||
if (delayLeft > 0) {
|
|
||||||
await new Promise(resolve => setTimeout(resolve, delayLeft))
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$fetchState.error = error
|
|
||||||
this.$fetchState.pending = false
|
|
||||||
this.$fetchState.timestamp = Date.now()
|
|
||||||
|
|
||||||
this.$nextTick(() => this.$nuxt.nbFetching--)
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
import { hasFetch, normalizeError, addLifecycleHook, purifyData, createGetCounter } from '../utils'
|
|
||||||
|
|
||||||
async function serverPrefetch() {
|
|
||||||
if (!this._fetchOnServer) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call and await on $fetch
|
|
||||||
try {
|
|
||||||
await this.$options.fetch.call(this)
|
|
||||||
} catch (err) {
|
|
||||||
if (process.dev) {
|
|
||||||
console.error('Error in fetch():', err)
|
|
||||||
}
|
|
||||||
this.$fetchState.error = normalizeError(err)
|
|
||||||
}
|
|
||||||
this.$fetchState.pending = false
|
|
||||||
|
|
||||||
// Define an ssrKey for hydration
|
|
||||||
this._fetchKey = this._fetchKey || this.$ssrContext.fetchCounters['']++
|
|
||||||
|
|
||||||
// Add data-fetch-key on parent element of Component
|
|
||||||
const attrs = this.$vnode.data.attrs = this.$vnode.data.attrs || {}
|
|
||||||
attrs['data-fetch-key'] = this._fetchKey
|
|
||||||
|
|
||||||
// Add to ssrContext for window.__NUXT__.fetch
|
|
||||||
|
|
||||||
if (this.$ssrContext.nuxt.fetch[this._fetchKey] !== undefined) {
|
|
||||||
console.warn(`Duplicate fetch key detected (${this._fetchKey}). This may lead to unexpected results.`)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$ssrContext.nuxt.fetch[this._fetchKey] =
|
|
||||||
this.$fetchState.error ? { _error: this.$fetchState.error } : purifyData(this._data)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
created() {
|
|
||||||
if (!hasFetch(this)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof this.$options.fetchOnServer === 'function') {
|
|
||||||
this._fetchOnServer = this.$options.fetchOnServer.call(this) !== false
|
|
||||||
} else {
|
|
||||||
this._fetchOnServer = this.$options.fetchOnServer !== false
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultKey = this.$options._scopeId || this.$options.name || ''
|
|
||||||
const getCounter = createGetCounter(this.$ssrContext.fetchCounters, defaultKey)
|
|
||||||
|
|
||||||
if (typeof this.$options.fetchKey === 'function') {
|
|
||||||
this._fetchKey = this.$options.fetchKey.call(this, getCounter)
|
|
||||||
} else {
|
|
||||||
const key = 'string' === typeof this.$options.fetchKey ? this.$options.fetchKey : defaultKey
|
|
||||||
this._fetchKey = key ? key + ':' + getCounter(key) : String(getCounter(key))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Added for remove vue undefined warning while ssr
|
|
||||||
this.$fetch = () => {} // issue #8043
|
|
||||||
Vue.util.defineReactive(this, '$fetchState', {
|
|
||||||
pending: true,
|
|
||||||
error: null,
|
|
||||||
timestamp: Date.now()
|
|
||||||
})
|
|
||||||
|
|
||||||
addLifecycleHook(this, 'serverPrefetch', serverPrefetch)
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,68 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
import Router from 'vue-router'
|
|
||||||
import { normalizeURL, decode } from 'ufo'
|
|
||||||
import { interopDefault } from './utils'
|
|
||||||
import scrollBehavior from './router.scrollBehavior.js'
|
|
||||||
|
|
||||||
const _e09ac034 = () => interopDefault(import('..\\pages\\config\\index.vue' /* webpackChunkName: "pages/config/index" */))
|
|
||||||
const _4bf07cff = () => interopDefault(import('..\\pages\\login.vue' /* webpackChunkName: "pages/login" */))
|
|
||||||
const _4885a1ad = () => interopDefault(import('..\\pages\\audiobook\\_id\\index.vue' /* webpackChunkName: "pages/audiobook/_id/index" */))
|
|
||||||
const _73d517ff = () => interopDefault(import('..\\pages\\audiobook\\_id\\edit.vue' /* webpackChunkName: "pages/audiobook/_id/edit" */))
|
|
||||||
const _fb6e4c30 = () => interopDefault(import('..\\pages\\index.vue' /* webpackChunkName: "pages/index" */))
|
|
||||||
|
|
||||||
const emptyFn = () => {}
|
|
||||||
|
|
||||||
Vue.use(Router)
|
|
||||||
|
|
||||||
export const routerOptions = {
|
|
||||||
mode: 'history',
|
|
||||||
base: '/',
|
|
||||||
linkActiveClass: 'nuxt-link-active',
|
|
||||||
linkExactActiveClass: 'nuxt-link-exact-active',
|
|
||||||
scrollBehavior,
|
|
||||||
|
|
||||||
routes: [{
|
|
||||||
path: "/config",
|
|
||||||
component: _e09ac034,
|
|
||||||
name: "config"
|
|
||||||
}, {
|
|
||||||
path: "/login",
|
|
||||||
component: _4bf07cff,
|
|
||||||
name: "login"
|
|
||||||
}, {
|
|
||||||
path: "/audiobook/:id",
|
|
||||||
component: _4885a1ad,
|
|
||||||
name: "audiobook-id"
|
|
||||||
}, {
|
|
||||||
path: "/audiobook/:id?/edit",
|
|
||||||
component: _73d517ff,
|
|
||||||
name: "audiobook-id-edit"
|
|
||||||
}, {
|
|
||||||
path: "/",
|
|
||||||
component: _fb6e4c30,
|
|
||||||
name: "index"
|
|
||||||
}],
|
|
||||||
|
|
||||||
fallback: false
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createRouter (ssrContext, config) {
|
|
||||||
const base = (config._app && config._app.basePath) || routerOptions.base
|
|
||||||
const router = new Router({ ...routerOptions, base })
|
|
||||||
|
|
||||||
// TODO: remove in Nuxt 3
|
|
||||||
const originalPush = router.push
|
|
||||||
router.push = function push (location, onComplete = emptyFn, onAbort) {
|
|
||||||
return originalPush.call(this, location, onComplete, onAbort)
|
|
||||||
}
|
|
||||||
|
|
||||||
const resolve = router.resolve.bind(router)
|
|
||||||
router.resolve = (to, current, append) => {
|
|
||||||
if (typeof to === 'string') {
|
|
||||||
to = normalizeURL(to)
|
|
||||||
}
|
|
||||||
return resolve(to, current, append)
|
|
||||||
}
|
|
||||||
|
|
||||||
return router
|
|
||||||
}
|
|
@ -1,76 +0,0 @@
|
|||||||
import { getMatchedComponents, setScrollRestoration } from './utils'
|
|
||||||
|
|
||||||
if (process.client) {
|
|
||||||
if ('scrollRestoration' in window.history) {
|
|
||||||
setScrollRestoration('manual')
|
|
||||||
|
|
||||||
// reset scrollRestoration to auto when leaving page, allowing page reload
|
|
||||||
// and back-navigation from other pages to use the browser to restore the
|
|
||||||
// scrolling position.
|
|
||||||
window.addEventListener('beforeunload', () => {
|
|
||||||
setScrollRestoration('auto')
|
|
||||||
})
|
|
||||||
|
|
||||||
// Setting scrollRestoration to manual again when returning to this page.
|
|
||||||
window.addEventListener('load', () => {
|
|
||||||
setScrollRestoration('manual')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldScrollToTop(route) {
|
|
||||||
const Pages = getMatchedComponents(route)
|
|
||||||
if (Pages.length === 1) {
|
|
||||||
const { options = {} } = Pages[0]
|
|
||||||
return options.scrollToTop !== false
|
|
||||||
}
|
|
||||||
return Pages.some(({ options }) => options && options.scrollToTop)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function (to, from, savedPosition) {
|
|
||||||
// If the returned position is falsy or an empty object, will retain current scroll position
|
|
||||||
let position = false
|
|
||||||
const isRouteChanged = to !== from
|
|
||||||
|
|
||||||
// savedPosition is only available for popstate navigations (back button)
|
|
||||||
if (savedPosition) {
|
|
||||||
position = savedPosition
|
|
||||||
} else if (isRouteChanged && shouldScrollToTop(to)) {
|
|
||||||
position = { x: 0, y: 0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
const nuxt = window.$nuxt
|
|
||||||
|
|
||||||
if (
|
|
||||||
// Initial load (vuejs/vue-router#3199)
|
|
||||||
!isRouteChanged ||
|
|
||||||
// Route hash changes
|
|
||||||
(to.path === from.path && to.hash !== from.hash)
|
|
||||||
) {
|
|
||||||
nuxt.$nextTick(() => nuxt.$emit('triggerScroll'))
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
// wait for the out transition to complete (if necessary)
|
|
||||||
nuxt.$once('triggerScroll', () => {
|
|
||||||
// coords will be used if no selector is provided,
|
|
||||||
// or if the selector didn't match any element.
|
|
||||||
if (to.hash) {
|
|
||||||
let hash = to.hash
|
|
||||||
// CSS.escape() is not supported with IE and Edge.
|
|
||||||
if (typeof window.CSS !== 'undefined' && typeof window.CSS.escape !== 'undefined') {
|
|
||||||
hash = '#' + window.CSS.escape(hash.substr(1))
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (document.querySelector(hash)) {
|
|
||||||
// scroll to anchor by returning the selector
|
|
||||||
position = { selector: hash }
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('Failed to save scroll position. Please add CSS.escape() polyfill (https://github.com/mathiasbynens/CSS.escape).')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resolve(position)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"name": "config",
|
|
||||||
"path": "/config",
|
|
||||||
"component": "C:\\\\Users\\\\Coop\\\\Documents\\\\NodeProjects\\\\audiobookshelf\\\\client\\\\pages\\\\config\\\\index.vue",
|
|
||||||
"chunkName": "pages/config/index",
|
|
||||||
"_name": "_e09ac034"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "login",
|
|
||||||
"path": "/login",
|
|
||||||
"component": "C:\\\\Users\\\\Coop\\\\Documents\\\\NodeProjects\\\\audiobookshelf\\\\client\\\\pages\\\\login.vue",
|
|
||||||
"chunkName": "pages/login",
|
|
||||||
"_name": "_4bf07cff"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "audiobook-id",
|
|
||||||
"path": "/audiobook/:id",
|
|
||||||
"component": "C:\\\\Users\\\\Coop\\\\Documents\\\\NodeProjects\\\\audiobookshelf\\\\client\\\\pages\\\\audiobook\\\\_id\\\\index.vue",
|
|
||||||
"chunkName": "pages/audiobook/_id/index",
|
|
||||||
"_name": "_4885a1ad"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "audiobook-id-edit",
|
|
||||||
"path": "/audiobook/:id?/edit",
|
|
||||||
"component": "C:\\\\Users\\\\Coop\\\\Documents\\\\NodeProjects\\\\audiobookshelf\\\\client\\\\pages\\\\audiobook\\\\_id\\\\edit.vue",
|
|
||||||
"chunkName": "pages/audiobook/_id/edit",
|
|
||||||
"_name": "_73d517ff"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "index",
|
|
||||||
"path": "/",
|
|
||||||
"component": "C:\\\\Users\\\\Coop\\\\Documents\\\\NodeProjects\\\\audiobookshelf\\\\client\\\\pages\\\\index.vue",
|
|
||||||
"chunkName": "pages/index",
|
|
||||||
"_name": "_fb6e4c30"
|
|
||||||
}
|
|
||||||
]
|
|
@ -1,312 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
import { joinURL, normalizeURL, withQuery } from 'ufo'
|
|
||||||
import fetch from 'node-fetch'
|
|
||||||
import middleware from './middleware.js'
|
|
||||||
import {
|
|
||||||
applyAsyncData,
|
|
||||||
middlewareSeries,
|
|
||||||
sanitizeComponent,
|
|
||||||
getMatchedComponents,
|
|
||||||
promisify
|
|
||||||
} from './utils.js'
|
|
||||||
import fetchMixin from './mixins/fetch.server'
|
|
||||||
import { createApp, NuxtError } from './index.js'
|
|
||||||
import NuxtLink from './components/nuxt-link.server.js' // should be included after ./index.js
|
|
||||||
|
|
||||||
// Update serverPrefetch strategy
|
|
||||||
Vue.config.optionMergeStrategies.serverPrefetch = Vue.config.optionMergeStrategies.created
|
|
||||||
|
|
||||||
// Fetch mixin
|
|
||||||
if (!Vue.__nuxt__fetch__mixin__) {
|
|
||||||
Vue.mixin(fetchMixin)
|
|
||||||
Vue.__nuxt__fetch__mixin__ = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Vue.__original_use__) {
|
|
||||||
Vue.__original_use__ = Vue.use
|
|
||||||
Vue.__install_times__ = 0
|
|
||||||
Vue.use = function (plugin, ...args) {
|
|
||||||
plugin.__nuxt_external_installed__ = Vue._installedPlugins.includes(plugin)
|
|
||||||
return Vue.__original_use__(plugin, ...args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Vue.__install_times__ === 2) {
|
|
||||||
Vue.__install_times__ = 0
|
|
||||||
Vue._installedPlugins = Vue._installedPlugins.filter(plugin => {
|
|
||||||
return plugin.__nuxt_external_installed__ === true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Vue.__install_times__++
|
|
||||||
|
|
||||||
// Component: <NuxtLink>
|
|
||||||
Vue.component(NuxtLink.name, NuxtLink)
|
|
||||||
Vue.component('NLink', NuxtLink)
|
|
||||||
|
|
||||||
if (!global.fetch) { global.fetch = fetch }
|
|
||||||
|
|
||||||
const noopApp = () => new Vue({ render: h => h('div', { domProps: { id: '__nuxt' } }) })
|
|
||||||
|
|
||||||
const createNext = ssrContext => (opts) => {
|
|
||||||
// If static target, render on client-side
|
|
||||||
ssrContext.redirected = opts
|
|
||||||
if (ssrContext.target === 'static' || !ssrContext.res) {
|
|
||||||
ssrContext.nuxt.serverRendered = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let fullPath = withQuery(opts.path, opts.query)
|
|
||||||
const $config = ssrContext.runtimeConfig || {}
|
|
||||||
const routerBase = ($config._app && $config._app.basePath) || '/'
|
|
||||||
if (!fullPath.startsWith('http') && (routerBase !== '/' && !fullPath.startsWith(routerBase))) {
|
|
||||||
fullPath = joinURL(routerBase, fullPath)
|
|
||||||
}
|
|
||||||
// Avoid loop redirect
|
|
||||||
if (decodeURI(fullPath) === decodeURI(ssrContext.url)) {
|
|
||||||
ssrContext.redirected = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ssrContext.res.writeHead(opts.status, {
|
|
||||||
Location: normalizeURL(fullPath)
|
|
||||||
})
|
|
||||||
ssrContext.res.end()
|
|
||||||
}
|
|
||||||
|
|
||||||
// This exported function will be called by `bundleRenderer`.
|
|
||||||
// This is where we perform data-prefetching to determine the
|
|
||||||
// state of our application before actually rendering it.
|
|
||||||
// Since data fetching is async, this function is expected to
|
|
||||||
// return a Promise that resolves to the app instance.
|
|
||||||
export default async (ssrContext) => {
|
|
||||||
// Create ssrContext.next for simulate next() of beforeEach() when wanted to redirect
|
|
||||||
ssrContext.redirected = false
|
|
||||||
ssrContext.next = createNext(ssrContext)
|
|
||||||
// Used for beforeNuxtRender({ Components, nuxtState })
|
|
||||||
ssrContext.beforeRenderFns = []
|
|
||||||
// Nuxt object (window.{{globals.context}}, defaults to window.__NUXT__)
|
|
||||||
ssrContext.nuxt = { layout: 'default', data: [], fetch: {}, error: null, state: null, serverRendered: true, routePath: '' }
|
|
||||||
|
|
||||||
ssrContext.fetchCounters = {}
|
|
||||||
|
|
||||||
// Remove query from url is static target
|
|
||||||
|
|
||||||
// Public runtime config
|
|
||||||
ssrContext.nuxt.config = ssrContext.runtimeConfig.public
|
|
||||||
if (ssrContext.nuxt.config._app) {
|
|
||||||
__webpack_public_path__ = joinURL(ssrContext.nuxt.config._app.cdnURL, ssrContext.nuxt.config._app.assetsPath)
|
|
||||||
}
|
|
||||||
// Create the app definition and the instance (created for each request)
|
|
||||||
const { app, router, store } = await createApp(ssrContext, ssrContext.runtimeConfig.private)
|
|
||||||
const _app = new Vue(app)
|
|
||||||
// Add ssr route path to nuxt context so we can account for page navigation between ssr and csr
|
|
||||||
ssrContext.nuxt.routePath = app.context.route.path
|
|
||||||
|
|
||||||
// Add meta infos (used in renderer.js)
|
|
||||||
ssrContext.meta = _app.$meta()
|
|
||||||
|
|
||||||
// Keep asyncData for each matched component in ssrContext (used in app/utils.js via this.$ssrContext)
|
|
||||||
ssrContext.asyncData = {}
|
|
||||||
|
|
||||||
const beforeRender = async () => {
|
|
||||||
// Call beforeNuxtRender() methods
|
|
||||||
await Promise.all(ssrContext.beforeRenderFns.map(fn => promisify(fn, { Components, nuxtState: ssrContext.nuxt })))
|
|
||||||
|
|
||||||
ssrContext.rendered = () => {
|
|
||||||
// Add the state from the vuex store
|
|
||||||
ssrContext.nuxt.state = store.state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const renderErrorPage = async () => {
|
|
||||||
// Don't server-render the page in static target
|
|
||||||
if (ssrContext.target === 'static') {
|
|
||||||
ssrContext.nuxt.serverRendered = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load layout for error page
|
|
||||||
const layout = (NuxtError.options || NuxtError).layout
|
|
||||||
const errLayout = typeof layout === 'function' ? layout.call(NuxtError, app.context) : layout
|
|
||||||
ssrContext.nuxt.layout = errLayout || 'default'
|
|
||||||
await _app.loadLayout(errLayout)
|
|
||||||
_app.setLayout(errLayout)
|
|
||||||
|
|
||||||
await beforeRender()
|
|
||||||
return _app
|
|
||||||
}
|
|
||||||
const render404Page = () => {
|
|
||||||
app.context.error({ statusCode: 404, path: ssrContext.url, message: 'This page could not be found' })
|
|
||||||
return renderErrorPage()
|
|
||||||
}
|
|
||||||
|
|
||||||
const s = Date.now()
|
|
||||||
|
|
||||||
// Components are already resolved by setContext -> getRouteData (app/utils.js)
|
|
||||||
const Components = getMatchedComponents(app.context.route)
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Dispatch store nuxtServerInit
|
|
||||||
*/
|
|
||||||
if (store._actions && store._actions.nuxtServerInit) {
|
|
||||||
try {
|
|
||||||
await store.dispatch('nuxtServerInit', app.context)
|
|
||||||
} catch (err) {
|
|
||||||
console.debug('Error occurred when calling nuxtServerInit: ', err.message)
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ...If there is a redirect or an error, stop the process
|
|
||||||
if (ssrContext.redirected) {
|
|
||||||
return noopApp()
|
|
||||||
}
|
|
||||||
if (ssrContext.nuxt.error) {
|
|
||||||
return renderErrorPage()
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Call global middleware (nuxt.config.js)
|
|
||||||
*/
|
|
||||||
let midd = []
|
|
||||||
midd = midd.map((name) => {
|
|
||||||
if (typeof name === 'function') {
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
if (typeof middleware[name] !== 'function') {
|
|
||||||
app.context.error({ statusCode: 500, message: 'Unknown middleware ' + name })
|
|
||||||
}
|
|
||||||
return middleware[name]
|
|
||||||
})
|
|
||||||
await middlewareSeries(midd, app.context)
|
|
||||||
// ...If there is a redirect or an error, stop the process
|
|
||||||
if (ssrContext.redirected) {
|
|
||||||
return noopApp()
|
|
||||||
}
|
|
||||||
if (ssrContext.nuxt.error) {
|
|
||||||
return renderErrorPage()
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Set layout
|
|
||||||
*/
|
|
||||||
let layout = Components.length ? Components[0].options.layout : NuxtError.layout
|
|
||||||
if (typeof layout === 'function') {
|
|
||||||
layout = layout(app.context)
|
|
||||||
}
|
|
||||||
await _app.loadLayout(layout)
|
|
||||||
if (ssrContext.nuxt.error) {
|
|
||||||
return renderErrorPage()
|
|
||||||
}
|
|
||||||
layout = _app.setLayout(layout)
|
|
||||||
ssrContext.nuxt.layout = _app.layoutName
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Call middleware (layout + pages)
|
|
||||||
*/
|
|
||||||
midd = []
|
|
||||||
|
|
||||||
layout = sanitizeComponent(layout)
|
|
||||||
if (layout.options.middleware) {
|
|
||||||
midd = midd.concat(layout.options.middleware)
|
|
||||||
}
|
|
||||||
|
|
||||||
Components.forEach((Component) => {
|
|
||||||
if (Component.options.middleware) {
|
|
||||||
midd = midd.concat(Component.options.middleware)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
midd = midd.map((name) => {
|
|
||||||
if (typeof name === 'function') {
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
if (typeof middleware[name] !== 'function') {
|
|
||||||
app.context.error({ statusCode: 500, message: 'Unknown middleware ' + name })
|
|
||||||
}
|
|
||||||
return middleware[name]
|
|
||||||
})
|
|
||||||
await middlewareSeries(midd, app.context)
|
|
||||||
// ...If there is a redirect or an error, stop the process
|
|
||||||
if (ssrContext.redirected) {
|
|
||||||
return noopApp()
|
|
||||||
}
|
|
||||||
if (ssrContext.nuxt.error) {
|
|
||||||
return renderErrorPage()
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Call .validate()
|
|
||||||
*/
|
|
||||||
let isValid = true
|
|
||||||
try {
|
|
||||||
for (const Component of Components) {
|
|
||||||
if (typeof Component.options.validate !== 'function') {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
isValid = await Component.options.validate(app.context)
|
|
||||||
|
|
||||||
if (!isValid) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (validationError) {
|
|
||||||
// ...If .validate() threw an error
|
|
||||||
app.context.error({
|
|
||||||
statusCode: validationError.statusCode || '500',
|
|
||||||
message: validationError.message
|
|
||||||
})
|
|
||||||
return renderErrorPage()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...If .validate() returned false
|
|
||||||
if (!isValid) {
|
|
||||||
// Render a 404 error page
|
|
||||||
return render404Page()
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no Components found, returns 404
|
|
||||||
if (!Components.length) {
|
|
||||||
return render404Page()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call asyncData & fetch hooks on components matched by the route.
|
|
||||||
const asyncDatas = await Promise.all(Components.map((Component) => {
|
|
||||||
const promises = []
|
|
||||||
|
|
||||||
// Call asyncData(context)
|
|
||||||
if (Component.options.asyncData && typeof Component.options.asyncData === 'function') {
|
|
||||||
const promise = promisify(Component.options.asyncData, app.context)
|
|
||||||
promise.then((asyncDataResult) => {
|
|
||||||
ssrContext.asyncData[Component.cid] = asyncDataResult
|
|
||||||
applyAsyncData(Component)
|
|
||||||
return asyncDataResult
|
|
||||||
})
|
|
||||||
promises.push(promise)
|
|
||||||
} else {
|
|
||||||
promises.push(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call fetch(context)
|
|
||||||
if (Component.options.fetch && Component.options.fetch.length) {
|
|
||||||
promises.push(Component.options.fetch(app.context))
|
|
||||||
} else {
|
|
||||||
promises.push(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.all(promises)
|
|
||||||
}))
|
|
||||||
|
|
||||||
if (process.env.DEBUG && asyncDatas.length) console.debug('Data fetching ' + ssrContext.url + ': ' + (Date.now() - s) + 'ms')
|
|
||||||
|
|
||||||
// datas are the first row of each
|
|
||||||
ssrContext.nuxt.data = asyncDatas.map(r => r[0] || {})
|
|
||||||
|
|
||||||
// ...If there is a redirect or an error, stop the process
|
|
||||||
if (ssrContext.redirected) {
|
|
||||||
return noopApp()
|
|
||||||
}
|
|
||||||
if (ssrContext.nuxt.error) {
|
|
||||||
return renderErrorPage()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call beforeNuxtRender methods & add store state
|
|
||||||
await beforeRender()
|
|
||||||
|
|
||||||
return _app
|
|
||||||
}
|
|
@ -1,148 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
import Vuex from 'vuex'
|
|
||||||
|
|
||||||
Vue.use(Vuex)
|
|
||||||
|
|
||||||
const VUEX_PROPERTIES = ['state', 'getters', 'actions', 'mutations']
|
|
||||||
|
|
||||||
let store = {};
|
|
||||||
|
|
||||||
(function updateModules () {
|
|
||||||
store = normalizeRoot(require('..\\store\\index.js'), 'store/index.js')
|
|
||||||
|
|
||||||
// If store is an exported method = classic mode (deprecated)
|
|
||||||
|
|
||||||
if (typeof store === 'function') {
|
|
||||||
return console.warn('Classic mode for store/ is deprecated and will be removed in Nuxt 3.')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enforce store modules
|
|
||||||
store.modules = store.modules || {}
|
|
||||||
|
|
||||||
resolveStoreModules(require('..\\store\\audiobooks.js'), 'audiobooks.js')
|
|
||||||
resolveStoreModules(require('..\\store\\settings.js'), 'settings.js')
|
|
||||||
|
|
||||||
// If the environment supports hot reloading...
|
|
||||||
|
|
||||||
if (process.client && module.hot) {
|
|
||||||
// Whenever any Vuex module is updated...
|
|
||||||
module.hot.accept([
|
|
||||||
'..\\store\\audiobooks.js',
|
|
||||||
'..\\store\\index.js',
|
|
||||||
'..\\store\\settings.js',
|
|
||||||
], () => {
|
|
||||||
// Update `root.modules` with the latest definitions.
|
|
||||||
updateModules()
|
|
||||||
// Trigger a hot update in the store.
|
|
||||||
window.$nuxt.$store.hotUpdate(store)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
|
|
||||||
// createStore
|
|
||||||
export const createStore = store instanceof Function ? store : () => {
|
|
||||||
return new Vuex.Store(Object.assign({
|
|
||||||
strict: (process.env.NODE_ENV !== 'production')
|
|
||||||
}, store))
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeRoot (moduleData, filePath) {
|
|
||||||
moduleData = moduleData.default || moduleData
|
|
||||||
|
|
||||||
if (moduleData.commit) {
|
|
||||||
throw new Error(`[nuxt] ${filePath} should export a method that returns a Vuex instance.`)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof moduleData !== 'function') {
|
|
||||||
// Avoid TypeError: setting a property that has only a getter when overwriting top level keys
|
|
||||||
moduleData = Object.assign({}, moduleData)
|
|
||||||
}
|
|
||||||
return normalizeModule(moduleData, filePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeModule (moduleData, filePath) {
|
|
||||||
if (moduleData.state && typeof moduleData.state !== 'function') {
|
|
||||||
console.warn(`'state' should be a method that returns an object in ${filePath}`)
|
|
||||||
|
|
||||||
const state = Object.assign({}, moduleData.state)
|
|
||||||
// Avoid TypeError: setting a property that has only a getter when overwriting top level keys
|
|
||||||
moduleData = Object.assign({}, moduleData, { state: () => state })
|
|
||||||
}
|
|
||||||
return moduleData
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveStoreModules (moduleData, filename) {
|
|
||||||
moduleData = moduleData.default || moduleData
|
|
||||||
// Remove store src + extension (./foo/index.js -> foo/index)
|
|
||||||
const namespace = filename.replace(/\.(js|mjs)$/, '')
|
|
||||||
const namespaces = namespace.split('/')
|
|
||||||
let moduleName = namespaces[namespaces.length - 1]
|
|
||||||
const filePath = `store/${filename}`
|
|
||||||
|
|
||||||
moduleData = moduleName === 'state'
|
|
||||||
? normalizeState(moduleData, filePath)
|
|
||||||
: normalizeModule(moduleData, filePath)
|
|
||||||
|
|
||||||
// If src is a known Vuex property
|
|
||||||
if (VUEX_PROPERTIES.includes(moduleName)) {
|
|
||||||
const property = moduleName
|
|
||||||
const propertyStoreModule = getStoreModule(store, namespaces, { isProperty: true })
|
|
||||||
|
|
||||||
// Replace state since it's a function
|
|
||||||
mergeProperty(propertyStoreModule, moduleData, property)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// If file is foo/index.js, it should be saved as foo
|
|
||||||
const isIndexModule = (moduleName === 'index')
|
|
||||||
if (isIndexModule) {
|
|
||||||
namespaces.pop()
|
|
||||||
moduleName = namespaces[namespaces.length - 1]
|
|
||||||
}
|
|
||||||
|
|
||||||
const storeModule = getStoreModule(store, namespaces)
|
|
||||||
|
|
||||||
for (const property of VUEX_PROPERTIES) {
|
|
||||||
mergeProperty(storeModule, moduleData[property], property)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (moduleData.namespaced === false) {
|
|
||||||
delete storeModule.namespaced
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeState (moduleData, filePath) {
|
|
||||||
if (typeof moduleData !== 'function') {
|
|
||||||
console.warn(`${filePath} should export a method that returns an object`)
|
|
||||||
const state = Object.assign({}, moduleData)
|
|
||||||
return () => state
|
|
||||||
}
|
|
||||||
return normalizeModule(moduleData, filePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStoreModule (storeModule, namespaces, { isProperty = false } = {}) {
|
|
||||||
// If ./mutations.js
|
|
||||||
if (!namespaces.length || (isProperty && namespaces.length === 1)) {
|
|
||||||
return storeModule
|
|
||||||
}
|
|
||||||
|
|
||||||
const namespace = namespaces.shift()
|
|
||||||
|
|
||||||
storeModule.modules[namespace] = storeModule.modules[namespace] || {}
|
|
||||||
storeModule.modules[namespace].namespaced = true
|
|
||||||
storeModule.modules[namespace].modules = storeModule.modules[namespace].modules || {}
|
|
||||||
|
|
||||||
return getStoreModule(storeModule.modules[namespace], namespaces, { isProperty })
|
|
||||||
}
|
|
||||||
|
|
||||||
function mergeProperty (storeModule, moduleData, property) {
|
|
||||||
if (!moduleData) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (property === 'state') {
|
|
||||||
storeModule.state = moduleData || storeModule.state
|
|
||||||
} else {
|
|
||||||
storeModule[property] = Object.assign({}, storeModule[property], moduleData)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,630 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
import { isSamePath as _isSamePath, joinURL, normalizeURL, withQuery, withoutTrailingSlash } from 'ufo'
|
|
||||||
|
|
||||||
// window.{{globals.loadedCallback}} hook
|
|
||||||
// Useful for jsdom testing or plugins (https://github.com/tmpvar/jsdom#dealing-with-asynchronous-script-loading)
|
|
||||||
if (process.client) {
|
|
||||||
window.onNuxtReadyCbs = []
|
|
||||||
window.onNuxtReady = (cb) => {
|
|
||||||
window.onNuxtReadyCbs.push(cb)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createGetCounter (counterObject, defaultKey = '') {
|
|
||||||
return function getCounter (id = defaultKey) {
|
|
||||||
if (counterObject[id] === undefined) {
|
|
||||||
counterObject[id] = 0
|
|
||||||
}
|
|
||||||
return counterObject[id]++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function empty () {}
|
|
||||||
|
|
||||||
export function globalHandleError (error) {
|
|
||||||
if (Vue.config.errorHandler) {
|
|
||||||
Vue.config.errorHandler(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function interopDefault (promise) {
|
|
||||||
return promise.then(m => m.default || m)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function hasFetch(vm) {
|
|
||||||
return vm.$options && typeof vm.$options.fetch === 'function' && !vm.$options.fetch.length
|
|
||||||
}
|
|
||||||
export function purifyData(data) {
|
|
||||||
if (process.env.NODE_ENV === 'production') {
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
return Object.entries(data).filter(
|
|
||||||
([key, value]) => {
|
|
||||||
const valid = !(value instanceof Function) && !(value instanceof Promise)
|
|
||||||
if (!valid) {
|
|
||||||
console.warn(`${key} is not able to be stringified. This will break in a production environment.`)
|
|
||||||
}
|
|
||||||
return valid
|
|
||||||
}
|
|
||||||
).reduce((obj, [key, value]) => {
|
|
||||||
obj[key] = value
|
|
||||||
return obj
|
|
||||||
}, {})
|
|
||||||
}
|
|
||||||
export function getChildrenComponentInstancesUsingFetch(vm, instances = []) {
|
|
||||||
const children = vm.$children || []
|
|
||||||
for (const child of children) {
|
|
||||||
if (child.$fetch) {
|
|
||||||
instances.push(child)
|
|
||||||
continue; // Don't get the children since it will reload the template
|
|
||||||
}
|
|
||||||
if (child.$children) {
|
|
||||||
getChildrenComponentInstancesUsingFetch(child, instances)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return instances
|
|
||||||
}
|
|
||||||
|
|
||||||
export function applyAsyncData (Component, asyncData) {
|
|
||||||
if (
|
|
||||||
// For SSR, we once all this function without second param to just apply asyncData
|
|
||||||
// Prevent doing this for each SSR request
|
|
||||||
!asyncData && Component.options.__hasNuxtData
|
|
||||||
) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const ComponentData = Component.options._originDataFn || Component.options.data || function () { return {} }
|
|
||||||
Component.options._originDataFn = ComponentData
|
|
||||||
|
|
||||||
Component.options.data = function () {
|
|
||||||
const data = ComponentData.call(this, this)
|
|
||||||
if (this.$ssrContext) {
|
|
||||||
asyncData = this.$ssrContext.asyncData[Component.cid]
|
|
||||||
}
|
|
||||||
return { ...data, ...asyncData }
|
|
||||||
}
|
|
||||||
|
|
||||||
Component.options.__hasNuxtData = true
|
|
||||||
|
|
||||||
if (Component._Ctor && Component._Ctor.options) {
|
|
||||||
Component._Ctor.options.data = Component.options.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function sanitizeComponent (Component) {
|
|
||||||
// If Component already sanitized
|
|
||||||
if (Component.options && Component._Ctor === Component) {
|
|
||||||
return Component
|
|
||||||
}
|
|
||||||
if (!Component.options) {
|
|
||||||
Component = Vue.extend(Component) // fix issue #6
|
|
||||||
Component._Ctor = Component
|
|
||||||
} else {
|
|
||||||
Component._Ctor = Component
|
|
||||||
Component.extendOptions = Component.options
|
|
||||||
}
|
|
||||||
// If no component name defined, set file path as name, (also fixes #5703)
|
|
||||||
if (!Component.options.name && Component.options.__file) {
|
|
||||||
Component.options.name = Component.options.__file
|
|
||||||
}
|
|
||||||
return Component
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getMatchedComponents (route, matches = false, prop = 'components') {
|
|
||||||
return Array.prototype.concat.apply([], route.matched.map((m, index) => {
|
|
||||||
return Object.keys(m[prop]).map((key) => {
|
|
||||||
matches && matches.push(index)
|
|
||||||
return m[prop][key]
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getMatchedComponentsInstances (route, matches = false) {
|
|
||||||
return getMatchedComponents(route, matches, 'instances')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function flatMapComponents (route, fn) {
|
|
||||||
return Array.prototype.concat.apply([], route.matched.map((m, index) => {
|
|
||||||
return Object.keys(m.components).reduce((promises, key) => {
|
|
||||||
if (m.components[key]) {
|
|
||||||
promises.push(fn(m.components[key], m.instances[key], m, key, index))
|
|
||||||
} else {
|
|
||||||
delete m.components[key]
|
|
||||||
}
|
|
||||||
return promises
|
|
||||||
}, [])
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resolveRouteComponents (route, fn) {
|
|
||||||
return Promise.all(
|
|
||||||
flatMapComponents(route, async (Component, instance, match, key) => {
|
|
||||||
// If component is a function, resolve it
|
|
||||||
if (typeof Component === 'function' && !Component.options) {
|
|
||||||
try {
|
|
||||||
Component = await Component()
|
|
||||||
} catch (error) {
|
|
||||||
// Handle webpack chunk loading errors
|
|
||||||
// This may be due to a new deployment or a network problem
|
|
||||||
if (
|
|
||||||
error &&
|
|
||||||
error.name === 'ChunkLoadError' &&
|
|
||||||
typeof window !== 'undefined' &&
|
|
||||||
window.sessionStorage
|
|
||||||
) {
|
|
||||||
const timeNow = Date.now()
|
|
||||||
const previousReloadTime = parseInt(window.sessionStorage.getItem('nuxt-reload'))
|
|
||||||
|
|
||||||
// check for previous reload time not to reload infinitely
|
|
||||||
if (!previousReloadTime || previousReloadTime + 60000 < timeNow) {
|
|
||||||
window.sessionStorage.setItem('nuxt-reload', timeNow)
|
|
||||||
window.location.reload(true /* skip cache */)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match.components[key] = Component = sanitizeComponent(Component)
|
|
||||||
return typeof fn === 'function' ? fn(Component, instance, match, key) : Component
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getRouteData (route) {
|
|
||||||
if (!route) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Make sure the components are resolved (code-splitting)
|
|
||||||
await resolveRouteComponents(route)
|
|
||||||
// Send back a copy of route with meta based on Component definition
|
|
||||||
return {
|
|
||||||
...route,
|
|
||||||
meta: getMatchedComponents(route).map((Component, index) => {
|
|
||||||
return { ...Component.options.meta, ...(route.matched[index] || {}).meta }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function setContext (app, context) {
|
|
||||||
// If context not defined, create it
|
|
||||||
if (!app.context) {
|
|
||||||
app.context = {
|
|
||||||
isStatic: process.static,
|
|
||||||
isDev: true,
|
|
||||||
isHMR: false,
|
|
||||||
app,
|
|
||||||
store: app.store,
|
|
||||||
payload: context.payload,
|
|
||||||
error: context.error,
|
|
||||||
base: app.router.options.base,
|
|
||||||
env: {"serverUrl":"http://localhost:3333","baseUrl":"http://0.0.0.0"}
|
|
||||||
}
|
|
||||||
// Only set once
|
|
||||||
|
|
||||||
if (context.req) {
|
|
||||||
app.context.req = context.req
|
|
||||||
}
|
|
||||||
if (context.res) {
|
|
||||||
app.context.res = context.res
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.ssrContext) {
|
|
||||||
app.context.ssrContext = context.ssrContext
|
|
||||||
}
|
|
||||||
app.context.redirect = (status, path, query) => {
|
|
||||||
if (!status) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
app.context._redirected = true
|
|
||||||
// if only 1 or 2 arguments: redirect('/') or redirect('/', { foo: 'bar' })
|
|
||||||
let pathType = typeof path
|
|
||||||
if (typeof status !== 'number' && (pathType === 'undefined' || pathType === 'object')) {
|
|
||||||
query = path || {}
|
|
||||||
path = status
|
|
||||||
pathType = typeof path
|
|
||||||
status = 302
|
|
||||||
}
|
|
||||||
if (pathType === 'object') {
|
|
||||||
path = app.router.resolve(path).route.fullPath
|
|
||||||
}
|
|
||||||
// "/absolute/route", "./relative/route" or "../relative/route"
|
|
||||||
if (/(^[.]{1,2}\/)|(^\/(?!\/))/.test(path)) {
|
|
||||||
app.context.next({
|
|
||||||
path,
|
|
||||||
query,
|
|
||||||
status
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
path = withQuery(path, query)
|
|
||||||
if (process.server) {
|
|
||||||
app.context.next({
|
|
||||||
path,
|
|
||||||
status
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (process.client) {
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/Location/replace
|
|
||||||
window.location.replace(path)
|
|
||||||
|
|
||||||
// Throw a redirect error
|
|
||||||
throw new Error('ERR_REDIRECT')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (process.server) {
|
|
||||||
app.context.beforeNuxtRender = fn => context.beforeRenderFns.push(fn)
|
|
||||||
}
|
|
||||||
if (process.client) {
|
|
||||||
app.context.nuxtState = window.__NUXT__
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dynamic keys
|
|
||||||
const [currentRouteData, fromRouteData] = await Promise.all([
|
|
||||||
getRouteData(context.route),
|
|
||||||
getRouteData(context.from)
|
|
||||||
])
|
|
||||||
|
|
||||||
if (context.route) {
|
|
||||||
app.context.route = currentRouteData
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.from) {
|
|
||||||
app.context.from = fromRouteData
|
|
||||||
}
|
|
||||||
|
|
||||||
app.context.next = context.next
|
|
||||||
app.context._redirected = false
|
|
||||||
app.context._errored = false
|
|
||||||
app.context.isHMR = Boolean(context.isHMR)
|
|
||||||
app.context.params = app.context.route.params || {}
|
|
||||||
app.context.query = app.context.route.query || {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function middlewareSeries (promises, appContext) {
|
|
||||||
if (!promises.length || appContext._redirected || appContext._errored) {
|
|
||||||
return Promise.resolve()
|
|
||||||
}
|
|
||||||
return promisify(promises[0], appContext)
|
|
||||||
.then(() => {
|
|
||||||
return middlewareSeries(promises.slice(1), appContext)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function promisify (fn, context) {
|
|
||||||
let promise
|
|
||||||
if (fn.length === 2) {
|
|
||||||
console.warn('Callback-based asyncData, fetch or middleware calls are deprecated. ' +
|
|
||||||
'Please switch to promises or async/await syntax')
|
|
||||||
|
|
||||||
// fn(context, callback)
|
|
||||||
promise = new Promise((resolve) => {
|
|
||||||
fn(context, function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
context.error(err)
|
|
||||||
}
|
|
||||||
data = data || {}
|
|
||||||
resolve(data)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
promise = fn(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (promise && promise instanceof Promise && typeof promise.then === 'function') {
|
|
||||||
return promise
|
|
||||||
}
|
|
||||||
return Promise.resolve(promise)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Imported from vue-router
|
|
||||||
export function getLocation (base, mode) {
|
|
||||||
if (mode === 'hash') {
|
|
||||||
return window.location.hash.replace(/^#\//, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
base = decodeURI(base).slice(0, -1) // consideration is base is normalized with trailing slash
|
|
||||||
let path = decodeURI(window.location.pathname)
|
|
||||||
|
|
||||||
if (base && path.startsWith(base)) {
|
|
||||||
path = path.slice(base.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
const fullPath = (path || '/') + window.location.search + window.location.hash
|
|
||||||
|
|
||||||
return normalizeURL(fullPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Imported from path-to-regexp
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compile a string to a template function for the path.
|
|
||||||
*
|
|
||||||
* @param {string} str
|
|
||||||
* @param {Object=} options
|
|
||||||
* @return {!function(Object=, Object=)}
|
|
||||||
*/
|
|
||||||
export function compile (str, options) {
|
|
||||||
return tokensToFunction(parse(str, options), options)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getQueryDiff (toQuery, fromQuery) {
|
|
||||||
const diff = {}
|
|
||||||
const queries = { ...toQuery, ...fromQuery }
|
|
||||||
for (const k in queries) {
|
|
||||||
if (String(toQuery[k]) !== String(fromQuery[k])) {
|
|
||||||
diff[k] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return diff
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizeError (err) {
|
|
||||||
let message
|
|
||||||
if (!(err.message || typeof err === 'string')) {
|
|
||||||
try {
|
|
||||||
message = JSON.stringify(err, null, 2)
|
|
||||||
} catch (e) {
|
|
||||||
message = `[${err.constructor.name}]`
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
message = err.message || err
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...err,
|
|
||||||
message,
|
|
||||||
statusCode: (err.statusCode || err.status || (err.response && err.response.status) || 500)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main path matching regexp utility.
|
|
||||||
*
|
|
||||||
* @type {RegExp}
|
|
||||||
*/
|
|
||||||
const PATH_REGEXP = new RegExp([
|
|
||||||
// Match escaped characters that would otherwise appear in future matches.
|
|
||||||
// This allows the user to escape special characters that won't transform.
|
|
||||||
'(\\\\.)',
|
|
||||||
// Match Express-style parameters and un-named parameters with a prefix
|
|
||||||
// and optional suffixes. Matches appear as:
|
|
||||||
//
|
|
||||||
// "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined]
|
|
||||||
// "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined, undefined]
|
|
||||||
// "/*" => ["/", undefined, undefined, undefined, undefined, "*"]
|
|
||||||
'([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))'
|
|
||||||
].join('|'), 'g')
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a string for the raw tokens.
|
|
||||||
*
|
|
||||||
* @param {string} str
|
|
||||||
* @param {Object=} options
|
|
||||||
* @return {!Array}
|
|
||||||
*/
|
|
||||||
function parse (str, options) {
|
|
||||||
const tokens = []
|
|
||||||
let key = 0
|
|
||||||
let index = 0
|
|
||||||
let path = ''
|
|
||||||
const defaultDelimiter = (options && options.delimiter) || '/'
|
|
||||||
let res
|
|
||||||
|
|
||||||
while ((res = PATH_REGEXP.exec(str)) != null) {
|
|
||||||
const m = res[0]
|
|
||||||
const escaped = res[1]
|
|
||||||
const offset = res.index
|
|
||||||
path += str.slice(index, offset)
|
|
||||||
index = offset + m.length
|
|
||||||
|
|
||||||
// Ignore already escaped sequences.
|
|
||||||
if (escaped) {
|
|
||||||
path += escaped[1]
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const next = str[index]
|
|
||||||
const prefix = res[2]
|
|
||||||
const name = res[3]
|
|
||||||
const capture = res[4]
|
|
||||||
const group = res[5]
|
|
||||||
const modifier = res[6]
|
|
||||||
const asterisk = res[7]
|
|
||||||
|
|
||||||
// Push the current path onto the tokens.
|
|
||||||
if (path) {
|
|
||||||
tokens.push(path)
|
|
||||||
path = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
const partial = prefix != null && next != null && next !== prefix
|
|
||||||
const repeat = modifier === '+' || modifier === '*'
|
|
||||||
const optional = modifier === '?' || modifier === '*'
|
|
||||||
const delimiter = res[2] || defaultDelimiter
|
|
||||||
const pattern = capture || group
|
|
||||||
|
|
||||||
tokens.push({
|
|
||||||
name: name || key++,
|
|
||||||
prefix: prefix || '',
|
|
||||||
delimiter,
|
|
||||||
optional,
|
|
||||||
repeat,
|
|
||||||
partial,
|
|
||||||
asterisk: Boolean(asterisk),
|
|
||||||
pattern: pattern ? escapeGroup(pattern) : (asterisk ? '.*' : '[^' + escapeString(delimiter) + ']+?')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match any characters still remaining.
|
|
||||||
if (index < str.length) {
|
|
||||||
path += str.substr(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the path exists, push it onto the end.
|
|
||||||
if (path) {
|
|
||||||
tokens.push(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
return tokens
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prettier encoding of URI path segments.
|
|
||||||
*
|
|
||||||
* @param {string}
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
function encodeURIComponentPretty (str, slashAllowed) {
|
|
||||||
const re = slashAllowed ? /[?#]/g : /[/?#]/g
|
|
||||||
return encodeURI(str).replace(re, (c) => {
|
|
||||||
return '%' + c.charCodeAt(0).toString(16).toUpperCase()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode the asterisk parameter. Similar to `pretty`, but allows slashes.
|
|
||||||
*
|
|
||||||
* @param {string}
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
function encodeAsterisk (str) {
|
|
||||||
return encodeURIComponentPretty(str, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Escape a regular expression string.
|
|
||||||
*
|
|
||||||
* @param {string} str
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
function escapeString (str) {
|
|
||||||
return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, '\\$1')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Escape the capturing group by escaping special characters and meaning.
|
|
||||||
*
|
|
||||||
* @param {string} group
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
function escapeGroup (group) {
|
|
||||||
return group.replace(/([=!:$/()])/g, '\\$1')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Expose a method for transforming tokens into the path function.
|
|
||||||
*/
|
|
||||||
function tokensToFunction (tokens, options) {
|
|
||||||
// Compile all the tokens into regexps.
|
|
||||||
const matches = new Array(tokens.length)
|
|
||||||
|
|
||||||
// Compile all the patterns before compilation.
|
|
||||||
for (let i = 0; i < tokens.length; i++) {
|
|
||||||
if (typeof tokens[i] === 'object') {
|
|
||||||
matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$', flags(options))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return function (obj, opts) {
|
|
||||||
let path = ''
|
|
||||||
const data = obj || {}
|
|
||||||
const options = opts || {}
|
|
||||||
const encode = options.pretty ? encodeURIComponentPretty : encodeURIComponent
|
|
||||||
|
|
||||||
for (let i = 0; i < tokens.length; i++) {
|
|
||||||
const token = tokens[i]
|
|
||||||
|
|
||||||
if (typeof token === 'string') {
|
|
||||||
path += token
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
const value = data[token.name || 'pathMatch']
|
|
||||||
let segment
|
|
||||||
|
|
||||||
if (value == null) {
|
|
||||||
if (token.optional) {
|
|
||||||
// Prepend partial segment prefixes.
|
|
||||||
if (token.partial) {
|
|
||||||
path += token.prefix
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
throw new TypeError('Expected "' + token.name + '" to be defined')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(value)) {
|
|
||||||
if (!token.repeat) {
|
|
||||||
throw new TypeError('Expected "' + token.name + '" to not repeat, but received `' + JSON.stringify(value) + '`')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.length === 0) {
|
|
||||||
if (token.optional) {
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
throw new TypeError('Expected "' + token.name + '" to not be empty')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let j = 0; j < value.length; j++) {
|
|
||||||
segment = encode(value[j])
|
|
||||||
|
|
||||||
if (!matches[i].test(segment)) {
|
|
||||||
throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received `' + JSON.stringify(segment) + '`')
|
|
||||||
}
|
|
||||||
|
|
||||||
path += (j === 0 ? token.prefix : token.delimiter) + segment
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
segment = token.asterisk ? encodeAsterisk(value) : encode(value)
|
|
||||||
|
|
||||||
if (!matches[i].test(segment)) {
|
|
||||||
throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"')
|
|
||||||
}
|
|
||||||
|
|
||||||
path += token.prefix + segment
|
|
||||||
}
|
|
||||||
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the flags for a regexp from the options.
|
|
||||||
*
|
|
||||||
* @param {Object} options
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
function flags (options) {
|
|
||||||
return options && options.sensitive ? '' : 'i'
|
|
||||||
}
|
|
||||||
|
|
||||||
export function addLifecycleHook(vm, hook, fn) {
|
|
||||||
if (!vm.$options[hook]) {
|
|
||||||
vm.$options[hook] = []
|
|
||||||
}
|
|
||||||
if (!vm.$options[hook].includes(fn)) {
|
|
||||||
vm.$options[hook].push(fn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const urlJoin = joinURL
|
|
||||||
|
|
||||||
export const stripTrailingSlash = withoutTrailingSlash
|
|
||||||
|
|
||||||
export const isSamePath = _isSamePath
|
|
||||||
|
|
||||||
export function setScrollRestoration (newVal) {
|
|
||||||
try {
|
|
||||||
window.history.scrollRestoration = newVal;
|
|
||||||
} catch(e) {}
|
|
||||||
}
|
|
@ -1,86 +0,0 @@
|
|||||||
{
|
|
||||||
"AudioPlayer": {
|
|
||||||
"description": "Auto imported from components/AudioPlayer.vue"
|
|
||||||
},
|
|
||||||
"AppAppbar": {
|
|
||||||
"description": "Auto imported from components/app/Appbar.vue"
|
|
||||||
},
|
|
||||||
"AppBookShelf": {
|
|
||||||
"description": "Auto imported from components/app/BookShelf.vue"
|
|
||||||
},
|
|
||||||
"AppBookShelfToolbar": {
|
|
||||||
"description": "Auto imported from components/app/BookShelfToolbar.vue"
|
|
||||||
},
|
|
||||||
"AppStreamContainer": {
|
|
||||||
"description": "Auto imported from components/app/StreamContainer.vue"
|
|
||||||
},
|
|
||||||
"CardsBookCard": {
|
|
||||||
"description": "Auto imported from components/cards/BookCard.vue"
|
|
||||||
},
|
|
||||||
"CardsBookCover": {
|
|
||||||
"description": "Auto imported from components/cards/BookCover.vue"
|
|
||||||
},
|
|
||||||
"ControlsFilterSelect": {
|
|
||||||
"description": "Auto imported from components/controls/FilterSelect.vue"
|
|
||||||
},
|
|
||||||
"ControlsOrderSelect": {
|
|
||||||
"description": "Auto imported from components/controls/OrderSelect.vue"
|
|
||||||
},
|
|
||||||
"ControlsVolumeControl": {
|
|
||||||
"description": "Auto imported from components/controls/VolumeControl.vue"
|
|
||||||
},
|
|
||||||
"ModalsEditModal": {
|
|
||||||
"description": "Auto imported from components/modals/EditModal.vue"
|
|
||||||
},
|
|
||||||
"ModalsModal": {
|
|
||||||
"description": "Auto imported from components/modals/Modal.vue"
|
|
||||||
},
|
|
||||||
"TablesAudioFilesTable": {
|
|
||||||
"description": "Auto imported from components/tables/AudioFilesTable.vue"
|
|
||||||
},
|
|
||||||
"TablesOtherFilesTable": {
|
|
||||||
"description": "Auto imported from components/tables/OtherFilesTable.vue"
|
|
||||||
},
|
|
||||||
"TablesTracksTable": {
|
|
||||||
"description": "Auto imported from components/tables/TracksTable.vue"
|
|
||||||
},
|
|
||||||
"UiBtn": {
|
|
||||||
"description": "Auto imported from components/ui/Btn.vue"
|
|
||||||
},
|
|
||||||
"UiLoadingIndicator": {
|
|
||||||
"description": "Auto imported from components/ui/LoadingIndicator.vue"
|
|
||||||
},
|
|
||||||
"UiMenu": {
|
|
||||||
"description": "Auto imported from components/ui/Menu.vue"
|
|
||||||
},
|
|
||||||
"UiTextareaInput": {
|
|
||||||
"description": "Auto imported from components/ui/TextareaInput.vue"
|
|
||||||
},
|
|
||||||
"UiTextareaWithLabel": {
|
|
||||||
"description": "Auto imported from components/ui/TextareaWithLabel.vue"
|
|
||||||
},
|
|
||||||
"UiTextInput": {
|
|
||||||
"description": "Auto imported from components/ui/TextInput.vue"
|
|
||||||
},
|
|
||||||
"UiTextInputWithLabel": {
|
|
||||||
"description": "Auto imported from components/ui/TextInputWithLabel.vue"
|
|
||||||
},
|
|
||||||
"UiTooltip": {
|
|
||||||
"description": "Auto imported from components/ui/Tooltip.vue"
|
|
||||||
},
|
|
||||||
"WidgetsScanAlert": {
|
|
||||||
"description": "Auto imported from components/widgets/ScanAlert.vue"
|
|
||||||
},
|
|
||||||
"ModalsEditTabsCover": {
|
|
||||||
"description": "Auto imported from components/modals/edit-tabs/Cover.vue"
|
|
||||||
},
|
|
||||||
"ModalsEditTabsDetails": {
|
|
||||||
"description": "Auto imported from components/modals/edit-tabs/Details.vue"
|
|
||||||
},
|
|
||||||
"ModalsEditTabsMatch": {
|
|
||||||
"description": "Auto imported from components/modals/edit-tabs/Match.vue"
|
|
||||||
},
|
|
||||||
"ModalsEditTabsTracks": {
|
|
||||||
"description": "Auto imported from components/modals/edit-tabs/Tracks.vue"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html {{ HTML_ATTRS }}>
|
|
||||||
<head {{ HEAD_ATTRS }}>
|
|
||||||
{{ HEAD }}
|
|
||||||
</head>
|
|
||||||
<body {{ BODY_ATTRS }}>
|
|
||||||
{{ APP }}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,23 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Server error</title>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" name=viewport>
|
|
||||||
<style>
|
|
||||||
.__nuxt-error-page{padding: 1rem;background:#f7f8fb;color:#47494e;text-align:center;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;font-family:sans-serif;font-weight:100!important;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-font-smoothing:antialiased;position:absolute;top:0;left:0;right:0;bottom:0}.__nuxt-error-page .error{max-width:450px}.__nuxt-error-page .title{font-size:24px;font-size:1.5rem;margin-top:15px;color:#47494e;margin-bottom:8px}.__nuxt-error-page .description{color:#7f828b;line-height:21px;margin-bottom:10px}.__nuxt-error-page a{color:#7f828b!important;text-decoration:none}.__nuxt-error-page .logo{position:fixed;left:12px;bottom:12px}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="__nuxt-error-page">
|
|
||||||
<div class="error">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="90" height="90" fill="#DBE1EC" viewBox="0 0 48 48"><path d="M22 30h4v4h-4zm0-16h4v12h-4zm1.99-10C12.94 4 4 12.95 4 24s8.94 20 19.99 20S44 35.05 44 24 35.04 4 23.99 4zM24 40c-8.84 0-16-7.16-16-16S15.16 8 24 8s16 7.16 16 16-7.16 16-16 16z"/></svg>
|
|
||||||
<div class="title">Server error</div>
|
|
||||||
<div class="description">{{ message }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="logo">
|
|
||||||
<a href="https://nuxtjs.org" target="_blank" rel="noopener">Nuxt</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -43,7 +43,7 @@
|
|||||||
<div class="relative">
|
<div class="relative">
|
||||||
<!-- Track -->
|
<!-- Track -->
|
||||||
<div ref="track" class="w-full h-2 bg-gray-700 relative cursor-pointer transform duration-100 hover:scale-y-125 overflow-hidden" @mousemove="mousemoveTrack" @mouseleave="mouseleaveTrack" @click.stop="clickTrack">
|
<div ref="track" class="w-full h-2 bg-gray-700 relative cursor-pointer transform duration-100 hover:scale-y-125 overflow-hidden" @mousemove="mousemoveTrack" @mouseleave="mouseleaveTrack" @click.stop="clickTrack">
|
||||||
<div ref="readyTrack" class="h-full bg-gray-500 absolute top-0 left-0 pointer-events-none" />
|
<div ref="readyTrack" class="h-full bg-gray-600 absolute top-0 left-0 pointer-events-none" />
|
||||||
<div ref="bufferTrack" class="h-full bg-gray-400 absolute top-0 left-0 pointer-events-none" />
|
<div ref="bufferTrack" class="h-full bg-gray-400 absolute top-0 left-0 pointer-events-none" />
|
||||||
<div ref="playedTrack" class="h-full bg-gray-200 absolute top-0 left-0 pointer-events-none" />
|
<div ref="playedTrack" class="h-full bg-gray-200 absolute top-0 left-0 pointer-events-none" />
|
||||||
<div ref="trackCursor" class="h-full w-0.5 bg-gray-50 absolute top-0 left-0 opacity-0 pointer-events-none" />
|
<div ref="trackCursor" class="h-full w-0.5 bg-gray-50 absolute top-0 left-0 opacity-0 pointer-events-none" />
|
||||||
|
@ -11,9 +11,9 @@
|
|||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<ul v-show="showMenu" class="absolute z-10 mt-1 w-full bg-bg border border-black-200 shadow-lg max-h-56 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" tabindex="-1" role="listbox" aria-labelledby="listbox-label" aria-activedescendant="listbox-option-3">
|
<ul v-show="showMenu" class="absolute z-10 mt-1 w-full bg-bg border border-black-200 shadow-lg max-h-56 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" role="listbox" aria-labelledby="listbox-label">
|
||||||
<template v-for="item in items">
|
<template v-for="item in items">
|
||||||
<li :key="item.value" class="text-gray-50 select-none relative py-2 pr-9 cursor-pointer hover:bg-black-400" id="listbox-option-0" role="option" @click="clickedOption(item.value)">
|
<li :key="item.value" class="text-gray-50 select-none relative py-2 pr-9 cursor-pointer hover:bg-black-400" :class="item.value === selected ? 'bg-primary bg-opacity-50' : ''" role="option" @click="clickedOption(item.value)">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span class="font-normal ml-3 block truncate">{{ item.text }}</span>
|
<span class="font-normal ml-3 block truncate">{{ item.text }}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -68,6 +68,7 @@ export default {
|
|||||||
},
|
},
|
||||||
clickedOption(val) {
|
clickedOption(val) {
|
||||||
if (this.selected === val) {
|
if (this.selected === val) {
|
||||||
|
this.showMenu = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.selected = val
|
this.selected = val
|
||||||
|
@ -12,9 +12,9 @@
|
|||||||
</span> -->
|
</span> -->
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<ul v-show="showMenu" class="absolute z-10 mt-1 w-full bg-bg border border-black-200 shadow-lg max-h-56 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" tabindex="-1" role="listbox" aria-labelledby="listbox-label" aria-activedescendant="listbox-option-3">
|
<ul v-show="showMenu" class="absolute z-10 mt-1 w-full bg-bg border border-black-200 shadow-lg max-h-56 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" role="listbox" aria-labelledby="listbox-label">
|
||||||
<template v-for="item in items">
|
<template v-for="item in items">
|
||||||
<li :key="item.value" class="text-gray-50 select-none relative py-2 pr-9 cursor-pointer hover:bg-black-400" id="listbox-option-0" role="option" @click="clickedOption(item.value)">
|
<li :key="item.value" class="text-gray-50 select-none relative py-2 pr-9 cursor-pointer hover:bg-black-400" :class="item.value === selected ? 'bg-primary bg-opacity-50' : ''" role="option" @click="clickedOption(item.value)">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span class="font-normal ml-3 block truncate">{{ item.text }}</span>
|
<span class="font-normal ml-3 block truncate">{{ item.text }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -6,12 +6,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="absolute -top-10 left-0 w-full flex">
|
<div class="absolute -top-10 left-0 w-full flex">
|
||||||
<div class="h-10 w-28 rounded-t-lg flex items-center justify-center mr-1 cursor-pointer hover:bg-bg font-book border-t border-l border-r border-black-300" :class="selectedTab === 'details' ? 'bg-bg' : 'bg-primary text-gray-400'" @click="selectTab('details')">Details</div>
|
<div class="w-28 rounded-t-lg flex items-center justify-center mr-1 cursor-pointer hover:bg-bg font-book border-t border-l border-r border-black-300 tab" :class="selectedTab === 'details' ? 'tab-selected bg-bg pb-px' : 'bg-primary text-gray-400'" @click="selectTab('details')">Details</div>
|
||||||
<div class="h-10 w-28 rounded-t-lg flex items-center justify-center mr-1 cursor-pointer hover:bg-bg font-book border-t border-l border-r border-black-300" :class="selectedTab === 'cover' ? 'bg-bg' : 'bg-primary text-gray-400'" @click="selectTab('cover')">Cover</div>
|
<div class="w-28 rounded-t-lg flex items-center justify-center mr-1 cursor-pointer hover:bg-bg font-book border-t border-l border-r border-black-300 tab" :class="selectedTab === 'cover' ? 'tab-selected bg-bg pb-px' : 'bg-primary text-gray-400'" @click="selectTab('cover')">Cover</div>
|
||||||
<div class="h-10 w-28 rounded-t-lg flex items-center justify-center mr-1 cursor-pointer hover:bg-bg font-book border-t border-l border-r border-black-300" :class="selectedTab === 'match' ? 'bg-bg' : 'bg-primary text-gray-400'" @click="selectTab('match')">Match</div>
|
<div class="w-28 rounded-t-lg flex items-center justify-center mr-1 cursor-pointer hover:bg-bg font-book border-t border-l border-r border-black-300 tab" :class="selectedTab === 'match' ? 'tab-selected bg-bg pb-px' : 'bg-primary text-gray-400'" @click="selectTab('match')">Match</div>
|
||||||
<div class="h-10 w-28 rounded-t-lg flex items-center justify-center cursor-pointer hover:bg-bg font-book border-t border-l border-r border-black-300" :class="selectedTab === 'tracks' ? 'bg-bg' : 'bg-primary text-gray-400'" @click="selectTab('tracks')">Tracks</div>
|
<div class="w-28 rounded-t-lg flex items-center justify-center cursor-pointer hover:bg-bg font-book border-t border-l border-r border-black-300 tab" :class="selectedTab === 'tracks' ? 'tab-selected bg-bg pb-px' : 'bg-primary text-gray-400'" @click="selectTab('tracks')">Tracks</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-4 w-full h-full text-sm py-6 rounded-b-lg rounded-tr-lg bg-bg shadow-lg">
|
<div class="px-4 w-full h-full text-sm py-6 rounded-b-lg rounded-tr-lg bg-bg shadow-lg border border-black-300">
|
||||||
<keep-alive>
|
<keep-alive>
|
||||||
<component v-if="audiobook" :is="tabName" :audiobook="audiobook" :processing.sync="processing" @close="show = false" />
|
<component v-if="audiobook" :is="tabName" :audiobook="audiobook" :processing.sync="processing" @close="show = false" />
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
@ -83,4 +83,13 @@ export default {
|
|||||||
},
|
},
|
||||||
mounted() {}
|
mounted() {}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.tab {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
.tab.tab-selected {
|
||||||
|
height: 41px;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div ref="wrapper" class="modal-bg w-full h-full fixed top-0 left-0 bg-primary bg-opacity-50 flex items-center justify-center z-20 opacity-0">
|
<div ref="wrapper" class="modal modal-bg w-full h-full fixed top-0 left-0 bg-primary bg-opacity-50 flex items-center justify-center z-20 opacity-0">
|
||||||
<div class="absolute top-0 left-0 right-0 w-full h-36 bg-gradient-to-t from-transparent via-black-500 to-black-700 opacity-90 pointer-events-none" />
|
<div class="absolute top-0 left-0 right-0 w-full h-36 bg-gradient-to-t from-transparent via-black-500 to-black-700 opacity-90 pointer-events-none" />
|
||||||
|
|
||||||
<div class="absolute top-5 right-5 h-12 w-12 flex items-center justify-center cursor-pointer text-white hover:text-gray-300" @click="show = false">
|
<div class="absolute top-5 right-5 h-12 w-12 flex items-center justify-center cursor-pointer text-white hover:text-gray-300" @click="show = false">
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="w-full h-full overflow-hidden overflow-y-auto px-1">
|
<div class="w-full h-full overflow-hidden overflow-y-auto px-1">
|
||||||
<div v-if="userProgress" class="bg-primary rounded-md w-full px-4 py-1 mb-4 border border-success border-opacity-50">
|
<div v-if="userProgress" class="bg-success bg-opacity-40 rounded-md w-full px-4 py-1 mb-4 border border-success border-opacity-50">
|
||||||
<div class="w-full flex items-center">
|
<div class="w-full flex items-center">
|
||||||
<p>
|
<p>
|
||||||
Your progress: <span class="font-mono text-lg">{{ (userProgress * 100).toFixed(0) }}%</span>
|
Your progress: <span class="font-mono text-lg">{{ (userProgress * 100).toFixed(0) }}%</span>
|
||||||
@ -12,12 +12,16 @@
|
|||||||
<form @submit.prevent="submitForm">
|
<form @submit.prevent="submitForm">
|
||||||
<ui-text-input-with-label v-model="details.title" label="Title" />
|
<ui-text-input-with-label v-model="details.title" label="Title" />
|
||||||
|
|
||||||
<ui-text-input-with-label v-model="details.author" label="Author" class="mt-4" />
|
<ui-text-input-with-label v-model="details.author" label="Author" class="mt-2" />
|
||||||
|
|
||||||
<ui-textarea-with-label v-model="details.description" :rows="6" label="Description" class="mt-4" />
|
<ui-text-input-with-label v-model="details.series" label="Series" class="mt-2" />
|
||||||
|
|
||||||
|
<ui-textarea-with-label v-model="details.description" :rows="3" label="Description" class="mt-2" />
|
||||||
|
|
||||||
|
<ui-multi-select v-model="details.genre" label="Genre" :items="genres" class="mt-2" @addOption="addGenre" />
|
||||||
|
|
||||||
<div class="flex py-4">
|
<div class="flex py-4">
|
||||||
<ui-btn color="error" small @click.stop.prevent="deleteAudiobook">Remove</ui-btn>
|
<ui-btn color="error" type="button" small @click.stop.prevent="deleteAudiobook">Remove</ui-btn>
|
||||||
<div class="flex-grow" />
|
<div class="flex-grow" />
|
||||||
<ui-btn type="submit">Submit</ui-btn>
|
<ui-btn type="submit">Submit</ui-btn>
|
||||||
</div>
|
</div>
|
||||||
@ -39,9 +43,12 @@ export default {
|
|||||||
details: {
|
details: {
|
||||||
title: null,
|
title: null,
|
||||||
description: null,
|
description: null,
|
||||||
author: null
|
author: null,
|
||||||
|
series: null,
|
||||||
|
genre: []
|
||||||
},
|
},
|
||||||
resettingProgress: false
|
resettingProgress: false,
|
||||||
|
genres: ['adventure', 'autobiography', 'biography', 'childrens', 'comedy', 'crime', 'dystopian', 'fantasy', 'fiction', 'health', 'history', 'horror', 'mystery', 'new_adult', 'nonfiction', 'philosophy', 'politics', 'religion', 'romance', 'sci-fi', 'self-help', 'short_story', 'technology', 'thriller', 'true_crime', 'western', 'young_adult']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@ -75,6 +82,12 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
addGenre(genre) {
|
||||||
|
this.genres.push({
|
||||||
|
text: genre,
|
||||||
|
value: genre
|
||||||
|
})
|
||||||
|
},
|
||||||
async submitForm() {
|
async submitForm() {
|
||||||
console.log('Submit form', this.details)
|
console.log('Submit form', this.details)
|
||||||
this.isProcessing = true
|
this.isProcessing = true
|
||||||
@ -96,6 +109,8 @@ export default {
|
|||||||
this.details.title = this.book.title
|
this.details.title = this.book.title
|
||||||
this.details.description = this.book.description
|
this.details.description = this.book.description
|
||||||
this.details.author = this.book.author
|
this.details.author = this.book.author
|
||||||
|
this.details.genre = this.book.genre || []
|
||||||
|
this.details.series = this.book.series
|
||||||
},
|
},
|
||||||
resetProgress() {
|
resetProgress() {
|
||||||
if (confirm(`Are you sure you want to reset your progress?`)) {
|
if (confirm(`Are you sure you want to reset your progress?`)) {
|
||||||
|
216
client/components/ui/MultiSelect.vue
Normal file
216
client/components/ui/MultiSelect.vue
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
<template>
|
||||||
|
<div class="w-full">
|
||||||
|
<p class="px-1 text-sm">{{ label }}</p>
|
||||||
|
<div ref="wrapper" class="relative">
|
||||||
|
<form @submit.prevent="submitForm">
|
||||||
|
<div ref="inputWrapper" style="min-height: 40px" class="flex-wrap relative w-full shadow-sm flex items-center bg-primary border border-gray-600 rounded-md px-2 py-1 cursor-text" @click.stop.prevent="clickWrapper" @mouseup.stop.prevent @mousedown.prevent>
|
||||||
|
<div v-for="item in selected" :key="item" class="rounded-full px-2 py-1 ma-0.5 text-xs bg-bg flex flex-nowrap whitespace-nowrap items-center">{{ snakeToNormal(item) }}</div>
|
||||||
|
<input ref="input" v-model="textInput" style="min-width: 40px; width: 40px" class="h-full bg-primary focus:outline-none px-1" @keydown="keydownInput" @focus="inputFocus" @blur="inputBlur" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<ul ref="menu" v-show="showMenu" class="absolute z-50 mt-1 w-full bg-bg border border-black-200 shadow-lg max-h-56 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" role="listbox" aria-labelledby="listbox-label">
|
||||||
|
<template v-for="item in itemsToShow">
|
||||||
|
<li :key="item" class="text-gray-50 select-none relative py-2 pr-9 cursor-pointer hover:bg-black-400" role="option" @click="clickedOption($event, item)" @mouseup.stop.prevent @mousedown.prevent>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="font-normal ml-3 block truncate">{{ snakeToNormal(item) }}</span>
|
||||||
|
</div>
|
||||||
|
<span v-if="selected.includes(item)" class="text-yellow-400 absolute inset-y-0 right-0 flex items-center pr-4">
|
||||||
|
<span class="material-icons text-xl">checkmark</span>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
<li v-if="!itemsToShow.length" class="text-gray-50 select-none relative py-2 pr-9" role="option">
|
||||||
|
<div class="flex items-center justify-center">
|
||||||
|
<span class="font-normal">No items</span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
items: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
label: String
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
textInput: null,
|
||||||
|
currentSearch: null,
|
||||||
|
isTyping: false,
|
||||||
|
typingTimeout: null,
|
||||||
|
isFocused: false,
|
||||||
|
menu: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
selected: {
|
||||||
|
get() {
|
||||||
|
return this.value
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
this.$emit('input', val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showMenu() {
|
||||||
|
return this.isFocused
|
||||||
|
},
|
||||||
|
itemsToShow() {
|
||||||
|
if (!this.currentSearch || !this.textInput) {
|
||||||
|
return this.items
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.items.filter((i) => {
|
||||||
|
var normie = this.snakeToNormal(i)
|
||||||
|
var iValue = String(normie).toLowerCase()
|
||||||
|
return iValue.includes(this.currentSearch.toLowerCase())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
snakeToNormal(kebab) {
|
||||||
|
if (!kebab) {
|
||||||
|
return 'err'
|
||||||
|
}
|
||||||
|
return String(kebab)
|
||||||
|
.split('_')
|
||||||
|
.map((t) => t.slice(0, 1).toUpperCase() + t.slice(1))
|
||||||
|
.join(' ')
|
||||||
|
},
|
||||||
|
normalToSnake(normie) {
|
||||||
|
return normie
|
||||||
|
.trim()
|
||||||
|
.split(' ')
|
||||||
|
.map((t) => t.toLowerCase())
|
||||||
|
.join('_')
|
||||||
|
},
|
||||||
|
setMatchingItems() {
|
||||||
|
if (!this.textInput) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.currentSearch = this.textInput
|
||||||
|
// this.itemsToShow = this.items.filter((i) => {
|
||||||
|
// var iValue = String(i.value).toLowerCase()
|
||||||
|
// return iValue.includes(this.currentSearch.toLowerCase())
|
||||||
|
// })
|
||||||
|
},
|
||||||
|
keydownInput() {
|
||||||
|
clearTimeout(this.typingTimeout)
|
||||||
|
this.isTyping = true
|
||||||
|
this.typingTimeout = setTimeout(() => {
|
||||||
|
// this.setMatchingItems()
|
||||||
|
this.currentSearch = this.textInput
|
||||||
|
}, 100)
|
||||||
|
this.setInputWidth()
|
||||||
|
},
|
||||||
|
setInputWidth() {
|
||||||
|
setTimeout(() => {
|
||||||
|
var value = this.$refs.input.value
|
||||||
|
var len = value.length * 7 + 24
|
||||||
|
this.$refs.input.style.width = len + 'px'
|
||||||
|
this.recalcMenuPos()
|
||||||
|
}, 50)
|
||||||
|
},
|
||||||
|
recalcMenuPos() {
|
||||||
|
if (!this.menu) return
|
||||||
|
var boundingBox = this.$refs.inputWrapper.getBoundingClientRect()
|
||||||
|
this.menu.style.top = boundingBox.y + boundingBox.height - 4 + 'px'
|
||||||
|
this.menu.style.left = boundingBox.x + 'px'
|
||||||
|
this.menu.style.width = boundingBox.width + 'px'
|
||||||
|
},
|
||||||
|
unmountMountMenu() {
|
||||||
|
if (!this.$refs.menu) return
|
||||||
|
this.menu = this.$refs.menu
|
||||||
|
|
||||||
|
var boundingBox = this.$refs.inputWrapper.getBoundingClientRect()
|
||||||
|
this.menu.remove()
|
||||||
|
document.body.appendChild(this.menu)
|
||||||
|
this.menu.style.top = boundingBox.y + boundingBox.height - 4 + 'px'
|
||||||
|
this.menu.style.left = boundingBox.x + 'px'
|
||||||
|
this.menu.style.width = boundingBox.width + 'px'
|
||||||
|
},
|
||||||
|
inputFocus() {
|
||||||
|
if (!this.menu) {
|
||||||
|
this.unmountMountMenu()
|
||||||
|
}
|
||||||
|
this.isFocused = true
|
||||||
|
this.recalcMenuPos()
|
||||||
|
// this.$refs.input.style.width = '100px'
|
||||||
|
},
|
||||||
|
inputBlur() {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (document.activeElement === this.$refs.input) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.isFocused = false
|
||||||
|
}, 50)
|
||||||
|
},
|
||||||
|
focus() {
|
||||||
|
if (this.$refs.input) this.$refs.input.focus()
|
||||||
|
},
|
||||||
|
blur() {
|
||||||
|
if (this.$refs.input) this.$refs.input.blur()
|
||||||
|
},
|
||||||
|
clickedOption(e, itemValue) {
|
||||||
|
e.stopPropagation()
|
||||||
|
e.preventDefault()
|
||||||
|
if (this.$refs.input) this.$refs.input.focus()
|
||||||
|
// this.$nextTick(() => {
|
||||||
|
// this.$refs.input.focus()
|
||||||
|
// })
|
||||||
|
var newSelected = null
|
||||||
|
if (this.selected.includes(itemValue)) {
|
||||||
|
newSelected = this.selected.filter((s) => s !== itemValue)
|
||||||
|
} else {
|
||||||
|
newSelected = this.selected.concat([itemValue])
|
||||||
|
// this.blur()
|
||||||
|
}
|
||||||
|
this.textInput = null
|
||||||
|
this.currentSearch = null
|
||||||
|
this.$emit('input', newSelected)
|
||||||
|
},
|
||||||
|
clickWrapper() {
|
||||||
|
if (this.showMenu) {
|
||||||
|
return this.blur()
|
||||||
|
}
|
||||||
|
this.focus()
|
||||||
|
},
|
||||||
|
insertNewItem(item) {
|
||||||
|
var kebabItem = this.normalToSnake(item)
|
||||||
|
this.selected.push(kebabItem)
|
||||||
|
this.$emit('addOption', kebabItem)
|
||||||
|
this.$emit('input', this.selected)
|
||||||
|
this.textInput = null
|
||||||
|
this.currentSearch = null
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.blur()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
submitForm() {
|
||||||
|
if (!this.textInput) return
|
||||||
|
|
||||||
|
var cleaned = this.textInput.toLowerCase().trim()
|
||||||
|
var cleanedKebab = this.normalToSnake(cleaned)
|
||||||
|
var matchesItem = this.items.find((i) => {
|
||||||
|
return i === cleaned || cleanedKebab === i
|
||||||
|
})
|
||||||
|
if (matchesItem) {
|
||||||
|
this.clickedOption(matchesItem.value)
|
||||||
|
} else {
|
||||||
|
this.insertNewItem(this.textInput)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {}
|
||||||
|
}
|
||||||
|
</script>
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<p class="px-1">{{ label }}</p>
|
<p class="px-1 text-sm">{{ label }}</p>
|
||||||
<ui-text-input v-model="inputValue" :disabled="disabled" class="w-full" />
|
<ui-text-input v-model="inputValue" :disabled="disabled" class="w-full" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<p class="px-1">{{ label }}</p>
|
<p class="px-1 text-sm">{{ label }}</p>
|
||||||
<ui-textarea-input v-model="inputValue" :rows="rows" class="w-full" />
|
<ui-textarea-input v-model="inputValue" :rows="rows" class="w-full" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "audiobookshelf-client",
|
"name": "audiobookshelf-client",
|
||||||
"version": "0.9.5",
|
"version": "0.9.51",
|
||||||
"description": "Audiobook manager and player",
|
"description": "Audiobook manager and player",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "audiobookshelf",
|
"name": "audiobookshelf",
|
||||||
"version": "0.9.5",
|
"version": "0.9.51",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -3,11 +3,12 @@ class Book {
|
|||||||
this.olid = null
|
this.olid = null
|
||||||
this.title = null
|
this.title = null
|
||||||
this.author = null
|
this.author = null
|
||||||
|
this.series = null
|
||||||
this.publishYear = null
|
this.publishYear = null
|
||||||
this.publisher = null
|
this.publisher = null
|
||||||
this.description = null
|
this.description = null
|
||||||
this.cover = null
|
this.cover = null
|
||||||
this.genres = []
|
this.genre = []
|
||||||
|
|
||||||
if (book) {
|
if (book) {
|
||||||
this.construct(book)
|
this.construct(book)
|
||||||
@ -18,11 +19,12 @@ class Book {
|
|||||||
this.olid = book.olid
|
this.olid = book.olid
|
||||||
this.title = book.title
|
this.title = book.title
|
||||||
this.author = book.author
|
this.author = book.author
|
||||||
|
this.series = book.series
|
||||||
this.publishYear = book.publish_year
|
this.publishYear = book.publish_year
|
||||||
this.publisher = book.publisher
|
this.publisher = book.publisher
|
||||||
this.description = book.description
|
this.description = book.description
|
||||||
this.cover = book.cover
|
this.cover = book.cover
|
||||||
this.genres = book.genres
|
this.genre = book.genre
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSON() {
|
toJSON() {
|
||||||
@ -30,11 +32,12 @@ class Book {
|
|||||||
olid: this.olid,
|
olid: this.olid,
|
||||||
title: this.title,
|
title: this.title,
|
||||||
author: this.author,
|
author: this.author,
|
||||||
|
series: this.series,
|
||||||
publishYear: this.publish_year,
|
publishYear: this.publish_year,
|
||||||
publisher: this.publisher,
|
publisher: this.publisher,
|
||||||
description: this.description,
|
description: this.description,
|
||||||
cover: this.cover,
|
cover: this.cover,
|
||||||
genres: this.genres
|
genre: this.genre
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,10 +45,11 @@ class Book {
|
|||||||
this.olid = data.olid || null
|
this.olid = data.olid || null
|
||||||
this.title = data.title || null
|
this.title = data.title || null
|
||||||
this.author = data.author || null
|
this.author = data.author || null
|
||||||
|
this.series = data.series || null
|
||||||
this.publishYear = data.publish_year || null
|
this.publishYear = data.publish_year || null
|
||||||
this.description = data.description || null
|
this.description = data.description || null
|
||||||
this.cover = data.cover || null
|
this.cover = data.cover || null
|
||||||
this.genres = data.genres || []
|
this.genre = data.genre || []
|
||||||
}
|
}
|
||||||
|
|
||||||
update(payload) {
|
update(payload) {
|
||||||
@ -53,12 +57,12 @@ class Book {
|
|||||||
for (const key in payload) {
|
for (const key in payload) {
|
||||||
if (payload[key] === undefined) continue;
|
if (payload[key] === undefined) continue;
|
||||||
|
|
||||||
if (key === 'genres') {
|
if (key === 'genre') {
|
||||||
if (payload['genres'] === null && this.genres !== null) {
|
if (payload['genre'] === null && this.genre !== null) {
|
||||||
this.genres = []
|
this.genre = []
|
||||||
hasUpdates = true
|
hasUpdates = true
|
||||||
} else if (payload['genres'].join(',') !== this.genres.join(',')) {
|
} else if (payload['genre'].join(',') !== this.genre.join(',')) {
|
||||||
this.genres = payload['genres']
|
this.genre = payload['genre']
|
||||||
hasUpdates = true
|
hasUpdates = true
|
||||||
}
|
}
|
||||||
} else if (this[key] !== undefined && payload[key] !== this[key]) {
|
} else if (this[key] !== undefined && payload[key] !== this[key]) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user