mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-31 02:17:01 -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/ | ||||
| /audiobooks/ | ||||
| /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"> | ||||
|       <!-- 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="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="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" /> | ||||
|  | ||||
| @ -11,9 +11,9 @@ | ||||
|       </span> | ||||
|     </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"> | ||||
|         <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"> | ||||
|             <span class="font-normal ml-3 block truncate">{{ item.text }}</span> | ||||
|           </div> | ||||
| @ -68,6 +68,7 @@ export default { | ||||
|     }, | ||||
|     clickedOption(val) { | ||||
|       if (this.selected === val) { | ||||
|         this.showMenu = false | ||||
|         return | ||||
|       } | ||||
|       this.selected = val | ||||
|  | ||||
| @ -12,9 +12,9 @@ | ||||
|       </span> --> | ||||
|     </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"> | ||||
|         <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"> | ||||
|             <span class="font-normal ml-3 block truncate">{{ item.text }}</span> | ||||
|           </div> | ||||
|  | ||||
| @ -6,12 +6,12 @@ | ||||
|       </div> | ||||
|     </template> | ||||
|     <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="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="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="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 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="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="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="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 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> | ||||
|         <component v-if="audiobook" :is="tabName" :audiobook="audiobook" :processing.sync="processing" @close="show = false" /> | ||||
|       </keep-alive> | ||||
| @ -83,4 +83,13 @@ export default { | ||||
|   }, | ||||
|   mounted() {} | ||||
| } | ||||
| </script> | ||||
| </script> | ||||
| 
 | ||||
| <style> | ||||
| .tab { | ||||
|   height: 40px; | ||||
| } | ||||
| .tab.tab-selected { | ||||
|   height: 41px; | ||||
| } | ||||
| </style> | ||||
| @ -1,5 +1,5 @@ | ||||
| <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-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> | ||||
|   <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"> | ||||
|         <p> | ||||
|           Your progress: <span class="font-mono text-lg">{{ (userProgress * 100).toFixed(0) }}%</span> | ||||
| @ -12,12 +12,16 @@ | ||||
|     <form @submit.prevent="submitForm"> | ||||
|       <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"> | ||||
|         <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" /> | ||||
|         <ui-btn type="submit">Submit</ui-btn> | ||||
|       </div> | ||||
| @ -39,9 +43,12 @@ export default { | ||||
|       details: { | ||||
|         title: 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: { | ||||
| @ -75,6 +82,12 @@ export default { | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     addGenre(genre) { | ||||
|       this.genres.push({ | ||||
|         text: genre, | ||||
|         value: genre | ||||
|       }) | ||||
|     }, | ||||
|     async submitForm() { | ||||
|       console.log('Submit form', this.details) | ||||
|       this.isProcessing = true | ||||
| @ -96,6 +109,8 @@ export default { | ||||
|       this.details.title = this.book.title | ||||
|       this.details.description = this.book.description | ||||
|       this.details.author = this.book.author | ||||
|       this.details.genre = this.book.genre || [] | ||||
|       this.details.series = this.book.series | ||||
|     }, | ||||
|     resetProgress() { | ||||
|       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> | ||||
|   <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" /> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| <template> | ||||
|   <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" /> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "audiobookshelf-client", | ||||
|   "version": "0.9.5", | ||||
|   "version": "0.9.51", | ||||
|   "description": "Audiobook manager and player", | ||||
|   "main": "index.js", | ||||
|   "scripts": { | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "audiobookshelf", | ||||
|   "version": "0.9.5", | ||||
|   "version": "0.9.51", | ||||
|   "description": "", | ||||
|   "main": "index.js", | ||||
|   "scripts": { | ||||
|  | ||||
| @ -3,11 +3,12 @@ class Book { | ||||
|     this.olid = null | ||||
|     this.title = null | ||||
|     this.author = null | ||||
|     this.series = null | ||||
|     this.publishYear = null | ||||
|     this.publisher = null | ||||
|     this.description = null | ||||
|     this.cover = null | ||||
|     this.genres = [] | ||||
|     this.genre = [] | ||||
| 
 | ||||
|     if (book) { | ||||
|       this.construct(book) | ||||
| @ -18,11 +19,12 @@ class Book { | ||||
|     this.olid = book.olid | ||||
|     this.title = book.title | ||||
|     this.author = book.author | ||||
|     this.series = book.series | ||||
|     this.publishYear = book.publish_year | ||||
|     this.publisher = book.publisher | ||||
|     this.description = book.description | ||||
|     this.cover = book.cover | ||||
|     this.genres = book.genres | ||||
|     this.genre = book.genre | ||||
|   } | ||||
| 
 | ||||
|   toJSON() { | ||||
| @ -30,11 +32,12 @@ class Book { | ||||
|       olid: this.olid, | ||||
|       title: this.title, | ||||
|       author: this.author, | ||||
|       series: this.series, | ||||
|       publishYear: this.publish_year, | ||||
|       publisher: this.publisher, | ||||
|       description: this.description, | ||||
|       cover: this.cover, | ||||
|       genres: this.genres | ||||
|       genre: this.genre | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| @ -42,10 +45,11 @@ class Book { | ||||
|     this.olid = data.olid || null | ||||
|     this.title = data.title || null | ||||
|     this.author = data.author || null | ||||
|     this.series = data.series || null | ||||
|     this.publishYear = data.publish_year || null | ||||
|     this.description = data.description || null | ||||
|     this.cover = data.cover || null | ||||
|     this.genres = data.genres || [] | ||||
|     this.genre = data.genre || [] | ||||
|   } | ||||
| 
 | ||||
|   update(payload) { | ||||
| @ -53,12 +57,12 @@ class Book { | ||||
|     for (const key in payload) { | ||||
|       if (payload[key] === undefined) continue; | ||||
| 
 | ||||
|       if (key === 'genres') { | ||||
|         if (payload['genres'] === null && this.genres !== null) { | ||||
|           this.genres = [] | ||||
|       if (key === 'genre') { | ||||
|         if (payload['genre'] === null && this.genre !== null) { | ||||
|           this.genre = [] | ||||
|           hasUpdates = true | ||||
|         } else if (payload['genres'].join(',') !== this.genres.join(',')) { | ||||
|           this.genres = payload['genres'] | ||||
|         } else if (payload['genre'].join(',') !== this.genre.join(',')) { | ||||
|           this.genre = payload['genre'] | ||||
|           hasUpdates = true | ||||
|         } | ||||
|       } else if (this[key] !== undefined && payload[key] !== this[key]) { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user