// ==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/[email protected]/dist/vue.global.prod.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/zipson.min.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/html2canvas.min.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/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/[email protected]/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
// Weibo
],
[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
// WeChat
],
[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
// QQ
],
[[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,
// Twitter
/(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]],
[
// Google
/(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);