refactor: migrate app to Alpine.js

This commit is contained in:
abdus committed 2026-10-10 11:07:10 +00:00
1 parent 766212f119
commit bc236db952
9 files changed
+243 -190

No files matched your search

+111 -121
View File
@@ -1,113 +1,12 @@
import Alpine from './vendor/alpine.js';
import {render, styles, resolveFont} from './renderer.js';
import {translations} from './translations.js';
import {setupPWA} from './pwa.js';
const text = document.getElementById('text');
const style = document.getElementById('style');
const output = document.getElementById('output');
const status = document.getElementById('status');
const language = document.getElementById('language');
const theme = document.getElementById('theme');
let lang = document.documentElement.lang;
let statusKey = '';
const t = key => translations[lang][key];
const showStatus = key => {
statusKey = key;
status.textContent = key ? t(key) : '';
};
const savePreference = (key, value) => {
function savePreference(key, value) {
try { localStorage.setItem(`ascii-pwa-${key}`, value); } catch { /* Storage may be disabled. */ }
};
function translate() {
document.documentElement.lang = lang;
language.value = lang;
document.querySelectorAll('[data-i18n]').forEach(element => {
element.textContent = t(element.dataset.i18n);
});
output.setAttribute('aria-label', t('outputLabel'));
document.querySelector('.logo').setAttribute('aria-label', t('homeLabel'));
updateFontNavigation();
showStatus(statusKey);
}
let drawVersion = 0;
let drawTimer;
function updateFontNavigation() {
const next = styles[(styles.indexOf(style.value) + 1) % styles.length];
document.getElementById('next-font').textContent = t('nextFont').replace('{name}', next);
}
async function draw() {
const version = ++drawVersion;
const copy = document.getElementById('copy');
const download = document.getElementById('download');
copy.disabled = true;
download.disabled = true;
try {
const result = await render(text.value, style.value);
if (version !== drawVersion) return;
output.textContent = result;
showStatus('');
} catch {
if (version !== drawVersion) return;
output.textContent = '';
showStatus('renderFailed');
}
document.getElementById('copy').disabled = !output.textContent.trim();
document.getElementById('download').disabled = !output.textContent.trim();
}
// Explicit shared URLs take precedence; a normal visit always opens the homepage.
const params = new URL(window.location.href).searchParams;
style.replaceChildren(...styles.map(font => {
const option = document.createElement('option');
option.value = font;
option.textContent = font;
return option;
}));
style.value = 'Standard';
try {
const savedFont = localStorage.getItem('ascii-pwa-font');
if (savedFont) style.value = resolveFont(savedFont);
} catch { /* Use the default when saved preferences are unavailable. */ }
if (params.has('text')) text.value = params.get('text').slice(0, 500);
if (params.has('font')) style.value = resolveFont(params.get('font'));
savePreference('font', style.value);
theme.value = document.documentElement.dataset.theme;
translate();
draw();
document.getElementById('render-form').addEventListener('submit', event => {
event.preventDefault();
draw();
});
style.addEventListener('change', () => {
clearTimeout(drawTimer);
savePreference('font', style.value);
updateFontNavigation();
draw();
});
text.addEventListener('input', () => {
clearTimeout(drawTimer);
++drawVersion;
document.getElementById('copy').disabled = true;
document.getElementById('download').disabled = true;
drawTimer = setTimeout(draw, 150);
});
for (const [id, direction] of [['prev-font', -1], ['next-font', 1]]) {
document.getElementById(id).addEventListener('click', () => {
const index = (styles.indexOf(style.value) + direction + styles.length) % styles.length;
style.value = styles[index];
style.dispatchEvent(new Event('change'));
});
}
language.addEventListener('change', () => {
lang = language.value;
savePreference('language', lang);
translate();
});
theme.addEventListener('change', () => {
document.documentElement.dataset.theme = theme.value;
savePreference('theme', theme.value);
});
function copyText(value) {
const field = document.createElement('textarea');
const focused = document.activeElement;
@@ -126,20 +25,109 @@ function copyText(value) {
}
}
document.getElementById('copy').addEventListener('click', () => {
Alpine.data('asciiApp', () => {
let drawVersion = 0;
let drawTimer;
let pwa;
return {
styles,
text: 'hello world',
font: 'Standard',
ascii: '',
busy: true,
statusKey: '',
language: document.documentElement.lang,
theme: document.documentElement.dataset.theme,
installAvailable: false,
updateAvailable: false,
installed: false,
get nextFont() {
return styles[(styles.indexOf(this.font) + 1) % styles.length];
},
get canCopy() {
return !this.busy && !!this.ascii.trim();
},
t(key) {
return translations[this.language][key];
},
init() {
try {
copyText(output.textContent);
showStatus('copied');
const saved = localStorage.getItem('ascii-pwa-font');
if (saved) this.font = resolveFont(saved);
} catch { /* Use the default when saved preferences are unavailable. */ }
const params = new URL(window.location.href).searchParams;
if (params.has('text')) this.text = params.get('text').slice(0, 500);
if (params.has('font')) this.font = resolveFont(params.get('font'));
savePreference('font', this.font);
this.$watch('text', () => this.scheduleDraw());
this.$watch('font', () => {
savePreference('font', this.font);
this.draw();
});
this.$watch('language', () => {
document.documentElement.lang = this.language;
savePreference('language', this.language);
});
this.$watch('theme', () => {
document.documentElement.dataset.theme = this.theme;
savePreference('theme', this.theme);
});
pwa = setupPWA({
installAvailable: value => { this.installAvailable = value; },
updateAvailable: value => { this.updateAvailable = value; },
installed: value => { this.installed = value; },
failed: () => { this.statusKey = 'offlineFailed'; },
});
this.draw();
},
destroy() {
clearTimeout(drawTimer);
++drawVersion;
pwa?.destroy();
},
scheduleDraw() {
clearTimeout(drawTimer);
++drawVersion;
this.busy = true;
drawTimer = setTimeout(() => this.draw(), 150);
},
async draw() {
clearTimeout(drawTimer);
const version = ++drawVersion;
this.busy = true;
try {
const result = await render(this.text, this.font);
if (version !== drawVersion) return;
this.ascii = result;
this.statusKey = '';
} catch {
if (version !== drawVersion) return;
this.ascii = '';
this.statusKey = 'renderFailed';
} finally {
if (version === drawVersion) this.busy = false;
}
},
cycleFont(direction) {
this.font = styles[(styles.indexOf(this.font) + direction + styles.length) % styles.length];
},
copy() {
try {
copyText(this.ascii);
this.statusKey = 'copied';
} catch {
const range = document.createRange();
range.selectNodeContents(output);
range.selectNodeContents(this.$refs.output);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
showStatus('copyFailed');
this.statusKey = 'copyFailed';
}
});
document.getElementById('download').addEventListener('click', () => {
const url = URL.createObjectURL(new Blob([output.textContent + '\n'], {type: 'text/plain;charset=utf-8'}));
},
download() {
const url = URL.createObjectURL(new Blob([this.ascii + '\n'], {type: 'text/plain;charset=utf-8'}));
const link = document.createElement('a');
link.href = url;
link.download = 'ascii-art.txt';
@@ -147,21 +135,23 @@ document.getElementById('download').addEventListener('click', () => {
link.click();
link.remove();
setTimeout(() => URL.revokeObjectURL(url), 1000);
showStatus('downloaded');
});
document.getElementById('share').addEventListener('click', () => {
this.statusKey = 'downloaded';
},
share() {
const url = new URL('./', window.location.href);
url.searchParams.set('text', text.value);
url.searchParams.set('font', style.value);
url.searchParams.set('text', this.text);
url.searchParams.set('font', this.font);
try {
copyText(url.href);
showStatus('linkCopied');
this.statusKey = 'linkCopied';
} catch {
window.history.replaceState(null, '', url);
showStatus('linkFailed');
this.statusKey = 'linkFailed';
}
},
install() { return pwa.install(); },
update() { pwa.update(); },
};
});
setupPWA({
failed() { showStatus('offlineFailed'); },
});
Alpine.start();
+31 -29
View File
@@ -14,58 +14,60 @@
<script type="module" src="./app.js"></script>
</head>
<body>
<main>
<main x-data="asciiApp" x-cloak>
<header>
<a class="logo" href="./" aria-label="ascii homepage">
<a class="logo" href="./" :aria-label="t('homeLabel')">
<img src="./icons/ascii.svg" width="64" height="64" alt="">
<h1>ascii!</h1>
</a>
</header>
<form id="render-form">
<label for="text" data-i18n="textLabel">your text</label>
<textarea id="text" name="text" rows="2" maxlength="500" spellcheck="false">hello world</textarea>
<form id="render-form" @submit.prevent="draw()">
<label for="text" x-text="t('textLabel')">your text</label>
<textarea id="text" name="text" rows="2" maxlength="500" spellcheck="false" x-model="text"></textarea>
<div class="controls">
<label><span data-i18n="styleLabel">lettering</span>
<select id="style" name="font">
<option value="Standard">Standard</option>
<label><span x-text="t('styleLabel')">lettering</span>
<select id="style" name="font" x-model="font">
<template x-for="name in styles" :key="name">
<option :value="name" :selected="name === font" x-text="name"></option>
</template>
</select>
</label>
<div class="font-navigation">
<button id="prev-font" type="button" data-i18n="prev">prev</button>
<button id="next-font" type="button">next font: 3-D</button>
<button id="prev-font" type="button" @click="cycleFont(-1)" x-text="t('prev')">prev</button>
<button id="next-font" type="button" @click="cycleFont(1)" x-text="t('nextFont').replace('{name}', nextFont)"></button>
</div>
</div>
</form>
<div class="output-heading">
<p data-i18n="preview">your ASCII art</p>
<p x-text="t('preview')">your ASCII art</p>
<div class="actions">
<button id="copy" type="button" data-i18n="copy">copy</button>
<button id="download" type="button" data-i18n="download">download .txt</button>
<button id="share" type="button" data-i18n="share">copy link</button>
<button id="copy" type="button" @click="copy()" :disabled="!canCopy" x-text="t('copy')">copy</button>
<button id="download" type="button" @click="download()" :disabled="!canCopy" x-text="t('download')">download .txt</button>
<button id="share" type="button" @click="share()" x-text="t('share')">copy link</button>
</div>
</div>
<pre id="output" tabindex="0" aria-label="ASCII art"></pre>
<p class="note" data-i18n="glyphNote">Character support varies by font.</p>
<p id="status" role="status" aria-live="polite"></p>
<pre id="output" x-ref="output" tabindex="0" :aria-label="t('outputLabel')" :aria-busy="busy" x-text="ascii"></pre>
<p class="note" x-text="t('glyphNote')">Character support varies by font.</p>
<p id="status" role="status" aria-live="polite" x-text="statusKey ? t(statusKey) : ''"></p>
<footer>
<div class="preferences">
<label><span data-i18n="theme">appearance</span>
<select id="theme">
<option value="system" data-i18n="system">system</option>
<option value="dark" data-i18n="dark">dark</option>
<option value="light" data-i18n="light">light</option>
<label><span x-text="t('theme')">appearance</span>
<select id="theme" x-model="theme">
<option value="system" x-text="t('system')">system</option>
<option value="dark" x-text="t('dark')">dark</option>
<option value="light" x-text="t('light')">light</option>
</select>
</label>
<label><span data-i18n="language">language</span>
<select id="language"><option value="en">English</option><option value="tr">Türkçe</option></select>
<label><span x-text="t('language')">language</span>
<select id="language" x-model="language"><option value="en">English</option><option value="tr">Türkçe</option></select>
</label>
</div>
<div class="install-controls">
<button id="install" type="button" hidden data-i18n="install">install app</button>
<button id="update" type="button" hidden data-i18n="update">update app</button>
<details id="install-guide">
<summary data-i18n="install">install app</summary>
<p data-i18n="installGuide">Use your browser’s install option. In Safari on iPhone or iPad, choose Share → Add to Home Screen.</p>
<button id="install" type="button" x-show="installAvailable" @click="install()" x-text="t('install')">install app</button>
<button id="update" type="button" x-show="updateAvailable" @click="update()" x-text="t('update')">update app</button>
<details id="install-guide" x-show="!installed && !installAvailable">
<summary x-text="t('install')">install app</summary>
<p x-text="t('installGuide')">Use your browser’s install option. In Safari on iPhone or iPad, choose Share → Add to Home Screen.</p>
</details>
</div>
</footer>
+46 -37
View File
@@ -1,51 +1,60 @@
export function setupPWA({failed}) {
const install = document.getElementById('install');
const guide = document.getElementById('install-guide');
const update = document.getElementById('update');
const standalone = () => window.matchMedia('(display-mode: standalone)').matches || navigator.standalone;
// Browser prompt and worker objects stay outside Alpine's reactive state.
export function setupPWA({installAvailable, updateAvailable, installed, failed}) {
let prompt;
guide.hidden = !!standalone();
window.addEventListener('beforeinstallprompt', event => {
let registration;
const cleanup = [];
const listen = (target, name, handler) => {
target.addEventListener(name, handler);
cleanup.push(() => target.removeEventListener(name, handler));
};
const standalone = () => window.matchMedia('(display-mode: standalone)').matches || !!navigator.standalone;
installed(standalone());
listen(window, 'beforeinstallprompt', event => {
event.preventDefault();
prompt = event;
install.hidden = false;
guide.hidden = true;
installAvailable(true);
});
install.addEventListener('click', async () => {
if (!prompt) return;
const pending = prompt;
listen(window, 'appinstalled', () => {
prompt = undefined;
install.hidden = true;
try {
await pending.prompt();
await pending.userChoice;
} catch {
// Keep manual installation guidance available if the prompt fails.
} finally {
guide.hidden = !!standalone();
}
installAvailable(false);
installed(true);
});
window.addEventListener('appinstalled', () => {
prompt = undefined;
install.hidden = true;
guide.hidden = true;
});
if (!('serviceWorker' in navigator)) return;
let destroyed = false;
if ('serviceWorker' in navigator) {
let controlled = !!navigator.serviceWorker.controller;
navigator.serviceWorker.addEventListener('controllerchange', () => {
listen(navigator.serviceWorker, 'controllerchange', () => {
if (controlled) window.location.reload();
controlled = true;
});
navigator.serviceWorker.register('./sw.js', {scope: './', type: 'module', updateViaCache: 'none'})
.then(registration => {
const showUpdate = () => { update.hidden = !registration.waiting; };
.then(value => {
if (destroyed) return;
registration = value;
const showUpdate = () => updateAvailable(!!registration.waiting);
showUpdate();
registration.addEventListener('updatefound', () => {
const worker = registration.installing;
if (worker) worker.addEventListener('statechange', showUpdate);
listen(registration, 'updatefound', () => {
if (registration.installing) listen(registration.installing, 'statechange', showUpdate);
});
update.addEventListener('click', () => {
if (registration.waiting) registration.waiting.postMessage('ACTIVATE_UPDATE');
});
}).catch(failed);
}).catch(() => { if (!destroyed) failed(); });
}
return {
async install() {
if (!prompt) return;
const pending = prompt;
prompt = undefined;
installAvailable(false);
try {
await pending.prompt();
await pending.userChoice;
} catch { /* Manual installation guidance remains available. */ }
},
update() {
registration?.waiting?.postMessage('ACTIVATE_UPDATE');
},
destroy() {
destroyed = true;
cleanup.forEach(remove => remove());
},
};
}
+1 -1
View File
@@ -34,7 +34,7 @@
}
}
* { box-sizing: border-box; }
[hidden] { display: none !important; }
[hidden], [x-cloak] { display: none !important; }
body { margin: 0; padding: clamp(1rem, 4vw, 3rem); background: var(--background); color: var(--foreground); font-family: plexmono, Consolas, monospace; line-height: 1.6; }
main { max-width: 1120px; margin: auto; min-width: 0; }
header { margin-bottom: 2rem; }
+2 -2
View File
@@ -1,11 +1,11 @@
// Bump VERSION whenever a shell file changes. New versions wait for approval.
const VERSION = 'v7';
const VERSION = 'v8';
const BASE = self.registration.scope;
const PREFIX = `ascii-pwa-${encodeURIComponent(BASE)}-`;
const CACHE = `${PREFIX}${VERSION}`;
const SHELL = [
'./', './index.html', './style.css', './appearance.js', './app.js',
'./renderer.js', './font-catalog.js', './vendor/figlet.js',
'./renderer.js', './font-catalog.js', './vendor/figlet.js', './vendor/alpine.js',
'./translations.js', './pwa.js', './manifest.webmanifest',
'./fonts/IBMPlexMono-Regular.subset.woff2',
'./fonts/IBMPlexMono-Bold.subset.woff2',
+5
View File
@@ -8,3 +8,8 @@ retain their embedded authorship and license notices unchanged.
No CDN or npm installation is needed at runtime. Font data is loaded locally
when selected and saved in a separate browser cache for later offline use.
`alpine.js` is the unmodified ES module build of
[`Alpine.js 3.14.9`](https://github.com/alpinejs/alpine/tree/v3.14.9).
Its MIT license is retained as `alpine.LICENSE`. The bundled Vue reactivity
code's MIT license is retained as `vue.LICENSE`.
+21
View File
@@ -0,0 +1,21 @@
# MIT License
Copyright © 2019-2021 Caleb Porzio and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+5
View File
@@ -0,0 +1,5 @@
var rt=!1,nt=!1,U=[],it=-1;function Vt(e){On(e)}function On(e){U.includes(e)||U.push(e),Cn()}function Ee(e){let t=U.indexOf(e);t!==-1&&t>it&&U.splice(t,1)}function Cn(){!nt&&!rt&&(rt=!0,queueMicrotask(Tn))}function Tn(){rt=!1,nt=!0;for(let e=0;e<U.length;e++)U[e](),it=e;U.length=0,it=-1,nt=!1}var R,D,L,st,ot=!0;function qt(e){ot=!1,e(),ot=!0}function Ut(e){R=e.reactive,L=e.release,D=t=>e.effect(t,{scheduler:r=>{ot?Vt(r):r()}}),st=e.raw}function at(e){D=e}function Wt(e){let t=()=>{};return[n=>{let i=D(n);return e._x_effects||(e._x_effects=new Set,e._x_runEffects=()=>{e._x_effects.forEach(o=>o())}),e._x_effects.add(i),t=()=>{i!==void 0&&(e._x_effects.delete(i),L(i))},i},()=>{t()}]}function Se(e,t){let r=!0,n,i=D(()=>{let o=e();JSON.stringify(o),r?n=o:queueMicrotask(()=>{t(o,n),n=o}),r=!1});return()=>L(i)}var Gt=[],Jt=[],Yt=[];function Xt(e){Yt.push(e)}function ee(e,t){typeof t=="function"?(e._x_cleanups||(e._x_cleanups=[]),e._x_cleanups.push(t)):(t=e,Jt.push(t))}function Ae(e){Gt.push(e)}function Oe(e,t,r){e._x_attributeCleanups||(e._x_attributeCleanups={}),e._x_attributeCleanups[t]||(e._x_attributeCleanups[t]=[]),e._x_attributeCleanups[t].push(r)}function ct(e,t){e._x_attributeCleanups&&Object.entries(e._x_attributeCleanups).forEach(([r,n])=>{(t===void 0||t.includes(r))&&(n.forEach(i=>i()),delete e._x_attributeCleanups[r])})}function Zt(e){if(e._x_cleanups)for(;e._x_cleanups.length;)e._x_cleanups.pop()()}var lt=new MutationObserver(pt),ut=!1;function le(){lt.observe(document,{subtree:!0,childList:!0,attributes:!0,attributeOldValue:!0}),ut=!0}function ft(){Rn(),lt.disconnect(),ut=!1}var ce=[];function Rn(){let e=lt.takeRecords();ce.push(()=>e.length>0&&pt(e));let t=ce.length;queueMicrotask(()=>{if(ce.length===t)for(;ce.length>0;)ce.shift()()})}function _(e){if(!ut)return e();ft();let t=e();return le(),t}var dt=!1,ve=[];function Qt(){dt=!0}function er(){dt=!1,pt(ve),ve=[]}function pt(e){if(dt){ve=ve.concat(e);return}let t=new Set,r=new Set,n=new Map,i=new Map;for(let o=0;o<e.length;o++)if(!e[o].target._x_ignoreMutationObserver&&(e[o].type==="childList"&&(e[o].addedNodes.forEach(s=>s.nodeType===1&&t.add(s)),e[o].removedNodes.forEach(s=>s.nodeType===1&&r.add(s))),e[o].type==="attributes")){let s=e[o].target,a=e[o].attributeName,c=e[o].oldValue,l=()=>{n.has(s)||n.set(s,[]),n.get(s).push({name:a,value:s.getAttribute(a)})},u=()=>{i.has(s)||i.set(s,[]),i.get(s).push(a)};s.hasAttribute(a)&&c===null?l():s.hasAttribute(a)?(u(),l()):u()}i.forEach((o,s)=>{ct(s,o)}),n.forEach((o,s)=>{Gt.forEach(a=>a(s,o))});for(let o of r)t.has(o)||Jt.forEach(s=>s(o));t.forEach(o=>{o._x_ignoreSelf=!0,o._x_ignore=!0});for(let o of t)r.has(o)||o.isConnected&&(delete o._x_ignoreSelf,delete o._x_ignore,Yt.forEach(s=>s(o)),o._x_ignore=!0,o._x_ignoreSelf=!0);t.forEach(o=>{delete o._x_ignoreSelf,delete o._x_ignore}),t=null,r=null,n=null,i=null}function Ce(e){return F(j(e))}function P(e,t,r){return e._x_dataStack=[t,...j(r||e)],()=>{e._x_dataStack=e._x_dataStack.filter(n=>n!==t)}}function j(e){return e._x_dataStack?e._x_dataStack:typeof ShadowRoot=="function"&&e instanceof ShadowRoot?j(e.host):e.parentNode?j(e.parentNode):[]}function F(e){return new Proxy({objects:e},Mn)}var Mn={ownKeys({objects:e}){return Array.from(new Set(e.flatMap(t=>Object.keys(t))))},has({objects:e},t){return t==Symbol.unscopables?!1:e.some(r=>Object.prototype.hasOwnProperty.call(r,t)||Reflect.has(r,t))},get({objects:e},t,r){return t=="toJSON"?Nn:Reflect.get(e.find(n=>Reflect.has(n,t))||{},t,r)},set({objects:e},t,r,n){let i=e.find(s=>Object.prototype.hasOwnProperty.call(s,t))||e[e.length-1],o=Object.getOwnPropertyDescriptor(i,t);return o?.set&&o?.get?Reflect.set(i,t,r,n):Reflect.set(i,t,r)}};function Nn(){return Reflect.ownKeys(this).reduce((t,r)=>(t[r]=Reflect.get(this,r),t),{})}function Te(e){let t=n=>typeof n=="object"&&!Array.isArray(n)&&n!==null,r=(n,i="")=>{Object.entries(Object.getOwnPropertyDescriptors(n)).forEach(([o,{value:s,enumerable:a}])=>{if(a===!1||s===void 0||typeof s=="object"&&s!==null&&s.__v_skip)return;let c=i===""?o:`${i}.${o}`;typeof s=="object"&&s!==null&&s._x_interceptor?n[o]=s.initialize(e,c,o):t(s)&&s!==n&&!(s instanceof Element)&&r(s,c)})};return r(e)}function Re(e,t=()=>{}){let r={initialValue:void 0,_x_interceptor:!0,initialize(n,i,o){return e(this.initialValue,()=>Dn(n,i),s=>mt(n,i,s),i,o)}};return t(r),n=>{if(typeof n=="object"&&n!==null&&n._x_interceptor){let i=r.initialize.bind(r);r.initialize=(o,s,a)=>{let c=n.initialize(o,s,a);return r.initialValue=c,i(o,s,a)}}else r.initialValue=n;return r}}function Dn(e,t){return t.split(".").reduce((r,n)=>r[n],e)}function mt(e,t,r){if(typeof t=="string"&&(t=t.split(".")),t.length===1)e[t[0]]=r;else{if(t.length===0)throw error;return e[t[0]]||(e[t[0]]={}),mt(e[t[0]],t.slice(1),r)}}var tr={};function y(e,t){tr[e]=t}function ue(e,t){return Object.entries(tr).forEach(([r,n])=>{let i=null;function o(){if(i)return i;{let[s,a]=_t(t);return i={interceptor:Re,...s},ee(t,a),i}}Object.defineProperty(e,`$${r}`,{geLine truncated
${r?'Expression: "'+r+`"
`:""}`,t),setTimeout(()=>{throw e},0)}var Me=!0;function De(e){let t=Me;Me=!1;let r=e();return Me=t,r}function M(e,t,r={}){let n;return x(e,t)(i=>n=i,r),n}function x(...e){return nr(...e)}var nr=gt;function ir(e){nr=e}function gt(e,t){let r={};ue(r,e);let n=[r,...j(e)],i=typeof t=="function"?Pn(n,t):kn(n,t,e);return rr.bind(null,e,t,i)}function Pn(e,t){return(r=()=>{},{scope:n={},params:i=[]}={})=>{let o=t.apply(F([n,...e]),i);Ne(r,o)}}var ht={};function In(e,t){if(ht[e])return ht[e];let r=Object.getPrototypeOf(async function(){}).constructor,n=/^[\n\s]*if.*\(.*\)/.test(e.trim())||/^(let|const)\s/.test(e.trim())?`(async()=>{ ${e} })()`:e,o=(()=>{try{let s=new r(["__self","scope"],`with (scope) { __self.result = ${n} }; __self.finished = true; return __self.result;`);return Object.defineProperty(s,"name",{value:`[Alpine] ${e}`}),s}catch(s){return te(s,t,e),Promise.resolve()}})();return ht[e]=o,o}function kn(e,t,r){let n=In(t,r);return(i=()=>{},{scope:o={},params:s=[]}={})=>{n.result=void 0,n.finished=!1;let a=F([o,...e]);if(typeof n=="function"){let c=n(n,a).catch(l=>te(l,r,t));n.finished?(Ne(i,n.result,a,s,r),n.result=void 0):c.then(l=>{Ne(i,l,a,s,r)}).catch(l=>te(l,r,t)).finally(()=>n.result=void 0)}}}function Ne(e,t,r,n,i){if(Me&&typeof t=="function"){let o=t.apply(r,n);o instanceof Promise?o.then(s=>Ne(e,s,r,n)).catch(s=>te(s,i,t)):e(o)}else typeof t=="object"&&t instanceof Promise?t.then(o=>e(o)):e(t)}var bt="x-";function C(e=""){return bt+e}function or(e){bt=e}var Pe={};function d(e,t){return Pe[e]=t,{before(r){if(!Pe[r]){console.warn(String.raw`Cannot find directive \`${r}\`. \`${e}\` will use the default order of execution`);return}let n=W.indexOf(r);W.splice(n>=0?n:W.indexOf("DEFAULT"),0,e)}}}function sr(e){return Object.keys(Pe).includes(e)}function de(e,t,r){if(t=Array.from(t),e._x_virtualDirectives){let o=Object.entries(e._x_virtualDirectives).map(([a,c])=>({name:a,value:c})),s=wt(o);o=o.map(a=>s.find(c=>c.name===a.name)?{name:`x-bind:${a.name}`,value:`"${a.value}"`}:a),t=t.concat(o)}let n={};return t.map(lr((o,s)=>n[o]=s)).filter(fr).map($n(n,r)).sort(jn).map(o=>Ln(e,o))}function wt(e){return Array.from(e).map(lr()).filter(t=>!fr(t))}var xt=!1,fe=new Map,ar=Symbol();function cr(e){xt=!0;let t=Symbol();ar=t,fe.set(t,[]);let r=()=>{for(;fe.get(t).length;)fe.get(t).shift()();fe.delete(t)},n=()=>{xt=!1,r()};e(r),n()}function _t(e){let t=[],r=a=>t.push(a),[n,i]=Wt(e);return t.push(i),[{Alpine:B,effect:n,cleanup:r,evaluateLater:x.bind(x,e),evaluate:M.bind(M,e)},()=>t.forEach(a=>a())]}function Ln(e,t){let r=()=>{},n=Pe[t.type]||r,[i,o]=_t(e);Oe(e,t.original,o);let s=()=>{e._x_ignore||e._x_ignoreSelf||(n.inline&&n.inline(e,t,i),n=n.bind(n,e,t,i),xt?fe.get(ar).push(n):n())};return s.runCleanups=o,s}var Ie=(e,t)=>({name:r,value:n})=>(r.startsWith(e)&&(r=r.replace(e,t)),{name:r,value:n}),ke=e=>e;function lr(e=()=>{}){return({name:t,value:r})=>{let{name:n,value:i}=ur.reduce((o,s)=>s(o),{name:t,value:r});return n!==t&&e(n,t),{name:n,value:i}}}var ur=[];function re(e){ur.push(e)}function fr({name:e}){return dr().test(e)}var dr=()=>new RegExp(`^${bt}([^:^.]+)\\b`);function $n(e,t){return({name:r,value:n})=>{let i=r.match(dr()),o=r.match(/:([a-zA-Z0-9\-_:]+)/),s=r.match(/\.[^.\]]+(?=[^\]]*$)/g)||[],a=t||e[r]||r;return{type:i?i[1]:null,value:o?o[1]:null,modifiers:s.map(c=>c.replace(".","")),expression:n,original:a}}}var yt="DEFAULT",W=["ignore","ref","data","id","anchor","bind","init","for","model","modelable","transition","show","if",yt,"teleport"];function jn(e,t){let r=W.indexOf(e.type)===-1?yt:e.type,n=W.indexOf(t.type)===-1?yt:t.type;return W.indexOf(r)-W.indexOf(n)}function G(e,t,r={}){e.dispatchEvent(new CustomEvent(t,{detail:r,bubbles:!0,composed:!0,cancelable:!0}))}function T(e,t){if(typeof ShadowRoot=="function"&&e instanceof ShadowRoot){Array.from(e.children).forEach(i=>T(i,t));return}let r=!1;if(t(e,()=>r=!0),r)return;let n=e.firstElementChild;for(;n;)T(n,t,!1),n=n.nextElementSibling}function E(e,...t){console.warn(`Alpine Warning: ${e}`,...t)}var pr=!1;function mr(){pr&&E("Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems."),pr=!0,document.body||E("Unable to initialize. Trying to load Alpine before `<body>` is available. Did you forget to add `defer` in Alpine's `<script>` tag?"),G(document,"alpine:init"),G(document,"alpine:initializing"),le(),Xt(t=>v(t,T)),ee(t=>St(t)),Ae((t,r)=>{de(t,r).forEach(n=>n())});let e=t=>!J(t.parentElement,!0);Array.from(document.querySelectorAll(gr().join(","))).filter(e).forEach(t=>{v(t)}),G(document,"alpine:initialized"),setTimeout(()=>{Fn()})}var Et=[],_r=[];function hr(){return Et.map(e=>e())}function gr(){return Et.concat(_r).map(e=>e())}function Le(e){Et.push(e)}function $e(e){_r.push(e)}function J(e,t=!1){return z(e,r=>{if((t?gr():hr()).some(i=>r.matches(i)))return!0})}function z(e,t){if(e){if(t(e))return e;if(e._x_teleportBack&&(e=e._x_teleportBack),!!e.parentElement)return z(e.parentElement,t)}}function xLine truncated
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2018-present, Yuxi (Evan) You
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.