91 Plus

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

As of 12. 12. 2023. See the latest version.

Before you install, Greasy Fork would like you to know that this script contains antifeatures, which are things there for the script author's benefit, rather than yours.

This script contains code that will track your browsing. The author of this script explains: 使用 Google Analytics 了解使用情況

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         91 Plus
// @namespace    https://github.com/DonkeyBear
// @version      1.0.2
// @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
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @antifeature  tracking  使用 Google Analytics 了解使用情況
// ==/UserScript==

(e=>{if(typeof GM_addStyle=="function"){GM_addStyle(e);return}const t=document.createElement("style");t.textContent=e,document.head.append(t)})(" #trigger-overlay[data-v-1d03dd38]{position:fixed;top:0;left:0;right:0;bottom:0;z-index:500}.material-symbols-rounded[data-v-db75303e]{cursor:pointer;-webkit-user-select:none;user-select:none;color:var(--46f2ba82);font-size:1.25rem;padding:0 .75rem}.slide-and-fade-enter-active[data-v-95b1456e],.slide-and-fade-leave-active[data-v-95b1456e]{transition:all .2s}.slide-and-fade-enter-from[data-v-95b1456e],.slide-and-fade-leave-to[data-v-95b1456e]{transform:translateY(10%);opacity:0}#plus91-sheet-popup[data-v-95b1456e]{position:absolute;left:0;right:0;bottom:3rem;background:#fafafa;border:1px solid lightgray;padding:1rem 2rem;border-radius:1rem;margin:0 1rem}.transpose-range-container input[type=range][data-v-95b1456e]{width:100%}.transpose-button-group[data-v-95b1456e]{display:flex;margin-bottom:1rem}.transpose-button-group .capo-button[data-v-95b1456e]{border:0;background:transparent}.transpose-button-group .capo-button.info[data-v-95b1456e]{flex-grow:1}.transpose-button-group .capo-button.increase[data-v-95b1456e]{padding-left:1rem}.transpose-button-group .capo-button.decrease[data-v-95b1456e]{padding-right:1rem}.slide-and-fade-enter-active[data-v-430d5768],.slide-and-fade-leave-active[data-v-430d5768]{transition:all .2s}.slide-and-fade-enter-from[data-v-430d5768],.slide-and-fade-leave-to[data-v-430d5768]{transform:translateY(10%);opacity:0}#plus91-settings-popup[data-v-430d5768]{position:absolute;left:0;right:0;bottom:3rem;background:#fafafa;border:1px solid lightgray;border-radius:1rem;margin:0 1rem;padding:1rem}#plus91-settings-popup .setting-item[data-v-430d5768]{display:flex;align-items:center;justify-content:space-between;padding:.5rem 1rem;border-radius:.5rem;cursor:pointer}#plus91-settings-popup .setting-item[data-v-430d5768]:hover{background:rgba(0,0,0,.05)}.icon-button[data-v-29985f23]{display:flex;flex-direction:column;align-items:center;cursor:pointer}.icon-button .button-text[data-v-29985f23]{font-size:.5rem;color:var(--045d7cb5)}.slide-and-fade-enter-active[data-v-2a47f935],.slide-and-fade-leave-active[data-v-2a47f935]{transition:all .2s}.slide-and-fade-enter-from[data-v-2a47f935],.slide-and-fade-leave-to[data-v-2a47f935]{transform:translateY(10%);opacity:0}#plus91-menu-popup[data-v-2a47f935]{position:absolute;left:0;right:0;bottom:3rem;background:#fafafa;border:1px solid lightgray;padding:1rem 2rem;border-radius:1rem;margin:0 1rem}#plus91-menu-popup .menu-popup-container[data-v-2a47f935]{display:flex;justify-content:space-around}.slide-enter-active[data-v-6cf0f22c],.slide-leave-active[data-v-6cf0f22c]{transition:transform .2s}.slide-enter-from[data-v-6cf0f22c],.slide-leave-to[data-v-6cf0f22c]{transform:translateY(100%)}#plus91-footer[data-v-6cf0f22c]{z-index:800;display:flex;justify-content:center;position:fixed;left:0;right:0;bottom:0}.footer-container[data-v-6cf0f22c]{width:min(100vw,768px);background:rgba(50,80,200,.5);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);padding:.6rem;display:flex;justify-content:space-between;align-items:center;border-radius:1rem 1rem 0 0}.slide-enter-active[data-v-183b97c5],.slide-leave-active[data-v-183b97c5]{transition:transform .2s}.slide-enter-from[data-v-183b97c5],.slide-leave-to[data-v-183b97c5]{transform:translateY(-100%)}#plus91-header[data-v-183b97c5]{z-index:800;display:flex;justify-content:center;position:fixed;left:0;right:0;top:0}.header-container[data-v-183b97c5]{width:min(100vw,768px);background:rgba(50,80,200,.5);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);padding:.6rem;display:flex;justify-content:space-between;align-items:center;border-radius:0 0 1rem 1rem}.header-container input[data-v-183b97c5]{flex-grow:1;width:100%;border-radius:50rem;border:0;font-size:.75rem;padding:.35rem 1rem;background:rgba(255,255,255,.6666666667);color:#0009;opacity:.5;transition:all .2s}.header-container input[data-v-183b97c5]:focus-visible{outline:0;opacity:1}.fade-enter-active[data-v-a07a2507],.fade-leave-active[data-v-a07a2507]{transition:opacity .2s}.fade-enter-from[data-v-a07a2507],.fade-leave-to[data-v-a07a2507]{opacity:0}#dark-mode-overlay[data-v-a07a2507]{position:fixed;top:0;left:0;right:0;bottom:0;z-index:1000;-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)}.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(0,0,128,.2)}input[type=range]::-webkit-slider-thumb{background:rgb(141,193,226)}#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{display:none!important} ");

(function (vue) {
  '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;
  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$1(v) {
    return typeof v === "object" && v !== null;
  }
  function normalizeOptions(options, factoryOptions) {
    options = isObject$1(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$9 = {};
  const _hoisted_1$8 = { id: "trigger-overlay" };
  function _sfc_render(_ctx, _cache) {
    return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$8);
  }
  const TriggerOverlay = /* @__PURE__ */ _export_sfc(_sfc_main$9, [["render", _sfc_render], ["__scopeId", "data-v-1d03dd38"]]);
  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)();
  class MonkeyStorage {
    /**
     * @param {String} key 
     * @returns {String|null}
     */
    static getItem(key) {
      return _GM_getValue(key, null);
    }
    /**
     * @param {String} key 
     * @param {String} value 
     * @returns {void}
     */
    static setItem(key, value) {
      _GM_setValue(key, value);
    }
  }
  var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
  var lib = {};
  var compress$1 = {};
  var any = {};
  var constants = {};
  (function(exports) {
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.FLOAT_COMPRESSION_PRECISION = 1e3;
    exports.DATE_LOW_PRECISION = 1e5;
    exports.FLOAT_FULL_PRECISION_DELIMITER = ",";
    exports.FLOAT_REDUCED_PRECISION_DELIMITER = ".";
    exports.INTEGER_TOKEN = "¢";
    exports.FLOAT_TOKEN = "£";
    exports.STRING_TOKEN = "¨";
    exports.DATE_TOKEN = "ø";
    exports.LP_DATE_TOKEN = "±";
    exports.UNREFERENCED_INTEGER_TOKEN = "¤";
    exports.UNREFERENCED_FLOAT_TOKEN = "¥";
    exports.UNREFERENCED_STRING_TOKEN = "´";
    exports.UNREFERENCED_DATE_TOKEN = "¿";
    exports.UNREFERENCED_LP_DATE_TOKEN = "ÿ";
    exports.REF_INTEGER_TOKEN = "º";
    exports.REF_FLOAT_TOKEN = "Ý";
    exports.REF_STRING_TOKEN = "ß";
    exports.REF_DATE_TOKEN = "×";
    exports.REF_LP_DATE_TOKEN = "ü";
    exports.NULL_TOKEN = "§";
    exports.UNDEFINED_TOKEN = "µ";
    exports.BOOLEAN_TRUE_TOKEN = "»";
    exports.BOOLEAN_FALSE_TOKEN = "«";
    exports.ESCAPE_CHARACTER = "\\";
    exports.ESCAPED_STRING_TOKEN = "" + exports.ESCAPE_CHARACTER + exports.STRING_TOKEN;
    exports.ESCAPED_UNREFERENCED_STRING_TOKEN = "" + exports.ESCAPE_CHARACTER + exports.UNREFERENCED_STRING_TOKEN;
    exports.REGEX_ESCAPE_CHARACTER = new RegExp(exports.ESCAPE_CHARACTER.replace("\\", "\\\\"), "g");
    exports.REGEX_ESCAPED_ESCAPE_CHARACTER = new RegExp(exports.ESCAPE_CHARACTER.replace("\\", "\\\\") + exports.ESCAPE_CHARACTER.replace("\\", "\\\\"), "g");
    exports.REGEX_STRING_TOKEN = new RegExp(exports.STRING_TOKEN, "g");
    exports.REGEX_ESCAPED_STRING_TOKEN = new RegExp(exports.ESCAPE_CHARACTER + exports.ESCAPED_STRING_TOKEN, "g");
    exports.REGEX_UNREFERENCED_STRING_TOKEN = new RegExp(exports.UNREFERENCED_STRING_TOKEN, "g");
    exports.REGEX_UNREFERENCED_ESCAPED_STRING_TOKEN = new RegExp(exports.ESCAPE_CHARACTER + exports.ESCAPED_UNREFERENCED_STRING_TOKEN, "g");
    exports.DATE_REGEX = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z/;
    exports.OBJECT_START_TOKEN = "{";
    exports.OBJECT_END_TOKEN = "}";
    exports.TEMPLATE_OBJECT_START = "¦";
    exports.TEMPLATE_OBJECT_END = "‡";
    exports.TEMPLATE_OBJECT_FINAL = "—";
    exports.ARRAY_START_TOKEN = "|";
    exports.ARRAY_END_TOKEN = "÷";
    exports.ARRAY_REPEAT_TOKEN = "þ";
    exports.ARRAY_REPEAT_MANY_TOKEN = "^";
    exports.ARRAY_REPEAT_COUNT_THRESHOLD = 4;
    exports.REFERENCE_HEADER_LENGTH = 1;
    exports.DELIMITING_TOKENS_THRESHOLD = 122;
    exports.STRING_IDENT_PREFIX = "$";
    exports.INTEGER_SMALL_EXCLUSIVE_BOUND_LOWER = -10;
    exports.INTEGER_SMALL_EXCLUSIVE_BOUND_UPPER = 10;
    exports.INTEGER_SMALL_TOKEN_EXCLUSIVE_BOUND_LOWER = 191;
    exports.INTEGER_SMALL_TOKEN_EXCLUSIVE_BOUND_UPPER = 211;
    exports.INTEGER_SMALL_TOKEN_OFFSET = -201;
    exports.INTEGER_SMALL_TOKEN_ELEMENT_OFFSET = 9;
    exports.INTEGER_SMALL_TOKENS = ["À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ç", "È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï", "Ð", "Ñ", "Ò"];
  })(constants);
  Object.defineProperty(any, "__esModule", { value: true });
  var constants_1$a = constants;
  function compressAny(compressors2, context, obj, invertedIndex, writer2, options) {
    var type = typeof obj;
    if (type === "number") {
      compressors2.number(compressors2, context, obj, invertedIndex, writer2, options);
    } else if (type === "string") {
      compressors2.string(compressors2, context, obj, invertedIndex, writer2, options);
    } else if (type === "boolean") {
      writer2.write(obj ? constants_1$a.BOOLEAN_TRUE_TOKEN : constants_1$a.BOOLEAN_FALSE_TOKEN);
    } else if (obj === null) {
      writer2.write(constants_1$a.NULL_TOKEN);
    } else if (obj === void 0) {
      writer2.write(constants_1$a.UNDEFINED_TOKEN);
    } else if (Array.isArray(obj)) {
      compressors2.array(compressors2, context, obj, invertedIndex, writer2, options);
    } else if (obj instanceof Date) {
      compressors2.date(compressors2, context, obj.getTime(), invertedIndex, writer2, options);
    } else {
      compressors2.object(compressors2, context, obj, invertedIndex, writer2, options);
    }
  }
  any.compressAny = compressAny;
  var array = {};
  var writer = {};
  var __extends = commonjsGlobal && commonjsGlobal.__extends || function() {
    var extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(d, b) {
      d.__proto__ = b;
    } || function(d, b) {
      for (var p in b)
        if (b.hasOwnProperty(p))
          d[p] = b[p];
    };
    return function(d, b) {
      extendStatics(d, b);
      function __() {
        this.constructor = d;
      }
      d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
  }();
  Object.defineProperty(writer, "__esModule", { value: true });
  var ZipsonWriter = (
    /** @class */
    function() {
      function ZipsonWriter2() {
      }
      return ZipsonWriter2;
    }()
  );
  writer.ZipsonWriter = ZipsonWriter;
  var ZipsonStringWriter = (
    /** @class */
    function(_super) {
      __extends(ZipsonStringWriter2, _super);
      function ZipsonStringWriter2() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this.value = "";
        return _this;
      }
      ZipsonStringWriter2.prototype.write = function(data) {
        this.value += data;
      };
      ZipsonStringWriter2.prototype.end = function() {
      };
      return ZipsonStringWriter2;
    }(ZipsonWriter)
  );
  writer.ZipsonStringWriter = ZipsonStringWriter;
  var util$1 = {};
  Object.defineProperty(util$1, "__esModule", { value: true });
  var constants_1$9 = constants;
  var maxInteger = 2147483648;
  var minInteger = -2147483649;
  var base62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  function compressInteger(number2) {
    if (number2 === 0) {
      return "0";
    }
    var result = "";
    var carry = number2 < 0 ? -number2 : number2;
    var current = 0;
    var fraction;
    while (carry > 0) {
      carry = carry / 62;
      fraction = carry % 1;
      current = fraction * 62 + 0.1 << 0;
      carry -= fraction;
      result = base62[current] + result;
    }
    result = number2 < 0 ? "-" + result : result;
    return result;
  }
  util$1.compressInteger = compressInteger;
  function decompressInteger(compressedInteger) {
    var value = 0;
    if (compressedInteger[0] === "0") {
      return value;
    } else {
      var negative = compressedInteger[0] === "-";
      var multiplier = 1;
      var leftBound = negative ? 1 : 0;
      for (var i = compressedInteger.length - 1; i >= leftBound; i--) {
        var code = compressedInteger.charCodeAt(i);
        var current = code - 48;
        if (code >= 97) {
          current -= 13;
        } else if (code >= 65) {
          current -= 7;
        }
        value += current * multiplier;
        multiplier *= 62;
      }
      return negative ? -value : value;
    }
  }
  util$1.decompressInteger = decompressInteger;
  function compressFloat(float, fullPrecision) {
    if (fullPrecision === void 0) {
      fullPrecision = false;
    }
    if (fullPrecision) {
      var _a = float.toString().split("."), integer = _a[0], fraction = _a[1];
      var operator = integer === "-0" ? "-" : "";
      return "" + operator + compressInteger(parseInt(integer)) + constants_1$9.FLOAT_FULL_PRECISION_DELIMITER + fraction;
    } else {
      var integer = float >= maxInteger ? Math.floor(float) : float <= minInteger ? Math.ceil(float) : float << 0;
      var fraction = Math.round(constants_1$9.FLOAT_COMPRESSION_PRECISION * (float % 1));
      return "" + compressInteger(integer) + constants_1$9.FLOAT_REDUCED_PRECISION_DELIMITER + compressInteger(fraction);
    }
  }
  util$1.compressFloat = compressFloat;
  function decompressFloat(compressedFloat) {
    if (compressedFloat.indexOf(constants_1$9.FLOAT_FULL_PRECISION_DELIMITER) > -1) {
      var _a = compressedFloat.split(constants_1$9.FLOAT_FULL_PRECISION_DELIMITER), integer = _a[0], fraction = _a[1];
      var mult = integer === "-0" ? -1 : 1;
      var uncompressedInteger = decompressInteger(integer);
      return mult * parseFloat(uncompressedInteger + "." + fraction);
    } else {
      var _b = compressedFloat.split(constants_1$9.FLOAT_REDUCED_PRECISION_DELIMITER), integer = _b[0], fraction = _b[1];
      var uncompressedInteger = decompressInteger(integer);
      var uncompressedFraction = decompressInteger(fraction);
      return uncompressedInteger + uncompressedFraction / constants_1$9.FLOAT_COMPRESSION_PRECISION;
    }
  }
  util$1.decompressFloat = decompressFloat;
  Object.defineProperty(array, "__esModule", { value: true });
  var constants_1$8 = constants;
  var writer_1 = writer;
  var util_1$5 = util$1;
  function compressArray(compressors2, context, array2, invertedIndex, writer2, options) {
    context.arrayLevel++;
    if (context.arrayLevel > context.arrayItemWriters.length) {
      context.arrayItemWriters.push(new writer_1.ZipsonStringWriter());
    }
    var arrayItemWriter = context.arrayItemWriters[context.arrayLevel - 1];
    var parentWriter = context.arrayItemWriters[context.arrayLevel - 2] || writer2;
    parentWriter.write(constants_1$8.ARRAY_START_TOKEN);
    var previousItem = "";
    var repeatedTimes = 0;
    var repeatManyCount = 0;
    var templateObject = new compressors2.template.Object(array2[0], array2[1]);
    if (templateObject.isTemplating) {
      templateObject.compressTemplate(compressors2, context, invertedIndex, parentWriter, options);
    }
    for (var i = 0; i < array2.length; i++) {
      var item = array2[i];
      arrayItemWriter.value = "";
      if (item === void 0) {
        item = null;
      }
      if (i > 1 && templateObject.isTemplating) {
        templateObject.isNextTemplateable(array2[i], parentWriter);
      }
      if (templateObject.isTemplating) {
        templateObject.compressTemplateValues(compressors2, context, invertedIndex, arrayItemWriter, options, array2[i]);
      } else {
        compressors2.any(compressors2, context, item, invertedIndex, arrayItemWriter, options);
      }
      if (arrayItemWriter.value === previousItem) {
        repeatedTimes++;
        if (repeatedTimes >= constants_1$8.ARRAY_REPEAT_COUNT_THRESHOLD) {
          if (repeatManyCount === 0) {
            parentWriter.write(constants_1$8.ARRAY_REPEAT_MANY_TOKEN);
          }
          repeatManyCount++;
        } else {
          parentWriter.write(constants_1$8.ARRAY_REPEAT_TOKEN);
        }
      } else {
        repeatedTimes = 0;
        if (repeatManyCount > 0) {
          parentWriter.write(util_1$5.compressInteger(repeatManyCount));
          repeatManyCount = 0;
        }
        parentWriter.write(arrayItemWriter.value);
        previousItem = arrayItemWriter.value;
      }
    }
    if (repeatManyCount > 0) {
      parentWriter.write(util_1$5.compressInteger(repeatManyCount));
    }
    if (templateObject.isTemplating) {
      templateObject.end(parentWriter);
    }
    parentWriter.write(constants_1$8.ARRAY_END_TOKEN);
    context.arrayLevel--;
  }
  array.compressArray = compressArray;
  var string = {};
  Object.defineProperty(string, "__esModule", { value: true });
  var constants_1$7 = constants;
  var util_1$4 = util$1;
  function compressString(compressors2, context, obj, invertedIndex, writer2, options) {
    var foundRef;
    var stringIdent = constants_1$7.STRING_IDENT_PREFIX + obj;
    if (options.detectUtcTimestamps && obj[obj.length - 1] === "Z" && obj.match(constants_1$7.DATE_REGEX)) {
      var date2 = Date.parse(obj);
      compressors2.date(compressors2, context, date2, invertedIndex, writer2, options);
    } else if ((foundRef = invertedIndex.stringMap[stringIdent]) !== void 0) {
      writer2.write("" + constants_1$7.REF_STRING_TOKEN + foundRef);
    } else {
      var ref2 = util_1$4.compressInteger(invertedIndex.stringCount);
      var newRef = "" + constants_1$7.STRING_TOKEN + obj.replace(constants_1$7.REGEX_ESCAPE_CHARACTER, constants_1$7.ESCAPE_CHARACTER + constants_1$7.ESCAPE_CHARACTER).replace(constants_1$7.REGEX_STRING_TOKEN, constants_1$7.ESCAPED_STRING_TOKEN) + constants_1$7.STRING_TOKEN;
      if (ref2.length + constants_1$7.REFERENCE_HEADER_LENGTH + 1 < newRef.length) {
        invertedIndex.stringMap[stringIdent] = ref2;
        invertedIndex.stringCount++;
        writer2.write(newRef);
      } else {
        writer2.write("" + constants_1$7.UNREFERENCED_STRING_TOKEN + obj.replace(constants_1$7.REGEX_ESCAPE_CHARACTER, constants_1$7.ESCAPE_CHARACTER + constants_1$7.ESCAPE_CHARACTER).replace(constants_1$7.REGEX_UNREFERENCED_STRING_TOKEN, constants_1$7.ESCAPED_UNREFERENCED_STRING_TOKEN) + constants_1$7.UNREFERENCED_STRING_TOKEN);
      }
    }
  }
  string.compressString = compressString;
  var number = {};
  Object.defineProperty(number, "__esModule", { value: true });
  var constants_1$6 = constants;
  var util_1$3 = util$1;
  function compressNumber(compressors2, context, obj, invertedIndex, writer2, options) {
    var foundRef;
    if (obj % 1 === 0) {
      if (obj < constants_1$6.INTEGER_SMALL_EXCLUSIVE_BOUND_UPPER && obj > constants_1$6.INTEGER_SMALL_EXCLUSIVE_BOUND_LOWER) {
        writer2.write(constants_1$6.INTEGER_SMALL_TOKENS[obj + constants_1$6.INTEGER_SMALL_TOKEN_ELEMENT_OFFSET]);
      } else if ((foundRef = invertedIndex.integerMap[obj]) !== void 0) {
        writer2.write("" + constants_1$6.REF_INTEGER_TOKEN + foundRef);
      } else {
        var ref2 = util_1$3.compressInteger(invertedIndex.integerCount);
        var compressedInteger = util_1$3.compressInteger(obj);
        var newRef = "" + constants_1$6.INTEGER_TOKEN + compressedInteger;
        if (ref2.length + constants_1$6.REFERENCE_HEADER_LENGTH < newRef.length) {
          invertedIndex.integerMap[obj] = ref2;
          invertedIndex.integerCount++;
          writer2.write(newRef);
        } else {
          writer2.write("" + constants_1$6.UNREFERENCED_INTEGER_TOKEN + compressedInteger);
        }
      }
    } else {
      var compressedFloat = util_1$3.compressFloat(obj, options.fullPrecisionFloats);
      if ((foundRef = invertedIndex.floatMap[compressedFloat]) !== void 0) {
        writer2.write("" + constants_1$6.REF_FLOAT_TOKEN + foundRef);
      } else {
        var ref2 = util_1$3.compressInteger(invertedIndex.floatCount);
        var newRef = "" + constants_1$6.FLOAT_TOKEN + compressedFloat;
        if (ref2.length + constants_1$6.REFERENCE_HEADER_LENGTH < newRef.length) {
          invertedIndex.floatMap[compressedFloat] = ref2;
          invertedIndex.floatCount++;
          writer2.write(newRef);
        } else {
          writer2.write("" + constants_1$6.UNREFERENCED_FLOAT_TOKEN + compressedFloat);
        }
      }
    }
  }
  number.compressNumber = compressNumber;
  var object$1 = {};
  Object.defineProperty(object$1, "__esModule", { value: true });
  var constants_1$5 = constants;
  function compressObject(compressors2, context, obj, invertedIndex, writer2, options) {
    writer2.write(constants_1$5.OBJECT_START_TOKEN);
    var keys = Object.keys(obj);
    var templateObject = new compressors2.template.Object(obj[keys[0]], obj[keys[1]]);
    if (templateObject.isTemplating) {
      templateObject.compressTemplate(compressors2, context, invertedIndex, writer2, options);
    }
    for (var i = 0; i < keys.length; i++) {
      if (i > 1 && templateObject.isTemplating) {
        templateObject.isNextTemplateable(obj[keys[i]], writer2);
      }
      if (templateObject.isTemplating) {
        compressors2.string(compressors2, context, keys[i], invertedIndex, writer2, options);
        templateObject.compressTemplateValues(compressors2, context, invertedIndex, writer2, options, obj[keys[i]]);
      } else {
        var key = keys[i];
        var val = obj[key];
        if (val !== void 0) {
          compressors2.string(compressors2, context, key, invertedIndex, writer2, options);
          compressors2.any(compressors2, context, val, invertedIndex, writer2, options);
        }
      }
    }
    if (templateObject.isTemplating) {
      templateObject.end(writer2);
    }
    writer2.write(constants_1$5.OBJECT_END_TOKEN);
  }
  object$1.compressObject = compressObject;
  var date = {};
  Object.defineProperty(date, "__esModule", { value: true });
  var constants_1$4 = constants;
  var util_1$2 = util$1;
  function compressDate(compressors2, context, obj, invertedIndex, writer2, options) {
    var foundRef;
    var lowPrecisionDate = obj / constants_1$4.DATE_LOW_PRECISION;
    var isLowPrecision = lowPrecisionDate % 1 === 0;
    if (isLowPrecision) {
      if ((foundRef = invertedIndex.lpDateMap[lowPrecisionDate]) !== void 0) {
        writer2.write("" + constants_1$4.REF_LP_DATE_TOKEN + foundRef);
      } else {
        var ref2 = util_1$2.compressInteger(invertedIndex.lpDateCount);
        var compressedDate = util_1$2.compressInteger(lowPrecisionDate);
        var newRef = "" + constants_1$4.LP_DATE_TOKEN + compressedDate;
        if (ref2.length + constants_1$4.REFERENCE_HEADER_LENGTH < newRef.length) {
          invertedIndex.lpDateMap[lowPrecisionDate] = ref2;
          invertedIndex.lpDateCount++;
          writer2.write(newRef);
        } else {
          writer2.write("" + constants_1$4.UNREFERENCED_LP_DATE_TOKEN + compressedDate);
        }
      }
    } else {
      if ((foundRef = invertedIndex.dateMap[obj]) !== void 0) {
        writer2.write("" + constants_1$4.REF_DATE_TOKEN + foundRef);
      } else {
        var ref2 = util_1$2.compressInteger(invertedIndex.dateCount);
        var compressedDate = util_1$2.compressInteger(obj);
        var newRef = "" + constants_1$4.DATE_TOKEN + compressedDate;
        if (ref2.length + constants_1$4.REFERENCE_HEADER_LENGTH < newRef.length) {
          invertedIndex.dateMap[obj] = ref2;
          invertedIndex.dateCount++;
          writer2.write(newRef);
        } else {
          writer2.write("" + constants_1$4.UNREFERENCED_DATE_TOKEN + compressedDate);
        }
      }
    }
  }
  date.compressDate = compressDate;
  var object = {};
  var util = {};
  Object.defineProperty(util, "__esModule", { value: true });
  function isObject(obj) {
    var type = typeof obj;
    if (type === "number") {
      return false;
    } else if (type === "string") {
      return false;
    } else if (type === "boolean") {
      return false;
    } else if (obj === null) {
      return false;
    } else if (Array.isArray(obj)) {
      return false;
    } else if (obj instanceof Date) {
      return false;
    } else if (obj === void 0) {
      return false;
    } else {
      return true;
    }
  }
  util.isObject = isObject;
  Object.defineProperty(object, "__esModule", { value: true });
  var constants_1$3 = constants;
  var util_1$1 = util;
  var TemplateObject = (
    /** @class */
    function() {
      function TemplateObject2(a, b) {
        this.isTemplating = false;
        this.struct = [];
        if (a != null && b != null) {
          this.isTemplating = buildTemplate(a, b, this.struct);
        }
      }
      TemplateObject2.prototype.compressTemplate = function(compressors2, context, invertedIndex, writer2, options) {
        compresObjectTemplate(compressors2, context, invertedIndex, writer2, options, this.struct);
      };
      TemplateObject2.prototype.compressTemplateValues = function(compressors2, context, invertedIndex, writer2, options, obj) {
        compressObjectValues(compressors2, context, invertedIndex, writer2, options, this.struct, obj);
      };
      TemplateObject2.prototype.isNextTemplateable = function(obj, writer2) {
        this.isTemplating = conformsToStructure(this.struct, obj);
        if (!this.isTemplating) {
          writer2.write(constants_1$3.TEMPLATE_OBJECT_FINAL);
        }
      };
      TemplateObject2.prototype.end = function(writer2) {
        writer2.write(constants_1$3.TEMPLATE_OBJECT_FINAL);
      };
      return TemplateObject2;
    }()
  );
  object.TemplateObject = TemplateObject;
  function buildTemplate(a, b, struct, level) {
    if (level === void 0) {
      level = 0;
    }
    if (level > 6) {
      return false;
    }
    var keysA = Object.keys(a);
    var keysB = Object.keys(b);
    if (keysA.length !== keysB.length) {
      return false;
    }
    if (keysA.length > 10) {
      return false;
    }
    keysA.sort(function(a2, b2) {
      return a2.localeCompare(b2);
    });
    keysB.sort(function(a2, b2) {
      return a2.localeCompare(b2);
    });
    for (var i = 0; i < keysA.length; i++) {
      var keyA = keysA[i];
      var keyB = keysB[i];
      if (keyA !== keyB) {
        return false;
      }
      var valueA = a[keyA];
      var valueB = b[keyB];
      if (util_1$1.isObject(valueA)) {
        if (!util_1$1.isObject(valueB)) {
          return false;
        }
        var nextStruct = [];
        struct.push([keyA, nextStruct]);
        if (!buildTemplate(valueA, valueB, nextStruct, level + 1)) {
          return false;
        }
      } else if (util_1$1.isObject(valueB)) {
        return false;
      } else {
        struct.push([keyA]);
      }
    }
    return level > 0 || util_1$1.isObject(a);
  }
  function conformsToStructure(struct, obj) {
    if (!obj) {
      return false;
    }
    if (Object.keys(obj).length !== struct.length) {
      return false;
    }
    for (var i = 0; i < struct.length; i++) {
      var key = struct[i][0];
      var isNested = struct[i].length > 1;
      if (obj[key] === void 0) {
        return false;
      }
      if (isNested) {
        var x = struct[i];
        x[1];
        if (!conformsToStructure(struct[i][1], obj[key])) {
          return false;
        }
      } else {
        if (util_1$1.isObject(obj[key])) {
          return false;
        }
      }
    }
    return true;
  }
  function compresObjectTemplate(compressors2, context, invertedIndex, writer2, options, struct) {
    writer2.write(constants_1$3.TEMPLATE_OBJECT_START);
    for (var i = 0; i < struct.length; i++) {
      var key = struct[i][0];
      var isNested = struct[i].length > 1;
      compressors2.string(compressors2, context, key, invertedIndex, writer2, options);
      if (isNested) {
        compresObjectTemplate(compressors2, context, invertedIndex, writer2, options, struct[i][1]);
      }
    }
    writer2.write(constants_1$3.TEMPLATE_OBJECT_END);
  }
  function compressObjectValues(compressors2, context, invertedIndex, writer2, options, struct, obj) {
    for (var i = 0; i < struct.length; i++) {
      var key = struct[i][0];
      var value = obj[key];
      var isNested = struct[i].length > 1;
      if (isNested) {
        compressObjectValues(compressors2, context, invertedIndex, writer2, options, struct[i][1], value);
      } else {
        compressors2.any(compressors2, context, value, invertedIndex, writer2, options);
      }
    }
  }
  Object.defineProperty(compress$1, "__esModule", { value: true });
  var any_1 = any;
  var array_1 = array;
  var string_1 = string;
  var number_1 = number;
  var object_1 = object$1;
  var date_1 = date;
  var object_2 = object;
  var compressors = {
    any: any_1.compressAny,
    array: array_1.compressArray,
    object: object_1.compressObject,
    string: string_1.compressString,
    date: date_1.compressDate,
    number: number_1.compressNumber,
    template: {
      Object: object_2.TemplateObject
    }
  };
  function makeCompressContext() {
    return {
      arrayItemWriters: [],
      arrayLevel: 0
    };
  }
  compress$1.makeCompressContext = makeCompressContext;
  function makeInvertedIndex() {
    return {
      stringMap: {},
      integerMap: {},
      floatMap: {},
      dateMap: {},
      lpDateMap: {},
      stringCount: 0,
      integerCount: 0,
      floatCount: 0,
      dateCount: 0,
      lpDateCount: 0
    };
  }
  compress$1.makeInvertedIndex = makeInvertedIndex;
  function compress(context, obj, invertedIndex, writer2, options) {
    compressors.any(compressors, context, obj, invertedIndex, writer2, options);
  }
  compress$1.compress = compress;
  var decompress$1 = {};
  var common = {};
  (function(exports) {
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.SKIP_SCALAR = {};
    (function(TargetType) {
      TargetType["ARRAY"] = "ARRAY";
      TargetType["OBJECT"] = "OBJECT";
      TargetType["SCALAR"] = "SCALAR";
      TargetType["TEMPLATE_OBJECT"] = "TEMPLATE_OBJECT";
      TargetType["TEMPLATE_OBJECT_PROPERTIES"] = "TEMPLATE_OBJECT_PROPERTIES";
      TargetType["TEMPLATE_OBJECT_ELEMENTS"] = "TEMPLATE_OBJECT_ELEMENTS";
    })(exports.TargetType || (exports.TargetType = {}));
  })(common);
  var stages = {};
  var scalar = {};
  Object.defineProperty(scalar, "__esModule", { value: true });
  var constants_1$2 = constants;
  var common_1$3 = common;
  var util_1 = util$1;
  function decompressScalar(token, data, cursor, orderedIndex) {
    var startIndex = cursor.index;
    var endIndex = cursor.index + 1;
    var foundStringToken;
    if (token === constants_1$2.STRING_TOKEN && (foundStringToken = constants_1$2.STRING_TOKEN) || token === constants_1$2.UNREFERENCED_STRING_TOKEN && (foundStringToken = constants_1$2.UNREFERENCED_STRING_TOKEN)) {
      var escaped = true;
      while (escaped && endIndex < data.length) {
        endIndex = data.indexOf(foundStringToken, endIndex);
        var iNumEscapeCharacters = 1;
        escaped = false;
        while (data[endIndex - iNumEscapeCharacters] === constants_1$2.ESCAPE_CHARACTER) {
          escaped = iNumEscapeCharacters % 2 === 1;
          iNumEscapeCharacters++;
        }
        endIndex++;
      }
      if (endIndex <= startIndex) {
        endIndex = data.length;
      }
    } else {
      while (!(data.charCodeAt(endIndex) > constants_1$2.DELIMITING_TOKENS_THRESHOLD) && endIndex < data.length) {
        endIndex++;
      }
    }
    if (!cursor.drain && endIndex === data.length) {
      return common_1$3.SKIP_SCALAR;
    }
    cursor.index = endIndex - 1;
    var tokenCharCode = token.charCodeAt(0);
    if (tokenCharCode > constants_1$2.INTEGER_SMALL_TOKEN_EXCLUSIVE_BOUND_LOWER && tokenCharCode < constants_1$2.INTEGER_SMALL_TOKEN_EXCLUSIVE_BOUND_UPPER) {
      return tokenCharCode + constants_1$2.INTEGER_SMALL_TOKEN_OFFSET;
    } else if (token === constants_1$2.ARRAY_REPEAT_MANY_TOKEN) {
      return util_1.decompressInteger(data.substring(startIndex + 1, endIndex));
    } else if (token === constants_1$2.REF_STRING_TOKEN) {
      return orderedIndex.strings[util_1.decompressInteger(data.substring(startIndex + 1, endIndex))];
    } else if (token === constants_1$2.REF_INTEGER_TOKEN) {
      return orderedIndex.integers[util_1.decompressInteger(data.substring(startIndex + 1, endIndex))];
    } else if (token === constants_1$2.REF_FLOAT_TOKEN) {
      return orderedIndex.floats[util_1.decompressInteger(data.substring(startIndex + 1, endIndex))];
    } else if (token === constants_1$2.REF_DATE_TOKEN) {
      return orderedIndex.dates[util_1.decompressInteger(data.substring(startIndex + 1, endIndex))];
    } else if (token === constants_1$2.REF_LP_DATE_TOKEN) {
      return orderedIndex.lpDates[util_1.decompressInteger(data.substring(startIndex + 1, endIndex))];
    } else if (token === constants_1$2.STRING_TOKEN) {
      return orderedIndex.strings[orderedIndex.strings.length] = data.substring(startIndex + 1, endIndex - 1).replace(constants_1$2.REGEX_ESCAPED_ESCAPE_CHARACTER, constants_1$2.ESCAPE_CHARACTER).replace(constants_1$2.REGEX_ESCAPED_STRING_TOKEN, constants_1$2.STRING_TOKEN);
    } else if (token === constants_1$2.INTEGER_TOKEN) {
      return orderedIndex.integers[orderedIndex.integers.length] = util_1.decompressInteger(data.substring(startIndex + 1, endIndex));
    } else if (token === constants_1$2.FLOAT_TOKEN) {
      return orderedIndex.floats[orderedIndex.floats.length] = util_1.decompressFloat(data.substring(startIndex + 1, endIndex));
    } else if (token === constants_1$2.DATE_TOKEN) {
      return orderedIndex.dates[orderedIndex.dates.length] = new Date(util_1.decompressInteger(data.substring(startIndex + 1, endIndex))).toISOString();
    } else if (token === constants_1$2.LP_DATE_TOKEN) {
      return orderedIndex.lpDates[orderedIndex.lpDates.length] = new Date(constants_1$2.DATE_LOW_PRECISION * util_1.decompressInteger(data.substring(startIndex + 1, endIndex))).toISOString();
    } else if (token === constants_1$2.UNREFERENCED_STRING_TOKEN) {
      return data.substring(startIndex + 1, endIndex - 1).replace(constants_1$2.REGEX_ESCAPED_ESCAPE_CHARACTER, constants_1$2.ESCAPE_CHARACTER).replace(constants_1$2.REGEX_UNREFERENCED_ESCAPED_STRING_TOKEN, constants_1$2.UNREFERENCED_STRING_TOKEN);
    } else if (token === constants_1$2.UNREFERENCED_INTEGER_TOKEN) {
      return util_1.decompressInteger(data.substring(startIndex + 1, endIndex));
    } else if (token === constants_1$2.UNREFERENCED_FLOAT_TOKEN) {
      return util_1.decompressFloat(data.substring(startIndex + 1, endIndex));
    } else if (token === constants_1$2.UNREFERENCED_DATE_TOKEN) {
      return new Date(util_1.decompressInteger(data.substring(startIndex + 1, endIndex))).toISOString();
    } else if (token === constants_1$2.UNREFERENCED_LP_DATE_TOKEN) {
      return new Date(constants_1$2.DATE_LOW_PRECISION * util_1.decompressInteger(data.substring(startIndex + 1, endIndex))).toISOString();
    } else if (token === constants_1$2.BOOLEAN_TRUE_TOKEN) {
      return true;
    } else if (token === constants_1$2.BOOLEAN_FALSE_TOKEN) {
      return false;
    } else if (token === constants_1$2.NULL_TOKEN) {
      return null;
    } else if (token === constants_1$2.UNDEFINED_TOKEN) {
      return void 0;
    }
    throw new Error("Unexpected scalar " + token + " at " + startIndex + "-" + endIndex);
  }
  scalar.decompressScalar = decompressScalar;
  var element = {};
  var template = {};
  Object.defineProperty(template, "__esModule", { value: true });
  function appendTemplateObjectValue(templateObjectTarget, targetValue) {
    var currentPath = templateObjectTarget.paths[templateObjectTarget.currentPathIndex];
    var i = 0;
    var targetObject = templateObjectTarget.currentObject;
    for (; i < currentPath.length - 1; i++) {
      var fragment = currentPath[i];
      targetObject = targetObject[fragment] = targetObject[fragment] || {};
    }
    if (targetValue !== void 0) {
      targetObject[currentPath[i]] = targetValue;
    }
  }
  function appendTemplateObjectPropertiesValue(templateObjectElementsTarget, targetValue) {
    if (templateObjectElementsTarget.currentPathIndex === -1) {
      templateObjectElementsTarget.value[targetValue] = templateObjectElementsTarget.currentObject = {};
    } else {
      appendTemplateObjectValue(templateObjectElementsTarget, targetValue);
    }
    if (++templateObjectElementsTarget.currentPathIndex === templateObjectElementsTarget.expectedPaths) {
      templateObjectElementsTarget.currentPathIndex = -1;
    }
  }
  template.appendTemplateObjectPropertiesValue = appendTemplateObjectPropertiesValue;
  function appendTemplateObjectElementsValue(templateObjectPropertiesTarget, targetValue) {
    if (templateObjectPropertiesTarget.currentPathIndex === 0) {
      templateObjectPropertiesTarget.currentObject = {};
      templateObjectPropertiesTarget.value.push(templateObjectPropertiesTarget.currentObject);
    }
    appendTemplateObjectValue(templateObjectPropertiesTarget, targetValue);
    if (++templateObjectPropertiesTarget.currentPathIndex === templateObjectPropertiesTarget.expectedPaths) {
      templateObjectPropertiesTarget.currentPathIndex = 0;
    }
  }
  template.appendTemplateObjectElementsValue = appendTemplateObjectElementsValue;
  Object.defineProperty(element, "__esModule", { value: true });
  var constants_1$1 = constants;
  var common_1$2 = common;
  var scalar_1$1 = scalar;
  var template_1 = template;
  function decompressElement(c, cursor, data, orderedIndex) {
    var targetValue;
    if (c === constants_1$1.ARRAY_END_TOKEN || c === constants_1$1.OBJECT_END_TOKEN) {
      targetValue = cursor.currentTarget.value;
      cursor.currentTarget = cursor.stack[cursor.pointer - 1];
      cursor.pointer--;
    } else {
      targetValue = scalar_1$1.decompressScalar(c, data, cursor, orderedIndex);
      if (targetValue === common_1$2.SKIP_SCALAR) {
        return false;
      }
    }
    if (cursor.currentTarget.type === common_1$2.TargetType.SCALAR) {
      cursor.currentTarget.value = targetValue;
    } else if (cursor.currentTarget.type === common_1$2.TargetType.ARRAY) {
      cursor.currentTarget.value[cursor.currentTarget.value.length] = targetValue;
    } else if (cursor.currentTarget.type === common_1$2.TargetType.OBJECT) {
      if (cursor.currentTarget.key != null) {
        cursor.currentTarget.value[cursor.currentTarget.key] = targetValue;
        cursor.currentTarget.key = void 0;
      } else {
        cursor.currentTarget.key = targetValue;
      }
    } else if (cursor.currentTarget.type === common_1$2.TargetType.TEMPLATE_OBJECT) {
      cursor.currentTarget.currentToken = targetValue;
      cursor.currentTarget.currentTokens.push(targetValue);
    } else if (cursor.currentTarget.type === common_1$2.TargetType.TEMPLATE_OBJECT_PROPERTIES) {
      template_1.appendTemplateObjectPropertiesValue(cursor.currentTarget, targetValue);
    } else if (cursor.currentTarget.type === common_1$2.TargetType.TEMPLATE_OBJECT_ELEMENTS) {
      template_1.appendTemplateObjectElementsValue(cursor.currentTarget, targetValue);
    }
    return true;
  }
  element.decompressElement = decompressElement;
  Object.defineProperty(stages, "__esModule", { value: true });
  var constants_1 = constants;
  var common_1$1 = common;
  var scalar_1 = scalar;
  var element_1 = element;
  function decompressStages(cursor, data, orderedIndex) {
    for (; cursor.index < data.length; cursor.index++) {
      var c = data[cursor.index];
      if (c === constants_1.ARRAY_START_TOKEN) {
        cursor.currentTarget = { type: common_1$1.TargetType.ARRAY, value: [] };
        cursor.stack[++cursor.pointer] = cursor.currentTarget;
      } else if (c === constants_1.OBJECT_START_TOKEN) {
        cursor.currentTarget = { type: common_1$1.TargetType.OBJECT, value: {} };
        cursor.stack[++cursor.pointer] = cursor.currentTarget;
      } else if (c === constants_1.ARRAY_REPEAT_TOKEN && (cursor.currentTarget.type === common_1$1.TargetType.ARRAY || cursor.currentTarget.type === common_1$1.TargetType.TEMPLATE_OBJECT_ELEMENTS)) {
        var repeatedItem = cursor.currentTarget.value[cursor.currentTarget.value.length - 1];
        cursor.currentTarget.value.push(repeatedItem);
      } else if (c === constants_1.ARRAY_REPEAT_MANY_TOKEN && (cursor.currentTarget.type === common_1$1.TargetType.ARRAY || cursor.currentTarget.type === common_1$1.TargetType.TEMPLATE_OBJECT_ELEMENTS)) {
        var repeatCount = scalar_1.decompressScalar(data[cursor.index], data, cursor, orderedIndex);
        if (repeatCount === common_1$1.SKIP_SCALAR) {
          return;
        }
        var repeatedItem = cursor.currentTarget.value[cursor.currentTarget.value.length - 1];
        for (var i = 0; i < repeatCount; i++) {
          cursor.currentTarget.value.push(repeatedItem);
        }
      } else if (c === constants_1.TEMPLATE_OBJECT_START && (cursor.currentTarget.type === common_1$1.TargetType.TEMPLATE_OBJECT || cursor.currentTarget.type === common_1$1.TargetType.OBJECT || cursor.currentTarget.type === common_1$1.TargetType.ARRAY)) {
        if (cursor.currentTarget.type !== common_1$1.TargetType.TEMPLATE_OBJECT) {
          var parentTarget = cursor.currentTarget;
          cursor.currentTarget = { type: common_1$1.TargetType.TEMPLATE_OBJECT, value: void 0, currentTokens: [], currentRoute: [], paths: [], level: 0, parentTarget };
          cursor.stack[++cursor.pointer] = cursor.currentTarget;
        } else {
          for (var i = 0; i < cursor.currentTarget.currentTokens.length - 1; i++) {
            var currentToken = cursor.currentTarget.currentTokens[i];
            cursor.currentTarget.paths[cursor.currentTarget.paths.length] = cursor.currentTarget.currentRoute.concat(currentToken);
          }
          if (cursor.currentTarget.currentToken != null) {
            cursor.currentTarget.currentRoute.push(cursor.currentTarget.currentToken);
          }
          cursor.currentTarget.currentTokens = [];
          cursor.currentTarget.level++;
        }
      } else if (c === constants_1.TEMPLATE_OBJECT_END && cursor.currentTarget.type === common_1$1.TargetType.TEMPLATE_OBJECT) {
        for (var i = 0; i < cursor.currentTarget.currentTokens.length; i++) {
          var currentToken = cursor.currentTarget.currentTokens[i];
          cursor.currentTarget.paths[cursor.currentTarget.paths.length] = cursor.currentTarget.currentRoute.concat(currentToken);
        }
        cursor.currentTarget.currentTokens = [];
        cursor.currentTarget.currentRoute = cursor.currentTarget.currentRoute.slice(0, -1);
        cursor.currentTarget.level--;
        if (cursor.currentTarget.level < 0) {
          var paths = cursor.currentTarget.paths;
          var parentTarget = cursor.currentTarget.parentTarget;
          cursor.pointer--;
          if (parentTarget.type === common_1$1.TargetType.ARRAY) {
            cursor.currentTarget = { type: common_1$1.TargetType.TEMPLATE_OBJECT_ELEMENTS, value: parentTarget.value, paths, currentPathIndex: 0, expectedPaths: paths.length, currentObject: {} };
          } else if (parentTarget.type === common_1$1.TargetType.OBJECT) {
            cursor.currentTarget = { type: common_1$1.TargetType.TEMPLATE_OBJECT_PROPERTIES, value: parentTarget.value, paths, currentPathIndex: -1, expectedPaths: paths.length, currentObject: {} };
          }
          cursor.stack[++cursor.pointer] = cursor.currentTarget;
        }
      } else if (c === constants_1.TEMPLATE_OBJECT_FINAL) {
        cursor.currentTarget = cursor.stack[--cursor.pointer];
      } else {
        if (!element_1.decompressElement(c, cursor, data, orderedIndex)) {
          return;
        }
      }
    }
  }
  stages.decompressStages = decompressStages;
  Object.defineProperty(decompress$1, "__esModule", { value: true });
  var common_1 = common;
  var stages_1 = stages;
  function makeOrderedIndex() {
    return {
      strings: [],
      integers: [],
      floats: [],
      dates: [],
      lpDates: []
    };
  }
  decompress$1.makeOrderedIndex = makeOrderedIndex;
  function makeCursor(drain) {
    var rootTarget = { type: common_1.TargetType.SCALAR, value: void 0 };
    var stack = new Array(10);
    stack[0] = rootTarget;
    return { index: 0, rootTarget, stack, currentTarget: rootTarget, pointer: 0, drain };
  }
  function decompress(data, orderedIndex) {
    var cursor = makeCursor(true);
    stages_1.decompressStages(cursor, data, orderedIndex);
    return cursor.rootTarget.value;
  }
  decompress$1.decompress = decompress;
  function decompressIncremental(orderedIndex) {
    var cursor = makeCursor(false);
    var buffer = "";
    function increment(data) {
      if (data === null) {
        cursor.drain = true;
      } else if (data.length === 0) {
        return;
      } else {
        buffer += data;
      }
      var cursorIndexBefore = cursor.index;
      stages_1.decompressStages(cursor, buffer, orderedIndex);
      var movedAmount = cursor.index - cursorIndexBefore;
      if (movedAmount > 0) {
        buffer = buffer.substring(movedAmount);
        cursor.index -= movedAmount;
      }
    }
    return { increment, cursor };
  }
  decompress$1.decompressIncremental = decompressIncremental;
  (function(exports) {
    function __export(m) {
      for (var p in m)
        if (!exports.hasOwnProperty(p))
          exports[p] = m[p];
    }
    Object.defineProperty(exports, "__esModule", { value: true });
    var compress_1 = compress$1;
    var writer_12 = writer;
    var decompress_1 = decompress$1;
    __export(writer);
    __export(common);
    function parse(data) {
      var orderedIndex = decompress_1.makeOrderedIndex();
      return decompress_1.decompress(data, orderedIndex);
    }
    exports.parse = parse;
    function parseIncremental() {
      var orderedIndex = decompress_1.makeOrderedIndex();
      var _a = decompress_1.decompressIncremental(orderedIndex), cursor = _a.cursor, increment = _a.increment;
      return function(data) {
        increment(data);
        if (data === null) {
          return cursor.rootTarget.value;
        }
      };
    }
    exports.parseIncremental = parseIncremental;
    function stringifyTo(data, writer2, options) {
      if (options === void 0) {
        options = {};
      }
      var invertedIndex = compress_1.makeInvertedIndex();
      var context = compress_1.makeCompressContext();
      compress_1.compress(context, data, invertedIndex, writer2, options);
      writer2.end();
    }
    exports.stringifyTo = stringifyTo;
    function stringify(data, options) {
      var writer2 = new writer_12.ZipsonStringWriter();
      stringifyTo(data, writer2, options);
      return writer2.value;
    }
    exports.stringify = stringify;
  })(lib);
  const useStore = defineStore("store", {
    state() {
      return {
        // ####################
        // 元件相關狀態
        // ####################
        isDarkMode: false,
        isToolbarsShow: false,
        isPopupShow: {
          sheet: false,
          settings: false,
          menu: false
        },
        // ####################
        // 偏好設定相關狀態
        // ####################
        agreeToArchiveSheet: true,
        // ####################
        // 譜面相關狀態
        // ####################
        transpose: 0,
        /** 在 `StoreHandler` 裡賦值 */
        originalCapo: 0,
        /** 在 `StoreHandler` 裡賦值,HTML 格式 */
        originalKey: ""
      };
    },
    persist: {
      key: "preferences",
      storage: MonkeyStorage,
      deserialize: lib.parse,
      serialize: lib.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();
        }
        this.isToolbarsShow = !this.isToolbarsShow;
      },
      closePopups() {
        for (const popup in this.isPopupShow) {
          this.isPopupShow[popup] = false;
        }
      },
      /** @param {'sheet'|'settings'|'menu'} name */
      openPopup(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$7 = { class: "material-symbols-rounded" };
  const _sfc_main$8 = {
    __name: "ToolbarIcon",
    props: {
      icon: String,
      color: {
        type: String,
        default: "#fffe"
      }
    },
    setup(__props) {
      vue.useCssVars((_ctx) => ({
        "46f2ba82": __props.color
      }));
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createElementBlock("span", _hoisted_1$7, vue.toDisplayString(__props.icon), 1);
      };
    }
  };
  const ToolbarIcon = /* @__PURE__ */ _export_sfc(_sfc_main$8, [["__scopeId", "data-v-db75303e"]]);
  const _hoisted_1$6 = { id: "plus91-sheet-popup" };
  const _hoisted_2$5 = { class: "sheet-popup-container" };
  const _hoisted_3$1 = { class: "transpose-button-group" };
  const _hoisted_4$1 = { class: "text-capo" };
  const _hoisted_5$1 = ["innerHTML"];
  const _hoisted_6$1 = { class: "transpose-range-container" };
  const _hoisted_7 = ["value"];
  const _sfc_main$7 = {
    __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$6, [
              vue.createElementVNode("div", _hoisted_2$5, [
                vue.createElementVNode("div", _hoisted_3$1, [
                  vue.createElementVNode("button", {
                    class: "capo-button decrease",
                    onClick: _cache[0] || (_cache[0] = () => {
                      vue.unref(store).plusTranspose(-1);
                    })
                  }, [
                    vue.createVNode(ToolbarIcon, {
                      icon: "arrow_left",
                      color: "black"
                    })
                  ]),
                  vue.createElementVNode("button", {
                    class: "capo-button info",
                    onClick: _cache[1] || (_cache[1] = () => {
                      vue.unref(store).transpose = 0;
                    })
                  }, [
                    vue.createTextVNode(" CAPO:"),
                    vue.createElementVNode("span", _hoisted_4$1, vue.toDisplayString(vue.unref(store).currentCapo), 1),
                    vue.createTextVNode(" ("),
                    vue.createElementVNode("span", {
                      class: "text-key",
                      innerHTML: vue.unref(store).currentKey
                    }, null, 8, _hoisted_5$1),
                    vue.createTextVNode(") ")
                  ]),
                  vue.createElementVNode("button", {
                    class: "capo-button increase",
                    onClick: _cache[2] || (_cache[2] = () => {
                      vue.unref(store).plusTranspose(1);
                    })
                  }, [
                    vue.createVNode(ToolbarIcon, {
                      icon: "arrow_right",
                      color: "black"
                    })
                  ])
                ]),
                vue.createElementVNode("div", _hoisted_6$1, [
                  vue.createElementVNode("input", {
                    type: "range",
                    min: "-11",
                    max: "11",
                    value: vue.unref(store).currentCapo,
                    onInput: _cache[3] || (_cache[3] = ($event) => {
                      vue.unref(store).transpose = $event.target.value - vue.unref(store).originalCapo;
                    })
                  }, null, 40, _hoisted_7)
                ])
              ])
            ], 512), [
              [vue.vShow, vue.unref(store).isPopupShow.sheet]
            ])
          ]),
          _: 1
        });
      };
    }
  };
  const SheetPopup = /* @__PURE__ */ _export_sfc(_sfc_main$7, [["__scopeId", "data-v-95b1456e"]]);
  const _withScopeId = (n) => (vue.pushScopeId("data-v-430d5768"), n = n(), vue.popScopeId(), n);
  const _hoisted_1$5 = { id: "plus91-settings-popup" };
  const _hoisted_2$4 = { class: "settings-popup-container" };
  const _hoisted_3 = { class: "setting-item" };
  const _hoisted_4 = /* @__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$6 = {
    __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$5, [
              vue.createElementVNode("div", _hoisted_2$4, [
                vue.createElementVNode("label", _hoisted_3, [
                  _hoisted_4,
                  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$6, [["__scopeId", "data-v-430d5768"]]);
  const _hoisted_1$4 = { class: "icon-button" };
  const _hoisted_2$3 = { class: "button-text" };
  const _sfc_main$5 = {
    __name: "MenuButton",
    props: {
      icon: String,
      name: String,
      color: String
    },
    setup(__props) {
      vue.useCssVars((_ctx) => ({
        "045d7cb5": __props.color
      }));
      const props = __props;
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$4, [
          vue.createVNode(ToolbarIcon, {
            icon: props.icon,
            color: props.color
          }, null, 8, ["icon", "color"]),
          vue.createElementVNode("div", _hoisted_2$3, vue.toDisplayString(props.name), 1)
        ]);
      };
    }
  };
  const MenuButton = /* @__PURE__ */ _export_sfc(_sfc_main$5, [["__scopeId", "data-v-29985f23"]]);
  const _hoisted_1$3 = { id: "plus91-menu-popup" };
  const _hoisted_2$2 = { class: "menu-popup-container" };
  const _sfc_main$4 = {
    __name: "MenuPopup",
    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.createVNode(MenuButton, {
                  icon: "keyboard",
                  name: "快捷鍵",
                  color: "gray"
                }),
                vue.createVNode(MenuButton, {
                  icon: "info",
                  name: "使用說明",
                  color: "gray"
                })
              ])
            ], 512), [
              [vue.vShow, vue.unref(store).isPopupShow.menu]
            ])
          ]),
          _: 1
        });
      };
    }
  };
  const MenuPopup = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["__scopeId", "data-v-2a47f935"]]);
  const _hoisted_1$2 = { id: "plus91-footer" };
  const _hoisted_2$1 = { class: "footer-container" };
  const _sfc_main$3 = {
    __name: "Footer",
    props: {
      active: Boolean
    },
    setup(__props) {
      const store = useStore();
      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",
                  onClick: _cache[0] || (_cache[0] = ($event) => vue.unref(store).openPopup("sheet"))
                }),
                vue.createVNode(ToolbarIcon, {
                  icon: "settings",
                  onClick: _cache[1] || (_cache[1] = ($event) => vue.unref(store).openPopup("settings"))
                }),
                vue.createVNode(SheetPopup),
                vue.createVNode(SettingsPopup),
                vue.createVNode(MenuPopup)
              ])
            ], 512), [
              [vue.vShow, __props.active]
            ])
          ]),
          _: 1
        });
      };
    }
  };
  const Footer = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["__scopeId", "data-v-6cf0f22c"]]);
  const _hoisted_1$1 = { id: "plus91-header" };
  const _hoisted_2 = { class: "header-container" };
  const _sfc_main$2 = {
    __name: "Header",
    props: {
      active: Boolean
    },
    setup(__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();
      };
      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: "arrow_back_ios_new",
                  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"])
                }, null, 544), [
                  [
                    vue.vModelText,
                    searchText.value,
                    void 0,
                    { trim: true }
                  ]
                ]),
                vue.createVNode(ToolbarIcon, {
                  icon: "search",
                  onClick: _cache[1] || (_cache[1] = ($event) => search())
                })
              ])
            ], 512), [
              [vue.vShow, __props.active]
            ])
          ]),
          _: 1
        });
      };
    }
  };
  const Header = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-183b97c5"]]);
  const _hoisted_1 = { id: "dark-mode-overlay" };
  const _sfc_main$1 = {
    __name: "DarkModeOverlay",
    props: {
      active: Boolean
    },
    setup(__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-a07a2507"]]);
  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(Header, {
            active: vue.unref(store).isToolbarsShow
          }, null, 8, ["active"]),
          vue.createVNode(Footer, {
            active: vue.unref(store).isToolbarsShow
          }, null, 8, ["active"]),
          vue.createVNode(DarkModeOverlay, {
            active: vue.unref(store).isDarkMode
          }, null, 8, ["active"])
        ], 64);
      };
    }
  };
  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;
    }
  }
  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 injectIconFont() {
    const materialSymbols = document.createElement("link");
    materialSymbols.rel = "stylesheet";
    materialSymbols.href = "https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@24,500,0,0";
    document.head.appendChild(materialSymbols);
  }
  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);
  }
  class StoreHandler {
    constructor() {
      /** 當 `#store.transpose` 變動時,將譜面上的和弦進行移調 */
      __privateAdd(this, _watchTranspose);
      // 命 `#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 params = getQueryParams();
      if (params.transpose) {
        __privateGet(this, _store).transpose = params.transpose;
      }
    }
    start() {
      __privateMethod(this, _watchTranspose, watchTranspose_fn).call(this);
      return this;
    }
  }
  _store = new WeakMap();
  _watchTranspose = new WeakSet();
  watchTranspose_fn = function() {
    vue.watch(() => {
      return __privateGet(this, _store).transpose;
    }, (newValue, oldValue) => {
      ChordSheetElement.transposeSheet(newValue - oldValue);
    });
  };
  function init() {
    redirect();
    injectGtag();
    injectIconFont();
    initMutationObserver();
    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);