Website Structure

This commit is contained in:
supalerk-ar66 2026-01-13 10:46:40 +07:00
parent 62812f2090
commit 71f0676a62
22365 changed files with 4265753 additions and 791 deletions

View file

@ -0,0 +1,420 @@
/* eslint-disable no-useless-escape */
import { ref, reactive } from 'vue'
import { injectProp } from '../../utils/private.inject-obj-prop/inject-obj-prop.js'
/**
* __ QUASAR_SSR __ -> runs on SSR on client or server
* __ QUASAR_SSR_SERVER __ -> runs on SSR on server
* __ QUASAR_SSR_CLIENT __ -> runs on SSR on client
* __ QUASAR_SSR_PWA __ -> built with SSR+PWA; may run on SSR on client or on PWA client
* (needs runtime detection)
*/
export const isRuntimeSsrPreHydration = __QUASAR_SSR_SERVER__
? { value: true }
: ref(
__QUASAR_SSR_CLIENT__ && (
__QUASAR_SSR_PWA__ ? document.body.getAttribute('data-server-rendered') !== null : true
)
)
let preHydrationBrowser
function getMatch (userAgent, platformMatch) {
const match = /(edg|edge|edga|edgios)\/([\w.]+)/.exec(userAgent)
|| /(opr)[\/]([\w.]+)/.exec(userAgent)
|| /(vivaldi)[\/]([\w.]+)/.exec(userAgent)
|| /(chrome|crios)[\/]([\w.]+)/.exec(userAgent)
|| /(version)(applewebkit)[\/]([\w.]+).*(safari)[\/]([\w.]+)/.exec(userAgent)
|| /(webkit)[\/]([\w.]+).*(version)[\/]([\w.]+).*(safari)[\/]([\w.]+)/.exec(userAgent)
|| /(firefox|fxios)[\/]([\w.]+)/.exec(userAgent)
|| /(webkit)[\/]([\w.]+)/.exec(userAgent)
|| /(opera)(?:.*version|)[\/]([\w.]+)/.exec(userAgent)
|| []
return {
browser: match[ 5 ] || match[ 3 ] || match[ 1 ] || '',
version: match[ 4 ] || match[ 2 ] || '0',
platform: platformMatch[ 0 ] || ''
}
}
function getPlatformMatch (userAgent) {
return /(ipad)/.exec(userAgent)
|| /(ipod)/.exec(userAgent)
|| /(windows phone)/.exec(userAgent)
|| /(iphone)/.exec(userAgent)
|| /(kindle)/.exec(userAgent)
|| /(silk)/.exec(userAgent)
|| /(android)/.exec(userAgent)
|| /(win)/.exec(userAgent)
|| /(mac)/.exec(userAgent)
|| /(linux)/.exec(userAgent)
|| /(cros)/.exec(userAgent)
// TODO: Remove BlackBerry detection. BlackBerry OS, BlackBerry 10, and BlackBerry PlayBook OS
// is officially dead as of January 4, 2022 (https://www.blackberry.com/us/en/support/devices/end-of-life)
|| /(playbook)/.exec(userAgent)
|| /(bb)/.exec(userAgent)
|| /(blackberry)/.exec(userAgent)
|| []
}
const hasTouch = __QUASAR_SSR_SERVER__
? false
: 'ontouchstart' in window || window.navigator.maxTouchPoints > 0
function getPlatform (UA) {
const userAgent = UA.toLowerCase()
const platformMatch = getPlatformMatch(userAgent)
const matched = getMatch(userAgent, platformMatch)
const browser = {
mobile: false,
desktop: false,
cordova: false,
capacitor: false,
nativeMobile: false,
// nativeMobileWrapper: void 0,
electron: false,
bex: false,
linux: false,
mac: false,
win: false,
cros: false,
chrome: false,
firefox: false,
opera: false,
safari: false,
vivaldi: false,
edge: false,
edgeChromium: false,
ie: false,
webkit: false,
android: false,
ios: false,
ipad: false,
iphone: false,
ipod: false,
kindle: false,
winphone: false,
blackberry: false,
playbook: false,
silk: false
}
if (matched.browser) {
browser[ matched.browser ] = true
browser.version = matched.version
browser.versionNumber = parseInt(matched.version, 10)
}
if (matched.platform) {
browser[ matched.platform ] = true
}
const knownMobiles = browser.android
|| browser.ios
|| browser.bb
|| browser.blackberry
|| browser.ipad
|| browser.iphone
|| browser.ipod
|| browser.kindle
|| browser.playbook
|| browser.silk
|| browser[ 'windows phone' ]
// These are all considered mobile platforms, meaning they run a mobile browser
if (
knownMobiles === true
|| userAgent.indexOf('mobile') !== -1
) {
browser.mobile = true
}
// If it's not mobile we should consider it's desktop platform, meaning it runs a desktop browser
// It's a workaround for anonymized user agents
// (browser.cros || browser.mac || browser.linux || browser.win)
else {
browser.desktop = true
}
if (browser[ 'windows phone' ]) {
browser.winphone = true
delete browser[ 'windows phone' ]
}
if (browser.edga || browser.edgios || browser.edg) {
browser.edge = true
matched.browser = 'edge'
}
else if (browser.crios) {
browser.chrome = true
matched.browser = 'chrome'
}
else if (browser.fxios) {
browser.firefox = true
matched.browser = 'firefox'
}
// Set iOS if on iPod, iPad or iPhone
if (browser.ipod || browser.ipad || browser.iphone) {
browser.ios = true
}
if (browser.vivaldi) {
matched.browser = 'vivaldi'
browser.vivaldi = true
}
// TODO: The assumption about WebKit based browsers below is not completely accurate.
// Google released Blink(a fork of WebKit) engine on April 3, 2013, which is really different than WebKit today.
// Today, one might want to check for WebKit to deal with its bugs, which is used on all browsers on iOS, and Safari browser on all platforms.
if (
// Chrome, Opera 15+, Vivaldi and Safari are webkit based browsers
browser.chrome
|| browser.opr
|| browser.safari
|| browser.vivaldi
// we expect unknown, non iOS mobile browsers to be webkit based
|| (
browser.mobile === true
&& browser.ios !== true
&& knownMobiles !== true
)
) {
browser.webkit = true
}
// Opera 15+ are identified as opr
if (browser.opr) {
matched.browser = 'opera'
browser.opera = true
}
// Some browsers are marked as Safari but are not
if (browser.safari) {
if (browser.blackberry || browser.bb) {
matched.browser = 'blackberry'
browser.blackberry = true
}
else if (browser.playbook) {
matched.browser = 'playbook'
browser.playbook = true
}
else if (browser.android) {
matched.browser = 'android'
browser.android = true
}
else if (browser.kindle) {
matched.browser = 'kindle'
browser.kindle = true
}
else if (browser.silk) {
matched.browser = 'silk'
browser.silk = true
}
}
// Assign the name and platform variable
browser.name = matched.browser
browser.platform = matched.platform
if (__QUASAR_SSR_SERVER__ !== true) {
if (userAgent.indexOf('electron') !== -1) {
browser.electron = true
}
else if (document.location.href.indexOf('-extension://') !== -1) {
browser.bex = true
}
else {
if (window.Capacitor !== void 0) {
browser.capacitor = true
browser.nativeMobile = true
browser.nativeMobileWrapper = 'capacitor'
}
else if (window._cordovaNative !== void 0 || window.cordova !== void 0) {
browser.cordova = true
browser.nativeMobile = true
browser.nativeMobileWrapper = 'cordova'
}
if (isRuntimeSsrPreHydration.value === true) {
/*
* We need to remember the current state as
* everything that follows can only be corrected client-side,
* but we don't want to brake the hydration.
*
* The "client" object is imported throughout the UI and should
* be as accurate as possible given all the knowledge that we posses
* because decisions are required to be made immediately, even
* before the hydration occurs.
*/
preHydrationBrowser = { is: { ...browser } }
}
/*
* All the following should be client-side corrections only
*/
if (
hasTouch === true
&& browser.mac === true
&& (
(browser.desktop === true && browser.safari === true)
|| (
browser.nativeMobile === true
&& browser.android !== true
&& browser.ios !== true
&& browser.ipad !== true
)
)
) {
/*
* Correction needed for iOS since the default
* setting on iPad is to request desktop view; if we have
* touch support and the user agent says it's a
* desktop, we infer that it's an iPhone/iPad with desktop view
* so we must fix the false positives
*/
delete browser.mac
delete browser.desktop
const platform = Math.min(window.innerHeight, window.innerWidth) > 414
? 'ipad'
: 'iphone'
Object.assign(browser, {
mobile: true,
ios: true,
platform,
[ platform ]: true
})
}
if (
browser.mobile !== true
&& window.navigator.userAgentData
&& window.navigator.userAgentData.mobile
) {
/*
* Correction needed on client-side when
* we also have the navigator userAgentData
*/
delete browser.desktop
browser.mobile = true
}
}
}
return browser
}
const userAgent = __QUASAR_SSR_SERVER__
? ''
: navigator.userAgent || navigator.vendor || window.opera
const ssrClient = {
has: {
touch: false,
webStorage: false
},
within: { iframe: false }
}
// We export "client" for hydration error-free parts,
// like touch directives who do not (and must NOT) wait
// for the client takeover;
// Do NOT import this directly in your app, unless you really know
// what you are doing.
export const client = __QUASAR_SSR_SERVER__
? ssrClient
: {
userAgent,
is: getPlatform(userAgent),
has: {
touch: hasTouch
},
within: {
iframe: window.self !== window.top
}
}
const Platform = {
install (opts) {
const { $q } = opts
if (__QUASAR_SSR_SERVER__) {
$q.platform = this.parseSSR(opts.ssrContext)
}
else if (isRuntimeSsrPreHydration.value === true) {
// takeover should increase accuracy for
// the rest of the props; we also avoid
// hydration errors
opts.onSSRHydrated.push(() => {
Object.assign($q.platform, client)
isRuntimeSsrPreHydration.value = false
})
// we need to make platform reactive
// for the takeover phase
$q.platform = reactive(this)
}
else {
$q.platform = this
}
}
}
if (__QUASAR_SSR_SERVER__) {
Platform.parseSSR = (ssrContext) => {
const userAgent = ssrContext.req.headers[ 'user-agent' ] || ssrContext.req.headers[ 'User-Agent' ] || ''
return {
...client,
userAgent,
is: getPlatform(userAgent)
}
}
}
else {
// do not access window.localStorage without
// devland actually using it as this will get
// reported under "Cookies" in Google Chrome
let hasWebStorage
injectProp(client.has, 'webStorage', () => {
if (hasWebStorage !== void 0) {
return hasWebStorage
}
try {
if (window.localStorage) {
hasWebStorage = true
return true
}
}
catch (_) {}
hasWebStorage = false
return false
})
Object.assign(Platform, client)
if (isRuntimeSsrPreHydration.value === true) {
// must match with server-side before
// client taking over in order to prevent
// hydration errors
Object.assign(Platform, preHydrationBrowser, ssrClient)
// free up memory
preHydrationBrowser = null
}
}
export default Platform

View file

@ -0,0 +1,221 @@
{
"meta": {
"docsUrl": "https://v2.quasar.dev/options/platform-detection"
},
"injection": "$q.platform",
"props": {
"userAgent": {
"type": "String",
"desc": "Client browser User Agent",
"examples": [ "'mozilla/5.0 (macintosh; intel mac os x 10_14_5) applewebkit/537.36 (khtml, like gecko) chrome/75.0.3770.100 safari/537.36'" ]
},
"is": {
"type": "Object",
"desc": "Client browser details (property names depend on browser)",
"definition": {
"name": {
"type": "String",
"desc": "Browser name",
"examples": [ "'chrome'" ]
},
"platform": {
"type": "String",
"desc": "Platform name",
"examples": [ "'mac'" ]
},
"version": {
"type": "String",
"required": false,
"desc": "Detailed browser version",
"examples": [ "'71.0.3578.98'" ]
},
"versionNumber": {
"type": "Number",
"required": false,
"desc": "Major browser version as a number"
},
"mobile": {
"type": "Boolean",
"desc": "Whether the platform is mobile"
},
"desktop": {
"type": "Boolean",
"desc": "Whether the platform is desktop"
},
"cordova": {
"type": "Boolean",
"desc": "Whether the platform is Cordova"
},
"capacitor": {
"type": "Boolean",
"desc": "Whether the platform is Capacitor"
},
"nativeMobile": {
"type": "Boolean",
"desc": "Whether the platform is a native mobile wrapper"
},
"nativeMobileWrapper": {
"type": "String",
"required": false,
"values": [ "'cordova'", "'capacitor'" ],
"desc": "Type of the native mobile wrapper"
},
"electron": {
"type": "Boolean",
"desc": "Whether the platform is Electron"
},
"bex": {
"type": "Boolean",
"desc": "Whether the platform is BEX(Browser Extension)"
},
"linux": {
"type": "Boolean",
"desc": "Whether the operating system is Linux"
},
"mac": {
"type": "Boolean",
"desc": "Whether the operating system is Mac OS"
},
"win": {
"type": "Boolean",
"desc": "Whether the operating system is Windows"
},
"cros": {
"type": "Boolean",
"desc": "Whether the operating system is Chrome OS"
},
"chrome": {
"type": "Boolean",
"desc": "Whether the browser is Google Chrome"
},
"firefox": {
"type": "Boolean",
"desc": "Whether the browser is Firefox"
},
"opera": {
"type": "Boolean",
"desc": "Whether the browser is Opera"
},
"safari": {
"type": "Boolean",
"desc": "Whether the browser is Safari"
},
"vivaldi": {
"type": "Boolean",
"desc": "Whether the browser is Vivaldi"
},
"edge": {
"type": "Boolean",
"desc": "Whether the browser is Microsoft Edge Legacy"
},
"edgeChromium": {
"type": "Boolean",
"desc": "Whether the browser is Microsoft Edge (Chromium)"
},
"ie": {
"type": "Boolean",
"desc": "Whether the browser is Internet Explorer"
},
"webkit": {
"type": "Boolean",
"desc": "Whether the browser is a Webkit or Webkit-based one"
},
"android": {
"type": "Boolean",
"desc": "Whether the operating system is Android"
},
"ios": {
"type": "Boolean",
"desc": "Whether the operating system is iOS"
},
"ipad": {
"type": "Boolean",
"desc": "Whether the device is an iPad"
},
"iphone": {
"type": "Boolean",
"desc": "Whether the device is an iPhone"
},
"ipod": {
"type": "Boolean",
"desc": "Whether the device is an iPod"
},
"kindle": {
"type": "Boolean",
"desc": "Whether the device is a Kindle"
},
"winphone": {
"type": "Boolean",
"desc": "Whether the operating system is Windows Phone"
},
"blackberry": {
"type": "Boolean",
"desc": "Whether the device is a Blackberry"
},
"playbook": {
"type": "Boolean",
"desc": "Whether the device is a Blackberry Playbook"
},
"silk": {
"type": "Boolean",
"desc": "Whether the browser is Amazon Silk"
}
},
"examples": [ "{ chrome: true, version: '71.0.3578.98', versionNumber: 71, mac: true, desktop: true, webkit: true, name: 'chrome', platform: 'mac' }" ]
},
"has": {
"type": "Object",
"desc": "Client browser detectable properties",
"definition": {
"touch": {
"type": "Boolean",
"desc": "Client browser runs on device with touch support"
},
"webStorage": {
"type": "Boolean",
"desc": "Client browser has Web Storage support"
}
},
"examples": [ "{ touch: false, webStorage: true }" ]
},
"within": {
"type": "Object",
"desc": "Client browser environment",
"definition": {
"iframe": {
"type": "Boolean",
"desc": "Does the app run under an iframe?"
}
},
"examples": [ "{ iframe: false }" ]
}
},
"methods": {
"parseSSR": {
"desc": "For SSR usage only, and only on the global import (not on $q.platform)",
"params": {
"ssrContext": {
"type": "Object",
"desc": "SSR Context Object",
"required": true
}
},
"returns": {
"type": "Object",
"tsType": "Platform",
"desc": "Platform object (like $q.platform) for SSR usage purposes"
}
}
}
}

View file

@ -0,0 +1,112 @@
/**
* Ignored specs:
* [(method)parseSSR]
*/
import { describe, test, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import Platform from './Platform.js'
const mountPlugin = () => mount({ template: '<div />' })
describe('[Platform API]', () => {
describe('[Injection]', () => {
test('is injected into $q', () => {
const wrapper = mountPlugin()
expect(Platform).toBe(wrapper.vm.$q.platform)
})
})
describe('[Props]', () => {
describe('[(prop)userAgent]', () => {
test('is correct type', () => {
mountPlugin()
expect(Platform.userAgent).toBeTypeOf('string')
})
})
describe('[(prop)is]', () => {
test('is correct type', () => {
mountPlugin()
const expected = {
name: expect.any(String),
platform: expect.any(String),
version: expect.any(String),
versionNumber: expect.any(Number),
mobile: expect.any(Boolean),
desktop: expect.any(Boolean),
cordova: expect.any(Boolean),
capacitor: expect.any(Boolean),
nativeMobile: expect.any(Boolean),
nativeMobileWrapper: expect.$any([
'cordova',
'capacitor'
]),
electron: expect.any(Boolean),
bex: expect.any(Boolean),
linux: expect.any(Boolean),
mac: expect.any(Boolean),
win: expect.any(Boolean),
cros: expect.any(Boolean),
chrome: expect.any(Boolean),
firefox: expect.any(Boolean),
opera: expect.any(Boolean),
safari: expect.any(Boolean),
vivaldi: expect.any(Boolean),
edge: expect.any(Boolean),
edgeChromium: expect.any(Boolean),
ie: expect.any(Boolean),
webkit: expect.any(Boolean),
android: expect.any(Boolean),
ios: expect.any(Boolean),
ipad: expect.any(Boolean),
iphone: expect.any(Boolean),
ipod: expect.any(Boolean),
kindle: expect.any(Boolean),
winphone: expect.any(Boolean),
blackberry: expect.any(Boolean),
playbook: expect.any(Boolean),
silk: expect.any(Boolean)
}
const actualKeys = Object.keys(Platform.is)
expect(actualKeys).not.toHaveLength(0)
expect(actualKeys).toSatisfy(
keys => keys.every(key => expected[ key ] !== void 0)
)
actualKeys.forEach(key => {
expect(Platform.is[ key ]).toStrictEqual(expected[ key ])
})
})
})
describe('[(prop)has]', () => {
test('is correct type', () => {
mountPlugin()
expect(Platform.has).toStrictEqual({
touch: expect.any(Boolean),
webStorage: expect.any(Boolean)
})
})
})
describe('[(prop)within]', () => {
test('is correct type', () => {
mountPlugin()
expect(Platform.within).toStrictEqual({
iframe: expect.any(Boolean)
})
})
})
})
})