Greasy Fork is available in English.

91 Plus

自由轉調、輕鬆練歌,打造 91 譜的最佳體驗!

// ==UserScript==
// @name         91 Plus
// @namespace    https://github.com/DonkeyBear
// @version      1.8.4
// @author       DonkeyBear
// @description  自由轉調、輕鬆練歌,打造 91 譜的最佳體驗!
// @icon         https://www.91pu.com.tw/icons/favicon-32x32.png
// @match        *://www.91pu.com.tw/m/*
// @match        *://www.91pu.com.tw/song/*
// @require      https://cdn.jsdelivr.net/npm/[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);