自由轉調、輕鬆練歌,打造 91 譜的最佳體驗!
// ==UserScript== // @name 91 Plus // @namespace https://github.com/DonkeyBear // @version 1.7.0 // @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)})(' @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-33d92514]{color:var(--2d5c579c);font-size:var(--0168355c);-webkit-text-stroke:var(--5c7f6b49) var(--2d5c579c)}.bi[data-v-33d92514]:before{transition:text-shadow .2s}.bi[active=true][data-v-33d92514]:before{text-shadow:0 0 .5rem rgb(75,156,169)}.toolbar-icon[data-v-2b5c1219]{cursor:pointer;padding:.25rem .75rem}.adjust-widget[data-v-1c41dc23]{display:flex}.adjust-widget .adjust-button[data-v-1c41dc23]{border:0;border-radius:.25rem;background:transparent}.adjust-widget .adjust-button[data-v-1c41dc23]:hover{background:rgba(0,0,0,.025)}.adjust-widget .adjust-button[data-v-1c41dc23]:disabled{opacity:.25}.adjust-widget .adjust-button.adjust-button-middle[data-v-1c41dc23]{flex-grow:1;color:var(--791a4684);font-size:calc(var(--37e19b46) * .75);font-weight:700}.adjust-widget .adjust-button.adjust-button-left[data-v-1c41dc23]{padding-right:1rem}.adjust-widget .adjust-button.adjust-button-right[data-v-1c41dc23]{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}.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-1eb5094e]{font-size:.5rem;font-weight:900;color:#666;text-align:center}.chord-container .chord-chart[data-v-1eb5094e]{margin:-.6rem 0 -.25rem}.slide-and-fade-enter-active[data-v-84de48ae],.slide-and-fade-leave-active[data-v-84de48ae]{transition:all .2s}.slide-and-fade-enter-from[data-v-84de48ae],.slide-and-fade-leave-to[data-v-84de48ae]{transform:translateY(10%);opacity:0}#plus91-chord-popup[data-v-84de48ae]{position:absolute;left:0;right:0;bottom:100%;background:#fafafa;border:1px solid lightgray;border-radius:1rem;margin:.5rem 1rem;padding:1rem}#plus91-chord-popup .banner[data-v-84de48ae]{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-84de48ae]{flex-grow:1;margin-left:.5rem}#plus91-chord-popup .chord-popup-container[data-v-84de48ae]{display:grid;grid-template-columns:repeat(6,1fr);column-gap:.5rem;padding-top:.4rem}.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}.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;padding:1rem}#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-d041b049],.slide-and-fade-leave-active[data-v-d041b049]{transition:all .2s}.slide-and-fade-enter-from[data-v-d041b049],.slide-and-fade-leave-to[data-v-d041b049]{transform:translateY(10%);opacity:0}#plus91-menu-popup[data-v-d041b049]{position:absolute;left:0;right:0;bottom:100%;background:#fafafa;border:1px solid lightgray;padding:1rem 2rem;border-radius:1rem;margin:.5rem 1rem}#plus91-menu-popup .menu-popup-container[data-v-d041b049]{display:flex;justify-content:space-around}.hotkey-item[data-v-9f1c2e95]{display:flex;justify-content:space-between;align-items:center;padding:0 .25rem;border-radius:.25rem;height:1.4rem}.hotkey-item[data-v-9f1c2e95]:nth-child(odd){background:rgba(0,0,0,.025)}.desc.title[data-v-9f1c2e95]{font-size:.55rem;color:#999}.hotkeys[data-v-9f1c2e95]{display:flex}.hr[data-v-9f1c2e95]{display:flex;flex-grow:1;border-top:1px solid lightgray;margin-left:.25rem}kbd[data-v-9f1c2e95]{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-a88e7568],.slide-and-fade-leave-active[data-v-a88e7568]{transition:all .2s}.slide-and-fade-enter-from[data-v-a88e7568],.slide-and-fade-leave-to[data-v-a88e7568]{transform:translateY(10%);opacity:0}#plus91-hotkey-popup[data-v-a88e7568]{position:absolute;left:0;right:0;bottom:100%;background:#fafafa;border:1px solid lightgray;padding:1rem 2rem;border-radius:1rem;margin:.5rem 1rem}#plus91-hotkey-popup .hotkey-popup-container[data-v-a88e7568]{display:flex;color:#444}#plus91-hotkey-popup section[data-v-a88e7568]{flex-grow:1;width:50%;margin:-.1rem 0}#plus91-hotkey-popup section.left-part[data-v-a88e7568]{border-right:1px solid lightgray;margin-left:-.5rem;padding-right:.5rem}#plus91-hotkey-popup section.right-part[data-v-a88e7568]{padding-left:.5rem;margin-right:-.5rem}#plus91-hotkey-popup kbd[data-v-a88e7568]{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-a29224c8],.slide-leave-active[data-v-a29224c8]{transition:transform .2s}.slide-enter-from[data-v-a29224c8],.slide-leave-to[data-v-a29224c8]{transform:translateY(100%)}#plus91-footer[data-v-a29224c8]{z-index:1000;display:flex;justify-content:center;position:fixed;left:0;right:0;bottom:0}.footer-container[data-v-a29224c8]{width:min(100vw,768px);background:rgba(75,156,169,.65);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);padding:.25rem .5rem .75rem;display:flex;justify-content:space-between;align-items:center;border-radius:1rem 1rem 0 0;border:1px solid rgb(90,140,160);border-bottom:0}.slide-enter-active[data-v-265bcfd2],.slide-leave-active[data-v-265bcfd2]{transition:transform .2s}.slide-enter-from[data-v-265bcfd2],.slide-leave-to[data-v-265bcfd2]{transform:translateY(-100%)}#plus91-header[data-v-265bcfd2]{z-index:1000;display:flex;justify-content:center;position:fixed;left:0;right:0;top:0}.header-container[data-v-265bcfd2]{width:min(100vw,768px);background:rgba(75,156,169,.65);-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);padding:.25rem .5rem;display:flex;justify-content:space-between;align-items:center;border-radius:0 0 1rem 1rem;border:1px solid rgb(90,140,160);border-top:0}.header-container input[data-v-265bcfd2]{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-265bcfd2]:focus-visible{outline:0;opacity:1}.fade-enter-active[data-v-e9c78cc3],.fade-leave-active[data-v-e9c78cc3]{transition:opacity .2s}.fade-enter-from[data-v-e9c78cc3],.fade-leave-to[data-v-e9c78cc3]{opacity:0}#dark-mode-overlay[data-v-e9c78cc3]{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{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, _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$1 = () => { }; function addSubscription(subscriptions, callback, detached, onCleanup = noop$1) { 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$1 ); 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 after2(callback) { afterCallbackList.push(callback); } function onError(callback) { onErrorCallbackList.push(callback); } triggerSubscriptions(actionSubscriptions, { args, name, store, after: after2, 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$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)(); 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); } } } 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$1 = {}; 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(data2) { this.value += data2; }; 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$1, "__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$1.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 x2 = struct[i]; x2[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$1; 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, data2, 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 < data2.length) { endIndex = data2.indexOf(foundStringToken, endIndex); var iNumEscapeCharacters = 1; escaped = false; while (data2[endIndex - iNumEscapeCharacters] === constants_1$2.ESCAPE_CHARACTER) { escaped = iNumEscapeCharacters % 2 === 1; iNumEscapeCharacters++; } endIndex++; } if (endIndex <= startIndex) { endIndex = data2.length; } } else { while (!(data2.charCodeAt(endIndex) > constants_1$2.DELIMITING_TOKENS_THRESHOLD) && endIndex < data2.length) { endIndex++; } } if (!cursor.drain && endIndex === data2.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(data2.substring(startIndex + 1, endIndex)); } else if (token === constants_1$2.REF_STRING_TOKEN) { return orderedIndex.strings[util_1.decompressInteger(data2.substring(startIndex + 1, endIndex))]; } else if (token === constants_1$2.REF_INTEGER_TOKEN) { return orderedIndex.integers[util_1.decompressInteger(data2.substring(startIndex + 1, endIndex))]; } else if (token === constants_1$2.REF_FLOAT_TOKEN) { return orderedIndex.floats[util_1.decompressInteger(data2.substring(startIndex + 1, endIndex))]; } else if (token === constants_1$2.REF_DATE_TOKEN) { return orderedIndex.dates[util_1.decompressInteger(data2.substring(startIndex + 1, endIndex))]; } else if (token === constants_1$2.REF_LP_DATE_TOKEN) { return orderedIndex.lpDates[util_1.decompressInteger(data2.substring(startIndex + 1, endIndex))]; } else if (token === constants_1$2.STRING_TOKEN) { return orderedIndex.strings[orderedIndex.strings.length] = data2.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(data2.substring(startIndex + 1, endIndex)); } else if (token === constants_1$2.FLOAT_TOKEN) { return orderedIndex.floats[orderedIndex.floats.length] = util_1.decompressFloat(data2.substring(startIndex + 1, endIndex)); } else if (token === constants_1$2.DATE_TOKEN) { return orderedIndex.dates[orderedIndex.dates.length] = new Date(util_1.decompressInteger(data2.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(data2.substring(startIndex + 1, endIndex))).toISOString(); } else if (token === constants_1$2.UNREFERENCED_STRING_TOKEN) { return data2.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(data2.substring(startIndex + 1, endIndex)); } else if (token === constants_1$2.UNREFERENCED_FLOAT_TOKEN) { return util_1.decompressFloat(data2.substring(startIndex + 1, endIndex)); } else if (token === constants_1$2.UNREFERENCED_DATE_TOKEN) { return new Date(util_1.decompressInteger(data2.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(data2.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, data2, 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, data2, 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, data2, orderedIndex) { for (; cursor.index < data2.length; cursor.index++) { var c = data2[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(data2[cursor.index], data2, 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, data2, 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(data2, orderedIndex) { var cursor = makeCursor(true); stages_1.decompressStages(cursor, data2, orderedIndex); return cursor.rootTarget.value; } decompress$1.decompress = decompress; function decompressIncremental(orderedIndex) { var cursor = makeCursor(false); var buffer = ""; function increment(data2) { if (data2 === null) { cursor.drain = true; } else if (data2.length === 0) { return; } else { buffer += data2; } 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(data2) { var orderedIndex = decompress_1.makeOrderedIndex(); return decompress_1.decompress(data2, 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(data2) { increment(data2); if (data2 === null) { return cursor.rootTarget.value; } }; } exports.parseIncremental = parseIncremental; function stringifyTo(data2, writer2, options) { if (options === void 0) { options = {}; } var invertedIndex = compress_1.makeInvertedIndex(); var context = compress_1.makeCompressContext(); compress_1.compress(context, data2, invertedIndex, writer2, options); writer2.end(); } exports.stringifyTo = stringifyTo; function stringify(data2, options) { var writer2 = new writer_12.ZipsonStringWriter(); stringifyTo(data2, writer2, options); return writer2.value; } exports.stringify = stringify; })(lib); 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: 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(); } 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) => ({ "2d5c579c": __props.color, "0168355c": __props.size, "5c7f6b49": __props.stroke })); 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-33d92514"]]); const _hoisted_1$d = { class: "toolbar-icon" }; const _sfc_main$e = { __name: "ToolbarIcon", props: { icon: { type: String, required: true }, stroke: { type: String, default: "0" }, active: { type: Boolean, default: false }, color: { type: String, default: "whitesmoke" } }, setup(__props) { return (_ctx, _cache) => { return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$d, [ vue.createVNode(BootstrapIcon, { icon: __props.icon, color: __props.color, size: "1.3rem", stroke: __props.stroke, active: __props.active }, null, 8, ["icon", "color", "stroke", "active"]) ]); }; } }; const ToolbarIcon = /* @__PURE__ */ _export_sfc(_sfc_main$e, [["__scopeId", "data-v-2b5c1219"]]); const _hoisted_1$c = { class: "adjust-widget" }; const _hoisted_2$b = ["disabled"]; const _hoisted_3$6 = ["disabled"]; const _hoisted_4$4 = ["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) => ({ "791a4684": __props.color, "37e19b46": __props.size })); 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$6), 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$4) ]); }; } }; const AdjustWidget = /* @__PURE__ */ _export_sfc(_sfc_main$d, [["__scopeId", "data-v-1c41dc23"]]); 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`); }); }; 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 = window.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 root2 = chordName.match(/^[A-G]#?/)[0]; const rest = chordName.replace(/^[A-G]#?/, ""); return `${rest} ${root2}`; } const _hoisted_1$b = { id: "plus91-sheet-popup" }; const _hoisted_2$a = { class: "sheet-popup-container" }; const _hoisted_3$5 = { class: "text-capo" }; const _hoisted_4$3 = ["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$5, vue.toDisplayString(vue.unref(store).currentCapo), 1), vue.createTextVNode(" ("), vue.createElementVNode("span", { class: "text-key", innerHTML: vue.unref(store).currentKey }, null, 8, _hoisted_4$3), 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 methods$1 = {}; const names = []; function registerMethods(name, m) { if (Array.isArray(name)) { for (const _name of name) { registerMethods(_name, m); } return; } if (typeof name === "object") { for (const _name in name) { registerMethods(_name, name[_name]); } return; } addMethodNames(Object.getOwnPropertyNames(m)); methods$1[name] = Object.assign(methods$1[name] || {}, m); } function getMethodsFor(name) { return methods$1[name] || {}; } function getMethodNames() { return [...new Set(names)]; } function addMethodNames(_names) { names.push(..._names); } function map(array2, block) { let i; const il = array2.length; const result = []; for (i = 0; i < il; i++) { result.push(block(array2[i])); } return result; } function filter(array2, block) { let i; const il = array2.length; const result = []; for (i = 0; i < il; i++) { if (block(array2[i])) { result.push(array2[i]); } } return result; } function radians(d) { return d % 360 * Math.PI / 180; } function camelCase(s) { return s.toLowerCase().replace(/-(.)/g, function(m, g) { return g.toUpperCase(); }); } function unCamelCase(s) { return s.replace(/([A-Z])/g, function(m, g) { return "-" + g.toLowerCase(); }); } function capitalize(s) { return s.charAt(0).toUpperCase() + s.slice(1); } function proportionalSize(element2, width2, height2, box) { if (width2 == null || height2 == null) { box = box || element2.bbox(); if (width2 == null) { width2 = box.width / box.height * height2; } else if (height2 == null) { height2 = box.height / box.width * width2; } } return { width: width2, height: height2 }; } function getOrigin(o, element2) { const origin = o.origin; let ox = o.ox != null ? o.ox : o.originX != null ? o.originX : "center"; let oy = o.oy != null ? o.oy : o.originY != null ? o.originY : "center"; if (origin != null) { [ox, oy] = Array.isArray(origin) ? origin : typeof origin === "object" ? [origin.x, origin.y] : [origin, origin]; } const condX = typeof ox === "string"; const condY = typeof oy === "string"; if (condX || condY) { const { height: height2, width: width2, x: x2, y: y2 } = element2.bbox(); if (condX) { ox = ox.includes("left") ? x2 : ox.includes("right") ? x2 + width2 : x2 + width2 / 2; } if (condY) { oy = oy.includes("top") ? y2 : oy.includes("bottom") ? y2 + height2 : y2 + height2 / 2; } } return [ox, oy]; } const svg = "http://www.w3.org/2000/svg"; const html = "http://www.w3.org/1999/xhtml"; const xmlns = "http://www.w3.org/2000/xmlns/"; const xlink = "http://www.w3.org/1999/xlink"; const svgjs = "http://svgjs.dev/svgjs"; const globals = { window: typeof window === "undefined" ? null : window, document: typeof document === "undefined" ? null : document }; class Base { // constructor (node/*, {extensions = []} */) { // // this.tags = [] // // // // for (let extension of extensions) { // // extension.setup.call(this, node) // // this.tags.push(extension.name) // // } // } } const elements = {}; const root = "___SYMBOL___ROOT___"; function create(name, ns = svg) { return globals.document.createElementNS(ns, name); } function makeInstance(element2, isHTML = false) { if (element2 instanceof Base) return element2; if (typeof element2 === "object") { return adopter(element2); } if (element2 == null) { return new elements[root](); } if (typeof element2 === "string" && element2.charAt(0) !== "<") { return adopter(globals.document.querySelector(element2)); } const wrapper = isHTML ? globals.document.createElement("div") : create("svg"); wrapper.innerHTML = element2; element2 = adopter(wrapper.firstChild); wrapper.removeChild(wrapper.firstChild); return element2; } function nodeOrNew(name, node) { return node && node.ownerDocument && node instanceof node.ownerDocument.defaultView.Node ? node : create(name); } function adopt(node) { if (!node) return null; if (node.instance instanceof Base) return node.instance; if (node.nodeName === "#document-fragment") { return new elements.Fragment(node); } let className = capitalize(node.nodeName || "Dom"); if (className === "LinearGradient" || className === "RadialGradient") { className = "Gradient"; } else if (!elements[className]) { className = "Dom"; } return new elements[className](node); } let adopter = adopt; function register(element2, name = element2.name, asRoot = false) { elements[name] = element2; if (asRoot) elements[root] = element2; addMethodNames(Object.getOwnPropertyNames(element2.prototype)); return element2; } function getClass(name) { return elements[name]; } let did = 1e3; function eid(name) { return "Svgjs" + capitalize(name) + did++; } function assignNewId(node) { for (let i = node.children.length - 1; i >= 0; i--) { assignNewId(node.children[i]); } if (node.id) { node.id = eid(node.nodeName); return node; } return node; } function extend(modules, methods2) { let key, i; modules = Array.isArray(modules) ? modules : [modules]; for (i = modules.length - 1; i >= 0; i--) { for (key in methods2) { modules[i].prototype[key] = methods2[key]; } } } function wrapWithAttrCheck(fn) { return function(...args) { const o = args[args.length - 1]; if (o && o.constructor === Object && !(o instanceof Array)) { return fn.apply(this, args.slice(0, -1)).attr(o); } else { return fn.apply(this, args); } }; } function siblings() { return this.parent().children(); } function position() { return this.parent().index(this); } function next() { return this.siblings()[this.position() + 1]; } function prev() { return this.siblings()[this.position() - 1]; } function forward() { const i = this.position(); const p = this.parent(); p.add(this.remove(), i + 1); return this; } function backward() { const i = this.position(); const p = this.parent(); p.add(this.remove(), i ? i - 1 : 0); return this; } function front() { const p = this.parent(); p.add(this.remove()); return this; } function back() { const p = this.parent(); p.add(this.remove(), 0); return this; } function before(element2) { element2 = makeInstance(element2); element2.remove(); const i = this.position(); this.parent().add(element2, i); return this; } function after(element2) { element2 = makeInstance(element2); element2.remove(); const i = this.position(); this.parent().add(element2, i + 1); return this; } function insertBefore(element2) { element2 = makeInstance(element2); element2.before(this); return this; } function insertAfter(element2) { element2 = makeInstance(element2); element2.after(this); return this; } registerMethods("Dom", { siblings, position, next, prev, forward, backward, front, back, before, after, insertBefore, insertAfter }); const numberAndUnit = /^([+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?)([a-z%]*)$/i; const hex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i; const rgb = /rgb\((\d+),(\d+),(\d+)\)/; const reference = /(#[a-z_][a-z0-9\-_]*)/i; const transforms = /\)\s*,?\s*/; const whitespace = /\s/g; const isHex = /^#[a-f0-9]{3}$|^#[a-f0-9]{6}$/i; const isRgb = /^rgb\(/; const isBlank = /^(\s+)?$/; const isNumber = /^[+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i; const isImage = /\.(jpg|jpeg|png|gif|svg)(\?[^=]+.*)?/i; const delimiter = /[\s,]+/; const isPathLetter = /[MLHVCSQTAZ]/i; function classes() { const attr2 = this.attr("class"); return attr2 == null ? [] : attr2.trim().split(delimiter); } function hasClass(name) { return this.classes().indexOf(name) !== -1; } function addClass(name) { if (!this.hasClass(name)) { const array2 = this.classes(); array2.push(name); this.attr("class", array2.join(" ")); } return this; } function removeClass(name) { if (this.hasClass(name)) { this.attr("class", this.classes().filter(function(c) { return c !== name; }).join(" ")); } return this; } function toggleClass(name) { return this.hasClass(name) ? this.removeClass(name) : this.addClass(name); } registerMethods("Dom", { classes, hasClass, addClass, removeClass, toggleClass }); function css(style, val) { const ret = {}; if (arguments.length === 0) { this.node.style.cssText.split(/\s*;\s*/).filter(function(el) { return !!el.length; }).forEach(function(el) { const t = el.split(/\s*:\s*/); ret[t[0]] = t[1]; }); return ret; } if (arguments.length < 2) { if (Array.isArray(style)) { for (const name of style) { const cased = camelCase(name); ret[name] = this.node.style[cased]; } return ret; } if (typeof style === "string") { return this.node.style[camelCase(style)]; } if (typeof style === "object") { for (const name in style) { this.node.style[camelCase(name)] = style[name] == null || isBlank.test(style[name]) ? "" : style[name]; } } } if (arguments.length === 2) { this.node.style[camelCase(style)] = val == null || isBlank.test(val) ? "" : val; } return this; } function show() { return this.css("display", ""); } function hide() { return this.css("display", "none"); } function visible() { return this.css("display") !== "none"; } registerMethods("Dom", { css, show, hide, visible }); function data(a, v, r) { if (a == null) { return this.data(map(filter(this.node.attributes, (el) => el.nodeName.indexOf("data-") === 0), (el) => el.nodeName.slice(5))); } else if (a instanceof Array) { const data2 = {}; for (const key of a) { data2[key] = this.data(key); } return data2; } else if (typeof a === "object") { for (v in a) { this.data(v, a[v]); } } else if (arguments.length < 2) { try { return JSON.parse(this.attr("data-" + a)); } catch (e) { return this.attr("data-" + a); } } else { this.attr("data-" + a, v === null ? null : r === true || typeof v === "string" || typeof v === "number" ? v : JSON.stringify(v)); } return this; } registerMethods("Dom", { data }); function remember(k, v) { if (typeof arguments[0] === "object") { for (const key in k) { this.remember(key, k[key]); } } else if (arguments.length === 1) { return this.memory()[k]; } else { this.memory()[k] = v; } return this; } function forget() { if (arguments.length === 0) { this._memory = {}; } else { for (let i = arguments.length - 1; i >= 0; i--) { delete this.memory()[arguments[i]]; } } return this; } function memory() { return this._memory = this._memory || {}; } registerMethods("Dom", { remember, forget, memory }); function sixDigitHex(hex2) { return hex2.length === 4 ? ["#", hex2.substring(1, 2), hex2.substring(1, 2), hex2.substring(2, 3), hex2.substring(2, 3), hex2.substring(3, 4), hex2.substring(3, 4)].join("") : hex2; } function componentHex(component) { const integer = Math.round(component); const bounded = Math.max(0, Math.min(255, integer)); const hex2 = bounded.toString(16); return hex2.length === 1 ? "0" + hex2 : hex2; } function is(object2, space) { for (let i = space.length; i--; ) { if (object2[space[i]] == null) { return false; } } return true; } function getParameters(a, b) { const params = is(a, "rgb") ? { _a: a.r, _b: a.g, _c: a.b, _d: 0, space: "rgb" } : is(a, "xyz") ? { _a: a.x, _b: a.y, _c: a.z, _d: 0, space: "xyz" } : is(a, "hsl") ? { _a: a.h, _b: a.s, _c: a.l, _d: 0, space: "hsl" } : is(a, "lab") ? { _a: a.l, _b: a.a, _c: a.b, _d: 0, space: "lab" } : is(a, "lch") ? { _a: a.l, _b: a.c, _c: a.h, _d: 0, space: "lch" } : is(a, "cmyk") ? { _a: a.c, _b: a.m, _c: a.y, _d: a.k, space: "cmyk" } : { _a: 0, _b: 0, _c: 0, space: "rgb" }; params.space = b || params.space; return params; } function cieSpace(space) { if (space === "lab" || space === "xyz" || space === "lch") { return true; } else { return false; } } function hueToRgb(p, q, t) { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; return p; } class Color { constructor(...inputs) { this.init(...inputs); } // Test if given value is a color static isColor(color) { return color && (color instanceof Color || this.isRgb(color) || this.test(color)); } // Test if given value is an rgb object static isRgb(color) { return color && typeof color.r === "number" && typeof color.g === "number" && typeof color.b === "number"; } /* Generating random colors */ static random(mode = "vibrant", t, u) { const { random, round, sin, PI: pi } = Math; if (mode === "vibrant") { const l = (81 - 57) * random() + 57; const c = (83 - 45) * random() + 45; const h = 360 * random(); const color = new Color(l, c, h, "lch"); return color; } else if (mode === "sine") { t = t == null ? random() : t; const r = round(80 * sin(2 * pi * t / 0.5 + 0.01) + 150); const g = round(50 * sin(2 * pi * t / 0.5 + 4.6) + 200); const b = round(100 * sin(2 * pi * t / 0.5 + 2.3) + 150); const color = new Color(r, g, b); return color; } else if (mode === "pastel") { const l = (94 - 86) * random() + 86; const c = (26 - 9) * random() + 9; const h = 360 * random(); const color = new Color(l, c, h, "lch"); return color; } else if (mode === "dark") { const l = 10 + 10 * random(); const c = (125 - 75) * random() + 86; const h = 360 * random(); const color = new Color(l, c, h, "lch"); return color; } else if (mode === "rgb") { const r = 255 * random(); const g = 255 * random(); const b = 255 * random(); const color = new Color(r, g, b); return color; } else if (mode === "lab") { const l = 100 * random(); const a = 256 * random() - 128; const b = 256 * random() - 128; const color = new Color(l, a, b, "lab"); return color; } else if (mode === "grey") { const grey = 255 * random(); const color = new Color(grey, grey, grey); return color; } else { throw new Error("Unsupported random color mode"); } } // Test if given value is a color string static test(color) { return typeof color === "string" && (isHex.test(color) || isRgb.test(color)); } cmyk() { const { _a, _b, _c } = this.rgb(); const [r, g, b] = [_a, _b, _c].map((v) => v / 255); const k = Math.min(1 - r, 1 - g, 1 - b); if (k === 1) { return new Color(0, 0, 0, 1, "cmyk"); } const c = (1 - r - k) / (1 - k); const m = (1 - g - k) / (1 - k); const y2 = (1 - b - k) / (1 - k); const color = new Color(c, m, y2, k, "cmyk"); return color; } hsl() { const { _a, _b, _c } = this.rgb(); const [r, g, b] = [_a, _b, _c].map((v) => v / 255); const max = Math.max(r, g, b); const min = Math.min(r, g, b); const l = (max + min) / 2; const isGrey = max === min; const delta = max - min; const s = isGrey ? 0 : l > 0.5 ? delta / (2 - max - min) : delta / (max + min); const h = isGrey ? 0 : max === r ? ((g - b) / delta + (g < b ? 6 : 0)) / 6 : max === g ? ((b - r) / delta + 2) / 6 : max === b ? ((r - g) / delta + 4) / 6 : 0; const color = new Color(360 * h, 100 * s, 100 * l, "hsl"); return color; } init(a = 0, b = 0, c = 0, d = 0, space = "rgb") { a = !a ? 0 : a; if (this.space) { for (const component in this.space) { delete this[this.space[component]]; } } if (typeof a === "number") { space = typeof d === "string" ? d : space; d = typeof d === "string" ? 0 : d; Object.assign(this, { _a: a, _b: b, _c: c, _d: d, space }); } else if (a instanceof Array) { this.space = b || (typeof a[3] === "string" ? a[3] : a[4]) || "rgb"; Object.assign(this, { _a: a[0], _b: a[1], _c: a[2], _d: a[3] || 0 }); } else if (a instanceof Object) { const values = getParameters(a, b); Object.assign(this, values); } else if (typeof a === "string") { if (isRgb.test(a)) { const noWhitespace = a.replace(whitespace, ""); const [_a2, _b2, _c2] = rgb.exec(noWhitespace).slice(1, 4).map((v) => parseInt(v)); Object.assign(this, { _a: _a2, _b: _b2, _c: _c2, _d: 0, space: "rgb" }); } else if (isHex.test(a)) { const hexParse = (v) => parseInt(v, 16); const [, _a2, _b2, _c2] = hex.exec(sixDigitHex(a)).map(hexParse); Object.assign(this, { _a: _a2, _b: _b2, _c: _c2, _d: 0, space: "rgb" }); } else throw Error("Unsupported string format, can't construct Color"); } const { _a, _b, _c, _d } = this; const components = this.space === "rgb" ? { r: _a, g: _b, b: _c } : this.space === "xyz" ? { x: _a, y: _b, z: _c } : this.space === "hsl" ? { h: _a, s: _b, l: _c } : this.space === "lab" ? { l: _a, a: _b, b: _c } : this.space === "lch" ? { l: _a, c: _b, h: _c } : this.space === "cmyk" ? { c: _a, m: _b, y: _c, k: _d } : {}; Object.assign(this, components); } lab() { const { x: x2, y: y2, z } = this.xyz(); const l = 116 * y2 - 16; const a = 500 * (x2 - y2); const b = 200 * (y2 - z); const color = new Color(l, a, b, "lab"); return color; } lch() { const { l, a, b } = this.lab(); const c = Math.sqrt(a ** 2 + b ** 2); let h = 180 * Math.atan2(b, a) / Math.PI; if (h < 0) { h *= -1; h = 360 - h; } const color = new Color(l, c, h, "lch"); return color; } /* Conversion Methods */ rgb() { if (this.space === "rgb") { return this; } else if (cieSpace(this.space)) { let { x: x2, y: y2, z } = this; if (this.space === "lab" || this.space === "lch") { let { l, a, b: b2 } = this; if (this.space === "lch") { const { c, h } = this; const dToR = Math.PI / 180; a = c * Math.cos(dToR * h); b2 = c * Math.sin(dToR * h); } const yL = (l + 16) / 116; const xL = a / 500 + yL; const zL = yL - b2 / 200; const ct = 16 / 116; const mx = 8856e-6; const nm = 7.787; x2 = 0.95047 * (xL ** 3 > mx ? xL ** 3 : (xL - ct) / nm); y2 = 1 * (yL ** 3 > mx ? yL ** 3 : (yL - ct) / nm); z = 1.08883 * (zL ** 3 > mx ? zL ** 3 : (zL - ct) / nm); } const rU = x2 * 3.2406 + y2 * -1.5372 + z * -0.4986; const gU = x2 * -0.9689 + y2 * 1.8758 + z * 0.0415; const bU = x2 * 0.0557 + y2 * -0.204 + z * 1.057; const pow = Math.pow; const bd = 31308e-7; const r = rU > bd ? 1.055 * pow(rU, 1 / 2.4) - 0.055 : 12.92 * rU; const g = gU > bd ? 1.055 * pow(gU, 1 / 2.4) - 0.055 : 12.92 * gU; const b = bU > bd ? 1.055 * pow(bU, 1 / 2.4) - 0.055 : 12.92 * bU; const color = new Color(255 * r, 255 * g, 255 * b); return color; } else if (this.space === "hsl") { let { h, s, l } = this; h /= 360; s /= 100; l /= 100; if (s === 0) { l *= 255; const color2 = new Color(l, l, l); return color2; } const q = l < 0.5 ? l * (1 + s) : l + s - l * s; const p = 2 * l - q; const r = 255 * hueToRgb(p, q, h + 1 / 3); const g = 255 * hueToRgb(p, q, h); const b = 255 * hueToRgb(p, q, h - 1 / 3); const color = new Color(r, g, b); return color; } else if (this.space === "cmyk") { const { c, m, y: y2, k } = this; const r = 255 * (1 - Math.min(1, c * (1 - k) + k)); const g = 255 * (1 - Math.min(1, m * (1 - k) + k)); const b = 255 * (1 - Math.min(1, y2 * (1 - k) + k)); const color = new Color(r, g, b); return color; } else { return this; } } toArray() { const { _a, _b, _c, _d, space } = this; return [_a, _b, _c, _d, space]; } toHex() { const [r, g, b] = this._clamped().map(componentHex); return `#${r}${g}${b}`; } toRgb() { const [rV, gV, bV] = this._clamped(); const string2 = `rgb(${rV},${gV},${bV})`; return string2; } toString() { return this.toHex(); } xyz() { const { _a: r255, _b: g255, _c: b255 } = this.rgb(); const [r, g, b] = [r255, g255, b255].map((v) => v / 255); const rL = r > 0.04045 ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92; const gL = g > 0.04045 ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92; const bL = b > 0.04045 ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92; const xU = (rL * 0.4124 + gL * 0.3576 + bL * 0.1805) / 0.95047; const yU = (rL * 0.2126 + gL * 0.7152 + bL * 0.0722) / 1; const zU = (rL * 0.0193 + gL * 0.1192 + bL * 0.9505) / 1.08883; const x2 = xU > 8856e-6 ? Math.pow(xU, 1 / 3) : 7.787 * xU + 16 / 116; const y2 = yU > 8856e-6 ? Math.pow(yU, 1 / 3) : 7.787 * yU + 16 / 116; const z = zU > 8856e-6 ? Math.pow(zU, 1 / 3) : 7.787 * zU + 16 / 116; const color = new Color(x2, y2, z, "xyz"); return color; } /* Input and Output methods */ _clamped() { const { _a, _b, _c } = this.rgb(); const { max, min, round } = Math; const format = (v) => max(0, min(round(v), 255)); return [_a, _b, _c].map(format); } /* Constructing colors */ } class Point { // Initialize constructor(...args) { this.init(...args); } // Clone point clone() { return new Point(this); } init(x2, y2) { const base = { x: 0, y: 0 }; const source = Array.isArray(x2) ? { x: x2[0], y: x2[1] } : typeof x2 === "object" ? { x: x2.x, y: x2.y } : { x: x2, y: y2 }; this.x = source.x == null ? base.x : source.x; this.y = source.y == null ? base.y : source.y; return this; } toArray() { return [this.x, this.y]; } transform(m) { return this.clone().transformO(m); } // Transform point with matrix transformO(m) { if (!Matrix.isMatrixLike(m)) { m = new Matrix(m); } const { x: x2, y: y2 } = this; this.x = m.a * x2 + m.c * y2 + m.e; this.y = m.b * x2 + m.d * y2 + m.f; return this; } } function point(x2, y2) { return new Point(x2, y2).transformO(this.screenCTM().inverseO()); } function closeEnough(a, b, threshold) { return Math.abs(b - a) < (threshold || 1e-6); } class Matrix { constructor(...args) { this.init(...args); } static formatTransforms(o) { const flipBoth = o.flip === "both" || o.flip === true; const flipX = o.flip && (flipBoth || o.flip === "x") ? -1 : 1; const flipY = o.flip && (flipBoth || o.flip === "y") ? -1 : 1; const skewX = o.skew && o.skew.length ? o.skew[0] : isFinite(o.skew) ? o.skew : isFinite(o.skewX) ? o.skewX : 0; const skewY = o.skew && o.skew.length ? o.skew[1] : isFinite(o.skew) ? o.skew : isFinite(o.skewY) ? o.skewY : 0; const scaleX = o.scale && o.scale.length ? o.scale[0] * flipX : isFinite(o.scale) ? o.scale * flipX : isFinite(o.scaleX) ? o.scaleX * flipX : flipX; const scaleY = o.scale && o.scale.length ? o.scale[1] * flipY : isFinite(o.scale) ? o.scale * flipY : isFinite(o.scaleY) ? o.scaleY * flipY : flipY; const shear = o.shear || 0; const theta = o.rotate || o.theta || 0; const origin = new Point(o.origin || o.around || o.ox || o.originX, o.oy || o.originY); const ox = origin.x; const oy = origin.y; const position2 = new Point(o.position || o.px || o.positionX || NaN, o.py || o.positionY || NaN); const px = position2.x; const py = position2.y; const translate = new Point(o.translate || o.tx || o.translateX, o.ty || o.translateY); const tx = translate.x; const ty = translate.y; const relative = new Point(o.relative || o.rx || o.relativeX, o.ry || o.relativeY); const rx2 = relative.x; const ry2 = relative.y; return { scaleX, scaleY, skewX, skewY, shear, theta, rx: rx2, ry: ry2, tx, ty, ox, oy, px, py }; } static fromArray(a) { return { a: a[0], b: a[1], c: a[2], d: a[3], e: a[4], f: a[5] }; } static isMatrixLike(o) { return o.a != null || o.b != null || o.c != null || o.d != null || o.e != null || o.f != null; } // left matrix, right matrix, target matrix which is overwritten static matrixMultiply(l, r, o) { const a = l.a * r.a + l.c * r.b; const b = l.b * r.a + l.d * r.b; const c = l.a * r.c + l.c * r.d; const d = l.b * r.c + l.d * r.d; const e = l.e + l.a * r.e + l.c * r.f; const f = l.f + l.b * r.e + l.d * r.f; o.a = a; o.b = b; o.c = c; o.d = d; o.e = e; o.f = f; return o; } around(cx2, cy2, matrix) { return this.clone().aroundO(cx2, cy2, matrix); } // Transform around a center point aroundO(cx2, cy2, matrix) { const dx2 = cx2 || 0; const dy2 = cy2 || 0; return this.translateO(-dx2, -dy2).lmultiplyO(matrix).translateO(dx2, dy2); } // Clones this matrix clone() { return new Matrix(this); } // Decomposes this matrix into its affine parameters decompose(cx2 = 0, cy2 = 0) { const a = this.a; const b = this.b; const c = this.c; const d = this.d; const e = this.e; const f = this.f; const determinant = a * d - b * c; const ccw = determinant > 0 ? 1 : -1; const sx = ccw * Math.sqrt(a * a + b * b); const thetaRad = Math.atan2(ccw * b, ccw * a); const theta = 180 / Math.PI * thetaRad; const ct = Math.cos(thetaRad); const st = Math.sin(thetaRad); const lam = (a * c + b * d) / determinant; const sy = c * sx / (lam * a - b) || d * sx / (lam * b + a); const tx = e - cx2 + cx2 * ct * sx + cy2 * (lam * ct * sx - st * sy); const ty = f - cy2 + cx2 * st * sx + cy2 * (lam * st * sx + ct * sy); return { // Return the affine parameters scaleX: sx, scaleY: sy, shear: lam, rotate: theta, translateX: tx, translateY: ty, originX: cx2, originY: cy2, // Return the matrix parameters a: this.a, b: this.b, c: this.c, d: this.d, e: this.e, f: this.f }; } // Check if two matrices are equal equals(other) { if (other === this) return true; const comp = new Matrix(other); return closeEnough(this.a, comp.a) && closeEnough(this.b, comp.b) && closeEnough(this.c, comp.c) && closeEnough(this.d, comp.d) && closeEnough(this.e, comp.e) && closeEnough(this.f, comp.f); } // Flip matrix on x or y, at a given offset flip(axis, around) { return this.clone().flipO(axis, around); } flipO(axis, around) { return axis === "x" ? this.scaleO(-1, 1, around, 0) : axis === "y" ? this.scaleO(1, -1, 0, around) : this.scaleO(-1, -1, axis, around || axis); } // Initialize init(source) { const base = Matrix.fromArray([1, 0, 0, 1, 0, 0]); source = source instanceof Element ? source.matrixify() : typeof source === "string" ? Matrix.fromArray(source.split(delimiter).map(parseFloat)) : Array.isArray(source) ? Matrix.fromArray(source) : typeof source === "object" && Matrix.isMatrixLike(source) ? source : typeof source === "object" ? new Matrix().transform(source) : arguments.length === 6 ? Matrix.fromArray([].slice.call(arguments)) : base; this.a = source.a != null ? source.a : base.a; this.b = source.b != null ? source.b : base.b; this.c = source.c != null ? source.c : base.c; this.d = source.d != null ? source.d : base.d; this.e = source.e != null ? source.e : base.e; this.f = source.f != null ? source.f : base.f; return this; } inverse() { return this.clone().inverseO(); } // Inverses matrix inverseO() { const a = this.a; const b = this.b; const c = this.c; const d = this.d; const e = this.e; const f = this.f; const det = a * d - b * c; if (!det) throw new Error("Cannot invert " + this); const na = d / det; const nb = -b / det; const nc = -c / det; const nd = a / det; const ne = -(na * e + nc * f); const nf = -(nb * e + nd * f); this.a = na; this.b = nb; this.c = nc; this.d = nd; this.e = ne; this.f = nf; return this; } lmultiply(matrix) { return this.clone().lmultiplyO(matrix); } lmultiplyO(matrix) { const r = this; const l = matrix instanceof Matrix ? matrix : new Matrix(matrix); return Matrix.matrixMultiply(l, r, this); } // Left multiplies by the given matrix multiply(matrix) { return this.clone().multiplyO(matrix); } multiplyO(matrix) { const l = this; const r = matrix instanceof Matrix ? matrix : new Matrix(matrix); return Matrix.matrixMultiply(l, r, this); } // Rotate matrix rotate(r, cx2, cy2) { return this.clone().rotateO(r, cx2, cy2); } rotateO(r, cx2 = 0, cy2 = 0) { r = radians(r); const cos = Math.cos(r); const sin = Math.sin(r); const { a, b, c, d, e, f } = this; this.a = a * cos - b * sin; this.b = b * cos + a * sin; this.c = c * cos - d * sin; this.d = d * cos + c * sin; this.e = e * cos - f * sin + cy2 * sin - cx2 * cos + cx2; this.f = f * cos + e * sin - cx2 * sin - cy2 * cos + cy2; return this; } // Scale matrix scale(x2, y2, cx2, cy2) { return this.clone().scaleO(...arguments); } scaleO(x2, y2 = x2, cx2 = 0, cy2 = 0) { if (arguments.length === 3) { cy2 = cx2; cx2 = y2; y2 = x2; } const { a, b, c, d, e, f } = this; this.a = a * x2; this.b = b * y2; this.c = c * x2; this.d = d * y2; this.e = e * x2 - cx2 * x2 + cx2; this.f = f * y2 - cy2 * y2 + cy2; return this; } // Shear matrix shear(a, cx2, cy2) { return this.clone().shearO(a, cx2, cy2); } shearO(lx, cx2 = 0, cy2 = 0) { const { a, b, c, d, e, f } = this; this.a = a + b * lx; this.c = c + d * lx; this.e = e + f * lx - cy2 * lx; return this; } // Skew Matrix skew(x2, y2, cx2, cy2) { return this.clone().skewO(...arguments); } skewO(x2, y2 = x2, cx2 = 0, cy2 = 0) { if (arguments.length === 3) { cy2 = cx2; cx2 = y2; y2 = x2; } x2 = radians(x2); y2 = radians(y2); const lx = Math.tan(x2); const ly = Math.tan(y2); const { a, b, c, d, e, f } = this; this.a = a + b * lx; this.b = b + a * ly; this.c = c + d * lx; this.d = d + c * ly; this.e = e + f * lx - cy2 * lx; this.f = f + e * ly - cx2 * ly; return this; } // SkewX skewX(x2, cx2, cy2) { return this.skew(x2, 0, cx2, cy2); } // SkewY skewY(y2, cx2, cy2) { return this.skew(0, y2, cx2, cy2); } toArray() { return [this.a, this.b, this.c, this.d, this.e, this.f]; } // Convert matrix to string toString() { return "matrix(" + this.a + "," + this.b + "," + this.c + "," + this.d + "," + this.e + "," + this.f + ")"; } // Transform a matrix into another matrix by manipulating the space transform(o) { if (Matrix.isMatrixLike(o)) { const matrix = new Matrix(o); return matrix.multiplyO(this); } const t = Matrix.formatTransforms(o); const current = this; const { x: ox, y: oy } = new Point(t.ox, t.oy).transform(current); const transformer = new Matrix().translateO(t.rx, t.ry).lmultiplyO(current).translateO(-ox, -oy).scaleO(t.scaleX, t.scaleY).skewO(t.skewX, t.skewY).shearO(t.shear).rotateO(t.theta).translateO(ox, oy); if (isFinite(t.px) || isFinite(t.py)) { const origin = new Point(ox, oy).transform(transformer); const dx2 = isFinite(t.px) ? t.px - origin.x : 0; const dy2 = isFinite(t.py) ? t.py - origin.y : 0; transformer.translateO(dx2, dy2); } transformer.translateO(t.tx, t.ty); return transformer; } // Translate matrix translate(x2, y2) { return this.clone().translateO(x2, y2); } translateO(x2, y2) { this.e += x2 || 0; this.f += y2 || 0; return this; } valueOf() { return { a: this.a, b: this.b, c: this.c, d: this.d, e: this.e, f: this.f }; } } function ctm() { return new Matrix(this.node.getCTM()); } function screenCTM() { if (typeof this.isRoot === "function" && !this.isRoot()) { const rect = this.rect(1, 1); const m = rect.node.getScreenCTM(); rect.remove(); return new Matrix(m); } return new Matrix(this.node.getScreenCTM()); } register(Matrix, "Matrix"); function parser() { if (!parser.nodes) { const svg2 = makeInstance().size(2, 0); svg2.node.style.cssText = ["opacity: 0", "position: absolute", "left: -100%", "top: -100%", "overflow: hidden"].join(";"); svg2.attr("focusable", "false"); svg2.attr("aria-hidden", "true"); const path = svg2.path().node; parser.nodes = { svg: svg2, path }; } if (!parser.nodes.svg.node.parentNode) { const b = globals.document.body || globals.document.documentElement; parser.nodes.svg.addTo(b); } return parser.nodes; } function isNulledBox(box) { return !box.width && !box.height && !box.x && !box.y; } function domContains(node) { return node === globals.document || (globals.document.documentElement.contains || function(node2) { while (node2.parentNode) { node2 = node2.parentNode; } return node2 === globals.document; }).call(globals.document.documentElement, node); } class Box { constructor(...args) { this.init(...args); } addOffset() { this.x += globals.window.pageXOffset; this.y += globals.window.pageYOffset; return new Box(this); } init(source) { const base = [0, 0, 0, 0]; source = typeof source === "string" ? source.split(delimiter).map(parseFloat) : Array.isArray(source) ? source : typeof source === "object" ? [source.left != null ? source.left : source.x, source.top != null ? source.top : source.y, source.width, source.height] : arguments.length === 4 ? [].slice.call(arguments) : base; this.x = source[0] || 0; this.y = source[1] || 0; this.width = this.w = source[2] || 0; this.height = this.h = source[3] || 0; this.x2 = this.x + this.w; this.y2 = this.y + this.h; this.cx = this.x + this.w / 2; this.cy = this.y + this.h / 2; return this; } isNulled() { return isNulledBox(this); } // Merge rect box with another, return a new instance merge(box) { const x2 = Math.min(this.x, box.x); const y2 = Math.min(this.y, box.y); const width2 = Math.max(this.x + this.width, box.x + box.width) - x2; const height2 = Math.max(this.y + this.height, box.y + box.height) - y2; return new Box(x2, y2, width2, height2); } toArray() { return [this.x, this.y, this.width, this.height]; } toString() { return this.x + " " + this.y + " " + this.width + " " + this.height; } transform(m) { if (!(m instanceof Matrix)) { m = new Matrix(m); } let xMin = Infinity; let xMax = -Infinity; let yMin = Infinity; let yMax = -Infinity; const pts = [new Point(this.x, this.y), new Point(this.x2, this.y), new Point(this.x, this.y2), new Point(this.x2, this.y2)]; pts.forEach(function(p) { p = p.transform(m); xMin = Math.min(xMin, p.x); xMax = Math.max(xMax, p.x); yMin = Math.min(yMin, p.y); yMax = Math.max(yMax, p.y); }); return new Box(xMin, yMin, xMax - xMin, yMax - yMin); } } function getBox(el, getBBoxFn, retry) { let box; try { box = getBBoxFn(el.node); if (isNulledBox(box) && !domContains(el.node)) { throw new Error("Element not in the dom"); } } catch (e) { box = retry(el); } return box; } function bbox() { const getBBox = (node) => node.getBBox(); const retry = (el) => { try { const clone = el.clone().addTo(parser().svg).show(); const box2 = clone.node.getBBox(); clone.remove(); return box2; } catch (e) { throw new Error(`Getting bbox of element "${el.node.nodeName}" is not possible: ${e.toString()}`); } }; const box = getBox(this, getBBox, retry); const bbox2 = new Box(box); return bbox2; } function rbox(el) { const getRBox = (node) => node.getBoundingClientRect(); const retry = (el2) => { throw new Error(`Getting rbox of element "${el2.node.nodeName}" is not possible`); }; const box = getBox(this, getRBox, retry); const rbox2 = new Box(box); if (el) { return rbox2.transform(el.screenCTM().inverseO()); } return rbox2.addOffset(); } function inside(x2, y2) { const box = this.bbox(); return x2 > box.x && y2 > box.y && x2 < box.x + box.width && y2 < box.y + box.height; } registerMethods({ viewbox: { viewbox(x2, y2, width2, height2) { if (x2 == null) return new Box(this.attr("viewBox")); return this.attr("viewBox", new Box(x2, y2, width2, height2)); }, zoom(level, point2) { let { width: width2, height: height2 } = this.attr(["width", "height"]); if (!width2 && !height2 || typeof width2 === "string" || typeof height2 === "string") { width2 = this.node.clientWidth; height2 = this.node.clientHeight; } if (!width2 || !height2) { throw new Error("Impossible to get absolute width and height. Please provide an absolute width and height attribute on the zooming element"); } const v = this.viewbox(); const zoomX = width2 / v.width; const zoomY = height2 / v.height; const zoom = Math.min(zoomX, zoomY); if (level == null) { return zoom; } let zoomAmount = zoom / level; if (zoomAmount === Infinity) zoomAmount = Number.MAX_SAFE_INTEGER / 100; point2 = point2 || new Point(width2 / 2 / zoomX + v.x, height2 / 2 / zoomY + v.y); const box = new Box(v).transform(new Matrix({ scale: zoomAmount, origin: point2 })); return this.viewbox(box); } } }); register(Box, "Box"); class List extends Array { constructor(arr = [], ...args) { super(arr, ...args); if (typeof arr === "number") return this; this.length = 0; this.push(...arr); } } extend([List], { each(fnOrMethodName, ...args) { if (typeof fnOrMethodName === "function") { return this.map((el, i, arr) => { return fnOrMethodName.call(el, el, i, arr); }); } else { return this.map((el) => { return el[fnOrMethodName](...args); }); } }, toArray() { return Array.prototype.concat.apply([], this); } }); const reserved = ["toArray", "constructor", "each"]; List.extend = function(methods2) { methods2 = methods2.reduce((obj, name) => { if (reserved.includes(name)) return obj; if (name[0] === "_") return obj; obj[name] = function(...attrs2) { return this.each(name, ...attrs2); }; return obj; }, {}); extend([List], methods2); }; function baseFind(query, parent) { return new List(map((parent || globals.document).querySelectorAll(query), function(node) { return adopt(node); })); } function find(query) { return baseFind(query, this.node); } function findOne(query) { return adopt(this.node.querySelector(query)); } let listenerId = 0; const windowEvents = {}; function getEvents(instance) { let n = instance.getEventHolder(); if (n === globals.window) n = windowEvents; if (!n.events) n.events = {}; return n.events; } function getEventTarget(instance) { return instance.getEventTarget(); } function clearEvents(instance) { let n = instance.getEventHolder(); if (n === globals.window) n = windowEvents; if (n.events) n.events = {}; } function on(node, events, listener, binding, options) { const l = listener.bind(binding || node); const instance = makeInstance(node); const bag = getEvents(instance); const n = getEventTarget(instance); events = Array.isArray(events) ? events : events.split(delimiter); if (!listener._svgjsListenerId) { listener._svgjsListenerId = ++listenerId; } events.forEach(function(event) { const ev = event.split(".")[0]; const ns = event.split(".")[1] || "*"; bag[ev] = bag[ev] || {}; bag[ev][ns] = bag[ev][ns] || {}; bag[ev][ns][listener._svgjsListenerId] = l; n.addEventListener(ev, l, options || false); }); } function off(node, events, listener, options) { const instance = makeInstance(node); const bag = getEvents(instance); const n = getEventTarget(instance); if (typeof listener === "function") { listener = listener._svgjsListenerId; if (!listener) return; } events = Array.isArray(events) ? events : (events || "").split(delimiter); events.forEach(function(event) { const ev = event && event.split(".")[0]; const ns = event && event.split(".")[1]; let namespace, l; if (listener) { if (bag[ev] && bag[ev][ns || "*"]) { n.removeEventListener(ev, bag[ev][ns || "*"][listener], options || false); delete bag[ev][ns || "*"][listener]; } } else if (ev && ns) { if (bag[ev] && bag[ev][ns]) { for (l in bag[ev][ns]) { off(n, [ev, ns].join("."), l); } delete bag[ev][ns]; } } else if (ns) { for (event in bag) { for (namespace in bag[event]) { if (ns === namespace) { off(n, [event, ns].join(".")); } } } } else if (ev) { if (bag[ev]) { for (namespace in bag[ev]) { off(n, [ev, namespace].join(".")); } delete bag[ev]; } } else { for (event in bag) { off(n, event); } clearEvents(instance); } }); } function dispatch(node, event, data2, options) { const n = getEventTarget(node); if (event instanceof globals.window.Event) { n.dispatchEvent(event); } else { event = new globals.window.CustomEvent(event, { detail: data2, cancelable: true, ...options }); n.dispatchEvent(event); } return event; } class EventTarget extends Base { addEventListener() { } dispatch(event, data2, options) { return dispatch(this, event, data2, options); } dispatchEvent(event) { const bag = this.getEventHolder().events; if (!bag) return true; const events = bag[event.type]; for (const i in events) { for (const j in events[i]) { events[i][j](event); } } return !event.defaultPrevented; } // Fire given event fire(event, data2, options) { this.dispatch(event, data2, options); return this; } getEventHolder() { return this; } getEventTarget() { return this; } // Unbind event from listener off(event, listener, options) { off(this, event, listener, options); return this; } // Bind given event to listener on(event, listener, binding, options) { on(this, event, listener, binding, options); return this; } removeEventListener() { } } register(EventTarget, "EventTarget"); function noop() { } const timeline = { duration: 400, ease: ">", delay: 0 }; const attrs = { // fill and stroke "fill-opacity": 1, "stroke-opacity": 1, "stroke-width": 0, "stroke-linejoin": "miter", "stroke-linecap": "butt", fill: "#000000", stroke: "#000000", opacity: 1, // position x: 0, y: 0, cx: 0, cy: 0, // size width: 0, height: 0, // radius r: 0, rx: 0, ry: 0, // gradient offset: 0, "stop-opacity": 1, "stop-color": "#000000", // text "text-anchor": "start" }; class SVGArray extends Array { constructor(...args) { super(...args); this.init(...args); } clone() { return new this.constructor(this); } init(arr) { if (typeof arr === "number") return this; this.length = 0; this.push(...this.parse(arr)); return this; } // Parse whitespace separated string parse(array2 = []) { if (array2 instanceof Array) return array2; return array2.trim().split(delimiter).map(parseFloat); } toArray() { return Array.prototype.concat.apply([], this); } toSet() { return new Set(this); } toString() { return this.join(" "); } // Flattens the array if needed valueOf() { const ret = []; ret.push(...this); return ret; } } class SVGNumber { // Initialize constructor(...args) { this.init(...args); } convert(unit) { return new SVGNumber(this.value, unit); } // Divide number divide(number2) { number2 = new SVGNumber(number2); return new SVGNumber(this / number2, this.unit || number2.unit); } init(value, unit) { unit = Array.isArray(value) ? value[1] : unit; value = Array.isArray(value) ? value[0] : value; this.value = 0; this.unit = unit || ""; if (typeof value === "number") { this.value = isNaN(value) ? 0 : !isFinite(value) ? value < 0 ? -34e37 : 34e37 : value; } else if (typeof value === "string") { unit = value.match(numberAndUnit); if (unit) { this.value = parseFloat(unit[1]); if (unit[5] === "%") { this.value /= 100; } else if (unit[5] === "s") { this.value *= 1e3; } this.unit = unit[5]; } } else { if (value instanceof SVGNumber) { this.value = value.valueOf(); this.unit = value.unit; } } return this; } // Subtract number minus(number2) { number2 = new SVGNumber(number2); return new SVGNumber(this - number2, this.unit || number2.unit); } // Add number plus(number2) { number2 = new SVGNumber(number2); return new SVGNumber(this + number2, this.unit || number2.unit); } // Multiply number times(number2) { number2 = new SVGNumber(number2); return new SVGNumber(this * number2, this.unit || number2.unit); } toArray() { return [this.value, this.unit]; } toJSON() { return this.toString(); } toString() { return (this.unit === "%" ? ~~(this.value * 1e8) / 1e6 : this.unit === "s" ? this.value / 1e3 : this.value) + this.unit; } valueOf() { return this.value; } } const hooks = []; function registerAttrHook(fn) { hooks.push(fn); } function attr(attr2, val, ns) { if (attr2 == null) { attr2 = {}; val = this.node.attributes; for (const node of val) { attr2[node.nodeName] = isNumber.test(node.nodeValue) ? parseFloat(node.nodeValue) : node.nodeValue; } return attr2; } else if (attr2 instanceof Array) { return attr2.reduce((last, curr) => { last[curr] = this.attr(curr); return last; }, {}); } else if (typeof attr2 === "object" && attr2.constructor === Object) { for (val in attr2) this.attr(val, attr2[val]); } else if (val === null) { this.node.removeAttribute(attr2); } else if (val == null) { val = this.node.getAttribute(attr2); return val == null ? attrs[attr2] : isNumber.test(val) ? parseFloat(val) : val; } else { val = hooks.reduce((_val, hook) => { return hook(attr2, _val, this); }, val); if (typeof val === "number") { val = new SVGNumber(val); } else if (Color.isColor(val)) { val = new Color(val); } else if (val.constructor === Array) { val = new SVGArray(val); } if (attr2 === "leading") { if (this.leading) { this.leading(val); } } else { typeof ns === "string" ? this.node.setAttributeNS(ns, attr2, val.toString()) : this.node.setAttribute(attr2, val.toString()); } if (this.rebuild && (attr2 === "font-size" || attr2 === "x")) { this.rebuild(); } } return this; } class Dom extends EventTarget { constructor(node, attrs2) { super(); this.node = node; this.type = node.nodeName; if (attrs2 && node !== attrs2) { this.attr(attrs2); } } // Add given element at a position add(element2, i) { element2 = makeInstance(element2); if (element2.removeNamespace && this.node instanceof globals.window.SVGElement) { element2.removeNamespace(); } if (i == null) { this.node.appendChild(element2.node); } else if (element2.node !== this.node.childNodes[i]) { this.node.insertBefore(element2.node, this.node.childNodes[i]); } return this; } // Add element to given container and return self addTo(parent, i) { return makeInstance(parent).put(this, i); } // Returns all child elements children() { return new List(map(this.node.children, function(node) { return adopt(node); })); } // Remove all elements in this container clear() { while (this.node.hasChildNodes()) { this.node.removeChild(this.node.lastChild); } return this; } // Clone element clone(deep = true, assignNewIds = true) { this.writeDataToDom(); let nodeClone = this.node.cloneNode(deep); if (assignNewIds) { nodeClone = assignNewId(nodeClone); } return new this.constructor(nodeClone); } // Iterates over all children and invokes a given block each(block, deep) { const children = this.children(); let i, il; for (i = 0, il = children.length; i < il; i++) { block.apply(children[i], [i, children]); if (deep) { children[i].each(block, deep); } } return this; } element(nodeName, attrs2) { return this.put(new Dom(create(nodeName), attrs2)); } // Get first child first() { return adopt(this.node.firstChild); } // Get a element at the given index get(i) { return adopt(this.node.childNodes[i]); } getEventHolder() { return this.node; } getEventTarget() { return this.node; } // Checks if the given element is a child has(element2) { return this.index(element2) >= 0; } html(htmlOrFn, outerHTML) { return this.xml(htmlOrFn, outerHTML, html); } // Get / set id id(id) { if (typeof id === "undefined" && !this.node.id) { this.node.id = eid(this.type); } return this.attr("id", id); } // Gets index of given element index(element2) { return [].slice.call(this.node.childNodes).indexOf(element2.node); } // Get the last child last() { return adopt(this.node.lastChild); } // matches the element vs a css selector matches(selector) { const el = this.node; const matcher = el.matches || el.matchesSelector || el.msMatchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector || null; return matcher && matcher.call(el, selector); } // Returns the parent element instance parent(type) { let parent = this; if (!parent.node.parentNode) return null; parent = adopt(parent.node.parentNode); if (!type) return parent; do { if (typeof type === "string" ? parent.matches(type) : parent instanceof type) return parent; } while (parent = adopt(parent.node.parentNode)); return parent; } // Basically does the same as `add()` but returns the added element instead put(element2, i) { element2 = makeInstance(element2); this.add(element2, i); return element2; } // Add element to given container and return container putIn(parent, i) { return makeInstance(parent).add(this, i); } // Remove element remove() { if (this.parent()) { this.parent().removeElement(this); } return this; } // Remove a given child removeElement(element2) { this.node.removeChild(element2.node); return this; } // Replace this with element replace(element2) { element2 = makeInstance(element2); if (this.node.parentNode) { this.node.parentNode.replaceChild(element2.node, this.node); } return element2; } round(precision = 2, map2 = null) { const factor = 10 ** precision; const attrs2 = this.attr(map2); for (const i in attrs2) { if (typeof attrs2[i] === "number") { attrs2[i] = Math.round(attrs2[i] * factor) / factor; } } this.attr(attrs2); return this; } // Import / Export raw svg svg(svgOrFn, outerSVG) { return this.xml(svgOrFn, outerSVG, svg); } // Return id on string conversion toString() { return this.id(); } words(text) { this.node.textContent = text; return this; } wrap(node) { const parent = this.parent(); if (!parent) { return this.addTo(node); } const position2 = parent.index(this); return parent.put(node, position2).put(this); } // write svgjs data to the dom writeDataToDom() { this.each(function() { this.writeDataToDom(); }); return this; } // Import / Export raw svg xml(xmlOrFn, outerXML, ns) { if (typeof xmlOrFn === "boolean") { ns = outerXML; outerXML = xmlOrFn; xmlOrFn = null; } if (xmlOrFn == null || typeof xmlOrFn === "function") { outerXML = outerXML == null ? true : outerXML; this.writeDataToDom(); let current = this; if (xmlOrFn != null) { current = adopt(current.node.cloneNode(true)); if (outerXML) { const result = xmlOrFn(current); current = result || current; if (result === false) return ""; } current.each(function() { const result = xmlOrFn(this); const _this = result || this; if (result === false) { this.remove(); } else if (result && this !== _this) { this.replace(_this); } }, true); } return outerXML ? current.node.outerHTML : current.node.innerHTML; } outerXML = outerXML == null ? false : outerXML; const well = create("wrapper", ns); const fragment = globals.document.createDocumentFragment(); well.innerHTML = xmlOrFn; for (let len = well.children.length; len--; ) { fragment.appendChild(well.firstElementChild); } const parent = this.parent(); return outerXML ? this.replace(fragment) && parent : this.add(fragment); } } extend(Dom, { attr, find, findOne }); register(Dom, "Dom"); class Element extends Dom { constructor(node, attrs2) { super(node, attrs2); this.dom = {}; this.node.instance = this; if (node.hasAttribute("svgjs:data")) { this.setData(JSON.parse(node.getAttribute("svgjs:data")) || {}); } } // Move element by its center center(x2, y2) { return this.cx(x2).cy(y2); } // Move by center over x-axis cx(x2) { return x2 == null ? this.x() + this.width() / 2 : this.x(x2 - this.width() / 2); } // Move by center over y-axis cy(y2) { return y2 == null ? this.y() + this.height() / 2 : this.y(y2 - this.height() / 2); } // Get defs defs() { const root2 = this.root(); return root2 && root2.defs(); } // Relative move over x and y axes dmove(x2, y2) { return this.dx(x2).dy(y2); } // Relative move over x axis dx(x2 = 0) { return this.x(new SVGNumber(x2).plus(this.x())); } // Relative move over y axis dy(y2 = 0) { return this.y(new SVGNumber(y2).plus(this.y())); } getEventHolder() { return this; } // Set height of element height(height2) { return this.attr("height", height2); } // Move element to given x and y values move(x2, y2) { return this.x(x2).y(y2); } // return array of all ancestors of given type up to the root svg parents(until = this.root()) { const isSelector = typeof until === "string"; if (!isSelector) { until = makeInstance(until); } const parents = new List(); let parent = this; while ((parent = parent.parent()) && parent.node !== globals.document && parent.nodeName !== "#document-fragment") { parents.push(parent); if (!isSelector && parent.node === until.node) { break; } if (isSelector && parent.matches(until)) { break; } if (parent.node === this.root().node) { return null; } } return parents; } // Get referenced element form attribute value reference(attr2) { attr2 = this.attr(attr2); if (!attr2) return null; const m = (attr2 + "").match(reference); return m ? makeInstance(m[1]) : null; } // Get parent document root() { const p = this.parent(getClass(root)); return p && p.root(); } // set given data to the elements data property setData(o) { this.dom = o; return this; } // Set element size to given width and height size(width2, height2) { const p = proportionalSize(this, width2, height2); return this.width(new SVGNumber(p.width)).height(new SVGNumber(p.height)); } // Set width of element width(width2) { return this.attr("width", width2); } // write svgjs data to the dom writeDataToDom() { this.node.removeAttribute("svgjs:data"); if (Object.keys(this.dom).length) { this.node.setAttribute("svgjs:data", JSON.stringify(this.dom)); } return super.writeDataToDom(); } // Move over x-axis x(x2) { return this.attr("x", x2); } // Move over y-axis y(y2) { return this.attr("y", y2); } } extend(Element, { bbox, rbox, inside, point, ctm, screenCTM }); register(Element, "Element"); const sugar = { stroke: ["color", "width", "opacity", "linecap", "linejoin", "miterlimit", "dasharray", "dashoffset"], fill: ["color", "opacity", "rule"], prefix: function(t, a) { return a === "color" ? t : t + "-" + a; } }; ["fill", "stroke"].forEach(function(m) { const extension = {}; let i; extension[m] = function(o) { if (typeof o === "undefined") { return this.attr(m); } if (typeof o === "string" || o instanceof Color || Color.isRgb(o) || o instanceof Element) { this.attr(m, o); } else { for (i = sugar[m].length - 1; i >= 0; i--) { if (o[sugar[m][i]] != null) { this.attr(sugar.prefix(m, sugar[m][i]), o[sugar[m][i]]); } } } return this; }; registerMethods(["Element", "Runner"], extension); }); registerMethods(["Element", "Runner"], { // Let the user set the matrix directly matrix: function(mat, b, c, d, e, f) { if (mat == null) { return new Matrix(this); } return this.attr("transform", new Matrix(mat, b, c, d, e, f)); }, // Map rotation to transform rotate: function(angle, cx2, cy2) { return this.transform({ rotate: angle, ox: cx2, oy: cy2 }, true); }, // Map skew to transform skew: function(x2, y2, cx2, cy2) { return arguments.length === 1 || arguments.length === 3 ? this.transform({ skew: x2, ox: y2, oy: cx2 }, true) : this.transform({ skew: [x2, y2], ox: cx2, oy: cy2 }, true); }, shear: function(lam, cx2, cy2) { return this.transform({ shear: lam, ox: cx2, oy: cy2 }, true); }, // Map scale to transform scale: function(x2, y2, cx2, cy2) { return arguments.length === 1 || arguments.length === 3 ? this.transform({ scale: x2, ox: y2, oy: cx2 }, true) : this.transform({ scale: [x2, y2], ox: cx2, oy: cy2 }, true); }, // Map translate to transform translate: function(x2, y2) { return this.transform({ translate: [x2, y2] }, true); }, // Map relative translations to transform relative: function(x2, y2) { return this.transform({ relative: [x2, y2] }, true); }, // Map flip to transform flip: function(direction = "both", origin = "center") { if ("xybothtrue".indexOf(direction) === -1) { origin = direction; direction = "both"; } return this.transform({ flip: direction, origin }, true); }, // Opacity opacity: function(value) { return this.attr("opacity", value); } }); registerMethods("radius", { // Add x and y radius radius: function(x2, y2 = x2) { const type = (this._element || this).type; return type === "radialGradient" ? this.attr("r", new SVGNumber(x2)) : this.rx(x2).ry(y2); } }); registerMethods("Path", { // Get path length length: function() { return this.node.getTotalLength(); }, // Get point at length pointAt: function(length2) { return new Point(this.node.getPointAtLength(length2)); } }); registerMethods(["Element", "Runner"], { // Set font font: function(a, v) { if (typeof a === "object") { for (v in a) this.font(v, a[v]); return this; } return a === "leading" ? this.leading(v) : a === "anchor" ? this.attr("text-anchor", v) : a === "size" || a === "family" || a === "weight" || a === "stretch" || a === "variant" || a === "style" ? this.attr("font-" + a, v) : this.attr(a, v); } }); const methods = ["click", "dblclick", "mousedown", "mouseup", "mouseover", "mouseout", "mousemove", "mouseenter", "mouseleave", "touchstart", "touchmove", "touchleave", "touchend", "touchcancel"].reduce(function(last, event) { const fn = function(f) { if (f === null) { this.off(event); } else { this.on(event, f); } return this; }; last[event] = fn; return last; }, {}); registerMethods("Element", methods); function untransform() { return this.attr("transform", null); } function matrixify() { const matrix = (this.attr("transform") || "").split(transforms).slice(0, -1).map(function(str) { const kv = str.trim().split("("); return [kv[0], kv[1].split(delimiter).map(function(str2) { return parseFloat(str2); })]; }).reverse().reduce(function(matrix2, transform2) { if (transform2[0] === "matrix") { return matrix2.lmultiply(Matrix.fromArray(transform2[1])); } return matrix2[transform2[0]].apply(matrix2, transform2[1]); }, new Matrix()); return matrix; } function toParent(parent, i) { if (this === parent) return this; const ctm2 = this.screenCTM(); const pCtm = parent.screenCTM().inverse(); this.addTo(parent, i).untransform().transform(pCtm.multiply(ctm2)); return this; } function toRoot(i) { return this.toParent(this.root(), i); } function transform(o, relative) { if (o == null || typeof o === "string") { const decomposed = new Matrix(this).decompose(); return o == null ? decomposed : decomposed[o]; } if (!Matrix.isMatrixLike(o)) { o = { ...o, origin: getOrigin(o, this) }; } const cleanRelative = relative === true ? this : relative || false; const result = new Matrix(cleanRelative).transform(o); return this.attr("transform", result); } registerMethods("Element", { untransform, matrixify, toParent, toRoot, transform }); class Container extends Element { flatten(parent = this, index) { this.each(function() { if (this instanceof Container) { return this.flatten().ungroup(); } }); return this; } ungroup(parent = this.parent(), index = parent.index(this)) { index = index === -1 ? parent.children().length : index; this.each(function(i, children) { return children[children.length - i - 1].toParent(parent, index); }); return this.remove(); } } register(Container, "Container"); class Defs extends Container { constructor(node, attrs2 = node) { super(nodeOrNew("defs", node), attrs2); } flatten() { return this; } ungroup() { return this; } } register(Defs, "Defs"); class Shape extends Element { } register(Shape, "Shape"); function rx(rx2) { return this.attr("rx", rx2); } function ry(ry2) { return this.attr("ry", ry2); } function x$3(x2) { return x2 == null ? this.cx() - this.rx() : this.cx(x2 + this.rx()); } function y$3(y2) { return y2 == null ? this.cy() - this.ry() : this.cy(y2 + this.ry()); } function cx$1(x2) { return this.attr("cx", x2); } function cy$1(y2) { return this.attr("cy", y2); } function width$2(width2) { return width2 == null ? this.rx() * 2 : this.rx(new SVGNumber(width2).divide(2)); } function height$2(height2) { return height2 == null ? this.ry() * 2 : this.ry(new SVGNumber(height2).divide(2)); } var circled = { __proto__: null, rx, ry, x: x$3, y: y$3, cx: cx$1, cy: cy$1, width: width$2, height: height$2 }; class Ellipse extends Shape { constructor(node, attrs2 = node) { super(nodeOrNew("ellipse", node), attrs2); } size(width2, height2) { const p = proportionalSize(this, width2, height2); return this.rx(new SVGNumber(p.width).divide(2)).ry(new SVGNumber(p.height).divide(2)); } } extend(Ellipse, circled); registerMethods("Container", { // Create an ellipse ellipse: wrapWithAttrCheck(function(width2 = 0, height2 = width2) { return this.put(new Ellipse()).size(width2, height2).move(0, 0); }) }); register(Ellipse, "Ellipse"); class Fragment extends Dom { constructor(node = globals.document.createDocumentFragment()) { super(node); } // Import / Export raw xml xml(xmlOrFn, outerXML, ns) { if (typeof xmlOrFn === "boolean") { ns = outerXML; outerXML = xmlOrFn; xmlOrFn = null; } if (xmlOrFn == null || typeof xmlOrFn === "function") { const wrapper = new Dom(create("wrapper", ns)); wrapper.add(this.node.cloneNode(true)); return wrapper.xml(false, ns); } return super.xml(xmlOrFn, false, ns); } } register(Fragment, "Fragment"); function from(x2, y2) { return (this._element || this).type === "radialGradient" ? this.attr({ fx: new SVGNumber(x2), fy: new SVGNumber(y2) }) : this.attr({ x1: new SVGNumber(x2), y1: new SVGNumber(y2) }); } function to(x2, y2) { return (this._element || this).type === "radialGradient" ? this.attr({ cx: new SVGNumber(x2), cy: new SVGNumber(y2) }) : this.attr({ x2: new SVGNumber(x2), y2: new SVGNumber(y2) }); } var gradiented = { __proto__: null, from, to }; class Gradient extends Container { constructor(type, attrs2) { super(nodeOrNew(type + "Gradient", typeof type === "string" ? null : type), attrs2); } // custom attr to handle transform attr(a, b, c) { if (a === "transform") a = "gradientTransform"; return super.attr(a, b, c); } bbox() { return new Box(); } targets() { return baseFind("svg [fill*=" + this.id() + "]"); } // Alias string conversion to fill toString() { return this.url(); } // Update gradient update(block) { this.clear(); if (typeof block === "function") { block.call(this, this); } return this; } // Return the fill id url() { return "url(#" + this.id() + ")"; } } extend(Gradient, gradiented); registerMethods({ Container: { // Create gradient element in defs gradient(...args) { return this.defs().gradient(...args); } }, // define gradient Defs: { gradient: wrapWithAttrCheck(function(type, block) { return this.put(new Gradient(type)).update(block); }) } }); register(Gradient, "Gradient"); class Pattern extends Container { // Initialize node constructor(node, attrs2 = node) { super(nodeOrNew("pattern", node), attrs2); } // custom attr to handle transform attr(a, b, c) { if (a === "transform") a = "patternTransform"; return super.attr(a, b, c); } bbox() { return new Box(); } targets() { return baseFind("svg [fill*=" + this.id() + "]"); } // Alias string conversion to fill toString() { return this.url(); } // Update pattern by rebuilding update(block) { this.clear(); if (typeof block === "function") { block.call(this, this); } return this; } // Return the fill id url() { return "url(#" + this.id() + ")"; } } registerMethods({ Container: { // Create pattern element in defs pattern(...args) { return this.defs().pattern(...args); } }, Defs: { pattern: wrapWithAttrCheck(function(width2, height2, block) { return this.put(new Pattern()).update(block).attr({ x: 0, y: 0, width: width2, height: height2, patternUnits: "userSpaceOnUse" }); }) } }); register(Pattern, "Pattern"); class Image extends Shape { constructor(node, attrs2 = node) { super(nodeOrNew("image", node), attrs2); } // (re)load image load(url, callback) { if (!url) return this; const img = new globals.window.Image(); on(img, "load", function(e) { const p = this.parent(Pattern); if (this.width() === 0 && this.height() === 0) { this.size(img.width, img.height); } if (p instanceof Pattern) { if (p.width() === 0 && p.height() === 0) { p.size(this.width(), this.height()); } } if (typeof callback === "function") { callback.call(this, e); } }, this); on(img, "load error", function() { off(img); }); return this.attr("href", img.src = url, xlink); } } registerAttrHook(function(attr2, val, _this) { if (attr2 === "fill" || attr2 === "stroke") { if (isImage.test(val)) { val = _this.root().defs().image(val); } } if (val instanceof Image) { val = _this.root().defs().pattern(0, 0, (pattern) => { pattern.add(val); }); } return val; }); registerMethods({ Container: { // create image element, load image and set its size image: wrapWithAttrCheck(function(source, callback) { return this.put(new Image()).size(0, 0).load(source, callback); }) } }); register(Image, "Image"); class PointArray extends SVGArray { // Get bounding box of points bbox() { let maxX = -Infinity; let maxY = -Infinity; let minX = Infinity; let minY = Infinity; this.forEach(function(el) { maxX = Math.max(el[0], maxX); maxY = Math.max(el[1], maxY); minX = Math.min(el[0], minX); minY = Math.min(el[1], minY); }); return new Box(minX, minY, maxX - minX, maxY - minY); } // Move point string move(x2, y2) { const box = this.bbox(); x2 -= box.x; y2 -= box.y; if (!isNaN(x2) && !isNaN(y2)) { for (let i = this.length - 1; i >= 0; i--) { this[i] = [this[i][0] + x2, this[i][1] + y2]; } } return this; } // Parse point string and flat array parse(array2 = [0, 0]) { const points = []; if (array2 instanceof Array) { array2 = Array.prototype.concat.apply([], array2); } else { array2 = array2.trim().split(delimiter).map(parseFloat); } if (array2.length % 2 !== 0) array2.pop(); for (let i = 0, len = array2.length; i < len; i = i + 2) { points.push([array2[i], array2[i + 1]]); } return points; } // Resize poly string size(width2, height2) { let i; const box = this.bbox(); for (i = this.length - 1; i >= 0; i--) { if (box.width) this[i][0] = (this[i][0] - box.x) * width2 / box.width + box.x; if (box.height) this[i][1] = (this[i][1] - box.y) * height2 / box.height + box.y; } return this; } // Convert array to line object toLine() { return { x1: this[0][0], y1: this[0][1], x2: this[1][0], y2: this[1][1] }; } // Convert array to string toString() { const array2 = []; for (let i = 0, il = this.length; i < il; i++) { array2.push(this[i].join(",")); } return array2.join(" "); } transform(m) { return this.clone().transformO(m); } // transform points with matrix (similar to Point.transform) transformO(m) { if (!Matrix.isMatrixLike(m)) { m = new Matrix(m); } for (let i = this.length; i--; ) { const [x2, y2] = this[i]; this[i][0] = m.a * x2 + m.c * y2 + m.e; this[i][1] = m.b * x2 + m.d * y2 + m.f; } return this; } } const MorphArray = PointArray; function x$2(x2) { return x2 == null ? this.bbox().x : this.move(x2, this.bbox().y); } function y$2(y2) { return y2 == null ? this.bbox().y : this.move(this.bbox().x, y2); } function width$1(width2) { const b = this.bbox(); return width2 == null ? b.width : this.size(width2, b.height); } function height$1(height2) { const b = this.bbox(); return height2 == null ? b.height : this.size(b.width, height2); } var pointed = { __proto__: null, MorphArray, x: x$2, y: y$2, width: width$1, height: height$1 }; class Line extends Shape { // Initialize node constructor(node, attrs2 = node) { super(nodeOrNew("line", node), attrs2); } // Get array array() { return new PointArray([[this.attr("x1"), this.attr("y1")], [this.attr("x2"), this.attr("y2")]]); } // Move by left top corner move(x2, y2) { return this.attr(this.array().move(x2, y2).toLine()); } // Overwrite native plot() method plot(x1, y1, x2, y2) { if (x1 == null) { return this.array(); } else if (typeof y1 !== "undefined") { x1 = { x1, y1, x2, y2 }; } else { x1 = new PointArray(x1).toLine(); } return this.attr(x1); } // Set element size to given width and height size(width2, height2) { const p = proportionalSize(this, width2, height2); return this.attr(this.array().size(p.width, p.height).toLine()); } } extend(Line, pointed); registerMethods({ Container: { // Create a line element line: wrapWithAttrCheck(function(...args) { return Line.prototype.plot.apply(this.put(new Line()), args[0] != null ? args : [0, 0, 0, 0]); }) } }); register(Line, "Line"); class Marker extends Container { // Initialize node constructor(node, attrs2 = node) { super(nodeOrNew("marker", node), attrs2); } // Set height of element height(height2) { return this.attr("markerHeight", height2); } orient(orient) { return this.attr("orient", orient); } // Set marker refX and refY ref(x2, y2) { return this.attr("refX", x2).attr("refY", y2); } // Return the fill id toString() { return "url(#" + this.id() + ")"; } // Update marker update(block) { this.clear(); if (typeof block === "function") { block.call(this, this); } return this; } // Set width of element width(width2) { return this.attr("markerWidth", width2); } } registerMethods({ Container: { marker(...args) { return this.defs().marker(...args); } }, Defs: { // Create marker marker: wrapWithAttrCheck(function(width2, height2, block) { return this.put(new Marker()).size(width2, height2).ref(width2 / 2, height2 / 2).viewbox(0, 0, width2, height2).attr("orient", "auto").update(block); }) }, marker: { // Create and attach markers marker(marker, width2, height2, block) { let attr2 = ["marker"]; if (marker !== "all") attr2.push(marker); attr2 = attr2.join("-"); marker = arguments[1] instanceof Marker ? arguments[1] : this.defs().marker(width2, height2, block); return this.attr(attr2, marker); } } }); register(Marker, "Marker"); function makeSetterGetter(k, f) { return function(v) { if (v == null) return this[k]; this[k] = v; if (f) f.call(this); return this; }; } const easing = { "-": function(pos) { return pos; }, "<>": function(pos) { return -Math.cos(pos * Math.PI) / 2 + 0.5; }, ">": function(pos) { return Math.sin(pos * Math.PI / 2); }, "<": function(pos) { return -Math.cos(pos * Math.PI / 2) + 1; }, bezier: function(x1, y1, x2, y2) { return function(t) { if (t < 0) { if (x1 > 0) { return y1 / x1 * t; } else if (x2 > 0) { return y2 / x2 * t; } else { return 0; } } else if (t > 1) { if (x2 < 1) { return (1 - y2) / (1 - x2) * t + (y2 - x2) / (1 - x2); } else if (x1 < 1) { return (1 - y1) / (1 - x1) * t + (y1 - x1) / (1 - x1); } else { return 1; } } else { return 3 * t * (1 - t) ** 2 * y1 + 3 * t ** 2 * (1 - t) * y2 + t ** 3; } }; }, // see https://www.w3.org/TR/css-easing-1/#step-timing-function-algo steps: function(steps, stepPosition = "end") { stepPosition = stepPosition.split("-").reverse()[0]; let jumps = steps; if (stepPosition === "none") { --jumps; } else if (stepPosition === "both") { ++jumps; } return (t, beforeFlag = false) => { let step = Math.floor(t * steps); const jumping = t * step % 1 === 0; if (stepPosition === "start" || stepPosition === "both") { ++step; } if (beforeFlag && jumping) { --step; } if (t >= 0 && step < 0) { step = 0; } if (t <= 1 && step > jumps) { step = jumps; } return step / jumps; }; } }; class Stepper { done() { return false; } } class Ease extends Stepper { constructor(fn = timeline.ease) { super(); this.ease = easing[fn] || fn; } step(from2, to2, pos) { if (typeof from2 !== "number") { return pos < 1 ? from2 : to2; } return from2 + (to2 - from2) * this.ease(pos); } } class Controller extends Stepper { constructor(fn) { super(); this.stepper = fn; } done(c) { return c.done; } step(current, target, dt, c) { return this.stepper(current, target, dt, c); } } function recalculate() { const duration = (this._duration || 500) / 1e3; const overshoot = this._overshoot || 0; const eps = 1e-10; const pi = Math.PI; const os = Math.log(overshoot / 100 + eps); const zeta = -os / Math.sqrt(pi * pi + os * os); const wn = 3.9 / (zeta * duration); this.d = 2 * zeta * wn; this.k = wn * wn; } class Spring extends Controller { constructor(duration = 500, overshoot = 0) { super(); this.duration(duration).overshoot(overshoot); } step(current, target, dt, c) { if (typeof current === "string") return current; c.done = dt === Infinity; if (dt === Infinity) return target; if (dt === 0) return current; if (dt > 100) dt = 16; dt /= 1e3; const velocity = c.velocity || 0; const acceleration = -this.d * velocity - this.k * (current - target); const newPosition = current + velocity * dt + acceleration * dt * dt / 2; c.velocity = velocity + acceleration * dt; c.done = Math.abs(target - newPosition) + Math.abs(velocity) < 2e-3; return c.done ? target : newPosition; } } extend(Spring, { duration: makeSetterGetter("_duration", recalculate), overshoot: makeSetterGetter("_overshoot", recalculate) }); class PID extends Controller { constructor(p = 0.1, i = 0.01, d = 0, windup = 1e3) { super(); this.p(p).i(i).d(d).windup(windup); } step(current, target, dt, c) { if (typeof current === "string") return current; c.done = dt === Infinity; if (dt === Infinity) return target; if (dt === 0) return current; const p = target - current; let i = (c.integral || 0) + p * dt; const d = (p - (c.error || 0)) / dt; const windup = this._windup; if (windup !== false) { i = Math.max(-windup, Math.min(i, windup)); } c.error = p; c.integral = i; c.done = Math.abs(p) < 1e-3; return c.done ? target : current + (this.P * p + this.I * i + this.D * d); } } extend(PID, { windup: makeSetterGetter("_windup"), p: makeSetterGetter("P"), i: makeSetterGetter("I"), d: makeSetterGetter("D") }); const segmentParameters = { M: 2, L: 2, H: 1, V: 1, C: 6, S: 4, Q: 4, T: 2, A: 7, Z: 0 }; const pathHandlers = { M: function(c, p, p0) { p.x = p0.x = c[0]; p.y = p0.y = c[1]; return ["M", p.x, p.y]; }, L: function(c, p) { p.x = c[0]; p.y = c[1]; return ["L", c[0], c[1]]; }, H: function(c, p) { p.x = c[0]; return ["H", c[0]]; }, V: function(c, p) { p.y = c[0]; return ["V", c[0]]; }, C: function(c, p) { p.x = c[4]; p.y = c[5]; return ["C", c[0], c[1], c[2], c[3], c[4], c[5]]; }, S: function(c, p) { p.x = c[2]; p.y = c[3]; return ["S", c[0], c[1], c[2], c[3]]; }, Q: function(c, p) { p.x = c[2]; p.y = c[3]; return ["Q", c[0], c[1], c[2], c[3]]; }, T: function(c, p) { p.x = c[0]; p.y = c[1]; return ["T", c[0], c[1]]; }, Z: function(c, p, p0) { p.x = p0.x; p.y = p0.y; return ["Z"]; }, A: function(c, p) { p.x = c[5]; p.y = c[6]; return ["A", c[0], c[1], c[2], c[3], c[4], c[5], c[6]]; } }; const mlhvqtcsaz = "mlhvqtcsaz".split(""); for (let i = 0, il = mlhvqtcsaz.length; i < il; ++i) { pathHandlers[mlhvqtcsaz[i]] = function(i2) { return function(c, p, p0) { if (i2 === "H") c[0] = c[0] + p.x; else if (i2 === "V") c[0] = c[0] + p.y; else if (i2 === "A") { c[5] = c[5] + p.x; c[6] = c[6] + p.y; } else { for (let j = 0, jl = c.length; j < jl; ++j) { c[j] = c[j] + (j % 2 ? p.y : p.x); } } return pathHandlers[i2](c, p, p0); }; }(mlhvqtcsaz[i].toUpperCase()); } function makeAbsolut(parser2) { const command = parser2.segment[0]; return pathHandlers[command](parser2.segment.slice(1), parser2.p, parser2.p0); } function segmentComplete(parser2) { return parser2.segment.length && parser2.segment.length - 1 === segmentParameters[parser2.segment[0].toUpperCase()]; } function startNewSegment(parser2, token) { parser2.inNumber && finalizeNumber(parser2, false); const pathLetter = isPathLetter.test(token); if (pathLetter) { parser2.segment = [token]; } else { const lastCommand = parser2.lastCommand; const small = lastCommand.toLowerCase(); const isSmall = lastCommand === small; parser2.segment = [small === "m" ? isSmall ? "l" : "L" : lastCommand]; } parser2.inSegment = true; parser2.lastCommand = parser2.segment[0]; return pathLetter; } function finalizeNumber(parser2, inNumber) { if (!parser2.inNumber) throw new Error("Parser Error"); parser2.number && parser2.segment.push(parseFloat(parser2.number)); parser2.inNumber = inNumber; parser2.number = ""; parser2.pointSeen = false; parser2.hasExponent = false; if (segmentComplete(parser2)) { finalizeSegment(parser2); } } function finalizeSegment(parser2) { parser2.inSegment = false; if (parser2.absolute) { parser2.segment = makeAbsolut(parser2); } parser2.segments.push(parser2.segment); } function isArcFlag(parser2) { if (!parser2.segment.length) return false; const isArc = parser2.segment[0].toUpperCase() === "A"; const length2 = parser2.segment.length; return isArc && (length2 === 4 || length2 === 5); } function isExponential(parser2) { return parser2.lastToken.toUpperCase() === "E"; } function pathParser(d, toAbsolute = true) { let index = 0; let token = ""; const parser2 = { segment: [], inNumber: false, number: "", lastToken: "", inSegment: false, segments: [], pointSeen: false, hasExponent: false, absolute: toAbsolute, p0: new Point(), p: new Point() }; while (parser2.lastToken = token, token = d.charAt(index++)) { if (!parser2.inSegment) { if (startNewSegment(parser2, token)) { continue; } } if (token === ".") { if (parser2.pointSeen || parser2.hasExponent) { finalizeNumber(parser2, false); --index; continue; } parser2.inNumber = true; parser2.pointSeen = true; parser2.number += token; continue; } if (!isNaN(parseInt(token))) { if (parser2.number === "0" || isArcFlag(parser2)) { parser2.inNumber = true; parser2.number = token; finalizeNumber(parser2, true); continue; } parser2.inNumber = true; parser2.number += token; continue; } if (token === " " || token === ",") { if (parser2.inNumber) { finalizeNumber(parser2, false); } continue; } if (token === "-") { if (parser2.inNumber && !isExponential(parser2)) { finalizeNumber(parser2, false); --index; continue; } parser2.number += token; parser2.inNumber = true; continue; } if (token.toUpperCase() === "E") { parser2.number += token; parser2.hasExponent = true; continue; } if (isPathLetter.test(token)) { if (parser2.inNumber) { finalizeNumber(parser2, false); } else if (!segmentComplete(parser2)) { throw new Error("parser Error"); } else { finalizeSegment(parser2); } --index; } } if (parser2.inNumber) { finalizeNumber(parser2, false); } if (parser2.inSegment && segmentComplete(parser2)) { finalizeSegment(parser2); } return parser2.segments; } function arrayToString(a) { let s = ""; for (let i = 0, il = a.length; i < il; i++) { s += a[i][0]; if (a[i][1] != null) { s += a[i][1]; if (a[i][2] != null) { s += " "; s += a[i][2]; if (a[i][3] != null) { s += " "; s += a[i][3]; s += " "; s += a[i][4]; if (a[i][5] != null) { s += " "; s += a[i][5]; s += " "; s += a[i][6]; if (a[i][7] != null) { s += " "; s += a[i][7]; } } } } } } return s + " "; } class PathArray extends SVGArray { // Get bounding box of path bbox() { parser().path.setAttribute("d", this.toString()); return new Box(parser.nodes.path.getBBox()); } // Move path string move(x2, y2) { const box = this.bbox(); x2 -= box.x; y2 -= box.y; if (!isNaN(x2) && !isNaN(y2)) { for (let l, i = this.length - 1; i >= 0; i--) { l = this[i][0]; if (l === "M" || l === "L" || l === "T") { this[i][1] += x2; this[i][2] += y2; } else if (l === "H") { this[i][1] += x2; } else if (l === "V") { this[i][1] += y2; } else if (l === "C" || l === "S" || l === "Q") { this[i][1] += x2; this[i][2] += y2; this[i][3] += x2; this[i][4] += y2; if (l === "C") { this[i][5] += x2; this[i][6] += y2; } } else if (l === "A") { this[i][6] += x2; this[i][7] += y2; } } } return this; } // Absolutize and parse path to array parse(d = "M0 0") { if (Array.isArray(d)) { d = Array.prototype.concat.apply([], d).toString(); } return pathParser(d); } // Resize path string size(width2, height2) { const box = this.bbox(); let i, l; box.width = box.width === 0 ? 1 : box.width; box.height = box.height === 0 ? 1 : box.height; for (i = this.length - 1; i >= 0; i--) { l = this[i][0]; if (l === "M" || l === "L" || l === "T") { this[i][1] = (this[i][1] - box.x) * width2 / box.width + box.x; this[i][2] = (this[i][2] - box.y) * height2 / box.height + box.y; } else if (l === "H") { this[i][1] = (this[i][1] - box.x) * width2 / box.width + box.x; } else if (l === "V") { this[i][1] = (this[i][1] - box.y) * height2 / box.height + box.y; } else if (l === "C" || l === "S" || l === "Q") { this[i][1] = (this[i][1] - box.x) * width2 / box.width + box.x; this[i][2] = (this[i][2] - box.y) * height2 / box.height + box.y; this[i][3] = (this[i][3] - box.x) * width2 / box.width + box.x; this[i][4] = (this[i][4] - box.y) * height2 / box.height + box.y; if (l === "C") { this[i][5] = (this[i][5] - box.x) * width2 / box.width + box.x; this[i][6] = (this[i][6] - box.y) * height2 / box.height + box.y; } } else if (l === "A") { this[i][1] = this[i][1] * width2 / box.width; this[i][2] = this[i][2] * height2 / box.height; this[i][6] = (this[i][6] - box.x) * width2 / box.width + box.x; this[i][7] = (this[i][7] - box.y) * height2 / box.height + box.y; } } return this; } // Convert array to string toString() { return arrayToString(this); } } const getClassForType = (value) => { const type = typeof value; if (type === "number") { return SVGNumber; } else if (type === "string") { if (Color.isColor(value)) { return Color; } else if (delimiter.test(value)) { return isPathLetter.test(value) ? PathArray : SVGArray; } else if (numberAndUnit.test(value)) { return SVGNumber; } else { return NonMorphable; } } else if (morphableTypes.indexOf(value.constructor) > -1) { return value.constructor; } else if (Array.isArray(value)) { return SVGArray; } else if (type === "object") { return ObjectBag; } else { return NonMorphable; } }; class Morphable { constructor(stepper) { this._stepper = stepper || new Ease("-"); this._from = null; this._to = null; this._type = null; this._context = null; this._morphObj = null; } at(pos) { return this._morphObj.morph(this._from, this._to, pos, this._stepper, this._context); } done() { const complete = this._context.map(this._stepper.done).reduce(function(last, curr) { return last && curr; }, true); return complete; } from(val) { if (val == null) { return this._from; } this._from = this._set(val); return this; } stepper(stepper) { if (stepper == null) return this._stepper; this._stepper = stepper; return this; } to(val) { if (val == null) { return this._to; } this._to = this._set(val); return this; } type(type) { if (type == null) { return this._type; } this._type = type; return this; } _set(value) { if (!this._type) { this.type(getClassForType(value)); } let result = new this._type(value); if (this._type === Color) { result = this._to ? result[this._to[4]]() : this._from ? result[this._from[4]]() : result; } if (this._type === ObjectBag) { result = this._to ? result.align(this._to) : this._from ? result.align(this._from) : result; } result = result.toConsumable(); this._morphObj = this._morphObj || new this._type(); this._context = this._context || Array.apply(null, Array(result.length)).map(Object).map(function(o) { o.done = true; return o; }); return result; } } class NonMorphable { constructor(...args) { this.init(...args); } init(val) { val = Array.isArray(val) ? val[0] : val; this.value = val; return this; } toArray() { return [this.value]; } valueOf() { return this.value; } } class TransformBag { constructor(...args) { this.init(...args); } init(obj) { if (Array.isArray(obj)) { obj = { scaleX: obj[0], scaleY: obj[1], shear: obj[2], rotate: obj[3], translateX: obj[4], translateY: obj[5], originX: obj[6], originY: obj[7] }; } Object.assign(this, TransformBag.defaults, obj); return this; } toArray() { const v = this; return [v.scaleX, v.scaleY, v.shear, v.rotate, v.translateX, v.translateY, v.originX, v.originY]; } } TransformBag.defaults = { scaleX: 1, scaleY: 1, shear: 0, rotate: 0, translateX: 0, translateY: 0, originX: 0, originY: 0 }; const sortByKey = (a, b) => { return a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0; }; class ObjectBag { constructor(...args) { this.init(...args); } align(other) { const values = this.values; for (let i = 0, il = values.length; i < il; ++i) { if (values[i + 1] === other[i + 1]) { if (values[i + 1] === Color && other[i + 7] !== values[i + 7]) { const space = other[i + 7]; const color = new Color(this.values.splice(i + 3, 5))[space]().toArray(); this.values.splice(i + 3, 0, ...color); } i += values[i + 2] + 2; continue; } if (!other[i + 1]) { return this; } const defaultObject = new other[i + 1]().toArray(); const toDelete = values[i + 2] + 3; values.splice(i, toDelete, other[i], other[i + 1], other[i + 2], ...defaultObject); i += values[i + 2] + 2; } return this; } init(objOrArr) { this.values = []; if (Array.isArray(objOrArr)) { this.values = objOrArr.slice(); return; } objOrArr = objOrArr || {}; const entries = []; for (const i in objOrArr) { const Type = getClassForType(objOrArr[i]); const val = new Type(objOrArr[i]).toArray(); entries.push([i, Type, val.length, ...val]); } entries.sort(sortByKey); this.values = entries.reduce((last, curr) => last.concat(curr), []); return this; } toArray() { return this.values; } valueOf() { const obj = {}; const arr = this.values; while (arr.length) { const key = arr.shift(); const Type = arr.shift(); const num = arr.shift(); const values = arr.splice(0, num); obj[key] = new Type(values); } return obj; } } const morphableTypes = [NonMorphable, TransformBag, ObjectBag]; function registerMorphableType(type = []) { morphableTypes.push(...[].concat(type)); } function makeMorphable() { extend(morphableTypes, { to(val) { return new Morphable().type(this.constructor).from(this.toArray()).to(val); }, fromArray(arr) { this.init(arr); return this; }, toConsumable() { return this.toArray(); }, morph(from2, to2, pos, stepper, context) { const mapper = function(i, index) { return stepper.step(i, to2[index], pos, context[index], context); }; return this.fromArray(from2.map(mapper)); } }); } class Path extends Shape { // Initialize node constructor(node, attrs2 = node) { super(nodeOrNew("path", node), attrs2); } // Get array array() { return this._array || (this._array = new PathArray(this.attr("d"))); } // Clear array cache clear() { delete this._array; return this; } // Set height of element height(height2) { return height2 == null ? this.bbox().height : this.size(this.bbox().width, height2); } // Move by left top corner move(x2, y2) { return this.attr("d", this.array().move(x2, y2)); } // Plot new path plot(d) { return d == null ? this.array() : this.clear().attr("d", typeof d === "string" ? d : this._array = new PathArray(d)); } // Set element size to given width and height size(width2, height2) { const p = proportionalSize(this, width2, height2); return this.attr("d", this.array().size(p.width, p.height)); } // Set width of element width(width2) { return width2 == null ? this.bbox().width : this.size(width2, this.bbox().height); } // Move by left top corner over x-axis x(x2) { return x2 == null ? this.bbox().x : this.move(x2, this.bbox().y); } // Move by left top corner over y-axis y(y2) { return y2 == null ? this.bbox().y : this.move(this.bbox().x, y2); } } Path.prototype.MorphArray = PathArray; registerMethods({ Container: { // Create a wrapped path element path: wrapWithAttrCheck(function(d) { return this.put(new Path()).plot(d || new PathArray()); }) } }); register(Path, "Path"); function array() { return this._array || (this._array = new PointArray(this.attr("points"))); } function clear() { delete this._array; return this; } function move$2(x2, y2) { return this.attr("points", this.array().move(x2, y2)); } function plot(p) { return p == null ? this.array() : this.clear().attr("points", typeof p === "string" ? p : this._array = new PointArray(p)); } function size$1(width2, height2) { const p = proportionalSize(this, width2, height2); return this.attr("points", this.array().size(p.width, p.height)); } var poly = { __proto__: null, array, clear, move: move$2, plot, size: size$1 }; class Polygon extends Shape { // Initialize node constructor(node, attrs2 = node) { super(nodeOrNew("polygon", node), attrs2); } } registerMethods({ Container: { // Create a wrapped polygon element polygon: wrapWithAttrCheck(function(p) { return this.put(new Polygon()).plot(p || new PointArray()); }) } }); extend(Polygon, pointed); extend(Polygon, poly); register(Polygon, "Polygon"); class Polyline extends Shape { // Initialize node constructor(node, attrs2 = node) { super(nodeOrNew("polyline", node), attrs2); } } registerMethods({ Container: { // Create a wrapped polygon element polyline: wrapWithAttrCheck(function(p) { return this.put(new Polyline()).plot(p || new PointArray()); }) } }); extend(Polyline, pointed); extend(Polyline, poly); register(Polyline, "Polyline"); class Rect extends Shape { // Initialize node constructor(node, attrs2 = node) { super(nodeOrNew("rect", node), attrs2); } } extend(Rect, { rx, ry }); registerMethods({ Container: { // Create a rect element rect: wrapWithAttrCheck(function(width2, height2) { return this.put(new Rect()).size(width2, height2); }) } }); register(Rect, "Rect"); class Queue { constructor() { this._first = null; this._last = null; } // Shows us the first item in the list first() { return this._first && this._first.value; } // Shows us the last item in the list last() { return this._last && this._last.value; } push(value) { const item = typeof value.next !== "undefined" ? value : { value, next: null, prev: null }; if (this._last) { item.prev = this._last; this._last.next = item; this._last = item; } else { this._last = item; this._first = item; } return item; } // Removes the item that was returned from the push remove(item) { if (item.prev) item.prev.next = item.next; if (item.next) item.next.prev = item.prev; if (item === this._last) this._last = item.prev; if (item === this._first) this._first = item.next; item.prev = null; item.next = null; } shift() { const remove = this._first; if (!remove) return null; this._first = remove.next; if (this._first) this._first.prev = null; this._last = this._first ? this._last : null; return remove.value; } } const Animator = { nextDraw: null, frames: new Queue(), timeouts: new Queue(), immediates: new Queue(), timer: () => globals.window.performance || globals.window.Date, transforms: [], frame(fn) { const node = Animator.frames.push({ run: fn }); if (Animator.nextDraw === null) { Animator.nextDraw = globals.window.requestAnimationFrame(Animator._draw); } return node; }, timeout(fn, delay) { delay = delay || 0; const time = Animator.timer().now() + delay; const node = Animator.timeouts.push({ run: fn, time }); if (Animator.nextDraw === null) { Animator.nextDraw = globals.window.requestAnimationFrame(Animator._draw); } return node; }, immediate(fn) { const node = Animator.immediates.push(fn); if (Animator.nextDraw === null) { Animator.nextDraw = globals.window.requestAnimationFrame(Animator._draw); } return node; }, cancelFrame(node) { node != null && Animator.frames.remove(node); }, clearTimeout(node) { node != null && Animator.timeouts.remove(node); }, cancelImmediate(node) { node != null && Animator.immediates.remove(node); }, _draw(now) { let nextTimeout = null; const lastTimeout = Animator.timeouts.last(); while (nextTimeout = Animator.timeouts.shift()) { if (now >= nextTimeout.time) { nextTimeout.run(); } else { Animator.timeouts.push(nextTimeout); } if (nextTimeout === lastTimeout) break; } let nextFrame = null; const lastFrame = Animator.frames.last(); while (nextFrame !== lastFrame && (nextFrame = Animator.frames.shift())) { nextFrame.run(now); } let nextImmediate = null; while (nextImmediate = Animator.immediates.shift()) { nextImmediate(); } Animator.nextDraw = Animator.timeouts.first() || Animator.frames.first() ? globals.window.requestAnimationFrame(Animator._draw) : null; } }; const makeSchedule = function(runnerInfo) { const start = runnerInfo.start; const duration = runnerInfo.runner.duration(); const end = start + duration; return { start, duration, end, runner: runnerInfo.runner }; }; const defaultSource = function() { const w = globals.window; return (w.performance || w.Date).now(); }; class Timeline extends EventTarget { // Construct a new timeline on the given element constructor(timeSource = defaultSource) { super(); this._timeSource = timeSource; this._startTime = 0; this._speed = 1; this._persist = 0; this._nextFrame = null; this._paused = true; this._runners = []; this._runnerIds = []; this._lastRunnerId = -1; this._time = 0; this._lastSourceTime = 0; this._lastStepTime = 0; this._step = this._stepFn.bind(this, false); this._stepImmediate = this._stepFn.bind(this, true); } active() { return !!this._nextFrame; } finish() { this.time(this.getEndTimeOfTimeline() + 1); return this.pause(); } // Calculates the end of the timeline getEndTime() { const lastRunnerInfo = this.getLastRunnerInfo(); const lastDuration = lastRunnerInfo ? lastRunnerInfo.runner.duration() : 0; const lastStartTime = lastRunnerInfo ? lastRunnerInfo.start : this._time; return lastStartTime + lastDuration; } getEndTimeOfTimeline() { const endTimes = this._runners.map((i) => i.start + i.runner.duration()); return Math.max(0, ...endTimes); } getLastRunnerInfo() { return this.getRunnerInfoById(this._lastRunnerId); } getRunnerInfoById(id) { return this._runners[this._runnerIds.indexOf(id)] || null; } pause() { this._paused = true; return this._continue(); } persist(dtOrForever) { if (dtOrForever == null) return this._persist; this._persist = dtOrForever; return this; } play() { this._paused = false; return this.updateTime()._continue(); } reverse(yes) { const currentSpeed = this.speed(); if (yes == null) return this.speed(-currentSpeed); const positive = Math.abs(currentSpeed); return this.speed(yes ? -positive : positive); } // schedules a runner on the timeline schedule(runner, delay, when) { if (runner == null) { return this._runners.map(makeSchedule); } let absoluteStartTime = 0; const endTime = this.getEndTime(); delay = delay || 0; if (when == null || when === "last" || when === "after") { absoluteStartTime = endTime; } else if (when === "absolute" || when === "start") { absoluteStartTime = delay; delay = 0; } else if (when === "now") { absoluteStartTime = this._time; } else if (when === "relative") { const runnerInfo2 = this.getRunnerInfoById(runner.id); if (runnerInfo2) { absoluteStartTime = runnerInfo2.start + delay; delay = 0; } } else if (when === "with-last") { const lastRunnerInfo = this.getLastRunnerInfo(); const lastStartTime = lastRunnerInfo ? lastRunnerInfo.start : this._time; absoluteStartTime = lastStartTime; } else { throw new Error('Invalid value for the "when" parameter'); } runner.unschedule(); runner.timeline(this); const persist = runner.persist(); const runnerInfo = { persist: persist === null ? this._persist : persist, start: absoluteStartTime + delay, runner }; this._lastRunnerId = runner.id; this._runners.push(runnerInfo); this._runners.sort((a, b) => a.start - b.start); this._runnerIds = this._runners.map((info) => info.runner.id); this.updateTime()._continue(); return this; } seek(dt) { return this.time(this._time + dt); } source(fn) { if (fn == null) return this._timeSource; this._timeSource = fn; return this; } speed(speed) { if (speed == null) return this._speed; this._speed = speed; return this; } stop() { this.time(0); return this.pause(); } time(time) { if (time == null) return this._time; this._time = time; return this._continue(true); } // Remove the runner from this timeline unschedule(runner) { const index = this._runnerIds.indexOf(runner.id); if (index < 0) return this; this._runners.splice(index, 1); this._runnerIds.splice(index, 1); runner.timeline(null); return this; } // Makes sure, that after pausing the time doesn't jump updateTime() { if (!this.active()) { this._lastSourceTime = this._timeSource(); } return this; } // Checks if we are running and continues the animation _continue(immediateStep = false) { Animator.cancelFrame(this._nextFrame); this._nextFrame = null; if (immediateStep) return this._stepImmediate(); if (this._paused) return this; this._nextFrame = Animator.frame(this._step); return this; } _stepFn(immediateStep = false) { const time = this._timeSource(); let dtSource = time - this._lastSourceTime; if (immediateStep) dtSource = 0; const dtTime = this._speed * dtSource + (this._time - this._lastStepTime); this._lastSourceTime = time; if (!immediateStep) { this._time += dtTime; this._time = this._time < 0 ? 0 : this._time; } this._lastStepTime = this._time; this.fire("time", this._time); for (let k = this._runners.length; k--; ) { const runnerInfo = this._runners[k]; const runner = runnerInfo.runner; const dtToStart = this._time - runnerInfo.start; if (dtToStart <= 0) { runner.reset(); } } let runnersLeft = false; for (let i = 0, len = this._runners.length; i < len; i++) { const runnerInfo = this._runners[i]; const runner = runnerInfo.runner; let dt = dtTime; const dtToStart = this._time - runnerInfo.start; if (dtToStart <= 0) { runnersLeft = true; continue; } else if (dtToStart < dt) { dt = dtToStart; } if (!runner.active()) continue; const finished = runner.step(dt).done; if (!finished) { runnersLeft = true; } else if (runnerInfo.persist !== true) { const endTime = runner.duration() - runner.time() + this._time; if (endTime + runnerInfo.persist < this._time) { runner.unschedule(); --i; --len; } } } if (runnersLeft && !(this._speed < 0 && this._time === 0) || this._runnerIds.length && this._speed < 0 && this._time > 0) { this._continue(); } else { this.pause(); this.fire("finished"); } return this; } } registerMethods({ Element: { timeline: function(timeline2) { if (timeline2 == null) { this._timeline = this._timeline || new Timeline(); return this._timeline; } else { this._timeline = timeline2; return this; } } } }); class Runner extends EventTarget { constructor(options) { super(); this.id = Runner.id++; options = options == null ? timeline.duration : options; options = typeof options === "function" ? new Controller(options) : options; this._element = null; this._timeline = null; this.done = false; this._queue = []; this._duration = typeof options === "number" && options; this._isDeclarative = options instanceof Controller; this._stepper = this._isDeclarative ? options : new Ease(); this._history = {}; this.enabled = true; this._time = 0; this._lastTime = 0; this._reseted = true; this.transforms = new Matrix(); this.transformId = 1; this._haveReversed = false; this._reverse = false; this._loopsDone = 0; this._swing = false; this._wait = 0; this._times = 1; this._frameId = null; this._persist = this._isDeclarative ? true : null; } static sanitise(duration, delay, when) { let times = 1; let swing = false; let wait = 0; duration = duration || timeline.duration; delay = delay || timeline.delay; when = when || "last"; if (typeof duration === "object" && !(duration instanceof Stepper)) { delay = duration.delay || delay; when = duration.when || when; swing = duration.swing || swing; times = duration.times || times; wait = duration.wait || wait; duration = duration.duration || timeline.duration; } return { duration, delay, swing, times, wait, when }; } active(enabled) { if (enabled == null) return this.enabled; this.enabled = enabled; return this; } /* Private Methods =============== Methods that shouldn't be used externally */ addTransform(transform2, index) { this.transforms.lmultiplyO(transform2); return this; } after(fn) { return this.on("finished", fn); } animate(duration, delay, when) { const o = Runner.sanitise(duration, delay, when); const runner = new Runner(o.duration); if (this._timeline) runner.timeline(this._timeline); if (this._element) runner.element(this._element); return runner.loop(o).schedule(o.delay, o.when); } clearTransform() { this.transforms = new Matrix(); return this; } // TODO: Keep track of all transformations so that deletion is faster clearTransformsFromQueue() { if (!this.done || !this._timeline || !this._timeline._runnerIds.includes(this.id)) { this._queue = this._queue.filter((item) => { return !item.isTransform; }); } } delay(delay) { return this.animate(0, delay); } duration() { return this._times * (this._wait + this._duration) - this._wait; } during(fn) { return this.queue(null, fn); } ease(fn) { this._stepper = new Ease(fn); return this; } /* Runner Definitions ================== These methods help us define the runtime behaviour of the Runner or they help us make new runners from the current runner */ element(element2) { if (element2 == null) return this._element; this._element = element2; element2._prepareRunner(); return this; } finish() { return this.step(Infinity); } loop(times, swing, wait) { if (typeof times === "object") { swing = times.swing; wait = times.wait; times = times.times; } this._times = times || Infinity; this._swing = swing || false; this._wait = wait || 0; if (this._times === true) { this._times = Infinity; } return this; } loops(p) { const loopDuration = this._duration + this._wait; if (p == null) { const loopsDone = Math.floor(this._time / loopDuration); const relativeTime = this._time - loopsDone * loopDuration; const position2 = relativeTime / this._duration; return Math.min(loopsDone + position2, this._times); } const whole = Math.floor(p); const partial = p % 1; const time = loopDuration * whole + this._duration * partial; return this.time(time); } persist(dtOrForever) { if (dtOrForever == null) return this._persist; this._persist = dtOrForever; return this; } position(p) { const x2 = this._time; const d = this._duration; const w = this._wait; const t = this._times; const s = this._swing; const r = this._reverse; let position2; if (p == null) { const f = function(x3) { const swinging = s * Math.floor(x3 % (2 * (w + d)) / (w + d)); const backwards = swinging && !r || !swinging && r; const uncliped = Math.pow(-1, backwards) * (x3 % (w + d)) / d + backwards; const clipped = Math.max(Math.min(uncliped, 1), 0); return clipped; }; const endTime = t * (w + d) - w; position2 = x2 <= 0 ? Math.round(f(1e-5)) : x2 < endTime ? f(x2) : Math.round(f(endTime - 1e-5)); return position2; } const loopsDone = Math.floor(this.loops()); const swingForward = s && loopsDone % 2 === 0; const forwards = swingForward && !r || r && swingForward; position2 = loopsDone + (forwards ? p : 1 - p); return this.loops(position2); } progress(p) { if (p == null) { return Math.min(1, this._time / this.duration()); } return this.time(p * this.duration()); } /* Basic Functionality =================== These methods allow us to attach basic functions to the runner directly */ queue(initFn, runFn, retargetFn, isTransform) { this._queue.push({ initialiser: initFn || noop, runner: runFn || noop, retarget: retargetFn, isTransform, initialised: false, finished: false }); const timeline2 = this.timeline(); timeline2 && this.timeline()._continue(); return this; } reset() { if (this._reseted) return this; this.time(0); this._reseted = true; return this; } reverse(reverse) { this._reverse = reverse == null ? !this._reverse : reverse; return this; } schedule(timeline2, delay, when) { if (!(timeline2 instanceof Timeline)) { when = delay; delay = timeline2; timeline2 = this.timeline(); } if (!timeline2) { throw Error("Runner cannot be scheduled without timeline"); } timeline2.schedule(this, delay, when); return this; } step(dt) { if (!this.enabled) return this; dt = dt == null ? 16 : dt; this._time += dt; const position2 = this.position(); const running = this._lastPosition !== position2 && this._time >= 0; this._lastPosition = position2; const duration = this.duration(); const justStarted = this._lastTime <= 0 && this._time > 0; const justFinished = this._lastTime < duration && this._time >= duration; this._lastTime = this._time; if (justStarted) { this.fire("start", this); } const declarative = this._isDeclarative; this.done = !declarative && !justFinished && this._time >= duration; this._reseted = false; let converged = false; if (running || declarative) { this._initialise(running); this.transforms = new Matrix(); converged = this._run(declarative ? dt : position2); this.fire("step", this); } this.done = this.done || converged && declarative; if (justFinished) { this.fire("finished", this); } return this; } /* Runner animation methods ======================== Control how the animation plays */ time(time) { if (time == null) { return this._time; } const dt = time - this._time; this.step(dt); return this; } timeline(timeline2) { if (typeof timeline2 === "undefined") return this._timeline; this._timeline = timeline2; return this; } unschedule() { const timeline2 = this.timeline(); timeline2 && timeline2.unschedule(this); return this; } // Run each initialise function in the runner if required _initialise(running) { if (!running && !this._isDeclarative) return; for (let i = 0, len = this._queue.length; i < len; ++i) { const current = this._queue[i]; const needsIt = this._isDeclarative || !current.initialised && running; running = !current.finished; if (needsIt && running) { current.initialiser.call(this); current.initialised = true; } } } // Save a morpher to the morpher list so that we can retarget it later _rememberMorpher(method, morpher) { this._history[method] = { morpher, caller: this._queue[this._queue.length - 1] }; if (this._isDeclarative) { const timeline2 = this.timeline(); timeline2 && timeline2.play(); } } // Try to set the target for a morpher if the morpher exists, otherwise // Run each run function for the position or dt given _run(positionOrDt) { let allfinished = true; for (let i = 0, len = this._queue.length; i < len; ++i) { const current = this._queue[i]; const converged = current.runner.call(this, positionOrDt); current.finished = current.finished || converged === true; allfinished = allfinished && current.finished; } return allfinished; } // do nothing and return false _tryRetarget(method, target, extra) { if (this._history[method]) { if (!this._history[method].caller.initialised) { const index = this._queue.indexOf(this._history[method].caller); this._queue.splice(index, 1); return false; } if (this._history[method].caller.retarget) { this._history[method].caller.retarget.call(this, target, extra); } else { this._history[method].morpher.to(target); } this._history[method].caller.finished = false; const timeline2 = this.timeline(); timeline2 && timeline2.play(); return true; } return false; } } Runner.id = 0; class FakeRunner { constructor(transforms2 = new Matrix(), id = -1, done = true) { this.transforms = transforms2; this.id = id; this.done = done; } clearTransformsFromQueue() { } } extend([Runner, FakeRunner], { mergeWith(runner) { return new FakeRunner(runner.transforms.lmultiply(this.transforms), runner.id); } }); const lmultiply = (last, curr) => last.lmultiplyO(curr); const getRunnerTransform = (runner) => runner.transforms; function mergeTransforms() { const runners = this._transformationRunners.runners; const netTransform = runners.map(getRunnerTransform).reduce(lmultiply, new Matrix()); this.transform(netTransform); this._transformationRunners.merge(); if (this._transformationRunners.length() === 1) { this._frameId = null; } } class RunnerArray { constructor() { this.runners = []; this.ids = []; } add(runner) { if (this.runners.includes(runner)) return; const id = runner.id + 1; this.runners.push(runner); this.ids.push(id); return this; } clearBefore(id) { const deleteCnt = this.ids.indexOf(id + 1) || 1; this.ids.splice(0, deleteCnt, 0); this.runners.splice(0, deleteCnt, new FakeRunner()).forEach((r) => r.clearTransformsFromQueue()); return this; } edit(id, newRunner) { const index = this.ids.indexOf(id + 1); this.ids.splice(index, 1, id + 1); this.runners.splice(index, 1, newRunner); return this; } getByID(id) { return this.runners[this.ids.indexOf(id + 1)]; } length() { return this.ids.length; } merge() { let lastRunner = null; for (let i = 0; i < this.runners.length; ++i) { const runner = this.runners[i]; const condition = lastRunner && runner.done && lastRunner.done && (!runner._timeline || !runner._timeline._runnerIds.includes(runner.id)) && (!lastRunner._timeline || !lastRunner._timeline._runnerIds.includes(lastRunner.id)); if (condition) { this.remove(runner.id); const newRunner = runner.mergeWith(lastRunner); this.edit(lastRunner.id, newRunner); lastRunner = newRunner; --i; } else { lastRunner = runner; } } return this; } remove(id) { const index = this.ids.indexOf(id + 1); this.ids.splice(index, 1); this.runners.splice(index, 1); return this; } } registerMethods({ Element: { animate(duration, delay, when) { const o = Runner.sanitise(duration, delay, when); const timeline2 = this.timeline(); return new Runner(o.duration).loop(o).element(this).timeline(timeline2.play()).schedule(o.delay, o.when); }, delay(by, when) { return this.animate(0, by, when); }, // this function searches for all runners on the element and deletes the ones // which run before the current one. This is because absolute transformations // overwrite anything anyway so there is no need to waste time computing // other runners _clearTransformRunnersBefore(currentRunner) { this._transformationRunners.clearBefore(currentRunner.id); }, _currentTransform(current) { return this._transformationRunners.runners.filter((runner) => runner.id <= current.id).map(getRunnerTransform).reduce(lmultiply, new Matrix()); }, _addRunner(runner) { this._transformationRunners.add(runner); Animator.cancelImmediate(this._frameId); this._frameId = Animator.immediate(mergeTransforms.bind(this)); }, _prepareRunner() { if (this._frameId == null) { this._transformationRunners = new RunnerArray().add(new FakeRunner(new Matrix(this))); } } } }); const difference = (a, b) => a.filter((x2) => !b.includes(x2)); extend(Runner, { attr(a, v) { return this.styleAttr("attr", a, v); }, // Add animatable styles css(s, v) { return this.styleAttr("css", s, v); }, styleAttr(type, nameOrAttrs, val) { if (typeof nameOrAttrs === "string") { return this.styleAttr(type, { [nameOrAttrs]: val }); } let attrs2 = nameOrAttrs; if (this._tryRetarget(type, attrs2)) return this; let morpher = new Morphable(this._stepper).to(attrs2); let keys = Object.keys(attrs2); this.queue(function() { morpher = morpher.from(this.element()[type](keys)); }, function(pos) { this.element()[type](morpher.at(pos).valueOf()); return morpher.done(); }, function(newToAttrs) { const newKeys = Object.keys(newToAttrs); const differences = difference(newKeys, keys); if (differences.length) { const addedFromAttrs = this.element()[type](differences); const oldFromAttrs = new ObjectBag(morpher.from()).valueOf(); Object.assign(oldFromAttrs, addedFromAttrs); morpher.from(oldFromAttrs); } const oldToAttrs = new ObjectBag(morpher.to()).valueOf(); Object.assign(oldToAttrs, newToAttrs); morpher.to(oldToAttrs); keys = newKeys; attrs2 = newToAttrs; }); this._rememberMorpher(type, morpher); return this; }, zoom(level, point2) { if (this._tryRetarget("zoom", level, point2)) return this; let morpher = new Morphable(this._stepper).to(new SVGNumber(level)); this.queue(function() { morpher = morpher.from(this.element().zoom()); }, function(pos) { this.element().zoom(morpher.at(pos), point2); return morpher.done(); }, function(newLevel, newPoint) { point2 = newPoint; morpher.to(newLevel); }); this._rememberMorpher("zoom", morpher); return this; }, /** ** absolute transformations **/ // // M v -----|-----(D M v = F v)------|-----> T v // // 1. define the final state (T) and decompose it (once) // t = [tx, ty, the, lam, sy, sx] // 2. on every frame: pull the current state of all previous transforms // (M - m can change) // and then write this as m = [tx0, ty0, the0, lam0, sy0, sx0] // 3. Find the interpolated matrix F(pos) = m + pos * (t - m) // - Note F(0) = M // - Note F(1) = T // 4. Now you get the delta matrix as a result: D = F * inv(M) transform(transforms2, relative, affine) { relative = transforms2.relative || relative; if (this._isDeclarative && !relative && this._tryRetarget("transform", transforms2)) { return this; } const isMatrix = Matrix.isMatrixLike(transforms2); affine = transforms2.affine != null ? transforms2.affine : affine != null ? affine : !isMatrix; const morpher = new Morphable(this._stepper).type(affine ? TransformBag : Matrix); let origin; let element2; let current; let currentAngle; let startTransform; function setup() { element2 = element2 || this.element(); origin = origin || getOrigin(transforms2, element2); startTransform = new Matrix(relative ? void 0 : element2); element2._addRunner(this); if (!relative) { element2._clearTransformRunnersBefore(this); } } function run(pos) { if (!relative) this.clearTransform(); const { x: x2, y: y2 } = new Point(origin).transform(element2._currentTransform(this)); let target = new Matrix({ ...transforms2, origin: [x2, y2] }); let start = this._isDeclarative && current ? current : startTransform; if (affine) { target = target.decompose(x2, y2); start = start.decompose(x2, y2); const rTarget = target.rotate; const rCurrent = start.rotate; const possibilities = [rTarget - 360, rTarget, rTarget + 360]; const distances = possibilities.map((a) => Math.abs(a - rCurrent)); const shortest = Math.min(...distances); const index = distances.indexOf(shortest); target.rotate = possibilities[index]; } if (relative) { if (!isMatrix) { target.rotate = transforms2.rotate || 0; } if (this._isDeclarative && currentAngle) { start.rotate = currentAngle; } } morpher.from(start); morpher.to(target); const affineParameters = morpher.at(pos); currentAngle = affineParameters.rotate; current = new Matrix(affineParameters); this.addTransform(current); element2._addRunner(this); return morpher.done(); } function retarget(newTransforms) { if ((newTransforms.origin || "center").toString() !== (transforms2.origin || "center").toString()) { origin = getOrigin(newTransforms, element2); } transforms2 = { ...newTransforms, origin }; } this.queue(setup, run, retarget, true); this._isDeclarative && this._rememberMorpher("transform", morpher); return this; }, // Animatable x-axis x(x2, relative) { return this._queueNumber("x", x2); }, // Animatable y-axis y(y2) { return this._queueNumber("y", y2); }, dx(x2 = 0) { return this._queueNumberDelta("x", x2); }, dy(y2 = 0) { return this._queueNumberDelta("y", y2); }, dmove(x2, y2) { return this.dx(x2).dy(y2); }, _queueNumberDelta(method, to2) { to2 = new SVGNumber(to2); if (this._tryRetarget(method, to2)) return this; const morpher = new Morphable(this._stepper).to(to2); let from2 = null; this.queue(function() { from2 = this.element()[method](); morpher.from(from2); morpher.to(from2 + to2); }, function(pos) { this.element()[method](morpher.at(pos)); return morpher.done(); }, function(newTo) { morpher.to(from2 + new SVGNumber(newTo)); }); this._rememberMorpher(method, morpher); return this; }, _queueObject(method, to2) { if (this._tryRetarget(method, to2)) return this; const morpher = new Morphable(this._stepper).to(to2); this.queue(function() { morpher.from(this.element()[method]()); }, function(pos) { this.element()[method](morpher.at(pos)); return morpher.done(); }); this._rememberMorpher(method, morpher); return this; }, _queueNumber(method, value) { return this._queueObject(method, new SVGNumber(value)); }, // Animatable center x-axis cx(x2) { return this._queueNumber("cx", x2); }, // Animatable center y-axis cy(y2) { return this._queueNumber("cy", y2); }, // Add animatable move move(x2, y2) { return this.x(x2).y(y2); }, // Add animatable center center(x2, y2) { return this.cx(x2).cy(y2); }, // Add animatable size size(width2, height2) { let box; if (!width2 || !height2) { box = this._element.bbox(); } if (!width2) { width2 = box.width / box.height * height2; } if (!height2) { height2 = box.height / box.width * width2; } return this.width(width2).height(height2); }, // Add animatable width width(width2) { return this._queueNumber("width", width2); }, // Add animatable height height(height2) { return this._queueNumber("height", height2); }, // Add animatable plot plot(a, b, c, d) { if (arguments.length === 4) { return this.plot([a, b, c, d]); } if (this._tryRetarget("plot", a)) return this; const morpher = new Morphable(this._stepper).type(this._element.MorphArray).to(a); this.queue(function() { morpher.from(this._element.array()); }, function(pos) { this._element.plot(morpher.at(pos)); return morpher.done(); }); this._rememberMorpher("plot", morpher); return this; }, // Add leading method leading(value) { return this._queueNumber("leading", value); }, // Add animatable viewbox viewbox(x2, y2, width2, height2) { return this._queueObject("viewbox", new Box(x2, y2, width2, height2)); }, update(o) { if (typeof o !== "object") { return this.update({ offset: arguments[0], color: arguments[1], opacity: arguments[2] }); } if (o.opacity != null) this.attr("stop-opacity", o.opacity); if (o.color != null) this.attr("stop-color", o.color); if (o.offset != null) this.attr("offset", o.offset); return this; } }); extend(Runner, { rx, ry, from, to }); register(Runner, "Runner"); class Svg extends Container { constructor(node, attrs2 = node) { super(nodeOrNew("svg", node), attrs2); this.namespace(); } // Creates and returns defs element defs() { if (!this.isRoot()) return this.root().defs(); return adopt(this.node.querySelector("defs")) || this.put(new Defs()); } isRoot() { return !this.node.parentNode || !(this.node.parentNode instanceof globals.window.SVGElement) && this.node.parentNode.nodeName !== "#document-fragment"; } // Add namespaces namespace() { if (!this.isRoot()) return this.root().namespace(); return this.attr({ xmlns: svg, version: "1.1" }).attr("xmlns:xlink", xlink, xmlns).attr("xmlns:svgjs", svgjs, xmlns); } removeNamespace() { return this.attr({ xmlns: null, version: null }).attr("xmlns:xlink", null, xmlns).attr("xmlns:svgjs", null, xmlns); } // Check if this is a root svg // If not, call root() from this element root() { if (this.isRoot()) return this; return super.root(); } } registerMethods({ Container: { // Create nested svg document nested: wrapWithAttrCheck(function() { return this.put(new Svg()); }) } }); register(Svg, "Svg", true); let Symbol$1 = class Symbol2 extends Container { // Initialize node constructor(node, attrs2 = node) { super(nodeOrNew("symbol", node), attrs2); } }; registerMethods({ Container: { symbol: wrapWithAttrCheck(function() { return this.put(new Symbol$1()); }) } }); register(Symbol$1, "Symbol"); function plain(text) { if (this._build === false) { this.clear(); } this.node.appendChild(globals.document.createTextNode(text)); return this; } function length() { return this.node.getComputedTextLength(); } function x$1(x2, box = this.bbox()) { if (x2 == null) { return box.x; } return this.attr("x", this.attr("x") + x2 - box.x); } function y$1(y2, box = this.bbox()) { if (y2 == null) { return box.y; } return this.attr("y", this.attr("y") + y2 - box.y); } function move$1(x2, y2, box = this.bbox()) { return this.x(x2, box).y(y2, box); } function cx(x2, box = this.bbox()) { if (x2 == null) { return box.cx; } return this.attr("x", this.attr("x") + x2 - box.cx); } function cy(y2, box = this.bbox()) { if (y2 == null) { return box.cy; } return this.attr("y", this.attr("y") + y2 - box.cy); } function center(x2, y2, box = this.bbox()) { return this.cx(x2, box).cy(y2, box); } function ax(x2) { return this.attr("x", x2); } function ay(y2) { return this.attr("y", y2); } function amove(x2, y2) { return this.ax(x2).ay(y2); } function build(build2) { this._build = !!build2; return this; } var textable = { __proto__: null, plain, length, x: x$1, y: y$1, move: move$1, cx, cy, center, ax, ay, amove, build }; class Text extends Shape { // Initialize node constructor(node, attrs2 = node) { super(nodeOrNew("text", node), attrs2); this.dom.leading = new SVGNumber(1.3); this._rebuild = true; this._build = false; } // Set / get leading leading(value) { if (value == null) { return this.dom.leading; } this.dom.leading = new SVGNumber(value); return this.rebuild(); } // Rebuild appearance type rebuild(rebuild) { if (typeof rebuild === "boolean") { this._rebuild = rebuild; } if (this._rebuild) { const self2 = this; let blankLineOffset = 0; const leading = this.dom.leading; this.each(function(i) { const fontSize = globals.window.getComputedStyle(this.node).getPropertyValue("font-size"); const dy2 = leading * new SVGNumber(fontSize); if (this.dom.newLined) { this.attr("x", self2.attr("x")); if (this.text() === "\n") { blankLineOffset += dy2; } else { this.attr("dy", i ? dy2 + blankLineOffset : 0); blankLineOffset = 0; } } }); this.fire("rebuild"); } return this; } // overwrite method from parent to set data properly setData(o) { this.dom = o; this.dom.leading = new SVGNumber(o.leading || 1.3); return this; } // Set the text content text(text) { if (text === void 0) { const children = this.node.childNodes; let firstLine = 0; text = ""; for (let i = 0, len = children.length; i < len; ++i) { if (children[i].nodeName === "textPath") { if (i === 0) firstLine = 1; continue; } if (i !== firstLine && children[i].nodeType !== 3 && adopt(children[i]).dom.newLined === true) { text += "\n"; } text += children[i].textContent; } return text; } this.clear().build(true); if (typeof text === "function") { text.call(this, this); } else { text = (text + "").split("\n"); for (let j = 0, jl = text.length; j < jl; j++) { this.newLine(text[j]); } } return this.build(false).rebuild(); } } extend(Text, textable); registerMethods({ Container: { // Create text element text: wrapWithAttrCheck(function(text = "") { return this.put(new Text()).text(text); }), // Create plain text element plain: wrapWithAttrCheck(function(text = "") { return this.put(new Text()).plain(text); }) } }); register(Text, "Text"); class Tspan extends Shape { // Initialize node constructor(node, attrs2 = node) { super(nodeOrNew("tspan", node), attrs2); this._build = false; } // Shortcut dx dx(dx2) { return this.attr("dx", dx2); } // Shortcut dy dy(dy2) { return this.attr("dy", dy2); } // Create new line newLine() { this.dom.newLined = true; const text = this.parent(); if (!(text instanceof Text)) { return this; } const i = text.index(this); const fontSize = globals.window.getComputedStyle(this.node).getPropertyValue("font-size"); const dy2 = text.dom.leading * new SVGNumber(fontSize); return this.dy(i ? dy2 : 0).attr("x", text.x()); } // Set text content text(text) { if (text == null) return this.node.textContent + (this.dom.newLined ? "\n" : ""); if (typeof text === "function") { this.clear().build(true); text.call(this, this); this.build(false); } else { this.plain(text); } return this; } } extend(Tspan, textable); registerMethods({ Tspan: { tspan: wrapWithAttrCheck(function(text = "") { const tspan = new Tspan(); if (!this._build) { this.clear(); } return this.put(tspan).text(text); }) }, Text: { newLine: function(text = "") { return this.tspan(text).newLine(); } } }); register(Tspan, "Tspan"); class Circle extends Shape { constructor(node, attrs2 = node) { super(nodeOrNew("circle", node), attrs2); } radius(r) { return this.attr("r", r); } // Radius x value rx(rx2) { return this.attr("r", rx2); } // Alias radius x value ry(ry2) { return this.rx(ry2); } size(size2) { return this.radius(new SVGNumber(size2).divide(2)); } } extend(Circle, { x: x$3, y: y$3, cx: cx$1, cy: cy$1, width: width$2, height: height$2 }); registerMethods({ Container: { // Create circle element circle: wrapWithAttrCheck(function(size2 = 0) { return this.put(new Circle()).size(size2).move(0, 0); }) } }); register(Circle, "Circle"); class ClipPath extends Container { constructor(node, attrs2 = node) { super(nodeOrNew("clipPath", node), attrs2); } // Unclip all clipped elements and remove itself remove() { this.targets().forEach(function(el) { el.unclip(); }); return super.remove(); } targets() { return baseFind("svg [clip-path*=" + this.id() + "]"); } } registerMethods({ Container: { // Create clipping element clip: wrapWithAttrCheck(function() { return this.defs().put(new ClipPath()); }) }, Element: { // Distribute clipPath to svg element clipper() { return this.reference("clip-path"); }, clipWith(element2) { const clipper = element2 instanceof ClipPath ? element2 : this.parent().clip().add(element2); return this.attr("clip-path", "url(#" + clipper.id() + ")"); }, // Unclip element unclip() { return this.attr("clip-path", null); } } }); register(ClipPath, "ClipPath"); class ForeignObject extends Element { constructor(node, attrs2 = node) { super(nodeOrNew("foreignObject", node), attrs2); } } registerMethods({ Container: { foreignObject: wrapWithAttrCheck(function(width2, height2) { return this.put(new ForeignObject()).size(width2, height2); }) } }); register(ForeignObject, "ForeignObject"); function dmove(dx2, dy2) { this.children().forEach((child, i) => { let bbox2; try { bbox2 = child.bbox(); } catch (e) { return; } const m = new Matrix(child); const matrix = m.translate(dx2, dy2).transform(m.inverse()); const p = new Point(bbox2.x, bbox2.y).transform(matrix); child.move(p.x, p.y); }); return this; } function dx(dx2) { return this.dmove(dx2, 0); } function dy(dy2) { return this.dmove(0, dy2); } function height(height2, box = this.bbox()) { if (height2 == null) return box.height; return this.size(box.width, height2, box); } function move(x2 = 0, y2 = 0, box = this.bbox()) { const dx2 = x2 - box.x; const dy2 = y2 - box.y; return this.dmove(dx2, dy2); } function size(width2, height2, box = this.bbox()) { const p = proportionalSize(this, width2, height2, box); const scaleX = p.width / box.width; const scaleY = p.height / box.height; this.children().forEach((child, i) => { const o = new Point(box).transform(new Matrix(child).inverse()); child.scale(scaleX, scaleY, o.x, o.y); }); return this; } function width(width2, box = this.bbox()) { if (width2 == null) return box.width; return this.size(width2, box.height, box); } function x(x2, box = this.bbox()) { if (x2 == null) return box.x; return this.move(x2, box.y, box); } function y(y2, box = this.bbox()) { if (y2 == null) return box.y; return this.move(box.x, y2, box); } var containerGeometry = { __proto__: null, dmove, dx, dy, height, move, size, width, x, y }; class G extends Container { constructor(node, attrs2 = node) { super(nodeOrNew("g", node), attrs2); } } extend(G, containerGeometry); registerMethods({ Container: { // Create a group element group: wrapWithAttrCheck(function() { return this.put(new G()); }) } }); register(G, "G"); class A extends Container { constructor(node, attrs2 = node) { super(nodeOrNew("a", node), attrs2); } // Link target attribute target(target) { return this.attr("target", target); } // Link url to(url) { return this.attr("href", url, xlink); } } extend(A, containerGeometry); registerMethods({ Container: { // Create a hyperlink element link: wrapWithAttrCheck(function(url) { return this.put(new A()).to(url); }) }, Element: { unlink() { const link = this.linker(); if (!link) return this; const parent = link.parent(); if (!parent) { return this.remove(); } const index = parent.index(link); parent.add(this, index); link.remove(); return this; }, linkTo(url) { let link = this.linker(); if (!link) { link = new A(); this.wrap(link); } if (typeof url === "function") { url.call(link, link); } else { link.to(url); } return this; }, linker() { const link = this.parent(); if (link && link.node.nodeName.toLowerCase() === "a") { return link; } return null; } } }); register(A, "A"); class Mask extends Container { // Initialize node constructor(node, attrs2 = node) { super(nodeOrNew("mask", node), attrs2); } // Unmask all masked elements and remove itself remove() { this.targets().forEach(function(el) { el.unmask(); }); return super.remove(); } targets() { return baseFind("svg [mask*=" + this.id() + "]"); } } registerMethods({ Container: { mask: wrapWithAttrCheck(function() { return this.defs().put(new Mask()); }) }, Element: { // Distribute mask to svg element masker() { return this.reference("mask"); }, maskWith(element2) { const masker = element2 instanceof Mask ? element2 : this.parent().mask().add(element2); return this.attr("mask", "url(#" + masker.id() + ")"); }, // Unmask element unmask() { return this.attr("mask", null); } } }); register(Mask, "Mask"); class Stop extends Element { constructor(node, attrs2 = node) { super(nodeOrNew("stop", node), attrs2); } // add color stops update(o) { if (typeof o === "number" || o instanceof SVGNumber) { o = { offset: arguments[0], color: arguments[1], opacity: arguments[2] }; } if (o.opacity != null) this.attr("stop-opacity", o.opacity); if (o.color != null) this.attr("stop-color", o.color); if (o.offset != null) this.attr("offset", new SVGNumber(o.offset)); return this; } } registerMethods({ Gradient: { // Add a color stop stop: function(offset, color, opacity) { return this.put(new Stop()).update(offset, color, opacity); } } }); register(Stop, "Stop"); function cssRule(selector, rule) { if (!selector) return ""; if (!rule) return selector; let ret = selector + "{"; for (const i in rule) { ret += unCamelCase(i) + ":" + rule[i] + ";"; } ret += "}"; return ret; } class Style extends Element { constructor(node, attrs2 = node) { super(nodeOrNew("style", node), attrs2); } addText(w = "") { this.node.textContent += w; return this; } font(name, src, params = {}) { return this.rule("@font-face", { fontFamily: name, src, ...params }); } rule(selector, obj) { return this.addText(cssRule(selector, obj)); } } registerMethods("Dom", { style(selector, obj) { return this.put(new Style()).rule(selector, obj); }, fontface(name, src, params) { return this.put(new Style()).font(name, src, params); } }); register(Style, "Style"); class TextPath extends Text { // Initialize node constructor(node, attrs2 = node) { super(nodeOrNew("textPath", node), attrs2); } // return the array of the path track element array() { const track = this.track(); return track ? track.array() : null; } // Plot path if any plot(d) { const track = this.track(); let pathArray = null; if (track) { pathArray = track.plot(d); } return d == null ? pathArray : this; } // Get the path element track() { return this.reference("href"); } } registerMethods({ Container: { textPath: wrapWithAttrCheck(function(text, path) { if (!(text instanceof Text)) { text = this.text(text); } return text.path(path); }) }, Text: { // Create path for text to run on path: wrapWithAttrCheck(function(track, importNodes = true) { const textPath = new TextPath(); if (!(track instanceof Path)) { track = this.defs().path(track); } textPath.attr("href", "#" + track, xlink); let node; if (importNodes) { while (node = this.node.firstChild) { textPath.node.appendChild(node); } } return this.put(textPath); }), // Get the textPath children textPath() { return this.findOne("textPath"); } }, Path: { // creates a textPath from this path text: wrapWithAttrCheck(function(text) { if (!(text instanceof Text)) { text = new Text().addTo(this.parent()).text(text); } return text.path(this); }), targets() { return baseFind("svg textPath").filter((node) => { return (node.attr("href") || "").includes(this.id()); }); } } }); TextPath.prototype.MorphArray = PathArray; register(TextPath, "TextPath"); class Use extends Shape { constructor(node, attrs2 = node) { super(nodeOrNew("use", node), attrs2); } // Use element as a reference use(element2, file) { return this.attr("href", (file || "") + "#" + element2, xlink); } } registerMethods({ Container: { // Create a use element use: wrapWithAttrCheck(function(element2, file) { return this.put(new Use()).use(element2, file); }) } }); register(Use, "Use"); const SVG = makeInstance; extend([Svg, Symbol$1, Image, Pattern, Marker], getMethodsFor("viewbox")); extend([Line, Polyline, Polygon, Path], getMethodsFor("marker")); extend(Text, getMethodsFor("Text")); extend(Path, getMethodsFor("Path")); extend(Defs, getMethodsFor("Defs")); extend([Text, Tspan], getMethodsFor("Tspan")); extend([Rect, Ellipse, Gradient, Runner], getMethodsFor("radius")); extend(EventTarget, getMethodsFor("EventTarget")); extend(Dom, getMethodsFor("Dom")); extend(Element, getMethodsFor("Element")); extend(Shape, getMethodsFor("Shape")); extend([Container, Fragment], getMethodsFor("Container")); extend(Gradient, getMethodsFor("Gradient")); extend(Runner, getMethodsFor("Runner")); List.extend(getMethodNames()); registerMorphableType([SVGNumber, Color, Box, Matrix, SVGArray, PointArray, PathArray, Point]); makeMorphable(); class ChordBox { // sel can be a selector or an element. constructor(sel, params) { this.sel = sel; this.params = { ...{ numStrings: 6, numFrets: 5, x: 0, y: 0, width: 100, height: 120, strokeWidth: 1, showTuning: true, defaultColor: "#666", bgColor: "#fff", labelColor: "#fff", fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"', fontSize: void 0, fontStyle: "light", fontWeight: "100", labelWeight: "100" }, ...params }; ["bridgeColor", "stringColor", "fretColor", "strokeColor", "textColor"].forEach((param) => { this.params[param] = this.params[param] || this.params.defaultColor; }); ["stringWidth", "fretWidth"].forEach((param) => { this.params[param] = this.params[param] || this.params.strokeWidth; }); this.canvas = SVG().addTo(sel).size(this.params.width, this.params.height); this.width = this.params.width * 0.75; this.height = this.params.height * 0.75; this.x = this.params.x + this.params.width * 0.15; this.y = this.params.y + this.params.height * 0.15; this.numStrings = this.params.numStrings; this.numFrets = this.params.numFrets; this.spacing = this.width / this.numStrings; this.fretSpacing = this.height / (this.numFrets + 2); this.x += this.spacing / 2; this.y += this.fretSpacing; this.metrics = { circleRadius: this.width / 20, barreRadius: this.width / 25, fontSize: this.params.fontSize || Math.ceil(this.width / 8), barShiftX: this.width / 28, bridgeStrokeWidth: Math.ceil(this.height / 36) }; this.position = 0; this.positionText = 0; this.chord = []; this.bars = []; this.tuning = ["E", "A", "D", "G", "B", "E"]; } setNumFrets(numFrets) { this.numFrets = numFrets; this.fretSpacing = this.height / (this.numFrets + 1); return this; } setPositionText(position2) { this.positionText = position2; return this; } drawText(x2, y2, msg, attrs2) { const textAttrs = { ...{ family: this.params.fontFamily, size: this.metrics.fontSize, style: this.params.fontStyle, weight: this.params.fontWeight }, ...attrs2 }; const text = this.canvas.text(`${msg}`).stroke(this.params.textColor).fill(this.params.textColor).font(textAttrs); return text.move(x2 - text.length() / 2, y2); } drawLine(x2, y2, newX, newY) { return this.canvas.line(0, 0, newX - x2, newY - y2).move(x2, y2); } draw({ chord, position: position2, barres, positionText, tuning }) { this.chord = chord; this.position = position2 || 0; this.positionText = positionText || 0; this.barres = barres || []; this.tuning = tuning || ["E", "A", "D", "G", "B", "E"]; if (this.tuning.length === 0) { this.fretSpacing = this.height / (this.numFrets + 1); } const { spacing } = this; const { fretSpacing } = this; if (this.position <= 1) { const fromX = this.x; const fromY = this.y - this.metrics.bridgeStrokeWidth; this.canvas.rect(this.x + spacing * (this.numStrings - 1) - fromX, this.y - fromY).move(fromX, fromY).stroke({ width: 0 }).fill(this.params.bridgeColor); } else { this.drawText(this.x - this.spacing / 2 - this.spacing * 0.1, this.y + this.fretSpacing * this.positionText, this.position); } for (let i = 0; i < this.numStrings; i += 1) { this.drawLine(this.x + spacing * i, this.y, this.x + spacing * i, this.y + fretSpacing * this.numFrets).stroke({ width: this.params.stringWidth, color: this.params.stringColor }); } for (let i = 0; i < this.numFrets + 1; i += 1) { this.drawLine(this.x, this.y + fretSpacing * i, this.x + spacing * (this.numStrings - 1), this.y + fretSpacing * i).stroke({ width: this.params.fretWidth, color: this.params.fretColor }); } if (this.params.showTuning && this.tuning.length !== 0) { for (let i = 0; i < Math.min(this.numStrings, this.tuning.length); i += 1) { this.drawText(this.x + this.spacing * i, this.y + this.numFrets * this.fretSpacing + this.fretSpacing / 12, this.tuning[i]); } } for (let i = 0; i < this.chord.length; i += 1) { this.lightUp({ string: this.chord[i][0], fret: this.chord[i][1], label: this.chord.length > 2 ? this.chord[i][2] : void 0 }); } for (let i = 0; i < this.barres.length; i += 1) { this.lightBar(this.barres[i].fromString, this.barres[i].toString, this.barres[i].fret); } } lightUp({ string: string2, fret, label }) { const stringNum = this.numStrings - string2; const shiftPosition = this.position === 1 && this.positionText === 1 ? this.positionText : 0; const mute = fret === "x"; const fretNum = fret === "x" ? 0 : fret - shiftPosition; const x2 = this.x + this.spacing * stringNum; let y2 = this.y + this.fretSpacing * fretNum; if (fretNum === 0) { y2 -= this.metrics.bridgeStrokeWidth; } if (!mute) { this.canvas.circle().move(x2, y2 - this.fretSpacing / 2).radius(this.metrics.circleRadius).stroke({ color: this.params.strokeColor, width: this.params.strokeWidth }).fill(fretNum > 0 ? this.params.strokeColor : this.params.bgColor); } else { this.drawText(x2, y2 - this.fretSpacing, "X"); } if (label) { const fontSize = this.metrics.fontSize * 0.55; const textYShift = fontSize * 0.66; this.drawText(x2, y2 - this.fretSpacing / 2 - textYShift, label, { weight: this.params.labelWeight, size: fontSize }).stroke({ width: 0.7, color: fretNum !== 0 ? this.params.labelColor : this.params.strokeColor }).fill(fretNum !== 0 ? this.params.labelColor : this.params.strokeColor); } return this; } lightBar(stringFrom, stringTo, theFretNum) { let fretNum = theFretNum; if (this.position === 1 && this.positionText === 1) { fretNum -= this.positionText; } const stringFromNum = this.numStrings - stringFrom; const stringToNum = this.numStrings - stringTo; const x2 = this.x + this.spacing * stringFromNum - this.metrics.barShiftX; const xTo = this.x + this.spacing * stringToNum + this.metrics.barShiftX; const y2 = this.y + this.fretSpacing * (fretNum - 1) + this.fretSpacing / 4; const yTo = this.y + this.fretSpacing * (fretNum - 1) + this.fretSpacing / 4 * 3; this.canvas.rect(xTo - x2, yTo - y2).move(x2, y2).radius(this.metrics.barreRadius).fill(this.params.strokeColor); return this; } } const _hoisted_1$a = { key: 0, class: "chord-container" }; const _hoisted_2$9 = { class: "chord-name" }; const _hoisted_3$4 = ["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 width2 = chordRef.value.clientWidth; const chordBoxSelector = `.chord-chart[chord-name="${props.chord}"]`; const chordBox = new ChordBox(chordBoxSelector, { width: width2, height: width2 * 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$4) ])) : vue.createCommentVNode("", true); }; } }; const ChordChart = /* @__PURE__ */ _export_sfc(_sfc_main$b, [["__scopeId", "data-v-1eb5094e"]]); const _withScopeId$1 = (n) => (vue.pushScopeId("data-v-84de48ae"), n = n(), vue.popScopeId(), n); const _hoisted_1$9 = { id: "plus91-chord-popup" }; const _hoisted_2$8 = { class: "banner" }; const _hoisted_3$3 = /* @__PURE__ */ _withScopeId$1(() => /* @__PURE__ */ vue.createElementVNode("section", null, "此處的和弦圖示僅供參考,尤其把位部分較常出現錯誤,請注意。", -1)); const _hoisted_4$2 = { class: "chord-popup-container" }; const _sfc_main$a = { __name: "ChordPopup", setup(__props) { const store = useStore(); const chordList = vue.ref([]); vue.watch(store.isPopupShow, () => { if (!store.isPopupShow.chord) { return; } 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", _hoisted_1$9, [ vue.createElementVNode("div", _hoisted_2$8, [ vue.createVNode(BootstrapIcon, { icon: "info-circle-fill", color: "inherit", size: "inherit" }), _hoisted_3$3 ]), vue.createElementVNode("div", _hoisted_4$2, [ (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)) ]) ], 512), [ [vue.vShow, vue.unref(store).isPopupShow.chord] ]) ]), _: 1 }); }; } }; const ChordPopup = /* @__PURE__ */ _export_sfc(_sfc_main$a, [["__scopeId", "data-v-84de48ae"]]); 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 _sfc_main$6 = { __name: "MenuPopup", setup(__props) { const store = useStore(); 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: "#555", onClick: _cache[0] || (_cache[0] = () => { vue.unref(store).togglePopup("hotkey"); }) }), vue.createVNode(MenuButton, { icon: "youtube", name: "搜尋 YouTube", color: "#555", onClick: searchOnYoutube }), vue.createVNode(MenuButton, { icon: "github", name: "關於 91 Plus", color: "#555", onClick: goToGithubPage }) ]) ], 512), [ [vue.vShow, vue.unref(store).isPopupShow.menu] ]) ]), _: 1 }); }; } }; const MenuPopup = /* @__PURE__ */ _export_sfc(_sfc_main$6, [["__scopeId", "data-v-d041b049"]]); 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", null, vue.toDisplayString(key), 1); }), 256)) ])) : (vue.openBlock(), vue.createElementBlock("div", _hoisted_3$1)) ]); }; } }; const HotkeyItem = /* @__PURE__ */ _export_sfc(_sfc_main$5, [["__scopeId", "data-v-9f1c2e95"]]); 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) => { return vue.openBlock(), vue.createBlock(HotkeyItem, { hotkey: item.hotkey, desc: item.desc }, null, 8, ["hotkey", "desc"]); }), 256)) ]), vue.createElementVNode("section", _hoisted_4, [ (vue.openBlock(true), vue.createElementBlock(vue.Fragment, null, vue.renderList(vue.unref(hotkeyData).hotkeysRight, (item) => { return vue.openBlock(), vue.createBlock(HotkeyItem, { hotkey: item.hotkey, desc: item.desc }, null, 8, ["hotkey", "desc"]); }), 256)) ]) ]) ], 512), [ [vue.vShow, vue.unref(store).isPopupShow.hotkey] ]) ]), _: 1 }); }; } }; const HotkeyPopup = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["__scopeId", "data-v-a88e7568"]]); 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-beamed", 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", 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", 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", 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", 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 Footer = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["__scopeId", "data-v-a29224c8"]]); 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: "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 Header = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-265bcfd2"]]); 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-e9c78cc3"]]); 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); }; } }; 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);