From b5eb4811d6e2e98e6da5dfa7d81b1a039e222c17 Mon Sep 17 00:00:00 2001 From: defaultkavy Date: Sun, 6 Oct 2024 02:21:31 +0800 Subject: [PATCH] v0.3.0 --- dist/assets/index-359h29Hw.js | 1 + dist/assets/index-BXPCnb25.css | 1 + dist/assets/index-Bnhfn_iK.css | 1 - dist/assets/index-DMNcFlub.js | 1 - dist/index.html | 8 +- index.html | 2 + index.scss | 145 +++++++++-- package.json | 5 +- src/component/IonIcon/$IonIcon.ts | 17 ++ src/component/PostGrid/$PostGrid.ts | 77 +++++- src/component/PostTile/$PostTile.ts | 15 +- src/component/PostTile/_$PostTile.scss | 63 ++--- src/component/Searchbar/$Searchbar.ts | 312 +++++++++++++++++++++++ src/component/Searchbar/_$Searchbar.scss | 158 ++++++++++++ src/env.d.ts | 1 + src/main.ts | 116 +++++++-- src/route/gallery/$gallery.ts | 8 +- src/route/{posts => post}/$post.ts | 73 +++--- src/route/{posts => post}/_$post.scss | 27 +- src/structure/Booru.ts | 31 ++- src/structure/Commentary.ts | 4 +- src/structure/Post.ts | 56 ++-- src/structure/Tag.ts | 17 +- src/structure/Test.ts | 0 src/structure/User.ts | 4 +- vite.config.ts | 6 + 26 files changed, 973 insertions(+), 176 deletions(-) create mode 100644 dist/assets/index-359h29Hw.js create mode 100644 dist/assets/index-BXPCnb25.css delete mode 100644 dist/assets/index-Bnhfn_iK.css delete mode 100644 dist/assets/index-DMNcFlub.js create mode 100644 src/component/IonIcon/$IonIcon.ts create mode 100644 src/component/Searchbar/$Searchbar.ts create mode 100644 src/component/Searchbar/_$Searchbar.scss create mode 100644 src/env.d.ts rename src/route/{posts => post}/$post.ts (66%) rename src/route/{posts => post}/_$post.scss (84%) create mode 100644 src/structure/Test.ts create mode 100644 vite.config.ts diff --git a/dist/assets/index-359h29Hw.js b/dist/assets/index-359h29Hw.js new file mode 100644 index 0000000..6b744d0 --- /dev/null +++ b/dist/assets/index-359h29Hw.js @@ -0,0 +1 @@ +var At=Object.defineProperty;var dt=r=>{throw TypeError(r)};var Ct=(r,t,e)=>t in r?At(r,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):r[t]=e;var c=(r,t,e)=>Ct(r,typeof t!="symbol"?t+"":t,e),mt=(r,t,e)=>t.has(r)||dt("Cannot "+e);var x=(r,t,e)=>(mt(r,t,"read from private field"),e?e.call(r):t.get(r)),G=(r,t,e)=>t.has(r)?dt("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(r):t.set(r,e),B=(r,t,e,s)=>(mt(r,t,"write to private field"),s?s.call(r,e):t.set(r,e),e);(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))s(i);new MutationObserver(i=>{for(const n of i)if(n.type==="childList")for(const a of n.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&s(a)}).observe(document,{childList:!0,subtree:!0});function e(i){const n={};return i.integrity&&(n.integrity=i.integrity),i.referrerPolicy&&(n.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?n.credentials="include":i.crossOrigin==="anonymous"?n.credentials="omit":n.credentials="same-origin",n}function s(i){if(i.ep)return;i.ep=!0;const n=e(i);fetch(i.href,n)}})();class F{constructor(){c(this,"__hidden",!1);c(this,"domEvents",{});c(this,"parent");c(this,"events",new Q)}on(t,e,s){this.domEvents[t]||(this.domEvents[t]=new Map);const i=n=>{e(n,this)};return this.domEvents[t].set(e,i),this.events.on(t,e),this.dom.addEventListener(t,i,s),this}off(t,e,s){var n;const i=(n=this.domEvents[t])==null?void 0:n.get(e);return i&&this.dom.removeEventListener(t,i,s),this.events.off(t,e),this}once(t,e,s){const i=n=>{this.dom.removeEventListener(t,i,s),e(n,this)};return this.dom.addEventListener(t,i,s),this.events.once(t,e),this}hide(t,e=!0){return T.fluent(this,arguments,()=>this.__hidden,()=>{var s;if(t!==void 0)return t instanceof I?(this.__hidden=t.value,t.use(this,"hide")):this.__hidden=t,e&&((s=this.parent)==null||s.children.render()),this})}remove(){var t;return(t=this.parent)==null||t.children.remove(this).render(),this}replace(t){var e;return(e=this.parent)==null||e.children.replace(this,t).render(),this}contains(t){return t?t instanceof F?this.dom.contains(t.dom):t instanceof EventTarget?this.dom.contains(T(t).dom):this.dom.contains(t):!1}self(t){return t(this),this}inDOM(){return document.contains(this.dom)}isElement(){return this instanceof P}get element(){return this instanceof P?this:null}}class P extends F{constructor(e,s){super();c(this,"dom");c(this,"static_classes",new Set);this.dom=this.createDom(e,s),this.dom.$=this,this.setOptions(s)}createDom(e,s){return s!=null&&s.dom?s.dom:e==="svg"?document.createElementNS("http://www.w3.org/2000/svg","svg"):document.createElement((s==null?void 0:s.tagname)??e)}setOptions(e){return this.id(e==null?void 0:e.id),e&&e.class&&this.class(...e.class),this}id(e){return $.fluent(this,arguments,()=>this.dom.id,()=>$.set(this.dom,"id",e))}class(...e){return $.fluent(this,arguments,()=>this.dom.classList,()=>{this.dom.classList.forEach(s=>this.static_classes.has(s)??this.dom.classList.remove(s)),this.dom.classList.add(...e.detype())})}addClass(...e){return $.fluent(this,arguments,()=>this,()=>{this.dom.classList.add(...e.detype())})}removeClass(...e){return $.fluent(this,arguments,()=>this,()=>{this.dom.classList.remove(...e.detype())})}staticClass(...e){return $.fluent(this,arguments,()=>this.static_classes,()=>{this.removeClass(...this.static_classes),this.static_classes.clear(),this.addStaticClass(...e)})}addStaticClass(...e){return $.fluent(this,arguments,()=>this,()=>{e.detype().forEach(s=>this.static_classes.add(s)),this.addClass(...e)})}removeStaticClass(...e){return $.fluent(this,arguments,()=>this,()=>{e.detype().forEach(s=>this.static_classes.delete(s)),this.removeClass(...e)})}css(e){return $.fluent(this,arguments,()=>this.dom.style,()=>{Object.assign(this.dom.style,e)})}attribute(e,s){return arguments.length?arguments.length===1?e===void 0?null:this.dom.getAttribute(e):arguments.length===2?e?(s===null?this.dom.removeAttribute(e):s!==void 0&&this.dom.setAttribute(e,`${s}`),this):this:this:null}tabIndex(e){return $.fluent(this,arguments,()=>this.dom.tabIndex,()=>$.set(this.dom,"tabIndex",e))}focus(){return this.dom.focus(),this}blur(){return this.dom.blur(),this}animate(e,s,i){const n=this.dom.animate(e,s);return i&&(n.onfinish=()=>i(n)),n}getAnimations(e){return this.dom.getAnimations(e)}get dataset(){return this.dom.dataset}domRect(e){const s=this.dom.getBoundingClientRect();if(!e)return s;const i=e instanceof P?e.dom.getBoundingClientRect():e;return{...s,top:s.top-i.top,left:s.left-i.left,right:s.right-i.left,bottom:s.bottom-i.top,x:s.x-i.x,y:s.y-i.y}}}class K extends F{constructor(e){super();c(this,"dom");this.dom=e,this.dom.$=this}domRect(e){const s={bottom:innerHeight,height:innerHeight,left:0,right:innerWidth,top:0,width:innerWidth,x:0,y:0};if(!e)return s;const i=e instanceof P?e.dom.getBoundingClientRect():e;return{...s,top:s.top-i.top,left:s.left-i.left,right:s.right-i.left,bottom:s.bottom-i.top,x:s.x-i.x,y:s.y-i.y}}static from(e){return e.$ instanceof K?e.$:new K(e)}}const Rt="modulepreload",kt=function(r){return"/"+r},ft={},Nt=function(t,e,s){let i=Promise.resolve();if(e&&e.length>0){document.getElementsByTagName("link");const a=document.querySelector("meta[property=csp-nonce]"),o=(a==null?void 0:a.nonce)||(a==null?void 0:a.getAttribute("nonce"));i=Promise.allSettled(e.map(u=>{if(u=kt(u),u in ft)return;ft[u]=!0;const h=u.endsWith(".css"),m=h?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${u}"]${m}`))return;const p=document.createElement("link");if(p.rel=h?"stylesheet":Rt,h||(p.as="script"),p.crossOrigin="",p.href=u,o&&p.setAttribute("nonce",o),document.head.appendChild(p),h)return new Promise((b,l)=>{p.addEventListener("load",b),p.addEventListener("error",()=>l(new Error(`Unable to preload CSS for ${u}`)))})}))}function n(a){const o=new Event("vite:preloadError",{cancelable:!0});if(o.payload=a,window.dispatchEvent(o),!o.defaultPrevented)throw a}return i.then(a=>{for(const o of a||[])o.status==="rejected"&&n(o.reason);return t().catch(n)})};class Dt{constructor(t){c(this,"$container");c(this,"childList",new Set);this.$container=t}add(t,e=-1){if(e===-1||this.childList.size-1===e)this.childList.add(t);else{const s=[...this.childList];s.splice(e,0,t),this.childList.clear(),s.forEach(i=>this.childList.add(i))}t.parent=this.$container}remove(t){return this.childList.has(t)?(this.childList.delete(t),t.parent=void 0,this):this}removeAll(t=!0){this.childList.forEach(e=>this.remove(e)),t&&this.render()}replace(t,e){const s=this.array;return s.splice(s.indexOf(t),1,e),t.remove(),this.childList.clear(),s.forEach(i=>this.childList.add(i)),e.parent=this.$container,this}render(){const[t,e]=[this.array.map(i=>i.dom),Array.from(this.dom.childNodes)],s=[];for(;e.length||t.length;){const[i,n]=[e.at(0),t.at(0)];n?i?n!==i?(n.$.__hidden||(this.dom.insertBefore(n,i),s.push(n)),t.shift()):(n.$.__hidden&&this.dom.removeChild(n),t.shift(),e.shift()):(n.$.__hidden||this.dom.append(n),t.shift()):(i&&!s.includes(i)&&i.remove(),e.shift())}}indexOf(t){return this.array.indexOf(t)}get array(){return[...this.childList.values()]}get dom(){return this.$container.dom}}class I{constructor(t,e){c(this,"_value");c(this,"attributes",new Map);c(this,"linkStates",new Set);c(this,"options",{});this.set(t),e&&(this.options=e)}set(t){this._value=t,t instanceof I&&t.linkStates.add(this),this.update(),this.linkStates.forEach(e=>e.update())}update(){for(const[t,e]of this.attributes.entries())for(const s of e)t[s]instanceof Function?this.options.format?t[s](this.options.format(this.value)):t[s](this.value):s in t&&(t[s]=this.value)}toString(){return this.options.format?this.options.format(this.value):this.value instanceof Object?JSON.stringify(this.toJSON()):`${this.value}`}use(t,e){const s=this.attributes.get(t);s?s.add(e):this.attributes.set(t,new Set().add(e))}toJSON(){return this.value instanceof I?this.value.toJSON():this.value instanceof Object?I.toJSON(this.value):this.toString()}static toJSON(t){const e={};for(let[s,i]of Object.entries(t))i instanceof I?i=i.toJSON():i instanceof Object&&I.toJSON(i),Object.assign(e,{[s]:i});return e}get value(){return this._value instanceof I?this._value.value:this._value}}class q extends F{constructor(e){super();c(this,"dom");this.dom=new Text(e),this.dom.$=this}content(e){return $.fluent(this,arguments,()=>this.dom.textContent,()=>$.set(this.dom,"textContent",e))}}class Y extends P{constructor(t,e){super(t,e)}autocapitalize(t){return $.fluent(this,arguments,()=>this.dom.autocapitalize,()=>$.set(this.dom,"autocapitalize",t))}innerText(t){return $.fluent(this,arguments,()=>this.dom.innerText,()=>$.set(this.dom,"innerText",t))}title(t){return $.fluent(this,arguments,()=>this.dom.title,()=>$.set(this.dom,"title",t))}dir(t){return $.fluent(this,arguments,()=>this.dom.dir,()=>$.set(this.dom,"dir",t))}translate(t){return $.fluent(this,arguments,()=>this.dom.translate,()=>$.set(this.dom,"translate",t))}popover(t){return $.fluent(this,arguments,()=>this.dom.popover,()=>$.set(this.dom,"popover",t))}spellcheck(t){return $.fluent(this,arguments,()=>this.dom.spellcheck,()=>$.set(this.dom,"spellcheck",t))}inert(t){return $.fluent(this,arguments,()=>this.dom.inert,()=>$.set(this.dom,"inert",t))}lang(t){return $.fluent(this,arguments,()=>this.dom.lang,()=>$.set(this.dom,"lang",t))}draggable(t){return $.fluent(this,arguments,()=>this.dom.draggable,()=>$.set(this.dom,"draggable",t))}hidden(t){return $.fluent(this,arguments,()=>this.dom.hidden,()=>$.set(this.dom,"hidden",t))}click(){return this.dom.click(),this}attachInternals(){return this.dom.attachInternals()}hidePopover(){return this.dom.hidePopover(),this}showPopover(){return this.dom.showPopover(),this}togglePopover(){return this.dom.togglePopover(),this}get accessKeyLabel(){return this.dom.accessKeyLabel}get offsetHeight(){return this.dom.offsetHeight}get offsetLeft(){return this.dom.offsetLeft}get offsetParent(){return $(this.dom.offsetParent)}get offsetTop(){return this.dom.offsetTop}get offsetWidth(){return this.dom.offsetWidth}}class f extends Y{constructor(e,s){super(e,s);c(this,"children",new Dt(this));c(this,"__position_cursor",0)}content(e){return $.fluent(this,arguments,()=>this,()=>{this.children.removeAll(!1),this.insert(e)})}insert(e,s=-1){return $.fluent(this,arguments,()=>this,async()=>{if(e instanceof Function){let i=e(this);i instanceof Promise?e=await i:e=i}else e instanceof Promise&&(e=await e);e=$.orArrayResolve(e),this.__position_cursor=s<0?this.children.array.length+s:s;for(const i of e)if(i!=null){if(i instanceof Array)this.insert(i,this.__position_cursor);else if(typeof i=="string")this.children.add(new q(i),s);else if(i instanceof I){const n=new q(i.toString());i.use(n,"content"),this.children.add(n,s)}else if(i instanceof Promise){const n=(await Nt(async()=>{const{$Async:o}=await Promise.resolve().then(()=>Gt);return{$Async:o}},void 0)).$Async,a=new n().await(i);this.children.add(a,s)}else this.children.add(i,s);this.__position_cursor+=1}this.children.render()})}clear(){return this.children.removeAll(),this}$(e){return $(this.dom.querySelector(e))}$all(e){return Array.from(this.dom.querySelectorAll(e)).map(s=>$(s))}get scrollHeight(){return this.dom.scrollHeight}get scrollWidth(){return this.dom.scrollWidth}scrollTop(e){return $.fluent(this,arguments,()=>this.dom.scrollTop,()=>$.set(this.dom,"scrollTop",e))}scrollLeft(e){return $.fluent(this,arguments,()=>this.dom.scrollLeft,()=>$.set(this.dom,"scrollLeft",e))}}class Ht extends f{constructor(t){super("a",t),this.dom.addEventListener("click",e=>{$.anchorHandler&&this.href()&&(e.preventDefault(),$.anchorHandler(this,e))})}href(t){return $.fluent(this,arguments,()=>this.dom.href,()=>{t&&(this.dom.href=t)})}target(t){return $.fluent(this,arguments,()=>this.dom.target??void 0,()=>{t&&(this.dom.target=t)})}}class Vt extends P{constructor(t,e){super(t,e)}}var _;(r=>{function t(o,u,h,m){return u.length?(m(),o):h()}r.fluent=t;function e(o){return o instanceof Array?o:[o]}r.orArrayResolve=e;function s(o,u){return e(u).forEach(h=>{Object.getOwnPropertyNames(h.prototype).forEach(m=>{m!=="constructor"&&Object.defineProperty(o.prototype,m,Object.getOwnPropertyDescriptor(h.prototype,m)||Object.create(null))})}),o}r.mixin=s;function i(o,u,h){h!==void 0&&(o[u]=h)}r.set=i;function n(o){return new I(o)}r.state=n;function a(o){if(o.$)return o.$;if(o.nodeName.toLowerCase()==="body")return new f("body",{dom:o});if(o.nodeName.toLowerCase()==="#document")return K.from(o);if(o instanceof HTMLElement){const u=$.TagNameElementMap[o.tagName.toLowerCase()],h=u===f?new u(o.tagName,{dom:o}):new u({dom:o});if(h instanceof f)for(const m of Array.from(h.dom.childNodes))h.children.add($(m));return h}else if(o instanceof Text){const u=new q(o.textContent??"");return u.dom=o,u}else if(o instanceof SVGElement&&o.tagName.toLowerCase()==="svg")return new Vt("svg",{dom:o});throw`$NODE.FROM: NOT SUPPORT TARGET ELEMENT TYPE (${o.nodeName})`}r.from=a})(_||(_={}));class L{static create(...t){const e=class{};return Object.getOwnPropertyNames(L.prototype).forEach(s=>{s!=="constructor"&&t.includes(s)&&Object.defineProperty(e.prototype,s,Object.getOwnPropertyDescriptor(L.prototype,s)||Object.create(null))}),e}disabled(t){return $.fluent(this,arguments,()=>this.dom.disabled,()=>$.set(this.dom,"disabled",t))}checkValidity(){return this.dom.checkValidity()}reportValidity(){return this.dom.reportValidity()}formAction(t){return $.fluent(this,arguments,()=>this.dom.formAction,()=>$.set(this.dom,"formAction",t))}formEnctype(t){return $.fluent(this,arguments,()=>this.dom.formEnctype,()=>$.set(this.dom,"formEnctype",t))}formMethod(t){return $.fluent(this,arguments,()=>this.dom.formMethod,()=>$.set(this.dom,"formMethod",t))}formNoValidate(t){return $.fluent(this,arguments,()=>this.dom.formNoValidate,()=>$.set(this.dom,"formNoValidate",t))}formTarget(t){return $.fluent(this,arguments,()=>this.dom.formTarget,()=>$.set(this.dom,"formTarget",t))}autocomplete(t){return $.fluent(this,arguments,()=>this.dom.autocomplete,()=>$.set(this.dom,"autocomplete",t))}name(t){return $.fluent(this,arguments,()=>this.dom.name,()=>$.set(this.dom,"name",t))}maxLength(t){return $.fluent(this,arguments,()=>this.dom.maxLength,()=>$.set(this.dom,"maxLength",t))}minLength(t){return $.fluent(this,arguments,()=>this.dom.minLength,()=>$.set(this.dom,"minLength",t))}required(t){return $.fluent(this,arguments,()=>this.dom.required,()=>$.set(this.dom,"required",t))}label(t){return $.fluent(this,arguments,()=>this.dom.label,()=>$.set(this.dom,"label",t))}get form(){return this.dom.form?$(this.dom.form):null}get validationMessage(){return this.dom.validationMessage}get validity(){return this.dom.validity}get willValidate(){return this.dom.willValidate}}class wt extends f{constructor(t){super("button",t)}type(t){return $.fluent(this,arguments,()=>this.dom.type,()=>$.set(this.dom,"type",t))}}_.mixin(wt,L.create("disabled","checkValidity","formAction","formEnctype","formMethod","formNoValidate","formTarget","reportValidity"));class bt extends f{constructor(t){super("form",t)}action(t){return $.fluent(this,arguments,()=>this.dom.formAction,()=>$.set(this.dom,"formAction",t))}enctype(t){return $.fluent(this,arguments,()=>this.dom.formEnctype,()=>$.set(this.dom,"formEnctype",t))}method(t){return $.fluent(this,arguments,()=>this.dom.formMethod,()=>$.set(this.dom,"formMethod",t))}noValidate(t){return $.fluent(this,arguments,()=>this.dom.formNoValidate,()=>$.set(this.dom,"formNoValidate",t))}acceptCharset(t){return $.fluent(this,arguments,()=>this.dom.acceptCharset,()=>$.set(this.dom,"acceptCharset",t))}target(t){return $.fluent(this,arguments,()=>this.dom.formTarget,()=>$.set(this.dom,"formTarget",t))}requestSubmit(){return this.dom.requestSubmit(),this}reset(){return this.dom.reset(),this}submit(){return this.dom.submit(),this}get length(){return this.dom.length}get elements(){return Array.from(this.dom.elements).map(t=>$(t))}}_.mixin(bt,L.create("checkValidity","reportValidity","autocomplete"));class C extends Y{constructor(t){super("input",t)}value(t){return $.fluent(this,arguments,()=>this.type()==="number"?Number(this.dom.value):this.dom.value,()=>$.set(this.dom,"value",t,e=>{this.on("input",()=>{e.attributes.has(this.dom)!==!1&&(typeof e.value=="string"&&e.set(`${this.value()}`),typeof e.value=="number"&&e.set(Number(this.value())))})}))}type(t){return $.fluent(this,arguments,()=>this.dom.type,()=>$.set(this.dom,"type",t))}capture(t){return $.fluent(this,arguments,()=>this.dom.capture,()=>$.set(this.dom,"capture",t))}alt(t){return $.fluent(this,arguments,()=>this.dom.alt,()=>$.set(this.dom,"alt",t))}height(t){return $.fluent(this,arguments,()=>this.dom.height,()=>$.set(this.dom,"height",t))}width(t){return $.fluent(this,arguments,()=>this.dom.width,()=>$.set(this.dom,"width",t))}defaultValue(t){return $.fluent(this,arguments,()=>this.dom.defaultValue,()=>$.set(this.dom,"defaultValue",t))}dirName(t){return $.fluent(this,arguments,()=>this.dom.dirName,()=>$.set(this.dom,"dirName",t))}pattern(t){return $.fluent(this,arguments,()=>this.dom.pattern,()=>$.set(this.dom,"pattern",t))}placeholder(t){return $.fluent(this,arguments,()=>this.dom.placeholder,()=>$.set(this.dom,"placeholder",t))}readOnly(t){return $.fluent(this,arguments,()=>this.dom.readOnly,()=>$.set(this.dom,"readOnly",t))}selectionDirection(t){return $.fluent(this,arguments,()=>this.dom.selectionDirection,()=>$.set(this.dom,"selectionDirection",t))}selectionEnd(t){return $.fluent(this,arguments,()=>this.dom.selectionEnd,()=>$.set(this.dom,"selectionEnd",t))}selectionStart(t){return $.fluent(this,arguments,()=>this.dom.selectionStart,()=>$.set(this.dom,"selectionStart",t))}size(t){return $.fluent(this,arguments,()=>this.dom.size,()=>$.set(this.dom,"size",t))}src(t){return $.fluent(this,arguments,()=>this.dom.src,()=>$.set(this.dom,"src",t))}inputMode(t){return $.fluent(this,arguments,()=>this.dom.inputMode,()=>$.set(this.dom,"inputMode",t))}valueAsDate(t){return $.fluent(this,arguments,()=>this.dom.valueAsDate,()=>$.set(this.dom,"valueAsDate",t))}valueAsNumber(t){return $.fluent(this,arguments,()=>this.dom.valueAsNumber,()=>$.set(this.dom,"valueAsNumber",t))}webkitdirectory(t){return $.fluent(this,arguments,()=>this.dom.webkitdirectory,()=>$.set(this.dom,"webkitdirectory",t))}select(){return this.dom.select(),this}setCustomValidity(t){return this.dom.setCustomValidity(t),this}setRangeText(t,e,s,i){return typeof e=="number"&&typeof s=="number"&&this.dom.setRangeText(t,e,s,i),this.dom.setRangeText(t),this}setSelectionRange(t,e,s){return this.dom.setSelectionRange(t,e,s),this}showPicker(){return this.dom.showPicker(),this}get files(){return this.dom.files}get webkitEntries(){return this.dom.webkitEntries}get labels(){return Array.from(this.dom.labels??[]).map(t=>$(t))}}_.mixin(C,L.create("checkValidity","reportValidity","autocomplete","name","form","required","validationMessage","validity","willValidate","formAction","formEnctype","formMethod","formNoValidate","formTarget"));class jt extends C{constructor(t){super(t),this.type("number")}static from(t){return $.mixin(C,this)}stepDown(){return this.dom.stepDown(),this}stepUp(){return this.dom.stepUp(),this}max(t){return $.fluent(this,arguments,()=>this.dom.max===""?null:parseInt(this.dom.min),()=>$.set(this.dom,"max",t==null?void 0:t.toString()))}min(t){return $.fluent(this,arguments,()=>this.dom.min===""?null:parseInt(this.dom.min),()=>$.set(this.dom,"min",t==null?void 0:t.toString()))}step(t){return $.fluent(this,arguments,()=>Number(this.dom.step),()=>$.set(this.dom,"step",t==null?void 0:t.toString()))}}class Wt extends C{constructor(t){super(t),this.type("radio")}static from(t){return $.mixin(C,this)}checked(t){return $.fluent(this,arguments,()=>this.dom.checked,()=>$.set(this.dom,"checked",t))}defaultChecked(t){return $.fluent(this,arguments,()=>this.dom.defaultChecked,()=>$.set(this.dom,"defaultChecked",t))}}class Ft extends C{constructor(t){super(t),this.type("file")}static from(t){return $.mixin(C,this)}multiple(t){return $.fluent(this,arguments,()=>this.dom.multiple,()=>$.set(this.dom,"multiple",t))}accept(...t){return $.fluent(this,arguments,()=>this.dom.accept.split(","),()=>this.dom.accept=t.toString())}}_.mixin(C,[jt,Wt,Ft]);class St extends f{constructor(t){super("label",t)}for(t){return $.fluent(this,arguments,()=>this.dom.htmlFor,()=>{$.set(this.dom,"htmlFor",t)})}get control(){return this.dom.control}}_.mixin(St,L.create("form"));class Ut extends Y{constructor(t){super("img",t)}async load(t){return new Promise(e=>{const s=this.once("load",()=>{e(s)});typeof t=="string"?s.src(t):t.then(i=>s.src(i))})}static resize(t,e){return new Promise(s=>{const i=new Image;if(i.addEventListener("load",()=>{const n=document.createElement("canvas"),a=n.getContext("2d");if(!a)throw"$Image.resize: context undefined";const o=i.width/i.height,[u,h,m]=[o>1,o<1,o===1],p=e instanceof Array?e[0]:h?e:e*o,b=e instanceof Array?e[1]:u?e:e/o;n.height=b,n.width=p,a.drawImage(i,0,0,p,b),s(n.toDataURL())},{once:!0}),t instanceof File){const n=new FileReader;n.addEventListener("load",()=>i.src=n.result),n.readAsDataURL(t)}else i.src=t})}alt(t){return $.fluent(this,arguments,()=>this.dom.alt,()=>$.set(this.dom,"alt",t))}crossOrigin(t){return $.fluent(this,arguments,()=>this.dom.crossOrigin,()=>$.set(this.dom,"crossOrigin",t))}decoding(t){return $.fluent(this,arguments,()=>this.dom.decoding,()=>$.set(this.dom,"decoding",t))}height(t){return $.fluent(this,arguments,()=>this.dom.height,()=>$.set(this.dom,"height",t))}isMap(t){return $.fluent(this,arguments,()=>this.dom.isMap,()=>$.set(this.dom,"isMap",t))}loading(t){return $.fluent(this,arguments,()=>this.dom.loading,()=>$.set(this.dom,"loading",t))}referrerPolicy(t){return $.fluent(this,arguments,()=>this.dom.referrerPolicy,()=>$.set(this.dom,"referrerPolicy",t))}sizes(t){return $.fluent(this,arguments,()=>this.dom.sizes,()=>$.set(this.dom,"sizes",t))}src(t){return $.fluent(this,arguments,()=>this.dom.src,()=>$.set(this.dom,"src",t))}srcset(t){return $.fluent(this,arguments,()=>this.dom.srcset,()=>$.set(this.dom,"srcset",t))}useMap(t){return $.fluent(this,arguments,()=>this.dom.useMap,()=>$.set(this.dom,"useMap",t))}width(t){return $.fluent(this,arguments,()=>this.dom.width,()=>$.set(this.dom,"width",t))}decode(){return this.dom.decode()}get complete(){return this.dom.complete}get currentSrc(){return this.dom.currentSrc}get naturalHeight(){return this.dom.naturalHeight}get naturalWidth(){return this.dom.naturalWidth}get x(){return this.dom.x}get y(){return this.dom.y}}class qt extends f{constructor(t){super("canvas",t)}height(t){return $.fluent(this,arguments,()=>this.dom.height,()=>{$.set(this.dom,"height",t)})}width(t){return $.fluent(this,arguments,()=>this.dom.width,()=>{$.set(this.dom,"width",t)})}captureStream(t){return this.dom.captureStream(t)}getContext(t,e){return this.dom.getContext(t)}toBlob(t,e,s){return this.dom.toBlob(t,e,s),this}toDataURL(t,e){return this.dom.toDataURL(t,e)}transferControlToOffscreen(){return this.dom.transferControlToOffscreen()}}class zt extends f{constructor(t){super("dialog",t)}open(t){return $.fluent(this,arguments,()=>this.dom.open,()=>$.set(this.dom,"open",t))}returnValue(t){return $.fluent(this,arguments,()=>this.dom.returnValue,()=>$.set(this.dom,"returnValue",t))}close(){return this.dom.close(),this}show(){return this.dom.show(),this}showModal(){return this.dom.showModal(),this}}class Tt extends f{constructor(t){super("select")}add(t){return this.insert(t),this}item(t){return $(this.dom.item(t))}namedItem(t){return $(this.dom.namedItem(t))}multiple(t){return $.fluent(this,arguments,()=>this.dom.multiple,()=>$.set(this.dom,"multiple",t))}get length(){return this.dom.length}get size(){return this.dom.size}get options(){return Array.from(this.dom.options).map(t=>$(t))}get selectedIndex(){return this.dom.selectedIndex}get selectedOptions(){return Array.from(this.dom.selectedOptions).map(t=>$(t))}value(t){return $.fluent(this,arguments,()=>this.dom.value,()=>$.set(this.dom,"value",t,e=>{this.on("input",()=>{e.attributes.has(this.dom)!==!1&&(typeof e.value=="string"&&e.set(`${this.value()}`),typeof e.value=="number"&&e.set(Number(this.value())))})}))}get labels(){return Array.from(this.dom.labels??[]).map(t=>$(t))}}_.mixin(Tt,L.create("checkValidity","reportValidity","autocomplete","name","form","required","disabled","validationMessage","validity","willValidate"));class It extends f{constructor(t){super("option",t)}defaultSelected(t){return $.fluent(this,arguments,()=>this.dom.defaultSelected,()=>$.set(this.dom,"defaultSelected",t))}selected(t){return $.fluent(this,arguments,()=>this.dom.selected,()=>$.set(this.dom,"selected",t))}text(t){return $.fluent(this,arguments,()=>this.dom.text,()=>$.set(this.dom,"text",t))}value(t){return $.fluent(this,arguments,()=>this.dom.value,()=>$.set(this.dom,"value",t))}get form(){return this.dom.form?$(this.dom.form):null}get index(){return this.dom.index}}_.mixin(It,L.create("form","disabled","label"));class _t extends f{constructor(t){super("optgroup",t)}disabled(t){return $.fluent(this,arguments,()=>this.dom.disabled,()=>$.set(this.dom,"disabled",t))}}_.mixin(_t,L.create("disabled","label"));class Ot extends f{constructor(t){super("textarea",t)}cols(t){return $.fluent(this,arguments,()=>this.dom.cols,()=>$.set(this.dom,"cols",t))}wrap(t){return $.fluent(this,arguments,()=>this.dom.wrap,()=>$.set(this.dom,"wrap",t))}value(t){return $.fluent(this,arguments,()=>this.dom.value,()=>$.set(this.dom,"value",t))}defaultValue(t){return $.fluent(this,arguments,()=>this.dom.defaultValue,()=>$.set(this.dom,"defaultValue",t))}dirName(t){return $.fluent(this,arguments,()=>this.dom.dirName,()=>$.set(this.dom,"dirName",t))}placeholder(t){return $.fluent(this,arguments,()=>this.dom.placeholder,()=>$.set(this.dom,"placeholder",t))}readOnly(t){return $.fluent(this,arguments,()=>this.dom.readOnly,()=>$.set(this.dom,"readOnly",t))}selectionDirection(t){return $.fluent(this,arguments,()=>this.dom.selectionDirection,()=>$.set(this.dom,"selectionDirection",t))}selectionEnd(t){return $.fluent(this,arguments,()=>this.dom.selectionEnd,()=>$.set(this.dom,"selectionEnd",t))}selectionStart(t){return $.fluent(this,arguments,()=>this.dom.selectionStart,()=>$.set(this.dom,"selectionStart",t))}type(t){return $.fluent(this,arguments,()=>this.dom.type,()=>$.set(this.dom,"type",t))}inputMode(t){return $.fluent(this,arguments,()=>this.dom.inputMode,()=>$.set(this.dom,"inputMode",t))}select(){return this.dom.select(),this}setCustomValidity(t){return this.dom.setCustomValidity(t),this}setRangeText(t,e,s,i){return typeof e=="number"&&typeof s=="number"&&this.dom.setRangeText(t,e,s,i),this.dom.setRangeText(t),this}setSelectionRange(t,e,s){return this.dom.setSelectionRange(t,e,s),this}get labels(){return Array.from(this.dom.labels??[]).map(t=>$(t))}}_.mixin(Ot,L.create("checkValidity","reportValidity","autocomplete","name","form","required","disabled","minLength","maxLength","validationMessage","validity","willValidate"));var J;class xt extends f{constructor(e){super("async",e);G(this,J,!1)}await(e){return e instanceof Function?e(this).then(s=>this._loaded(s)):e.then(s=>this._loaded(s)),this}_loaded(e){if(B(this,J,!0),typeof e=="string")this.replace(new q(e));else if(e instanceof I){const s=new q(e.toString());e.use(s,"content"),this.replace(s)}else e==null?this.replace(new q(String(e))):this.replace(e);this.dom.dispatchEvent(new Event("load"))}get loaded(){return x(this,J)}}J=new WeakMap;const Gt=Object.freeze(Object.defineProperty({__proto__:null,$Async:xt},Symbol.toStringTag,{value:"Module"}));class Bt extends P{constructor(t,e){super(t,e)}autoplay(t){return $.fluent(this,arguments,()=>this.dom.autoplay,()=>$.set(this.dom,"autoplay",t))}get buffered(){return this.dom.buffered}controls(t){return $.fluent(this,arguments,()=>this.dom.controls,()=>$.set(this.dom,"controls",t))}crossOrigin(t){return $.fluent(this,arguments,()=>this.dom.crossOrigin,()=>$.set(this.dom,"crossOrigin",t))}get currentSrc(){return this.dom.currentSrc}currentTime(t){return $.fluent(this,arguments,()=>this.dom.currentTime,()=>$.set(this.dom,"currentTime",t))}defaultMuted(t){return $.fluent(this,arguments,()=>this.dom.defaultMuted,()=>$.set(this.dom,"defaultMuted",t))}defaultPlaybackRate(t){return $.fluent(this,arguments,()=>this.dom.defaultPlaybackRate,()=>$.set(this.dom,"defaultPlaybackRate",t))}disableRemotePlayback(t){return $.fluent(this,arguments,()=>this.dom.disableRemotePlayback,()=>$.set(this.dom,"disableRemotePlayback",t))}get duration(){return this.dom.duration}get ended(){return this.dom.ended}get error(){return this.dom.error}loop(t){return $.fluent(this,arguments,()=>this.dom.loop,()=>$.set(this.dom,"loop",t))}mediaKeys(t){return $.fluent(this,arguments,()=>this.dom.mediaKeys,()=>$.set(this.dom,"setMediaKeys",[t]))}muted(t){return $.fluent(this,arguments,()=>this.dom.muted,()=>$.set(this.dom,"muted",t))}get networkState(){return this.dom.networkState}get paused(){return this.dom.paused}playbackRate(t){return $.fluent(this,arguments,()=>this.dom.playbackRate,()=>$.set(this.dom,"playbackRate",t))}get played(){return this.dom.played}preload(t){return $.fluent(this,arguments,()=>this.dom.preload,()=>$.set(this.dom,"preload",t))}preservesPitch(t){return $.fluent(this,arguments,()=>this.dom.preservesPitch,()=>$.set(this.dom,"preservesPitch",t))}get readyState(){return this.dom.readyState}get remote(){return this.dom.remote}get seekable(){return this.dom.seekable}get seeking(){return this.dom.seeking}sinkId(t){return $.fluent(this,arguments,()=>this.dom.sinkId,()=>$.set(this.dom,"setSinkId",[t]))}src(t){return $.fluent(this,arguments,()=>this.dom.src,()=>$.set(this.dom,"src",t))}srcObject(t){return $.fluent(this,arguments,()=>this.dom.srcObject,()=>$.set(this.dom,"srcObject",t))}get textTracks(){return this.dom.textTracks}volume(t){return $.fluent(this,arguments,()=>this.dom.volume,()=>$.set(this.dom,"volume",t))}addTextTrack(t,e,s){return this.dom.addTextTrack(t,e,s)}canPlayType(t){return this.dom.canPlayType(t)}fastSeek(t){return this.dom.fastSeek(t),this}load(){return this.dom.load(),this}pause(){return this.dom.pause(),this}async play(){return await this.dom.play(),this}get isPlaying(){return this.currentTime()>0&&!this.paused&&!this.ended&&this.readyState>2}}class Xt extends Bt{constructor(t){super("video",t)}disablePictureInPicture(t){return $.fluent(this,arguments,()=>this.dom.disablePictureInPicture,()=>$.set(this.dom,"disablePictureInPicture",t))}height(t){return $.fluent(this,arguments,()=>this.dom.height,()=>$.set(this.dom,"height",t))}width(t){return $.fluent(this,arguments,()=>this.dom.width,()=>$.set(this.dom,"width",t))}playsInline(t){return $.fluent(this,arguments,()=>this.dom.playsInline,()=>$.set(this.dom,"playsInline",t))}poster(t){return $.fluent(this,arguments,()=>this.dom.poster,()=>$.set(this.dom,"poster",t))}get videoHeight(){return this.dom.videoHeight}get videoWidth(){return this.dom.videoWidth}cancelVideoFrameCallback(t){return this.dom.cancelVideoFrameCallback(t),this}getVideoPlaybackQuality(){return this.dom.getVideoPlaybackQuality()}requestPictureInPicture(){return this.dom.requestPictureInPicture()}requestVideoFrameCallback(t){return this.dom.requestVideoFrameCallback(t)}}function T(r){if(typeof r>"u"||r===null||r instanceof F)return r;if(typeof r=="string"){if(r.startsWith("::"))return Array.from(document.querySelectorAll(r.replace(/^::/,""))).map(t=>T(t));if(r.startsWith(":"))return T(document.querySelector(r.replace(/^:/,"")));if(r in T.TagNameElementMap){const t=T.TagNameElementMap[r];return t===Y?new Y(r):t===f?new f(r):new t}else return new f(r)}if(r instanceof Node)return r.$?r.$:_.from(r);throw`$: NOT SUPPORT TARGET ELEMENT TYPE ('${r}')`}(r=>{r.anchorHandler=null,r.TagNameElementMap={html:f,head:f,document:K,body:f,a:Ht,p:f,pre:f,code:f,blockquote:f,strong:f,h1:f,h2:f,h3:f,h4:f,h5:f,h6:f,div:f,ol:f,ul:f,dl:f,li:f,input:C,label:St,button:wt,form:bt,img:Ut,dialog:zt,canvas:qt,select:Tt,option:It,optgroup:_t,textarea:Ot,video:Xt,async:xt};function t(l,d,y,S){return d.length?(S(),l):y()}r.fluent=t;function e(l){return l instanceof Array?l:[l]}r.orArrayResolve=e;function s(l,d){return _.mixin(l,d)}r.mixin=s;function i(l,d,y,S){if(y!==void 0){if(y instanceof I){y.use(l,d),l[d]instanceof Function?l[d](...y.value):l[d]=y.value,S&&S(y);return}l[d]instanceof Function?l[d](...y):l[d]=y}}r.set=i;function n(l,d){return new I(l,d)}r.state=n;async function a(l,d){return new Promise(y=>{const S=new FileReader;S.onload=O=>{const w=r("img");if(w.once("load",A=>{const V=r("canvas"),j=V.getContext("2d"),R=w.height()/w.width(),[M,U]=[R>1?d/R:d,R>1?d:d*R];V.height(U).width(M),j==null||j.drawImage(w.dom,0,0,M,U),y(V.toDataURL(l.type))}),!O.target)throw"$.resize(): e.target is null";w.src(O.target.result)},S.readAsDataURL(l)})}r.resize=a;function o(l=1){return parseInt(getComputedStyle(document.documentElement).fontSize)*l}r.rem=o;function u(l){const d=new DOMParser().parseFromString(l,"text/html").body;return Array.from(d.children).map(y=>r(y))}r.html=u;function h(l,d,y){if(typeof d=="number")return Array(d).fill("").map(O=>{const w=S(l)?l[0](...l.slice(1)):r(l);return y&&y(w),w});{const O=[];for(const w of d){const A=l instanceof Function?l(...w):S(l)?l[0](...l.slice(1)):r(l);w instanceof Function?w(A):(w instanceof F||typeof w=="string")&&A.content(w),O.push(A)}return O}function S(O){return O instanceof Array&&O[0]instanceof Function}}r.builder=h;function m(l,d){return Object.assign(r.TagNameElementMap,{[l]:d}),r.TagNameElementMap}r.registerTagName=m;function p(...l){return new Q}r.events=p;function b(l){return l()}r.call=b})(T||(T={}));globalThis.$=T;class Q{constructor(){c(this,"eventMap",new Map)}fire(t,...e){var s;return(s=this.eventMap.get(t))==null||s.forEach(i=>i(...e)),this}on(t,e){const s=this.eventMap.get(t)??this.eventMap.set(t,new Set).get(t);return s==null||s.add(e),this}off(t,e){var s;return(s=this.eventMap.get(t))==null||s.delete(e),this}once(t,e){const s=(...n)=>{var a;(a=this.eventMap.get(t))==null||a.delete(s),e(...n)},i=this.eventMap.get(t)??this.eventMap.set(t,new Set).get(t);return i==null||i.add(s),this}}Array.prototype.detype=function(...r){return this.filter(t=>{if(r.length)for(const e of r)return typeof t!=typeof e;else return t!==void 0})};Object.defineProperties(Set.prototype,{array:{get:function(){return Array.from(this)}}});Set.prototype.sort=function(r){return this.array.sort(r)};class Et extends f{constructor(e){super("layout",e);c(this,"_property",{ROW_MAX_HEIGHT:200,GAP:0,IS_RENDERING:!1,RENDER_REQUEST:!1,COLUNM:1,TYPE:"justified",ROOT:null,ITEM_PROPERTIES:new Map});this.css({display:"block",position:"relative"}),new ResizeObserver(s=>{this.inDOM()&&(this.render(),this.dom.dispatchEvent(new Event("resize")))}).observe(this.dom),document.addEventListener("scroll",s=>{s.target===this.root().dom&&this.scrollCompute()}),new IntersectionObserver(s=>{this.inDOM()&&this.render()}).observe(this.dom)}type(e){return $.fluent(this,arguments,()=>this._property.TYPE,()=>$.set(this._property,"TYPE",e))}maxHeight(e){return $.fluent(this,arguments,()=>this._property.ROW_MAX_HEIGHT,()=>$.set(this._property,"ROW_MAX_HEIGHT",e))}column(e){return $.fluent(this,arguments,()=>this._property.COLUNM,()=>$.set(this._property,"COLUNM",e))}gap(e){return $.fluent(this,arguments,()=>this._property.GAP,()=>$.set(this._property,"GAP",e))}root(e){return $.fluent(this,arguments,()=>this._property.ROOT??$(document),()=>$.set(this._property,"ROOT",e))}get COL_WIDTH(){return(this.offsetWidth-this._property.GAP*(this._property.COLUNM-1))/this._property.COLUNM}justifiedCompute(){const e=[],s=this.offsetWidth;for(const i of this.children.array){const n=$(i);if(!(n instanceof P))continue;const a=n.attribute("layout-item-ratio"),o=a?parseFloat(a):n.dom.offsetWidth/n.dom.offsetHeight,u={$node:n,ratio:o};let h=e.at(-1);(!h||h.heightm+=l.ratio);const p=m+o,b=(s-this._property.GAP*h.items.length)/p;h.items.push(u),h.ratio=p,h.height=b}return e}waterfallCompute(){const e=[],s=this.COL_WIDTH,i=()=>{if(e.lengthn.height-a.height)[0]};for(const n of this.children.array){const a=$(n);if(!(a instanceof P))continue;const o=a.attribute("layout-item-ratio"),u=o?parseFloat(o):a.dom.offsetWidth/a.dom.offsetHeight,h={$node:a,ratio:u},m=i();let p=0;m.items.forEach(d=>p+=d.ratio);const b=s/(m.height+s/u),l=s/b;m.items.push(h),m.ratio=b,m.height=l}return e}render(){if(!this.inDOM())return this;if(this._property.ITEM_PROPERTIES.clear(),this._property.TYPE==="justified"){const e=this.justifiedCompute();let s=0;for(const i of e){let n=0;i.height>this._property.ROW_MAX_HEIGHT&&(i.height=this._property.ROW_MAX_HEIGHT);for(const a of i.items){const o=a.ratio*i.height;a.$node.css({position:"absolute",height:`${i.height}px`,width:`${o}px`,top:`${s}px`,left:`${n}px`}),a.$node.attribute("layout-item-ratio",a.ratio),this._property.ITEM_PROPERTIES.set(a.$node,{height:i.height,width:o,top:s,left:n,ratio:a.ratio,$node:a.$node}),n+=i.height*a.ratio+this._property.GAP}s+=i.height+this._property.GAP}this.css({height:`${s}px`})}else if(this._property.TYPE="waterfall"){const e=this.waterfallCompute(),s=this.COL_WIDTH;let i=0;for(const n of e){let a=0;for(const o of n.items){const u=s/o.ratio;o.$node.css({position:"absolute",height:`${u}px`,width:`${s}px`,top:`${a}px`,left:`${i}px`}),o.$node.attribute("layout-item-ratio",o.ratio),this._property.ITEM_PROPERTIES.set(o.$node,{height:u,width:s,top:a,left:i,ratio:o.ratio,$node:o.$node}),a+=u+this._property.GAP}i+=s+this._property.GAP}if(e.length){const n=e.sort((a,o)=>o.height-a.height)[0];this.css({height:`${n.height+n.items.length*this._property.GAP}px`})}else this.css({height:""})}return this.scrollCompute(),this}scrollCompute(){if(this.inDOM()===!1)return;const e=document.documentElement.scrollTop-this.dom.offsetTop;this._property.ITEM_PROPERTIES.forEach((s,i)=>{const n=s.top;s.top+s.height>e&&n{this.contentId=e,this.events.fire("rendered",{$view:this,previousContent:i,nextContent:s})},o=()=>{this.events.fire("afterSwitch",{$view:this,previousId:e})};return this.events.fire("beforeSwitch",{$view:this,preventDefault:()=>n=!0,targetId:e,previousContent:i,nextContent:s,switched:o,rendered:a}),n||(this.content(s),a(),o()),this}get currentContent(){if(this.contentId)return this.viewCache.get(this.contentId)}}$.registerTagName("view",Lt);var D;const g=class g extends Lt{constructor(e){super({tagname:"router",...e});G(this,D,"");c(this,"routes",new Map);g.routers.add(this)}base(e){return $.fluent(this,arguments,()=>x(this,D),()=>{B(this,D,e??x(this,D))})}map(e){e=$.orArrayResolve(e);for(const s of e)s instanceof Array?this.map(s):this.routes.set(s.path(),s);return this.resolve(),this}resolve(){return new Promise(e=>{if(!location.pathname.startsWith(x(this,D)))return e(2);const s=location.pathname.replace(x(this,D),"/").replace("//","/"),i=s.split("/").map(d=>`/${d}`),n=location.search,a=new Map(n.replace("?","").split("&").map(d=>d.split("="))),u=(()=>{const d=[];for(const[y,S]of this.routes){const O=$.orArrayResolve(y);for(const w of O){let A=0,V={},j={};const R=w.split("/").map(M=>`/${M}`);if(!(i.lengthObject.assign(j,{[lt]:a.get(lt)})),U.startsWith("/:")){A++,Object.assign(V,{[U.replace("/:","")]:i[M].replace("/","")});continue}else if(w.startsWith("#")&&w===location.hash){A++;continue}else if(U===i[M]){A++;continue}else break}d.push({deep:A,$route:S,params:V,query:j,pathId:Object.keys(j).length!==0?s+n:Object.keys(V).length!==0?s:O[0]})}}}return d.sort((y,S)=>S.deep-y.deep).at(0)})();if(!u)return e(1);const{$route:h,params:m,pathId:p,query:b}=u;if(p===this.contentId)return e(0);this.events.once("rendered",({nextContent:d,previousContent:y})=>{y==null||y.events.fire("afterShift",{$route:y}),d.events.fire("rendered",{$route:d}),e(0)});const l=this.viewCache.get(p)??h.build({params:m,query:b});this.viewCache.has(p)||this.setView(p,l),this.events.once("beforeSwitch",()=>{var d;l.events.fire("beforeShift",{$route:l}),(d=this.currentContent)==null||d.events.fire("beforeShift",{$route:this.currentContent})}),this.events.once("afterSwitch",()=>l.events.fire("afterShift",{$route:l})),this.switchView(p)})}static init(){if(!history.state||!("index"in history.state)){const e={index:g.historyIndex};history.replaceState(e,"")}else g.historyIndex=history.state.index;return g.navigationDirection=0,g.resolve(),window.addEventListener("popstate",()=>g.popstate()),window.addEventListener("scroll",()=>{this.setScrollHistory(this.historyIndex,location.href,document.documentElement.scrollTop)}),history.scrollRestoration="manual",this}static open(e,s){return e===void 0?this:(e=this.urlResolver(e),e.href===this.url.href?this:e.origin!==this.url.origin?(window.open(e,s),this):(g.clearForwardScrollHistory(),g.historyIndex++,history.pushState(g.historyState,"",e),g.stateChange(0),g.resolve(),this))}static back(){return this.historyIndex--,history.back(),this.stateChange(1),this}static replace(e){return e===void 0?this:(e=this.urlResolver(e),console.debug(e.href),history.replaceState(g.historyState,"",e),this.stateChange(2),g.resolve(),this)}static urlResolver(e){return e instanceof URL?e:(e.startsWith("/")&&(e=`${location.origin}${e}`),e.startsWith("#")&&(e=`${location.origin}${location.pathname}${e}`),new URL(e))}static popstate(){const e=history.state.index>g.historyIndex?0:history.state.indexe.resolve())),this.scrollRestoration(),this.setScrollHistory(this.historyIndex,location.href,document.documentElement.scrollTop)}static get historyState(){return{index:g.historyIndex}}static stateChange(e){const s=this.url,i=new URL(location.href);this.url=i,g.events.fire("stateChange",{beforeURL:s,afterURL:i,direction:e}),g.navigationDirection=e}static setScrollHistory(e,s,i){const n=this.getScrollHistory();if(!n)return sessionStorage.setItem(this.scrollHistoryKey,JSON.stringify({[e]:{url:s,value:i}}));n[e]={url:s,value:i},sessionStorage.setItem(this.scrollHistoryKey,JSON.stringify(n))}static getScrollHistory(){const e=sessionStorage.getItem(this.scrollHistoryKey);if(e)return JSON.parse(e)}static clearForwardScrollHistory(){const e=this.getScrollHistory();if(e)for(const s in e)Number(s)>this.historyIndex&&delete e[s],sessionStorage.setItem(this.scrollHistoryKey,JSON.stringify(e))}static scrollRestoration(){const e=this.getScrollHistory();if(e&&e[this.historyIndex])document.documentElement.scrollTop=e[this.historyIndex].value??0;else if(location.hash.length){const s=$(document.body).$(location.hash);s&&(document.documentElement.scrollTop=s.dom.offsetTop)}else document.documentElement.scrollTop=0}};D=new WeakMap,c(g,"routers",new Set),c(g,"events",new Q),c(g,"navigationDirection"),c(g,"historyIndex",0),c(g,"url",new URL(location.href)),c(g,"scrollHistoryKey","$ROUTER_SCROLL_HISTORY");let E=g;var it=(r=>(r[r.Forward=0]="Forward",r[r.Back=1]="Back",r[r.Replace=2]="Replace",r))(it||{});E.init();var z,H;const ut=class ut extends f{constructor(e){super("route",e);G(this,z,"");G(this,H);c(this,"rendered",!1)}path(e){return $.fluent(this,arguments,()=>x(this,z),()=>B(this,z,e??x(this,z)))}builder(e){return B(this,H,e),this}render(e){return x(this,H)&&this.content(x(this,H).call(this,{$route:this,params:e.params,query:e.query})),this.rendered=!0,this}build(e){return new ut({dom:this.dom.cloneNode()}).self(s=>{x(this,H)&&s.builder(x(this,H)).render({params:e.params,query:e.query})})}};z=new WeakMap,H=new WeakMap;let rt=ut;$.registerTagName("router",E);$.registerTagName("route",rt);Object.assign($,{open(r,t){return E.open(r,t)},replace(r){return E.replace(r)},back(){return E.back()}});const N=class N{constructor(t){c(this,"posts",new Map);c(this,"tags",new Map);Object.assign(this,t),this.origin.endsWith("/")&&(this.origin=this.origin.slice(0,-1)),N.manager.set(this.name,this)}static set(t){return this.used=t,this.name$.set(t.name),this.storageAPI=t.name,this.events.fire("set"),this}static get storageAPI(){return localStorage.getItem("booru_api")}static set storageAPI(t){t?localStorage.setItem("booru_api",t):localStorage.removeItem("booru_api")}};c(N,"used"),c(N,"events",new Q),c(N,"name$",$.state(N.name)),c(N,"manager",new Map);let v=N;const Kt=new Intl.NumberFormat("en",{notation:"compact"});class Mt{constructor(t,e){c(this,"post_count$",$.state(0,{format:t=>`${Kt.format(t)}`}));c(this,"name$",$.state(""));c(this,"booru");this.booru=t,Object.assign(this,e),this.$update()}static async fetch(t,e){var n;const s=await fetch(`${t.origin}/tags/${e}.json`).then(async a=>await a.json()),i=((n=t.tags.get(s.id))==null?void 0:n.update(s))??new this(t,s);return t.tags.set(i.id,i),i}static async fetchMultiple(t,e,s=1e3){let i="";if(e)for(const[u,h]of Object.entries(e))if(h instanceof Array)i+=`&search[${u}]=${h}`;else if(h instanceof Object)for(const[m,p]of Object.entries(h))i+=`&search[${u}${m}]=${p}`;else i+=`&search[${u}]=${h}`;return(await(await fetch(`${t.origin}/tags.json?limit=${s}${i}`)).json()).map(u=>{var m;const h=((m=t.tags.get(u.id))==null?void 0:m.update(u))??new this(t,u);return t.tags.set(h.id,h),h})}update(t){return Object.assign(this,t),this.$update(),this}$update(){this.post_count$.set(this.post_count),this.name$.set(this.name)}}var W=(r=>(r[r.General=0]="General",r[r.Artist=1]="Artist",r[r.Copyright=3]="Copyright",r[r.Character=4]="Character",r[r.Meta=5]="Meta",r))(W||{});class X{constructor(t){c(this,"name$",$.state("loding..."));Object.assign(this,t),this.update$()}static async fetch(t,e){var n;const s=await fetch(`${t.origin}/users/${e}.json`).then(async a=>await a.json()),i=((n=this.manager.get(s.id))==null?void 0:n.update(s))??new this(s);return this.manager.set(i.id,i),i}static async fetchMultiple(t,e,s=200){let i="";if(e)for(const[u,h]of Object.entries(e))if(h instanceof Array)i+=`&search[${u}]=${h}`;else if(h instanceof Object)for(const[m,p]of Object.entries(h))i+=`&search[${u}${m}]=${p}`;else i+=`&search[${u}]=${h}`;return(await(await fetch(`${t.origin}/users.json?limit=${s}${i}`)).json()).map(u=>{const h=new this(u);return this.manager.set(h.id,h),h})}update(t){return Object.assign(this,t),this.update$(),this}update$(){this.name$.set(this.name)}}c(X,"manager",new Map);const at=1e3,tt=at*60,et=tt*60,st=et*24,Yt=st*7,Z=new Intl.RelativeTimeFormat("en",{style:"long"});function Jt(r){r=Math.floor(r);const t=r/at,e=r/tt,s=r/et,i=r/st,n=r%1e3,a=Math.floor(r%6e4/1e3),o=Math.floor(r%36e5/6e4),u=Math.floor(r%(36e5*24)/36e5),h=a.toString().padStart(2,"0"),m=o.toString().padStart(2,"0"),p=u.toString().padStart(2,"0");return{seconds:t,minutes:e,hours:s,days:i,mil:n,s:a,min:o,h:u,ss:h,mm:m,hh:p}}function Qt(r,t=Date.now()){const e=r-t,s=Math.abs(e);if(sawait s.json());return this.update(e),X.fetchMultiple(this.booru,{id:[this.uploader_id,this.approver_id].detype(null)}).then(()=>this.update$()),this}static async fetchMultiple(e,s,i=20){let n="";if(s){if(typeof s=="string")n=s;else for(const[m,p]of Object.entries(s))if(p!==void 0){if(m==="tags"){n+=`${p}`;continue}n.at(-1)!=="="&&(n+=" "),n+=`${m}:${p}`}}const o=await(await fetch(`${e.origin}/posts.json?limit=${i}&tags=${n}&_method=get`)).json(),u=o.map(m=>{var b;const p=((b=e.posts.get(m.id))==null?void 0:b.update(m))??new this(e,m.id);return p.update(m),e.posts.set(p.id,p),p});if(!u.length)return u;const h=[...new Set(o.map(m=>[m.approver_id,m.uploader_id].detype(null)).flat())];return X.fetchMultiple(e,{id:h}).then(()=>u.forEach(m=>m.update$())),u}update$(){var e,s,i,n;this.uploader$.set(((e=this.uploader)==null?void 0:e.name$)??((s=this.uploader_id)==null?void 0:s.toString())),this.approver$.set(((i=this.approver)==null?void 0:i.name$)??((n=this.approver_id)==null?void 0:n.toString())??"None"),this.created_date$.set(Qt(+new Date(this.created_at))),this.favorites$.set(this.fav_count),this.score$.set(this.score),this.file_size$.set(Zt(this.file_size)),this.file_ext$.set(this.file_ext),this.dimension$.set(`${this.image_width}x${this.image_height}`),this.createdDate=new Date(this.created_at),this.fire("update")}update(e){return Object.assign(this,e),this.update$(),this}async fetchTags(){return await Mt.fetchMultiple(this.booru,{name:{_space:this.tag_string}})}get pathname(){return`/posts/${this.id}`}get uploader(){return X.manager.get(this.uploader_id)}get approver(){return this.approver_id?X.manager.get(this.approver_id):null}get isVideo(){return this.file_ext==="mp4"||this.file_ext==="webm"||this.file_ext==="zip"}get tags(){const e=this.tag_string.split(" ");return[...this.booru.tags.values()].filter(s=>e.includes(s.name))}}class Pt{constructor(t){Object.assign(this,t)}static async fetch(t,e){const s=await fetch(`${t.origin}/artist_commentaries/${e}.json`);return new this(await s.json())}static async fetchMultiple(t,e,s=200){let i="";if(e)for(const[u,h]of Object.entries(e))if(h instanceof Array)i+=`&search[${u}]=${h}`;else if(h instanceof Object)for(const[m,p]of Object.entries(h))i+=`&search[${u}${m}]=${p}`;else i+=`&search[${u}]=${h}`;return(await(await fetch(`${t.origin}/artist_commentaries.json?limit=${s}${i}`)).json()).map(u=>{const h=new this(u);return this.manager.set(h.id,h),h})}}c(Pt,"manager",new Map);const te=$("route").path("/posts/:id").id("post").builder(({$route:r,params:t})=>{if(!Number(t.id))return"404";const e=new nt(v.used,+t.id),s={$viewer:$("div").class("viewer"),$tags:$("div").class("post-tags"),$commentary:$("section").class("commentary")};i();async function i(){await e.fetch(),s.$viewer.content([e.isVideo?$("video").src(e.file_ext==="zip"?e.large_file_url:e.file_url).controls(!0):$("img").src(e.large_file_url)]),n(),a();async function n(){const o=await e.fetchTags(),[u,h,m,p,b]=[o.filter(d=>d.category===W.Artist),o.filter(d=>d.category===W.Character),o.filter(d=>d.category===W.General),o.filter(d=>d.category===W.Meta),o.filter(d=>d.category===W.Copyright)];s.$tags.content([l("Artist",u),l("Character",h),l("Copyright",b),l("Meta",p),l("General",m)]);function l(d,y){return y.length?[$("h3").content(d),$("section").content([y.map(S=>$("div").class("tag").content([$("a").class("tag-name").content(S.name).href(`/posts?tags=${S.name}`),$("span").class("tag-post-count").content(S.post_count$)]))])]:null}}async function a(){const o=(await Pt.fetchMultiple(v.used,{post:{_id:e.id}})).at(0);if(!o)return s.$commentary.content("No commentary");s.$commentary.content([o.original_title?$("h3").content(o.original_title):null,$("pre").content(o.original_description)])}}return[s.$viewer,$("div").class("content").content([$("h3").content("Artist's Commentary"),s.$commentary.content("loading...")]),$("div").class("sidebar").content([$("section").class("post-info").content([new k("id").name("Post").value(`#${t.id}`),new k("uploader").name("Uploader").value(e.uploader$),new k("approver").name("Approver").value(e.approver$),new k("date").name("Date").value(e.created_date$),new k("size").name("Size").value([e.file_size$,e.dimension$]),new k("file").name("File Type").value(e.file_ext$),$("div").class("inline").content([new k("favorites").name("Favorites").value(e.favorites$),new k("score").name("Score").value(e.score$)]),$("button").content("Copy link").on("click",(n,a)=>{n.preventDefault(),navigator.clipboard.writeText(`${v.used.origin}${location.pathname}`),a.content("Copied!"),setTimeout(()=>{a.content("Copy link")},2e3)})]),s.$tags.content("loading...")]).self(n=>{let a=0;addEventListener("scroll",()=>{n.inDOM()&&(a=document.documentElement.scrollTop)}),r.on("beforeShift",()=>{innerWidth>800&&n.css({position:"absolute",top:`${a}px`})}).on("afterShift",()=>n.css({position:"",top:""}))})]});class k extends f{constructor(e){super("div");c(this,"$name",$("span").class("property-name"));c(this,"$values",$("div").class("property-values"));this.staticClass("property").attribute("property-id",e),this.content([this.$name,this.$values])}name(e){return this.$name.content(e),this}value(e){const s=$.orArrayResolve(e);return this.$values.content(s.map(i=>$("span").staticClass("property-value").content(i))),this}}class ee extends f{constructor(e){super("post-tile");c(this,"post");c(this,"$video");c(this,"duration$",$.state(""));this.post=e,this.$video=this.post.isVideo?$("video").width(this.post.image_width).height(this.post.image_height).disablePictureInPicture(!0).loop(!0).muted(!0).hide(!0):null,this.attribute("filetype",this.post.file_ext),this.durationUpdate(),this.build()}build(){var s,i;let e;(s=this.$video)==null||s.on("playing",(n,a)=>{e=setInterval(()=>{this.durationUpdate()},500)}),(i=this.$video)==null||i.on("pause",()=>{clearInterval(e),this.durationUpdate()}),this.content([this.post.isVideo?$("span").class("duration").content(this.duration$):null,$("a").href(this.post.pathname).content(()=>[this.$video,$("img").css({opacity:"0"}).width(this.post.image_width).height(this.post.image_height).src(this.post.preview_file_url).loading("lazy").once("load",(n,a)=>{this.post.isVideo||a.src(this.post.large_file_url),a.animate({opacity:[0,1]},{duration:300,fill:"both"})})]).on("mouseenter",()=>{var n,a;(n=this.$video)!=null&&n.isPlaying||(a=this.$video)==null||a.src(this.post.large_file_url).hide(!1).play().catch(o=>{})}).on("mouseleave",()=>{var n;(n=this.$video)==null||n.pause().currentTime(0).hide(!0)})])}durationUpdate(){if(!this.$video)return;const e=Jt(this.post.media_asset.duration*1e3-this.$video.currentTime()*1e3);this.duration$.set(`${e.hh}:${e.mm}:${e.ss}`)}}class pt extends Et{constructor(e){super();c(this,"posts",new Set);c(this,"$posts",new Set);c(this,"tags");this.tags=e==null?void 0:e.tags,this.addStaticClass("post-grid"),this.type("waterfall").gap(10),this.init()}async init(){setInterval(()=>{this.inDOM()&&document.documentElement.scrollTop===0&&this.updateNewest()},1e4),v.events.on("set",()=>{this.removeAll()}),this.on("resize",()=>this.resize()),this.loader()}async loader(){if(!this.inDOM())return setTimeout(()=>this.loader(),100);for(;this.inDOM()&&document.documentElement.scrollHeight<=innerHeight*2;)if(await this.getPosts(),!this.posts.size)return;document.documentElement.scrollTop+innerHeight>document.documentElement.scrollHeight-innerHeight*2&&await this.getPosts(),setTimeout(()=>this.loader(),100)}resize(){const e=Math.round(this.dom.clientWidth/300);this.column(e>=2?e:2)}addPost(e){e=$.orArrayResolve(e);for(const i of e){if(!i.file_url||this.posts.has(i))continue;const n=new ee(i);this.$posts.add(n),this.posts.add(i)}const s=[...this.$posts.values()].sort((i,n)=>+n.post.createdDate-+i.post.createdDate);return this.content(s).render(),this}removeAll(){return this.posts.clear(),this.$posts.clear(),this.animate({opacity:[1,0]},{duration:300,easing:"ease"},()=>this.clear().render()),this}async updateNewest(){const e=this.sortedPosts.at(0),s=await nt.fetchMultiple(v.used,{tags:this.tags,id:e?`>${e.id}`:void 0});return this.addPost(s),this}async getPosts(){const e=this.sortedPosts.at(-1),s=await nt.fetchMultiple(v.used,{tags:this.tags,id:e?`<${e.id}`:void 0});return this.addPost(s),this}get sortedPosts(){return this.posts.array.sort((e,s)=>+s.createdDate-+e.createdDate)}}class se extends f{constructor(){super("searchbar");c(this,"$tagInput",new ne(this));c(this,"$selectionList",new ie);c(this,"typingTimer",null);c(this,"$filter",$("div").class("filter"));this.build(),window.addEventListener("keyup",e=>{!this.inDOM()&&e.key==="/"&&this.activate(),this.inDOM()&&e.key==="Escape"&&this.inactivate()})}build(){this.content([$("div").class("input-container").content([this.$tagInput.on("input",()=>this.inputHandler()).on("keydown",e=>this.keyHandler(e)),$("ion-icon").name("close-circle-outline").title("Clear Input").on("click",()=>this.$tagInput.clearAll())]).on("click",e=>{e.target===this.$tagInput.dom&&this.$tagInput.addTag().input()}),$("div").class("selection-list-container").content([this.$selectionList]),this.$filter.on("click",()=>{location.hash==="#search"&&$.back()})])}activate(){return this.hide(!1),this.$filter.animate({opacity:[0,.5]},{duration:300,easing:"ease"}),this.$tagInput.input(),this}inactivate(){return this.animate({opacity:[.5,0]},{duration:300,easing:"ease"},()=>this.hide(!0)),this}keyHandler(e){var n,a;const s=()=>{e.preventDefault(),this.$tagInput.addTag().input()},i=o=>{const u=this.$tagInput.children.indexOf(this.$tagInput.$inputor),h=this.$tagInput.children.array.at(u+1);this.$tagInput.addTag(o.value()),h?this.$tagInput.editTag(h):this.$tagInput.input()};switch(e.key){case"ArrowUp":{e.preventDefault(),this.$selectionList.focusPrevSelection(),this.$tagInput.value((n=this.$selectionList.focused)==null?void 0:n.value());break}case"ArrowDown":{e.preventDefault(),this.$selectionList.focusNextSelection(),this.$tagInput.value((a=this.$selectionList.focused)==null?void 0:a.value());break}case" ":s();break;case"Enter":{e.preventDefault(),this.$selectionList.focused?i(this.$selectionList.focused):(this.$tagInput.addTag(),this.search());break}case"Tab":{e.preventDefault();const o=this.$tagInput.children.indexOf(this.$tagInput.$inputor);if(e.shiftKey){this.$tagInput.editTag(this.$tagInput.children.array.at(o-1));break}if(this.$selectionList.focused)i(this.$selectionList.focused);else{const u=this.$tagInput.children.array.at(o+1);u?this.$tagInput.editTag(u):this.$tagInput.addTag().input()}break}case"Backspace":{const o=this.$tagInput.children.indexOf(this.$tagInput.$inputor);o!==0&&!this.$tagInput.$input.value().length&&(e.preventDefault(),this.$tagInput.editTag(this.$tagInput.children.array.at(o-1)));break}}}inputHandler(){this.typingTimer&&(clearTimeout(this.typingTimer),this.typingTimer=null),this.typingTimer=setTimeout(async()=>{this.typingTimer=null,this.getSearchSuggestions()},200)}async getSearchSuggestions(){const e=this.$tagInput.$input.value();if(!e.length)return this.$selectionList.clearSelections();const s=await Mt.fetchMultiple(v.used,{fuzzy_name_matches:e,order:"similarity"});this.$selectionList.clearSelections().addSelections(s.map(i=>new re().value(i.name).content([$("span").class("tag-name").content(i.name),$("span").class("tag-category").content(W[i.category])]).on("click",()=>{this.$tagInput.addTag(i.name).input()}))),this.$tagInput.$input.value().length||this.$selectionList.clearSelections()}search(){return $.replace(`/posts?tags=${this.$tagInput.query}`),this.$tagInput.clearAll(),this.inactivate(),this}checkURL(e,s){e.hash==="#search"&&this.inactivate(),s.hash==="#search"&&this.activate()}}class ie extends f{constructor(){super("selection-list");c(this,"focused",null);c(this,"selections",new Set)}addSelections(e){e=$.orArrayResolve(e);for(const s of e)this.selections.add(s);return this.insert(e),this}clearSelections(){return this.focused=null,this.selections.clear(),this.clear(),this}focusSelection(e){return this.blurSelection(),this.focused=e,e.focus(),this}blurSelection(){var e;return(e=this.focused)==null||e.blur(),this.focused=null,this}focusNextSelection(){const e=this.selections.array,s=e.at(0);if(this.focused){const i=e.at(e.indexOf(this.focused)+1);i?this.focusSelection(i):s&&this.focusSelection(s)}else s&&this.focusSelection(s)}focusPrevSelection(){const e=this.selections.array;if(this.focused){const s=e.at(e.indexOf(this.focused)-1);s&&this.focusSelection(s)}else{const s=e.at(0);s&&this.focusSelection(s)}}}class re extends f{constructor(){super("selection");c(this,"property",{value:""})}value(e){return $.fluent(this,arguments,()=>this.property.value,()=>$.set(this.property,"value",e))}focus(){return this.addClass("active"),this}blur(){return this.removeClass("active"),this}}class ne extends f{constructor(e){super("tag-input");c(this,"$input",$("input").type("text"));c(this,"$sizer",$("span").class("sizer"));c(this,"$inputor",$("div").class("input-wrapper").content([this.$sizer,this.$input.on("input",()=>{this.$sizer.content(this.$input.value())})]));c(this,"tags",new Set);c(this,"$seachbar");this.$seachbar=e}input(){return this.insert(this.$inputor),this.$input.focus(),this.$input.value()?this.$seachbar.getSearchSuggestions():this.$seachbar.$selectionList.clearSelections(),this}addTag(e){if(e=e??this.$input.value(),!e.length)return this;const s=new oe(e);return s.on("click",()=>this.editTag(s)),this.tags.add(s),this.value(""),this.$inputor.replace(s),this}editTag(e){return this.addTag(),this.tags.delete(e),e.replace(this.$inputor),this.value(e.name),this.$input.focus(),this.$seachbar.getSearchSuggestions(),this}clearAll(){return this.value(""),this.tags.clear(),this.clear(),this}value(e){return e===void 0?this:(this.$input.value(e),this.$sizer.content(e),this)}get query(){return this.tags.array.map(e=>e.name).toString().replace(",","+")}}class oe extends f{constructor(e){super("tag");c(this,"name");this.name=e,this.build()}build(){this.content(this.name)}}class ae extends f{constructor(){super("ion-icon")}name(t){return this.attribute("name",t),this}size(t){return this.attribute("size",t),this}}$.registerTagName("ion-icon",ae);$.anchorHandler=r=>{$.open(r.href(),r.target())};const[ot,he]=[new v({origin:"https://danbooru.donmai.us",name:"Danbooru"}),new v({origin:"https://safebooru.donmai.us",name:"Safebooru"}),new v({origin:"https://testbooru.donmai.us",name:"Testbooru"})];v.set(v.manager.get(v.storageAPI??"")??ot);const ht=new se().hide(!0);location.hash==="#search"&&ht.activate();$(document.body).content([$("nav").content([$("div").class("title").content([$("a").class("booru-name").content([$("h1").content(v.name$)]).href("/"),$("a").class("version").target("_blank").content("v0.3.0").href("https://git.defaultkavy.com/defaultkavy/danbooru-viewer")]),$("div").class("searchbar").content(["Search in ",v.name$]).self(r=>E.events.on("stateChange",({beforeURL:t,afterURL:e})=>{t.hash==="#search"&&r.hide(!1),e.hash==="#search"&&r.hide(!0)})).on("click",()=>$.open(location.href+"#search")),$("div").class("buttons").content([$("ion-icon").class("search").name("search-outline").title("Search").self(r=>E.events.on("stateChange",({beforeURL:t,afterURL:e})=>{t.hash==="#search"&&r.hide(!1),e.hash==="#search"&&r.hide(!0)})).on("click",()=>$.open(location.href+"#search")),$("ion-icon").class("switch").name("swap-horizontal").title("Switch Booru").on("click",()=>{v.used===ot?v.set(he):v.set(ot)})])]),ht,$("router").base("/").map([$("route").id("posts").path("/").builder(()=>new pt),$("route").id("posts").path("/posts?tags").builder(({query:r})=>new pt({tags:r.tags})),te]).on("beforeSwitch",r=>{r.preventDefault();function e(){var i;r.$view.content(r.nextContent),r.rendered(),(i=r.nextContent.element)==null||i.class("animated").animate({opacity:[0,1],transform:E.navigationDirection===it.Forward?["translateX(40%)","translateX(0%)"]:["translateX(-40%)","translateX(0%)"]},{duration:300,easing:"ease"},()=>{var n;r.switched(),(n=r.nextContent.element)==null||n.removeClass("animated")})}function s(){var i,n;(n=(i=r.previousContent)==null?void 0:i.element)==null||n.class("animated").animate({opacity:[1,0],transform:E.navigationDirection===it.Forward?["translateX(0%)","translateX(-40%)"]:["translateX(0%)","translateX(40%)"]},{duration:300,easing:"ease"},()=>{var a,o;(o=(a=r.previousContent)==null?void 0:a.element)==null||o.removeClass("animated"),e()})}r.previousContent?s():e()})]);E.events.on("stateChange",({beforeURL:r,afterURL:t})=>{ht.checkURL(r,t)}); diff --git a/dist/assets/index-BXPCnb25.css b/dist/assets/index-BXPCnb25.css new file mode 100644 index 0000000..e6ba7ea --- /dev/null +++ b/dist/assets/index-BXPCnb25.css @@ -0,0 +1 @@ +post-tile{display:block;transition:.3s all ease;position:relative}post-tile[filetype=mp4] span.duration,post-tile[filetype=webm] span.duration,post-tile[filetype=zip] span.duration{background-color:#fff;color:#000}post-tile span.duration{position:absolute;background-color:#00000050;bottom:.3rem;right:.3rem;padding:2px 4px;border-radius:4px;font-size:12px;text-transform:uppercase;z-index:2}post-tile img{border-radius:10px;height:100%;width:100%;vertical-align:top}post-tile video{border-radius:10px;height:100%;width:100%;object-fit:cover;position:absolute;z-index:1}searchbar{display:flex;align-items:center;flex-direction:column;width:100%;z-index:200;position:fixed;height:100%}searchbar div.input-container{margin-top:.4rem;background-color:var(--background-color-light);border-radius:.4rem;font-size:1rem;width:500px;padding:.4rem;max-width:calc(100% - 2rem);box-sizing:border-box;z-index:201;display:flex;align-items:center;border:1px solid var(--secondary-color-dark)}searchbar div.input-container:focus-within{outline:none}searchbar div.input-container tag-input{display:flex;gap:.4rem;width:100%;overflow:hidden;padding-inline:.4rem;box-sizing:border-box;cursor:text}searchbar div.input-container tag-input tag{display:inline-block;padding:.2rem .4rem;background-color:var(--secondary-color-dark);color:var(--secondary-color);border-radius:.4rem;cursor:pointer}searchbar div.input-container ion-icon{font-size:20px;color:var(--secondary-color-dark);cursor:pointer}searchbar div.input-container ion-icon:hover{color:var(--secondary-color)}searchbar div.selection-list-container{overflow:hidden;border-radius:.4rem;background-color:var(--background-color);z-index:201;max-width:calc(100% - 2rem);width:500px}searchbar div.selection-list-container selection-list{display:block;max-height:40vh;overflow-y:scroll;overflow-x:hidden;position:relative}searchbar div.selection-list-container selection-list selection{display:flex;justify-content:space-between;align-items:center;padding:.4rem 1rem;cursor:pointer}searchbar div.selection-list-container selection-list selection:hover{background-color:color-mix(in srgb,var(--background-color-lighter) 50%,transparent)}searchbar div.selection-list-container selection-list selection.active{background-color:var(--background-color-lighter)}searchbar div.selection-list-container selection-list selection .tag-category{padding:.1rem .4rem;border-radius:.4rem;font-size:.9rem;background-color:var(--secondary-color-dark);color:var(--secondary-color)}searchbar div.filter{background-color:var(--background-color);opacity:.5;position:fixed;top:0;height:100%;width:100%;z-index:199}.input-wrapper{color:var(--primary-color);border:1px solid var(--secondary-color);border-radius:.4rem;position:relative;box-sizing:border-box;line-height:1em;font-size:14px;padding:4px 8px;display:inline-block;max-width:100%;text-overflow:ellipsis}.input-wrapper span.sizer{font-family:inherit;white-space:pre;height:1em;display:inline-block;font-size:inherit;line-height:inherit;box-sizing:border-box;position:relative;opacity:0;min-width:2px;-webkit-user-select:none;user-select:none;vertical-align:top}.input-wrapper input{height:100%;text-overflow:ellipsis;font-family:inherit;background:none;color:inherit;top:0;left:0;font-size:inherit;line-height:inherit;padding:inherit;position:absolute;box-sizing:border-box;width:100%;border:none;outline:none}#post{padding:0}#post section{background-color:#2f2f45;border-radius:20px;padding:20px}#post div.viewer{height:calc(100vh - 2rem - var(--nav-height));display:flex;justify-content:center;align-items:center;background-color:#000;border-radius:20px;overflow:hidden;width:calc(100vw - 300px - 4rem);margin:1rem}@media (max-width: 800px){#post div.viewer{width:100%;height:calc(100vh - var(--nav-height));border-radius:0;margin:0}}#post div.viewer img{max-width:100%;max-height:100%}#post div.viewer video{max-width:100%;max-height:100%;-webkit-user-drag:none;transition:all .3s ease}#post div.content{width:calc(100vw - 300px - 4rem);display:flex;flex-direction:column;padding:1rem;box-sizing:border-box}@media (max-width: 800px){#post div.content{width:100%}}#post div.content::-webkit-scrollbar{background-color:#000;width:4px}#post div.content::-webkit-scrollbar-thumb{background-color:#aeaeec;border-radius:2px}#post div.content>h3{padding-left:1rem;margin-block:1rem}#post div.content section.commentary *{text-wrap:wrap;word-break:break-word}#post div.sidebar{position:fixed;top:calc(var(--nav-height) + 1rem);right:1rem;display:flex;flex-direction:column;gap:.4rem;width:300px;overflow:scroll;overflow-x:hidden;height:calc(100vh - 2rem - var(--nav-height));border-radius:20px}@media (max-width: 800px){#post div.sidebar{position:static;width:100%;overflow:visible;height:100%;padding:1rem;box-sizing:border-box}}#post div.sidebar::-webkit-scrollbar{background-color:#000;width:0px}#post div.sidebar::-webkit-scrollbar-thumb{background-color:#aeaeec}#post div.sidebar h3{padding-left:1rem;margin-block:.6rem}#post div.sidebar .post-info{background-color:#2f2f45;border-radius:20px;padding:20px;display:flex;flex-direction:column;gap:.4rem}#post div.sidebar div.property{display:flex;gap:.6rem;align-items:center}#post div.sidebar div.property div.property-values{display:flex;gap:.4rem}#post div.sidebar div.property div.property-values span.property-value{background-color:var(--secondary-color-dark);color:var(--secondary-color);padding:2px 4px;border-radius:4px}#post div.sidebar div.inline{display:flex;gap:1rem}#post div.sidebar div.post-tags{display:flex;flex-direction:column;gap:.2rem}#post div.sidebar div.post-tags div.tag{align-items:center}#post div.sidebar div.post-tags div.tag a.tag-name{word-break:break-word;color:#d1d1ee;text-decoration:none}#post div.sidebar div.post-tags div.tag span.tag-post-count{background-color:var(--secondary-color-dark);color:var(--secondary-color);padding:0 4px;border-radius:4px;font-size:12px;margin-left:.4rem}page#root layout *{transition:all .3s ease}page#root .loader{text-align:center;padding-block:2rem}:root{--background-color: #1e1e2c;--background-color-lighter: #3b3b66;--background-color-light: #24243b;--primary-color: #d1d1ee;--primary-color-dark: #9696b3;--primary-color-darker: #72728d;--secondary-color: #aeaeec;--secondary-color-dark: #424268;--nav-height: 50px}html{overflow-x:hidden;font-size:14px}html ::-webkit-scrollbar{background-color:var(--background-color);width:8px}html ::-webkit-scrollbar-thumb{background-color:#aeaeec;border-radius:2px}body{background-color:var(--background-color);color:var(--primary-color);margin:0;font-family:Microsoft Yahei}nav{display:flex;width:100%;height:var(--nav-height);position:fixed;top:0;z-index:100;background-color:var(--background-color);justify-content:space-between;align-items:center;padding-inline:1rem;box-sizing:border-box}nav div.title{display:flex;align-items:center;gap:.4rem}nav div.title a.booru-name{color:var(--secondary-color);text-decoration:none}nav div.title a.booru-name h1{margin:0}nav div.title a.version{color:var(--background-color);background-color:var(--secondary-color);padding:.2em .4em;border-radius:.4rem;font-size:.8rem;text-decoration:none}nav button{padding:2px 4px;border-radius:.4rem;border:none}nav div.searchbar{padding:.4rem 10%;max-width:500px;background-color:var(--background-color);border:1px solid var(--primary-color-darker);border-radius:.4rem;color:var(--primary-color-dark);cursor:pointer}nav div.searchbar:hover{background-color:var(--background-color-light);color:var(--primary-color)}nav div.buttons{display:flex;align-items:center;gap:1rem}nav div.buttons ion-icon{background-color:var(--background-color);transition:all .3s ease;border-radius:1rem;padding:.4rem}nav div.buttons ion-icon:hover{background-color:var(--background-color-lighter)}nav div.buttons ion-icon.search{display:none}@media (max-width: 800px){nav div.searchbar{display:none}nav div.buttons ion-icon.search{display:inline-block}}router{display:block;position:relative;padding-top:var(--nav-height)}router route{display:block;position:relative;padding-inline:10px}button{background-color:#aeaeec;padding:2px 4px;border-radius:.4rem;border:none;cursor:pointer}ion-icon{font-size:24px;color:var(--primary-color);cursor:pointer}ion-icon:hover{color:var(--secondary-color)} diff --git a/dist/assets/index-Bnhfn_iK.css b/dist/assets/index-Bnhfn_iK.css deleted file mode 100644 index 6f04e61..0000000 --- a/dist/assets/index-Bnhfn_iK.css +++ /dev/null @@ -1 +0,0 @@ -post{display:block}post[filetype=mp4] span.duration,post[filetype=webm] span.duration,post[filetype=zip] span.duration{background-color:#fff;color:#000}post span.duration{position:absolute;background-color:#00000050;bottom:.3rem;right:.3rem;padding:2px 4px;border-radius:4px;font-size:12px;text-transform:uppercase;z-index:2}post img{border-radius:10px;height:100%;width:100%}post video{border-radius:10px;height:100%;width:100%;object-fit:cover;position:absolute}#post{padding:0}#post section{background-color:#2f2f45;border-radius:20px;padding:20px}#post div.viewer{height:calc(100vh - 2rem);display:flex;justify-content:center;align-items:center;background-color:#000;border-radius:20px;overflow:hidden;width:calc(100vw - 300px - 4rem);margin:1rem}@media (max-width: 800px){#post div.viewer{width:100%;height:100vh;border-radius:0;margin:0}}#post div.viewer img{max-width:100%;max-height:100%}#post div.viewer video{max-width:100%;max-height:100%}#post div.content{width:calc(100vw - 300px - 4rem);display:flex;flex-direction:column;padding:1rem;box-sizing:border-box}@media (max-width: 800px){#post div.content{width:100%}}#post div.content::-webkit-scrollbar{background-color:#000;width:4px}#post div.content::-webkit-scrollbar-thumb{background-color:#aeaeec;border-radius:2px}#post div.content>h3{padding-left:1rem;margin-block:1rem}#post div.content section.commentary *{text-wrap:wrap;word-break:break-word}#post div.sidebar{position:fixed;top:1rem;right:1rem;display:flex;flex-direction:column;gap:.4rem;width:300px;overflow:scroll;overflow-x:hidden;height:calc(100vh - 2rem);border-radius:20px}@media (max-width: 800px){#post div.sidebar{position:static;width:100%;overflow:visible;height:100%;padding:1rem;box-sizing:border-box}}#post div.sidebar::-webkit-scrollbar{background-color:#000;width:0px}#post div.sidebar::-webkit-scrollbar-thumb{background-color:#aeaeec}#post div.sidebar h3{padding-left:1rem;margin-block:.6rem}#post div.sidebar .post-info{background-color:#2f2f45;border-radius:20px;padding:20px;display:flex;flex-direction:column;gap:.4rem}#post div.sidebar div.property{display:flex;gap:.6rem;align-items:center}#post div.sidebar div.property div.property-values{display:flex;gap:.4rem}#post div.sidebar div.property div.property-values span.property-value{background-color:#525278;color:#aeaeec;padding:2px 4px;border-radius:4px}#post div.sidebar div.inline{display:flex;gap:1rem}#post div.sidebar div.post-tags{display:flex;flex-direction:column;gap:.2rem}#post div.sidebar div.post-tags div.tag{align-items:center}#post div.sidebar div.post-tags div.tag a.tag-name{word-break:break-word;color:#d1d1ee;text-decoration:none}#post div.sidebar div.post-tags div.tag span.tag-post-count{background-color:#525278;color:#aeaeec;padding:0 4px;border-radius:4px;font-size:12px;margin-left:.4rem}page#root layout *{transition:all .3s ease}page#root .loader{text-align:center;padding-block:2rem}html{overflow-x:hidden}body{background-color:#1e1e2c;color:#d1d1ee;margin:0;font-family:Microsoft Yahei;font-size:14px}body::-webkit-scrollbar{background-color:#000;width:8px}body::-webkit-scrollbar-thumb{background-color:#aeaeec;border-radius:2px}app{display:block}app view{display:block}app view page{min-height:100%;padding:1rem;display:block} diff --git a/dist/assets/index-DMNcFlub.js b/dist/assets/index-DMNcFlub.js deleted file mode 100644 index 101bab6..0000000 --- a/dist/assets/index-DMNcFlub.js +++ /dev/null @@ -1 +0,0 @@ -var _t=Object.defineProperty;var bt=(i,t,e)=>t in i?_t(i,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[t]=e;var c=(i,t,e)=>(bt(i,typeof t!="symbol"?t+"":t,e),e),Q=(i,t,e)=>{if(!t.has(i))throw TypeError("Cannot "+e)};var Z=(i,t,e)=>(Q(i,t,"read from private field"),e?e.call(i):t.get(i)),tt=(i,t,e)=>{if(t.has(i))throw TypeError("Cannot add the same private member more than once");t instanceof WeakSet?t.add(i):t.set(i,e)},et=(i,t,e,s)=>(Q(i,t,"write to private field"),s?s.call(i,e):t.set(i,e),e);(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))s(r);new MutationObserver(r=>{for(const n of r)if(n.type==="childList")for(const h of n.addedNodes)h.tagName==="LINK"&&h.rel==="modulepreload"&&s(h)}).observe(document,{childList:!0,subtree:!0});function e(r){const n={};return r.integrity&&(n.integrity=r.integrity),r.referrerPolicy&&(n.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?n.credentials="include":r.crossOrigin==="anonymous"?n.credentials="omit":n.credentials="same-origin",n}function s(r){if(r.ep)return;r.ep=!0;const n=e(r);fetch(r.href,n)}})();class M{constructor(){c(this,"__hidden",!1);c(this,"domEvents",{});c(this,"parent")}on(t,e,s){this.domEvents[t]||(this.domEvents[t]=new Map);const r=n=>e(n,this);return this.domEvents[t].set(e,r),this.dom.addEventListener(t,r,s),this}off(t,e,s){var n;const r=(n=this.domEvents[t])==null?void 0:n.get(e);return r&&this.dom.removeEventListener(t,r,s),this}once(t,e,s){const r=n=>{this.dom.removeEventListener(t,r,s),e(n,this)};return this.dom.addEventListener(t,r,s),this}hide(t,e=!0){return _.fluent(this,arguments,()=>this.__hidden,()=>{var s;if(t!==void 0)return t instanceof O?(this.__hidden=t.value,t.use(this,"hide")):this.__hidden=t,e&&((s=this.parent)==null||s.children.render()),this})}remove(){var t;return(t=this.parent)==null||t.children.remove(this).render(),this}replace(t){var e;return(e=this.parent)==null||e.children.replace(this,t).render(),this}contains(t){return t?t instanceof M?this.dom.contains(t.dom):t instanceof EventTarget?this.dom.contains(_(t).dom):this.dom.contains(t):!1}self(t){return t(this),this}inDOM(){return document.contains(this.dom)}isElement(){return this instanceof E}}class E extends M{constructor(e,s){super();c(this,"dom");c(this,"static_classes",new Set);this.dom=this.createDom(e,s),this.dom.$=this,this.setOptions(s)}createDom(e,s){return s!=null&&s.dom?s.dom:e==="svg"?document.createElementNS("http://www.w3.org/2000/svg","svg"):document.createElement(e)}setOptions(e){return this.id(e==null?void 0:e.id),e&&e.class&&this.class(...e.class),this}id(e){return $.fluent(this,arguments,()=>this.dom.id,()=>$.set(this.dom,"id",e))}class(...e){return $.fluent(this,arguments,()=>this.dom.classList,()=>{this.dom.classList.forEach(s=>this.static_classes.has(s)??this.dom.classList.remove(s)),this.dom.classList.add(...e.detype())})}addClass(...e){return $.fluent(this,arguments,()=>this,()=>{this.dom.classList.add(...e.detype())})}removeClass(...e){return $.fluent(this,arguments,()=>this,()=>{this.dom.classList.remove(...e.detype())})}staticClass(...e){return $.fluent(this,arguments,()=>this.static_classes,()=>{this.removeClass(...this.static_classes),this.static_classes.clear(),this.addStaticClass(...e)})}addStaticClass(...e){return $.fluent(this,arguments,()=>this,()=>{e.detype().forEach(s=>this.static_classes.add(s)),this.addClass(...e)})}removeStaticClass(...e){return $.fluent(this,arguments,()=>this,()=>{e.detype().forEach(s=>this.static_classes.delete(s)),this.removeClass(...e)})}css(e){return $.fluent(this,arguments,()=>this.dom.style,()=>{Object.assign(this.dom.style,e)})}attribute(e,s){return arguments.length?arguments.length===1?e===void 0?null:this.dom.getAttribute(e):arguments.length===2?e?(s===null?this.dom.removeAttribute(e):s!==void 0&&this.dom.setAttribute(e,`${s}`),this):this:this:null}tabIndex(e){return $.fluent(this,arguments,()=>this.dom.tabIndex,()=>$.set(this.dom,"tabIndex",e))}focus(){return this.dom.focus(),this}blur(){return this.dom.blur(),this}animate(e,s,r){const n=this.dom.animate(e,s);return r&&r(n),this}getAnimations(e){return this.dom.getAnimations(e)}get dataset(){return this.dom.dataset}domRect(e){const s=this.dom.getBoundingClientRect();if(!e)return s;const r=e instanceof E?e.dom.getBoundingClientRect():e;return{...s,top:s.top-r.top,left:s.left-r.left,right:s.right-r.left,bottom:s.bottom-r.top,x:s.x-r.x,y:s.y-r.y}}}class C extends M{constructor(e){super();c(this,"dom");this.dom=e,this.dom.$=this}domRect(e){const s={bottom:innerHeight,height:innerHeight,left:0,right:innerWidth,top:0,width:innerWidth,x:0,y:0};if(!e)return s;const r=e instanceof E?e.dom.getBoundingClientRect():e;return{...s,top:s.top-r.top,left:s.left-r.left,right:s.right-r.left,bottom:s.bottom-r.top,x:s.x-r.x,y:s.y-r.y}}static from(e){return e.$ instanceof C?e.$:new C(e)}}class Ot{constructor(t){c(this,"$container");c(this,"childList",new Set);this.$container=t}add(t,e=-1){if(e===-1||this.childList.size-1===e)this.childList.add(t),t.parent=this.$container;else{const s=[...this.childList];s.splice(e,0,t),this.childList.clear(),s.forEach(r=>this.childList.add(r))}}remove(t){return this.childList.has(t)?(this.childList.delete(t),t.parent=void 0,this):this}removeAll(t=!0){this.childList.forEach(e=>this.remove(e)),t&&this.render()}replace(t,e){const s=this.array;return s.splice(s.indexOf(t),1,e),t.remove(),this.childList.clear(),s.forEach(r=>this.childList.add(r)),this}render(){const[t,e]=[this.array.map(r=>r.dom),Array.from(this.dom.childNodes)],s=[];for(;e.length||t.length;){const[r,n]=[e.at(0),t.at(0)];n?r?n!==r?(n.$.__hidden||(this.dom.insertBefore(n,r),s.push(n)),t.shift()):(n.$.__hidden&&this.dom.removeChild(n),t.shift(),e.shift()):(n.$.__hidden||this.dom.append(n),t.shift()):(r&&!s.includes(r)&&r.remove(),e.shift())}}get array(){return[...this.childList.values()]}get dom(){return this.$container.dom}}class O{constructor(t,e){c(this,"_value");c(this,"attributes",new Map);c(this,"linkStates",new Set);c(this,"options",{});this.set(t),e&&(this.options=e)}set(t){this._value=t,t instanceof O&&t.linkStates.add(this),this.update(),this.linkStates.forEach(e=>e.update())}update(){for(const[t,e]of this.attributes.entries())for(const s of e)t[s]instanceof Function?this.options.format?t[s](this.options.format(this.value)):t[s](this.value):s in t&&(t[s]=this.value)}toString(){return this.options.format?this.options.format(this.value):this.value instanceof Object?JSON.stringify(this.toJSON()):`${this.value}`}use(t,e){const s=this.attributes.get(t);s?s.add(e):this.attributes.set(t,new Set().add(e))}toJSON(){return this.value instanceof O?this.value.toJSON():this.value instanceof Object?O.toJSON(this.value):this.toString()}static toJSON(t){const e={};for(let[s,r]of Object.entries(t))r instanceof O?r=r.toJSON():r instanceof Object&&O.toJSON(r),Object.assign(e,{[s]:r});return e}get value(){return this._value instanceof O?this._value.value:this._value}}class V extends M{constructor(e){super();c(this,"dom");this.dom=new Text(e),this.dom.$=this}content(e){return $.fluent(this,arguments,()=>this.dom.textContent,()=>$.set(this.dom,"textContent",e))}}class j extends E{constructor(t,e){super(t,e)}autocapitalize(t){return $.fluent(this,arguments,()=>this.dom.autocapitalize,()=>$.set(this.dom,"autocapitalize",t))}innerText(t){return $.fluent(this,arguments,()=>this.dom.innerText,()=>$.set(this.dom,"innerText",t))}title(t){return $.fluent(this,arguments,()=>this.dom.title,()=>$.set(this.dom,"title",t))}dir(t){return $.fluent(this,arguments,()=>this.dom.dir,()=>$.set(this.dom,"dir",t))}translate(t){return $.fluent(this,arguments,()=>this.dom.translate,()=>$.set(this.dom,"translate",t))}popover(t){return $.fluent(this,arguments,()=>this.dom.popover,()=>$.set(this.dom,"popover",t))}spellcheck(t){return $.fluent(this,arguments,()=>this.dom.spellcheck,()=>$.set(this.dom,"spellcheck",t))}inert(t){return $.fluent(this,arguments,()=>this.dom.inert,()=>$.set(this.dom,"inert",t))}lang(t){return $.fluent(this,arguments,()=>this.dom.lang,()=>$.set(this.dom,"lang",t))}draggable(t){return $.fluent(this,arguments,()=>this.dom.draggable,()=>$.set(this.dom,"draggable",t))}hidden(t){return $.fluent(this,arguments,()=>this.dom.hidden,()=>$.set(this.dom,"hidden",t))}click(){return this.dom.click(),this}attachInternals(){return this.dom.attachInternals()}hidePopover(){return this.dom.hidePopover(),this}showPopover(){return this.dom.showPopover(),this}togglePopover(){return this.dom.togglePopover(),this}get accessKeyLabel(){return this.dom.accessKeyLabel}get offsetHeight(){return this.dom.offsetHeight}get offsetLeft(){return this.dom.offsetLeft}get offsetParent(){return $(this.dom.offsetParent)}get offsetTop(){return this.dom.offsetTop}get offsetWidth(){return this.dom.offsetWidth}}class p extends j{constructor(e,s){super(e,s);c(this,"children",new Ot(this));c(this,"__position_cursor",0)}content(e){return $.fluent(this,arguments,()=>this,()=>{this.children.removeAll(!1),this.insert(e)})}insert(e,s=-1){return $.fluent(this,arguments,()=>this,()=>{e instanceof Function&&(e=e(this)),e=$.orArrayResolve(e),this.__position_cursor=s<0?this.children.array.length+s:s;for(const r of e)if(r!=null){if(r instanceof Array)this.insert(r,this.__position_cursor);else if(typeof r=="string")this.children.add(new V(r),s);else if(r instanceof O){const n=new V(r.toString());r.use(n,"content"),this.children.add(n,s)}else this.children.add(r,s);this.__position_cursor+=1}this.children.render()})}clear(){return this.children.removeAll(),this}$(e){return $(this.dom.querySelector(e))}$all(e){return Array.from(this.dom.querySelectorAll(e)).map(s=>$(s))}get scrollHeight(){return this.dom.scrollHeight}get scrollWidth(){return this.dom.scrollWidth}scrollTop(e){return $.fluent(this,arguments,()=>this.dom.scrollTop,()=>$.set(this.dom,"scrollTop",e))}scrollLeft(e){return $.fluent(this,arguments,()=>this.dom.scrollLeft,()=>$.set(this.dom,"scrollLeft",e))}}class Tt extends p{constructor(t){super("a",t),this.dom.addEventListener("click",e=>{$.anchorPreventDefault&&e.preventDefault(),$.anchorHandler&&this.href()&&$.anchorHandler(this,e)})}href(t){return $.fluent(this,arguments,()=>this.dom.href,()=>{t&&(this.dom.href=t)})}target(t){return $.fluent(this,arguments,()=>this.dom.target??void 0,()=>{t&&(this.dom.target=t)})}}class Lt extends E{constructor(t,e){super(t,e)}}var b;(i=>{function t(o,d,a,l){return d.length?(l(),o):a()}i.fluent=t;function e(o){return o instanceof Array?o:[o]}i.orArrayResolve=e;function s(o,d){return e(d).forEach(a=>{Object.getOwnPropertyNames(a.prototype).forEach(l=>{l!=="constructor"&&Object.defineProperty(o.prototype,l,Object.getOwnPropertyDescriptor(a.prototype,l)||Object.create(null))})}),o}i.mixin=s;function r(o,d,a){a!==void 0&&(o[d]=a)}i.set=r;function n(o){return new O(o)}i.state=n;function h(o){if(o.$)return o.$;if(o.nodeName.toLowerCase()==="body")return new p("body",{dom:o});if(o.nodeName.toLowerCase()==="#document")return C.from(o);if(o instanceof HTMLElement){const d=$.TagNameElementMap[o.tagName.toLowerCase()],a=d===p?new d(o.tagName,{dom:o}):new d({dom:o});if(a instanceof p)for(const l of Array.from(a.dom.childNodes))a.children.add($(l));return a}else if(o instanceof Text){const d=new V(o.textContent??"");return d.dom=o,d}else if(o instanceof SVGElement&&o.tagName.toLowerCase()==="svg")return new Lt("svg",{dom:o});throw`$NODE.FROM: NOT SUPPORT TARGET ELEMENT TYPE (${o.nodeName})`}i.from=h})(b||(b={}));class T{static create(...t){const e=class{};return Object.getOwnPropertyNames(T.prototype).forEach(s=>{s!=="constructor"&&t.includes(s)&&Object.defineProperty(e.prototype,s,Object.getOwnPropertyDescriptor(T.prototype,s)||Object.create(null))}),e}disabled(t){return $.fluent(this,arguments,()=>this.dom.disabled,()=>$.set(this.dom,"disabled",t))}checkValidity(){return this.dom.checkValidity()}reportValidity(){return this.dom.reportValidity()}formAction(t){return $.fluent(this,arguments,()=>this.dom.formAction,()=>$.set(this.dom,"formAction",t))}formEnctype(t){return $.fluent(this,arguments,()=>this.dom.formEnctype,()=>$.set(this.dom,"formEnctype",t))}formMethod(t){return $.fluent(this,arguments,()=>this.dom.formMethod,()=>$.set(this.dom,"formMethod",t))}formNoValidate(t){return $.fluent(this,arguments,()=>this.dom.formNoValidate,()=>$.set(this.dom,"formNoValidate",t))}formTarget(t){return $.fluent(this,arguments,()=>this.dom.formTarget,()=>$.set(this.dom,"formTarget",t))}autocomplete(t){return $.fluent(this,arguments,()=>this.dom.autocomplete,()=>$.set(this.dom,"autocomplete",t))}name(t){return $.fluent(this,arguments,()=>this.dom.name,()=>$.set(this.dom,"name",t))}maxLength(t){return $.fluent(this,arguments,()=>this.dom.maxLength,()=>$.set(this.dom,"maxLength",t))}minLength(t){return $.fluent(this,arguments,()=>this.dom.minLength,()=>$.set(this.dom,"minLength",t))}required(t){return $.fluent(this,arguments,()=>this.dom.required,()=>$.set(this.dom,"required",t))}label(t){return $.fluent(this,arguments,()=>this.dom.label,()=>$.set(this.dom,"label",t))}get form(){return this.dom.form?$(this.dom.form):null}get validationMessage(){return this.dom.validationMessage}get validity(){return this.dom.validity}get willValidate(){return this.dom.willValidate}}class at extends p{constructor(t){super("button",t)}type(t){return $.fluent(this,arguments,()=>this.dom.type,()=>$.set(this.dom,"type",t))}}b.mixin(at,T.create("disabled","checkValidity","formAction","formEnctype","formMethod","formNoValidate","formTarget","reportValidity"));class ht extends p{constructor(t){super("form",t)}action(t){return $.fluent(this,arguments,()=>this.dom.formAction,()=>$.set(this.dom,"formAction",t))}enctype(t){return $.fluent(this,arguments,()=>this.dom.formEnctype,()=>$.set(this.dom,"formEnctype",t))}method(t){return $.fluent(this,arguments,()=>this.dom.formMethod,()=>$.set(this.dom,"formMethod",t))}noValidate(t){return $.fluent(this,arguments,()=>this.dom.formNoValidate,()=>$.set(this.dom,"formNoValidate",t))}acceptCharset(t){return $.fluent(this,arguments,()=>this.dom.acceptCharset,()=>$.set(this.dom,"acceptCharset",t))}target(t){return $.fluent(this,arguments,()=>this.dom.formTarget,()=>$.set(this.dom,"formTarget",t))}requestSubmit(){return this.dom.requestSubmit(),this}reset(){return this.dom.reset(),this}submit(){return this.dom.submit(),this}get length(){return this.dom.length}get elements(){return Array.from(this.dom.elements).map(t=>$(t))}}b.mixin(ht,T.create("checkValidity","reportValidity","autocomplete"));class ut extends E{constructor(t){super("input",t)}value(t){return $.fluent(this,arguments,()=>this.type()==="number"?Number(this.dom.value):this.dom.value,()=>$.set(this.dom,"value",t,e=>{this.on("input",()=>{e.attributes.has(this.dom)!==!1&&(typeof e.value=="string"&&e.set(`${this.value()}`),typeof e.value=="number"&&e.set(Number(this.value())))})}))}type(t){return $.fluent(this,arguments,()=>this.dom.type,()=>$.set(this.dom,"type",t))}capture(t){return $.fluent(this,arguments,()=>this.dom.capture,()=>$.set(this.dom,"capture",t))}alt(t){return $.fluent(this,arguments,()=>this.dom.alt,()=>$.set(this.dom,"alt",t))}height(t){return $.fluent(this,arguments,()=>this.dom.height,()=>$.set(this.dom,"height",t))}width(t){return $.fluent(this,arguments,()=>this.dom.width,()=>$.set(this.dom,"width",t))}defaultValue(t){return $.fluent(this,arguments,()=>this.dom.defaultValue,()=>$.set(this.dom,"defaultValue",t))}dirName(t){return $.fluent(this,arguments,()=>this.dom.dirName,()=>$.set(this.dom,"dirName",t))}pattern(t){return $.fluent(this,arguments,()=>this.dom.pattern,()=>$.set(this.dom,"pattern",t))}placeholder(t){return $.fluent(this,arguments,()=>this.dom.placeholder,()=>$.set(this.dom,"placeholder",t))}readOnly(t){return $.fluent(this,arguments,()=>this.dom.readOnly,()=>$.set(this.dom,"readOnly",t))}selectionDirection(t){return $.fluent(this,arguments,()=>this.dom.selectionDirection,()=>$.set(this.dom,"selectionDirection",t))}selectionEnd(t){return $.fluent(this,arguments,()=>this.dom.selectionEnd,()=>$.set(this.dom,"selectionEnd",t))}selectionStart(t){return $.fluent(this,arguments,()=>this.dom.selectionStart,()=>$.set(this.dom,"selectionStart",t))}size(t){return $.fluent(this,arguments,()=>this.dom.size,()=>$.set(this.dom,"size",t))}src(t){return $.fluent(this,arguments,()=>this.dom.src,()=>$.set(this.dom,"src",t))}inputMode(t){return $.fluent(this,arguments,()=>this.dom.inputMode,()=>$.set(this.dom,"inputMode",t))}valueAsDate(t){return $.fluent(this,arguments,()=>this.dom.valueAsDate,()=>$.set(this.dom,"valueAsDate",t))}valueAsNumber(t){return $.fluent(this,arguments,()=>this.dom.valueAsNumber,()=>$.set(this.dom,"valueAsNumber",t))}webkitdirectory(t){return $.fluent(this,arguments,()=>this.dom.webkitdirectory,()=>$.set(this.dom,"webkitdirectory",t))}select(){return this.dom.select(),this}setCustomValidity(t){return this.dom.setCustomValidity(t),this}setRangeText(t,e,s,r){return typeof e=="number"&&typeof s=="number"&&this.dom.setRangeText(t,e,s,r),this.dom.setRangeText(t),this}setSelectionRange(t,e,s){return this.dom.setSelectionRange(t,e,s),this}showPicker(){return this.dom.showPicker(),this}get files(){return this.dom.files}get webkitEntries(){return this.dom.webkitEntries}get labels(){return Array.from(this.dom.labels??[]).map(t=>$(t))}}b.mixin(ut,T.create("checkValidity","reportValidity","autocomplete","name","form","required","validationMessage","validity","willValidate","formAction","formEnctype","formMethod","formNoValidate","formTarget"));class dt extends p{constructor(t){super("label",t)}for(t){return $.fluent(this,arguments,()=>this.dom.htmlFor,()=>{$.set(this.dom,"htmlFor",t)})}get control(){return this.dom.control}}b.mixin(dt,T.create("form"));class Et extends j{constructor(t){super("img",t)}async load(t){return new Promise(e=>{const s=this.once("load",()=>{e(s)});typeof t=="string"?s.src(t):t.then(r=>s.src(r))})}static resize(t,e){return new Promise(s=>{const r=new Image;if(r.addEventListener("load",()=>{const n=document.createElement("canvas"),h=n.getContext("2d");if(!h)throw"$Image.resize: context undefined";const o=r.width/r.height,[d,a,l]=[o>1,o<1,o===1],u=e instanceof Array?e[0]:a?e:e*o,m=e instanceof Array?e[1]:d?e:e/o;n.height=m,n.width=u,h.drawImage(r,0,0,u,m),s(n.toDataURL())},{once:!0}),t instanceof File){const n=new FileReader;n.addEventListener("load",()=>r.src=n.result),n.readAsDataURL(t)}else r.src=t})}alt(t){return $.fluent(this,arguments,()=>this.dom.alt,()=>$.set(this.dom,"alt",t))}crossOrigin(t){return $.fluent(this,arguments,()=>this.dom.crossOrigin,()=>$.set(this.dom,"crossOrigin",t))}decoding(t){return $.fluent(this,arguments,()=>this.dom.decoding,()=>$.set(this.dom,"decoding",t))}height(t){return $.fluent(this,arguments,()=>this.dom.height,()=>$.set(this.dom,"height",t))}isMap(t){return $.fluent(this,arguments,()=>this.dom.isMap,()=>$.set(this.dom,"isMap",t))}loading(t){return $.fluent(this,arguments,()=>this.dom.loading,()=>$.set(this.dom,"loading",t))}referrerPolicy(t){return $.fluent(this,arguments,()=>this.dom.referrerPolicy,()=>$.set(this.dom,"referrerPolicy",t))}sizes(t){return $.fluent(this,arguments,()=>this.dom.sizes,()=>$.set(this.dom,"sizes",t))}src(t){return $.fluent(this,arguments,()=>this.dom.src,()=>$.set(this.dom,"src",t))}srcset(t){return $.fluent(this,arguments,()=>this.dom.srcset,()=>$.set(this.dom,"srcset",t))}useMap(t){return $.fluent(this,arguments,()=>this.dom.useMap,()=>$.set(this.dom,"useMap",t))}width(t){return $.fluent(this,arguments,()=>this.dom.width,()=>$.set(this.dom,"width",t))}decode(){return this.dom.decode()}get complete(){return this.dom.complete}get currentSrc(){return this.dom.currentSrc}get naturalHeight(){return this.dom.naturalHeight}get naturalWidth(){return this.dom.naturalWidth}get x(){return this.dom.x}get y(){return this.dom.y}}class xt extends p{constructor(t){super("canvas",t)}height(t){return $.fluent(this,arguments,()=>this.dom.height,()=>{$.set(this.dom,"height",t)})}width(t){return $.fluent(this,arguments,()=>this.dom.width,()=>{$.set(this.dom,"width",t)})}captureStream(t){return this.dom.captureStream(t)}getContext(t,e){return this.dom.getContext(t)}toBlob(t,e,s){return this.dom.toBlob(t,e,s),this}toDataURL(t,e){return this.dom.toDataURL(t,e)}transferControlToOffscreen(){return this.dom.transferControlToOffscreen()}}class St extends p{constructor(t){super("dialog",t)}open(t){return $.fluent(this,arguments,()=>this.dom.open,()=>$.set(this.dom,"open",t))}returnValue(t){return $.fluent(this,arguments,()=>this.dom.returnValue,()=>$.set(this.dom,"returnValue",t))}close(){return this.dom.close(),this}show(){return this.dom.show(),this}showModal(){return this.dom.showModal(),this}}class ct{on(t,e){return this.events.on(t,e),this}off(t,e){return this.events.off(t,e),this}once(t,e){return this.events.once(t,e),this}}class W{constructor(){c(this,"eventMap",new Map)}register(...t){return t.forEach(e=>{const s=new st(e);this.eventMap.set(s.name,s)}),this}delete(t){return this.eventMap.delete(t),this}fire(t,...e){const s=this.get(t);return s instanceof st&&s.fire(...e),this}on(t,e){return this.get(t).add(e),this}off(t,e){return this.get(t).delete(e),this}once(t,e){const s=(...r)=>{this.get(t).delete(s),e(...r)};return this.get(t).add(s),this}get(t){const e=this.eventMap.get(t);if(!e)throw new Error("EVENT NOT EXIST");return e}}class st{constructor(t){c(this,"name");c(this,"callbackList",new Set);this.name=t}fire(...t){this.callbackList.forEach(e=>e(...t))}add(t){this.callbackList.add(t)}delete(t){this.callbackList.delete(t)}}class lt extends p{constructor(e){super("view",e);c(this,"view_cache",new Map);c(this,"event",new W().register("switch"));c(this,"content_id",null)}setView(e,s){return this.view_cache.set(e,s),this}deleteView(e){return this.view_cache.delete(e),this}deleteAllView(){return this.view_cache.clear(),this}switchView(e){const s=this.view_cache.get(e);return s===void 0?this:(this.content(s),this.content_id=e,this.event.fire("switch",e),this)}}class mt extends p{constructor(t){super("select")}add(t){return this.insert(t),this}item(t){return $(this.dom.item(t))}namedItem(t){return $(this.dom.namedItem(t))}multiple(t){return $.fluent(this,arguments,()=>this.dom.multiple,()=>$.set(this.dom,"multiple",t))}get length(){return this.dom.length}get size(){return this.dom.size}get options(){return Array.from(this.dom.options).map(t=>$(t))}get selectedIndex(){return this.dom.selectedIndex}get selectedOptions(){return Array.from(this.dom.selectedOptions).map(t=>$(t))}value(t){return $.fluent(this,arguments,()=>this.dom.value,()=>$.set(this.dom,"value",t))}get labels(){return Array.from(this.dom.labels??[]).map(t=>$(t))}}b.mixin(mt,T.create("checkValidity","reportValidity","autocomplete","name","form","required","disabled","validationMessage","validity","willValidate"));class ft extends p{constructor(t){super("option",t)}defaultSelected(t){return $.fluent(this,arguments,()=>this.dom.defaultSelected,()=>$.set(this.dom,"defaultSelected",t))}selected(t){return $.fluent(this,arguments,()=>this.dom.selected,()=>$.set(this.dom,"selected",t))}text(t){return $.fluent(this,arguments,()=>this.dom.text,()=>$.set(this.dom,"text",t))}value(t){return $.fluent(this,arguments,()=>this.dom.value,()=>$.set(this.dom,"value",t))}get form(){return this.dom.form?$(this.dom.form):null}get index(){return this.dom.index}}b.mixin(ft,T.create("form","disabled","label"));class pt extends p{constructor(t){super("optgroup",t)}disabled(t){return $.fluent(this,arguments,()=>this.dom.disabled,()=>$.set(this.dom,"disabled",t))}}b.mixin(pt,T.create("disabled","label"));class gt extends p{constructor(t){super("textarea",t)}cols(t){return $.fluent(this,arguments,()=>this.dom.cols,()=>$.set(this.dom,"cols",t))}wrap(t){return $.fluent(this,arguments,()=>this.dom.wrap,()=>$.set(this.dom,"wrap",t))}value(t){return $.fluent(this,arguments,()=>this.dom.value,()=>$.set(this.dom,"value",t))}defaultValue(t){return $.fluent(this,arguments,()=>this.dom.defaultValue,()=>$.set(this.dom,"defaultValue",t))}dirName(t){return $.fluent(this,arguments,()=>this.dom.dirName,()=>$.set(this.dom,"dirName",t))}placeholder(t){return $.fluent(this,arguments,()=>this.dom.placeholder,()=>$.set(this.dom,"placeholder",t))}readOnly(t){return $.fluent(this,arguments,()=>this.dom.readOnly,()=>$.set(this.dom,"readOnly",t))}selectionDirection(t){return $.fluent(this,arguments,()=>this.dom.selectionDirection,()=>$.set(this.dom,"selectionDirection",t))}selectionEnd(t){return $.fluent(this,arguments,()=>this.dom.selectionEnd,()=>$.set(this.dom,"selectionEnd",t))}selectionStart(t){return $.fluent(this,arguments,()=>this.dom.selectionStart,()=>$.set(this.dom,"selectionStart",t))}type(t){return $.fluent(this,arguments,()=>this.dom.type,()=>$.set(this.dom,"type",t))}inputMode(t){return $.fluent(this,arguments,()=>this.dom.inputMode,()=>$.set(this.dom,"inputMode",t))}select(){return this.dom.select(),this}setCustomValidity(t){return this.dom.setCustomValidity(t),this}setRangeText(t,e,s,r){return typeof e=="number"&&typeof s=="number"&&this.dom.setRangeText(t,e,s,r),this.dom.setRangeText(t),this}setSelectionRange(t,e,s){return this.dom.setSelectionRange(t,e,s),this}get labels(){return Array.from(this.dom.labels??[]).map(t=>$(t))}}b.mixin(gt,T.create("checkValidity","reportValidity","autocomplete","name","form","required","disabled","minLength","maxLength","validationMessage","validity","willValidate"));var N;class Mt extends p{constructor(e){super("async",e);tt(this,N,!1)}await(e){return e.then(s=>this._loaded(s)),this}_loaded(e){et(this,N,!0),this.replace(e),this.dom.dispatchEvent(new Event("load"))}get loaded(){return Z(this,N)}}N=new WeakMap;class Pt extends E{constructor(t,e){super(t,e)}autoplay(t){return $.fluent(this,arguments,()=>this.dom.autoplay,()=>$.set(this.dom,"autoplay",t))}get buffered(){return this.dom.buffered}controls(t){return $.fluent(this,arguments,()=>this.dom.controls,()=>$.set(this.dom,"controls",t))}crossOrigin(t){return $.fluent(this,arguments,()=>this.dom.crossOrigin,()=>$.set(this.dom,"crossOrigin",t))}get currentSrc(){return this.dom.currentSrc}currentTime(t){return $.fluent(this,arguments,()=>this.dom.currentTime,()=>$.set(this.dom,"currentTime",t))}defaultMuted(t){return $.fluent(this,arguments,()=>this.dom.defaultMuted,()=>$.set(this.dom,"defaultMuted",t))}defaultPlaybackRate(t){return $.fluent(this,arguments,()=>this.dom.defaultPlaybackRate,()=>$.set(this.dom,"defaultPlaybackRate",t))}disableRemotePlayback(t){return $.fluent(this,arguments,()=>this.dom.disableRemotePlayback,()=>$.set(this.dom,"disableRemotePlayback",t))}get duration(){return this.dom.duration}get ended(){return this.dom.ended}get error(){return this.dom.error}loop(t){return $.fluent(this,arguments,()=>this.dom.loop,()=>$.set(this.dom,"loop",t))}mediaKeys(t){return $.fluent(this,arguments,()=>this.dom.mediaKeys,()=>$.set(this.dom,"setMediaKeys",[t]))}muted(t){return $.fluent(this,arguments,()=>this.dom.muted,()=>$.set(this.dom,"muted",t))}get networkState(){return this.dom.networkState}get paused(){return this.dom.paused}playbackRate(t){return $.fluent(this,arguments,()=>this.dom.playbackRate,()=>$.set(this.dom,"playbackRate",t))}get played(){return this.dom.played}preload(t){return $.fluent(this,arguments,()=>this.dom.preload,()=>$.set(this.dom,"preload",t))}preservesPitch(t){return $.fluent(this,arguments,()=>this.dom.preservesPitch,()=>$.set(this.dom,"preservesPitch",t))}get readyState(){return this.dom.readyState}get remote(){return this.dom.remote}get seekable(){return this.dom.seekable}get seeking(){return this.dom.seeking}sinkId(t){return $.fluent(this,arguments,()=>this.dom.sinkId,()=>$.set(this.dom,"setSinkId",[t]))}src(t){return $.fluent(this,arguments,()=>this.dom.src,()=>$.set(this.dom,"src",t))}srcObject(t){return $.fluent(this,arguments,()=>this.dom.srcObject,()=>$.set(this.dom,"srcObject",t))}get textTracks(){return this.dom.textTracks}volume(t){return $.fluent(this,arguments,()=>this.dom.volume,()=>$.set(this.dom,"volume",t))}addTextTrack(t,e,s){return this.dom.addTextTrack(t,e,s)}canPlayType(t){return this.dom.canPlayType(t)}fastSeek(t){return this.dom.fastSeek(t),this}load(){return this.dom.load(),this}pause(){return this.dom.pause(),this}async play(){return await this.dom.play(),this}get isPlaying(){return this.currentTime()>0&&!this.paused&&!this.ended&&this.readyState>2}}class Rt extends Pt{constructor(t){super("video",t)}disablePictureInPicture(t){return $.fluent(this,arguments,()=>this.dom.disablePictureInPicture,()=>$.set(this.dom,"disablePictureInPicture",t))}height(t){return $.fluent(this,arguments,()=>this.dom.height,()=>$.set(this.dom,"height",t))}width(t){return $.fluent(this,arguments,()=>this.dom.width,()=>$.set(this.dom,"width",t))}playsInline(t){return $.fluent(this,arguments,()=>this.dom.playsInline,()=>$.set(this.dom,"playsInline",t))}poster(t){return $.fluent(this,arguments,()=>this.dom.poster,()=>$.set(this.dom,"poster",t))}get videoHeight(){return this.dom.videoHeight}get videoWidth(){return this.dom.videoWidth}cancelVideoFrameCallback(t){return this.dom.cancelVideoFrameCallback(t),this}getVideoPlaybackQuality(){return this.dom.getVideoPlaybackQuality()}requestPictureInPicture(){return this.dom.requestPictureInPicture()}requestVideoFrameCallback(t){return this.dom.requestVideoFrameCallback(t)}}function _(i){if(typeof i>"u"||i===null||i instanceof M)return i;if(typeof i=="string"){if(i.startsWith("::"))return Array.from(document.querySelectorAll(i.replace(/^::/,""))).map(t=>_(t));if(i.startsWith(":"))return _(document.querySelector(i.replace(/^:/,"")));if(i in _.TagNameElementMap){const t=_.TagNameElementMap[i];return t===j?new j(i):t===p?new p(i):new t}else return new p(i)}if(i instanceof Node)return i.$?i.$:b.from(i);throw`$: NOT SUPPORT TARGET ELEMENT TYPE ('${i}')`}(i=>{i.anchorHandler=null,i.anchorPreventDefault=!1,i.TagNameElementMap={document:C,body:p,a:Tt,p,pre:p,code:p,blockquote:p,strong:p,h1:p,h2:p,h3:p,h4:p,h5:p,h6:p,div:p,ol:p,ul:p,dl:p,li:p,input:ut,label:dt,button:at,form:ht,img:Et,dialog:St,canvas:xt,view:lt,select:mt,option:ft,optgroup:pt,textarea:gt,video:Rt,async:Mt};function t(u,m,g,f){return m.length?(f(),u):g()}i.fluent=t;function e(u){return u instanceof Array?u:[u]}i.orArrayResolve=e;function s(u,m){return b.mixin(u,m)}i.mixin=s;function r(u,m,g,f){if(g!==void 0){if(g instanceof O){g.use(u,m),u[m]instanceof Function?u[m](...g.value):u[m]=g.value,f&&f(g);return}u[m]instanceof Function?u[m](...g):u[m]=g}}i.set=r;function n(u,m){return new O(u,m)}i.state=n;async function h(u,m){return new Promise(g=>{const f=new FileReader;f.onload=w=>{const v=i("img");if(v.once("load",H=>{const q=i("canvas"),z=q.getContext("2d"),D=v.height()/v.width(),[K,X]=[D>1?m/D:m,D>1?m:m*D];q.height(X).width(K),z==null||z.drawImage(v.dom,0,0,K,X),g(q.toDataURL(u.type))}),!w.target)throw"$.resize(): e.target is null";v.src(w.target.result)},f.readAsDataURL(u)})}i.resize=h;function o(u=1){return parseInt(getComputedStyle(document.documentElement).fontSize)*u}i.rem=o;function d(u){const m=new DOMParser().parseFromString(u,"text/html").body;return Array.from(m.children).map(g=>i(g))}i.html=d;function a(u,m,g){if(typeof m=="number")return Array(m).fill("").map(w=>{const v=f(u)?u[0](...u.slice(1)):i(u);return g&&g(v),v});{const w=[];for(const v of m){const H=u instanceof Function?u(...v):f(u)?u[0](...u.slice(1)):i(u);v instanceof Function?v(H):(v instanceof M||typeof v=="string")&&H.content(v),w.push(H)}return w}function f(w){return w instanceof Array&&w[0]instanceof Function}}i.builder=a;function l(u,m){return Object.assign(i.TagNameElementMap,{[u]:m}),i.TagNameElementMap}i.registerTagName=l})(_||(_={}));globalThis.$=_;Array.prototype.detype=function(...i){return this.filter(t=>{if(i.length)for(const e of i)return typeof t!=typeof e;else return t!==void 0})};class At extends p{constructor(e){super("layout",e);c(this,"_property",{ROW_MAX_HEIGHT:200,GAP:0,IS_RENDERING:!1,RENDER_REQUEST:!1,COLUNM:1,TYPE:"justified",ROOT:null,THRESHOLD:null});this.css({display:"block",position:"relative"}),new ResizeObserver(s=>{this.inDOM()&&(this.render(),this.dom.dispatchEvent(new Event("resize")))}).observe(this.dom),document.addEventListener("scroll",s=>{s.target===this.root().dom&&this.scrollCompute()}),new IntersectionObserver(s=>{this.inDOM()&&this.render()}).observe(this.dom)}type(e){return $.fluent(this,arguments,()=>this._property.TYPE,()=>$.set(this._property,"TYPE",e))}maxHeight(e){return $.fluent(this,arguments,()=>this._property.ROW_MAX_HEIGHT,()=>$.set(this._property,"ROW_MAX_HEIGHT",e))}column(e){return $.fluent(this,arguments,()=>this._property.COLUNM,()=>$.set(this._property,"COLUNM",e))}gap(e){return $.fluent(this,arguments,()=>this._property.GAP,()=>$.set(this._property,"GAP",e))}root(e){return $.fluent(this,arguments,()=>this._property.ROOT??$(document),()=>$.set(this._property,"ROOT",e))}threshold(e){return $.fluent(this,arguments,()=>this._property.THRESHOLD??innerHeight,()=>$.set(this._property,"THRESHOLD",e))}get COL_WIDTH(){return(this.offsetWidth-this._property.GAP*(this._property.COLUNM-1))/this._property.COLUNM}computeLayout(){return this._property.TYPE==="justified"?this.justifiedCompute():this.justifiedCompute()}justifiedCompute(){const e=[],s=this.offsetWidth;for(const r of this.children.array){const n=$(r);if(!(n instanceof E))continue;const h=n.attribute("layout-item-ratio"),o=h?parseFloat(h):n.dom.offsetWidth/n.dom.offsetHeight,d={$node:n,ratio:o};let a=e.at(-1);(!a||a.heightl+=g.ratio);const u=l+o,m=(s-this._property.GAP*a.items.length)/u;a.items.push(d),a.ratio=u,a.height=m}return e}waterfallCompute(){const e=[],s=this.COL_WIDTH,r=()=>{if(e.lengthn.height-h.height)[0]};for(const n of this.children.array){const h=$(n);if(!(h instanceof E))continue;const o=h.attribute("layout-item-ratio"),d=o?parseFloat(o):h.dom.offsetWidth/h.dom.offsetHeight,a={$node:h,ratio:d},l=r();let u=0;l.items.forEach(f=>u+=f.ratio);const m=s/(l.height+s/d),g=s/m;l.items.push(a),l.ratio=m,l.height=g}return e}render(){if(this.inDOM()){if(this._property.TYPE==="justified"){const e=this.justifiedCompute();let s=0;for(const r of e){let n=0;r.height>this._property.ROW_MAX_HEIGHT&&(r.height=this._property.ROW_MAX_HEIGHT);for(const h of r.items){const o=h.ratio*r.height;h.$node.css({position:"absolute",height:`${r.height}px`,width:`${o}px`,top:`${s}px`,left:`${n}px`}),h.$node.attribute("layout-item-ratio",h.ratio),n+=r.height*h.ratio+this._property.GAP}s+=r.height+this._property.GAP}this.css({height:`${s}px`})}else if(this._property.TYPE="waterfall"){const e=this.waterfallCompute(),s=this.COL_WIDTH;let r=0;for(const n of e){let h=0;for(const o of n.items){const d=s/o.ratio;o.$node.css({position:"absolute",height:`${d}px`,width:`${s}px`,top:`${h}px`,left:`${r}px`}),o.$node.attribute("layout-item-ratio",o.ratio),h+=d+this._property.GAP}r+=s+this._property.GAP}if(e.length){const n=e.sort((h,o)=>o.height-h.height)[0];this.css({height:`${n.height+n.items.length*this._property.GAP}px`})}}return this.scrollCompute(),this}}scrollCompute(){if(this.inDOM()===!1)return;const e=this.threshold();this.children.array.forEach(s=>{if(!s.isElement())return;const r=s.domRect();r.bottom<-e||r.top>innerHeight+e?s.hide(!0,!1):s.hide(!1,!1)}),this.children.render()}}$.registerTagName("layout",At);class $t{constructor(t,e){c(this,"path");c(this,"builder");this.path=t,this.builder=e instanceof Function?e:s=>e}}class yt{constructor(t){c(this,"id");c(this,"content");c(this,"events",new W().register("open","load"));this.id=t}}b.mixin(yt,ct);const y=class y{constructor(t,e){c(this,"routeMap",new Map);c(this,"recordMap",new Map);c(this,"$view");c(this,"events",new W().register("notfound","load"));c(this,"basePath");c(this,"popstate",(()=>{history.state.index>y.index||history.state.indexr.resolvePath()),y.recoveryScrollPosition(),y.events.fire("pathchange",{prevURL:e,nextURL:y.currentPath,navigation:"Forward"}),this}static back(){const t=y.currentPath;return history.back(),y.currentPath=new URL(location.href),y.events.fire("pathchange",{prevURL:t,nextURL:y.currentPath,navigation:"Back"}),this}static replace(t){if(t===void 0)return this;if(typeof t=="string"&&!t.startsWith(location.origin)&&(t=location.origin+t),t=new URL(t),t.origin!==location.origin)return this;if(t.href===location.href)return this;const e=y.currentPath;return history.replaceState({index:y.index},"",t),y.currentPath=new URL(location.href),$.routers.forEach(s=>s.resolvePath(t.pathname)),y.events.fire("pathchange",{prevURL:e,nextURL:y.currentPath,navigation:"Forward"}),this}setStateData(t,e){return history.state.data===void 0&&(history.state.data={}),history.state.data[t]=e,this}resolvePath(t=location.pathname){if(!t.startsWith(this.basePath))return;t=t.replace(this.basePath,"/").replace("//","/");let e=!1;const s=n=>{const h=this.recordMap.get(n);return h?(e=!0,h.content&&!this.$view.contains(h.content)&&this.$view.switchView(n),h.events.fire("open",{path:t,record:h}),!0):!1},r=(n,h,o)=>{const d=new yt(n);let a=h.builder({params:o,record:d,loaded:()=>{d.events.fire("load",{path:n,record:d}),this.events.fire("load",{path:n})}});typeof a=="string"&&(a=new V(a)),a!==void 0&&(d.content=a,this.recordMap.set(n,d),this.$view.setView(n,a).switchView(n),d.events.fire("open",{path:t,record:d}),e=!0)};for(const[n,h]of this.routeMap.entries()){if(n instanceof Function){const u=n(t);u&&(s(u)||r(u,h,void 0));continue}const[o,d]=[n.split("/").map(u=>`/${u}`),t.split("/").map(u=>`/${u}`)];o.shift(),d.shift();const a={};let l="";for(let u=0;un=!0;this.events.fire("notfound",{path:t,preventDefault:h}),n||this.$view.clear()}}static recoveryScrollPosition(){const t=this.getScrollHistory(this.index,location.href);t?document.documentElement.scrollTop=t.scroll:(document.documentElement.scrollTop=0,this.setScrollHistory(this.index,location.href,0))}static getScrollHistory(t,e){const s=this.scrollHistoryData;return!s||!s[t]?null:s[t].href===e?s[t]:null}static setScrollHistory(t,e,s){let r=this.scrollHistoryData;if(!r)r={[t]:{href:e,scroll:s}},sessionStorage.setItem(this.SCROLL_HISTORY_KEY,JSON.stringify(r));else{const n=r[t];if(n&&n.href!==e){let h=0;for(;r[t+h];)delete r[t+h],h++}r[t]={href:e,scroll:s},sessionStorage.setItem(this.SCROLL_HISTORY_KEY,JSON.stringify(r))}return r[t]}static get scrollHistoryData(){const t=sessionStorage.getItem(this.SCROLL_HISTORY_KEY);return t?JSON.parse(t):null}static on(t,e){return this.events.on(t,e),this}static off(t,e){return this.events.off(t,e),this}static once(t,e){return this.events.once(t,e),this}};c(y,"index",0),c(y,"events",new W().register("pathchange","notfound","load")),c(y,"currentPath",new URL(location.href)),c(y,"SCROLL_HISTORY_KEY","$router_scroll_history");let L=y;b.mixin(L,ct);window.addEventListener("scroll",()=>{L.setScrollHistory(L.index,location.href,document.documentElement.scrollTop)});Object.assign($,{routers:new Set,open(i){return L.open(i)},replace(i){return L.replace(i)},back(){return L.back()}});class It{constructor(t){Object.assign(this,t),this.api.endsWith("/")&&(this.api=this.api.slice(0,-1))}}const Ct=new Intl.NumberFormat("en",{notation:"compact"});class B{constructor(t){c(this,"post_count$",$.state(0,{format:t=>`${Ct.format(t)}`}));c(this,"name$",$.state(""));Object.assign(this,t),this.$update()}static async fetch(t,e){var n;const s=await fetch(`${t.api}/tags/${e}.json`).then(async h=>await h.json()),r=((n=this.manager.get(s.id))==null?void 0:n.update(s))??new this(s);return this.manager.set(r.id,r),r}static async fetchMultiple(t,e,s=1e3){let r="";if(e)for(const[d,a]of Object.entries(e))if(a instanceof Array)r+=`&search[${d}]=${a}`;else if(a instanceof Object)for(const[l,u]of Object.entries(a))r+=`&search[${d}${l}]=${u}`;else r+=`&search[${d}]=${a}`;return(await(await fetch(`${t.api}/tags.json?limit=${s}${r}`)).json()).map(d=>{var l;const a=((l=this.manager.get(d.id))==null?void 0:l.update(d))??new this(d);return this.manager.set(a.id,a),a})}update(t){return Object.assign(this,t),this.$update(),this}$update(){this.post_count$.set(this.post_count),this.name$.set(this.name)}}c(B,"manager",new Map);var P=(i=>(i[i.General=0]="General",i[i.Artist=1]="Artist",i[i.Copyright=3]="Copyright",i[i.Character=4]="Character",i[i.Meta=5]="Meta",i))(P||{});class I{constructor(t){c(this,"name$",$.state("loding..."));Object.assign(this,t),this.update$()}static async fetch(t,e){var n;const s=await fetch(`${t.api}/users/${e}.json`).then(async h=>await h.json()),r=((n=this.manager.get(s.id))==null?void 0:n.update(s))??new this(s);return this.manager.set(r.id,r),r}static async fetchMultiple(t,e,s=200){let r="";if(e)for(const[d,a]of Object.entries(e))if(a instanceof Array)r+=`&search[${d}]=${a}`;else if(a instanceof Object)for(const[l,u]of Object.entries(a))r+=`&search[${d}${l}]=${u}`;else r+=`&search[${d}]=${a}`;return(await(await fetch(`${t.api}/users.json?limit=${s}${r}`)).json()).map(d=>{const a=new this(d);return this.manager.set(a.id,a),a})}update(t){return Object.assign(this,t),this.update$(),this}update$(){this.name$.set(this.name)}}c(I,"manager",new Map);const J=1e3,F=J*60,U=F*60,G=U*24,Nt=G*7,k=new Intl.RelativeTimeFormat("en",{style:"long"});function Ht(i){i=Math.floor(i);const t=i/J,e=i/F,s=i/U,r=i/G,n=i%1e3,h=Math.floor(i%6e4/1e3),o=Math.floor(i%36e5/6e4),d=Math.floor(i%(36e5*24)/36e5),a=h.toString().padStart(2,"0"),l=o.toString().padStart(2,"0"),u=d.toString().padStart(2,"0");return{seconds:t,minutes:e,hours:s,days:r,mil:n,s:h,min:o,h:d,ss:a,mm:l,hh:u}}function Dt(i,t=Date.now()){const e=i-t,s=Math.abs(e);if(sawait h.json()),r=((n=this.manager.get(s.id))==null?void 0:n.update(s))??new this(s);return this.manager.set(r.id,r),I.fetchMultiple(t,{id:[r.uploader_id,r.approver_id].detype(null)}).then(()=>r.update$()),r}static async fetchMultiple(t,e,s=20){let r="";if(e)if(typeof e=="string")r=e;else{r+="&tags=";for(const[a,l]of Object.entries(e)){if(a==="tags"){r+=`${l}`;continue}r.at(-1)!=="="&&(r+=" "),r+=`${a}:${l}`}}const h=await(await fetch(`${t.api}/posts.json?limit=${s}${r}&_method=get`)).json(),o=h.map(a=>{var u;const l=((u=this.manager.get(a.id))==null?void 0:u.update(a))??new this(a);return this.manager.set(l.id,l),l});if(!o.length)return o;const d=[...new Set(h.map(a=>[a.approver_id,a.uploader_id].detype(null)).flat())];return I.fetchMultiple(t,{id:d}).then(()=>o.forEach(a=>a.update$())),o}update$(){var t,e,s;this.uploader$.set(((t=this.uploader)==null?void 0:t.name$)??this.uploader_id.toString()),this.approver$.set(((e=this.approver)==null?void 0:e.name$)??((s=this.approver_id)==null?void 0:s.toString())??"None"),this.created_date$.set(Dt(+new Date(this.created_at))),this.favorites$.set(this.fav_count),this.score$.set(this.score)}update(t){return Object.assign(this,t),this.update$(),this}get pathname(){return`/posts/${this.id}`}get uploader(){return I.manager.get(this.uploader_id)}get approver(){return this.approver_id?I.manager.get(this.approver_id):null}get isVideo(){return this.file_ext==="mp4"||this.file_ext==="webm"||this.file_ext==="zip"}get tags(){const t=this.tag_string.split(" ");return[...B.manager.values()].filter(e=>t.includes(e.name))}}c(R,"manager",new Map);class Y extends p{constructor(e){super("post");c(this,"post");c(this,"$video");c(this,"duration$",$.state(""));this.post=e,this.$video=this.post.isVideo?$("video").width(this.post.image_width).height(this.post.image_height).disablePictureInPicture(!0).loop(!0).muted(!0).hide(!0):null,this.attribute("filetype",this.post.file_ext),this.durationUpdate(),this.build()}build(){var s,r;let e;(s=this.$video)==null||s.on("playing",(n,h)=>{e=setInterval(()=>{this.durationUpdate()},100)}),(r=this.$video)==null||r.on("pause",()=>{clearInterval(e),this.durationUpdate()}),this.content([this.post.isVideo?$("span").class("duration").content(this.duration$):null,$("a").href(this.post.pathname).content(n=>[this.$video,$("img").width(this.post.image_width).height(this.post.image_height).src(this.post.preview_file_url).loading("lazy").once("load",(h,o)=>{this.post.isVideo||o.src(this.post.large_file_url)})]).on("mouseenter",()=>{var n,h;(n=this.$video)!=null&&n.isPlaying||(h=this.$video)==null||h.src(this.post.large_file_url).hide(!1).play().catch(o=>{})}).on("mouseleave",()=>{var n;(n=this.$video)==null||n.pause().currentTime(0).hide(!0)})])}durationUpdate(){if(!this.$video)return;const e=Ht(this.post.media_asset.duration*1e3-this.$video.currentTime()*1e3);this.duration$.set(`${e.hh}:${e.mm}:${e.ss}`)}}const A=100,Vt=new $t(i=>{if(i==="/posts"||i==="/")return"/"},({record:i})=>{const t=$("page").id("root");async function e(r){const n=await R.fetchMultiple(S,r.length?{tags:r}:void 0,A),h=n.filter(f=>f.file_url),o=$("layout").class("post-grid").type("waterfall").column(5).maxHeight(300).gap(10).content([h.map(f=>new Y(f))]).on("resize",()=>{g()});g();let d=n.at(0),a=n.at(-1),l=!1,u=n.length!==A;const m=$("div").class("loader").content(u?"It's End":"Loading...");return window.addEventListener("scroll",async()=>{if(!o.inDOM()||u||l||document.documentElement.scrollTopw.file_url).map(w=>new Y(w))).render(),f.length!==A&&(m.content("It's End"),u=!0),a=f.at(-1),l=!1}),setInterval(async()=>{if(!o.inDOM()||document.documentElement.scrollTop!==0)return;const f=await R.fetchMultiple(S,r.length?{tags:r,id:`${d.id+1}..`}:{id:`${d.id+1}..`},A),w=f.filter(v=>v.file_url);f.length&&(d=f.at(0)),w.length&&o.insert(w.map(v=>new Y(v)),0).render()},1e4),{$layout:o,$loader:m};function g(){if(innerWidth<350)o.column(1);else if(innerWidth<700)o.column(2);else{const f=Math.round(innerWidth/300);o.column(f)}}}const s=new Map;return i.on("open",async()=>{const r=new URL(location.href).searchParams.get("tags")??"",n=s.get(r);if(n){t.content(n),n.render();return}else t.clear();const{$layout:h,$loader:o}=await e(r);t.content([h,o]),h.render(),s.set(r,h),L.recoveryScrollPosition()}),t});class vt{constructor(t){Object.assign(this,t)}static async fetch(t,e){const s=await fetch(`${t.api}/artist_commentaries/${e}.json`);return new this(await s.json())}static async fetchMultiple(t,e,s=200){let r="";if(e)for(const[d,a]of Object.entries(e))if(a instanceof Array)r+=`&search[${d}]=${a}`;else if(a instanceof Object)for(const[l,u]of Object.entries(a))r+=`&search[${d}${l}]=${u}`;else r+=`&search[${d}]=${a}`;return(await(await fetch(`${t.api}/artist_commentaries.json?limit=${s}${r}`)).json()).map(d=>{const a=new this(d);return this.manager.set(a.id,a),a})}}c(vt,"manager",new Map);const jt=new $t("/posts/:id",({params:i})=>{if(!Number(i.id))return"404";const t={$viewer:$("div").class("viewer"),$tags:$("div").class("post-tags"),$commentary:$("section").class("commentary")},e={uploader$:$.state("loading..."),approver$:$.state("loading..."),date$:$.state("loading..."),size$:$.state("loading..."),dimension$:$.state("loading..."),favorites$:$.state(0),score$:$.state(0),ext$:$.state("loading...")};async function s(){const r=R.manager.get(+i.id)??await R.fetch(S,+i.id);t.$viewer.content([r.isVideo?$("video").src(r.file_ext==="zip"?r.large_file_url:r.file_url).controls(!0):$("img").src(r.large_file_url).once("load",(o,d)=>{d.src(r.file_url)})]),e.uploader$.set(r.uploader$),e.approver$.set(r.approver$),e.date$.set(r.created_date$),e.size$.set(kt(r.file_size)),e.dimension$.set(`${r.image_width}x${r.image_height}`),e.favorites$.set(r.favorites$),e.score$.set(r.score$),e.ext$.set(r.file_ext.toUpperCase()),n(),h();async function n(){const o=await B.fetchMultiple(S,{name:{_space:r.tag_string}}),[d,a,l,u,m]=[o.filter(f=>f.category===P.Artist),o.filter(f=>f.category===P.Character),o.filter(f=>f.category===P.General),o.filter(f=>f.category===P.Meta),o.filter(f=>f.category===P.Copyright)];t.$tags.content([g("Artist",d),g("Character",a),g("Copyright",m),g("Meta",u),g("General",l)]);function g(f,w){return w.length?[$("h3").content(f),$("section").content([w.map(v=>$("div").class("tag").content([$("a").class("tag-name").content(v.name).href(`/posts?tags=${v.name}`),$("span").class("tag-post-count").content(v.post_count$)]))])]:null}}async function h(){const o=(await vt.fetchMultiple(S,{post:{_id:r.id}})).at(0);if(!o)return t.$commentary.content("No commentary");t.$commentary.content([o.original_title?$("h3").content(o.original_title):null,$("pre").content(o.original_description)])}}return s(),$("page").id("post").content([t.$viewer,$("div").class("content").content([$("h3").content("Artist's Commentary"),t.$commentary.content("loading...")]),$("div").class("sidebar").content([$("section").class("post-info").content([new x("id").name("Post").value(`#${i.id}`),new x("uploader").name("Uploader").value(e.uploader$),new x("approver").name("Approver").value(e.approver$),new x("date").name("Date").value(e.date$),new x("size").name("Size").value([e.size$,e.dimension$]),new x("file").name("File Type").value(e.ext$),$("div").class("inline").content([new x("favorites").name("Favorites").value(e.favorites$),new x("score").name("Score").value(e.score$)]),$("a").content("Copy link").href(`${S.api}${location.pathname}`).on("click",(r,n)=>{navigator.clipboard.writeText(n.href()),n.content("Copied!"),setTimeout(()=>{n.content("Copy link")},2e3)})]),t.$tags.content("loading...")])])});class x extends p{constructor(e){super("div");c(this,"$name",$("span").class("property-name"));c(this,"$values",$("div").class("property-values"));this.staticClass("property").attribute("property-id",e),this.content([this.$name,this.$values])}name(e){return this.$name.content(e),this}value(e){const s=$.orArrayResolve(e);return this.$values.content(s.map(r=>$("span").staticClass("property-value").content(r))),this}}const S=new It({api:"https://danbooru.donmai.us",name:"Testbooru"}),wt=new L("/");$.anchorPreventDefault=!0;$.anchorHandler=i=>{$.open(i.href())};$(document.body).content([$("app").content([wt.$view])]);wt.addRoute([Vt,jt]).listen(); diff --git a/dist/index.html b/dist/index.html index 8efd20b..f39e1be 100644 --- a/dist/index.html +++ b/dist/index.html @@ -4,9 +4,11 @@ - Danbooru Viewer v0.1 - - + Danbooru Viewer v0.2.5 + + + + diff --git a/index.html b/index.html index 0145299..971b3de 100644 --- a/index.html +++ b/index.html @@ -6,6 +6,8 @@ Danbooru Viewer v0.2.5 + + diff --git a/index.scss b/index.scss index bdc11a8..b8aaf9e 100644 --- a/index.scss +++ b/index.scss @@ -1,38 +1,147 @@ @import '/src/component/PostGrid/$PostGrid'; @import '/src/component/PostTile/$PostTile'; -@import '/src/route/posts/$post'; +@import '/src/component/Searchbar/$Searchbar'; +@import '/src/route/post/$post'; @import '/src/route/gallery/$gallery'; +:root { + --background-color: #1e1e2c; + --background-color-lighter: #3b3b66; + --background-color-light: #24243b; + --primary-color: #d1d1ee; + --primary-color-dark: #9696b3; + --primary-color-darker: #72728d; + --secondary-color: #aeaeec; + --secondary-color-dark: #424268; + + --nav-height: 50px; +} html { overflow-x: hidden; -} -body { - background-color: #1e1e2c; - color: #d1d1ee; - margin: 0; - font-family: Microsoft Yahei; font-size: 14px; - &::-webkit-scrollbar { - background-color: #000000; + ::-webkit-scrollbar { + background-color: var(--background-color); width: 8px; } - &::-webkit-scrollbar-thumb { + ::-webkit-scrollbar-thumb { background-color: #aeaeec; border-radius: 2px; } } +body { + background-color: var(--background-color); + color: var(--primary-color); + margin: 0; + font-family: Microsoft Yahei; +} -app { - display: block; - view { - display: block; +nav { + display: flex; + width: 100%; + height: var(--nav-height); + position: fixed; + top: 0; + z-index: 100; + background-color: var(--background-color); + justify-content: space-between; + align-items: center; + padding-inline: 1rem; + box-sizing: border-box; - page { - min-height: 100%; - padding: 1rem; - display: block; + div.title { + display: flex; + align-items: center; + gap: 0.4rem; + a.booru-name { + color: var(--secondary-color); + text-decoration: none; + h1 { + margin: 0; + } + } + a.version { + color: var(--background-color); + background-color: var(--secondary-color); + padding: 0.2em 0.4em; + border-radius: 0.4rem; + font-size: 0.8rem; + text-decoration: none; } } + button { + padding: 2px 4px; + border-radius: 0.4rem; + border: none; + } + div.searchbar { + padding: 0.4rem 10%; + max-width: 500px; + background-color: var(--background-color); + border: 1px solid var(--primary-color-darker); + border-radius: 0.4rem; + color: var(--primary-color-dark); + cursor: pointer; + &:hover { + background-color: var(--background-color-light); + color: var(--primary-color); + } + } + div.buttons { + display: flex; + align-items: center; + gap: 1rem; + ion-icon { + background-color: var(--background-color); + transition: all 0.3s ease; + border-radius: 1rem; + padding: 0.4rem; + &:hover { + background-color: var(--background-color-lighter); + } + } + ion-icon.search { + display: none; + } + } + + @media (max-width: 800px) { + div.searchbar { + display: none; + } + div.buttons ion-icon.search { + display: inline-block; + } + } +} + +router { + display: block; + position: relative; + padding-top: var(--nav-height); + + route { + display: block; + position: relative; + padding-inline: 10px; + } +} + +button { + background-color: #aeaeec; + padding: 2px 4px; + border-radius: 0.4rem; + border: none; + cursor: pointer; +} + +ion-icon { + font-size: 24px; + color: var(--primary-color); + cursor: pointer; + + &:hover { + color: var(--secondary-color); + } } \ No newline at end of file diff --git a/package.json b/package.json index c3db975..2c2b2d8 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,10 @@ "name": "danbooru-viewer", "module": "index.ts", "type": "module", - "version": "0.2.5", + "version": "0.3.0", "devDependencies": { - "@types/bun": "latest" + "@types/bun": "latest", + "vite": "^5.4.8" }, "peerDependencies": { "typescript": "^5.0.0" diff --git a/src/component/IonIcon/$IonIcon.ts b/src/component/IonIcon/$IonIcon.ts new file mode 100644 index 0000000..45f063e --- /dev/null +++ b/src/component/IonIcon/$IonIcon.ts @@ -0,0 +1,17 @@ +import { $Container } from "elexis"; + +export class $IonIcon extends $Container { + constructor() { + super('ion-icon'); + } + + name(name: string) { + this.attribute('name', name); + return this; + } + + size(size: 'small' | 'large') { + this.attribute('size', size); + return this; + } +} \ No newline at end of file diff --git a/src/component/PostGrid/$PostGrid.ts b/src/component/PostGrid/$PostGrid.ts index d601ea5..4d9c639 100644 --- a/src/component/PostGrid/$PostGrid.ts +++ b/src/component/PostGrid/$PostGrid.ts @@ -1,9 +1,80 @@ import { $Layout } from "@elexis/layout"; +import { Booru } from "../../structure/Booru"; +import { Post } from "../../structure/Post"; +import { $PostTile } from "../PostTile/$PostTile"; +interface $PostGridOptions { + tags?: string +} export class $PostGrid extends $Layout { - constructor() { + posts = new Set(); + $posts = new Set<$PostTile>(); + tags?: string; + constructor(options?: $PostGridOptions) { super(); - this.addStaticClass('post-grid') - this.type('waterfall').column(5).maxHeight(300).gap(10); + this.tags = options?.tags; + this.addStaticClass('post-grid'); + this.type('waterfall').gap(10); + this.init(); } + + protected async init() { + setInterval(() => { if (this.inDOM() && document.documentElement.scrollTop === 0) this.updateNewest(); }, 10000); + Booru.events.on('set', () => { this.removeAll(); }) + this.on('resize', () => this.resize()) + this.loader(); + } + + protected async loader() { + if (!this.inDOM()) return setTimeout(() => this.loader(), 100);; + while (this.inDOM() && document.documentElement.scrollHeight <= innerHeight * 2) { + await this.getPosts(); + if (!this.posts.size) return; + } + if (document.documentElement.scrollTop + innerHeight > document.documentElement.scrollHeight - innerHeight * 2) await this.getPosts(); + setTimeout(() => this.loader(), 100); + } + + protected resize() { + const col = Math.round(this.dom.clientWidth / 300); + this.column(col >= 2 ? col : 2); + } + + addPost(posts: OrArray) { + posts = $.orArrayResolve(posts); + for (const post of posts) { + if (!post.file_url) continue; + if (this.posts.has(post)) continue; + const $post = new $PostTile(post); + this.$posts.add($post); + this.posts.add(post); + } + const $posts = [...this.$posts.values()].sort((a, b) => +b.post.createdDate - +a.post.createdDate); + this.content($posts).render(); + return this; + } + + removeAll() { + this.posts.clear(); + this.$posts.clear(); + this.animate({opacity: [1, 0]}, {duration: 300, easing: 'ease'}, () => this.clear().render()) + return this; + } + + async updateNewest() { + const latestPost = this.sortedPosts.at(0); + const posts = await Post.fetchMultiple(Booru.used, {tags: this.tags, id: latestPost ? `>${latestPost.id}` : undefined}); + this.addPost(posts); + return this; + } + + async getPosts() { + const oldestPost = this.sortedPosts.at(-1); + const posts = await Post.fetchMultiple(Booru.used, {tags: this.tags, id: oldestPost ? `<${oldestPost.id}` : undefined}); + this.addPost(posts); + return this; + } + + get sortedPosts() { return this.posts.array.sort((a, b) => +b.createdDate - +a.createdDate); } + } \ No newline at end of file diff --git a/src/component/PostTile/$PostTile.ts b/src/component/PostTile/$PostTile.ts index 9d9b9a1..700c3a7 100644 --- a/src/component/PostTile/$PostTile.ts +++ b/src/component/PostTile/$PostTile.ts @@ -6,7 +6,7 @@ export class $PostTile extends $Container { $video: $Video | null; duration$ = $.state(``); constructor(post: Post) { - super('post'); + super('post-tile'); this.post = post; this.$video = this.post.isVideo ? $('video').width(this.post.image_width).height(this.post.image_height).disablePictureInPicture(true).loop(true).muted(true).hide(true) : null; this.attribute('filetype', this.post.file_ext); @@ -19,7 +19,7 @@ export class $PostTile extends $Container { this.$video?.on('playing', (e, $video) => { timer = setInterval(() => { this.durationUpdate(); - }, 100) + }, 500) }) this.$video?.on('pause', () => { clearInterval(timer); @@ -27,15 +27,18 @@ export class $PostTile extends $Container { }) this.content([ this.post.isVideo ? $('span').class('duration').content(this.duration$) : null, - $('a').href(this.post.pathname).content($a => [ + $('a').href(this.post.pathname).content(() => [ this.$video, - $('img').width(this.post.image_width).height(this.post.image_height).src(this.post.preview_file_url).loading('lazy') + $('img').css({opacity: '0'}).width(this.post.image_width).height(this.post.image_height).src(this.post.preview_file_url).loading('lazy') .once('load', (e, $img) => { - if (!this.post.isVideo) $img.src(this.post.large_file_url) + if (!this.post.isVideo) $img.src(this.post.large_file_url); + $img.animate({opacity: [0, 1]}, {duration: 300, fill: 'both'}); }) ]) .on('mouseenter', () => { - if (!this.$video?.isPlaying) this.$video?.src(this.post.large_file_url).hide(false).play().catch(err => undefined) + if (!this.$video?.isPlaying) { + this.$video?.src(this.post.large_file_url).hide(false).play().catch(err => undefined) + } }) .on('mouseleave', () => { this.$video?.pause().currentTime(0).hide(true); diff --git a/src/component/PostTile/_$PostTile.scss b/src/component/PostTile/_$PostTile.scss index 0200476..28d2604 100644 --- a/src/component/PostTile/_$PostTile.scss +++ b/src/component/PostTile/_$PostTile.scss @@ -1,33 +1,38 @@ -post { +post-tile { display: block; + transition: 0.3s all ease; + position: relative; - &[filetype="mp4"], &[filetype="webm"], &[filetype="zip"] { - span.duration { - background-color: #ffffff; - color: #000000; + + &[filetype="mp4"], &[filetype="webm"], &[filetype="zip"] { + span.duration { + background-color: #ffffff; + color: #000000; + } + } + span.duration { + position: absolute; + background-color: #00000050; + bottom: 0.3rem; + right: 0.3rem; + padding: 2px 4px; + border-radius: 4px; + font-size: 12px; + text-transform: uppercase; + z-index: 2; + } + img { + border-radius: 10px; + height: 100%; + width: 100%; + vertical-align: top; + } + video { + border-radius: 10px; + height: 100%; + width: 100%; + object-fit: cover; + position: absolute; + z-index: 1; } - } - span.duration { - position: absolute; - background-color: #00000050; - bottom: 0.3rem; - right: 0.3rem; - padding: 2px 4px; - border-radius: 4px; - font-size: 12px; - text-transform: uppercase; - z-index: 2; - } - img { - border-radius: 10px; - height: 100%; - width: 100%; - } - video { - border-radius: 10px; - height: 100%; - width: 100%; - object-fit: cover; - position: absolute; - } } \ No newline at end of file diff --git a/src/component/Searchbar/$Searchbar.ts b/src/component/Searchbar/$Searchbar.ts new file mode 100644 index 0000000..f15b366 --- /dev/null +++ b/src/component/Searchbar/$Searchbar.ts @@ -0,0 +1,312 @@ +import { $Container } from "elexis"; +import { Tag, TagCategory } from "../../structure/Tag"; +import { Booru } from "../../structure/Booru"; + +export class $Searchbar extends $Container { + $tagInput = new $TagInput(this); + $selectionList = new $SelectionList(); + typingTimer: Timer | null = null; + $filter = $('div').class('filter'); + constructor() { + super('searchbar'); + this.build(); + window.addEventListener('keyup', (e) => { + if (!this.inDOM() && e.key === '/') this.activate(); + if (this.inDOM() && e.key === 'Escape') this.inactivate(); + }) + } + + private build() { + this + .content([ + $('div').class('input-container') + .content([ + this.$tagInput + .on('input', () => this.inputHandler()) + .on('keydown', (e) => this.keyHandler(e)), + $('ion-icon').name('close-circle-outline').title('Clear Input') + .on('click', () => this.$tagInput.clearAll()) + ]) + .on('click', (e) => { + if (e.target === this.$tagInput.dom) this.$tagInput.addTag().input(); + }), + $('div').class('selection-list-container').content([ + this.$selectionList + ]), + this.$filter.on('click', () => { + if (location.hash === '#search') $.back(); + }) + ]) + } + + activate() { + this.hide(false); + this.$filter + .animate({ + opacity: [0, 0.5] + }, { duration: 300, easing: 'ease'}) + this.$tagInput.input(); + return this; + } + + inactivate() { + this.animate({ + opacity: [0.5, 0] + }, { duration: 300, easing: 'ease'}, () => this.hide(true)) + return this; + } + + private keyHandler(e: KeyboardEvent) { + const addTag = () => {e.preventDefault(); this.$tagInput.addTag().input()} + const addSelectedTag = ($selection: $Selection) => { + const inputIndex = this.$tagInput.children.indexOf(this.$tagInput.$inputor); + const nextTag = this.$tagInput.children.array.at(inputIndex + 1) as $Tag; + this.$tagInput.addTag($selection.value()); + if (nextTag) this.$tagInput.editTag(nextTag); + else this.$tagInput.input(); + } + switch (e.key) { + case 'ArrowUp': { + e.preventDefault(); + this.$selectionList.focusPrevSelection(); + this.$tagInput.value(this.$selectionList.focused?.value()); + break; + } + case 'ArrowDown': { + e.preventDefault(); + this.$selectionList.focusNextSelection(); + this.$tagInput.value(this.$selectionList.focused?.value()); + break; + } + case ' ': addTag(); break; + case 'Enter': { + e.preventDefault(); + if (this.$selectionList.focused) addSelectedTag(this.$selectionList.focused); + else { + this.$tagInput.addTag(); + this.search(); + } + break; + } + case 'Tab': { + e.preventDefault(); + const inputIndex = this.$tagInput.children.indexOf(this.$tagInput.$inputor) + if (e.shiftKey) { + this.$tagInput.editTag(this.$tagInput.children.array.at(inputIndex - 1) as $Tag) + break; + } + if (this.$selectionList.focused) addSelectedTag(this.$selectionList.focused); + else { + const nextTag = this.$tagInput.children.array.at(inputIndex + 1) as $Tag; + if (nextTag) this.$tagInput.editTag(nextTag); + else this.$tagInput.addTag().input(); + } + break; + } + case 'Backspace': { + const inputIndex = this.$tagInput.children.indexOf(this.$tagInput.$inputor) + if (inputIndex !== 0 && !this.$tagInput.$input.value().length) { + e.preventDefault(); + this.$tagInput.editTag(this.$tagInput.children.array.at(inputIndex - 1) as $Tag) + } + break; + } + } + } + + private inputHandler() { + if (this.typingTimer) { + clearTimeout(this.typingTimer); + this.typingTimer = null; + } + this.typingTimer = setTimeout(async() => { + this.typingTimer = null; + this.getSearchSuggestions(); + }, 200); + } + + async getSearchSuggestions() { + const input = this.$tagInput.$input.value() + if (!input.length) return this.$selectionList.clearSelections(); + const tags = await Tag.fetchMultiple(Booru.used, {fuzzy_name_matches: input, order: 'similarity'}); + this.$selectionList + .clearSelections() + .addSelections(tags.map(tag => new $Selection().value(tag.name) + .content([ + $('span').class('tag-name').content(tag.name), + $('span').class('tag-category').content(TagCategory[tag.category]) + ]) + .on('click', () => {this.$tagInput.addTag(tag.name).input()}) + )) + if (!this.$tagInput.$input.value().length) this.$selectionList.clearSelections(); + } + + search() { + $.replace(`/posts?tags=${this.$tagInput.query}`); + this.$tagInput.clearAll(); + this.inactivate(); + return this; + } + + checkURL(beforeURL: URL, afterURL: URL) { + if (beforeURL.hash === '#search') this.inactivate(); + if (afterURL.hash === '#search') this.activate(); + } +} + +class $SelectionList extends $Container { + focused: $Selection | null = null; + selections = new Set<$Selection>(); + constructor() { + super('selection-list'); + } + + addSelections(selections: OrArray<$Selection>) { + selections = $.orArrayResolve(selections); + for (const $selection of selections) { + this.selections.add($selection); + } + this.insert(selections); + return this; + } + + clearSelections() { + this.focused = null; + this.selections.clear(); + this.clear(); + return this; + } + + focusSelection(selection: $Selection) { + this.blurSelection(); + this.focused = selection; + selection.focus(); + return this; + } + + blurSelection() { + this.focused?.blur(); + this.focused = null; + return this; + } + + focusNextSelection() { + const selections = this.selections.array; + const first = selections.at(0); + if (this.focused) { + const next = selections.at(selections.indexOf(this.focused) + 1); + if (next) this.focusSelection(next); + else if (first) this.focusSelection(first); + } else if (first) this.focusSelection(first); + } + + focusPrevSelection() { + const selections = this.selections.array; + if (this.focused) { + const next = selections.at(selections.indexOf(this.focused) - 1); + if (next) this.focusSelection(next); + } else { + const next = selections.at(0); + if (next) this.focusSelection(next); + } + } +} + +class $Selection extends $Container { + private property = { + value: '' + } + constructor() { + super('selection'); + } + + value(): string; + value(value: string): this; + value(value?: string) { return $.fluent(this, arguments, () => this.property.value, () => $.set(this.property, 'value', value))} + + focus() { + this.addClass('active'); + return this; + } + + blur() { + this.removeClass('active'); + return this; + } +} + +class $TagInput extends $Container { + $input = $('input').type('text'); + $sizer = $('span').class('sizer'); + $inputor = $('div').class('input-wrapper').content([ + this.$sizer, + this.$input + .on('input', () => { + this.$sizer.content(this.$input.value()); + }) + ]) + tags = new Set<$Tag>(); + $seachbar: $Searchbar + constructor($seachbar: $Searchbar) { + super('tag-input'); + this.$seachbar = $seachbar; + } + + input() { + this.insert(this.$inputor); + this.$input.focus(); + if (this.$input.value()) this.$seachbar.getSearchSuggestions(); + else this.$seachbar.$selectionList.clearSelections(); + return this; + } + + addTag(tagName?: string) { + tagName = tagName ?? this.$input.value(); + if (!tagName.length) return this; + const $tag = new $Tag(tagName); + $tag.on('click', () => this.editTag($tag)) + this.tags.add($tag); + this.value(''); + this.$inputor.replace($tag); + return this; + } + + editTag($tag: $Tag) { + this.addTag(); + this.tags.delete($tag); + $tag.replace(this.$inputor); + this.value($tag.name); + this.$input.focus(); + this.$seachbar.getSearchSuggestions(); + return this; + } + + clearAll() { + this.value(''); + this.tags.clear(); + this.clear(); + return this; + } + + value(value?: string) { + if (value === undefined) return this; + this.$input.value(value); + this.$sizer.content(value); + return this; + } + + get query() { return this.tags.array.map(tag => tag.name).toString().replace(',', '+') } +} + +class $Tag extends $Container { + name: string; + constructor(name: string) { + super('tag'); + this.name = name; + this.build(); + } + + private build() { + this.content(this.name) + } +} \ No newline at end of file diff --git a/src/component/Searchbar/_$Searchbar.scss b/src/component/Searchbar/_$Searchbar.scss new file mode 100644 index 0000000..a3b1d41 --- /dev/null +++ b/src/component/Searchbar/_$Searchbar.scss @@ -0,0 +1,158 @@ +searchbar { + display: flex; + // justify-content: center; + align-items: center; + flex-direction: column; + width: 100%; + z-index: 200; + position: fixed; + height: 100%; + + div.input-container { + margin-top: 0.4rem; + background-color: var(--background-color-light); + border-radius: 0.4rem; + font-size: 1rem; + width: 500px; + padding: 0.4rem 0.4rem; + max-width: calc(100% - 2rem); + box-sizing: border-box; + z-index: 201; + display: flex; + align-items: center; + border: 1px solid var(--secondary-color-dark); + + &:focus-within { + outline: none; + // border-color: var(--primary-color-dark); + } + + tag-input { + display: flex; + gap: 0.4rem; + width: 100%; + overflow: hidden; + padding-inline: 0.4rem; + box-sizing: border-box; + cursor: text; + + tag { + display: inline-block; + padding: 0.2rem 0.4rem; + background-color: var(--secondary-color-dark); + color: var(--secondary-color); + border-radius: 0.4rem; + cursor: pointer; + } + } + + ion-icon { + font-size: 20px; + color: var(--secondary-color-dark); + cursor: pointer; + &:hover { + color: var(--secondary-color); + } + } + } + + div.selection-list-container { + overflow: hidden; + border-radius: 0.4rem; + background-color: var(--background-color); + z-index: 201; + max-width: calc(100% - 2rem); + width: 500px; + + selection-list { + display: block; + max-height: 40vh; + overflow-y: scroll; + overflow-x: hidden; + position: relative; + + selection { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.4rem 1rem; + cursor: pointer; + + &:hover { + background-color: color-mix(in srgb, var(--background-color-lighter) 50%, transparent); + } + &.active { + background-color: var(--background-color-lighter); + } + .tag-name { + } + .tag-category { + padding: 0.1rem 0.4rem; + border-radius: 0.4rem; + font-size: 0.9rem; + background-color: var(--secondary-color-dark); + color: var(--secondary-color); + } + + } + } + } + + div.filter { + background-color: var(--background-color); + opacity: 0.5; + position: fixed; + top: 0; + height: 100%; + width: 100%; + z-index: 199; + } +} + +.input-wrapper { + color: var(--primary-color); + border: 1px solid var(--secondary-color); + border-radius: 0.4rem; + position: relative; + box-sizing: border-box; + line-height: 1em; + font-size: 14px; + padding: 4px 8px; + display: inline-block; + max-width: 100%; + text-overflow: ellipsis; + // overflow: hidden; + + span.sizer { + font-family: inherit; + white-space: pre; + height: 1em; + display: inline-block; + font-size: inherit; + line-height: inherit; + box-sizing: border-box; + position: relative; + opacity: 0; + min-width: 2px; + user-select: none; + vertical-align: top; + } + input { + color: inherit; + height: 100%; + text-overflow: ellipsis; + font-family: inherit; + background: none; + color: inherit; + top: 0; + left: 0; + font-size: inherit; + line-height: inherit; + padding: inherit; + position: absolute; + box-sizing: border-box; + width: 100%; + border: none; + outline: none; + } +} \ No newline at end of file diff --git a/src/env.d.ts b/src/env.d.ts new file mode 100644 index 0000000..05904ad --- /dev/null +++ b/src/env.d.ts @@ -0,0 +1 @@ +declare const __APP_VERSION__: string; \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 7b4dba4..a772294 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,27 +1,101 @@ import 'elexis'; import '@elexis/layout'; import '@elexis/router'; -import { Booru } from './structure/Booru'; -import { Router } from '@elexis/router'; -import { home_route } from './route/gallery/$gallery'; -import { posts_route } from './route/posts/$post'; - -export const booru = new Booru({ - api: 'https://danbooru.donmai.us', - name: 'Testbooru' -}) - -const router = new Router('/'); -$.anchorPreventDefault = true; -$.anchorHandler = ($a) => { $.open($a.href())} - +import { Booru, type BooruOptions } from './structure/Booru'; +import { post_route } from './route/post/$post'; +import { $PostGrid } from './component/PostGrid/$PostGrid'; +import { $Router, $RouterNavigationDirection } from '@elexis/router'; +import { $Searchbar } from './component/Searchbar/$Searchbar'; +import { $IonIcon } from './component/IonIcon/$IonIcon'; +// declare elexis module +declare module 'elexis' { + export namespace $ { + export interface TagNameElementMap { + 'ion-icon': typeof $IonIcon + } + } +} +$.registerTagName('ion-icon', $IonIcon) +$.anchorHandler = ($a) => { $.open($a.href(), $a.target())} +// settings +export const [danbooru, safebooru]: Booru[] = [ + new Booru({ origin: 'https://danbooru.donmai.us', name: 'Danbooru' }), + new Booru({ origin: 'https://safebooru.donmai.us', name: 'Safebooru' }), + new Booru({ origin: 'https://testbooru.donmai.us', name: 'Testbooru' }), +] +Booru.set(Booru.manager.get(Booru.storageAPI ?? '') ?? danbooru); +const $searchbar = new $Searchbar().hide(true); +if (location.hash === '#search') $searchbar.activate(); +// render $(document.body).content([ - $('app').content([ - router.$view - ]) + // Navigation Bar + $('nav').content([ + // Title + $('div').class('title').content([ + $('a').class('booru-name').content([$('h1').content(Booru.name$)]).href('/'), + $('a').class('version').target('_blank').content(`v${__APP_VERSION__}`).href(`https://git.defaultkavy.com/defaultkavy/danbooru-viewer`) + ]), + // Searchbar + $('div').class('searchbar').content(['Search in ', Booru.name$]) + .self($self => $Router.events.on('stateChange', ({beforeURL, afterURL}) => {if (beforeURL.hash === '#search') $self.hide(false); if (afterURL.hash === '#search') $self.hide(true)})) + .on('click', () => $.open(location.href + '#search')), + // Buttons + $('div').class('buttons').content([ + // Search Icon + $('ion-icon').class('search').name('search-outline').title('Search') + .self($self => $Router.events.on('stateChange', ({beforeURL, afterURL}) => {if (beforeURL.hash === '#search') $self.hide(false); if (afterURL.hash === '#search') $self.hide(true)})) + .on('click', () => $.open(location.href + '#search')), + // Switch Button + $('ion-icon').class('switch').name('swap-horizontal').title('Switch Booru') + .on('click', () => { + if (Booru.used === danbooru) Booru.set(safebooru); + else Booru.set(danbooru); + }) + ]) + ]), + // Searchbar + $searchbar, + // Base Router + $('router').base('/').map([ + // Home Page + $('route').id('posts').path('/').builder(() => new $PostGrid()), + // Posts Page + $('route').id('posts').path('/posts?tags').builder(({query}) => new $PostGrid({tags: query.tags})), + // Post Page + post_route + ]).on('beforeSwitch', (e) => { + const DURATION = 300; + e.preventDefault(); + function intro() { + e.$view.content(e.nextContent); + e.rendered(); + e.nextContent.element?.class('animated').animate({ + opacity: [0, 1], + transform: $Router.navigationDirection === $RouterNavigationDirection.Forward ? [`translateX(40%)`, `translateX(0%)`] : [`translateX(-40%)`, `translateX(0%)`] + }, { + duration: DURATION, + easing: 'ease' + }, () => { + e.switched(); + e.nextContent.element?.removeClass('animated') + }) + } + function outro() { + e.previousContent?.element?.class('animated').animate({ + opacity: [1, 0], + transform: $Router.navigationDirection === $RouterNavigationDirection.Forward ? [`translateX(0%)`, `translateX(-40%)`] : [`translateX(0%)`, `translateX(40%)`] + }, { + duration: DURATION, + easing: 'ease' + }, () => { + e.previousContent?.element?.removeClass('animated'); + intro(); + }) + } + + if (e.previousContent) outro(); + else intro(); + }) ]) -router.addRoute([ - home_route, - posts_route -]).listen(); \ No newline at end of file +$Router.events.on('stateChange', ({beforeURL, afterURL}) => { $searchbar.checkURL(beforeURL, afterURL) }) \ No newline at end of file diff --git a/src/route/gallery/$gallery.ts b/src/route/gallery/$gallery.ts index c070a6e..ca192c1 100644 --- a/src/route/gallery/$gallery.ts +++ b/src/route/gallery/$gallery.ts @@ -1,12 +1,9 @@ -import { Route, Router } from "@elexis/router"; import { Post } from "../../structure/Post"; import { booru } from "../../main"; import { $PostGrid } from "../../component/PostGrid/$PostGrid"; import { $PostTile } from "../../component/PostTile/$PostTile"; const MAX_POST_LENGTH = 100; -export const home_route = new Route((path) => { - if (path === '/posts' || path === '/') return '/'; -}, ({record}) => { +export const home_route = $('route').path(['/', '/posts']).builder((record) => { const $page = $('page').id('root'); async function load(tags: string) { const posts = await Post.fetchMultiple(booru, tags.length ? {tags: tags} : undefined, MAX_POST_LENGTH) @@ -61,7 +58,7 @@ export const home_route = new Route((path) => { } const gridManager = new Map(); - record.on('open', async () => { + record.$route.events.on('opened', async () => { const tags = new URL(location.href).searchParams.get('tags') ?? ''; const $cacheGrid = gridManager.get(tags); if ($cacheGrid) { @@ -78,7 +75,6 @@ export const home_route = new Route((path) => { ]); $layout.render(); gridManager.set(tags, $layout); - Router.recoveryScrollPosition(); }) return $page; }) \ No newline at end of file diff --git a/src/route/posts/$post.ts b/src/route/post/$post.ts similarity index 66% rename from src/route/posts/$post.ts rename to src/route/post/$post.ts index dd76313..4c0969f 100644 --- a/src/route/posts/$post.ts +++ b/src/route/post/$post.ts @@ -1,48 +1,30 @@ -import { Route } from "@elexis/router"; import { Post } from "../../structure/Post"; -import { booru } from "../../main"; import { $Container, type $ContainerContentType } from "elexis"; -import { digitalUnit } from "../../structure/Util"; import { Tag, TagCategory } from "../../structure/Tag"; import { ArtistCommentary } from "../../structure/Commentary"; +import { Booru } from "../../structure/Booru"; -export const posts_route = new Route('/posts/:id', ({params}) => { +export const post_route = $('route').path('/posts/:id').id('post').builder(({$route, params}) => { if (!Number(params.id)) return '404'; + const post = new Post(Booru.used, +params.id); const ele = { $viewer: $('div').class('viewer'), $tags: $('div').class('post-tags'), $commentary: $('section').class('commentary') } - const value = { - uploader$: $.state('loading...'), - approver$: $.state('loading...'), - date$: $.state('loading...'), - size$: $.state('loading...'), - dimension$: $.state(`loading...`), - favorites$: $.state(0), - score$: $.state(0), - ext$: $.state(`loading...`), - } + load(); async function load() { - const post = Post.manager.get(+params.id) ?? await Post.fetch(booru, +params.id); + await post.fetch(); ele.$viewer.content([ post.isVideo ? $('video').src(post.file_ext === 'zip' ? post.large_file_url : post.file_url).controls(true) - : $('img').src(post.large_file_url).once('load', (e, $img) => { $img.src(post.file_url)}) + : $('img').src(post.large_file_url)//.once('load', (e, $img) => { $img.src(post.file_url)}) ]) - value.uploader$.set(post.uploader$); - value.approver$.set(post.approver$); - value.date$.set(post.created_date$); - value.size$.set(digitalUnit(post.file_size)); - value.dimension$.set(`${post.image_width}x${post.image_height}`) - value.favorites$.set(post.favorites$) - value.score$.set(post.score$) - value.ext$.set(post.file_ext.toUpperCase()) loadTags(); loadCommentary(); async function loadTags() { - const tags = await Tag.fetchMultiple(booru, {name: {_space: post.tag_string}}); + const tags = await post.fetchTags(); const [artist_tags, char_tags, gen_tags, meta_tags, copy_tags] = [ tags.filter(tag => tag.category === TagCategory.Artist), tags.filter(tag => tag.category === TagCategory.Character), @@ -71,7 +53,7 @@ export const posts_route = new Route('/posts/:id', ({params}) => { } } async function loadCommentary() { - const commentary = (await ArtistCommentary.fetchMultiple(booru, {post: {_id: post.id}})).at(0); + const commentary = (await ArtistCommentary.fetchMultiple(Booru.used, {post: {_id: post.id}})).at(0); if (!commentary) return ele.$commentary.content('No commentary'); ele.$commentary.content([ commentary.original_title ? $('h3').content(commentary.original_title) : null, @@ -79,9 +61,7 @@ export const posts_route = new Route('/posts/:id', ({params}) => { ]) } } - - load(); - return $('page').id('post').content([ + return [ ele.$viewer, $('div').class('content').content([ $('h3').content(`Artist's Commentary`), @@ -90,27 +70,34 @@ export const posts_route = new Route('/posts/:id', ({params}) => { $('div').class('sidebar').content([ $('section').class('post-info').content([ new $Property('id').name('Post').value(`#${params.id}`), - new $Property('uploader').name('Uploader').value(value.uploader$), - new $Property('approver').name('Approver').value(value.approver$), - new $Property('date').name('Date').value(value.date$), - new $Property('size').name('Size').value([value.size$, value.dimension$]), - new $Property('file').name('File Type').value(value.ext$), + new $Property('uploader').name('Uploader').value(post.uploader$), + new $Property('approver').name('Approver').value(post.approver$), + new $Property('date').name('Date').value(post.created_date$), + new $Property('size').name('Size').value([post.file_size$, post.dimension$]), + new $Property('file').name('File Type').value(post.file_ext$), $('div').class('inline').content([ - new $Property('favorites').name('Favorites').value(value.favorites$), - new $Property('score').name('Score').value(value.score$) + new $Property('favorites').name('Favorites').value(post.favorites$), + new $Property('score').name('Score').value(post.score$) ]), - $('a').content('Copy link').href(`${booru.api}${location.pathname}`) - .on('click', (e, $a) => { - navigator.clipboard.writeText($a.href()); - $a.content('Copied!'); + $('button').content('Copy link') + .on('click', (e, $button) => { + e.preventDefault(); + navigator.clipboard.writeText(`${Booru.used.origin}${location.pathname}`); + $button.content('Copied!'); setTimeout(() => { - $a.content('Copy link') + $button.content('Copy link') }, 2000); }) ]), ele.$tags.content('loading...') - ]) - ]) + ]).self($sidebar => { + let scrollTop = 0; + addEventListener('scroll', () => { if ($sidebar.inDOM()) scrollTop = document.documentElement.scrollTop }) + $route + .on('beforeShift', () => { if (innerWidth > 800) $sidebar.css({position: `absolute`, top: `${scrollTop}px`}) }) + .on('afterShift', () => $sidebar.css({position: '', top: ''})) + }) + ] }) class $Property extends $Container { diff --git a/src/route/posts/_$post.scss b/src/route/post/_$post.scss similarity index 84% rename from src/route/posts/_$post.scss rename to src/route/post/_$post.scss index 505d409..2d61cc4 100644 --- a/src/route/posts/_$post.scss +++ b/src/route/post/_$post.scss @@ -7,7 +7,7 @@ } div.viewer { - height: calc(100vh - 2rem); + height: calc(100vh - 2rem - var(--nav-height)); display: flex; justify-content: center; align-items: center; @@ -19,7 +19,7 @@ @media (max-width: 800px) { width: 100%; - height: calc(100vh); + height: calc(100vh - var(--nav-height)); border-radius: 0; margin:0; } @@ -27,10 +27,14 @@ img { max-width: 100%; max-height: 100%; + // transition: all 0.3s ease; } + video { max-width: 100%; max-height: 100%; + -webkit-user-drag: none; + transition: all 0.3s ease; } } @@ -70,7 +74,7 @@ div.sidebar { position: fixed; - top: 1rem; + top: calc(var(--nav-height) + 1rem); right: 1rem; display: flex; flex-direction: column; @@ -78,7 +82,7 @@ width: 300px; overflow: scroll; overflow-x: hidden; - height: calc(100vh - 2rem); + height: calc(100vh - 2rem - var(--nav-height)); border-radius: 20px; @media (max-width: 800px) { @@ -120,8 +124,8 @@ display: flex; gap: 0.4rem; span.property-value { - background-color: #525278; - color: #aeaeec; + background-color: var(--secondary-color-dark); + color: var(--secondary-color); padding: 2px 4px; border-radius: 4px; } @@ -143,8 +147,8 @@ text-decoration: none; } span.tag-post-count { - background-color: #525278; - color: #aeaeec; + background-color: var(--secondary-color-dark); + color: var(--secondary-color); padding: 0px 4px; border-radius: 4px; font-size: 12px; @@ -153,4 +157,9 @@ } } } -} \ No newline at end of file +} + +// animated resolver +// .animated div.sidebar { +// top: 0 !important; +// } \ No newline at end of file diff --git a/src/structure/Booru.ts b/src/structure/Booru.ts index 608830b..6e74510 100644 --- a/src/structure/Booru.ts +++ b/src/structure/Booru.ts @@ -1,11 +1,38 @@ +import { $EventManager, type $EventMap } from "elexis"; +import type { Post } from "./Post"; +import type { Tag } from "./Tag"; + export interface BooruOptions { - api: string; + origin: string; name: string; } export interface Booru extends BooruOptions {} export class Booru { + static used: Booru; + static events = new $EventManager(); + static name$ = $.state(this.name); + static manager = new Map() + posts = new Map(); + tags = new Map(); constructor(options: BooruOptions) { Object.assign(this, options); - if (this.api.endsWith('/')) this.api = this.api.slice(0, -1) + if (this.origin.endsWith('/')) this.origin = this.origin.slice(0, -1); + Booru.manager.set(this.name, this); } + + static set(booru: Booru) { + this.used = booru; + this.name$.set(booru.name); + this.storageAPI = booru.name; + this.events.fire('set'); + return this; + } + + static get storageAPI() { return localStorage.getItem('booru_api'); } + static set storageAPI(name: string | null) { if (name) localStorage.setItem('booru_api', name); else localStorage.removeItem('booru_api') } + +} + +interface BooruEventMap extends $EventMap { + set: [] } \ No newline at end of file diff --git a/src/structure/Commentary.ts b/src/structure/Commentary.ts index d072746..5619fce 100644 --- a/src/structure/Commentary.ts +++ b/src/structure/Commentary.ts @@ -8,7 +8,7 @@ export class ArtistCommentary { } static async fetch(booru: Booru, id: id) { - const req = await fetch(`${booru.api}/artist_commentaries/${id}.json`); + const req = await fetch(`${booru.origin}/artist_commentaries/${id}.json`); const post = new this(await req.json()); return post; } @@ -26,7 +26,7 @@ export class ArtistCommentary { else searchQuery += `&search[${key}]=${val}` } } - const req = await fetch(`${booru.api}/artist_commentaries.json?limit=${limit}${searchQuery}`); + const req = await fetch(`${booru.origin}/artist_commentaries.json?limit=${limit}${searchQuery}`); const dataArray: ArtistCommentaryData[] = await req.json(); const list = dataArray.map(data => { const instance = new this(data); diff --git a/src/structure/Post.ts b/src/structure/Post.ts index 4a5972b..c675d02 100644 --- a/src/structure/Post.ts +++ b/src/structure/Post.ts @@ -1,28 +1,34 @@ -import { $ } from "elexis"; -import type { Booru } from "./Booru"; +import { $, $EventManager } from "elexis"; +import { Booru } from "./Booru"; import { Tag } from "./Tag"; import { User } from "./User"; -import { dateFrom } from "./Util"; +import { dateFrom, digitalUnit } from "./Util"; export interface PostOptions {} export interface Post extends PostData {} -export class Post { - static manager = new Map(); +export class Post extends $EventManager<{update: []}> { uploader$ = $.state(this.uploader?.name$ ?? this.uploader_id?.toString()); approver$ = $.state(this.approver?.name$ ?? this.approver_id?.toString() ?? 'None'); created_date$ = $.state(``); favorites$ = $.state(this.fav_count); score$ = $.state(this.score); - constructor(data: PostData) { - Object.assign(this, data); - this.update$(); + file_size$ = $.state(''); + file_ext$ = $.state(this.file_ext); + dimension$ = $.state(''); + createdDate = new Date(this.created_at); + + booru: Booru; + constructor(booru: Booru, id: id) { + super(); + this.booru = booru; + this.id = id; + booru.posts.set(this.id, this); } - static async fetch(booru: Booru, id: id) { - const data = await fetch(`${booru.api}/posts/${id}.json`).then(async data => await data.json()) as PostData; - const instance = this.manager.get(data.id)?.update(data) ?? new this(data); - this.manager.set(instance.id, instance); - User.fetchMultiple(booru, {id: [instance.uploader_id, instance.approver_id].detype(null)}).then(() => instance.update$()); - return instance; + async fetch() { + const data = await fetch(`${this.booru.origin}/posts/${this.id}.json`).then(async data => await data.json()) as PostData; + this.update(data); + User.fetchMultiple(this.booru, {id: [this.uploader_id, this.approver_id].detype(null)}).then(() => this.update$()); + return this; } static async fetchMultiple(booru: Booru, tags?: Partial | string, limit = 20) { @@ -30,19 +36,20 @@ export class Post { if (tags) { if (typeof tags === 'string') tagsQuery = tags; else { - tagsQuery += '&tags=' for (const [key, val] of Object.entries(tags)) { + if (val === undefined) continue; if (key === 'tags') { tagsQuery += `${val}`; continue; } if (tagsQuery.at(-1) !== '=') tagsQuery += ' '; // add space between tags tagsQuery += `${key}:${val}` } } } - const req = await fetch(`${booru.api}/posts.json?limit=${limit}${tagsQuery}&_method=get`); + const req = await fetch(`${booru.origin}/posts.json?limit=${limit}&tags=${tagsQuery}&_method=get`); const dataArray: PostData[] = await req.json(); const list = dataArray.map(data => { - const instance = this.manager.get(data.id)?.update(data) ?? new this(data); - this.manager.set(instance.id, instance); + const instance = booru.posts.get(data.id)?.update(data) ?? new this(booru, data.id); + instance.update(data); + booru.posts.set(instance.id, instance); return instance; }); if (!list.length) return list; @@ -52,11 +59,16 @@ export class Post { } update$() { - this.uploader$.set(this.uploader?.name$ ?? this.uploader_id.toString()); + this.uploader$.set(this.uploader?.name$ ?? this.uploader_id?.toString()); this.approver$.set(this.approver?.name$ ?? this.approver_id?.toString() ?? 'None'); this.created_date$.set(dateFrom(+new Date(this.created_at))); this.favorites$.set(this.fav_count); this.score$.set(this.score); + this.file_size$.set(digitalUnit(this.file_size)); + this.file_ext$.set(this.file_ext as any); + this.dimension$.set(`${this.image_width}x${this.image_height}`); + this.createdDate = new Date(this.created_at); + this.fire('update'); } update(data: PostData) { @@ -65,13 +77,17 @@ export class Post { return this; } + async fetchTags() { + return await Tag.fetchMultiple(this.booru, {name: {_space: this.tag_string}}); + } + get pathname() { return `/posts/${this.id}` } get uploader() { return User.manager.get(this.uploader_id); } get approver() { if (this.approver_id) return User.manager.get(this.approver_id); else return null } get isVideo() { return this.file_ext === 'mp4' || this.file_ext === 'webm' || this.file_ext === 'zip' } get tags() { const tag_list = this.tag_string.split(' '); - return [...Tag.manager.values()].filter(tag => tag_list.includes(tag.name)) + return [...this.booru.tags.values()].filter(tag => tag_list.includes(tag.name)) } } diff --git a/src/structure/Tag.ts b/src/structure/Tag.ts index 7815299..3e59db7 100644 --- a/src/structure/Tag.ts +++ b/src/structure/Tag.ts @@ -4,18 +4,19 @@ const INTL_number = new Intl.NumberFormat('en', {notation: 'compact'}) export interface TagOptions {} export interface Tag extends TagData {} export class Tag { - static manager = new Map(); post_count$ = $.state(0, {format: (value) => `${INTL_number.format(value)}`}); name$ = $.state(''); - constructor(data: TagData) { + booru: Booru; + constructor(booru: Booru, data: TagData) { + this.booru = booru; Object.assign(this, data); this.$update(); } static async fetch(booru: Booru, id: id) { - const data = await fetch(`${booru.api}/tags/${id}.json`).then(async data => await data.json()) as TagData; - const instance = this.manager.get(data.id)?.update(data) ?? new this(data); - this.manager.set(instance.id, instance); + const data = await fetch(`${booru.origin}/tags/${id}.json`).then(async data => await data.json()) as TagData; + const instance = booru.tags.get(data.id)?.update(data) ?? new this(booru, data); + booru.tags.set(instance.id, instance); return instance; } @@ -32,11 +33,11 @@ export class Tag { else searchQuery += `&search[${key}]=${val}` } } - const req = await fetch(`${booru.api}/tags.json?limit=${limit}${searchQuery}`); + const req = await fetch(`${booru.origin}/tags.json?limit=${limit}${searchQuery}`); const dataArray: TagData[] = await req.json(); const list = dataArray.map(data => { - const instance = this.manager.get(data.id)?.update(data) ?? new this(data); - this.manager.set(instance.id, instance); + const instance = booru.tags.get(data.id)?.update(data) ?? new this(booru, data); + booru.tags.set(instance.id, instance); return instance; }); return list; diff --git a/src/structure/Test.ts b/src/structure/Test.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/structure/User.ts b/src/structure/User.ts index f3bb1ee..efe8a24 100644 --- a/src/structure/User.ts +++ b/src/structure/User.ts @@ -11,7 +11,7 @@ export class User { } static async fetch(booru: Booru, id: id) { - const data = await fetch(`${booru.api}/users/${id}.json`).then(async data => await data.json()) as UserData; + const data = await fetch(`${booru.origin}/users/${id}.json`).then(async data => await data.json()) as UserData; const instance = this.manager.get(data.id)?.update(data) ?? new this(data); this.manager.set(instance.id, instance); return instance; @@ -30,7 +30,7 @@ export class User { else searchQuery += `&search[${key}]=${val}` } } - const req = await fetch(`${booru.api}/users.json?limit=${limit}${searchQuery}`); + const req = await fetch(`${booru.origin}/users.json?limit=${limit}${searchQuery}`); const dataArray: UserData[] = await req.json(); const list = dataArray.map(data => { const instance = new this(data); diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..f0b2a75 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite'; +export default defineConfig({ + define: { + __APP_VERSION__: JSON.stringify(process.env.npm_package_version) + } +}) \ No newline at end of file