在您安裝前,Greasy Fork镜像希望您了解本腳本包含“可能不受歡迎的功能”,可能幫助腳本的作者獲利,而不能給你帶來任何收益。
此腳本內的代碼會追蹤您的瀏覽行為。
腳本的作者解釋:
使用 Google Analytics 了解使用情況
自由轉調、輕鬆練歌,打造 91 譜的最佳體驗!
- // ==UserScript==
- // @name 91 Plus
- // @namespace https://github.com/DonkeyBear
- // @version 1.8.4
- // @author DonkeyBear
- // @description 自由轉調、輕鬆練歌,打造 91 譜的最佳體驗!
- // @icon https://www.91pu.com.tw/icons/favicon-32x32.png
- // @match *://www.91pu.com.tw/m/*
- // @match *://www.91pu.com.tw/song/*
- // @require https://cdn.jsdelivr.net/npm/vue@3.3.10/dist/vue.global.prod.js
- // @require https://cdn.jsdelivr.net/npm/zipson@0.2.12/dist/zipson.min.js
- // @require https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js
- // @require https://cdn.jsdelivr.net/npm/vexchords@1.2.0/dist/vexchords.dev.min.js
- // @grant GM_addStyle
- // @grant GM_getValue
- // @grant GM_setValue
- // @grant unsafeWindow
- // @antifeature tracking 使用 Google Analytics 了解使用情況
- // ==/UserScript==
- (e=>{if(typeof GM_addStyle=="function"){GM_addStyle(e);return}const a=document.createElement("style");a.textContent=e,document.head.append(a)})(' @import"https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.2/font/bootstrap-icons.min.css";#trigger-overlay[data-v-658df74c]{position:fixed;top:0;left:0;right:0;bottom:0;z-index:500}.bi[data-v-6e52047a]{color:var(--2b53b793);font-size:var(--4bbf91d1);-webkit-text-stroke:var(--5ab9f408) var(--2b53b793)}.bi[data-v-6e52047a]:before{transition:text-shadow .2s}.bi[active=true][data-v-6e52047a]:before{text-shadow:0 0 .5rem rgb(75,156,169)}.toolbar-icon[data-v-3dbcd695]{cursor:pointer;padding:.25rem .75rem;display:flex;flex-direction:column;align-items:center;gap:.15rem}.toolbar-icon-text[data-v-3dbcd695]{color:color-mix(in srgb,var(--52bb32ad) 70%,rgba(75,156,169,.65));font-size:.5rem;letter-spacing:.15rem;margin-right:-.15rem}.adjust-widget[data-v-cb8ab81d]{display:flex}.adjust-widget .adjust-button[data-v-cb8ab81d]{border:0;border-radius:.25rem;background:transparent}.adjust-widget .adjust-button[data-v-cb8ab81d]:hover{background:rgba(0,0,0,.025)}.adjust-widget .adjust-button[data-v-cb8ab81d]:disabled{opacity:.25}.adjust-widget .adjust-button.adjust-button-middle[data-v-cb8ab81d]{flex-grow:1;color:var(--13e75dc8);font-size:calc(var(--10392328) * .75);font-weight:700}.adjust-widget .adjust-button.adjust-button-left[data-v-cb8ab81d]{padding-right:1rem}.adjust-widget .adjust-button.adjust-button-right[data-v-cb8ab81d]{padding-left:1rem}.slide-and-fade-enter-active[data-v-f161c46c],.slide-and-fade-leave-active[data-v-f161c46c]{transition:all .2s}.slide-and-fade-enter-from[data-v-f161c46c],.slide-and-fade-leave-to[data-v-f161c46c]{transform:translateY(10%);opacity:0}#plus91-sheet-popup[data-v-f161c46c]{position:absolute;left:0;right:0;bottom:100%;background:#fafafa;border:1px solid lightgray;padding:1rem 2rem;border-radius:1rem;margin:.5rem 1rem;max-height:50vh;overflow-y:scroll}#plus91-sheet-popup[data-v-f161c46c]::-webkit-scrollbar{display:none}.transpose-range-container[data-v-f161c46c]{margin-top:1rem}.transpose-range-container input[type=range][data-v-f161c46c]{width:100%}.instrument-select-container[data-v-f161c46c]{display:flex;border:1px solid lightgray;border-radius:.25rem;margin-top:1rem;background:white}.instrument-select-container .instrument-select-button[data-v-f161c46c]{width:33.3333333333%;border:0;border-right:1px solid lightgray;background:transparent;color:#666;padding:.5rem;font-size:.65rem;font-weight:700;cursor:pointer!important}.instrument-select-container .instrument-select-button[data-v-f161c46c]:last-child{border:0;border-radius:0 .25rem .25rem 0}.instrument-select-container .instrument-select-button[data-v-f161c46c]:first-child{border-radius:.25rem 0 0 .25rem}.instrument-select-container .instrument-select-button[data-v-f161c46c]:hover{background:whitesmoke}.chord-container .chord-name[data-v-735734f6]{font-size:.5rem;font-weight:900;color:#666;text-align:center}.chord-container .chord-chart[data-v-735734f6]{margin:-.6rem 0 -.25rem}.slide-and-fade-enter-active[data-v-2210cdf0],.slide-and-fade-leave-active[data-v-2210cdf0]{transition:all .2s}.slide-and-fade-enter-from[data-v-2210cdf0],.slide-and-fade-leave-to[data-v-2210cdf0]{transform:translateY(10%);opacity:0}#plus91-chord-popup[data-v-2210cdf0]{position:absolute;left:0;right:0;bottom:100%;background:#fafafa;border:1px solid lightgray;border-radius:1rem;margin:.5rem 1rem;max-height:50vh;overflow-y:scroll;padding:1rem}#plus91-chord-popup[data-v-2210cdf0]::-webkit-scrollbar{display:none}#plus91-chord-popup .banner[data-v-2210cdf0]{display:flex;align-items:center;background:rgba(75,156,169,.25);color:color-mix(in srgb,rgba(75,156,169,.65) 50%,black 50%);border-radius:.5rem;padding:.5rem .75rem;margin-bottom:.25rem}#plus91-chord-popup .banner section[data-v-2210cdf0]{flex-grow:1;margin-left:.5rem}#plus91-chord-popup .chord-popup-container[data-v-2210cdf0]{display:grid;grid-template-columns:repeat(6,1fr);column-gap:.5rem;padding-top:.4rem}#plus91-chord-popup.banner-only .banner[data-v-2210cdf0]{margin-bottom:0;background:rgba(246,210,102,.25);color:color-mix(in srgb,#f6d266 50%,black 35%)}#plus91-chord-popup.banner-only .chord-popup-container[data-v-2210cdf0]{padding-top:0}.slide-and-fade-enter-active[data-v-eff17405],.slide-and-fade-leave-active[data-v-eff17405]{transition:all .2s}.slide-and-fade-enter-from[data-v-eff17405],.slide-and-fade-leave-to[data-v-eff17405]{transform:translateY(10%);opacity:0}#plus91-font-popup[data-v-eff17405]{position:absolute;left:0;right:0;bottom:100%;background:#fafafa;border:1px solid lightgray;padding:1rem 2rem;border-radius:1rem;margin:.5rem 1rem;max-height:50vh;overflow-y:scroll}#plus91-font-popup[data-v-eff17405]::-webkit-scrollbar{display:none}.slide-and-fade-enter-active[data-v-e329f5af],.slide-and-fade-leave-active[data-v-e329f5af]{transition:all .2s}.slide-and-fade-enter-from[data-v-e329f5af],.slide-and-fade-leave-to[data-v-e329f5af]{transform:translateY(10%);opacity:0}#plus91-settings-popup[data-v-e329f5af]{position:absolute;left:0;right:0;bottom:100%;background:#fafafa;border:1px solid lightgray;border-radius:1rem;margin:.5rem 1rem;max-height:50vh;overflow-y:scroll;padding:1rem}#plus91-settings-popup[data-v-e329f5af]::-webkit-scrollbar{display:none}#plus91-settings-popup .setting-item[data-v-e329f5af]{display:flex;align-items:center;justify-content:space-between;padding:.5rem 1rem;border-radius:.5rem;cursor:pointer}#plus91-settings-popup .setting-item[data-v-e329f5af]:hover{background:rgba(0,0,0,.05)}.icon-button[data-v-e9902592]{display:flex;flex-direction:column;align-items:center;cursor:pointer;padding:0 .6rem .4rem;border-radius:.25rem}.icon-button[data-v-e9902592]:hover{background:rgba(0,0,0,.025)}.icon-button .button-text[data-v-e9902592]{font-size:.5rem;color:var(--9047bc34)}.slide-and-fade-enter-active[data-v-47af8eb5],.slide-and-fade-leave-active[data-v-47af8eb5]{transition:all .2s}.slide-and-fade-enter-from[data-v-47af8eb5],.slide-and-fade-leave-to[data-v-47af8eb5]{transform:translateY(10%);opacity:0}#plus91-menu-popup[data-v-47af8eb5]{position:absolute;left:0;right:0;bottom:100%;background:#fafafa;border:1px solid lightgray;padding:1rem 2rem;border-radius:1rem;margin:.5rem 1rem;max-height:50vh;overflow-y:scroll}#plus91-menu-popup[data-v-47af8eb5]::-webkit-scrollbar{display:none}#plus91-menu-popup .menu-popup-container[data-v-47af8eb5]{display:flex;justify-content:space-around}.hotkey-item[data-v-3c43f6cf]{display:flex;justify-content:space-between;align-items:center;padding:0 .25rem;border-radius:.25rem;height:1.4rem}.hotkey-item[data-v-3c43f6cf]:nth-child(odd){background:rgba(0,0,0,.025)}.desc.title[data-v-3c43f6cf]{font-size:.55rem;color:#999}.hotkeys[data-v-3c43f6cf]{display:flex}.hr[data-v-3c43f6cf]{display:flex;flex-grow:1;border-top:1px solid lightgray;margin-left:.25rem}kbd[data-v-3c43f6cf]{font-size:.6rem;border:solid lightgray;border-width:1px .1rem .15rem;border-radius:.2rem;padding:0 .2rem;letter-spacing:-.025rem;color:#666;margin-left:.15rem}.slide-and-fade-enter-active[data-v-eb86b87c],.slide-and-fade-leave-active[data-v-eb86b87c]{transition:all .2s}.slide-and-fade-enter-from[data-v-eb86b87c],.slide-and-fade-leave-to[data-v-eb86b87c]{transform:translateY(10%);opacity:0}#plus91-hotkey-popup[data-v-eb86b87c]{position:absolute;left:0;right:0;bottom:100%;background:#fafafa;border:1px solid lightgray;padding:1rem 2rem;border-radius:1rem;margin:.5rem 1rem;max-height:50vh;overflow-y:scroll}#plus91-hotkey-popup[data-v-eb86b87c]::-webkit-scrollbar{display:none}#plus91-hotkey-popup .hotkey-popup-container[data-v-eb86b87c]{display:flex;color:#444}#plus91-hotkey-popup section[data-v-eb86b87c]{flex-grow:1;width:50%;margin:-.1rem 0}#plus91-hotkey-popup section.left-part[data-v-eb86b87c]{border-right:1px solid lightgray;margin-left:-.5rem;padding-right:.5rem}#plus91-hotkey-popup section.right-part[data-v-eb86b87c]{padding-left:.5rem;margin-right:-.5rem}#plus91-hotkey-popup kbd[data-v-eb86b87c]{font-size:.65rem;border:solid lightgray;border-width:1px .1rem .15rem;border-radius:.2rem;padding:0 .2rem;letter-spacing:-.025rem;color:#666}.slide-enter-active[data-v-4e274b3c],.slide-leave-active[data-v-4e274b3c]{transition:transform .2s}.slide-enter-from[data-v-4e274b3c],.slide-leave-to[data-v-4e274b3c]{transform:translateY(100%)}#plus91-footer[data-v-4e274b3c]{z-index:1000;display:flex;justify-content:center;position:fixed;left:0;right:0;bottom:0}.footer-container[data-v-4e274b3c]{width:min(100vw,768px);background:rgba(75,156,169,.65);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);padding:.25rem .75rem .75rem;display:flex;justify-content:space-between;align-items:center;border-top:1px solid rgb(90,140,160)}@media (min-width: 768px){.footer-container[data-v-4e274b3c]{border-radius:1rem 1rem 0 0}}.slide-enter-active[data-v-5ddafe3d],.slide-leave-active[data-v-5ddafe3d]{transition:transform .2s}.slide-enter-from[data-v-5ddafe3d],.slide-leave-to[data-v-5ddafe3d]{transform:translateY(-100%)}#plus91-header[data-v-5ddafe3d]{z-index:1000;display:flex;justify-content:center;position:fixed;left:0;right:0;top:0}.header-container[data-v-5ddafe3d]{width:min(100vw,768px);background:rgba(75,156,169,.65);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);padding:.25rem .75rem;display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid rgb(90,140,160)}@media (min-width: 768px){.header-container[data-v-5ddafe3d]{border-radius:0 0 1rem 1rem}}.header-container input[data-v-5ddafe3d]{flex-grow:1;width:100%;border-radius:50rem;border:0;font-size:.8rem;font-weight:700;padding:.35rem 1.25rem;background:rgba(255,255,255,.6666666667);color:#0009;opacity:.5;transition:all .2s}.header-container input[data-v-5ddafe3d]:focus-visible{outline:0;opacity:1}.fade-enter-active[data-v-6cf58435],.fade-leave-active[data-v-6cf58435]{transition:opacity .2s}.fade-enter-from[data-v-6cf58435],.fade-leave-to[data-v-6cf58435]{opacity:0}#dark-mode-overlay[data-v-6cf58435]{position:fixed;top:0;left:0;right:0;bottom:0;z-index:800;-webkit-backdrop-filter:invert(1) hue-rotate(145deg) saturate(.75);backdrop-filter:invert(1) hue-rotate(145deg) saturate(.75);pointer-events:none}html{background:#fafafa url(/templets/pu/images/tone-bg.gif)}#vue-91plus{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,sans-serif}.tfunc2{margin:10px}#mtitle{font-family:system-ui}input[type=range],input[type=range]::-webkit-slider-thumb,input[type=range]::-webkit-slider-runnable-track{-webkit-appearance:none;box-shadow:none}input[type=range]::-webkit-slider-thumb,input[type=range]::-webkit-slider-runnable-track{border:1px solid rgba(68,68,68,.25)}input[type=range]::-webkit-slider-thumb{background:#60748d}#viptoneWindow.window,#bottomad,.update_vip_bar,.wmask,header,footer,.autoscroll,.backplace,.set .keys,.set .plays,.set .clear,.setint .hr:nth-child(4),.setint .hr:nth-child(5),.setint .hr:nth-child(6),.adsbygoogle,[class^=AD2M],[id^=adGeek]{display:none!important} ');
- (function (vue, zipson, vexchords, html2canvas) {
- 'use strict';
- var __defProp = Object.defineProperty;
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
- var __publicField = (obj, key, value) => {
- __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
- return value;
- };
- var __accessCheck = (obj, member, msg) => {
- if (!member.has(obj))
- throw TypeError("Cannot " + msg);
- };
- var __privateGet = (obj, member, getter) => {
- __accessCheck(obj, member, "read from private field");
- return getter ? getter.call(obj) : member.get(obj);
- };
- var __privateAdd = (obj, member, value) => {
- if (member.has(obj))
- throw TypeError("Cannot add the same private member more than once");
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
- };
- var __privateSet = (obj, member, value, setter) => {
- __accessCheck(obj, member, "write to private field");
- setter ? setter.call(obj, value) : member.set(obj, value);
- return value;
- };
- var __privateMethod = (obj, member, method) => {
- __accessCheck(obj, member, "access private method");
- return method;
- };
- var _unformat, unformat_fn, _store, _watchTranspose, watchTranspose_fn, _watchFontSize, watchFontSize_fn;
- var isVue2 = false;
- /*!
- * pinia v2.1.7
- * (c) 2023 Eduardo San Martin Morote
- * @license MIT
- */
- let activePinia;
- const setActivePinia = (pinia2) => activePinia = pinia2;
- const piniaSymbol = (
- /* istanbul ignore next */
- Symbol()
- );
- function isPlainObject(o) {
- return o && typeof o === "object" && Object.prototype.toString.call(o) === "[object Object]" && typeof o.toJSON !== "function";
- }
- var MutationType;
- (function(MutationType2) {
- MutationType2["direct"] = "direct";
- MutationType2["patchObject"] = "patch object";
- MutationType2["patchFunction"] = "patch function";
- })(MutationType || (MutationType = {}));
- function createPinia() {
- const scope = vue.effectScope(true);
- const state = scope.run(() => vue.ref({}));
- let _p = [];
- let toBeInstalled = [];
- const pinia2 = vue.markRaw({
- install(app) {
- setActivePinia(pinia2);
- {
- pinia2._a = app;
- app.provide(piniaSymbol, pinia2);
- app.config.globalProperties.$pinia = pinia2;
- toBeInstalled.forEach((plugin) => _p.push(plugin));
- toBeInstalled = [];
- }
- },
- use(plugin) {
- if (!this._a && !isVue2) {
- toBeInstalled.push(plugin);
- } else {
- _p.push(plugin);
- }
- return this;
- },
- _p,
- // it's actually undefined here
- // @ts-expect-error
- _a: null,
- _e: scope,
- _s: /* @__PURE__ */ new Map(),
- state
- });
- return pinia2;
- }
- const noop = () => {
- };
- function addSubscription(subscriptions, callback, detached, onCleanup = noop) {
- subscriptions.push(callback);
- const removeSubscription = () => {
- const idx = subscriptions.indexOf(callback);
- if (idx > -1) {
- subscriptions.splice(idx, 1);
- onCleanup();
- }
- };
- if (!detached && vue.getCurrentScope()) {
- vue.onScopeDispose(removeSubscription);
- }
- return removeSubscription;
- }
- function triggerSubscriptions(subscriptions, ...args) {
- subscriptions.slice().forEach((callback) => {
- callback(...args);
- });
- }
- const fallbackRunWithContext = (fn) => fn();
- function mergeReactiveObjects(target, patchToApply) {
- if (target instanceof Map && patchToApply instanceof Map) {
- patchToApply.forEach((value, key) => target.set(key, value));
- }
- if (target instanceof Set && patchToApply instanceof Set) {
- patchToApply.forEach(target.add, target);
- }
- for (const key in patchToApply) {
- if (!patchToApply.hasOwnProperty(key))
- continue;
- const subPatch = patchToApply[key];
- const targetValue = target[key];
- if (isPlainObject(targetValue) && isPlainObject(subPatch) && target.hasOwnProperty(key) && !vue.isRef(subPatch) && !vue.isReactive(subPatch)) {
- target[key] = mergeReactiveObjects(targetValue, subPatch);
- } else {
- target[key] = subPatch;
- }
- }
- return target;
- }
- const skipHydrateSymbol = (
- /* istanbul ignore next */
- Symbol()
- );
- function shouldHydrate(obj) {
- return !isPlainObject(obj) || !obj.hasOwnProperty(skipHydrateSymbol);
- }
- const { assign } = Object;
- function isComputed(o) {
- return !!(vue.isRef(o) && o.effect);
- }
- function createOptionsStore(id, options, pinia2, hot) {
- const { state, actions, getters } = options;
- const initialState = pinia2.state.value[id];
- let store;
- function setup() {
- if (!initialState && true) {
- {
- pinia2.state.value[id] = state ? state() : {};
- }
- }
- const localState = vue.toRefs(pinia2.state.value[id]);
- return assign(localState, actions, Object.keys(getters || {}).reduce((computedGetters, name) => {
- computedGetters[name] = vue.markRaw(vue.computed(() => {
- setActivePinia(pinia2);
- const store2 = pinia2._s.get(id);
- return getters[name].call(store2, store2);
- }));
- return computedGetters;
- }, {}));
- }
- store = createSetupStore(id, setup, options, pinia2, hot, true);
- return store;
- }
- function createSetupStore($id, setup, options = {}, pinia2, hot, isOptionsStore) {
- let scope;
- const optionsForPlugin = assign({ actions: {} }, options);
- const $subscribeOptions = {
- deep: true
- // flush: 'post',
- };
- let isListening;
- let isSyncListening;
- let subscriptions = [];
- let actionSubscriptions = [];
- let debuggerEvents;
- const initialState = pinia2.state.value[$id];
- if (!isOptionsStore && !initialState && true) {
- {
- pinia2.state.value[$id] = {};
- }
- }
- vue.ref({});
- let activeListener;
- function $patch(partialStateOrMutator) {
- let subscriptionMutation;
- isListening = isSyncListening = false;
- if (typeof partialStateOrMutator === "function") {
- partialStateOrMutator(pinia2.state.value[$id]);
- subscriptionMutation = {
- type: MutationType.patchFunction,
- storeId: $id,
- events: debuggerEvents
- };
- } else {
- mergeReactiveObjects(pinia2.state.value[$id], partialStateOrMutator);
- subscriptionMutation = {
- type: MutationType.patchObject,
- payload: partialStateOrMutator,
- storeId: $id,
- events: debuggerEvents
- };
- }
- const myListenerId = activeListener = Symbol();
- vue.nextTick().then(() => {
- if (activeListener === myListenerId) {
- isListening = true;
- }
- });
- isSyncListening = true;
- triggerSubscriptions(subscriptions, subscriptionMutation, pinia2.state.value[$id]);
- }
- const $reset = isOptionsStore ? function $reset2() {
- const { state } = options;
- const newState = state ? state() : {};
- this.$patch(($state) => {
- assign($state, newState);
- });
- } : (
- /* istanbul ignore next */
- noop
- );
- function $dispose() {
- scope.stop();
- subscriptions = [];
- actionSubscriptions = [];
- pinia2._s.delete($id);
- }
- function wrapAction(name, action) {
- return function() {
- setActivePinia(pinia2);
- const args = Array.from(arguments);
- const afterCallbackList = [];
- const onErrorCallbackList = [];
- function after(callback) {
- afterCallbackList.push(callback);
- }
- function onError(callback) {
- onErrorCallbackList.push(callback);
- }
- triggerSubscriptions(actionSubscriptions, {
- args,
- name,
- store,
- after,
- onError
- });
- let ret;
- try {
- ret = action.apply(this && this.$id === $id ? this : store, args);
- } catch (error) {
- triggerSubscriptions(onErrorCallbackList, error);
- throw error;
- }
- if (ret instanceof Promise) {
- return ret.then((value) => {
- triggerSubscriptions(afterCallbackList, value);
- return value;
- }).catch((error) => {
- triggerSubscriptions(onErrorCallbackList, error);
- return Promise.reject(error);
- });
- }
- triggerSubscriptions(afterCallbackList, ret);
- return ret;
- };
- }
- const partialStore = {
- _p: pinia2,
- // _s: scope,
- $id,
- $onAction: addSubscription.bind(null, actionSubscriptions),
- $patch,
- $reset,
- $subscribe(callback, options2 = {}) {
- const removeSubscription = addSubscription(subscriptions, callback, options2.detached, () => stopWatcher());
- const stopWatcher = scope.run(() => vue.watch(() => pinia2.state.value[$id], (state) => {
- if (options2.flush === "sync" ? isSyncListening : isListening) {
- callback({
- storeId: $id,
- type: MutationType.direct,
- events: debuggerEvents
- }, state);
- }
- }, assign({}, $subscribeOptions, options2)));
- return removeSubscription;
- },
- $dispose
- };
- const store = vue.reactive(partialStore);
- pinia2._s.set($id, store);
- const runWithContext = pinia2._a && pinia2._a.runWithContext || fallbackRunWithContext;
- const setupStore = runWithContext(() => pinia2._e.run(() => (scope = vue.effectScope()).run(setup)));
- for (const key in setupStore) {
- const prop = setupStore[key];
- if (vue.isRef(prop) && !isComputed(prop) || vue.isReactive(prop)) {
- if (!isOptionsStore) {
- if (initialState && shouldHydrate(prop)) {
- if (vue.isRef(prop)) {
- prop.value = initialState[key];
- } else {
- mergeReactiveObjects(prop, initialState[key]);
- }
- }
- {
- pinia2.state.value[$id][key] = prop;
- }
- }
- } else if (typeof prop === "function") {
- const actionValue = wrapAction(key, prop);
- {
- setupStore[key] = actionValue;
- }
- optionsForPlugin.actions[key] = prop;
- } else
- ;
- }
- {
- assign(store, setupStore);
- assign(vue.toRaw(store), setupStore);
- }
- Object.defineProperty(store, "$state", {
- get: () => pinia2.state.value[$id],
- set: (state) => {
- $patch(($state) => {
- assign($state, state);
- });
- }
- });
- pinia2._p.forEach((extender) => {
- {
- assign(store, scope.run(() => extender({
- store,
- app: pinia2._a,
- pinia: pinia2,
- options: optionsForPlugin
- })));
- }
- });
- if (initialState && isOptionsStore && options.hydrate) {
- options.hydrate(store.$state, initialState);
- }
- isListening = true;
- isSyncListening = true;
- return store;
- }
- function defineStore(idOrOptions, setup, setupOptions) {
- let id;
- let options;
- const isSetupStore = typeof setup === "function";
- if (typeof idOrOptions === "string") {
- id = idOrOptions;
- options = isSetupStore ? setupOptions : setup;
- } else {
- options = idOrOptions;
- id = idOrOptions.id;
- }
- function useStore2(pinia2, hot) {
- const hasContext = vue.hasInjectionContext();
- pinia2 = // in test mode, ignore the argument provided as we can always retrieve a
- // pinia instance with getActivePinia()
- pinia2 || (hasContext ? vue.inject(piniaSymbol, null) : null);
- if (pinia2)
- setActivePinia(pinia2);
- pinia2 = activePinia;
- if (!pinia2._s.has(id)) {
- if (isSetupStore) {
- createSetupStore(id, setup, options, pinia2);
- } else {
- createOptionsStore(id, options, pinia2);
- }
- }
- const store = pinia2._s.get(id);
- return store;
- }
- useStore2.$id = id;
- return useStore2;
- }
- function isObject(v) {
- return typeof v === "object" && v !== null;
- }
- function normalizeOptions(options, factoryOptions) {
- options = isObject(options) ? options : /* @__PURE__ */ Object.create(null);
- return new Proxy(options, {
- get(target, key, receiver) {
- if (key === "key")
- return Reflect.get(target, key, receiver);
- return Reflect.get(target, key, receiver) || Reflect.get(factoryOptions, key, receiver);
- }
- });
- }
- function get(state, path) {
- return path.reduce((obj, p) => {
- return obj == null ? void 0 : obj[p];
- }, state);
- }
- function set(state, path, val) {
- return path.slice(0, -1).reduce((obj, p) => {
- if (/^(__proto__)$/.test(p))
- return {};
- else
- return obj[p] = obj[p] || {};
- }, state)[path[path.length - 1]] = val, state;
- }
- function pick(baseState, paths) {
- return paths.reduce((substate, path) => {
- const pathArray = path.split(".");
- return set(substate, pathArray, get(baseState, pathArray));
- }, {});
- }
- function hydrateStore(store, { storage, serializer, key, debug }) {
- try {
- const fromStorage = storage == null ? void 0 : storage.getItem(key);
- if (fromStorage)
- store.$patch(serializer == null ? void 0 : serializer.deserialize(fromStorage));
- } catch (error) {
- if (debug)
- console.error(error);
- }
- }
- function persistState(state, { storage, serializer, key, paths, debug }) {
- try {
- const toStore = Array.isArray(paths) ? pick(state, paths) : state;
- storage.setItem(key, serializer.serialize(toStore));
- } catch (error) {
- if (debug)
- console.error(error);
- }
- }
- function createPersistedState(factoryOptions = {}) {
- return (context) => {
- const { auto = false } = factoryOptions;
- const {
- options: { persist = auto },
- store,
- pinia: pinia2
- } = context;
- if (!persist)
- return;
- if (!(store.$id in pinia2.state.value)) {
- const original_store = pinia2._s.get(store.$id.replace("__hot:", ""));
- if (original_store)
- Promise.resolve().then(() => original_store.$persist());
- return;
- }
- const persistences = (Array.isArray(persist) ? persist.map((p) => normalizeOptions(p, factoryOptions)) : [normalizeOptions(persist, factoryOptions)]).map(
- ({
- storage = localStorage,
- beforeRestore = null,
- afterRestore = null,
- serializer = {
- serialize: JSON.stringify,
- deserialize: JSON.parse
- },
- key = store.$id,
- paths = null,
- debug = false
- }) => {
- var _a;
- return {
- storage,
- beforeRestore,
- afterRestore,
- serializer,
- key: ((_a = factoryOptions.key) != null ? _a : (k) => k)(typeof key == "string" ? key : key(store.$id)),
- paths,
- debug
- };
- }
- );
- store.$persist = () => {
- persistences.forEach((persistence) => {
- persistState(store.$state, persistence);
- });
- };
- store.$hydrate = ({ runHooks = true } = {}) => {
- persistences.forEach((persistence) => {
- const { beforeRestore, afterRestore } = persistence;
- if (runHooks)
- beforeRestore == null ? void 0 : beforeRestore(context);
- hydrateStore(store, persistence);
- if (runHooks)
- afterRestore == null ? void 0 : afterRestore(context);
- });
- };
- persistences.forEach((persistence) => {
- const { beforeRestore, afterRestore } = persistence;
- beforeRestore == null ? void 0 : beforeRestore(context);
- hydrateStore(store, persistence);
- afterRestore == null ? void 0 : afterRestore(context);
- store.$subscribe(
- (_mutation, state) => {
- persistState(state, persistence);
- },
- {
- detached: true
- }
- );
- });
- };
- }
- var src_default = createPersistedState();
- const _export_sfc = (sfc, props) => {
- const target = sfc.__vccOpts || sfc;
- for (const [key, val] of props) {
- target[key] = val;
- }
- return target;
- };
- const _sfc_main$g = {};
- const _hoisted_1$f = { id: "trigger-overlay" };
- function _sfc_render(_ctx, _cache) {
- return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$f);
- }
- const TriggerOverlay = /* @__PURE__ */ _export_sfc(_sfc_main$g, [["render", _sfc_render], ["__scopeId", "data-v-658df74c"]]);
- const _Chord = class _Chord {
- /** @param {string} chordString */
- constructor(chordString) {
- this.chordString = chordString;
- }
- /**
- * @param {number} delta
- * @returns {Chord}
- */
- transpose(delta) {
- this.chordString = this.chordString.replaceAll(/[A-G][#b]?/g, (note) => {
- const isSharp = _Chord.sharps.includes(note);
- const scale = isSharp ? _Chord.sharps : _Chord.flats;
- const noteIndex = scale.indexOf(note);
- const transposedIndex = (noteIndex + delta + 12) % 12;
- const transposedNote = scale[transposedIndex];
- return transposedNote;
- });
- return this;
- }
- /** @returns {Chord} */
- switchModifier() {
- this.chordString = this.chordString.replaceAll(/[A-G][#b]/g, (note) => {
- const scale = note.includes("#") ? _Chord.sharps : _Chord.flats;
- const newScale = note.includes("#") ? _Chord.flats : _Chord.sharps;
- const noteIndex = scale.indexOf(note);
- return newScale[noteIndex];
- });
- return this;
- }
- /** @returns {Chord} */
- useSharpModifier() {
- this.chordString = this.chordString.replaceAll(/[A-G]b/g, (note) => {
- const noteIndex = _Chord.flats.indexOf(note);
- return _Chord.sharps[noteIndex];
- });
- return this;
- }
- /** @returns {Chord} */
- useFlatModifier() {
- this.chordString = this.chordString.replaceAll(/[A-G]#/g, (note) => {
- const noteIndex = _Chord.sharps.indexOf(note);
- return _Chord.flats[noteIndex];
- });
- return this;
- }
- /** @returns {string} */
- toString() {
- return this.chordString;
- }
- /** @returns {string} */
- toFormattedString() {
- return this.chordString.replaceAll(
- /[#b]/g,
- /* html */
- `<sup>$&</sup>`
- );
- }
- };
- __publicField(_Chord, "sharps", ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]);
- __publicField(_Chord, "flats", ["C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B"]);
- let Chord = _Chord;
- var _GM_getValue = /* @__PURE__ */ (() => typeof GM_getValue != "undefined" ? GM_getValue : void 0)();
- var _GM_setValue = /* @__PURE__ */ (() => typeof GM_setValue != "undefined" ? GM_setValue : void 0)();
- var _unsafeWindow = /* @__PURE__ */ (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)();
- class MonkeyStorage {
- /**
- * @param {String} key
- * @returns {String|null}
- */
- static getItem(key) {
- if (_GM_getValue) {
- return _GM_getValue(key, null);
- } else {
- return localStorage.getItem(key);
- }
- }
- /**
- * @param {String} key
- * @param {String} value
- * @returns {void}
- */
- static setItem(key, value) {
- if (_GM_setValue) {
- _GM_setValue(key, value);
- } else {
- localStorage.setItem(key, value);
- }
- }
- }
- const useStore = defineStore("store", {
- state() {
- return {
- // ####################
- // 元件相關狀態
- // ####################
- isDarkMode: false,
- isToolbarsShow: false,
- isPopupShow: {
- sheet: false,
- chord: false,
- font: false,
- settings: false,
- menu: false,
- // 選單內功能
- hotkey: false
- },
- // ####################
- // 偏好設定相關狀態
- // ####################
- agreeToArchiveSheet: true,
- // ####################
- // 譜面相關狀態
- // ####################
- transpose: 0,
- /** 在 `StoreHandler` 裡賦值 */
- originalCapo: 0,
- /** 在 `StoreHandler` 裡賦值,HTML 格式 */
- originalKey: "",
- /** `font-size` 的變化值 */
- fontSizeDelta: 0,
- /** 在 `StoreHandler` 裡賦值,單位為 px */
- originalFontSize: 0,
- /** 在 `StoreHandler` 裡賦值,單位為 px */
- originalLineHeight: 0
- };
- },
- persist: {
- key: "plus91-preferences",
- storage: MonkeyStorage,
- deserialize: zipson.parse,
- serialize: zipson.stringify,
- paths: ["isDarkMode", "agreeToArchiveSheet"],
- beforeRestore() {
- console.log("[91 Plus] 讀取偏好設置中");
- },
- afterRestore() {
- console.log("[91 Plus] 偏好設置讀取完畢");
- },
- debug: true
- },
- getters: {
- currentCapo() {
- return this.originalCapo + this.transpose;
- },
- currentKey() {
- return new Chord(this.originalKey).transpose(-this.transpose).toFormattedString();
- }
- },
- actions: {
- toggleToolbars() {
- if (this.isToolbarsShow) {
- this.closePopups();
- } else {
- this.isPopupShow.sheet = true;
- }
- this.isToolbarsShow = !this.isToolbarsShow;
- },
- closePopups() {
- for (const popup in this.isPopupShow) {
- this.isPopupShow[popup] = false;
- }
- },
- /** @param {'sheet'|'chord'|'font'|'settings'|'menu'|'hotkey'} name */
- togglePopup(name) {
- for (const popup in this.isPopupShow) {
- if (popup === name) {
- this.isPopupShow[popup] = !this.isPopupShow[popup];
- } else {
- this.isPopupShow[popup] = false;
- }
- }
- },
- plusTranspose(numberToPlus) {
- let newTranspose = this.transpose + numberToPlus;
- const newCapo = this.originalCapo + newTranspose;
- if (newCapo === 12 || newCapo === -12) {
- newTranspose = -this.originalCapo;
- }
- this.transpose = newTranspose;
- }
- }
- });
- const _hoisted_1$e = ["active"];
- const _sfc_main$f = {
- __name: "BootstrapIcon",
- props: {
- icon: {
- type: String,
- required: true
- },
- color: {
- type: String,
- default: "whitesmoke"
- },
- size: {
- type: String,
- default: "1rem"
- },
- stroke: {
- type: String,
- default: "0"
- },
- active: {
- type: Boolean,
- default: false
- }
- },
- setup(__props) {
- vue.useCssVars((_ctx) => ({
- "2b53b793": __props.color,
- "4bbf91d1": __props.size,
- "5ab9f408": __props.stroke
- }));
- const props = __props;
- return (_ctx, _cache) => {
- return vue.openBlock(), vue.createElementBlock("i", {
- class: vue.normalizeClass(`bi bi-${props.icon}`),
- active: props.active
- }, null, 10, _hoisted_1$e);
- };
- }
- };
- const BootstrapIcon = /* @__PURE__ */ _export_sfc(_sfc_main$f, [["__scopeId", "data-v-6e52047a"]]);
- const _hoisted_1$d = { class: "toolbar-icon" };
- const _hoisted_2$c = { class: "toolbar-icon-text" };
- const _sfc_main$e = {
- __name: "ToolbarIcon",
- props: {
- icon: {
- type: String,
- required: true
- },
- text: {
- type: String,
- required: true
- },
- stroke: {
- type: String,
- default: "0"
- },
- active: {
- type: Boolean,
- default: false
- },
- color: {
- type: String,
- default: "whitesmoke"
- }
- },
- setup(__props) {
- vue.useCssVars((_ctx) => ({
- "52bb32ad": __props.color
- }));
- const props = __props;
- return (_ctx, _cache) => {
- return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$d, [
- vue.createVNode(BootstrapIcon, {
- size: "1.3rem",
- icon: props.icon,
- color: props.color,
- stroke: props.stroke,
- active: props.active
- }, null, 8, ["icon", "color", "stroke", "active"]),
- vue.createElementVNode("div", _hoisted_2$c, vue.toDisplayString(props.text), 1)
- ]);
- };
- }
- };
- const ToolbarIcon = /* @__PURE__ */ _export_sfc(_sfc_main$e, [["__scopeId", "data-v-3dbcd695"]]);
- const _hoisted_1$c = { class: "adjust-widget" };
- const _hoisted_2$b = ["disabled"];
- const _hoisted_3$5 = ["disabled"];
- const _hoisted_4$3 = ["disabled"];
- const _sfc_main$d = {
- __name: "AdjustWidget",
- props: {
- iconLeft: {
- type: String,
- default: "caret-left-fill"
- },
- iconRight: {
- type: String,
- default: "caret-right-fill"
- },
- disabledLeft: {
- type: Boolean,
- default: false
- },
- disabledMiddle: {
- type: Boolean,
- default: false
- },
- disabledRight: {
- type: Boolean,
- default: false
- },
- color: {
- type: String,
- default: "#444"
- },
- size: {
- type: String,
- default: "1.25rem"
- },
- onclickLeft: Function,
- onclickMiddle: Function,
- onclickRight: Function
- },
- setup(__props) {
- vue.useCssVars((_ctx) => ({
- "13e75dc8": __props.color,
- "10392328": __props.size
- }));
- const props = __props;
- return (_ctx, _cache) => {
- return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$c, [
- vue.createElementVNode("button", {
- class: "adjust-button adjust-button-left",
- onClick: _cache[0] || (_cache[0] = (...args) => props.onclickLeft && props.onclickLeft(...args)),
- disabled: props.disabledLeft
- }, [
- vue.createVNode(BootstrapIcon, {
- icon: props.iconLeft,
- color: props.color,
- size: props.size
- }, null, 8, ["icon", "color", "size"])
- ], 8, _hoisted_2$b),
- vue.createElementVNode("button", {
- class: "adjust-button adjust-button-middle",
- onClick: _cache[1] || (_cache[1] = (...args) => props.onclickMiddle && props.onclickMiddle(...args)),
- disabled: props.disabledMiddle
- }, [
- vue.renderSlot(_ctx.$slots, "default", {}, void 0, true)
- ], 8, _hoisted_3$5),
- vue.createElementVNode("button", {
- class: "adjust-button adjust-button-right",
- onClick: _cache[2] || (_cache[2] = (...args) => props.onclickRight && props.onclickRight(...args)),
- disabled: props.disabledRight
- }, [
- vue.createVNode(BootstrapIcon, {
- icon: props.iconRight,
- color: props.color,
- size: props.size
- }, null, 8, ["icon", "color", "size"])
- ], 8, _hoisted_4$3)
- ]);
- };
- }
- };
- const AdjustWidget = /* @__PURE__ */ _export_sfc(_sfc_main$d, [["__scopeId", "data-v-cb8ab81d"]]);
- class ChordSheetElement {
- /** @param {HTMLElement} chordSheetElement */
- constructor(chordSheetElement) {
- /** @param {NodeList} nodeList */
- __privateAdd(this, _unformat);
- this.chordSheetElement = chordSheetElement;
- }
- /**
- * 將 Header 和譜上的和弦移調,並實質修改於 DOM
- * @param {number} delta 相對於當前調的移調值
- */
- static transposeSheet(delta) {
- $("#tone_z .tf").each(function() {
- const chord = new Chord($(this).text());
- const newChordHTML = chord.transpose(-delta).toFormattedString();
- $(this).html(newChordHTML);
- });
- }
- /** @returns {ChordSheetElement} */
- formatUnderlines() {
- const underlineEl = this.chordSheetElement.querySelectorAll("u");
- const doubleUnderlineEl = this.chordSheetElement.querySelectorAll("abbr");
- underlineEl.forEach((el) => {
- el.innerText = `{_${el.innerText}_}`;
- });
- doubleUnderlineEl.forEach((el) => {
- el.innerText = `{=${el.innerText}=}`;
- });
- return this;
- }
- /** @returns {ChordSheetElement} */
- unformatUnderlines() {
- const underlineEl = this.chordSheetElement.querySelectorAll("u");
- const doubleUnderlineEl = this.chordSheetElement.querySelectorAll("abbr");
- __privateMethod(this, _unformat, unformat_fn).call(this, underlineEl);
- __privateMethod(this, _unformat, unformat_fn).call(this, doubleUnderlineEl);
- return this;
- }
- }
- _unformat = new WeakSet();
- unformat_fn = function(nodeList) {
- nodeList.forEach((el) => {
- el.innerHTML = el.innerText.replaceAll(/{_|{=|=}|_}/g, "").replaceAll(
- /[a-zA-Z0-9#/]+/g,
- /* html */
- `<span class="tf">$&</span>`
- );
- });
- };
- class ChordSheetDocument {
- constructor() {
- this.el = {
- mtitle: document.getElementById("mtitle"),
- tkinfo: document.querySelector(".tkinfo"),
- capoSelect: document.querySelector(".capo .select"),
- tinfo: document.querySelector(".tinfo"),
- tone_z: document.getElementById("tone_z")
- };
- }
- getId() {
- const urlParams = new URLSearchParams(window.location.search);
- return Number(urlParams.get("id"));
- }
- getTitle() {
- return this.el.mtitle.innerText.trim();
- }
- getKey() {
- var _a;
- const match = (_a = this.el.tkinfo) == null ? void 0 : _a.innerText.match(new RegExp("(?<=原調:)\\w*"));
- return match ? match[0].trim() : "";
- }
- getPlay() {
- var _a;
- const match = (_a = this.el.capoSelect) == null ? void 0 : _a.innerText.split(/\s*\/\s*/);
- return match ? match[1].trim() : "";
- }
- getCapo() {
- var _a;
- const match = (_a = this.el.capoSelect) == null ? void 0 : _a.innerText.split(/\s*\/\s*/);
- return match ? Number(match[0]) : 0;
- }
- getSinger() {
- var _a;
- const match = (_a = this.el.tinfo) == null ? void 0 : _a.innerText.match(new RegExp("(?<=演唱:).*(?=\\n|$)"));
- return match ? match[0].trim() : "";
- }
- getComposer() {
- var _a;
- const match = (_a = this.el.tinfo) == null ? void 0 : _a.innerText.match(new RegExp("(?<=曲:).*?(?=詞:|$)"));
- return match ? match[0].trim() : "";
- }
- getLyricist() {
- var _a;
- const match = (_a = this.el.tinfo) == null ? void 0 : _a.innerText.match(new RegExp("(?<=詞:).*?(?=曲:|$)"));
- return match ? match[0].trim() : "";
- }
- getBpm() {
- var _a;
- const match = (_a = this.el.tkinfo) == null ? void 0 : _a.innerText.match(/\d+/);
- return match ? Number(match[0]) : 0;
- }
- getSheetText() {
- const formattedChordSheet = this.el.tone_z.innerText.replaceAll(/\s+?\n/g, "\n").replaceAll("\n\n", "\n").trim().replaceAll(/\s+/g, (match) => {
- return `{%${match.length}%}`;
- });
- return formattedChordSheet;
- }
- }
- class StoreHandler {
- constructor() {
- /** 當 `#store.transpose` 變動時,將譜面上的和弦進行移調 */
- __privateAdd(this, _watchTranspose);
- __privateAdd(this, _watchFontSize);
- // 命 `#store` 為私有屬性,在建立實例時再賦值,避免衝突
- __privateAdd(this, _store, void 0);
- __privateSet(this, _store, useStore());
- }
- initState() {
- const capoSelected = $(".capo .select").eq(0).text().trim();
- const originalCapo = +capoSelected.split(/\s*\/\s*/)[0];
- const originalKey = capoSelected.split(/\s*\/\s*/)[1];
- __privateGet(this, _store).originalCapo = originalCapo;
- __privateGet(this, _store).originalKey = originalKey;
- const fontSize = +$("#tone_z").css("font-size").match(/^\d+/)[0];
- const lineHeight = +$("#tone_z > p").css("line-height").match(/^\d+/)[0];
- __privateGet(this, _store).originalFontSize = fontSize;
- __privateGet(this, _store).originalLineHeight = lineHeight;
- const params = getQueryParams();
- if (params.transpose) {
- __privateGet(this, _store).transpose = params.transpose;
- }
- }
- start() {
- __privateMethod(this, _watchTranspose, watchTranspose_fn).call(this);
- __privateMethod(this, _watchFontSize, watchFontSize_fn).call(this);
- return this;
- }
- static handleKeydown(key) {
- const store = useStore();
- switch (key) {
- case " ": {
- store.toggleToolbars();
- break;
- }
- case "/": {
- if (!store.isToolbarsShow) {
- store.toggleToolbars();
- store.closePopups();
- }
- setTimeout(() => {
- $("#plus91-header input").get(0).focus();
- }, 0);
- break;
- }
- case "Escape": {
- if (store.isToolbarsShow) {
- store.toggleToolbars();
- }
- break;
- }
- }
- if (store.isPopupShow.sheet) {
- switch (key) {
- case "ArrowLeft": {
- store.plusTranspose(-1);
- break;
- }
- case "ArrowRight": {
- store.plusTranspose(1);
- break;
- }
- case "ArrowDown": {
- store.transpose = 0;
- break;
- }
- }
- }
- }
- }
- _store = new WeakMap();
- _watchTranspose = new WeakSet();
- watchTranspose_fn = function() {
- vue.watch(() => {
- return __privateGet(this, _store).transpose;
- }, (newValue, oldValue) => {
- ChordSheetElement.transposeSheet((newValue - oldValue) % 12);
- });
- };
- _watchFontSize = new WeakSet();
- watchFontSize_fn = function() {
- vue.watch(() => {
- return __privateGet(this, _store).fontSizeDelta;
- }, (newValue) => {
- const oFontSize = __privateGet(this, _store).originalFontSize;
- const oLineHeight = __privateGet(this, _store).originalLineHeight;
- $("#tone_z").css("font-size", `${oFontSize + newValue}px`);
- $("#tone_z > p").css("line-height", `${oLineHeight + newValue}px`);
- });
- };
- var LIBVERSION = "2.0.0-beta.3", EMPTY = "", UNKNOWN = "?", FUNC_TYPE = "function", UNDEF_TYPE = "undefined", OBJ_TYPE = "object", STR_TYPE = "string", MAJOR = "major", MODEL = "model", NAME = "name", TYPE = "type", VENDOR = "vendor", VERSION = "version", ARCHITECTURE = "architecture", CONSOLE = "console", MOBILE = "mobile", TABLET = "tablet", SMARTTV = "smarttv", WEARABLE = "wearable", XR = "xr", EMBEDDED = "embedded", USER_AGENT = "user-agent", UA_MAX_LENGTH = 500, BRANDS = "brands", FORMFACTORS = "formFactors", FULLVERLIST = "fullVersionList", PLATFORM = "platform", PLATFORMVER = "platformVersion", BITNESS = "bitness", CH_HEADER = "sec-ch-ua", CH_HEADER_FULL_VER_LIST = CH_HEADER + "-full-version-list", CH_HEADER_ARCH = CH_HEADER + "-arch", CH_HEADER_BITNESS = CH_HEADER + "-" + BITNESS, CH_HEADER_FORM_FACTORS = CH_HEADER + "-form-factors", CH_HEADER_MOBILE = CH_HEADER + "-" + MOBILE, CH_HEADER_MODEL = CH_HEADER + "-" + MODEL, CH_HEADER_PLATFORM = CH_HEADER + "-" + PLATFORM, CH_HEADER_PLATFORM_VER = CH_HEADER_PLATFORM + "-version", CH_ALL_VALUES = [BRANDS, FULLVERLIST, MOBILE, MODEL, PLATFORM, PLATFORMVER, ARCHITECTURE, FORMFACTORS, BITNESS], UA_BROWSER = "browser", UA_CPU = "cpu", UA_DEVICE = "device", UA_ENGINE = "engine", UA_OS = "os", UA_RESULT = "result", AMAZON = "Amazon", APPLE = "Apple", ASUS = "ASUS", BLACKBERRY = "BlackBerry", GOOGLE = "Google", HUAWEI = "Huawei", LENOVO = "Lenovo", LG = "LG", MICROSOFT = "Microsoft", MOTOROLA = "Motorola", SAMSUNG = "Samsung", SHARP = "Sharp", SONY = "Sony", XIAOMI = "Xiaomi", ZEBRA = "Zebra", PREFIX_MOBILE = "Mobile ", SUFFIX_BROWSER = " Browser", CHROME = "Chrome", EDGE = "Edge", FIREFOX = "Firefox", OPERA = "Opera", FACEBOOK = "Facebook", SOGOU = "Sogou", WINDOWS = "Windows";
- var isWindow = typeof window !== UNDEF_TYPE, NAVIGATOR = isWindow && window.navigator ? window.navigator : void 0, NAVIGATOR_UADATA = NAVIGATOR && NAVIGATOR.userAgentData ? NAVIGATOR.userAgentData : void 0;
- var extend = function(defaultRgx, extensions) {
- var mergedRgx = {};
- var extraRgx = extensions;
- if (!isExtensions(extensions)) {
- extraRgx = {};
- for (var i in extensions) {
- for (var j in extensions[i]) {
- extraRgx[j] = extensions[i][j].concat(extraRgx[j] ? extraRgx[j] : []);
- }
- }
- }
- for (var k in defaultRgx) {
- mergedRgx[k] = extraRgx[k] && extraRgx[k].length % 2 === 0 ? extraRgx[k].concat(defaultRgx[k]) : defaultRgx[k];
- }
- return mergedRgx;
- }, enumerize = function(arr) {
- var enums = {};
- for (var i = 0; i < arr.length; i++) {
- enums[arr[i].toUpperCase()] = arr[i];
- }
- return enums;
- }, has = function(str1, str2) {
- if (typeof str1 === OBJ_TYPE && str1.length > 0) {
- for (var i in str1) {
- if (lowerize(str1[i]) == lowerize(str2))
- return true;
- }
- return false;
- }
- return isString(str1) ? lowerize(str2).indexOf(lowerize(str1)) !== -1 : false;
- }, isExtensions = function(obj, deep) {
- for (var prop in obj) {
- return /^(browser|cpu|device|engine|os)$/.test(prop) || (deep ? isExtensions(obj[prop]) : false);
- }
- }, isString = function(val) {
- return typeof val === STR_TYPE;
- }, itemListToArray = function(header) {
- if (!header)
- return void 0;
- var arr = [];
- var tokens = strip(/\\?\"/g, header).split(",");
- for (var i = 0; i < tokens.length; i++) {
- if (tokens[i].indexOf(";") > -1) {
- var token = trim(tokens[i]).split(";v=");
- arr[i] = { brand: token[0], version: token[1] };
- } else {
- arr[i] = trim(tokens[i]);
- }
- }
- return arr;
- }, lowerize = function(str) {
- return isString(str) ? str.toLowerCase() : str;
- }, majorize = function(version) {
- return isString(version) ? strip(/[^\d\.]/g, version).split(".")[0] : void 0;
- }, setProps = function(arr) {
- for (var i in arr) {
- var propName = arr[i];
- if (typeof propName == OBJ_TYPE && propName.length == 2) {
- this[propName[0]] = propName[1];
- } else {
- this[propName] = void 0;
- }
- }
- return this;
- }, strip = function(pattern, str) {
- return isString(str) ? str.replace(pattern, EMPTY) : str;
- }, stripQuotes = function(str) {
- return strip(/\\?\"/g, str);
- }, trim = function(str, len) {
- if (isString(str)) {
- str = strip(/^\s\s*/, str);
- return typeof len === UNDEF_TYPE ? str : str.substring(0, UA_MAX_LENGTH);
- }
- };
- var rgxMapper = function(ua, arrays) {
- if (!ua || !arrays)
- return;
- var i = 0, j, k, p, q, matches, match;
- while (i < arrays.length && !matches) {
- var regex = arrays[i], props = arrays[i + 1];
- j = k = 0;
- while (j < regex.length && !matches) {
- if (!regex[j]) {
- break;
- }
- matches = regex[j++].exec(ua);
- if (!!matches) {
- for (p = 0; p < props.length; p++) {
- match = matches[++k];
- q = props[p];
- if (typeof q === OBJ_TYPE && q.length > 0) {
- if (q.length === 2) {
- if (typeof q[1] == FUNC_TYPE) {
- this[q[0]] = q[1].call(this, match);
- } else {
- this[q[0]] = q[1];
- }
- } else if (q.length === 3) {
- if (typeof q[1] === FUNC_TYPE && !(q[1].exec && q[1].test)) {
- this[q[0]] = match ? q[1].call(this, match, q[2]) : void 0;
- } else {
- this[q[0]] = match ? match.replace(q[1], q[2]) : void 0;
- }
- } else if (q.length === 4) {
- this[q[0]] = match ? q[3].call(this, match.replace(q[1], q[2])) : void 0;
- }
- } else {
- this[q] = match ? match : void 0;
- }
- }
- }
- }
- i += 2;
- }
- }, strMapper = function(str, map) {
- for (var i in map) {
- if (typeof map[i] === OBJ_TYPE && map[i].length > 0) {
- for (var j = 0; j < map[i].length; j++) {
- if (has(map[i][j], str)) {
- return i === UNKNOWN ? void 0 : i;
- }
- }
- } else if (has(map[i], str)) {
- return i === UNKNOWN ? void 0 : i;
- }
- }
- return map.hasOwnProperty("*") ? map["*"] : str;
- };
- var windowsVersionMap = {
- "ME": "4.90",
- "NT 3.11": "NT3.51",
- "NT 4.0": "NT4.0",
- "2000": "NT 5.0",
- "XP": ["NT 5.1", "NT 5.2"],
- "Vista": "NT 6.0",
- "7": "NT 6.1",
- "8": "NT 6.2",
- "8.1": "NT 6.3",
- "10": ["NT 6.4", "NT 10.0"],
- "RT": "ARM"
- }, formFactorsMap = {
- "embedded": "Automotive",
- "mobile": "Mobile",
- "tablet": ["Tablet", "EInk"],
- "smarttv": "TV",
- "wearable": "Watch",
- "xr": ["VR", "XR"],
- "?": ["Desktop", "Unknown"],
- "*": void 0
- };
- var defaultRegexes = {
- browser: [
- [
- // Most common regardless engine
- /\b(?:crmo|crios)\/([\w\.]+)/i
- // Chrome for Android/iOS
- ],
- [VERSION, [NAME, PREFIX_MOBILE + "Chrome"]],
- [
- /edg(?:e|ios|a)?\/([\w\.]+)/i
- // Microsoft Edge
- ],
- [VERSION, [NAME, "Edge"]],
- [
- // Presto based
- /(opera mini)\/([-\w\.]+)/i,
- // Opera Mini
- /(opera [mobiletab]{3,6})\b.+version\/([-\w\.]+)/i,
- // Opera Mobi/Tablet
- /(opera)(?:.+version\/|[\/ ]+)([\w\.]+)/i
- // Opera
- ],
- [NAME, VERSION],
- [
- /opios[\/ ]+([\w\.]+)/i
- // Opera mini on iphone >= 8.0
- ],
- [VERSION, [NAME, OPERA + " Mini"]],
- [
- /\bop(?:rg)?x\/([\w\.]+)/i
- // Opera GX
- ],
- [VERSION, [NAME, OPERA + " GX"]],
- [
- /\bopr\/([\w\.]+)/i
- // Opera Webkit
- ],
- [VERSION, [NAME, OPERA]],
- [
- // Mixed
- /\bb[ai]*d(?:uhd|[ub]*[aekoprswx]{5,6})[\/ ]?([\w\.]+)/i
- // Baidu
- ],
- [VERSION, [NAME, "Baidu"]],
- [
- /(kindle)\/([\w\.]+)/i,
- // Kindle
- /(lunascape|maxthon|netfront|jasmine|blazer|sleipnir)[\/ ]?([\w\.]*)/i,
- // Lunascape/Maxthon/Netfront/Jasmine/Blazer/Sleipnir
- // Trident based
- /(avant|iemobile|slim)\s?(?:browser)?[\/ ]?([\w\.]*)/i,
- // Avant/IEMobile/SlimBrowser
- /(?:ms|\()(ie) ([\w\.]+)/i,
- // Internet Explorer
- // Webkit/KHTML based // Flock/RockMelt/Midori/Epiphany/Silk/Skyfire/Bolt/Iron/Iridium/PhantomJS/Bowser/QupZilla/Falkon
- /(flock|rockmelt|midori|epiphany|silk|skyfire|ovibrowser|bolt|iron|vivaldi|iridium|phantomjs|bowser|quark|qupzilla|falkon|rekonq|puffin|brave|whale(?!.+naver)|qqbrowserlite|duckduckgo|klar)\/([-\w\.]+)/i,
- // Rekonq/Puffin/Brave/Whale/QQBrowserLite/QQ//Vivaldi/DuckDuckGo/Klar
- /(heytap|ovi)browser\/([\d\.]+)/i,
- // HeyTap/Ovi
- /(weibo)__([\d\.]+)/i
- ],
- [NAME, VERSION],
- [
- /\bddg\/([\w\.]+)/i
- // DuckDuckGo
- ],
- [VERSION, [NAME, "DuckDuckGo"]],
- [
- /(?:\buc? ?browser|(?:juc.+)ucweb)[\/ ]?([\w\.]+)/i
- // UCBrowser
- ],
- [VERSION, [NAME, "UCBrowser"]],
- [
- /microm.+\bqbcore\/([\w\.]+)/i,
- // WeChat Desktop for Windows Built-in Browser
- /\bqbcore\/([\w\.]+).+microm/i,
- /micromessenger\/([\w\.]+)/i
- ],
- [VERSION, [NAME, "WeChat"]],
- [
- /konqueror\/([\w\.]+)/i
- // Konqueror
- ],
- [VERSION, [NAME, "Konqueror"]],
- [
- /trident.+rv[: ]([\w\.]{1,9})\b.+like gecko/i
- // IE11
- ],
- [VERSION, [NAME, "IE"]],
- [
- /ya(?:search)?browser\/([\w\.]+)/i
- // Yandex
- ],
- [VERSION, [NAME, "Yandex"]],
- [
- /slbrowser\/([\w\.]+)/i
- // Smart Lenovo Browser
- ],
- [VERSION, [NAME, "Smart " + LENOVO + SUFFIX_BROWSER]],
- [
- /(avast|avg)\/([\w\.]+)/i
- // Avast/AVG Secure Browser
- ],
- [[NAME, /(.+)/, "$1 Secure" + SUFFIX_BROWSER], VERSION],
- [
- /\bfocus\/([\w\.]+)/i
- // Firefox Focus
- ],
- [VERSION, [NAME, FIREFOX + " Focus"]],
- [
- /\bopt\/([\w\.]+)/i
- // Opera Touch
- ],
- [VERSION, [NAME, OPERA + " Touch"]],
- [
- /coc_coc\w+\/([\w\.]+)/i
- // Coc Coc Browser
- ],
- [VERSION, [NAME, "Coc Coc"]],
- [
- /dolfin\/([\w\.]+)/i
- // Dolphin
- ],
- [VERSION, [NAME, "Dolphin"]],
- [
- /coast\/([\w\.]+)/i
- // Opera Coast
- ],
- [VERSION, [NAME, OPERA + " Coast"]],
- [
- /miuibrowser\/([\w\.]+)/i
- // MIUI Browser
- ],
- [VERSION, [NAME, "MIUI" + SUFFIX_BROWSER]],
- [
- /fxios\/([\w\.-]+)/i
- // Firefox for iOS
- ],
- [VERSION, [NAME, PREFIX_MOBILE + FIREFOX]],
- [
- /\bqihu|(qi?ho?o?|360)browser/i
- // 360
- ],
- [[NAME, "360" + SUFFIX_BROWSER]],
- [
- /\b(qq)\/([\w\.]+)/i
- ],
- [[NAME, /(.+)/, "$1Browser"], VERSION],
- [
- /(oculus|sailfish|huawei|vivo|pico)browser\/([\w\.]+)/i
- ],
- [[NAME, /(.+)/, "$1" + SUFFIX_BROWSER], VERSION],
- [
- // Oculus/Sailfish/HuaweiBrowser/VivoBrowser/PicoBrowser
- /samsungbrowser\/([\w\.]+)/i
- // Samsung Internet
- ],
- [VERSION, [NAME, SAMSUNG + " Internet"]],
- [
- /(comodo_dragon)\/([\w\.]+)/i
- // Comodo Dragon
- ],
- [[NAME, /_/g, " "], VERSION],
- [
- /metasr[\/ ]?([\d\.]+)/i
- // Sogou Explorer
- ],
- [VERSION, [NAME, SOGOU + " Explorer"]],
- [
- /(sogou)mo\w+\/([\d\.]+)/i
- // Sogou Mobile
- ],
- [[NAME, SOGOU + " Mobile"], VERSION],
- [
- /(electron)\/([\w\.]+) safari/i,
- // Electron-based App
- /(tesla)(?: qtcarbrowser|\/(20\d\d\.[-\w\.]+))/i,
- // Tesla
- /m?(qqbrowser|2345Explorer)[\/ ]?([\w\.]+)/i
- // QQBrowser/2345 Browser
- ],
- [NAME, VERSION],
- [
- /(lbbrowser|rekonq)/i,
- // LieBao Browser/Rekonq
- /\[(linkedin)app\]/i
- // LinkedIn App for iOS & Android
- ],
- [NAME],
- [
- // WebView
- /((?:fban\/fbios|fb_iab\/fb4a)(?!.+fbav)|;fbav\/([\w\.]+);)/i
- // Facebook App for iOS & Android
- ],
- [[NAME, FACEBOOK], VERSION],
- [
- /(Klarna)\/([\w\.]+)/i,
- // Klarna Shopping Browser for iOS & Android
- /(kakao(?:talk|story))[\/ ]([\w\.]+)/i,
- // Kakao App
- /(naver)\(.*?(\d+\.[\w\.]+).*\)/i,
- // Naver InApp
- /safari (line)\/([\w\.]+)/i,
- // Line App for iOS
- /\b(line)\/([\w\.]+)\/iab/i,
- // Line App for Android
- /(alipay)client\/([\w\.]+)/i,
- // Alipay
- /(twitter)(?:and| f.+e\/([\w\.]+))/i,
- /(chromium|instagram|snapchat)[\/ ]([-\w\.]+)/i
- // Chromium/Instagram/Snapchat
- ],
- [NAME, VERSION],
- [
- /\bgsa\/([\w\.]+) .*safari\//i
- // Google Search Appliance on iOS
- ],
- [VERSION, [NAME, "GSA"]],
- [
- /musical_ly(?:.+app_?version\/|_)([\w\.]+)/i
- // TikTok
- ],
- [VERSION, [NAME, "TikTok"]],
- [
- /headlesschrome(?:\/([\w\.]+)| )/i
- // Chrome Headless
- ],
- [VERSION, [NAME, CHROME + " Headless"]],
- [
- / wv\).+(chrome)\/([\w\.]+)/i
- // Chrome WebView
- ],
- [[NAME, CHROME + " WebView"], VERSION],
- [
- /droid.+ version\/([\w\.]+)\b.+(?:mobile safari|safari)/i
- // Android Browser
- ],
- [VERSION, [NAME, "Android" + SUFFIX_BROWSER]],
- [
- /chrome\/([\w\.]+) mobile/i
- // Chrome Mobile
- ],
- [VERSION, [NAME, PREFIX_MOBILE + "Chrome"]],
- [
- /(chrome|omniweb|arora|[tizenoka]{5} ?browser)\/v?([\w\.]+)/i
- // Chrome/OmniWeb/Arora/Tizen/Nokia
- ],
- [NAME, VERSION],
- [
- /version\/([\w\.\,]+) .*mobile(?:\/\w+ | ?)safari/i
- // Safari Mobile
- ],
- [VERSION, [NAME, PREFIX_MOBILE + "Safari"]],
- [
- /iphone .*mobile(?:\/\w+ | ?)safari/i
- ],
- [[NAME, PREFIX_MOBILE + "Safari"]],
- [
- /version\/([\w\.\,]+) .*(safari)/i
- // Safari
- ],
- [VERSION, NAME],
- [
- /webkit.+?(mobile ?safari|safari)(\/[\w\.]+)/i
- // Safari < 3.0
- ],
- [NAME, [VERSION, "1"]],
- [
- /(webkit|khtml)\/([\w\.]+)/i
- ],
- [NAME, VERSION],
- [
- // Gecko based
- /(?:mobile|tablet);.*(firefox)\/([\w\.-]+)/i
- // Firefox Mobile
- ],
- [[NAME, PREFIX_MOBILE + FIREFOX], VERSION],
- [
- /(navigator|netscape\d?)\/([-\w\.]+)/i
- // Netscape
- ],
- [[NAME, "Netscape"], VERSION],
- [
- /(wolvic)\/([\w\.]+)/i
- // Wolvic
- ],
- [NAME, VERSION],
- [
- /mobile vr; rv:([\w\.]+)\).+firefox/i
- // Firefox Reality
- ],
- [VERSION, [NAME, FIREFOX + " Reality"]],
- [
- /ekiohf.+(flow)\/([\w\.]+)/i,
- // Flow
- /(swiftfox)/i,
- // Swiftfox
- /(icedragon|iceweasel|camino|chimera|fennec|maemo browser|minimo|conkeror)[\/ ]?([\w\.\+]+)/i,
- // IceDragon/Iceweasel/Camino/Chimera/Fennec/Maemo/Minimo/Conkeror
- /(seamonkey|k-meleon|icecat|iceape|firebird|phoenix|palemoon|basilisk|waterfox)\/([-\w\.]+)$/i,
- // Firefox/SeaMonkey/K-Meleon/IceCat/IceApe/Firebird/Phoenix
- /(firefox)\/([\w\.]+)/i,
- // Other Firefox-based
- /(mozilla)\/([\w\.]+) .+rv\:.+gecko\/\d+/i,
- // Mozilla
- // Other
- /(polaris|lynx|dillo|icab|doris|amaya|w3m|netsurf|obigo|mosaic|(?:go|ice|up)[\. ]?browser)[-\/ ]?v?([\w\.]+)/i,
- // Polaris/Lynx/Dillo/iCab/Doris/Amaya/w3m/NetSurf/Obigo/Mosaic/Go/ICE/UP.Browser
- /(links) \(([\w\.]+)/i
- // Links
- ],
- [NAME, [VERSION, /_/g, "."]],
- [
- /(cobalt)\/([\w\.]+)/i
- // Cobalt
- ],
- [NAME, [VERSION, /[^\d\.]+./, EMPTY]]
- ],
- cpu: [
- [
- /\b(?:(amd|x|x86[-_]?|wow|win)64)\b/i
- // AMD64 (x64)
- ],
- [[ARCHITECTURE, "amd64"]],
- [
- /(ia32(?=;))/i,
- // IA32 (quicktime)
- /((?:i[346]|x)86)[;\)]/i
- // IA32 (x86)
- ],
- [[ARCHITECTURE, "ia32"]],
- [
- /\b(aarch64|arm(v?8e?l?|_?64))\b/i
- // ARM64
- ],
- [[ARCHITECTURE, "arm64"]],
- [
- /\b(arm(?:v[67])?ht?n?[fl]p?)\b/i
- // ARMHF
- ],
- [[ARCHITECTURE, "armhf"]],
- [
- // PocketPC mistakenly identified as PowerPC
- /windows (ce|mobile); ppc;/i
- ],
- [[ARCHITECTURE, "arm"]],
- [
- /((?:ppc|powerpc)(?:64)?)(?: mac|;|\))/i
- // PowerPC
- ],
- [[ARCHITECTURE, /ower/, EMPTY, lowerize]],
- [
- /(sun4\w)[;\)]/i
- // SPARC
- ],
- [[ARCHITECTURE, "sparc"]],
- [
- /((?:avr32|ia64(?=;))|68k(?=\))|\barm(?=v(?:[1-7]|[5-7]1)l?|;|eabi)|(?=atmel )avr|(?:irix|mips|sparc)(?:64)?\b|pa-risc)/i
- // IA64, 68K, ARM/64, AVR/32, IRIX/64, MIPS/64, SPARC/64, PA-RISC
- ],
- [[ARCHITECTURE, lowerize]]
- ],
- device: [
- [
- //////////////////////////
- // MOBILES & TABLETS
- /////////////////////////
- // Samsung
- /\b(sch-i[89]0\d|shw-m380s|sm-[ptx]\w{2,4}|gt-[pn]\d{2,4}|sgh-t8[56]9|nexus 10)/i
- ],
- [MODEL, [VENDOR, SAMSUNG], [TYPE, TABLET]],
- [
- /\b((?:s[cgp]h|gt|sm)-\w+|sc[g-]?[\d]+a?|galaxy nexus)/i,
- /samsung[- ]([-\w]+)/i,
- /sec-(sgh\w+)/i
- ],
- [MODEL, [VENDOR, SAMSUNG], [TYPE, MOBILE]],
- [
- // Apple
- /(?:\/|\()(ip(?:hone|od)[\w, ]*)(?:\/|;)/i
- // iPod/iPhone
- ],
- [MODEL, [VENDOR, APPLE], [TYPE, MOBILE]],
- [
- /\((ipad);[-\w\),; ]+apple/i,
- // iPad
- /applecoremedia\/[\w\.]+ \((ipad)/i,
- /\b(ipad)\d\d?,\d\d?[;\]].+ios/i
- ],
- [MODEL, [VENDOR, APPLE], [TYPE, TABLET]],
- [
- /(macintosh);/i
- ],
- [MODEL, [VENDOR, APPLE]],
- [
- // Sharp
- /\b(sh-?[altvz]?\d\d[a-ekm]?)/i
- ],
- [MODEL, [VENDOR, SHARP], [TYPE, MOBILE]],
- [
- // Huawei
- /\b((?:ag[rs][23]?|bah2?|sht?|btv)-a?[lw]\d{2})\b(?!.+d\/s)/i
- ],
- [MODEL, [VENDOR, HUAWEI], [TYPE, TABLET]],
- [
- /(?:huawei|honor)([-\w ]+)[;\)]/i,
- /\b(nexus 6p|\w{2,4}e?-[atu]?[ln][\dx][012359c][adn]?)\b(?!.+d\/s)/i
- ],
- [MODEL, [VENDOR, HUAWEI], [TYPE, MOBILE]],
- [
- // Xiaomi
- /\b(poco[\w ]+|m2\d{3}j\d\d[a-z]{2})(?: bui|\))/i,
- // Xiaomi POCO
- /\b; (\w+) build\/hm\1/i,
- // Xiaomi Hongmi 'numeric' models
- /\b(hm[-_ ]?note?[_ ]?(?:\d\w)?) bui/i,
- // Xiaomi Hongmi
- /\b(redmi[\-_ ]?(?:note|k)?[\w_ ]+)(?: bui|\))/i,
- // Xiaomi Redmi
- /oid[^\)]+; (m?[12][0-389][01]\w{3,6}[c-y])( bui|; wv|\))/i,
- // Xiaomi Redmi 'numeric' models
- /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note lte|max|cc)?[_ ]?(?:\d?\w?)[_ ]?(?:plus|se|lite)?)(?: bui|\))/i
- // Xiaomi Mi
- ],
- [[MODEL, /_/g, " "], [VENDOR, XIAOMI], [TYPE, MOBILE]],
- [
- /oid[^\)]+; (2\d{4}(283|rpbf)[cgl])( bui|\))/i,
- // Redmi Pad
- /\b(mi[-_ ]?(?:pad)(?:[\w_ ]+))(?: bui|\))/i
- // Mi Pad tablets
- ],
- [[MODEL, /_/g, " "], [VENDOR, XIAOMI], [TYPE, TABLET]],
- [
- // OPPO
- /; (\w+) bui.+ oppo/i,
- /\b(cph[12]\d{3}|p(?:af|c[al]|d\w|e[ar])[mt]\d0|x9007|a101op)\b/i
- ],
- [MODEL, [VENDOR, "OPPO"], [TYPE, MOBILE]],
- [
- /\b(opd2\d{3}a?) bui/i
- ],
- [MODEL, [VENDOR, "OPPO"], [TYPE, TABLET]],
- [
- // Vivo
- /vivo (\w+)(?: bui|\))/i,
- /\b(v[12]\d{3}\w?[at])(?: bui|;)/i
- ],
- [MODEL, [VENDOR, "Vivo"], [TYPE, MOBILE]],
- [
- // Realme
- /\b(rmx[1-3]\d{3})(?: bui|;|\))/i
- ],
- [MODEL, [VENDOR, "Realme"], [TYPE, MOBILE]],
- [
- // Motorola
- /\b(milestone|droid(?:[2-4x]| (?:bionic|x2|pro|razr))?:?( 4g)?)\b[\w ]+build\//i,
- /\bmot(?:orola)?[- ](\w*)/i,
- /((?:moto[\w\(\) ]+|xt\d{3,4}|nexus 6)(?= bui|\)))/i
- ],
- [MODEL, [VENDOR, MOTOROLA], [TYPE, MOBILE]],
- [
- /\b(mz60\d|xoom[2 ]{0,2}) build\//i
- ],
- [MODEL, [VENDOR, MOTOROLA], [TYPE, TABLET]],
- [
- // LG
- /((?=lg)?[vl]k\-?\d{3}) bui| 3\.[-\w; ]{10}lg?-([06cv9]{3,4})/i
- ],
- [MODEL, [VENDOR, LG], [TYPE, TABLET]],
- [
- /(lm(?:-?f100[nv]?|-[\w\.]+)(?= bui|\))|nexus [45])/i,
- /\blg[-e;\/ ]+((?!browser|netcast|android tv)\w+)/i,
- /\blg-?([\d\w]+) bui/i
- ],
- [MODEL, [VENDOR, LG], [TYPE, MOBILE]],
- [
- // Lenovo
- /(ideatab[-\w ]+)/i,
- /lenovo ?(s[56]000[-\w]+|tab(?:[\w ]+)|yt[-\d\w]{6}|tb[-\d\w]{6})/i
- ],
- [MODEL, [VENDOR, LENOVO], [TYPE, TABLET]],
- [
- // Nokia
- /(?:maemo|nokia).*(n900|lumia \d+)/i,
- /nokia[-_ ]?([-\w\.]*)/i
- ],
- [[MODEL, /_/g, " "], [VENDOR, "Nokia"], [TYPE, MOBILE]],
- [
- /(pixel c)\b/i
- // Google Pixel C
- ],
- [MODEL, [VENDOR, GOOGLE], [TYPE, TABLET]],
- [
- /droid.+; (pixel[\daxl ]{0,6})(?: bui|\))/i
- // Google Pixel
- ],
- [MODEL, [VENDOR, GOOGLE], [TYPE, MOBILE]],
- [
- // Sony
- /droid.+ (a?\d[0-2]{2}so|[c-g]\d{4}|so[-gl]\w+|xq-a\w[4-7][12])(?= bui|\).+chrome\/(?![1-6]{0,1}\d\.))/i
- ],
- [MODEL, [VENDOR, SONY], [TYPE, MOBILE]],
- [
- /sony tablet [ps]/i,
- /\b(?:sony)?sgp\w+(?: bui|\))/i
- ],
- [[MODEL, "Xperia Tablet"], [VENDOR, SONY], [TYPE, TABLET]],
- [
- // OnePlus
- / (kb2005|in20[12]5|be20[12][59])\b/i,
- /(?:one)?(?:plus)? (a\d0\d\d)(?: b|\))/i
- ],
- [MODEL, [VENDOR, "OnePlus"], [TYPE, MOBILE]],
- [
- // Amazon
- /(alexa)webm/i,
- /(kf[a-z]{2}wi|aeo[c-r]{2})( bui|\))/i,
- // Kindle Fire without Silk / Echo Show
- /(kf[a-z]+)( bui|\)).+silk\//i
- // Kindle Fire HD
- ],
- [MODEL, [VENDOR, AMAZON], [TYPE, TABLET]],
- [
- /((?:sd|kf)[0349hijorstuw]+)( bui|\)).+silk\//i
- // Fire Phone
- ],
- [[MODEL, /(.+)/g, "Fire Phone $1"], [VENDOR, AMAZON], [TYPE, MOBILE]],
- [
- // BlackBerry
- /(playbook);[-\w\),; ]+(rim)/i
- // BlackBerry PlayBook
- ],
- [MODEL, VENDOR, [TYPE, TABLET]],
- [
- /\b((?:bb[a-f]|st[hv])100-\d)/i,
- /\(bb10; (\w+)/i
- // BlackBerry 10
- ],
- [MODEL, [VENDOR, BLACKBERRY], [TYPE, MOBILE]],
- [
- // Asus
- /(?:\b|asus_)(transfo[prime ]{4,10} \w+|eeepc|slider \w+|nexus 7|padfone|p00[cj])/i
- ],
- [MODEL, [VENDOR, ASUS], [TYPE, TABLET]],
- [
- / (z[bes]6[027][012][km][ls]|zenfone \d\w?)\b/i
- ],
- [MODEL, [VENDOR, ASUS], [TYPE, MOBILE]],
- [
- // HTC
- /(nexus 9)/i
- // HTC Nexus 9
- ],
- [MODEL, [VENDOR, "HTC"], [TYPE, TABLET]],
- [
- /(htc)[-;_ ]{1,2}([\w ]+(?=\)| bui)|\w+)/i,
- // HTC
- // ZTE
- /(zte)[- ]([\w ]+?)(?: bui|\/|\))/i,
- /(alcatel|geeksphone|nexian|panasonic(?!(?:;|\.))|sony(?!-bra))[-_ ]?([-\w]*)/i
- // Alcatel/GeeksPhone/Nexian/Panasonic/Sony
- ],
- [VENDOR, [MODEL, /_/g, " "], [TYPE, MOBILE]],
- [
- // Acer
- /droid.+; ([ab][1-7]-?[0178a]\d\d?)/i
- ],
- [MODEL, [VENDOR, "Acer"], [TYPE, TABLET]],
- [
- // Meizu
- /droid.+; (m[1-5] note) bui/i,
- /\bmz-([-\w]{2,})/i
- ],
- [MODEL, [VENDOR, "Meizu"], [TYPE, MOBILE]],
- [
- // Ulefone
- /; ((?:power )?armor(?:[\w ]{0,8}))(?: bui|\))/i
- ],
- [MODEL, [VENDOR, "Ulefone"], [TYPE, MOBILE]],
- [
- // MIXED
- /(blackberry|benq|palm(?=\-)|sonyericsson|acer|asus|dell|meizu|motorola|polytron|infinix|tecno)[-_ ]?([-\w]*)/i,
- // BlackBerry/BenQ/Palm/Sony-Ericsson/Acer/Asus/Dell/Meizu/Motorola/Polytron
- /(hp) ([\w ]+\w)/i,
- // HP iPAQ
- /(asus)-?(\w+)/i,
- // Asus
- /(microsoft); (lumia[\w ]+)/i,
- // Microsoft Lumia
- /(lenovo)[-_ ]?([-\w]+)/i,
- // Lenovo
- /(jolla)/i,
- // Jolla
- /(oppo) ?([\w ]+) bui/i
- // OPPO
- ],
- [VENDOR, MODEL, [TYPE, MOBILE]],
- [
- /(kobo)\s(ereader|touch)/i,
- // Kobo
- /(archos) (gamepad2?)/i,
- // Archos
- /(hp).+(touchpad(?!.+tablet)|tablet)/i,
- // HP TouchPad
- /(kindle)\/([\w\.]+)/i
- // Kindle
- ],
- [VENDOR, MODEL, [TYPE, TABLET]],
- [
- /(surface duo)/i
- // Surface Duo
- ],
- [MODEL, [VENDOR, MICROSOFT], [TYPE, TABLET]],
- [
- /droid [\d\.]+; (fp\du?)(?: b|\))/i
- // Fairphone
- ],
- [MODEL, [VENDOR, "Fairphone"], [TYPE, MOBILE]],
- [
- /(shield[\w ]+) b/i
- // Nvidia Shield Tablets
- ],
- [MODEL, [VENDOR, "Nvidia"], [TYPE, TABLET]],
- [
- /(sprint) (\w+)/i
- // Sprint Phones
- ],
- [VENDOR, MODEL, [TYPE, MOBILE]],
- [
- /(kin\.[onetw]{3})/i
- // Microsoft Kin
- ],
- [[MODEL, /\./g, " "], [VENDOR, MICROSOFT], [TYPE, MOBILE]],
- [
- /droid.+; ([c6]+|et5[16]|mc[239][23]x?|vc8[03]x?)\)/i
- // Zebra
- ],
- [MODEL, [VENDOR, ZEBRA], [TYPE, TABLET]],
- [
- /droid.+; (ec30|ps20|tc[2-8]\d[kx])\)/i
- ],
- [MODEL, [VENDOR, ZEBRA], [TYPE, MOBILE]],
- [
- ///////////////////
- // SMARTTVS
- ///////////////////
- /smart-tv.+(samsung)/i
- // Samsung
- ],
- [VENDOR, [TYPE, SMARTTV]],
- [
- /hbbtv.+maple;(\d+)/i
- ],
- [[MODEL, /^/, "SmartTV"], [VENDOR, SAMSUNG], [TYPE, SMARTTV]],
- [
- /(nux; netcast.+smarttv|lg (netcast\.tv-201\d|android tv))/i
- // LG SmartTV
- ],
- [[VENDOR, LG], [TYPE, SMARTTV]],
- [
- /(apple) ?tv/i
- // Apple TV
- ],
- [VENDOR, [MODEL, APPLE + " TV"], [TYPE, SMARTTV]],
- [
- /crkey/i
- // Google Chromecast
- ],
- [[MODEL, CHROME + "cast"], [VENDOR, GOOGLE], [TYPE, SMARTTV]],
- [
- /droid.+aft(\w+)( bui|\))/i
- // Fire TV
- ],
- [MODEL, [VENDOR, AMAZON], [TYPE, SMARTTV]],
- [
- /\(dtv[\);].+(aquos)/i,
- /(aquos-tv[\w ]+)\)/i
- // Sharp
- ],
- [MODEL, [VENDOR, SHARP], [TYPE, SMARTTV]],
- [
- /(bravia[\w ]+)( bui|\))/i
- // Sony
- ],
- [MODEL, [VENDOR, SONY], [TYPE, SMARTTV]],
- [
- /(mitv-\w{5}) bui/i
- // Xiaomi
- ],
- [MODEL, [VENDOR, XIAOMI], [TYPE, SMARTTV]],
- [
- /Hbbtv.*(technisat) (.*);/i
- // TechniSAT
- ],
- [VENDOR, MODEL, [TYPE, SMARTTV]],
- [
- /\b(roku)[\dx]*[\)\/]((?:dvp-)?[\d\.]*)/i,
- // Roku
- /hbbtv\/\d+\.\d+\.\d+ +\([\w\+ ]*; *([\w\d][^;]*);([^;]*)/i
- // HbbTV devices
- ],
- [[VENDOR, trim], [MODEL, trim], [TYPE, SMARTTV]],
- [
- /\b(android tv|smart[- ]?tv|opera tv|tv; rv:)\b/i
- // SmartTV from Unidentified Vendors
- ],
- [[TYPE, SMARTTV]],
- [
- ///////////////////
- // CONSOLES
- ///////////////////
- /(ouya)/i,
- // Ouya
- /(nintendo) (\w+)/i
- // Nintendo
- ],
- [VENDOR, MODEL, [TYPE, CONSOLE]],
- [
- /droid.+; (shield) bui/i
- // Nvidia
- ],
- [MODEL, [VENDOR, "Nvidia"], [TYPE, CONSOLE]],
- [
- /(playstation \w+)/i
- // Playstation
- ],
- [MODEL, [VENDOR, SONY], [TYPE, CONSOLE]],
- [
- /\b(xbox(?: one)?(?!; xbox))[\); ]/i
- // Microsoft Xbox
- ],
- [MODEL, [VENDOR, MICROSOFT], [TYPE, CONSOLE]],
- [
- ///////////////////
- // WEARABLES
- ///////////////////
- /((pebble))app/i
- // Pebble
- ],
- [VENDOR, MODEL, [TYPE, WEARABLE]],
- [
- /(watch)(?: ?os[,\/]|\d,\d\/)[\d\.]+/i
- // Apple Watch
- ],
- [MODEL, [VENDOR, APPLE], [TYPE, WEARABLE]],
- [
- /droid.+; (wt63?0{2,3})\)/i
- ],
- [MODEL, [VENDOR, ZEBRA], [TYPE, WEARABLE]],
- [
- ///////////////////
- // XR
- ///////////////////
- /droid.+; (glass) \d/i
- // Google Glass
- ],
- [MODEL, [VENDOR, GOOGLE], [TYPE, XR]],
- [
- /(quest( \d| pro)?)/i
- // Oculus Quest
- ],
- [MODEL, [VENDOR, FACEBOOK], [TYPE, XR]],
- [
- ///////////////////
- // EMBEDDED
- ///////////////////
- /(tesla)(?: qtcarbrowser|\/[-\w\.]+)/i
- // Tesla
- ],
- [VENDOR, [TYPE, EMBEDDED]],
- [
- /(aeobc)\b/i
- // Echo Dot
- ],
- [MODEL, [VENDOR, AMAZON], [TYPE, EMBEDDED]],
- [
- ////////////////////
- // MIXED (GENERIC)
- ///////////////////
- /droid .+?; ([^;]+?)(?: bui|; wv\)|\) applew).+? mobile safari/i
- // Android Phones from Unidentified Vendors
- ],
- [MODEL, [TYPE, MOBILE]],
- [
- /droid .+?; ([^;]+?)(?: bui|\) applew).+?(?! mobile) safari/i
- // Android Tablets from Unidentified Vendors
- ],
- [MODEL, [TYPE, TABLET]],
- [
- /\b((tablet|tab)[;\/]|focus\/\d(?!.+mobile))/i
- // Unidentifiable Tablet
- ],
- [[TYPE, TABLET]],
- [
- /(phone|mobile(?:[;\/]| [ \w\/\.]*safari)|pda(?=.+windows ce))/i
- // Unidentifiable Mobile
- ],
- [[TYPE, MOBILE]],
- [
- /(android[-\w\. ]{0,9});.+buil/i
- // Generic Android Device
- ],
- [MODEL, [VENDOR, "Generic"]]
- ],
- engine: [
- [
- /windows.+ edge\/([\w\.]+)/i
- // EdgeHTML
- ],
- [VERSION, [NAME, EDGE + "HTML"]],
- [
- /webkit\/537\.36.+chrome\/(?!27)([\w\.]+)/i
- // Blink
- ],
- [VERSION, [NAME, "Blink"]],
- [
- /(presto)\/([\w\.]+)/i,
- // Presto
- /(webkit|trident|netfront|netsurf|amaya|lynx|w3m|goanna)\/([\w\.]+)/i,
- // WebKit/Trident/NetFront/NetSurf/Amaya/Lynx/w3m/Goanna
- /ekioh(flow)\/([\w\.]+)/i,
- // Flow
- /(khtml|tasman|links)[\/ ]\(?([\w\.]+)/i,
- // KHTML/Tasman/Links
- /(icab)[\/ ]([23]\.[\d\.]+)/i,
- // iCab
- /\b(libweb)/i
- ],
- [NAME, VERSION],
- [
- /rv\:([\w\.]{1,9})\b.+(gecko)/i
- // Gecko
- ],
- [VERSION, NAME]
- ],
- os: [
- [
- // Windows
- /microsoft (windows) (vista|xp)/i
- // Windows (iTunes)
- ],
- [NAME, VERSION],
- [
- /(windows (?:phone(?: os)?|mobile))[\/ ]?([\d\.\w ]*)/i
- // Windows Phone
- ],
- [NAME, [VERSION, strMapper, windowsVersionMap]],
- [
- /windows nt 6\.2; (arm)/i,
- // Windows RT
- /windows[\/ ]?([ntce\d\. ]+\w)(?!.+xbox)/i,
- /(?:win(?=3|9|n)|win 9x )([nt\d\.]+)/i
- ],
- [[VERSION, strMapper, windowsVersionMap], [NAME, WINDOWS]],
- [
- // iOS/macOS
- /ip[honead]{2,4}\b(?:.*os ([\w]+) like mac|; opera)/i,
- // iOS
- /(?:ios;fbsv\/|iphone.+ios[\/ ])([\d\.]+)/i,
- /cfnetwork\/.+darwin/i
- ],
- [[VERSION, /_/g, "."], [NAME, "iOS"]],
- [
- /(mac os x) ?([\w\. ]*)/i,
- /(macintosh|mac_powerpc\b)(?!.+haiku)/i
- // Mac OS
- ],
- [[NAME, "macOS"], [VERSION, /_/g, "."]],
- [
- // Mobile OSes
- /droid ([\w\.]+)\b.+(android[- ]x86|harmonyos)/i
- // Android-x86/HarmonyOS
- ],
- [VERSION, NAME],
- [
- // Android/WebOS/QNX/Bada/RIM/Maemo/MeeGo/Sailfish OS
- /(android|webos|qnx|bada|rim tablet os|maemo|meego|sailfish)[-\/ ]?([\w\.]*)/i,
- /(blackberry)\w*\/([\w\.]*)/i,
- // Blackberry
- /(tizen|kaios)[\/ ]([\w\.]+)/i,
- // Tizen/KaiOS
- /\((series40);/i
- // Series 40
- ],
- [NAME, VERSION],
- [
- /\(bb(10);/i
- // BlackBerry 10
- ],
- [VERSION, [NAME, BLACKBERRY]],
- [
- /(?:symbian ?os|symbos|s60(?=;)|series60)[-\/ ]?([\w\.]*)/i
- // Symbian
- ],
- [VERSION, [NAME, "Symbian"]],
- [
- /mozilla\/[\d\.]+ \((?:mobile|tablet|tv|mobile; [\w ]+); rv:.+ gecko\/([\w\.]+)/i
- // Firefox OS
- ],
- [VERSION, [NAME, FIREFOX + " OS"]],
- [
- /web0s;.+rt(tv)/i,
- /\b(?:hp)?wos(?:browser)?\/([\w\.]+)/i
- // WebOS
- ],
- [VERSION, [NAME, "webOS"]],
- [
- /watch(?: ?os[,\/]|\d,\d\/)([\d\.]+)/i
- // watchOS
- ],
- [VERSION, [NAME, "watchOS"]],
- [
- // Google Chromecast
- /crkey\/([\d\.]+)/i
- // Google Chromecast
- ],
- [VERSION, [NAME, CHROME + "cast"]],
- [
- /(cros) [\w]+(?:\)| ([\w\.]+)\b)/i
- // Chromium OS
- ],
- [[NAME, "Chrome OS"], VERSION],
- [
- // Smart TVs
- /panasonic;(viera)/i,
- // Panasonic Viera
- /(netrange)mmh/i,
- // Netrange
- /(nettv)\/(\d+\.[\w\.]+)/i,
- // NetTV
- // Console
- /(nintendo|playstation) (\w+)/i,
- // Nintendo/Playstation
- /(xbox); +xbox ([^\);]+)/i,
- // Microsoft Xbox (360, One, X, S, Series X, Series S)
- // Other
- /\b(joli|palm)\b ?(?:os)?\/?([\w\.]*)/i,
- // Joli/Palm
- /(mint)[\/\(\) ]?(\w*)/i,
- // Mint
- /(mageia|vectorlinux)[; ]/i,
- // Mageia/VectorLinux
- /([kxln]?ubuntu|debian|suse|opensuse|gentoo|arch(?= linux)|slackware|fedora|mandriva|centos|pclinuxos|red ?hat|zenwalk|linpus|raspbian|plan 9|minix|risc os|contiki|deepin|manjaro|elementary os|sabayon|linspire)(?: gnu\/linux)?(?: enterprise)?(?:[- ]linux)?(?:-gnu)?[-\/ ]?(?!chrom|package)([-\w\.]*)/i,
- // Ubuntu/Debian/SUSE/Gentoo/Arch/Slackware/Fedora/Mandriva/CentOS/PCLinuxOS/RedHat/Zenwalk/Linpus/Raspbian/Plan9/Minix/RISCOS/Contiki/Deepin/Manjaro/elementary/Sabayon/Linspire
- /(hurd|linux) ?([\w\.]*)/i,
- // Hurd/Linux
- /(gnu) ?([\w\.]*)/i,
- // GNU
- /\b([-frentopcghs]{0,5}bsd|dragonfly)[\/ ]?(?!amd|[ix346]{1,2}86)([\w\.]*)/i,
- // FreeBSD/NetBSD/OpenBSD/PC-BSD/GhostBSD/DragonFly
- /(haiku) (\w+)/i
- // Haiku
- ],
- [NAME, VERSION],
- [
- /(sunos) ?([\w\.\d]*)/i
- // Solaris
- ],
- [[NAME, "Solaris"], VERSION],
- [
- /((?:open)?solaris)[-\/ ]?([\w\.]*)/i,
- // Solaris
- /(aix) ((\d)(?=\.|\)| )[\w\.])*/i,
- // AIX
- /\b(beos|os\/2|amigaos|morphos|openvms|fuchsia|hp-ux|serenityos)/i,
- // BeOS/OS2/AmigaOS/MorphOS/OpenVMS/Fuchsia/HP-UX/SerenityOS
- /(unix) ?([\w\.]*)/i
- // UNIX
- ],
- [NAME, VERSION]
- ]
- };
- var defaultProps = function() {
- var props = { init: {}, isIgnore: {}, isIgnoreRgx: {}, toString: {} };
- setProps.call(props.init, [
- [UA_BROWSER, [NAME, VERSION, MAJOR, TYPE]],
- [UA_CPU, [ARCHITECTURE]],
- [UA_DEVICE, [TYPE, MODEL, VENDOR]],
- [UA_ENGINE, [NAME, VERSION]],
- [UA_OS, [NAME, VERSION]]
- ]);
- setProps.call(props.isIgnore, [
- [UA_BROWSER, [VERSION, MAJOR]],
- [UA_ENGINE, [VERSION]],
- [UA_OS, [VERSION]]
- ]);
- setProps.call(props.isIgnoreRgx, [
- [UA_BROWSER, / ?browser$/i],
- [UA_OS, / ?os$/i]
- ]);
- setProps.call(props.toString, [
- [UA_BROWSER, [NAME, VERSION]],
- [UA_CPU, [ARCHITECTURE]],
- [UA_DEVICE, [VENDOR, MODEL]],
- [UA_ENGINE, [NAME, VERSION]],
- [UA_OS, [NAME, VERSION]]
- ]);
- return props;
- }();
- var createIData = function(item, itemType) {
- var init_props = defaultProps.init[itemType], is_ignoreProps = defaultProps.isIgnore[itemType] || 0, is_ignoreRgx = defaultProps.isIgnoreRgx[itemType] || 0, toString_props = defaultProps.toString[itemType] || 0;
- function IData() {
- setProps.call(this, init_props);
- }
- IData.prototype.getItem = function() {
- return item;
- };
- IData.prototype.withClientHints = function() {
- if (!NAVIGATOR_UADATA) {
- return item.parseCH().get();
- }
- return NAVIGATOR_UADATA.getHighEntropyValues(CH_ALL_VALUES).then(function(res) {
- return item.setCH(new UACHData(res, false)).parseCH().get();
- });
- };
- IData.prototype.withFeatureCheck = function() {
- return item.detectFeature().get();
- };
- if (itemType != UA_RESULT) {
- IData.prototype.is = function(strToCheck) {
- var is = false;
- for (var i in this) {
- if (this.hasOwnProperty(i) && !has(is_ignoreProps, i) && lowerize(is_ignoreRgx ? strip(is_ignoreRgx, this[i]) : this[i]) == lowerize(is_ignoreRgx ? strip(is_ignoreRgx, strToCheck) : strToCheck)) {
- is = true;
- if (strToCheck != UNDEF_TYPE)
- break;
- } else if (strToCheck == UNDEF_TYPE && is) {
- is = !is;
- break;
- }
- }
- return is;
- };
- IData.prototype.toString = function() {
- var str = EMPTY;
- for (var i in toString_props) {
- if (typeof this[toString_props[i]] !== UNDEF_TYPE) {
- str += (str ? " " : EMPTY) + this[toString_props[i]];
- }
- }
- return str || UNDEF_TYPE;
- };
- }
- if (!NAVIGATOR_UADATA) {
- IData.prototype.then = function(cb) {
- var that = this;
- var IDataResolve = function() {
- for (var prop in that) {
- if (that.hasOwnProperty(prop)) {
- this[prop] = that[prop];
- }
- }
- };
- IDataResolve.prototype = {
- is: IData.prototype.is,
- toString: IData.prototype.toString
- };
- var resolveData = new IDataResolve();
- cb(resolveData);
- return resolveData;
- };
- }
- return new IData();
- };
- function UACHData(uach, isHttpUACH) {
- uach = uach || {};
- setProps.call(this, CH_ALL_VALUES);
- if (isHttpUACH) {
- setProps.call(this, [
- [BRANDS, itemListToArray(uach[CH_HEADER])],
- [FULLVERLIST, itemListToArray(uach[CH_HEADER_FULL_VER_LIST])],
- [MOBILE, /\?1/.test(uach[CH_HEADER_MOBILE])],
- [MODEL, stripQuotes(uach[CH_HEADER_MODEL])],
- [PLATFORM, stripQuotes(uach[CH_HEADER_PLATFORM])],
- [PLATFORMVER, stripQuotes(uach[CH_HEADER_PLATFORM_VER])],
- [ARCHITECTURE, stripQuotes(uach[CH_HEADER_ARCH])],
- [FORMFACTORS, itemListToArray(uach[CH_HEADER_FORM_FACTORS])],
- [BITNESS, stripQuotes(uach[CH_HEADER_BITNESS])]
- ]);
- } else {
- for (var prop in uach) {
- if (this.hasOwnProperty(prop) && typeof uach[prop] !== UNDEF_TYPE)
- this[prop] = uach[prop];
- }
- }
- }
- function UAItem(itemType, ua, rgxMap, uaCH) {
- this.get = function(prop) {
- if (!prop)
- return this.data;
- return this.data.hasOwnProperty(prop) ? this.data[prop] : void 0;
- };
- this.set = function(prop, val) {
- this.data[prop] = val;
- return this;
- };
- this.setCH = function(ch) {
- this.uaCH = ch;
- return this;
- };
- this.detectFeature = function() {
- if (NAVIGATOR && NAVIGATOR.userAgent == this.ua) {
- switch (this.itemType) {
- case UA_BROWSER:
- if (NAVIGATOR.brave && typeof NAVIGATOR.brave.isBrave == FUNC_TYPE) {
- this.set(NAME, "Brave");
- }
- break;
- case UA_DEVICE:
- if (!this.get(TYPE) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[MOBILE]) {
- this.set(TYPE, MOBILE);
- }
- if (this.get(MODEL) == "Macintosh" && NAVIGATOR && typeof NAVIGATOR.standalone !== UNDEF_TYPE && NAVIGATOR.maxTouchPoints && NAVIGATOR.maxTouchPoints > 2) {
- this.set(MODEL, "iPad").set(TYPE, TABLET);
- }
- break;
- case UA_OS:
- if (!this.get(NAME) && NAVIGATOR_UADATA && NAVIGATOR_UADATA[PLATFORM]) {
- this.set(NAME, NAVIGATOR_UADATA[PLATFORM]);
- }
- break;
- case UA_RESULT:
- var data = this.data;
- var detect = function(itemType2) {
- return data[itemType2].getItem().detectFeature().get();
- };
- this.set(UA_BROWSER, detect(UA_BROWSER)).set(UA_CPU, detect(UA_CPU)).set(UA_DEVICE, detect(UA_DEVICE)).set(UA_ENGINE, detect(UA_ENGINE)).set(UA_OS, detect(UA_OS));
- }
- }
- return this;
- };
- this.parseUA = function() {
- if (this.itemType != UA_RESULT) {
- rgxMapper.call(this.data, this.ua, this.rgxMap);
- }
- if (this.itemType == UA_BROWSER) {
- this.set(MAJOR, majorize(this.get(VERSION)));
- }
- return this;
- };
- this.parseCH = function() {
- var uaCH2 = this.uaCH, rgxMap2 = this.rgxMap;
- switch (this.itemType) {
- case UA_BROWSER:
- var brands = uaCH2[FULLVERLIST] || uaCH2[BRANDS], prevName;
- if (brands) {
- for (var i in brands) {
- var brandName = strip(/(Google|Microsoft) /, brands[i].brand || brands[i]), brandVersion = brands[i].version;
- if (!/not.a.brand/i.test(brandName) && (!prevName || /chrom/i.test(prevName) && !/chromi/i.test(brandName))) {
- this.set(NAME, brandName).set(VERSION, brandVersion).set(MAJOR, majorize(brandVersion));
- prevName = brandName;
- }
- }
- }
- break;
- case UA_CPU:
- var archName = uaCH2[ARCHITECTURE];
- if (archName) {
- if (archName && uaCH2[BITNESS] == "64")
- archName += "64";
- rgxMapper.call(this.data, archName + ";", rgxMap2);
- }
- break;
- case UA_DEVICE:
- if (uaCH2[MOBILE]) {
- this.set(TYPE, MOBILE);
- }
- if (uaCH2[MODEL]) {
- this.set(MODEL, uaCH2[MODEL]);
- }
- if (uaCH2[MODEL] == "Xbox") {
- this.set(TYPE, CONSOLE).set(VENDOR, MICROSOFT);
- }
- if (uaCH2[FORMFACTORS]) {
- var ff;
- if (typeof uaCH2[FORMFACTORS] !== "string") {
- var idx = 0;
- while (!ff && idx < uaCH2[FORMFACTORS].length) {
- ff = strMapper(uaCH2[FORMFACTORS][idx++], formFactorsMap);
- }
- } else {
- ff = strMapper(uaCH2[FORMFACTORS], formFactorsMap);
- }
- this.set(TYPE, ff);
- }
- break;
- case UA_OS:
- var osName = uaCH2[PLATFORM];
- if (osName) {
- var osVersion = uaCH2[PLATFORMVER];
- if (osName == WINDOWS)
- osVersion = parseInt(majorize(osVersion), 10) >= 13 ? "11" : "10";
- this.set(NAME, osName).set(VERSION, osVersion);
- }
- if (this.get(NAME) == WINDOWS && uaCH2[MODEL] == "Xbox") {
- this.set(NAME, "Xbox").set(VERSION, void 0);
- }
- break;
- case UA_RESULT:
- var data = this.data;
- var parse2 = function(itemType2) {
- return data[itemType2].getItem().setCH(uaCH2).parseCH().get();
- };
- this.set(UA_BROWSER, parse2(UA_BROWSER)).set(UA_CPU, parse2(UA_CPU)).set(UA_DEVICE, parse2(UA_DEVICE)).set(UA_ENGINE, parse2(UA_ENGINE)).set(UA_OS, parse2(UA_OS));
- }
- return this;
- };
- setProps.call(this, [
- ["itemType", itemType],
- ["ua", ua],
- ["uaCH", uaCH],
- ["rgxMap", rgxMap],
- ["data", createIData(this, itemType)]
- ]);
- return this;
- }
- function UAParser(ua, extensions, headers) {
- if (typeof ua === OBJ_TYPE) {
- if (isExtensions(ua, true)) {
- if (typeof extensions === OBJ_TYPE) {
- headers = extensions;
- }
- extensions = ua;
- } else {
- headers = ua;
- extensions = void 0;
- }
- ua = void 0;
- } else if (typeof ua === STR_TYPE && !isExtensions(extensions, true)) {
- headers = extensions;
- extensions = void 0;
- }
- if (!(this instanceof UAParser)) {
- return new UAParser(ua, extensions, headers).getResult();
- }
- var userAgent2 = typeof ua === STR_TYPE ? ua : (
- // Passed user-agent string
- NAVIGATOR && NAVIGATOR.userAgent ? NAVIGATOR.userAgent : (
- // navigator.userAgent
- headers && headers[USER_AGENT] ? headers[USER_AGENT] : (
- // User-Agent from passed headers
- EMPTY
- )
- )
- ), httpUACH = new UACHData(headers, true), regexMap = extensions ? extend(defaultRegexes, extensions) : defaultRegexes, createItemFunc = function(itemType) {
- if (itemType == UA_RESULT) {
- return function() {
- return new UAItem(itemType, userAgent2, regexMap, httpUACH).set("ua", userAgent2).set(UA_BROWSER, this.getBrowser()).set(UA_CPU, this.getCPU()).set(UA_DEVICE, this.getDevice()).set(UA_ENGINE, this.getEngine()).set(UA_OS, this.getOS()).get();
- };
- } else {
- return function() {
- return new UAItem(itemType, userAgent2, regexMap[itemType], httpUACH).parseUA().get();
- };
- }
- };
- setProps.call(this, [
- ["getBrowser", createItemFunc(UA_BROWSER)],
- ["getCPU", createItemFunc(UA_CPU)],
- ["getDevice", createItemFunc(UA_DEVICE)],
- ["getEngine", createItemFunc(UA_ENGINE)],
- ["getOS", createItemFunc(UA_OS)],
- ["getResult", createItemFunc(UA_RESULT)],
- ["getUA", function() {
- return userAgent2;
- }],
- ["setUA", function(ua2) {
- if (isString(ua2))
- userAgent2 = ua2.length > UA_MAX_LENGTH ? trim(ua2, UA_MAX_LENGTH) : ua2;
- return this;
- }]
- ]).setUA(userAgent2);
- return this;
- }
- UAParser.VERSION = LIBVERSION;
- UAParser.BROWSER = enumerize([NAME, VERSION, MAJOR, TYPE]);
- UAParser.CPU = enumerize([ARCHITECTURE]);
- UAParser.DEVICE = enumerize([MODEL, VENDOR, TYPE, CONSOLE, MOBILE, SMARTTV, TABLET, WEARABLE, EMBEDDED]);
- UAParser.ENGINE = UAParser.OS = enumerize([NAME, VERSION]);
- function redirect() {
- const currentUrl = window.location.href;
- if (/\/song\//.test(currentUrl)) {
- const sheetId = currentUrl.match(new RegExp("(?<=\\/)\\d+(?=\\.)"))[0];
- const newUrl = `https://www.91pu.com.tw/m/tone.shtml?id=${sheetId}`;
- window.location.replace(newUrl);
- }
- }
- function injectGtag() {
- const newScript = document.createElement("script");
- newScript.src = "https://www.googletagmanager.com/gtag/js?id=G-JF4S3HZY31";
- newScript.async = true;
- document.head.appendChild(newScript);
- newScript.onload = () => {
- window.dataLayer = window.dataLayer || [];
- function gtag() {
- window.dataLayer.push(arguments);
- }
- gtag("js", /* @__PURE__ */ new Date());
- gtag("config", "G-JF4S3HZY31");
- };
- }
- function getQueryParams() {
- const url = new URL(window.location.href);
- const params = {
- transpose: +url.searchParams.get("transpose"),
- darkMode: !!url.searchParams.get("darkmode")
- };
- return params;
- }
- function changeTitle() {
- const newTitle = $("#mtitle").text().trim();
- document.title = `${newTitle} | 91+`;
- }
- function archiveChordSheet() {
- const sheet = document.getElementById("tone_z");
- const chordSheetDocument = new ChordSheetDocument();
- try {
- const chordSheetElement = new ChordSheetElement(sheet);
- chordSheetElement.formatUnderlines();
- const formBody = {
- id: chordSheetDocument.getId(),
- title: chordSheetDocument.getTitle(),
- key: chordSheetDocument.getKey(),
- play: chordSheetDocument.getPlay(),
- capo: chordSheetDocument.getCapo(),
- singer: chordSheetDocument.getSinger(),
- composer: chordSheetDocument.getComposer(),
- lyricist: chordSheetDocument.getLyricist(),
- bpm: chordSheetDocument.getBpm(),
- sheet_text: chordSheetDocument.getSheetText()
- };
- chordSheetElement.unformatUnderlines();
- fetch("https://91-plus-plus-api.fly.dev/archive", {
- method: "POST",
- headers: {
- "Content-Type": "application/json"
- },
- body: JSON.stringify(formBody)
- }).then((response) => {
- console.log("[91 Plus] 雲端樂譜備份成功:", response);
- }).catch((error) => {
- console.error("[91 Plus] 雲端樂譜備份失敗:", error);
- });
- } catch {
- console.warn("[91 Plus] 樂譜解析失敗,無法備份");
- fetch(
- `https://91-plus-plus-api.fly.dev/report?id=${chordSheetDocument.getId()}`
- );
- }
- }
- function initMutationObserver() {
- return new MutationObserver((records, observer) => {
- const isMutationDone = !!document.querySelector("#tone_z").childElementCount;
- if (!isMutationDone) {
- return;
- }
- $("body").trigger("mutation.done");
- observer.disconnect();
- }).observe(document.body, { childList: true, subtree: true });
- }
- function onDomReady(callback) {
- $("body").on("mutation.done", callback);
- }
- function handleEvents() {
- $("html").on("keydown", (event) => {
- const excludedTags = ["input"];
- const tagName = event.target.tagName.toLowerCase();
- if (excludedTags.includes(tagName)) {
- return;
- }
- StoreHandler.handleKeydown(event.key);
- });
- }
- function switchInstrument(instrument) {
- switch (instrument) {
- case "guitar": {
- $(".schord").trigger("click");
- break;
- }
- case "ukulele": {
- $(".ukschord").trigger("click");
- break;
- }
- default: {
- $(".nsChord").trigger("click");
- break;
- }
- }
- }
- function getChordShapes() {
- const chordShapes = _unsafeWindow.chord_shapes;
- return chordShapes;
- }
- function getChordList() {
- const chordList = [];
- $("#tone_z .tf").each(function() {
- chordList.push($(this).text());
- });
- return [...new Set(chordList)];
- }
- function convertChordName(chordName) {
- const root = chordName.match(/^[A-G]#?/)[0];
- const rest = chordName.replace(/^[A-G]#?/, "");
- return `${rest} ${root}`;
- }
- const userAgent = UAParser(navigator.userAgent);
- const _hoisted_1$b = { id: "plus91-sheet-popup" };
- const _hoisted_2$a = { class: "sheet-popup-container" };
- const _hoisted_3$4 = { class: "text-capo" };
- const _hoisted_4$2 = ["innerHTML"];
- const _hoisted_5$1 = { class: "transpose-range-container" };
- const _hoisted_6$1 = ["value"];
- const _hoisted_7 = { class: "instrument-select-container" };
- const _sfc_main$c = {
- __name: "SheetPopup",
- setup(__props) {
- const store = useStore();
- return (_ctx, _cache) => {
- return vue.openBlock(), vue.createBlock(vue.Transition, { name: "slide-and-fade" }, {
- default: vue.withCtx(() => [
- vue.withDirectives(vue.createElementVNode("div", _hoisted_1$b, [
- vue.createElementVNode("div", _hoisted_2$a, [
- vue.createVNode(AdjustWidget, {
- "onclick-left": () => {
- vue.unref(store).plusTranspose(-1);
- },
- "onclick-middle": () => {
- vue.unref(store).transpose = 0;
- },
- "onclick-right": () => {
- vue.unref(store).plusTranspose(1);
- }
- }, {
- default: vue.withCtx(() => [
- vue.createTextVNode(" CAPO:"),
- vue.createElementVNode("span", _hoisted_3$4, vue.toDisplayString(vue.unref(store).currentCapo), 1),
- vue.createTextVNode(" ("),
- vue.createElementVNode("span", {
- class: "text-key",
- innerHTML: vue.unref(store).currentKey
- }, null, 8, _hoisted_4$2),
- vue.createTextVNode(") ")
- ]),
- _: 1
- }, 8, ["onclick-left", "onclick-middle", "onclick-right"]),
- vue.createElementVNode("div", _hoisted_5$1, [
- vue.createElementVNode("input", {
- type: "range",
- min: "-11",
- max: "11",
- value: vue.unref(store).currentCapo,
- onInput: _cache[0] || (_cache[0] = ($event) => {
- vue.unref(store).transpose = $event.target.value - vue.unref(store).originalCapo;
- })
- }, null, 40, _hoisted_6$1)
- ]),
- vue.createElementVNode("div", _hoisted_7, [
- vue.createElementVNode("button", {
- class: "instrument-select-button",
- onClick: _cache[1] || (_cache[1] = () => {
- vue.unref(switchInstrument)("");
- })
- }, " 無 "),
- vue.createElementVNode("button", {
- class: "instrument-select-button",
- onClick: _cache[2] || (_cache[2] = () => {
- vue.unref(switchInstrument)("guitar");
- })
- }, " 吉他 "),
- vue.createElementVNode("button", {
- class: "instrument-select-button",
- onClick: _cache[3] || (_cache[3] = () => {
- vue.unref(switchInstrument)("ukulele");
- })
- }, " 烏克莉莉 ")
- ])
- ])
- ], 512), [
- [vue.vShow, vue.unref(store).isPopupShow.sheet]
- ])
- ]),
- _: 1
- });
- };
- }
- };
- const SheetPopup = /* @__PURE__ */ _export_sfc(_sfc_main$c, [["__scopeId", "data-v-f161c46c"]]);
- const _hoisted_1$a = {
- key: 0,
- class: "chord-container"
- };
- const _hoisted_2$9 = { class: "chord-name" };
- const _hoisted_3$3 = ["chord-name"];
- const _sfc_main$b = {
- __name: "ChordChart",
- props: {
- chord: String
- },
- setup(__props) {
- const props = __props;
- const chordRef = vue.ref(void 0);
- const chordShapes = getChordShapes();
- const isChordExist = vue.ref(true);
- vue.onMounted(() => {
- var _a;
- const formattedChordKey = convertChordName(props.chord);
- const chordShape = chordShapes[formattedChordKey];
- if (!chordShape) {
- return isChordExist.value = false;
- }
- const chordObject = {
- ...chordShape,
- // position: chordShape.position,
- // positionText: chordShape.position_text,
- barres: (_a = chordShape.bars) == null ? void 0 : _a.map((barre) => {
- return {
- ...barre,
- fromString: barre.from_string,
- toString: barre.to_string
- };
- }),
- chord: chordShape.chord.map(([stringNum, fretNum]) => {
- const raw = [stringNum, fretNum];
- if (isNaN(+fretNum)) {
- return raw;
- }
- let newFretNum = fretNum;
- newFretNum += chordShape.position || 0;
- newFretNum -= chordShape.position_text || 0;
- return [stringNum, newFretNum];
- })
- };
- vue.nextTick(() => {
- const width = chordRef.value.clientWidth;
- const chordBoxSelector = `.chord-chart[chord-name="${props.chord}"]`;
- const chordBox = new vexchords.ChordBox(chordBoxSelector, {
- width,
- height: width * 1.25,
- circleRadius: 5,
- numStrings: 6,
- numFrets: 5,
- showTuning: false,
- defaultColor: "#444",
- bgColor: "transparent"
- });
- chordBox.draw(chordObject);
- });
- });
- return (_ctx, _cache) => {
- return isChordExist.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_1$a, [
- vue.createElementVNode("div", _hoisted_2$9, vue.toDisplayString(props.chord), 1),
- vue.createElementVNode("div", {
- class: "chord-chart",
- "chord-name": props.chord,
- ref_key: "chordRef",
- ref: chordRef
- }, null, 8, _hoisted_3$3)
- ])) : vue.createCommentVNode("", true);
- };
- }
- };
- const ChordChart = /* @__PURE__ */ _export_sfc(_sfc_main$b, [["__scopeId", "data-v-735734f6"]]);
- const _hoisted_1$9 = { class: "banner" };
- const _hoisted_2$8 = { class: "chord-popup-container" };
- const _sfc_main$a = {
- __name: "ChordPopup",
- setup(__props) {
- const store = useStore();
- const canShowChord = vue.ref(true);
- if (userAgent.browser.name === "Mobile Safari") {
- canShowChord.value = false;
- }
- const bannerText = vue.ref("");
- const bannerTextList = [
- "此處的和弦圖示僅供參考!由於技術問題,目前尚無法準確繪製,尤其在把位較常出現錯誤,請注意。",
- "在 91 譜中沒有資料的和弦是畫不出來的呦!"
- ];
- const randomIndex = vue.ref(0);
- const refreshBanner = () => {
- if (!canShowChord.value) {
- bannerText.value = "很抱歉,由於技術問題,你所使用的瀏覽器目前尚無法繪製出和弦圖,開發者正在試著修正這個問題,敬請期待更新。";
- } else {
- randomIndex.value = Math.floor(Math.random() * bannerTextList.length);
- bannerText.value = bannerTextList[randomIndex.value];
- }
- };
- const chordList = vue.ref([]);
- vue.watch(store.isPopupShow, () => {
- if (!store.isPopupShow.chord) {
- return;
- }
- refreshBanner();
- chordList.value = getChordList();
- });
- return (_ctx, _cache) => {
- return vue.openBlock(), vue.createBlock(vue.Transition, { name: "slide-and-fade" }, {
- default: vue.withCtx(() => [
- vue.withDirectives(vue.createElementVNode("div", {
- id: "plus91-chord-popup",
- class: vue.normalizeClass({ "banner-only": !chordList.value.length })
- }, [
- vue.createElementVNode("div", _hoisted_1$9, [
- vue.createVNode(BootstrapIcon, {
- icon: "info-circle-fill",
- color: "inherit",
- size: "inherit"
- }),
- vue.createElementVNode("section", null, vue.toDisplayString(bannerText.value), 1)
- ]),
- vue.createElementVNode("div", _hoisted_2$8, [
- (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(chordList.value, (chord) => {
- return vue.openBlock(), vue.createBlock(ChordChart, {
- key: `${chord}_${( new Date()).getTime()}`,
- chord
- }, null, 8, ["chord"]);
- }), 128))
- ])
- ], 2), [
- [vue.vShow, vue.unref(store).isPopupShow.chord]
- ])
- ]),
- _: 1
- });
- };
- }
- };
- const ChordPopup = /* @__PURE__ */ _export_sfc(_sfc_main$a, [["__scopeId", "data-v-2210cdf0"]]);
- const _hoisted_1$8 = { id: "plus91-font-popup" };
- const _hoisted_2$7 = { class: "font-popup-container" };
- const _sfc_main$9 = {
- __name: "FontSizePopup",
- setup(__props) {
- const store = useStore();
- const getFontSize = vue.computed(() => {
- return store.originalFontSize + store.fontSizeDelta;
- });
- return (_ctx, _cache) => {
- return vue.openBlock(), vue.createBlock(vue.Transition, { name: "slide-and-fade" }, {
- default: vue.withCtx(() => [
- vue.withDirectives(vue.createElementVNode("div", _hoisted_1$8, [
- vue.createElementVNode("div", _hoisted_2$7, [
- vue.createVNode(AdjustWidget, {
- "onclick-left": () => {
- vue.unref(store).fontSizeDelta--;
- },
- "onclick-middle": () => {
- vue.unref(store).fontSizeDelta = 0;
- },
- "onclick-right": () => {
- vue.unref(store).fontSizeDelta++;
- },
- "disabled-left": getFontSize.value <= 8,
- "disabled-right": getFontSize.value >= 30
- }, {
- default: vue.withCtx(() => [
- vue.createTextVNode(vue.toDisplayString(getFontSize.value) + "px ", 1)
- ]),
- _: 1
- }, 8, ["onclick-left", "onclick-middle", "onclick-right", "disabled-left", "disabled-right"])
- ])
- ], 512), [
- [vue.vShow, vue.unref(store).isPopupShow.font]
- ])
- ]),
- _: 1
- });
- };
- }
- };
- const FontSizePopup = /* @__PURE__ */ _export_sfc(_sfc_main$9, [["__scopeId", "data-v-eff17405"]]);
- const _withScopeId = (n) => (vue.pushScopeId("data-v-e329f5af"), n = n(), vue.popScopeId(), n);
- const _hoisted_1$7 = { id: "plus91-settings-popup" };
- const _hoisted_2$6 = { class: "settings-popup-container" };
- const _hoisted_3$2 = { class: "setting-item" };
- const _hoisted_4$1 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("span", null, "深色模式", -1));
- const _hoisted_5 = { class: "setting-item" };
- const _hoisted_6 = /* @__PURE__ */ _withScopeId(() => /* @__PURE__ */ vue.createElementVNode("span", null, "協助測試雲端備份樂譜功能", -1));
- const _sfc_main$8 = {
- __name: "SettingsPopup",
- setup(__props) {
- const store = useStore();
- return (_ctx, _cache) => {
- return vue.openBlock(), vue.createBlock(vue.Transition, { name: "slide-and-fade" }, {
- default: vue.withCtx(() => [
- vue.withDirectives(vue.createElementVNode("div", _hoisted_1$7, [
- vue.createElementVNode("div", _hoisted_2$6, [
- vue.createElementVNode("label", _hoisted_3$2, [
- _hoisted_4$1,
- vue.withDirectives(vue.createElementVNode("input", {
- type: "checkbox",
- "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => vue.unref(store).isDarkMode = $event)
- }, null, 512), [
- [vue.vModelCheckbox, vue.unref(store).isDarkMode]
- ])
- ]),
- vue.createElementVNode("label", _hoisted_5, [
- _hoisted_6,
- vue.withDirectives(vue.createElementVNode("input", {
- type: "checkbox",
- "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => vue.unref(store).agreeToArchiveSheet = $event)
- }, null, 512), [
- [vue.vModelCheckbox, vue.unref(store).agreeToArchiveSheet]
- ])
- ])
- ])
- ], 512), [
- [vue.vShow, vue.unref(store).isPopupShow.settings]
- ])
- ]),
- _: 1
- });
- };
- }
- };
- const SettingsPopup = /* @__PURE__ */ _export_sfc(_sfc_main$8, [["__scopeId", "data-v-e329f5af"]]);
- const _hoisted_1$6 = { class: "icon-button" };
- const _hoisted_2$5 = { class: "button-text" };
- const _sfc_main$7 = {
- __name: "MenuButton",
- props: {
- icon: String,
- name: String,
- color: String
- },
- setup(__props) {
- vue.useCssVars((_ctx) => ({
- "9047bc34": __props.color
- }));
- const props = __props;
- return (_ctx, _cache) => {
- return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$6, [
- vue.createVNode(ToolbarIcon, {
- icon: props.icon,
- color: props.color
- }, null, 8, ["icon", "color"]),
- vue.createElementVNode("div", _hoisted_2$5, vue.toDisplayString(props.name), 1)
- ]);
- };
- }
- };
- const MenuButton = /* @__PURE__ */ _export_sfc(_sfc_main$7, [["__scopeId", "data-v-e9902592"]]);
- const _hoisted_1$5 = { id: "plus91-menu-popup" };
- const _hoisted_2$4 = { class: "menu-popup-container" };
- const BUTTON_COLOR = "#555";
- const _sfc_main$6 = {
- __name: "MenuPopup",
- setup(__props) {
- const store = useStore();
- const captureAsImage = () => {
- const content = document.querySelector("section.content");
- html2canvas(content).then((canvas) => {
- const newWindow = window.open();
- newWindow.document.write(`<img src="${canvas.toDataURL()}" />`);
- });
- };
- const searchOnYoutube = () => {
- const chordSheetDocument = new ChordSheetDocument();
- const title = chordSheetDocument.getTitle();
- const artist = chordSheetDocument.getSinger();
- const url = `https://www.youtube.com/results?search_query=${title}+${artist}`;
- window.open(url, "_blank").focus();
- };
- const goToGithubPage = () => {
- const url = "https://github.com/DonkeyBear/91Plus/blob/main/README.md";
- window.open(url, "_blank").focus();
- };
- return (_ctx, _cache) => {
- return vue.openBlock(), vue.createBlock(vue.Transition, { name: "slide-and-fade" }, {
- default: vue.withCtx(() => [
- vue.withDirectives(vue.createElementVNode("div", _hoisted_1$5, [
- vue.createElementVNode("div", _hoisted_2$4, [
- vue.createVNode(MenuButton, {
- icon: "keyboard",
- name: "快捷鍵",
- color: BUTTON_COLOR,
- onClick: _cache[0] || (_cache[0] = () => {
- vue.unref(store).togglePopup("hotkey");
- })
- }),
- vue.createVNode(MenuButton, {
- icon: "file-earmark-image",
- name: "擷取為圖片",
- color: BUTTON_COLOR,
- onClick: captureAsImage
- }),
- vue.createVNode(MenuButton, {
- icon: "youtube",
- name: "搜尋 YouTube",
- color: BUTTON_COLOR,
- onClick: searchOnYoutube
- }),
- vue.createVNode(MenuButton, {
- icon: "github",
- name: "關於 91 Plus",
- color: BUTTON_COLOR,
- onClick: goToGithubPage
- })
- ])
- ], 512), [
- [vue.vShow, vue.unref(store).isPopupShow.menu]
- ])
- ]),
- _: 1
- });
- };
- }
- };
- const MenuPopup = /* @__PURE__ */ _export_sfc(_sfc_main$6, [["__scopeId", "data-v-47af8eb5"]]);
- const _hoisted_1$4 = { class: "hotkey-item" };
- const _hoisted_2$3 = {
- key: 0,
- class: "hotkeys"
- };
- const _hoisted_3$1 = {
- key: 1,
- class: "hr"
- };
- const _sfc_main$5 = {
- __name: "HotkeyItem",
- props: {
- hotkey: {
- type: String,
- required: false
- },
- desc: String
- },
- setup(__props) {
- const props = __props;
- const hotkeyList = props.hotkey.split(" ");
- return (_ctx, _cache) => {
- return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$4, [
- vue.createElementVNode("div", {
- class: vue.normalizeClass(["desc", { "title": !__props.hotkey }])
- }, vue.toDisplayString(__props.desc), 3),
- __props.hotkey ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2$3, [
- (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(vue.unref(hotkeyList), (key) => {
- return vue.openBlock(), vue.createElementBlock("kbd", {
- key: `${key}_${__props.hotkey}_${__props.desc}`
- }, vue.toDisplayString(key), 1);
- }), 128))
- ])) : (vue.openBlock(), vue.createElementBlock("div", _hoisted_3$1))
- ]);
- };
- }
- };
- const HotkeyItem = /* @__PURE__ */ _export_sfc(_sfc_main$5, [["__scopeId", "data-v-3c43f6cf"]]);
- const hotkeysLeft = [
- {
- hotkey: "空白鍵",
- desc: "開啟 / 關閉功能選單"
- },
- {
- hotkey: "ESC",
- desc: "關閉功能選單"
- },
- {
- hotkey: "/",
- desc: "切換至搜尋框"
- }
- ];
- const hotkeysRight = [
- {
- hotkey: "",
- desc: "移調選單開啟時"
- },
- {
- hotkey: "← →",
- desc: "移調"
- },
- {
- hotkey: "↓",
- desc: "移回初始調"
- },
- {
- hotkey: "",
- desc: "在搜尋框內"
- },
- {
- hotkey: "Enter",
- desc: "搜尋"
- },
- {
- hotkey: "ESC",
- desc: "跳出搜尋框"
- }
- ];
- const hotkeyData = {
- hotkeysLeft,
- hotkeysRight
- };
- const _hoisted_1$3 = { id: "plus91-hotkey-popup" };
- const _hoisted_2$2 = { class: "hotkey-popup-container" };
- const _hoisted_3 = { class: "left-part" };
- const _hoisted_4 = { class: "right-part" };
- const _sfc_main$4 = {
- __name: "HotkeyPopup",
- setup(__props) {
- const store = useStore();
- return (_ctx, _cache) => {
- return vue.openBlock(), vue.createBlock(vue.Transition, { name: "slide-and-fade" }, {
- default: vue.withCtx(() => [
- vue.withDirectives(vue.createElementVNode("div", _hoisted_1$3, [
- vue.createElementVNode("div", _hoisted_2$2, [
- vue.createElementVNode("section", _hoisted_3, [
- (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(vue.unref(hotkeyData).hotkeysLeft, (item, index) => {
- return vue.openBlock(), vue.createBlock(HotkeyItem, {
- key: `${item.hotkey}_${item.desc}_${index}`,
- hotkey: item.hotkey,
- desc: item.desc
- }, null, 8, ["hotkey", "desc"]);
- }), 128))
- ]),
- vue.createElementVNode("section", _hoisted_4, [
- (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(vue.unref(hotkeyData).hotkeysRight, (item, index) => {
- return vue.openBlock(), vue.createBlock(HotkeyItem, {
- key: `${item.hotkey}_${item.desc}_${index}`,
- hotkey: item.hotkey,
- desc: item.desc
- }, null, 8, ["hotkey", "desc"]);
- }), 128))
- ])
- ])
- ], 512), [
- [vue.vShow, vue.unref(store).isPopupShow.hotkey]
- ])
- ]),
- _: 1
- });
- };
- }
- };
- const HotkeyPopup = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["__scopeId", "data-v-eb86b87c"]]);
- const _hoisted_1$2 = { id: "plus91-footer" };
- const _hoisted_2$1 = { class: "footer-container" };
- const _sfc_main$3 = {
- __name: "AppFooter",
- props: {
- active: Boolean
- },
- setup(__props) {
- const store = useStore();
- const props = __props;
- return (_ctx, _cache) => {
- return vue.openBlock(), vue.createBlock(vue.Transition, { name: "slide" }, {
- default: vue.withCtx(() => [
- vue.withDirectives(vue.createElementVNode("div", _hoisted_1$2, [
- vue.createElementVNode("div", _hoisted_2$1, [
- vue.createVNode(ToolbarIcon, {
- icon: "music-note-beamed",
- text: "譜面",
- stroke: ".05rem",
- active: vue.unref(store).isPopupShow.sheet,
- onClick: _cache[0] || (_cache[0] = ($event) => vue.unref(store).togglePopup("sheet"))
- }, null, 8, ["active"]),
- vue.createVNode(ToolbarIcon, {
- icon: "table",
- text: "和弦",
- active: vue.unref(store).isPopupShow.chord,
- onClick: _cache[1] || (_cache[1] = ($event) => vue.unref(store).togglePopup("chord"))
- }, null, 8, ["active"]),
- vue.createVNode(ToolbarIcon, {
- icon: "type",
- text: "字型",
- stroke: ".05rem",
- active: vue.unref(store).isPopupShow.font,
- onClick: _cache[2] || (_cache[2] = ($event) => vue.unref(store).togglePopup("font"))
- }, null, 8, ["active"]),
- vue.createVNode(ToolbarIcon, {
- icon: "gear-wide-connected",
- text: "設定",
- active: vue.unref(store).isPopupShow.settings,
- onClick: _cache[3] || (_cache[3] = ($event) => vue.unref(store).togglePopup("settings"))
- }, null, 8, ["active"]),
- vue.createVNode(ToolbarIcon, {
- icon: "list",
- text: "其他",
- stroke: ".05rem",
- active: vue.unref(store).isPopupShow.menu,
- onClick: _cache[4] || (_cache[4] = ($event) => vue.unref(store).togglePopup("menu"))
- }, null, 8, ["active"]),
- vue.createVNode(SheetPopup),
- vue.createVNode(ChordPopup),
- vue.createVNode(FontSizePopup),
- vue.createVNode(SettingsPopup),
- vue.createVNode(MenuPopup),
- vue.createVNode(HotkeyPopup)
- ])
- ], 512), [
- [vue.vShow, props.active]
- ])
- ]),
- _: 1
- });
- };
- }
- };
- const AppFooter = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["__scopeId", "data-v-4e274b3c"]]);
- const _hoisted_1$1 = { id: "plus91-header" };
- const _hoisted_2 = { class: "header-container" };
- const _sfc_main$2 = {
- __name: "AppHeader",
- props: {
- active: Boolean
- },
- setup(__props) {
- const props = __props;
- const searchText = vue.ref("");
- const search = () => {
- if (!searchText.value) {
- return;
- }
- const url = `https://www.91pu.com.tw/plus/search.php?keyword=${searchText.value}`;
- window.open(url, "_blank").focus();
- searchText.value = "";
- };
- const backToPreviousPage = () => {
- history.back();
- };
- return (_ctx, _cache) => {
- return vue.openBlock(), vue.createBlock(vue.Transition, { name: "slide" }, {
- default: vue.withCtx(() => [
- vue.withDirectives(vue.createElementVNode("div", _hoisted_1$1, [
- vue.createElementVNode("div", _hoisted_2, [
- vue.createVNode(ToolbarIcon, {
- icon: "chevron-left",
- stroke: ".04rem",
- onClick: backToPreviousPage
- }),
- vue.withDirectives(vue.createElementVNode("input", {
- type: "text",
- placeholder: "91 Plus",
- "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => searchText.value = $event),
- onKeydown: [
- vue.withKeys(search, ["enter"]),
- _cache[1] || (_cache[1] = vue.withKeys((event) => {
- event.target.blur();
- }, ["esc"]))
- ]
- }, null, 544), [
- [
- vue.vModelText,
- searchText.value,
- void 0,
- { trim: true }
- ]
- ]),
- vue.createVNode(ToolbarIcon, {
- icon: "search",
- stroke: ".03rem",
- onClick: _cache[2] || (_cache[2] = ($event) => search())
- })
- ])
- ], 512), [
- [vue.vShow, props.active]
- ])
- ]),
- _: 1
- });
- };
- }
- };
- const AppHeader = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-5ddafe3d"]]);
- const _hoisted_1 = { id: "dark-mode-overlay" };
- const _sfc_main$1 = {
- __name: "DarkModeOverlay",
- props: {
- active: Boolean
- },
- setup(__props) {
- const props = __props;
- return (_ctx, _cache) => {
- return vue.openBlock(), vue.createBlock(vue.Transition, { name: "fade" }, {
- default: vue.withCtx(() => [
- vue.withDirectives(vue.createElementVNode("div", _hoisted_1, null, 512), [
- [vue.vShow, props.active]
- ])
- ]),
- _: 1
- });
- };
- }
- };
- const DarkModeOverlay = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-6cf58435"]]);
- const _sfc_main = {
- __name: "App",
- setup(__props) {
- const store = useStore();
- return (_ctx, _cache) => {
- return vue.openBlock(), vue.createElementBlock(vue.Fragment, null, [
- vue.createVNode(TriggerOverlay, {
- onClick: vue.unref(store).toggleToolbars
- }, null, 8, ["onClick"]),
- vue.createVNode(AppHeader, {
- active: vue.unref(store).isToolbarsShow
- }, null, 8, ["active"]),
- vue.createVNode(AppFooter, {
- active: vue.unref(store).isToolbarsShow
- }, null, 8, ["active"]),
- vue.createVNode(DarkModeOverlay, {
- active: vue.unref(store).isDarkMode
- }, null, 8, ["active"])
- ], 64);
- };
- }
- };
- function init() {
- redirect();
- injectGtag();
- initMutationObserver();
- handleEvents();
- const storeHandler = new StoreHandler().start();
- onDomReady(() => {
- changeTitle();
- storeHandler.initState();
- const store = useStore();
- if (store.agreeToArchiveSheet) {
- archiveChordSheet();
- }
- });
- }
- const pinia = createPinia();
- pinia.use(src_default);
- vue.createApp(_sfc_main).use(pinia).mount(
- (() => {
- const app = document.createElement("div");
- app.id = "vue-91plus";
- document.body.append(app);
- return app;
- })()
- );
- init();
- })(Vue, zipson, vexchords, html2canvas);
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址