diff --git a/build/public/assets/index.4ee35f15.js b/build/public/assets/index.4ee35f15.js new file mode 100644 index 0000000..6b3125b --- /dev/null +++ b/build/public/assets/index.4ee35f15.js @@ -0,0 +1 @@ +import{d as e,c as t,a as o,w as n,b as l,r as a,o as s,e as i,t as r,F as d,f as c,g as u,h as p,v as g,i as h,j as m,p as y,k as f,l as v,m as w,n as b,q as x,s as C,u as k,x as A,y as S}from"./vendor.684f7bc8.js";var P=e({name:"app",computed:{showNav(){return!["game","replay"].includes(String(this.$route.name))}}});const T={id:"app"},I={key:0,class:"nav"},z=i("Index"),D=i("New game");P.render=function(e,i,r,d,c,u){const p=a("router-link"),g=a("router-view");return s(),t("div",T,[e.showNav?(s(),t("ul",I,[o("li",null,[o(p,{class:"btn",to:{name:"index"}},{default:n((()=>[z])),_:1})]),o("li",null,[o(p,{class:"btn",to:{name:"new-game"}},{default:n((()=>[D])),_:1})])])):l("",!0),o(g)])};const E=864e5,_=e=>{const t=Math.floor(e/E);e%=E;const o=Math.floor(e/36e5);e%=36e5;const n=Math.floor(e/6e4);e%=6e4;return`${t}d ${o}h ${n}m ${Math.floor(e/1e3)}s`};var M=1,V=1e3,N=()=>{const e=new Date;return Date.UTC(e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate(),e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds(),e.getUTCMilliseconds())},O=(e,t)=>_(t-e),U=_,B=e({name:"game-teaser",props:{game:{type:Object,required:!0}},computed:{style(){return{"background-image":`url("${this.game.imageUrl.replace("uploads/","uploads/r/")+"-375x210.webp"}")`}}},methods:{time(e,t){const o=t?"🏁":"⏳",n=e,l=t||N();return`${o} ${O(n,l)}`}}});const R={class:"game-info-text"},G=o("br",null,null,-1),$=o("br",null,null,-1),L=o("br",null,null,-1),F=i(" β†ͺ️ Watch replay ");B.render=function(e,d,c,u,p,g){const h=a("router-link");return s(),t("div",{class:"game-teaser",style:e.style},[o(h,{class:"game-info",to:{name:"game",params:{id:e.game.id}}},{default:n((()=>[o("span",R,[i(" 🧩 "+r(e.game.tilesFinished)+"/"+r(e.game.tilesTotal),1),G,i(" πŸ‘₯ "+r(e.game.players),1),$,i(" "+r(e.time(e.game.started,e.game.finished)),1),L])])),_:1},8,["to"]),e.game.hasReplay?(s(),t(h,{key:0,class:"game-replay",to:{name:"replay",params:{id:e.game.id}}},{default:n((()=>[F])),_:1},8,["to"])):l("",!0)],4)};var j=e({components:{GameTeaser:B},data:()=>({gamesRunning:[],gamesFinished:[]}),async created(){const e=await fetch("/api/index-data"),t=await e.json();this.gamesRunning=t.gamesRunning,this.gamesFinished=t.gamesFinished}});const W=o("h1",null,"Running games",-1),H=o("h1",null,"Finished games",-1);j.render=function(e,n,l,i,r,u){const p=a("game-teaser");return s(),t("div",null,[W,(s(!0),t(d,null,c(e.gamesRunning,((e,n)=>(s(),t("div",{class:"game-teaser-wrap",key:n},[o(p,{game:e},null,8,["game"])])))),128)),H,(s(!0),t(d,null,c(e.gamesFinished,((e,n)=>(s(),t("div",{class:"game-teaser-wrap",key:n},[o(p,{game:e},null,8,["game"])])))),128))])};var K=e({name:"image-teaser",props:{image:{type:Object,required:!0}},computed:{style(){return{backgroundImage:`url("${this.image.url.replace("uploads/","uploads/r/")+"-150x100.webp"}")`}}},emits:{click:null,editClick:null},methods:{onClick(){this.$emit("click")},onEditClick(){this.$emit("editClick")}}});K.render=function(e,n,l,a,i,r){return s(),t("div",{class:"imageteaser",style:e.style,onClick:n[2]||(n[2]=(...t)=>e.onClick&&e.onClick(...t))},[o("div",{class:"btn edit",onClick:n[1]||(n[1]=u(((...t)=>e.onEditClick&&e.onEditClick(...t)),["stop"]))},"✏️")],4)};var Y,q,Q,Z,X,J,ee,te,oe=e({name:"image-library",components:{ImageTeaser:K},props:{images:{type:Array,required:!0}},emits:{imageClicked:null,imageEditClicked:null},methods:{imageClicked(e){this.$emit("imageClicked",e)},imageEditClicked(e){this.$emit("imageEditClicked",e)}}});oe.render=function(e,o,n,l,i,r){const u=a("image-teaser");return s(),t("div",null,[(s(!0),t(d,null,c(e.images,((o,n)=>(s(),t(u,{image:o,onClick:t=>e.imageClicked(o),onEditClick:t=>e.imageEditClicked(o),key:n},null,8,["image","onClick","onEditClick"])))),128))])},(q=Y||(Y={}))[q.Flat=0]="Flat",q[q.Out=1]="Out",q[q.In=-1]="In",(Z=Q||(Q={}))[Z.FINAL=0]="FINAL",Z[Z.ANY=1]="ANY",(J=X||(X={}))[J.NORMAL=0]="NORMAL",J[J.ANY=1]="ANY",J[J.FLAT=2]="FLAT",(te=ee||(ee={}))[te.NORMAL=0]="NORMAL",te[te.REAL=1]="REAL";class ne{constructor(e){this.rand_high=e||3735929054,this.rand_low=1231121986^e}random(e,t){this.rand_high=(this.rand_high<<16)+(this.rand_high>>16)+this.rand_low&4294967295,this.rand_low=this.rand_low+this.rand_high&4294967295;return e+(this.rand_high>>>0)/4294967295*(t-e+1)|0}choice(e){return e[this.random(0,e.length-1)]}shuffle(e){const t=e.slice();for(let o=0;o<=t.length-2;o++){const e=this.random(o,t.length-1),n=t[o];t[o]=t[e],t[e]=n}return t}static serialize(e){return{rand_high:e.rand_high,rand_low:e.rand_low}}static unserialize(e){const t=new ne(0);return t.rand_high=e.rand_high,t.rand_low=e.rand_low,t}}const le=(e,t)=>{const o=`${e}`;return o.length>=t.length?o:t.substr(0,t.length-o.length)+o},ae=(...e)=>{const t=t=>(...o)=>{const n=new Date,l=le(n.getHours(),"00"),a=le(n.getMinutes(),"00"),s=le(n.getSeconds(),"00");console[t](`${l}:${a}:${s}`,...e,...o)};return{log:t("log"),error:t("error"),info:t("info")}};var se={hash:e=>{let t=0;for(let o=0;o{let t=e.toLowerCase();return t=t.replace(/[^a-z0-9]+/g,"-"),t=t.replace(/^-|-$/,""),t},uniqId:()=>Date.now().toString(36)+Math.random().toString(36).substring(2),encodeShape:function(e){return e.top+1<<0|e.right+1<<2|e.bottom+1<<4|e.left+1<<6},decodeShape:function(e){return{top:(e>>0&3)-1,right:(e>>2&3)-1,bottom:(e>>4&3)-1,left:(e>>6&3)-1}},encodePiece:function(e){return[e.idx,e.pos.x,e.pos.y,e.z,e.owner,e.group]},decodePiece:function(e){return{idx:e[0],pos:{x:e[1],y:e[2]},z:e[3],owner:e[4],group:e[5]}},encodePlayer:function(e){return[e.id,e.x,e.y,e.d,e.name,e.color,e.bgcolor,e.points,e.ts]},decodePlayer:function(e){return{id:e[0],x:e[1],y:e[2],d:e[3],name:e[4],color:e[5],bgcolor:e[6],points:e[7],ts:e[8]}},encodeGame:function(e){return[e.id,e.rng.type||"",ne.serialize(e.rng.obj),e.puzzle,e.players,e.evtInfos,e.scoreMode||Q.FINAL,e.shapeMode||X.ANY,e.snapMode||ee.NORMAL]},decodeGame:function(e){return{id:e[0],rng:{type:e[1],obj:ne.unserialize(e[2])},puzzle:e[3],players:e[4],evtInfos:e[5],scoreMode:e[6],shapeMode:e[7],snapMode:e[8]}},coordByPieceIdx:function(e,t){const o=e.width/e.tileSize;return{x:t%o,y:Math.floor(t/o)}},asQueryArgs:function(e){const t=[];for(const o in e){const n=[o,e[o]].map(encodeURIComponent);t.push(n.join("="))}return 0===t.length?"":`?${t.join("&")}`}};const ie={name:"responsive-image",props:{src:String,title:{type:String,default:""},height:{type:String,default:"100%"},width:{type:String,default:"100%"}},computed:{style(){return{display:"inline-block",verticalAlign:"text-bottom",backgroundImage:`url('${this.src}')`,backgroundRepeat:"no-repeat",backgroundSize:"contain",backgroundPosition:"center",width:this.width,height:this.height}}}};ie.render=function(e,o,n,l,a,i){return s(),t("div",{style:i.style,title:n.title},null,12,["title"])};var re=e({name:"tags-input",props:{modelValue:{type:Array,required:!0},autocompleteTags:{type:Function}},emits:{"update:modelValue":null},data:()=>({input:"",values:[],autocomplete:{idx:-1,values:[]}}),created(){this.values=this.modelValue},methods:{onKeyUp(e){return"ArrowDown"===e.code&&this.autocomplete.values.length>0?(this.autocomplete.idx0?(this.autocomplete.idx>0&&this.autocomplete.idx--,e.stopPropagation(),!1):","===e.key?(this.add(),e.stopPropagation(),!1):void(this.input&&this.autocompleteTags?(this.autocomplete.values=this.autocompleteTags(this.input,this.values),this.autocomplete.idx=-1):(this.autocomplete.values=[],this.autocomplete.idx=-1))},addVal(e){const t=e.replace(/,/g,"").trim();t&&(this.values.includes(t)||this.values.push(t),this.input="",this.autocomplete.values=[],this.autocomplete.idx=-1,this.$emit("update:modelValue",this.values),this.$refs.input.focus())},add(){const e=this.autocomplete.idx>=0?this.autocomplete.values[this.autocomplete.idx]:this.input;this.addVal(e)},rm(e){this.values=this.values.filter((t=>t!==e)),this.$emit("update:modelValue",this.values)}}});const de=m();y("data-v-a4fa5e7e");const ce={key:0,class:"autocomplete"};f();const ue=de(((e,n,a,i,u,m)=>(s(),t("div",null,[p(o("input",{ref:"input",class:"input",type:"text","onUpdate:modelValue":n[1]||(n[1]=t=>e.input=t),placeholder:"Plants, People",onChange:n[2]||(n[2]=(...t)=>e.onChange&&e.onChange(...t)),onKeydown:n[3]||(n[3]=h(((...t)=>e.add&&e.add(...t)),["enter"])),onKeyup:n[4]||(n[4]=(...t)=>e.onKeyUp&&e.onKeyUp(...t))},null,544),[[g,e.input]]),e.autocomplete.values?(s(),t("div",ce,[o("ul",null,[(s(!0),t(d,null,c(e.autocomplete.values,((o,n)=>(s(),t("li",{key:n,class:{active:n===e.autocomplete.idx},onClick:t=>e.addVal(o)},r(o),11,["onClick"])))),128))])])):l("",!0),(s(!0),t(d,null,c(e.values,((o,n)=>(s(),t("span",{key:n,class:"bit",onClick:t=>e.rm(o)},r(o)+" βœ–",9,["onClick"])))),128))]))));re.render=ue,re.__scopeId="data-v-a4fa5e7e";const pe=ae("NewImageDialog.vue");var ge=e({name:"new-image-dialog",components:{ResponsiveImage:ie,TagsInput:re},props:{autocompleteTags:{type:Function},uploadProgress:{type:Number},uploading:{type:String}},emits:{bgclick:null,setupGameClick:null,postToGalleryClick:null},data:()=>({previewUrl:"",file:null,title:"",tags:[],droppable:!1}),computed:{uploadProgressPercent(){return this.uploadProgress?Math.round(100*this.uploadProgress):0},canPostToGallery(){return!this.uploading&&!(!this.previewUrl||!this.file)},canSetupGameClick(){return!this.uploading&&!(!this.previewUrl||!this.file)}},methods:{imageFromDragEvt(e){var t;const o=null==(t=e.dataTransfer)?void 0:t.items;if(!o||0===o.length)return null;const n=o[0];return n.type.startsWith("image/")?n:null},onFileSelect(e){const t=e.target;if(!t.files)return;const o=t.files[0];o&&this.preview(o)},preview(e){const t=new FileReader;t.readAsDataURL(e),t.onload=t=>{this.previewUrl=t.target.result,this.file=e}},postToGallery(){this.$emit("postToGalleryClick",{file:this.file,title:this.title,tags:this.tags})},setupGameClick(){this.$emit("setupGameClick",{file:this.file,title:this.title,tags:this.tags})},onDrop(e){this.droppable=!1;const t=this.imageFromDragEvt(e);if(!t)return!1;const o=t.getAsFile();return!!o&&(this.file=o,this.preview(o),e.preventDefault(),!1)},onDragover(e){return!!this.imageFromDragEvt(e)&&(this.droppable=!0,e.preventDefault(),!1)},onDragleave(){pe.info("onDragleave"),this.droppable=!1}}});const he=o("div",{class:"drop-target"},null,-1),me={key:0,class:"has-image"},ye={key:1},fe={class:"upload"},ve=o("span",{class:"btn"},"Upload File",-1),we={class:"area-settings"},be=o("td",null,[o("label",null,"Title")],-1),xe=o("tr",null,[o("td",{colspan:"2"},[o("div",{class:"hint"},"Feel free to leave a credit to the artist/photographer in the title :)")])],-1),Ce=o("td",null,[o("label",null,"Tags")],-1),ke={class:"area-buttons"},Ae=i("πŸ–ΌοΈ Post to gallery"),Se=i("🧩 Post to gallery "),Pe=o("br",null,null,-1),Te=i(" + set up game");ge.render=function(e,n,l,c,h,m){const y=a("responsive-image"),f=a("tags-input");return s(),t("div",{class:"overlay new-image-dialog",onClick:n[11]||(n[11]=t=>e.$emit("bgclick"))},[o("div",{class:"overlay-content",onClick:n[10]||(n[10]=u((()=>{}),["stop"]))},[o("div",{class:["area-image",{"has-image":!!e.previewUrl,"no-image":!e.previewUrl,droppable:e.droppable}],onDrop:n[3]||(n[3]=(...t)=>e.onDrop&&e.onDrop(...t)),onDragover:n[4]||(n[4]=(...t)=>e.onDragover&&e.onDragover(...t)),onDragleave:n[5]||(n[5]=(...t)=>e.onDragleave&&e.onDragleave(...t))},[he,e.previewUrl?(s(),t("div",me,[o("span",{class:"remove btn",onClick:n[1]||(n[1]=t=>e.previewUrl="")},"X"),o(y,{src:e.previewUrl},null,8,["src"])])):(s(),t("div",ye,[o("label",fe,[o("input",{type:"file",style:{display:"none"},onChange:n[2]||(n[2]=(...t)=>e.onFileSelect&&e.onFileSelect(...t)),accept:"image/*"},null,32),ve])]))],34),o("div",we,[o("table",null,[o("tr",null,[be,o("td",null,[p(o("input",{type:"text","onUpdate:modelValue":n[6]||(n[6]=t=>e.title=t),placeholder:"Flower by @artist"},null,512),[[g,e.title]])])]),xe,o("tr",null,[Ce,o("td",null,[o(f,{modelValue:e.tags,"onUpdate:modelValue":n[7]||(n[7]=t=>e.tags=t),autocompleteTags:e.autocompleteTags},null,8,["modelValue","autocompleteTags"])])])])]),o("div",ke,[o("button",{class:"btn",disabled:!e.canPostToGallery,onClick:n[8]||(n[8]=(...t)=>e.postToGallery&&e.postToGallery(...t))},["postToGallery"===e.uploading?(s(),t(d,{key:0},[i("Uploading ("+r(e.uploadProgressPercent)+"%)",1)],64)):(s(),t(d,{key:1},[Ae],64))],8,["disabled"]),o("button",{class:"btn",disabled:!e.canSetupGameClick,onClick:n[9]||(n[9]=(...t)=>e.setupGameClick&&e.setupGameClick(...t))},["setupGame"===e.uploading?(s(),t(d,{key:0},[i("Uploading ("+r(e.uploadProgressPercent)+"%)",1)],64)):(s(),t(d,{key:1},[Se,Pe,Te],64))],8,["disabled"])])])])};var Ie=e({name:"edit-image-dialog",components:{ResponsiveImage:ie,TagsInput:re},props:{image:{type:Object,required:!0},autocompleteTags:{type:Function}},emits:{bgclick:null,saveClick:null},data:()=>({title:"",tags:[]}),created(){this.title=this.image.title,this.tags=this.image.tags.map((e=>e.title))},methods:{saveImage(){this.$emit("saveClick",{id:this.image.id,title:this.title,tags:this.tags})}}});const ze={class:"area-image"},De={class:"has-image"},Ee={class:"area-settings"},_e=o("td",null,[o("label",null,"Title")],-1),Me=o("tr",null,[o("td",{colspan:"2"},[o("div",{class:"hint"},"Feel free to leave a credit to the artist/photographer in the title :)")])],-1),Ve=o("td",null,[o("label",null,"Tags")],-1),Ne={class:"area-buttons"};Ie.render=function(e,n,l,i,r,d){const c=a("responsive-image"),h=a("tags-input");return s(),t("div",{class:"overlay edit-image-dialog",onClick:n[5]||(n[5]=t=>e.$emit("bgclick"))},[o("div",{class:"overlay-content",onClick:n[4]||(n[4]=u((()=>{}),["stop"]))},[o("div",ze,[o("div",De,[o(c,{src:e.image.url,title:e.image.title},null,8,["src","title"])])]),o("div",Ee,[o("table",null,[o("tr",null,[_e,o("td",null,[p(o("input",{type:"text","onUpdate:modelValue":n[1]||(n[1]=t=>e.title=t),placeholder:"Flower by @artist"},null,512),[[g,e.title]])])]),Me,o("tr",null,[Ve,o("td",null,[o(h,{modelValue:e.tags,"onUpdate:modelValue":n[2]||(n[2]=t=>e.tags=t),autocompleteTags:e.autocompleteTags},null,8,["modelValue","autocompleteTags"])])])])]),o("div",Ne,[o("button",{class:"btn",onClick:n[3]||(n[3]=(...t)=>e.saveImage&&e.saveImage(...t))},"πŸ–ΌοΈ Save image")])])])};var Oe=e({name:"new-game-dialog",components:{ResponsiveImage:ie},props:{image:{type:Object,required:!0}},emits:{newGame:null,bgclick:null},data:()=>({tiles:1e3,scoreMode:Q.ANY,shapeMode:X.NORMAL,snapMode:ee.NORMAL}),methods:{onNewGameClick(){this.$emit("newGame",{tiles:this.tilesInt,image:this.image,scoreMode:this.scoreModeInt,shapeMode:this.shapeModeInt,snapMode:this.snapModeInt})}},computed:{canStartNewGame(){return!!(this.tilesInt&&this.image&&this.image.url&&[0,1].includes(this.scoreModeInt))},scoreModeInt(){return parseInt(`${this.scoreMode}`,10)},shapeModeInt(){return parseInt(`${this.shapeMode}`,10)},snapModeInt(){return parseInt(`${this.snapMode}`,10)},tilesInt(){return parseInt(`${this.tiles}`,10)}}});const Ue={class:"area-image"},Be={class:"has-image"},Re={key:0,class:"image-title"},Ge={key:0,class:"image-title-title"},$e={key:1,class:"image-title-dim"},Le={class:"area-settings"},Fe=o("td",null,[o("label",null,"Pieces")],-1),je=o("td",null,[o("label",null,"Scoring: ")],-1),We=i(" Any (Score when pieces are connected to each other or on final location)"),He=o("br",null,null,-1),Ke=i(" Final (Score when pieces are put to their final location)"),Ye=o("td",null,[o("label",null,"Shapes: ")],-1),qe=i(" Normal"),Qe=o("br",null,null,-1),Ze=i(" Any (flat pieces can occur anywhere)"),Xe=o("br",null,null,-1),Je=i(" Flat (all pieces flat on all sides)"),et=o("td",null,[o("label",null,"Snapping: ")],-1),tt=i(" Normal (pieces snap to final destination and to each other)"),ot=o("br",null,null,-1),nt=i(" Real (pieces snap only to corners, already snapped pieces and to each other)"),lt={class:"area-buttons"};Oe.render=function(e,n,i,d,c,h){const m=a("responsive-image");return s(),t("div",{class:"overlay new-game-dialog",onClick:n[11]||(n[11]=t=>e.$emit("bgclick"))},[o("div",{class:"overlay-content",onClick:n[10]||(n[10]=u((()=>{}),["stop"]))},[o("div",Ue,[o("div",Be,[o(m,{src:e.image.url,title:e.image.title},null,8,["src","title"])]),e.image.title||e.image.width||e.image.height?(s(),t("div",Re,[e.image.title?(s(),t("span",Ge,'"'+r(e.image.title)+'"',1)):l("",!0),e.image.width||e.image.height?(s(),t("span",$e,"("+r(e.image.width)+" βœ• "+r(e.image.height)+")",1)):l("",!0)])):l("",!0)]),o("div",Le,[o("table",null,[o("tr",null,[Fe,o("td",null,[p(o("input",{type:"text","onUpdate:modelValue":n[1]||(n[1]=t=>e.tiles=t)},null,512),[[g,e.tiles]])])]),o("tr",null,[je,o("td",null,[o("label",null,[p(o("input",{type:"radio","onUpdate:modelValue":n[2]||(n[2]=t=>e.scoreMode=t),value:"1"},null,512),[[v,e.scoreMode]]),We]),He,o("label",null,[p(o("input",{type:"radio","onUpdate:modelValue":n[3]||(n[3]=t=>e.scoreMode=t),value:"0"},null,512),[[v,e.scoreMode]]),Ke])])]),o("tr",null,[Ye,o("td",null,[o("label",null,[p(o("input",{type:"radio","onUpdate:modelValue":n[4]||(n[4]=t=>e.shapeMode=t),value:"0"},null,512),[[v,e.shapeMode]]),qe]),Qe,o("label",null,[p(o("input",{type:"radio","onUpdate:modelValue":n[5]||(n[5]=t=>e.shapeMode=t),value:"1"},null,512),[[v,e.shapeMode]]),Ze]),Xe,o("label",null,[p(o("input",{type:"radio","onUpdate:modelValue":n[6]||(n[6]=t=>e.shapeMode=t),value:"2"},null,512),[[v,e.shapeMode]]),Je])])]),o("tr",null,[et,o("td",null,[o("label",null,[p(o("input",{type:"radio","onUpdate:modelValue":n[7]||(n[7]=t=>e.snapMode=t),value:"0"},null,512),[[v,e.snapMode]]),tt]),ot,o("label",null,[p(o("input",{type:"radio","onUpdate:modelValue":n[8]||(n[8]=t=>e.snapMode=t),value:"1"},null,512),[[v,e.snapMode]]),nt])])])])]),o("div",lt,[o("button",{class:"btn",disabled:!e.canStartNewGame,onClick:n[9]||(n[9]=(...t)=>e.onNewGameClick&&e.onNewGameClick(...t))}," 🧩 Generate Puzzle ",8,["disabled"])])])])};const at=async(e,t,o)=>new Promise(((n,l)=>{const a=new window.XMLHttpRequest;a.open(e,t,!0),a.withCredentials=!0;for(const e in o.headers||{})a.setRequestHeader(e,o.headers[e]);a.addEventListener("load",(function(e){n({status:this.status,text:this.responseText,json:async()=>JSON.parse(this.responseText)})})),a.addEventListener("error",(function(e){l(new Error("xhr error"))})),a.upload&&o.onUploadProgress&&a.upload.addEventListener("progress",(function(e){o.onUploadProgress&&o.onUploadProgress(e)})),a.send(o.body)}));var st=(e,t)=>at("post",e,t),it=e({components:{ImageLibrary:oe,NewImageDialog:ge,EditImageDialog:Ie,NewGameDialog:Oe},data:()=>({filters:{sort:"date_desc",tags:[]},images:[],tags:[],image:{id:0,filename:"",file:"",url:"",title:"",tags:[],created:0},dialog:"",uploading:"",uploadProgress:0}),async created(){await this.loadImages()},computed:{relevantTags(){return this.tags.filter((e=>e.total>0))}},methods:{autocompleteTags(e,t){return this.tags.filter((o=>!t.includes(o.title)&&o.title.toLowerCase().startsWith(e.toLowerCase()))).slice(0,10).map((e=>e.title))},toggleTag(e){this.filters.tags.includes(e.slug)?this.filters.tags=this.filters.tags.filter((t=>t!==e.slug)):this.filters.tags.push(e.slug),this.filtersChanged()},async loadImages(){const e=await fetch(`/api/newgame-data${se.asQueryArgs(this.filters)}`),t=await e.json();this.images=t.images,this.tags=t.tags},async filtersChanged(){await this.loadImages()},onImageClicked(e){this.image=e,this.dialog="new-game"},onImageEditClicked(e){this.image=e,this.dialog="edit-image"},async uploadImage(e){this.uploadProgress=0;const t=new FormData;t.append("file",e.file,e.file.name),t.append("title",e.title),t.append("tags",e.tags);const o=await st("/api/upload",{body:t,onUploadProgress:e=>{this.uploadProgress=e.loaded/e.total}});return this.uploadProgress=1,await o.json()},async saveImage(e){const t=await fetch("/api/save-image",{method:"post",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify({id:e.id,title:e.title,tags:e.tags})});return await t.json()},async onSaveImageClick(e){await this.saveImage(e),this.dialog="",await this.loadImages()},async postToGalleryClick(e){this.uploading="postToGallery",await this.uploadImage(e),this.uploading="",this.dialog="",await this.loadImages()},async setupGameClick(e){this.uploading="setupGame";const t=await this.uploadImage(e);this.uploading="",this.loadImages(),this.image=t,this.dialog="new-game"},async onNewGame(e){const t=await fetch("/api/newgame",{method:"post",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(e)});if(200===t.status){const e=await t.json();this.$router.push({name:"game",params:{id:e.id}})}}}});const rt={class:"upload-image-teaser"},dt=o("div",{class:"hint"},"(The image you upload will be added to the public gallery.)",-1),ct={key:0},ut=i(" Tags: "),pt=i(" Sort by: "),gt=o("option",{value:"date_desc"},"Newest first",-1),ht=o("option",{value:"date_asc"},"Oldest first",-1),mt=o("option",{value:"alpha_asc"},"A-Z",-1),yt=o("option",{value:"alpha_desc"},"Z-A",-1);it.render=function(e,n,i,u,g,h){const m=a("image-library"),y=a("new-image-dialog"),f=a("edit-image-dialog"),v=a("new-game-dialog");return s(),t("div",null,[o("div",rt,[o("div",{class:"btn btn-big",onClick:n[1]||(n[1]=t=>e.dialog="new-image")},"Upload your image"),dt]),o("div",null,[e.tags.length>0?(s(),t("label",ct,[ut,(s(!0),t(d,null,c(e.relevantTags,((o,n)=>(s(),t("span",{class:["bit",{on:e.filters.tags.includes(o.slug)}],key:n,onClick:t=>e.toggleTag(o)},r(o.title)+" ("+r(o.total)+")",11,["onClick"])))),128))])):l("",!0),o("label",null,[pt,p(o("select",{"onUpdate:modelValue":n[2]||(n[2]=t=>e.filters.sort=t),onChange:n[3]||(n[3]=(...t)=>e.filtersChanged&&e.filtersChanged(...t))},[gt,ht,mt,yt],544),[[w,e.filters.sort]])])]),o(m,{images:e.images,onImageClicked:e.onImageClicked,onImageEditClicked:e.onImageEditClicked},null,8,["images","onImageClicked","onImageEditClicked"]),"new-image"===e.dialog?(s(),t(y,{key:0,autocompleteTags:e.autocompleteTags,onBgclick:n[4]||(n[4]=t=>e.dialog=""),uploadProgress:e.uploadProgress,uploading:e.uploading,onPostToGalleryClick:e.postToGalleryClick,onSetupGameClick:e.setupGameClick},null,8,["autocompleteTags","uploadProgress","uploading","onPostToGalleryClick","onSetupGameClick"])):l("",!0),"edit-image"===e.dialog?(s(),t(f,{key:1,autocompleteTags:e.autocompleteTags,onBgclick:n[5]||(n[5]=t=>e.dialog=""),onSaveClick:e.onSaveImageClick,image:e.image},null,8,["autocompleteTags","onSaveClick","image"])):l("",!0),e.image&&"new-game"===e.dialog?(s(),t(v,{key:2,onBgclick:n[6]||(n[6]=t=>e.dialog=""),onNewGame:e.onNewGame,image:e.image},null,8,["onNewGame","image"])):l("",!0)])};var ft=e({name:"scores",props:{activePlayers:{type:Array,required:!0},idlePlayers:{type:Array,required:!0}},computed:{actives(){return this.activePlayers.sort(((e,t)=>t.points-e.points)),this.activePlayers},idles(){return this.idlePlayers.sort(((e,t)=>t.points-e.points)),this.idlePlayers}}});const vt={class:"scores"},wt=o("div",null,"Scores",-1),bt=o("td",null,"⚑",-1),xt=o("td",null,"πŸ’€",-1);ft.render=function(e,n,l,a,i,u){return s(),t("div",vt,[wt,o("table",null,[(s(!0),t(d,null,c(e.actives,((e,n)=>(s(),t("tr",{key:n,style:{color:e.color}},[bt,o("td",null,r(e.name),1),o("td",null,r(e.points),1)],4)))),128)),(s(!0),t(d,null,c(e.idles,((e,n)=>(s(),t("tr",{key:n,style:{color:e.color}},[xt,o("td",null,r(e.name),1),o("td",null,r(e.points),1)],4)))),128))])])};var Ct=e({name:"puzzle-status",props:{finished:{type:Boolean,required:!0},duration:{type:Number,required:!0},piecesDone:{type:Number,required:!0},piecesTotal:{type:Number,required:!0}},computed:{icon(){return this.finished?"🏁":"⏳"},durationStr(){return U(this.duration)}}});const kt={class:"timer"};Ct.render=function(e,n,l,a,i,d){return s(),t("div",kt,[o("div",null," 🧩 "+r(e.piecesDone)+"/"+r(e.piecesTotal),1),o("div",null,r(e.icon)+" "+r(e.durationStr),1),b(e.$slots,"default")])};var At=e({name:"settings-overlay",emits:{bgclick:null,"update:modelValue":null},props:{modelValue:{type:Object,required:!0}},methods:{updateVolume(e){this.modelValue.soundsVolume=e.target.value},decreaseVolume(){const e=parseInt(this.modelValue.soundsVolume,10)-5;this.modelValue.soundsVolume=Math.max(0,e)},increaseVolume(){const e=parseInt(this.modelValue.soundsVolume,10)+5;this.modelValue.soundsVolume=Math.min(100,e)}},created(){this.$watch("modelValue",(e=>{this.$emit("update:modelValue",e)}),{deep:!0})}});const St=m();y("data-v-a1d1c822");const Pt=o("td",null,[o("label",null,"Background: ")],-1),Tt=o("td",null,[o("label",null,"Color: ")],-1),It=o("td",null,[o("label",null,"Name: ")],-1),zt=o("td",null,[o("label",null,"Sounds: ")],-1),Dt=o("td",null,[o("label",null,"Sounds Volume: ")],-1),Et={class:"sound-volume"};f();const _t=St(((e,n,l,a,i,r)=>(s(),t("div",{class:"overlay transparent",onClick:n[9]||(n[9]=t=>e.$emit("bgclick"))},[o("table",{class:"overlay-content settings",onClick:n[8]||(n[8]=u((()=>{}),["stop"]))},[o("tr",null,[Pt,o("td",null,[p(o("input",{type:"color","onUpdate:modelValue":n[1]||(n[1]=t=>e.modelValue.background=t)},null,512),[[g,e.modelValue.background]])])]),o("tr",null,[Tt,o("td",null,[p(o("input",{type:"color","onUpdate:modelValue":n[2]||(n[2]=t=>e.modelValue.color=t)},null,512),[[g,e.modelValue.color]])])]),o("tr",null,[It,o("td",null,[p(o("input",{type:"text",maxLength:"16","onUpdate:modelValue":n[3]||(n[3]=t=>e.modelValue.name=t)},null,512),[[g,e.modelValue.name]])])]),o("tr",null,[zt,o("td",null,[p(o("input",{type:"checkbox","onUpdate:modelValue":n[4]||(n[4]=t=>e.modelValue.soundsEnabled=t)},null,512),[[x,e.modelValue.soundsEnabled]])])]),o("tr",null,[Dt,o("td",Et,[o("span",{onClick:n[5]||(n[5]=(...t)=>e.decreaseVolume&&e.decreaseVolume(...t))},"πŸ”‰"),o("input",{type:"range",min:"0",max:"100",value:e.modelValue.soundsVolume,onChange:n[6]||(n[6]=(...t)=>e.updateVolume&&e.updateVolume(...t))},null,40,["value"]),o("span",{onClick:n[7]||(n[7]=(...t)=>e.increaseVolume&&e.increaseVolume(...t))},"πŸ”Š")])])])]))));At.render=_t,At.__scopeId="data-v-a1d1c822";var Mt=e({name:"preview-overlay",props:{img:String},emits:{bgclick:null},computed:{previewStyle(){return{backgroundImage:`url('${this.img}')`}}}});const Vt={class:"preview"};Mt.render=function(e,n,l,a,i,r){return s(),t("div",{class:"overlay",onClick:n[1]||(n[1]=t=>e.$emit("bgclick"))},[o("div",Vt,[o("div",{class:"img",style:e.previewStyle},null,4)])])};var Nt=1,Ot=4,Ut=2,Bt=3,Rt=2,Gt=4,$t=3,Lt=9,Ft=1,jt=2,Wt=3,Ht=4,Kt=5,Yt=6,qt=7,Qt=8,Zt=10,Xt=11,Jt=12,eo=13,to=14,oo=1,no=2,lo=3;const ao=ae("Communication.js");let so,io=[],ro=e=>{io.push(e)},co=[],uo=e=>{co.push(e)};let po=0;const go=e=>{po!==e&&(po=e,uo(e))};function ho(e){if(2===po)try{so.send(JSON.stringify(e))}catch(t){ao.info("unable to send message.. maybe because ws is invalid?")}}let mo,yo;var fo={connect:function(e,t,o){return mo=0,yo={},go(3),new Promise((n=>{so=new WebSocket(e,o+"|"+t),so.onopen=()=>{go(2),ho([Bt])},so.onmessage=e=>{const t=JSON.parse(e.data),l=t[0];if(l===Ot){const e=t[1];n(e)}else{if(l!==Nt)throw`[ 2021-05-09 invalid connect msgType ${l} ]`;{const e=t[1],n=t[2];if(e===o&&yo[n])return void delete yo[n];ro(t)}}},so.onerror=()=>{throw go(1),"[ 2021-05-15 onerror ]"},so.onclose=e=>{4e3===e.code||1001===e.code?go(4):go(1)}}))},requestReplayData:async function(e,t){const o={gameId:e,offset:t},n=await fetch(`/api/replay-data${se.asQueryArgs(o)}`);return await n.json()},disconnect:function(){so&&so.close(4e3),mo=0,yo={}},sendClientEvent:function(e){mo++,yo[mo]=e,ho([Ut,mo,yo[mo]])},onServerChange:function(e){ro=e;for(const t of io)ro(t);io=[]},onConnectionStateChange:function(e){uo=e;for(const t of co)uo(t);co=[]},CODE_CUSTOM_DISCONNECT:4e3,CONN_STATE_NOT_CONNECTED:0,CONN_STATE_DISCONNECTED:1,CONN_STATE_CLOSED:4,CONN_STATE_CONNECTED:2,CONN_STATE_CONNECTING:3},vo=e({name:"connection-overlay",emits:{reconnect:null},props:{connectionState:Number},computed:{lostConnection(){return this.connectionState===fo.CONN_STATE_DISCONNECTED},connecting(){return this.connectionState===fo.CONN_STATE_CONNECTING},show(){return!(!this.lostConnection&&!this.connecting)}}});const wo={key:0,class:"overlay connection-lost"},bo={key:0,class:"overlay-content"},xo=o("div",null,"⁉️ LOST CONNECTION ⁉️",-1),Co={key:1,class:"overlay-content"},ko=o("div",null,"Connecting...",-1);vo.render=function(e,n,a,i,r,d){return e.show?(s(),t("div",wo,[e.lostConnection?(s(),t("div",bo,[xo,o("span",{class:"btn",onClick:n[1]||(n[1]=t=>e.$emit("reconnect"))},"Reconnect")])):l("",!0),e.connecting?(s(),t("div",Co,[ko])):l("",!0)])):l("",!0)};var Ao=e({name:"help-overlay",emits:{bgclick:null}});const So=o("tr",null,[o("td",null,"⬆️ Move up:"),o("td",null,[o("div",null,[o("kbd",null,"W"),i("/"),o("kbd",null,"↑"),i("/πŸ–±οΈ")])])],-1),Po=o("tr",null,[o("td",null,"⬇️ Move down:"),o("td",null,[o("div",null,[o("kbd",null,"S"),i("/"),o("kbd",null,"↓"),i("/πŸ–±οΈ")])])],-1),To=o("tr",null,[o("td",null,"⬅️ Move left:"),o("td",null,[o("div",null,[o("kbd",null,"A"),i("/"),o("kbd",null,"←"),i("/πŸ–±οΈ")])])],-1),Io=o("tr",null,[o("td",null,"➑️ Move right:"),o("td",null,[o("div",null,[o("kbd",null,"D"),i("/"),o("kbd",null,"β†’"),i("/πŸ–±οΈ")])])],-1),zo=o("tr",null,[o("td"),o("td",null,[o("div",null,[i("Move faster by holding "),o("kbd",null,"Shift")])])],-1),Do=o("tr",null,[o("td",null,"πŸ”+ Zoom in:"),o("td",null,[o("div",null,[o("kbd",null,"E"),i("/πŸ–±οΈ-Wheel")])])],-1),Eo=o("tr",null,[o("td",null,"πŸ”- Zoom out:"),o("td",null,[o("div",null,[o("kbd",null,"Q"),i("/πŸ–±οΈ-Wheel")])])],-1),_o=o("tr",null,[o("td",null,"πŸ–ΌοΈ Toggle preview:"),o("td",null,[o("div",null,[o("kbd",null,"Space")])])],-1),Mo=o("tr",null,[o("td",null,"πŸ§©βœ”οΈ Toggle fixed pieces:"),o("td",null,[o("div",null,[o("kbd",null,"F")])])],-1),Vo=o("tr",null,[o("td",null,"πŸ§©β“ Toggle loose pieces:"),o("td",null,[o("div",null,[o("kbd",null,"G")])])],-1),No=o("tr",null,[o("td",null,"πŸ”‰ Toggle sounds:"),o("td",null,[o("div",null,[o("kbd",null,"M")])])],-1),Oo=o("tr",null,[o("td",null,"⏫ Speed up (replay):"),o("td",null,[o("div",null,[o("kbd",null,"I")])])],-1),Uo=o("tr",null,[o("td",null,"⏬ Speed down (replay):"),o("td",null,[o("div",null,[o("kbd",null,"O")])])],-1),Bo=o("tr",null,[o("td",null,"⏸️ Pause (replay):"),o("td",null,[o("div",null,[o("kbd",null,"P")])])],-1);Ao.render=function(e,n,l,a,i,r){return s(),t("div",{class:"overlay transparent",onClick:n[2]||(n[2]=t=>e.$emit("bgclick"))},[o("table",{class:"overlay-content help",onClick:n[1]||(n[1]=u((()=>{}),["stop"]))},[So,Po,To,Io,zo,Do,Eo,_o,Mo,Vo,No,Oo,Uo,Bo])])};var Ro=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"/assets/click.bb97cb07.mp3"}),Go=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAW0lEQVQ4je1RywrAIAxLxP//5exixRWlVgZelpOKeTQFfnDypgy3eLIkSLLL8mxGPoHsU2hPAgDHBLvRX6hZZw/fwT0BtlLSONqCbWAmEIqMZOCDDlaDR3N03gOyDCn+y4DWmAAAAABJRU5ErkJggg=="}),$o=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAARElEQVQ4jWNgGAU0Af+hmBCbgYGBgYERhwHEAEYGBgYGJtIdiApYyLAZBVDsAqoagC1ACQJyY4ERg0GCISh6KA4DigEAou8LC+LnIJoAAAAASUVORK5CYII="}),Lo=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAcUlEQVQ4ja1TQQ7AIAgD///n7jCozA2Hbk00jbG1KIrcARszTugoBs49qioZj7r2kKACptkyAOCJsJuA+GzglwHjvMSSWFVaENWVASxh5eRLiq5fN/ASjI89VcP2K3hHpq1cEXNaMfnrL3TDZP2tDuoOA6MzCCXWr38AAAAASUVORK5CYII="}),Fo=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAU0lEQVQ4jWNgoAH4D8X42HDARKlt5BoAd82AuQAOGLGIYQQUPv0wF5CiCQUge4EsQ5C9QI4BjMguwBYeBAElscCIy1ZivMKIwSDBEBQ9FCckigEAU3QOD7TGvY4AAAAASUVORK5CYII="});function jo(){let e=0,t=0,o=1;const n=(n,l)=>{e+=n/o,t+=l/o},l=e=>{const t=o+.05*o*("in"===e?1:-1);return Math.min(Math.max(t,.1),6)},a=n=>({x:n.x/o-e,y:n.y/o-t}),s=n=>({x:(n.x+e)*o,y:(n.y+t)*o}),i=e=>({w:e.w*o,h:e.h*o}),r=e=>({w:e.w/o,h:e.h/o});return{getCurrentZoom:()=>o,move:n,canZoom:e=>o!=l(e),zoom:(e,t)=>((e,t)=>{if(o==e)return!1;const l=1-o/e;return n(-t.x*l,-t.y*l),o=e,!0})(l(e),t),worldToViewport:e=>{const{x:t,y:o}=s(e);return{x:Math.round(t),y:Math.round(o)}},worldToViewportRaw:s,worldDimToViewport:e=>{const{w:t,h:o}=i(e);return{w:Math.round(t),h:Math.round(o)}},worldDimToViewportRaw:i,viewportToWorld:e=>{const{x:t,y:o}=a(e);return{x:Math.round(t),y:Math.round(o)}},viewportToWorldRaw:a,viewportDimToWorld:e=>{const{w:t,h:o}=r(e);return{w:Math.round(t),h:Math.round(o)}},viewportDimToWorldRaw:r}}function Wo(e=0,t=0){const o=document.createElement("canvas");return o.width=e,o.height=t,o}var Ho={createCanvas:Wo,loadImageToBitmap:async function(e){return new Promise((t=>{const o=new Image;o.onload=()=>{createImageBitmap(o).then(t)},o.src=e}))},resizeBitmap:async function(e,t,o){const n=Wo(t,o);return n.getContext("2d").drawImage(e,0,0,e.width,e.height,0,0,t,o),await createImageBitmap(n)},colorizedCanvas:function(e,t,o){const n=Wo(e.width,e.height),l=n.getContext("2d");return l.save(),l.drawImage(t,0,0),l.fillStyle=o,l.globalCompositeOperation="source-in",l.fillRect(0,0,t.width,t.height),l.restore(),l.save(),l.globalCompositeOperation="destination-over",l.drawImage(e,0,0),l.restore(),n}};const Ko=ae("Debug.js");let Yo=0,qo=0;var Qo=e=>{Yo=performance.now(),qo=e},Zo=e=>{const t=performance.now(),o=t-Yo;o>qo&&Ko.log(e+": "+o),Yo=t};function Xo(e,t){const o=e.x-t.x,n=e.y-t.y;return Math.sqrt(o*o+n*n)}function Jo(e){return{x:e.x+e.w/2,y:e.y+e.h/2}}var en={pointSub:function(e,t){return{x:e.x-t.x,y:e.y-t.y}},pointAdd:function(e,t){return{x:e.x+t.x,y:e.y+t.y}},pointDistance:Xo,pointInBounds:function(e,t){return e.x>=t.x&&e.x<=t.x+t.w&&e.y>=t.y&&e.y<=t.y+t.h},rectCenter:Jo,rectMoved:function(e,t,o){return{x:e.x+t,y:e.y+o,w:e.w,h:e.h}},rectCenterDistance:function(e,t){return Xo(Jo(e),Jo(t))},rectsOverlap:function(e,t){return!(t.x>e.x+e.w||e.x>t.x+t.w||t.y>e.y+e.h||e.y>t.y+t.h)}};const tn=ae("PuzzleGraphics.js");function on(e,t){const o=se.coordByPieceIdx(e,t);return{x:o.x*e.tileSize,y:o.y*e.tileSize,w:e.tileSize,h:e.tileSize}}var nn={loadPuzzleBitmaps:async function(e){const t=await Ho.loadImageToBitmap(e.info.imageUrl),o=await Ho.resizeBitmap(t,e.info.width,e.info.height);return await async function(e,t,o){tn.log("start createPuzzleTileBitmaps");const n=o.tileSize,l=o.tileMarginWidth,a=o.tileDrawSize,s=n/100,i=[0,0,40,15,37,5,37,5,40,0,38,-5,38,-5,20,-20,50,-20,50,-20,80,-20,62,-5,62,-5,60,0,63,5,63,5,65,15,100,0],r=new Array(t.length),d={};function c(e){const t=`${e.top}${e.right}${e.left}${e.bottom}`;if(d[t])return d[t];const o=new Path2D,a={x:l,y:l},r=en.pointAdd(a,{x:n,y:0}),c=en.pointAdd(r,{x:0,y:n}),u=en.pointSub(c,{x:n,y:0});if(o.moveTo(a.x,a.y),0!==e.top)for(let n=0;nse.decodePiece(ln[e].puzzle.tiles[t]),bn=(e,t)=>wn(e,t).group,xn=(e,t)=>{const o=ln[e].puzzle.info;return 0===t||t===o.tilesX-1||t===o.tiles-o.tilesX||t===o.tiles-1},Cn=(e,t)=>{const o=ln[e].puzzle.info,n={x:(o.table.width-o.width)/2,y:(o.table.height-o.height)/2},l=function(e,t){const o=ln[e].puzzle.info,n=se.coordByPieceIdx(o,t),l=n.x*o.tileSize,a=n.y*o.tileSize;return{x:l,y:a}}(e,t);return en.pointAdd(n,l)},kn=(e,t)=>wn(e,t).pos,An=e=>{const t=$n(e),o=Ln(e),n=Math.round(t/4),l=Math.round(o/4);return{x:0-n,y:0-l,w:t+2*n,h:o+2*l}},Sn=(e,t)=>{const o=zn(e),n=wn(e,t);return{x:n.pos.x,y:n.pos.y,w:o,h:o}},Pn=(e,t)=>wn(e,t).z,Tn=(e,t)=>{for(const o of ln[e].puzzle.tiles){const e=se.decodePiece(o);if(e.owner===t)return e.idx}return-1},In=e=>ln[e].puzzle.info.tileDrawSize,zn=e=>ln[e].puzzle.info.tileSize,Dn=e=>ln[e].puzzle.data.maxGroup,En=e=>ln[e].puzzle.data.maxZ;function _n(e,t){const o=ln[e].puzzle.info,n=se.coordByPieceIdx(o,t);return[n.y>0?t-o.tilesX:-1,n.x0?t-1:-1]}const Mn=(e,t,o)=>{for(const n of t)vn(e,n,{z:o})},Vn=(e,t,o)=>{const n=kn(e,t);vn(e,t,{pos:en.pointAdd(n,o)})},Nn=(e,t,o)=>{const n=In(e),l=An(e),a=o;for(const s of t){const t=wn(e,s);t.pos.x+o.xl.x+l.w&&(a.x=Math.min(l.x+l.w-t.pos.x+n,a.x)),t.pos.y+o.yl.y+l.h&&(a.y=Math.min(l.y+l.h-t.pos.y+n,a.y))}for(const s of t)Vn(e,s,a)},On=(e,t)=>wn(e,t).owner,Un=(e,t)=>{for(const o of t)vn(e,o,{owner:-1,z:1})},Bn=(e,t,o)=>{for(const n of t)vn(e,n,{owner:o})};function Rn(e,t){const o=ln[e].puzzle.tiles,n=se.decodePiece(o[t]),l=[];if(n.group)for(const a of o){const e=se.decodePiece(a);e.group===n.group&&l.push(e.idx)}else l.push(n.idx);return l}const Gn=(e,t)=>{const o=sn(e,t);return o?o.points:0},$n=e=>ln[e].puzzle.info.table.width,Ln=e=>ln[e].puzzle.info.table.height;var Fn={setGame:function(e,t){ln[e]=t},exists:function(e){return!!ln[e]||!1},playerExists:dn,getActivePlayers:function(e,t){const o=t-30*V;return cn(e).filter((e=>e.ts>=o))},getIdlePlayers:function(e,t){const o=t-30*V;return cn(e).filter((e=>e.ts0))},addPlayer:function(e,t,o){dn(e,t)?yn(e,t,{ts:o}):rn(e,t,function(e,t){return{id:e,x:0,y:0,d:0,name:null,color:null,bgcolor:null,points:0,ts:t}}(t,o))},getFinishedPiecesCount:mn,getPieceCount:un,getImageUrl:function(e){return ln[e].puzzle.info.imageUrl},setImageUrl:function(e,t){ln[e].puzzle.info.imageUrl=t},get:function(e){return ln[e]||null},getAllGames:function(){return Object.values(ln).sort(((e,t)=>hn(e.id)===hn(t.id)?t.puzzle.data.started-e.puzzle.data.started:hn(e.id)?1:-1))},getPlayerBgColor:(e,t)=>{const o=sn(e,t);return o?o.bgcolor:null},getPlayerColor:(e,t)=>{const o=sn(e,t);return o?o.color:null},getPlayerName:(e,t)=>{const o=sn(e,t);return o?o.name:null},getPlayerIndexById:an,getPlayerIdByIndex:function(e,t){return ln[e].players.length>t?se.decodePlayer(ln[e].players[t]).id:null},changePlayer:yn,setPlayer:rn,setPiece:function(e,t,o){ln[e].puzzle.tiles[t]=se.encodePiece(o)},setPuzzleData:function(e,t){ln[e].puzzle.data=t},getTableWidth:$n,getTableHeight:Ln,getPuzzle:e=>ln[e].puzzle,getRng:e=>ln[e].rng.obj,getPuzzleWidth:e=>ln[e].puzzle.info.width,getPuzzleHeight:e=>ln[e].puzzle.info.height,getPiecesSortedByZIndex:function(e){return ln[e].puzzle.tiles.map(se.decodePiece).sort(((e,t)=>e.z-t.z))},getFirstOwnedPiece:(e,t)=>{const o=Tn(e,t);return o<0?null:ln[e].puzzle.tiles[o]},getPieceDrawOffset:e=>ln[e].puzzle.info.tileDrawOffset,getPieceDrawSize:In,getFinalPiecePos:Cn,getStartTs:e=>ln[e].puzzle.data.started,getFinishTs:e=>ln[e].puzzle.data.finished,handleInput:function(e,t,o,n,l){const a=ln[e].puzzle,s=function(e,t){return t in ln[e].evtInfos?ln[e].evtInfos[t]:{_last_mouse:null,_last_mouse_down:null}}(e,t),i=[],r=()=>{i.push([oo,a.data])},d=t=>{i.push([no,se.encodePiece(wn(e,t))])},c=e=>{for(const t of e)d(t)},u=()=>{const o=sn(e,t);o&&i.push([lo,se.encodePlayer(o)])},p=o[0];if(p===Yt){const l=o[1];yn(e,t,{bgcolor:l,ts:n}),u()}else if(p===qt){const l=o[1];yn(e,t,{color:l,ts:n}),u()}else if(p===Qt){const l=`${o[1]}`.substr(0,16);yn(e,t,{name:l,ts:n}),u()}else if(p===Lt){const l=o[1],a=o[2],s=sn(e,t);if(s){const o=s.x-l,i=s.y-a;yn(e,t,{ts:n,x:o,y:i}),u()}}else if(p===Ft){const l={x:o[1],y:o[2]};yn(e,t,{d:1,ts:n}),u(),s._last_mouse_down=l;const a=((e,t)=>{const o=ln[e].puzzle.info,n=ln[e].puzzle.tiles;let l=-1,a=-1;for(let s=0;sl)&&(l=e.z,a=s)}return a})(e,l);if(a>=0){const o=En(e)+1;fn(e,{maxZ:o}),r();const n=Rn(e,a);Mn(e,n,En(e)),Bn(e,n,t),c(n)}s._last_mouse=l}else if(p===Wt){const l=o[1],a=o[2],i={x:l,y:a};if(null===s._last_mouse_down)yn(e,t,{x:l,y:a,ts:n}),u();else{const o=Tn(e,t);if(o>=0){yn(e,t,{x:l,y:a,ts:n}),u();const r=Rn(e,o);let d=en.pointInBounds(i,An(e))&&en.pointInBounds(s._last_mouse_down,An(e));for(const t of r){const o=Sn(e,t);if(en.pointInBounds(i,o)){d=!0;break}}if(d){const t=l-s._last_mouse_down.x,o=a-s._last_mouse_down.y;Nn(e,r,{x:t,y:o}),c(r)}}else yn(e,t,{ts:n}),u();s._last_mouse_down=i}s._last_mouse=i}else if(p===jt){const i={x:o[1],y:o[2]},p=0;s._last_mouse_down=null;const g=Tn(e,t);if(g>=0){const o=Rn(e,g);Bn(e,o,0),c(o);const s=kn(e,g),i=Cn(e,g);let h=!1;if(gn(e)===ee.REAL){for(const t of o)if(xn(e,t)){h=!0;break}}else h=!0;if(h&&en.pointDistance(i,s){const l=ln[e].puzzle.info;if(o<0)return!1;if(((e,t,o)=>{const n=bn(e,t),l=bn(e,o);return!(!n||n!==l)})(e,t,o))return!1;const a=kn(e,t),s=en.pointAdd(kn(e,o),{x:n[0]*l.tileSize,y:n[1]*l.tileSize});if(en.pointDistance(a,s){const n=ln[e].puzzle.tiles,l=bn(e,t),a=bn(e,o);let s;const i=[];l&&i.push(l),a&&i.push(a),l?s=l:a?s=a:(fn(e,{maxGroup:Dn(e)+1}),r(),s=Dn(e));if(vn(e,t,{group:s}),d(t),vn(e,o,{group:s}),d(o),i.length>0)for(const r of n){const t=se.decodePiece(r);i.includes(t.group)&&(vn(e,t.idx,{group:s}),d(t.idx))}})(e,t,o),l=Rn(e,t),((e,t)=>-1===On(e,t))(e,o))Un(e,l);else{const t=((e,t)=>{let o=0;for(const n of t){const t=Pn(e,n);t>o&&(o=t)}return o})(e,l);Mn(e,l,t)}return c(l),!0}return!1};let a=!1;for(const t of Rn(e,g)){const n=_n(e,t);if(o(e,t,n[0],[0,1])||o(e,t,n[1],[-1,0])||o(e,t,n[2],[0,-1])||o(e,t,n[3],[1,0])){a=!0;break}}if(a&&pn(e)===Q.ANY){const o=Gn(e,t)+1;yn(e,t,{d:p,ts:n,points:o}),u()}else yn(e,t,{d:p,ts:n}),u();a&&gn(e)===ee.REAL&&mn(e)===un(e)&&(fn(e,{finished:n}),r()),a&&l&&l(t)}}else yn(e,t,{d:p,ts:n}),u();s._last_mouse=i}else if(p===Ht){const l=o[1],a=o[2];yn(e,t,{x:l,y:a,ts:n}),u(),s._last_mouse={x:l,y:a}}else if(p===Kt){const l=o[1],a=o[2];yn(e,t,{x:l,y:a,ts:n}),u(),s._last_mouse={x:l,y:a}}else yn(e,t,{ts:n}),u();return function(e,t,o){ln[e].evtInfos[t]=o}(e,t,s),i}};let jn=-10,Wn=20,Hn=2,Kn=15;class Yn{constructor(e){this.radius=10,this.previousRadius=10,this.explodingDuration=100,this.hasExploded=!1,this.alive=!0,this.color=function(e){return"rgba("+e.random(0,255)+","+e.random(0,255)+","+e.random(0,255)+", 0.8)"}(e),this.px=window.innerWidth/4+Math.random()*window.innerWidth/2,this.py=window.innerHeight,this.vx=jn+Math.random()*Wn,this.vy=-1*(Hn+Math.random()*Kn),this.duration=0}update(e){if(this.hasExploded){const e=200-this.radius;this.previousRadius=this.radius,this.radius+=e/10,this.explodingDuration--,0==this.explodingDuration&&(this.alive=!1)}else this.vx+=0,this.vy+=1,this.vy>=0&&e&&this.explode(e),this.px+=this.vx,this.py+=this.vy}draw(e){e.beginPath(),e.arc(this.px,this.py,this.previousRadius,0,2*Math.PI,!1),this.hasExploded||(e.fillStyle=this.color,e.lineWidth=1,e.fill())}explode(e){this.hasExploded=!0;const t=3+Math.floor(3*Math.random());for(let o=0;o{this.resize()}))}setSpeedParams(){let e=0,t=0;for(;e=0;)t+=1,e+=t;Hn=t/2,Kn=t-Hn;const o=1/4*this.canvas.width/(t/2);jn=-o,Wn=2*o}resize(){this.setSpeedParams()}init(){this.readyBombs=[],this.explodedBombs=[],this.particles=[];for(let e=0;e<1;e++)this.readyBombs.push(new Yn(this.rng))}update(){100*Math.random()<5&&this.readyBombs.push(new Yn(this.rng));const e=[];for(;this.explodedBombs.length>0;){const t=this.explodedBombs.shift();if(!t)break;t.update(),t.alive&&e.push(t)}this.explodedBombs=e;const t=[];for(;this.readyBombs.length>0;){const e=this.readyBombs.shift();if(!e)break;e.update(this.particles),e.hasExploded?this.explodedBombs.push(e):t.push(e)}this.readyBombs=t;const o=[];for(;this.particles.length>0;){const e=this.particles.shift();if(!e)break;e.update(),e.alive&&o.push(e)}this.particles=o}render(){this.ctx.beginPath(),this.ctx.fillStyle="rgba(0, 0, 0, 0.1)",this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height);for(let e=0;e{const t=e.color+" "+e.d;if(!y[t]){const o=e.d?r:d;if(e.color){const n=e.d?c:u;y[t]=await createImageBitmap(Ho.colorizedCanvas(o,n,e.color))}else y[t]=o}return y[t]},v=function(e,t){return t.width=window.innerWidth,t.height=window.innerHeight,e.appendChild(t),window.addEventListener("resize",(()=>{t.width=window.innerWidth,t.height=window.innerHeight,ol=!0})),t}(l,Ho.createCanvas()),w={final:!1,log:[],logPointer:0,speeds:[.5,1,2,5,10,20,50,100,250,500],speedIdx:1,paused:!1,lastRealTs:0,lastGameTs:0,gameStartTs:0,skipNonActionPhases:!0,dataOffset:0};fo.onConnectionStateChange((e=>{a.setConnectionState(e)}));const b=async e=>{const t=w.dataOffset;w.dataOffset+=1e4;const o=await fo.requestReplayData(e,t);return w.log=w.log.slice(w.logPointer),w.logPointer=0,w.log.push(...o.log),0===o.log.length&&(w.final=!0),o};let x=()=>0;const C=async()=>{if("play"===n){const n=await fo.connect(o,e,t),l=se.decodeGame(n);Fn.setGame(l.id,l),x=()=>N()}else{if("replay"!==n)throw"[ 2020-12-22 MODE invalid, must be play|replay ]";{const t=await b(e);if(!t.game)throw"[ 2021-05-29 no game received ]";const o=se.decodeGame(t.game);Fn.setGame(o.id,o),w.lastRealTs=N(),w.gameStartTs=parseInt(t.log[0][4],10),w.lastGameTs=w.gameStartTs,x=()=>w.lastGameTs}}ol=!0};await C();const k=Fn.getPieceDrawOffset(e),A=Fn.getPieceDrawSize(e),S=Fn.getPuzzleWidth(e),P=Fn.getPuzzleHeight(e),T=Fn.getTableWidth(e),I=Fn.getTableHeight(e),z={x:(T-S)/2,y:(I-P)/2},D={w:S,h:P},E={w:A,h:A},_=await nn.loadPuzzleBitmaps(Fn.getPuzzle(e)),V=new Qn(v,Fn.getRng(e));V.init();const O=v.getContext("2d");v.classList.add("loaded");const U=jo();U.move(-(T-v.width)/2,-(I-v.height)/2);const B=function(e,t,o,n){let l=[],a=!0,s=!1,i=!1,r=!1,d=!1,c=!1,u=!1,p=!1;const g=(e,t)=>{const n=o.viewportToWorld({x:e,y:t});return[n.x,n.y]},h=e=>g(e.offsetX,e.offsetY),m=()=>g(e.width/2,e.height/2),y=(e,t)=>{a&&("ShiftLeft"===t.code||"ShiftRight"===t.code?p=e:"ArrowUp"===t.code||"KeyW"===t.code?r=e:"ArrowDown"===t.code||"KeyS"===t.code?d=e:"ArrowLeft"===t.code||"KeyA"===t.code?s=e:"ArrowRight"===t.code||"KeyD"===t.code?i=e:"KeyQ"===t.code?u=e:"KeyE"===t.code&&(c=e))};let f=null;e.addEventListener("mousedown",(e=>{f=h(e),0===e.button&&v([Ft,...f])})),e.addEventListener("mouseup",(e=>{f=h(e),0===e.button&&v([jt,...f])})),e.addEventListener("mousemove",(e=>{f=h(e),v([Wt,...f])})),e.addEventListener("wheel",(e=>{if(f=h(e),o.canZoom(e.deltaY<0?"in":"out")){const t=e.deltaY<0?Ht:Kt;v([t,...f])}})),t.addEventListener("keydown",(e=>y(!0,e))),t.addEventListener("keyup",(e=>y(!1,e))),t.addEventListener("keypress",(e=>{a&&("Space"===e.code&&v([Zt]),"replay"===n&&("KeyI"===e.code&&v([eo]),"KeyO"===e.code&&v([to]),"KeyP"===e.code&&v([Jt])),"KeyF"===e.code&&(el=!el,ol=!0),"KeyG"===e.code&&(tl=!tl,ol=!0),"KeyM"===e.code&&v([Xt]))}));const v=e=>{l.push(e)};return{addEvent:v,consumeAll:()=>{if(0===l.length)return[];const e=l.slice();return l=[],e},createKeyEvents:()=>{const e=(s?1:0)-(i?1:0),t=(r?1:0)-(d?1:0);if(0!==e||0!==t){const n=(p?24:12)*Math.sqrt(o.getCurrentZoom()),l=o.viewportDimToWorld({w:e*n,h:t*n});v([Lt,l.w,l.h]),f&&(f[0]-=l.w,f[1]-=l.h)}if(c&&u);else if(c){if(o.canZoom("in")){const e=f||m();v([Ht,...e])}}else if(u&&o.canZoom("out")){const e=f||m();v([Kt,...e])}},setHotkeys:e=>{a=e}}}(v,window,U,n),R=Fn.getImageUrl(e),G=()=>{const t=Fn.getStartTs(e),o=Fn.getFinishTs(e),n=x();a.setFinished(!!o),a.setDuration((o||n)-t)};G(),a.setPiecesDone(Fn.getFinishedPiecesCount(e)),a.setPiecesTotal(Fn.getPieceCount(e));const $=x();a.setActivePlayers(Fn.getActivePlayers(e,$)),a.setIdlePlayers(Fn.getIdlePlayers(e,$));const L=!!Fn.getFinishTs(e);let F=L;const j=()=>F&&!L,W=()=>{const e=localStorage.getItem("sound_volume");if(null===e)return 100;const t=parseInt(e,10);return isNaN(t)?100:t},H=()=>{const e=localStorage.getItem("sound_enabled");return null!==e&&"1"===e},K=()=>{const e=W();i.volume=e/100,i.play()},Y=()=>"replay"===n?localStorage.getItem("bg_color")||"#222222":Fn.getPlayerBgColor(e,t)||localStorage.getItem("bg_color")||"#222222",q=()=>"replay"===n?localStorage.getItem("player_color")||"#ffffff":Fn.getPlayerColor(e,t)||localStorage.getItem("player_color")||"#ffffff";let Q="",Z="",X=!1;const J=e=>{X=e;const[t,o]=e?[Q,"grab"]:[Z,"default"];v.style.cursor=`url('${t}') ${g} ${m}, ${o}`},ee=e=>{Q=Ho.colorizedCanvas(r,c,e).toDataURL(),Z=Ho.colorizedCanvas(d,u,e).toDataURL(),J(X)};ee(q());const te=()=>{a.setReplaySpeed&&a.setReplaySpeed(w.speeds[w.speedIdx]),a.setReplayPaused&&a.setReplayPaused(w.paused)},oe=()=>{w.speedIdx+1{w.speedIdx>=1&&(w.speedIdx--,te())},le=()=>{w.paused=!w.paused,te()},ae=[];let ie;let re;if("play"===n?ae.push(setInterval((()=>{G()}),1e3)):"replay"===n&&te(),"play"===n)fo.onServerChange((o=>{o[0],o[1],o[2];const n=o[3];for(const[l,a]of n)switch(l){case lo:{const o=se.decodePlayer(a);o.id!==t&&(Fn.setPlayer(e,o.id,o),ol=!0)}break;case no:{const t=se.decodePiece(a);Fn.setPiece(e,t.idx,t),ol=!0}break;case oo:Fn.setPuzzleData(e,a),ol=!0}F=!!Fn.getFinishTs(e)}));else if("replay"===n){const t=(t,o)=>{const n=t;if(n[0]===Rt){const t=n[1];return Fn.addPlayer(e,t,o),!0}if(n[0]===Gt){const t=Fn.getPlayerIdByIndex(e,n[1]);if(!t)throw"[ 2021-05-17 player not found (update player) ]";return Fn.addPlayer(e,t,o),!0}if(n[0]===$t){const t=Fn.getPlayerIdByIndex(e,n[1]);if(!t)throw"[ 2021-05-17 player not found (handle input) ]";const l=n[2];return Fn.handleInput(e,t,l,o),!0}return!1};let o=w.lastGameTs;const n=async()=>{w.logPointer+1>=w.log.length&&await b(e);const l=N();if(w.paused)return w.lastRealTs=l,void(ie=setTimeout(n,50));const a=(l-w.lastRealTs)*w.speeds[w.speedIdx];let s=w.lastGameTs+a;for(;;){if(w.paused)break;const e=w.logPointer+1;if(e>=w.log.length)break;const n=w.log[w.logPointer],l=o+n[n.length-1],a=w.log[e],i=a[a.length-1],r=l+i;if(r>s){w.skipNonActionPhases&&s+500*M{let t=!1;const o=e.fps||60,n=e.slow||1,l=e.update,a=e.render,s=window.requestAnimationFrame,i=1/o,r=n*i;let d,c=0,u=window.performance.now();const p=()=>{for(d=window.performance.now(),c+=Math.min(1,(d-u)/1e3);c>r;)c-=r,l(i);a(c/n),u=d,t||s(p)};return s(p),{stop:()=>{t=!0}}})({update:()=>{B.createKeyEvents();for(const o of B.consumeAll())if("play"===n){const n=o[0];if(n===Lt){const e=o[1],t=o[2],n=U.worldDimToViewport({w:e,h:t});ol=!0,U.move(n.w,n.h)}else if(n===Wt){if(de&&!Fn.getFirstOwnedPiece(e,t)){const e={x:o[1],y:o[2]},t=U.worldToViewport(e),n=Math.round(t.x-de.x),l=Math.round(t.y-de.y);ol=!0,U.move(n,l),de=t}}else if(n===qt)ee(o[1]);else if(n===Ft){const e={x:o[1],y:o[2]};de=U.worldToViewport(e),J(!0)}else if(n===jt)de=null,J(!1);else if(n===Ht){const e={x:o[1],y:o[2]};ol=!0,U.zoom("in",U.worldToViewport(e))}else if(n===Kt){const e={x:o[1],y:o[2]};ol=!0,U.zoom("out",U.worldToViewport(e))}else n===Zt?a.togglePreview():n===Xt&&a.toggleSoundsEnabled();const l=x();Fn.handleInput(e,t,o,l,(e=>{H()&&K()})).length>0&&(ol=!0),fo.sendClientEvent(o)}else if("replay"===n){const e=o[0];if(e===Jt)le();else if(e===to)ne();else if(e===eo)oe();else if(e===Lt){const e=o[1],t=o[2];ol=!0,U.move(e,t)}else if(e===Wt){if(de){const e={x:o[1],y:o[2]},t=U.worldToViewport(e),n=Math.round(t.x-de.x),l=Math.round(t.y-de.y);ol=!0,U.move(n,l),de=t}}else if(e===qt)ee(o[1]);else if(e===Ft){const e={x:o[1],y:o[2]};de=U.worldToViewport(e),J(!0)}else if(e===jt)de=null,J(!1);else if(e===Ht){const e={x:o[1],y:o[2]};ol=!0,U.zoom("in",U.worldToViewport(e))}else if(e===Kt){const e={x:o[1],y:o[2]};ol=!0,U.zoom("out",U.worldToViewport(e))}else e===Zt&&a.togglePreview()}F=!!Fn.getFinishTs(e),j()&&(V.update(),ol=!0)},render:async()=>{if(!ol)return;const o=x();let l,s,i;window.DEBUG&&Qo(0),O.fillStyle=Y(),O.fillRect(0,0,v.width,v.height),window.DEBUG&&Zo("clear done"),l=U.worldToViewportRaw(z),s=U.worldDimToViewportRaw(D),O.fillStyle="rgba(255, 255, 255, .3)",O.fillRect(l.x,l.y,s.w,s.h),window.DEBUG&&Zo("board done");const r=Fn.getPiecesSortedByZIndex(e);window.DEBUG&&Zo("get tiles done"),s=U.worldDimToViewportRaw(E);for(const e of r)(-1===e.owner?el:tl)&&(i=_[e.idx],l=U.worldToViewportRaw({x:k+e.pos.x,y:k+e.pos.y}),O.drawImage(i,0,0,i.width,i.height,l.x,l.y,s.w,s.h));window.DEBUG&&Zo("tiles done");const d=[];for(const a of Fn.getActivePlayers(e,o))c=a,("replay"===n||c.id!==t)&&(i=await f(a),l=U.worldToViewport(a),O.drawImage(i,l.x-g,l.y-m),d.push([`${a.name} (${a.points})`,l.x,l.y+h]));var c;O.fillStyle="white",O.textAlign="center";for(const[e,t,n]of d)O.fillText(e,t,n);window.DEBUG&&Zo("players done"),a.setActivePlayers(Fn.getActivePlayers(e,o)),a.setIdlePlayers(Fn.getIdlePlayers(e,o)),a.setPiecesDone(Fn.getFinishedPiecesCount(e)),window.DEBUG&&Zo("HUD done"),j()&&V.render(),ol=!1}}),{setHotkeys:e=>{B.setHotkeys(e)},onBgChange:e=>{localStorage.setItem("bg_color",e),B.addEvent([Yt,e])},onColorChange:e=>{localStorage.setItem("player_color",e),B.addEvent([qt,e])},onNameChange:e=>{localStorage.setItem("player_name",e),B.addEvent([Qt,e])},onSoundsEnabledChange:e=>{localStorage.setItem("sound_enabled",e?"1":"0")},onSoundsVolumeChange:e=>{Zn.info("vol changed",e),localStorage.setItem("sound_volume",`${e}`),K()},replayOnSpeedUp:oe,replayOnSpeedDown:ne,replayOnPauseToggle:le,replayOnSkipToggle:()=>{w.skipNonActionPhases=!w.skipNonActionPhases},previewImageUrl:R,player:{background:Y(),color:q(),name:"replay"===n?localStorage.getItem("player_name")||"#ffffff":Fn.getPlayerName(e,t)||localStorage.getItem("player_name")||"anon",soundsEnabled:H(),soundsVolume:W()},disconnect:fo.disconnect,connect:C,unload:()=>{ae.forEach((e=>{clearInterval(e)})),ie&&clearTimeout(ie),re&&re.stop()}}}var ll=e({name:"game",components:{PuzzleStatus:Ct,Scores:ft,SettingsOverlay:At,PreviewOverlay:Mt,ConnectionOverlay:vo,HelpOverlay:Ao},data:()=>({activePlayers:[],idlePlayers:[],finished:!1,duration:0,piecesDone:0,piecesTotal:0,overlay:"",connectionState:0,g:{player:{background:"",color:"",name:"",soundsEnabled:!1,soundsVolume:100},previewImageUrl:"",setHotkeys:e=>{},onBgChange:e=>{},onColorChange:e=>{},onNameChange:e=>{},onSoundsEnabledChange:e=>{},onSoundsVolumeChange:e=>{},connect:()=>{},disconnect:()=>{},unload:()=>{}}}),async mounted(){this.$route.params.id&&(this.$watch((()=>this.g.player.background),(e=>{this.g.onBgChange(e)})),this.$watch((()=>this.g.player.color),(e=>{this.g.onColorChange(e)})),this.$watch((()=>this.g.player.name),(e=>{this.g.onNameChange(e)})),this.$watch((()=>this.g.player.soundsEnabled),(e=>{this.g.onSoundsEnabledChange(e)})),this.$watch((()=>this.g.player.soundsVolume),(e=>{this.g.onSoundsVolumeChange(e)})),this.g=await nl(`${this.$route.params.id}`,this.$clientId,this.$config.WS_ADDRESS,"play",this.$el,{setActivePlayers:e=>{this.activePlayers=e},setIdlePlayers:e=>{this.idlePlayers=e},setFinished:e=>{this.finished=e},setDuration:e=>{this.duration=e},setPiecesDone:e=>{this.piecesDone=e},setPiecesTotal:e=>{this.piecesTotal=e},togglePreview:()=>{this.toggle("preview",!1)},setConnectionState:e=>{this.connectionState=e},toggleSoundsEnabled:()=>{this.g.player.soundsEnabled=!this.g.player.soundsEnabled}}))},unmounted(){this.g.unload(),this.g.disconnect()},methods:{reconnect(){this.g.connect()},toggle(e,t){""===this.overlay?(this.overlay=e,t&&this.g.setHotkeys(!1)):(this.overlay="",t&&this.g.setHotkeys(!0))}}});const al={id:"game"},sl={class:"menu"},il={class:"tabs"},rl=i("🧩 Puzzles");ll.render=function(e,l,i,r,d,c){const u=a("settings-overlay"),g=a("preview-overlay"),h=a("help-overlay"),m=a("connection-overlay"),y=a("puzzle-status"),f=a("router-link"),v=a("scores");return s(),t("div",al,[p(o(u,{onBgclick:l[1]||(l[1]=t=>e.toggle("settings",!0)),modelValue:e.g.player,"onUpdate:modelValue":l[2]||(l[2]=t=>e.g.player=t)},null,8,["modelValue"]),[[C,"settings"===e.overlay]]),p(o(g,{onBgclick:l[3]||(l[3]=t=>e.toggle("preview",!1)),img:e.g.previewImageUrl},null,8,["img"]),[[C,"preview"===e.overlay]]),p(o(h,{onBgclick:l[4]||(l[4]=t=>e.toggle("help",!0))},null,512),[[C,"help"===e.overlay]]),o(m,{connectionState:e.connectionState,onReconnect:e.reconnect},null,8,["connectionState","onReconnect"]),o(y,{finished:e.finished,duration:e.duration,piecesDone:e.piecesDone,piecesTotal:e.piecesTotal},null,8,["finished","duration","piecesDone","piecesTotal"]),o("div",sl,[o("div",il,[o(f,{class:"opener",to:{name:"index"},target:"_blank"},{default:n((()=>[rl])),_:1}),o("div",{class:"opener",onClick:l[5]||(l[5]=t=>e.toggle("preview",!1))},"πŸ–ΌοΈ Preview"),o("div",{class:"opener",onClick:l[6]||(l[6]=t=>e.toggle("settings",!0))},"πŸ› οΈ Settings"),o("div",{class:"opener",onClick:l[7]||(l[7]=t=>e.toggle("help",!0))},"ℹ️ Help")])]),o(v,{activePlayers:e.activePlayers,idlePlayers:e.idlePlayers},null,8,["activePlayers","idlePlayers"])])};var dl=e({name:"replay",components:{PuzzleStatus:Ct,Scores:ft,SettingsOverlay:At,PreviewOverlay:Mt,HelpOverlay:Ao},data:()=>({activePlayers:[],idlePlayers:[],finished:!1,duration:0,piecesDone:0,piecesTotal:0,skipNoAction:!0,overlay:"",connectionState:0,g:{player:{background:"",color:"",name:"",soundsEnabled:!1,soundsVolume:100},previewImageUrl:"",setHotkeys:e=>{},onBgChange:e=>{},onColorChange:e=>{},onNameChange:e=>{},onSoundsEnabledChange:e=>{},onSoundsVolumeChange:e=>{},replayOnSpeedUp:()=>{},replayOnSpeedDown:()=>{},replayOnPauseToggle:()=>{},replayOnSkipToggle:()=>{},connect:()=>{},disconnect:()=>{},unload:()=>{}},replay:{speed:1,paused:!1}}),async mounted(){this.$route.params.id&&(this.$watch((()=>this.g.player.background),(e=>{this.g.onBgChange(e)})),this.$watch((()=>this.g.player.color),(e=>{this.g.onColorChange(e)})),this.$watch((()=>this.g.player.name),(e=>{this.g.onNameChange(e)})),this.$watch((()=>this.g.player.soundsEnabled),(e=>{this.g.onSoundsEnabledChange(e)})),this.$watch((()=>this.g.player.soundsVolume),(e=>{this.g.onSoundsVolumeChange(e)})),this.g=await nl(`${this.$route.params.id}`,this.$clientId,this.$config.WS_ADDRESS,"replay",this.$el,{setActivePlayers:e=>{this.activePlayers=e},setIdlePlayers:e=>{this.idlePlayers=e},setFinished:e=>{this.finished=e},setDuration:e=>{this.duration=e},setPiecesDone:e=>{this.piecesDone=e},setPiecesTotal:e=>{this.piecesTotal=e},togglePreview:()=>{this.toggle("preview",!1)},setConnectionState:e=>{this.connectionState=e},setReplaySpeed:e=>{this.replay.speed=e},setReplayPaused:e=>{this.replay.paused=e},toggleSoundsEnabled:()=>{this.g.player.soundsEnabled=!this.g.player.soundsEnabled}}))},unmounted(){this.g.unload(),this.g.disconnect()},methods:{toggle(e,t){""===this.overlay?(this.overlay=e,t&&this.g.setHotkeys(!1)):(this.overlay="",t&&this.g.setHotkeys(!0))}},computed:{replayText(){return"Replay-Speed: "+this.replay.speed+"x"+(this.replay.paused?" Paused":"")}}});const cl={id:"replay"},ul=i("Skip no action phases: "),pl={class:"menu"},gl={class:"tabs"},hl=i("🧩 Puzzles");dl.render=function(e,l,i,d,c,u){const g=a("settings-overlay"),h=a("preview-overlay"),m=a("help-overlay"),y=a("puzzle-status"),f=a("router-link"),v=a("scores");return s(),t("div",cl,[p(o(g,{onBgclick:l[1]||(l[1]=t=>e.toggle("settings",!0)),modelValue:e.g.player,"onUpdate:modelValue":l[2]||(l[2]=t=>e.g.player=t)},null,8,["modelValue"]),[[C,"settings"===e.overlay]]),p(o(h,{onBgclick:l[3]||(l[3]=t=>e.toggle("preview",!1)),img:e.g.previewImageUrl},null,8,["img"]),[[C,"preview"===e.overlay]]),p(o(m,{onBgclick:l[4]||(l[4]=t=>e.toggle("help",!0))},null,512),[[C,"help"===e.overlay]]),o(y,{finished:e.finished,duration:e.duration,piecesDone:e.piecesDone,piecesTotal:e.piecesTotal},{default:n((()=>[o("div",null,[o("div",null,r(e.replayText),1),o("div",null,[o("label",null,[ul,p(o("input",{type:"checkbox","onUpdate:modelValue":l[5]||(l[5]=t=>e.skipNoAction=t),onChange:l[6]||(l[6]=t=>e.g.replayOnSkipToggle())},null,544),[[x,e.skipNoAction]])])]),o("button",{class:"btn",onClick:l[7]||(l[7]=t=>e.g.replayOnSpeedUp())},"⏫"),o("button",{class:"btn",onClick:l[8]||(l[8]=t=>e.g.replayOnSpeedDown())},"⏬"),o("button",{class:"btn",onClick:l[9]||(l[9]=t=>e.g.replayOnPauseToggle())},"⏸️")])])),_:1},8,["finished","duration","piecesDone","piecesTotal"]),o("div",pl,[o("div",gl,[o(f,{class:"opener",to:{name:"index"},target:"_blank"},{default:n((()=>[hl])),_:1}),o("div",{class:"opener",onClick:l[10]||(l[10]=t=>e.toggle("preview",!1))},"πŸ–ΌοΈ Preview"),o("div",{class:"opener",onClick:l[11]||(l[11]=t=>e.toggle("settings",!0))},"πŸ› οΈ Settings"),o("div",{class:"opener",onClick:l[12]||(l[12]=t=>e.toggle("help",!0))},"ℹ️ Help")])]),o(v,{activePlayers:e.activePlayers,idlePlayers:e.idlePlayers},null,8,["activePlayers","idlePlayers"])])},(async()=>{const e=await fetch("/api/conf"),t=await e.json();const o=k({history:A(),routes:[{name:"index",path:"/",component:j},{name:"new-game",path:"/new-game",component:it},{name:"game",path:"/g/:id",component:ll},{name:"replay",path:"/replay/:id",component:dl}]});o.beforeEach(((e,t)=>{t.name&&document.documentElement.classList.remove(`view-${String(t.name)}`),document.documentElement.classList.add(`view-${String(e.name)}`)}));const n=S(P);n.config.globalProperties.$config=t,n.config.globalProperties.$clientId=function(){let e=localStorage.getItem("ID");return e||(e=se.uniqId(),localStorage.setItem("ID",e)),e}(),n.use(o),n.mount("#app")})(); diff --git a/build/public/assets/index.e0726e0c.js b/build/public/assets/index.e0726e0c.js deleted file mode 100644 index a54c646..0000000 --- a/build/public/assets/index.e0726e0c.js +++ /dev/null @@ -1 +0,0 @@ -import{d as e,c as t,a as o,w as n,b as l,r as a,o as s,e as i,t as r,F as d,f as c,g as u,h as p,v as g,i as h,j as m,p as y,k as f,l as v,m as w,n as b,q as x,s as C,u as k,x as A,y as S}from"./vendor.684f7bc8.js";var P=e({name:"app",computed:{showNav(){return!["game","replay"].includes(String(this.$route.name))}}});const T={id:"app"},I={key:0,class:"nav"},z=i("Index"),D=i("New game");P.render=function(e,i,r,d,c,u){const p=a("router-link"),g=a("router-view");return s(),t("div",T,[e.showNav?(s(),t("ul",I,[o("li",null,[o(p,{class:"btn",to:{name:"index"}},{default:n((()=>[z])),_:1})]),o("li",null,[o(p,{class:"btn",to:{name:"new-game"}},{default:n((()=>[D])),_:1})])])):l("",!0),o(g)])};const E=864e5,_=e=>{const t=Math.floor(e/E);e%=E;const o=Math.floor(e/36e5);e%=36e5;const n=Math.floor(e/6e4);e%=6e4;return`${t}d ${o}h ${n}m ${Math.floor(e/1e3)}s`};var M=1,V=1e3,N=()=>{const e=new Date;return Date.UTC(e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate(),e.getUTCHours(),e.getUTCMinutes(),e.getUTCSeconds(),e.getUTCMilliseconds())},O=(e,t)=>_(t-e),U=_,B=e({name:"game-teaser",props:{game:{type:Object,required:!0}},computed:{style(){return{"background-image":`url("${this.game.imageUrl.replace("uploads/","uploads/r/")+"-375x210.webp"}")`}}},methods:{time(e,t){const o=t?"🏁":"⏳",n=e,l=t||N();return`${o} ${O(n,l)}`}}});const R={class:"game-info-text"},G=o("br",null,null,-1),$=o("br",null,null,-1),L=o("br",null,null,-1),F=i(" β†ͺ️ Watch replay ");B.render=function(e,d,c,u,p,g){const h=a("router-link");return s(),t("div",{class:"game-teaser",style:e.style},[o(h,{class:"game-info",to:{name:"game",params:{id:e.game.id}}},{default:n((()=>[o("span",R,[i(" 🧩 "+r(e.game.tilesFinished)+"/"+r(e.game.tilesTotal),1),G,i(" πŸ‘₯ "+r(e.game.players),1),$,i(" "+r(e.time(e.game.started,e.game.finished)),1),L])])),_:1},8,["to"]),e.game.hasReplay?(s(),t(h,{key:0,class:"game-replay",to:{name:"replay",params:{id:e.game.id}}},{default:n((()=>[F])),_:1},8,["to"])):l("",!0)],4)};var j=e({components:{GameTeaser:B},data:()=>({gamesRunning:[],gamesFinished:[]}),async created(){const e=await fetch("/api/index-data"),t=await e.json();this.gamesRunning=t.gamesRunning,this.gamesFinished=t.gamesFinished}});const W=o("h1",null,"Running games",-1),H=o("h1",null,"Finished games",-1);j.render=function(e,n,l,i,r,u){const p=a("game-teaser");return s(),t("div",null,[W,(s(!0),t(d,null,c(e.gamesRunning,((e,n)=>(s(),t("div",{class:"game-teaser-wrap",key:n},[o(p,{game:e},null,8,["game"])])))),128)),H,(s(!0),t(d,null,c(e.gamesFinished,((e,n)=>(s(),t("div",{class:"game-teaser-wrap",key:n},[o(p,{game:e},null,8,["game"])])))),128))])};var K=e({name:"image-teaser",props:{image:{type:Object,required:!0}},computed:{style(){return{backgroundImage:`url("${this.image.url.replace("uploads/","uploads/r/")+"-150x100.webp"}")`}}},emits:{click:null,editClick:null},methods:{onClick(){this.$emit("click")},onEditClick(){this.$emit("editClick")}}});K.render=function(e,n,l,a,i,r){return s(),t("div",{class:"imageteaser",style:e.style,onClick:n[2]||(n[2]=(...t)=>e.onClick&&e.onClick(...t))},[o("div",{class:"btn edit",onClick:n[1]||(n[1]=u(((...t)=>e.onEditClick&&e.onEditClick(...t)),["stop"]))},"✏️")],4)};var Y,q,Q,Z,X,J,ee,te,oe=e({name:"image-library",components:{ImageTeaser:K},props:{images:{type:Array,required:!0}},emits:{imageClicked:null,imageEditClicked:null},methods:{imageClicked(e){this.$emit("imageClicked",e)},imageEditClicked(e){this.$emit("imageEditClicked",e)}}});oe.render=function(e,o,n,l,i,r){const u=a("image-teaser");return s(),t("div",null,[(s(!0),t(d,null,c(e.images,((o,n)=>(s(),t(u,{image:o,onClick:t=>e.imageClicked(o),onEditClick:t=>e.imageEditClicked(o),key:n},null,8,["image","onClick","onEditClick"])))),128))])},(q=Y||(Y={}))[q.Flat=0]="Flat",q[q.Out=1]="Out",q[q.In=-1]="In",(Z=Q||(Q={}))[Z.FINAL=0]="FINAL",Z[Z.ANY=1]="ANY",(J=X||(X={}))[J.NORMAL=0]="NORMAL",J[J.ANY=1]="ANY",J[J.FLAT=2]="FLAT",(te=ee||(ee={}))[te.NORMAL=0]="NORMAL",te[te.REAL=1]="REAL";class ne{constructor(e){this.rand_high=e||3735929054,this.rand_low=1231121986^e}random(e,t){this.rand_high=(this.rand_high<<16)+(this.rand_high>>16)+this.rand_low&4294967295,this.rand_low=this.rand_low+this.rand_high&4294967295;return e+(this.rand_high>>>0)/4294967295*(t-e+1)|0}choice(e){return e[this.random(0,e.length-1)]}shuffle(e){const t=e.slice();for(let o=0;o<=t.length-2;o++){const e=this.random(o,t.length-1),n=t[o];t[o]=t[e],t[e]=n}return t}static serialize(e){return{rand_high:e.rand_high,rand_low:e.rand_low}}static unserialize(e){const t=new ne(0);return t.rand_high=e.rand_high,t.rand_low=e.rand_low,t}}const le=(e,t)=>{const o=`${e}`;return o.length>=t.length?o:t.substr(0,t.length-o.length)+o},ae=(...e)=>{const t=t=>(...o)=>{const n=new Date,l=le(n.getHours(),"00"),a=le(n.getMinutes(),"00"),s=le(n.getSeconds(),"00");console[t](`${l}:${a}:${s}`,...e,...o)};return{log:t("log"),error:t("error"),info:t("info")}};var se={hash:e=>{let t=0;for(let o=0;o{let t=e.toLowerCase();return t=t.replace(/[^a-z0-9]+/g,"-"),t=t.replace(/^-|-$/,""),t},uniqId:()=>Date.now().toString(36)+Math.random().toString(36).substring(2),encodeShape:function(e){return e.top+1<<0|e.right+1<<2|e.bottom+1<<4|e.left+1<<6},decodeShape:function(e){return{top:(e>>0&3)-1,right:(e>>2&3)-1,bottom:(e>>4&3)-1,left:(e>>6&3)-1}},encodePiece:function(e){return[e.idx,e.pos.x,e.pos.y,e.z,e.owner,e.group]},decodePiece:function(e){return{idx:e[0],pos:{x:e[1],y:e[2]},z:e[3],owner:e[4],group:e[5]}},encodePlayer:function(e){return[e.id,e.x,e.y,e.d,e.name,e.color,e.bgcolor,e.points,e.ts]},decodePlayer:function(e){return{id:e[0],x:e[1],y:e[2],d:e[3],name:e[4],color:e[5],bgcolor:e[6],points:e[7],ts:e[8]}},encodeGame:function(e){return[e.id,e.rng.type||"",ne.serialize(e.rng.obj),e.puzzle,e.players,e.evtInfos,e.scoreMode||Q.FINAL,e.shapeMode||X.ANY,e.snapMode||ee.NORMAL]},decodeGame:function(e){return{id:e[0],rng:{type:e[1],obj:ne.unserialize(e[2])},puzzle:e[3],players:e[4],evtInfos:e[5],scoreMode:e[6],shapeMode:e[7],snapMode:e[8]}},coordByPieceIdx:function(e,t){const o=e.width/e.tileSize;return{x:t%o,y:Math.floor(t/o)}},asQueryArgs:function(e){const t=[];for(const o in e){const n=[o,e[o]].map(encodeURIComponent);t.push(n.join("="))}return 0===t.length?"":`?${t.join("&")}`}};const ie={name:"responsive-image",props:{src:String,title:{type:String,default:""},height:{type:String,default:"100%"},width:{type:String,default:"100%"}},computed:{style(){return{display:"inline-block",verticalAlign:"text-bottom",backgroundImage:`url('${this.src}')`,backgroundRepeat:"no-repeat",backgroundSize:"contain",backgroundPosition:"center",width:this.width,height:this.height}}}};ie.render=function(e,o,n,l,a,i){return s(),t("div",{style:i.style,title:n.title},null,12,["title"])};var re=e({name:"tags-input",props:{modelValue:{type:Array,required:!0},autocompleteTags:{type:Function}},emits:{"update:modelValue":null},data:()=>({input:"",values:[],autocomplete:{idx:-1,values:[]}}),created(){this.values=this.modelValue},methods:{onKeyUp(e){return"ArrowDown"===e.code&&this.autocomplete.values.length>0?(this.autocomplete.idx0?(this.autocomplete.idx>0&&this.autocomplete.idx--,e.stopPropagation(),!1):","===e.key?(this.add(),e.stopPropagation(),!1):void(this.input&&this.autocompleteTags?(this.autocomplete.values=this.autocompleteTags(this.input,this.values),this.autocomplete.idx=-1):(this.autocomplete.values=[],this.autocomplete.idx=-1))},addVal(e){const t=e.replace(/,/g,"").trim();t&&(this.values.includes(t)||this.values.push(t),this.input="",this.autocomplete.values=[],this.autocomplete.idx=-1,this.$emit("update:modelValue",this.values),this.$refs.input.focus())},add(){const e=this.autocomplete.idx>=0?this.autocomplete.values[this.autocomplete.idx]:this.input;this.addVal(e)},rm(e){this.values=this.values.filter((t=>t!==e)),this.$emit("update:modelValue",this.values)}}});const de=m();y("data-v-a4fa5e7e");const ce={key:0,class:"autocomplete"};f();const ue=de(((e,n,a,i,u,m)=>(s(),t("div",null,[p(o("input",{ref:"input",class:"input",type:"text","onUpdate:modelValue":n[1]||(n[1]=t=>e.input=t),placeholder:"Plants, People",onChange:n[2]||(n[2]=(...t)=>e.onChange&&e.onChange(...t)),onKeydown:n[3]||(n[3]=h(((...t)=>e.add&&e.add(...t)),["enter"])),onKeyup:n[4]||(n[4]=(...t)=>e.onKeyUp&&e.onKeyUp(...t))},null,544),[[g,e.input]]),e.autocomplete.values?(s(),t("div",ce,[o("ul",null,[(s(!0),t(d,null,c(e.autocomplete.values,((o,n)=>(s(),t("li",{key:n,class:{active:n===e.autocomplete.idx},onClick:t=>e.addVal(o)},r(o),11,["onClick"])))),128))])])):l("",!0),(s(!0),t(d,null,c(e.values,((o,n)=>(s(),t("span",{key:n,class:"bit",onClick:t=>e.rm(o)},r(o)+" βœ–",9,["onClick"])))),128))]))));re.render=ue,re.__scopeId="data-v-a4fa5e7e";const pe=ae("NewImageDialog.vue");var ge=e({name:"new-image-dialog",components:{ResponsiveImage:ie,TagsInput:re},props:{autocompleteTags:{type:Function},uploadProgress:{type:Number},uploading:{type:String}},emits:{bgclick:null,setupGameClick:null,postToGalleryClick:null},data:()=>({previewUrl:"",file:null,title:"",tags:[],droppable:!1}),computed:{uploadProgressPercent(){return this.uploadProgress?Math.round(100*this.uploadProgress):0},canPostToGallery(){return!this.uploading&&!(!this.previewUrl||!this.file)},canSetupGameClick(){return!this.uploading&&!(!this.previewUrl||!this.file)}},methods:{imageFromDragEvt(e){var t;const o=null==(t=e.dataTransfer)?void 0:t.items;if(!o||0===o.length)return null;const n=o[0];return n.type.startsWith("image/")?n:null},onFileSelect(e){const t=e.target;if(!t.files)return;const o=t.files[0];o&&this.preview(o)},preview(e){const t=new FileReader;t.readAsDataURL(e),t.onload=t=>{this.previewUrl=t.target.result,this.file=e}},postToGallery(){this.$emit("postToGalleryClick",{file:this.file,title:this.title,tags:this.tags})},setupGameClick(){this.$emit("setupGameClick",{file:this.file,title:this.title,tags:this.tags})},onDrop(e){this.droppable=!1;const t=this.imageFromDragEvt(e);if(!t)return!1;const o=t.getAsFile();return!!o&&(this.file=o,this.preview(o),e.preventDefault(),!1)},onDragover(e){return!!this.imageFromDragEvt(e)&&(this.droppable=!0,e.preventDefault(),!1)},onDragleave(){pe.info("onDragleave"),this.droppable=!1}}});const he=o("div",{class:"drop-target"},null,-1),me={key:0,class:"has-image"},ye={key:1},fe={class:"upload"},ve=o("span",{class:"btn"},"Upload File",-1),we={class:"area-settings"},be=o("td",null,[o("label",null,"Title")],-1),xe=o("tr",null,[o("td",{colspan:"2"},[o("div",{class:"hint"},"Feel free to leave a credit to the artist/photographer in the title :)")])],-1),Ce=o("td",null,[o("label",null,"Tags")],-1),ke={class:"area-buttons"},Ae=i("πŸ–ΌοΈ Post to gallery"),Se=i("🧩 Post to gallery "),Pe=o("br",null,null,-1),Te=i(" + set up game");ge.render=function(e,n,l,c,h,m){const y=a("responsive-image"),f=a("tags-input");return s(),t("div",{class:"overlay new-image-dialog",onClick:n[11]||(n[11]=t=>e.$emit("bgclick"))},[o("div",{class:"overlay-content",onClick:n[10]||(n[10]=u((()=>{}),["stop"]))},[o("div",{class:["area-image",{"has-image":!!e.previewUrl,"no-image":!e.previewUrl,droppable:e.droppable}],onDrop:n[3]||(n[3]=(...t)=>e.onDrop&&e.onDrop(...t)),onDragover:n[4]||(n[4]=(...t)=>e.onDragover&&e.onDragover(...t)),onDragleave:n[5]||(n[5]=(...t)=>e.onDragleave&&e.onDragleave(...t))},[he,e.previewUrl?(s(),t("div",me,[o("span",{class:"remove btn",onClick:n[1]||(n[1]=t=>e.previewUrl="")},"X"),o(y,{src:e.previewUrl},null,8,["src"])])):(s(),t("div",ye,[o("label",fe,[o("input",{type:"file",style:{display:"none"},onChange:n[2]||(n[2]=(...t)=>e.onFileSelect&&e.onFileSelect(...t)),accept:"image/*"},null,32),ve])]))],34),o("div",we,[o("table",null,[o("tr",null,[be,o("td",null,[p(o("input",{type:"text","onUpdate:modelValue":n[6]||(n[6]=t=>e.title=t),placeholder:"Flower by @artist"},null,512),[[g,e.title]])])]),xe,o("tr",null,[Ce,o("td",null,[o(f,{modelValue:e.tags,"onUpdate:modelValue":n[7]||(n[7]=t=>e.tags=t),autocompleteTags:e.autocompleteTags},null,8,["modelValue","autocompleteTags"])])])])]),o("div",ke,[o("button",{class:"btn",disabled:!e.canPostToGallery,onClick:n[8]||(n[8]=(...t)=>e.postToGallery&&e.postToGallery(...t))},["postToGallery"===e.uploading?(s(),t(d,{key:0},[i("Uploading ("+r(e.uploadProgressPercent)+"%)",1)],64)):(s(),t(d,{key:1},[Ae],64))],8,["disabled"]),o("button",{class:"btn",disabled:!e.canSetupGameClick,onClick:n[9]||(n[9]=(...t)=>e.setupGameClick&&e.setupGameClick(...t))},["setupGame"===e.uploading?(s(),t(d,{key:0},[i("Uploading ("+r(e.uploadProgressPercent)+"%)",1)],64)):(s(),t(d,{key:1},[Se,Pe,Te],64))],8,["disabled"])])])])};var Ie=e({name:"edit-image-dialog",components:{ResponsiveImage:ie,TagsInput:re},props:{image:{type:Object,required:!0},autocompleteTags:{type:Function}},emits:{bgclick:null,saveClick:null},data:()=>({title:"",tags:[]}),created(){this.title=this.image.title,this.tags=this.image.tags.map((e=>e.title))},methods:{saveImage(){this.$emit("saveClick",{id:this.image.id,title:this.title,tags:this.tags})}}});const ze={class:"area-image"},De={class:"has-image"},Ee={class:"area-settings"},_e=o("td",null,[o("label",null,"Title")],-1),Me=o("tr",null,[o("td",{colspan:"2"},[o("div",{class:"hint"},"Feel free to leave a credit to the artist/photographer in the title :)")])],-1),Ve=o("td",null,[o("label",null,"Tags")],-1),Ne={class:"area-buttons"};Ie.render=function(e,n,l,i,r,d){const c=a("responsive-image"),h=a("tags-input");return s(),t("div",{class:"overlay edit-image-dialog",onClick:n[5]||(n[5]=t=>e.$emit("bgclick"))},[o("div",{class:"overlay-content",onClick:n[4]||(n[4]=u((()=>{}),["stop"]))},[o("div",ze,[o("div",De,[o(c,{src:e.image.url,title:e.image.title},null,8,["src","title"])])]),o("div",Ee,[o("table",null,[o("tr",null,[_e,o("td",null,[p(o("input",{type:"text","onUpdate:modelValue":n[1]||(n[1]=t=>e.title=t),placeholder:"Flower by @artist"},null,512),[[g,e.title]])])]),Me,o("tr",null,[Ve,o("td",null,[o(h,{modelValue:e.tags,"onUpdate:modelValue":n[2]||(n[2]=t=>e.tags=t),autocompleteTags:e.autocompleteTags},null,8,["modelValue","autocompleteTags"])])])])]),o("div",Ne,[o("button",{class:"btn",onClick:n[3]||(n[3]=(...t)=>e.saveImage&&e.saveImage(...t))},"πŸ–ΌοΈ Save image")])])])};var Oe=e({name:"new-game-dialog",components:{ResponsiveImage:ie},props:{image:{type:Object,required:!0}},emits:{newGame:null,bgclick:null},data:()=>({tiles:1e3,scoreMode:Q.ANY,shapeMode:X.NORMAL,snapMode:ee.NORMAL}),methods:{onNewGameClick(){this.$emit("newGame",{tiles:this.tilesInt,image:this.image,scoreMode:this.scoreModeInt,shapeMode:this.shapeModeInt,snapMode:this.snapModeInt})}},computed:{canStartNewGame(){return!!(this.tilesInt&&this.image&&this.image.url&&[0,1].includes(this.scoreModeInt))},scoreModeInt(){return parseInt(`${this.scoreMode}`,10)},shapeModeInt(){return parseInt(`${this.shapeMode}`,10)},snapModeInt(){return parseInt(`${this.snapMode}`,10)},tilesInt(){return parseInt(`${this.tiles}`,10)}}});const Ue={class:"area-image"},Be={class:"has-image"},Re={key:0,class:"image-title"},Ge={class:"area-settings"},$e=o("td",null,[o("label",null,"Pieces")],-1),Le=o("td",null,[o("label",null,"Scoring: ")],-1),Fe=i(" Any (Score when pieces are connected to each other or on final location)"),je=o("br",null,null,-1),We=i(" Final (Score when pieces are put to their final location)"),He=o("td",null,[o("label",null,"Shapes: ")],-1),Ke=i(" Normal"),Ye=o("br",null,null,-1),qe=i(" Any (flat pieces can occur anywhere)"),Qe=o("br",null,null,-1),Ze=i(" Flat (all pieces flat on all sides)"),Xe=o("td",null,[o("label",null,"Snapping: ")],-1),Je=i(" Normal (pieces snap to final destination and to each other)"),et=o("br",null,null,-1),tt=i(" Real (pieces snap only to corners, already snapped pieces and to each other)"),ot={class:"area-buttons"};Oe.render=function(e,n,i,d,c,h){const m=a("responsive-image");return s(),t("div",{class:"overlay new-game-dialog",onClick:n[11]||(n[11]=t=>e.$emit("bgclick"))},[o("div",{class:"overlay-content",onClick:n[10]||(n[10]=u((()=>{}),["stop"]))},[o("div",Ue,[o("div",Be,[o(m,{src:e.image.url,title:e.image.title},null,8,["src","title"])]),e.image.title?(s(),t("div",Re,'"'+r(e.image.title)+'"',1)):l("",!0)]),o("div",Ge,[o("table",null,[o("tr",null,[$e,o("td",null,[p(o("input",{type:"text","onUpdate:modelValue":n[1]||(n[1]=t=>e.tiles=t)},null,512),[[g,e.tiles]])])]),o("tr",null,[Le,o("td",null,[o("label",null,[p(o("input",{type:"radio","onUpdate:modelValue":n[2]||(n[2]=t=>e.scoreMode=t),value:"1"},null,512),[[v,e.scoreMode]]),Fe]),je,o("label",null,[p(o("input",{type:"radio","onUpdate:modelValue":n[3]||(n[3]=t=>e.scoreMode=t),value:"0"},null,512),[[v,e.scoreMode]]),We])])]),o("tr",null,[He,o("td",null,[o("label",null,[p(o("input",{type:"radio","onUpdate:modelValue":n[4]||(n[4]=t=>e.shapeMode=t),value:"0"},null,512),[[v,e.shapeMode]]),Ke]),Ye,o("label",null,[p(o("input",{type:"radio","onUpdate:modelValue":n[5]||(n[5]=t=>e.shapeMode=t),value:"1"},null,512),[[v,e.shapeMode]]),qe]),Qe,o("label",null,[p(o("input",{type:"radio","onUpdate:modelValue":n[6]||(n[6]=t=>e.shapeMode=t),value:"2"},null,512),[[v,e.shapeMode]]),Ze])])]),o("tr",null,[Xe,o("td",null,[o("label",null,[p(o("input",{type:"radio","onUpdate:modelValue":n[7]||(n[7]=t=>e.snapMode=t),value:"0"},null,512),[[v,e.snapMode]]),Je]),et,o("label",null,[p(o("input",{type:"radio","onUpdate:modelValue":n[8]||(n[8]=t=>e.snapMode=t),value:"1"},null,512),[[v,e.snapMode]]),tt])])])])]),o("div",ot,[o("button",{class:"btn",disabled:!e.canStartNewGame,onClick:n[9]||(n[9]=(...t)=>e.onNewGameClick&&e.onNewGameClick(...t))}," 🧩 Generate Puzzle ",8,["disabled"])])])])};const nt=async(e,t,o)=>new Promise(((n,l)=>{const a=new window.XMLHttpRequest;a.open(e,t,!0),a.withCredentials=!0;for(const e in o.headers||{})a.setRequestHeader(e,o.headers[e]);a.addEventListener("load",(function(e){n({status:this.status,text:this.responseText,json:async()=>JSON.parse(this.responseText)})})),a.addEventListener("error",(function(e){l(new Error("xhr error"))})),a.upload&&o.onUploadProgress&&a.upload.addEventListener("progress",(function(e){o.onUploadProgress&&o.onUploadProgress(e)})),a.send(o.body)}));var lt=(e,t)=>nt("post",e,t),at=e({components:{ImageLibrary:oe,NewImageDialog:ge,EditImageDialog:Ie,NewGameDialog:Oe},data:()=>({filters:{sort:"date_desc",tags:[]},images:[],tags:[],image:{id:0,filename:"",file:"",url:"",title:"",tags:[],created:0},dialog:"",uploading:"",uploadProgress:0}),async created(){await this.loadImages()},computed:{relevantTags(){return this.tags.filter((e=>e.total>0))}},methods:{autocompleteTags(e,t){return this.tags.filter((o=>!t.includes(o.title)&&o.title.toLowerCase().startsWith(e.toLowerCase()))).slice(0,10).map((e=>e.title))},toggleTag(e){this.filters.tags.includes(e.slug)?this.filters.tags=this.filters.tags.filter((t=>t!==e.slug)):this.filters.tags.push(e.slug),this.filtersChanged()},async loadImages(){const e=await fetch(`/api/newgame-data${se.asQueryArgs(this.filters)}`),t=await e.json();this.images=t.images,this.tags=t.tags},async filtersChanged(){await this.loadImages()},onImageClicked(e){this.image=e,this.dialog="new-game"},onImageEditClicked(e){this.image=e,this.dialog="edit-image"},async uploadImage(e){this.uploadProgress=0;const t=new FormData;t.append("file",e.file,e.file.name),t.append("title",e.title),t.append("tags",e.tags);const o=await lt("/api/upload",{body:t,onUploadProgress:e=>{this.uploadProgress=e.loaded/e.total}});return this.uploadProgress=1,await o.json()},async saveImage(e){const t=await fetch("/api/save-image",{method:"post",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify({id:e.id,title:e.title,tags:e.tags})});return await t.json()},async onSaveImageClick(e){await this.saveImage(e),this.dialog="",await this.loadImages()},async postToGalleryClick(e){this.uploading="postToGallery",await this.uploadImage(e),this.uploading="",this.dialog="",await this.loadImages()},async setupGameClick(e){this.uploading="setupGame";const t=await this.uploadImage(e);this.uploading="",this.loadImages(),this.image=t,this.dialog="new-game"},async onNewGame(e){const t=await fetch("/api/newgame",{method:"post",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify(e)});if(200===t.status){const e=await t.json();this.$router.push({name:"game",params:{id:e.id}})}}}});const st={class:"upload-image-teaser"},it=o("div",{class:"hint"},"(The image you upload will be added to the public gallery.)",-1),rt={key:0},dt=i(" Tags: "),ct=i(" Sort by: "),ut=o("option",{value:"date_desc"},"Newest first",-1),pt=o("option",{value:"date_asc"},"Oldest first",-1),gt=o("option",{value:"alpha_asc"},"A-Z",-1),ht=o("option",{value:"alpha_desc"},"Z-A",-1);at.render=function(e,n,i,u,g,h){const m=a("image-library"),y=a("new-image-dialog"),f=a("edit-image-dialog"),v=a("new-game-dialog");return s(),t("div",null,[o("div",st,[o("div",{class:"btn btn-big",onClick:n[1]||(n[1]=t=>e.dialog="new-image")},"Upload your image"),it]),o("div",null,[e.tags.length>0?(s(),t("label",rt,[dt,(s(!0),t(d,null,c(e.relevantTags,((o,n)=>(s(),t("span",{class:["bit",{on:e.filters.tags.includes(o.slug)}],key:n,onClick:t=>e.toggleTag(o)},r(o.title)+" ("+r(o.total)+")",11,["onClick"])))),128))])):l("",!0),o("label",null,[ct,p(o("select",{"onUpdate:modelValue":n[2]||(n[2]=t=>e.filters.sort=t),onChange:n[3]||(n[3]=(...t)=>e.filtersChanged&&e.filtersChanged(...t))},[ut,pt,gt,ht],544),[[w,e.filters.sort]])])]),o(m,{images:e.images,onImageClicked:e.onImageClicked,onImageEditClicked:e.onImageEditClicked},null,8,["images","onImageClicked","onImageEditClicked"]),"new-image"===e.dialog?(s(),t(y,{key:0,autocompleteTags:e.autocompleteTags,onBgclick:n[4]||(n[4]=t=>e.dialog=""),uploadProgress:e.uploadProgress,uploading:e.uploading,onPostToGalleryClick:e.postToGalleryClick,onSetupGameClick:e.setupGameClick},null,8,["autocompleteTags","uploadProgress","uploading","onPostToGalleryClick","onSetupGameClick"])):l("",!0),"edit-image"===e.dialog?(s(),t(f,{key:1,autocompleteTags:e.autocompleteTags,onBgclick:n[5]||(n[5]=t=>e.dialog=""),onSaveClick:e.onSaveImageClick,image:e.image},null,8,["autocompleteTags","onSaveClick","image"])):l("",!0),e.image&&"new-game"===e.dialog?(s(),t(v,{key:2,onBgclick:n[6]||(n[6]=t=>e.dialog=""),onNewGame:e.onNewGame,image:e.image},null,8,["onNewGame","image"])):l("",!0)])};var mt=e({name:"scores",props:{activePlayers:{type:Array,required:!0},idlePlayers:{type:Array,required:!0}},computed:{actives(){return this.activePlayers.sort(((e,t)=>t.points-e.points)),this.activePlayers},idles(){return this.idlePlayers.sort(((e,t)=>t.points-e.points)),this.idlePlayers}}});const yt={class:"scores"},ft=o("div",null,"Scores",-1),vt=o("td",null,"⚑",-1),wt=o("td",null,"πŸ’€",-1);mt.render=function(e,n,l,a,i,u){return s(),t("div",yt,[ft,o("table",null,[(s(!0),t(d,null,c(e.actives,((e,n)=>(s(),t("tr",{key:n,style:{color:e.color}},[vt,o("td",null,r(e.name),1),o("td",null,r(e.points),1)],4)))),128)),(s(!0),t(d,null,c(e.idles,((e,n)=>(s(),t("tr",{key:n,style:{color:e.color}},[wt,o("td",null,r(e.name),1),o("td",null,r(e.points),1)],4)))),128))])])};var bt=e({name:"puzzle-status",props:{finished:{type:Boolean,required:!0},duration:{type:Number,required:!0},piecesDone:{type:Number,required:!0},piecesTotal:{type:Number,required:!0}},computed:{icon(){return this.finished?"🏁":"⏳"},durationStr(){return U(this.duration)}}});const xt={class:"timer"};bt.render=function(e,n,l,a,i,d){return s(),t("div",xt,[o("div",null," 🧩 "+r(e.piecesDone)+"/"+r(e.piecesTotal),1),o("div",null,r(e.icon)+" "+r(e.durationStr),1),b(e.$slots,"default")])};var Ct=e({name:"settings-overlay",emits:{bgclick:null,"update:modelValue":null},props:{modelValue:{type:Object,required:!0}},methods:{updateVolume(e){this.modelValue.soundsVolume=e.target.value},decreaseVolume(){const e=parseInt(this.modelValue.soundsVolume,10)-5;this.modelValue.soundsVolume=Math.max(0,e)},increaseVolume(){const e=parseInt(this.modelValue.soundsVolume,10)+5;this.modelValue.soundsVolume=Math.min(100,e)}},created(){this.$watch("modelValue",(e=>{this.$emit("update:modelValue",e)}),{deep:!0})}});const kt=m();y("data-v-a1d1c822");const At=o("td",null,[o("label",null,"Background: ")],-1),St=o("td",null,[o("label",null,"Color: ")],-1),Pt=o("td",null,[o("label",null,"Name: ")],-1),Tt=o("td",null,[o("label",null,"Sounds: ")],-1),It=o("td",null,[o("label",null,"Sounds Volume: ")],-1),zt={class:"sound-volume"};f();const Dt=kt(((e,n,l,a,i,r)=>(s(),t("div",{class:"overlay transparent",onClick:n[9]||(n[9]=t=>e.$emit("bgclick"))},[o("table",{class:"overlay-content settings",onClick:n[8]||(n[8]=u((()=>{}),["stop"]))},[o("tr",null,[At,o("td",null,[p(o("input",{type:"color","onUpdate:modelValue":n[1]||(n[1]=t=>e.modelValue.background=t)},null,512),[[g,e.modelValue.background]])])]),o("tr",null,[St,o("td",null,[p(o("input",{type:"color","onUpdate:modelValue":n[2]||(n[2]=t=>e.modelValue.color=t)},null,512),[[g,e.modelValue.color]])])]),o("tr",null,[Pt,o("td",null,[p(o("input",{type:"text",maxLength:"16","onUpdate:modelValue":n[3]||(n[3]=t=>e.modelValue.name=t)},null,512),[[g,e.modelValue.name]])])]),o("tr",null,[Tt,o("td",null,[p(o("input",{type:"checkbox","onUpdate:modelValue":n[4]||(n[4]=t=>e.modelValue.soundsEnabled=t)},null,512),[[x,e.modelValue.soundsEnabled]])])]),o("tr",null,[It,o("td",zt,[o("span",{onClick:n[5]||(n[5]=(...t)=>e.decreaseVolume&&e.decreaseVolume(...t))},"πŸ”‰"),o("input",{type:"range",min:"0",max:"100",value:e.modelValue.soundsVolume,onChange:n[6]||(n[6]=(...t)=>e.updateVolume&&e.updateVolume(...t))},null,40,["value"]),o("span",{onClick:n[7]||(n[7]=(...t)=>e.increaseVolume&&e.increaseVolume(...t))},"πŸ”Š")])])])]))));Ct.render=Dt,Ct.__scopeId="data-v-a1d1c822";var Et=e({name:"preview-overlay",props:{img:String},emits:{bgclick:null},computed:{previewStyle(){return{backgroundImage:`url('${this.img}')`}}}});const _t={class:"preview"};Et.render=function(e,n,l,a,i,r){return s(),t("div",{class:"overlay",onClick:n[1]||(n[1]=t=>e.$emit("bgclick"))},[o("div",_t,[o("div",{class:"img",style:e.previewStyle},null,4)])])};var Mt=1,Vt=4,Nt=2,Ot=3,Ut=2,Bt=4,Rt=3,Gt=9,$t=1,Lt=2,Ft=3,jt=4,Wt=5,Ht=6,Kt=7,Yt=8,qt=10,Qt=11,Zt=12,Xt=13,Jt=14,eo=1,to=2,oo=3;const no=ae("Communication.js");let lo,ao=[],so=e=>{ao.push(e)},io=[],ro=e=>{io.push(e)};let co=0;const uo=e=>{co!==e&&(co=e,ro(e))};function po(e){if(2===co)try{lo.send(JSON.stringify(e))}catch(t){no.info("unable to send message.. maybe because ws is invalid?")}}let go,ho;var mo={connect:function(e,t,o){return go=0,ho={},uo(3),new Promise((n=>{lo=new WebSocket(e,o+"|"+t),lo.onopen=()=>{uo(2),po([Ot])},lo.onmessage=e=>{const t=JSON.parse(e.data),l=t[0];if(l===Vt){const e=t[1];n(e)}else{if(l!==Mt)throw`[ 2021-05-09 invalid connect msgType ${l} ]`;{const e=t[1],n=t[2];if(e===o&&ho[n])return void delete ho[n];so(t)}}},lo.onerror=()=>{throw uo(1),"[ 2021-05-15 onerror ]"},lo.onclose=e=>{4e3===e.code||1001===e.code?uo(4):uo(1)}}))},requestReplayData:async function(e,t){const o={gameId:e,offset:t},n=await fetch(`/api/replay-data${se.asQueryArgs(o)}`);return await n.json()},disconnect:function(){lo&&lo.close(4e3),go=0,ho={}},sendClientEvent:function(e){go++,ho[go]=e,po([Nt,go,ho[go]])},onServerChange:function(e){so=e;for(const t of ao)so(t);ao=[]},onConnectionStateChange:function(e){ro=e;for(const t of io)ro(t);io=[]},CODE_CUSTOM_DISCONNECT:4e3,CONN_STATE_NOT_CONNECTED:0,CONN_STATE_DISCONNECTED:1,CONN_STATE_CLOSED:4,CONN_STATE_CONNECTED:2,CONN_STATE_CONNECTING:3},yo=e({name:"connection-overlay",emits:{reconnect:null},props:{connectionState:Number},computed:{lostConnection(){return this.connectionState===mo.CONN_STATE_DISCONNECTED},connecting(){return this.connectionState===mo.CONN_STATE_CONNECTING},show(){return!(!this.lostConnection&&!this.connecting)}}});const fo={key:0,class:"overlay connection-lost"},vo={key:0,class:"overlay-content"},wo=o("div",null,"⁉️ LOST CONNECTION ⁉️",-1),bo={key:1,class:"overlay-content"},xo=o("div",null,"Connecting...",-1);yo.render=function(e,n,a,i,r,d){return e.show?(s(),t("div",fo,[e.lostConnection?(s(),t("div",vo,[wo,o("span",{class:"btn",onClick:n[1]||(n[1]=t=>e.$emit("reconnect"))},"Reconnect")])):l("",!0),e.connecting?(s(),t("div",bo,[xo])):l("",!0)])):l("",!0)};var Co=e({name:"help-overlay",emits:{bgclick:null}});const ko=o("tr",null,[o("td",null,"⬆️ Move up:"),o("td",null,[o("div",null,[o("kbd",null,"W"),i("/"),o("kbd",null,"↑"),i("/πŸ–±οΈ")])])],-1),Ao=o("tr",null,[o("td",null,"⬇️ Move down:"),o("td",null,[o("div",null,[o("kbd",null,"S"),i("/"),o("kbd",null,"↓"),i("/πŸ–±οΈ")])])],-1),So=o("tr",null,[o("td",null,"⬅️ Move left:"),o("td",null,[o("div",null,[o("kbd",null,"A"),i("/"),o("kbd",null,"←"),i("/πŸ–±οΈ")])])],-1),Po=o("tr",null,[o("td",null,"➑️ Move right:"),o("td",null,[o("div",null,[o("kbd",null,"D"),i("/"),o("kbd",null,"β†’"),i("/πŸ–±οΈ")])])],-1),To=o("tr",null,[o("td"),o("td",null,[o("div",null,[i("Move faster by holding "),o("kbd",null,"Shift")])])],-1),Io=o("tr",null,[o("td",null,"πŸ”+ Zoom in:"),o("td",null,[o("div",null,[o("kbd",null,"E"),i("/πŸ–±οΈ-Wheel")])])],-1),zo=o("tr",null,[o("td",null,"πŸ”- Zoom out:"),o("td",null,[o("div",null,[o("kbd",null,"Q"),i("/πŸ–±οΈ-Wheel")])])],-1),Do=o("tr",null,[o("td",null,"πŸ–ΌοΈ Toggle preview:"),o("td",null,[o("div",null,[o("kbd",null,"Space")])])],-1),Eo=o("tr",null,[o("td",null,"πŸ§©βœ”οΈ Toggle fixed pieces:"),o("td",null,[o("div",null,[o("kbd",null,"F")])])],-1),_o=o("tr",null,[o("td",null,"πŸ§©β“ Toggle loose pieces:"),o("td",null,[o("div",null,[o("kbd",null,"G")])])],-1),Mo=o("tr",null,[o("td",null,"πŸ”‰ Toggle sounds:"),o("td",null,[o("div",null,[o("kbd",null,"M")])])],-1),Vo=o("tr",null,[o("td",null,"⏫ Speed up (replay):"),o("td",null,[o("div",null,[o("kbd",null,"I")])])],-1),No=o("tr",null,[o("td",null,"⏬ Speed down (replay):"),o("td",null,[o("div",null,[o("kbd",null,"O")])])],-1),Oo=o("tr",null,[o("td",null,"⏸️ Pause (replay):"),o("td",null,[o("div",null,[o("kbd",null,"P")])])],-1);Co.render=function(e,n,l,a,i,r){return s(),t("div",{class:"overlay transparent",onClick:n[2]||(n[2]=t=>e.$emit("bgclick"))},[o("table",{class:"overlay-content help",onClick:n[1]||(n[1]=u((()=>{}),["stop"]))},[ko,Ao,So,Po,To,Io,zo,Do,Eo,_o,Mo,Vo,No,Oo])])};var Uo=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"/assets/click.bb97cb07.mp3"}),Bo=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAW0lEQVQ4je1RywrAIAxLxP//5exixRWlVgZelpOKeTQFfnDypgy3eLIkSLLL8mxGPoHsU2hPAgDHBLvRX6hZZw/fwT0BtlLSONqCbWAmEIqMZOCDDlaDR3N03gOyDCn+y4DWmAAAAABJRU5ErkJggg=="}),Ro=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAARElEQVQ4jWNgGAU0Af+hmBCbgYGBgYERhwHEAEYGBgYGJtIdiApYyLAZBVDsAqoagC1ACQJyY4ERg0GCISh6KA4DigEAou8LC+LnIJoAAAAASUVORK5CYII="}),Go=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAcUlEQVQ4ja1TQQ7AIAgD///n7jCozA2Hbk00jbG1KIrcARszTugoBs49qioZj7r2kKACptkyAOCJsJuA+GzglwHjvMSSWFVaENWVASxh5eRLiq5fN/ASjI89VcP2K3hHpq1cEXNaMfnrL3TDZP2tDuoOA6MzCCXWr38AAAAASUVORK5CYII="}),$o=Object.freeze({__proto__:null,[Symbol.toStringTag]:"Module",default:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAU0lEQVQ4jWNgoAH4D8X42HDARKlt5BoAd82AuQAOGLGIYQQUPv0wF5CiCQUge4EsQ5C9QI4BjMguwBYeBAElscCIy1ZivMKIwSDBEBQ9FCckigEAU3QOD7TGvY4AAAAASUVORK5CYII="});function Lo(){let e=0,t=0,o=1;const n=(n,l)=>{e+=n/o,t+=l/o},l=e=>{const t=o+.05*o*("in"===e?1:-1);return Math.min(Math.max(t,.1),6)},a=n=>({x:n.x/o-e,y:n.y/o-t}),s=n=>({x:(n.x+e)*o,y:(n.y+t)*o}),i=e=>({w:e.w*o,h:e.h*o}),r=e=>({w:e.w/o,h:e.h/o});return{getCurrentZoom:()=>o,move:n,canZoom:e=>o!=l(e),zoom:(e,t)=>((e,t)=>{if(o==e)return!1;const l=1-o/e;return n(-t.x*l,-t.y*l),o=e,!0})(l(e),t),worldToViewport:e=>{const{x:t,y:o}=s(e);return{x:Math.round(t),y:Math.round(o)}},worldToViewportRaw:s,worldDimToViewport:e=>{const{w:t,h:o}=i(e);return{w:Math.round(t),h:Math.round(o)}},worldDimToViewportRaw:i,viewportToWorld:e=>{const{x:t,y:o}=a(e);return{x:Math.round(t),y:Math.round(o)}},viewportToWorldRaw:a,viewportDimToWorld:e=>{const{w:t,h:o}=r(e);return{w:Math.round(t),h:Math.round(o)}},viewportDimToWorldRaw:r}}function Fo(e=0,t=0){const o=document.createElement("canvas");return o.width=e,o.height=t,o}var jo={createCanvas:Fo,loadImageToBitmap:async function(e){return new Promise((t=>{const o=new Image;o.onload=()=>{createImageBitmap(o).then(t)},o.src=e}))},resizeBitmap:async function(e,t,o){const n=Fo(t,o);return n.getContext("2d").drawImage(e,0,0,e.width,e.height,0,0,t,o),await createImageBitmap(n)},colorizedCanvas:function(e,t,o){const n=Fo(e.width,e.height),l=n.getContext("2d");return l.save(),l.drawImage(t,0,0),l.fillStyle=o,l.globalCompositeOperation="source-in",l.fillRect(0,0,t.width,t.height),l.restore(),l.save(),l.globalCompositeOperation="destination-over",l.drawImage(e,0,0),l.restore(),n}};const Wo=ae("Debug.js");let Ho=0,Ko=0;var Yo=e=>{Ho=performance.now(),Ko=e},qo=e=>{const t=performance.now(),o=t-Ho;o>Ko&&Wo.log(e+": "+o),Ho=t};function Qo(e,t){const o=e.x-t.x,n=e.y-t.y;return Math.sqrt(o*o+n*n)}function Zo(e){return{x:e.x+e.w/2,y:e.y+e.h/2}}var Xo={pointSub:function(e,t){return{x:e.x-t.x,y:e.y-t.y}},pointAdd:function(e,t){return{x:e.x+t.x,y:e.y+t.y}},pointDistance:Qo,pointInBounds:function(e,t){return e.x>=t.x&&e.x<=t.x+t.w&&e.y>=t.y&&e.y<=t.y+t.h},rectCenter:Zo,rectMoved:function(e,t,o){return{x:e.x+t,y:e.y+o,w:e.w,h:e.h}},rectCenterDistance:function(e,t){return Qo(Zo(e),Zo(t))},rectsOverlap:function(e,t){return!(t.x>e.x+e.w||e.x>t.x+t.w||t.y>e.y+e.h||e.y>t.y+t.h)}};const Jo=ae("PuzzleGraphics.js");function en(e,t){const o=se.coordByPieceIdx(e,t);return{x:o.x*e.tileSize,y:o.y*e.tileSize,w:e.tileSize,h:e.tileSize}}var tn={loadPuzzleBitmaps:async function(e){const t=await jo.loadImageToBitmap(e.info.imageUrl),o=await jo.resizeBitmap(t,e.info.width,e.info.height);return await async function(e,t,o){Jo.log("start createPuzzleTileBitmaps");const n=o.tileSize,l=o.tileMarginWidth,a=o.tileDrawSize,s=n/100,i=[0,0,40,15,37,5,37,5,40,0,38,-5,38,-5,20,-20,50,-20,50,-20,80,-20,62,-5,62,-5,60,0,63,5,63,5,65,15,100,0],r=new Array(t.length),d={};function c(e){const t=`${e.top}${e.right}${e.left}${e.bottom}`;if(d[t])return d[t];const o=new Path2D,a={x:l,y:l},r=Xo.pointAdd(a,{x:n,y:0}),c=Xo.pointAdd(r,{x:0,y:n}),u=Xo.pointSub(c,{x:n,y:0});if(o.moveTo(a.x,a.y),0!==e.top)for(let n=0;nse.decodePiece(on[e].puzzle.tiles[t]),vn=(e,t)=>fn(e,t).group,wn=(e,t)=>{const o=on[e].puzzle.info;return 0===t||t===o.tilesX-1||t===o.tiles-o.tilesX||t===o.tiles-1},bn=(e,t)=>{const o=on[e].puzzle.info,n={x:(o.table.width-o.width)/2,y:(o.table.height-o.height)/2},l=function(e,t){const o=on[e].puzzle.info,n=se.coordByPieceIdx(o,t),l=n.x*o.tileSize,a=n.y*o.tileSize;return{x:l,y:a}}(e,t);return Xo.pointAdd(n,l)},xn=(e,t)=>fn(e,t).pos,Cn=e=>{const t=Rn(e),o=Gn(e),n=Math.round(t/4),l=Math.round(o/4);return{x:0-n,y:0-l,w:t+2*n,h:o+2*l}},kn=(e,t)=>{const o=Tn(e),n=fn(e,t);return{x:n.pos.x,y:n.pos.y,w:o,h:o}},An=(e,t)=>fn(e,t).z,Sn=(e,t)=>{for(const o of on[e].puzzle.tiles){const e=se.decodePiece(o);if(e.owner===t)return e.idx}return-1},Pn=e=>on[e].puzzle.info.tileDrawSize,Tn=e=>on[e].puzzle.info.tileSize,In=e=>on[e].puzzle.data.maxGroup,zn=e=>on[e].puzzle.data.maxZ;function Dn(e,t){const o=on[e].puzzle.info,n=se.coordByPieceIdx(o,t);return[n.y>0?t-o.tilesX:-1,n.x0?t-1:-1]}const En=(e,t,o)=>{for(const n of t)yn(e,n,{z:o})},_n=(e,t,o)=>{const n=xn(e,t);yn(e,t,{pos:Xo.pointAdd(n,o)})},Mn=(e,t,o)=>{const n=Pn(e),l=Cn(e),a=o;for(const s of t){const t=fn(e,s);t.pos.x+o.xl.x+l.w&&(a.x=Math.min(l.x+l.w-t.pos.x+n,a.x)),t.pos.y+o.yl.y+l.h&&(a.y=Math.min(l.y+l.h-t.pos.y+n,a.y))}for(const s of t)_n(e,s,a)},Vn=(e,t)=>fn(e,t).owner,Nn=(e,t)=>{for(const o of t)yn(e,o,{owner:-1,z:1})},On=(e,t,o)=>{for(const n of t)yn(e,n,{owner:o})};function Un(e,t){const o=on[e].puzzle.tiles,n=se.decodePiece(o[t]),l=[];if(n.group)for(const a of o){const e=se.decodePiece(a);e.group===n.group&&l.push(e.idx)}else l.push(n.idx);return l}const Bn=(e,t)=>{const o=ln(e,t);return o?o.points:0},Rn=e=>on[e].puzzle.info.table.width,Gn=e=>on[e].puzzle.info.table.height;var $n={setGame:function(e,t){on[e]=t},exists:function(e){return!!on[e]||!1},playerExists:sn,getActivePlayers:function(e,t){const o=t-30*V;return rn(e).filter((e=>e.ts>=o))},getIdlePlayers:function(e,t){const o=t-30*V;return rn(e).filter((e=>e.ts0))},addPlayer:function(e,t,o){sn(e,t)?hn(e,t,{ts:o}):an(e,t,function(e,t){return{id:e,x:0,y:0,d:0,name:null,color:null,bgcolor:null,points:0,ts:t}}(t,o))},getFinishedPiecesCount:gn,getPieceCount:dn,getImageUrl:function(e){return on[e].puzzle.info.imageUrl},setImageUrl:function(e,t){on[e].puzzle.info.imageUrl=t},get:function(e){return on[e]||null},getAllGames:function(){return Object.values(on).sort(((e,t)=>pn(e.id)===pn(t.id)?t.puzzle.data.started-e.puzzle.data.started:pn(e.id)?1:-1))},getPlayerBgColor:(e,t)=>{const o=ln(e,t);return o?o.bgcolor:null},getPlayerColor:(e,t)=>{const o=ln(e,t);return o?o.color:null},getPlayerName:(e,t)=>{const o=ln(e,t);return o?o.name:null},getPlayerIndexById:nn,getPlayerIdByIndex:function(e,t){return on[e].players.length>t?se.decodePlayer(on[e].players[t]).id:null},changePlayer:hn,setPlayer:an,setPiece:function(e,t,o){on[e].puzzle.tiles[t]=se.encodePiece(o)},setPuzzleData:function(e,t){on[e].puzzle.data=t},getTableWidth:Rn,getTableHeight:Gn,getPuzzle:e=>on[e].puzzle,getRng:e=>on[e].rng.obj,getPuzzleWidth:e=>on[e].puzzle.info.width,getPuzzleHeight:e=>on[e].puzzle.info.height,getPiecesSortedByZIndex:function(e){return on[e].puzzle.tiles.map(se.decodePiece).sort(((e,t)=>e.z-t.z))},getFirstOwnedPiece:(e,t)=>{const o=Sn(e,t);return o<0?null:on[e].puzzle.tiles[o]},getPieceDrawOffset:e=>on[e].puzzle.info.tileDrawOffset,getPieceDrawSize:Pn,getFinalPiecePos:bn,getStartTs:e=>on[e].puzzle.data.started,getFinishTs:e=>on[e].puzzle.data.finished,handleInput:function(e,t,o,n,l){const a=on[e].puzzle,s=function(e,t){return t in on[e].evtInfos?on[e].evtInfos[t]:{_last_mouse:null,_last_mouse_down:null}}(e,t),i=[],r=()=>{i.push([eo,a.data])},d=t=>{i.push([to,se.encodePiece(fn(e,t))])},c=e=>{for(const t of e)d(t)},u=()=>{const o=ln(e,t);o&&i.push([oo,se.encodePlayer(o)])},p=o[0];if(p===Ht){const l=o[1];hn(e,t,{bgcolor:l,ts:n}),u()}else if(p===Kt){const l=o[1];hn(e,t,{color:l,ts:n}),u()}else if(p===Yt){const l=`${o[1]}`.substr(0,16);hn(e,t,{name:l,ts:n}),u()}else if(p===Gt){const l=o[1],a=o[2],s=ln(e,t);if(s){const o=s.x-l,i=s.y-a;hn(e,t,{ts:n,x:o,y:i}),u()}}else if(p===$t){const l={x:o[1],y:o[2]};hn(e,t,{d:1,ts:n}),u(),s._last_mouse_down=l;const a=((e,t)=>{const o=on[e].puzzle.info,n=on[e].puzzle.tiles;let l=-1,a=-1;for(let s=0;sl)&&(l=e.z,a=s)}return a})(e,l);if(a>=0){const o=zn(e)+1;mn(e,{maxZ:o}),r();const n=Un(e,a);En(e,n,zn(e)),On(e,n,t),c(n)}s._last_mouse=l}else if(p===Ft){const l=o[1],a=o[2],i={x:l,y:a};if(null===s._last_mouse_down)hn(e,t,{x:l,y:a,ts:n}),u();else{const o=Sn(e,t);if(o>=0){hn(e,t,{x:l,y:a,ts:n}),u();const r=Un(e,o);let d=Xo.pointInBounds(i,Cn(e))&&Xo.pointInBounds(s._last_mouse_down,Cn(e));for(const t of r){const o=kn(e,t);if(Xo.pointInBounds(i,o)){d=!0;break}}if(d){const t=l-s._last_mouse_down.x,o=a-s._last_mouse_down.y;Mn(e,r,{x:t,y:o}),c(r)}}else hn(e,t,{ts:n}),u();s._last_mouse_down=i}s._last_mouse=i}else if(p===Lt){const i={x:o[1],y:o[2]},p=0;s._last_mouse_down=null;const g=Sn(e,t);if(g>=0){const o=Un(e,g);On(e,o,0),c(o);const s=xn(e,g),i=bn(e,g);let h=!1;if(un(e)===ee.REAL){for(const t of o)if(wn(e,t)){h=!0;break}}else h=!0;if(h&&Xo.pointDistance(i,s){const l=on[e].puzzle.info;if(o<0)return!1;if(((e,t,o)=>{const n=vn(e,t),l=vn(e,o);return!(!n||n!==l)})(e,t,o))return!1;const a=xn(e,t),s=Xo.pointAdd(xn(e,o),{x:n[0]*l.tileSize,y:n[1]*l.tileSize});if(Xo.pointDistance(a,s){const n=on[e].puzzle.tiles,l=vn(e,t),a=vn(e,o);let s;const i=[];l&&i.push(l),a&&i.push(a),l?s=l:a?s=a:(mn(e,{maxGroup:In(e)+1}),r(),s=In(e));if(yn(e,t,{group:s}),d(t),yn(e,o,{group:s}),d(o),i.length>0)for(const r of n){const t=se.decodePiece(r);i.includes(t.group)&&(yn(e,t.idx,{group:s}),d(t.idx))}})(e,t,o),l=Un(e,t),((e,t)=>-1===Vn(e,t))(e,o))Nn(e,l);else{const t=((e,t)=>{let o=0;for(const n of t){const t=An(e,n);t>o&&(o=t)}return o})(e,l);En(e,l,t)}return c(l),!0}return!1};let a=!1;for(const t of Un(e,g)){const n=Dn(e,t);if(o(e,t,n[0],[0,1])||o(e,t,n[1],[-1,0])||o(e,t,n[2],[0,-1])||o(e,t,n[3],[1,0])){a=!0;break}}if(a&&cn(e)===Q.ANY){const o=Bn(e,t)+1;hn(e,t,{d:p,ts:n,points:o}),u()}else hn(e,t,{d:p,ts:n}),u();a&&un(e)===ee.REAL&&gn(e)===dn(e)&&(mn(e,{finished:n}),r()),a&&l&&l(t)}}else hn(e,t,{d:p,ts:n}),u();s._last_mouse=i}else if(p===jt){const l=o[1],a=o[2];hn(e,t,{x:l,y:a,ts:n}),u(),s._last_mouse={x:l,y:a}}else if(p===Wt){const l=o[1],a=o[2];hn(e,t,{x:l,y:a,ts:n}),u(),s._last_mouse={x:l,y:a}}else hn(e,t,{ts:n}),u();return function(e,t,o){on[e].evtInfos[t]=o}(e,t,s),i}};let Ln=-10,Fn=20,jn=2,Wn=15;class Hn{constructor(e){this.radius=10,this.previousRadius=10,this.explodingDuration=100,this.hasExploded=!1,this.alive=!0,this.color=function(e){return"rgba("+e.random(0,255)+","+e.random(0,255)+","+e.random(0,255)+", 0.8)"}(e),this.px=window.innerWidth/4+Math.random()*window.innerWidth/2,this.py=window.innerHeight,this.vx=Ln+Math.random()*Fn,this.vy=-1*(jn+Math.random()*Wn),this.duration=0}update(e){if(this.hasExploded){const e=200-this.radius;this.previousRadius=this.radius,this.radius+=e/10,this.explodingDuration--,0==this.explodingDuration&&(this.alive=!1)}else this.vx+=0,this.vy+=1,this.vy>=0&&e&&this.explode(e),this.px+=this.vx,this.py+=this.vy}draw(e){e.beginPath(),e.arc(this.px,this.py,this.previousRadius,0,2*Math.PI,!1),this.hasExploded||(e.fillStyle=this.color,e.lineWidth=1,e.fill())}explode(e){this.hasExploded=!0;const t=3+Math.floor(3*Math.random());for(let o=0;o{this.resize()}))}setSpeedParams(){let e=0,t=0;for(;e=0;)t+=1,e+=t;jn=t/2,Wn=t-jn;const o=1/4*this.canvas.width/(t/2);Ln=-o,Fn=2*o}resize(){this.setSpeedParams()}init(){this.readyBombs=[],this.explodedBombs=[],this.particles=[];for(let e=0;e<1;e++)this.readyBombs.push(new Hn(this.rng))}update(){100*Math.random()<5&&this.readyBombs.push(new Hn(this.rng));const e=[];for(;this.explodedBombs.length>0;){const t=this.explodedBombs.shift();if(!t)break;t.update(),t.alive&&e.push(t)}this.explodedBombs=e;const t=[];for(;this.readyBombs.length>0;){const e=this.readyBombs.shift();if(!e)break;e.update(this.particles),e.hasExploded?this.explodedBombs.push(e):t.push(e)}this.readyBombs=t;const o=[];for(;this.particles.length>0;){const e=this.particles.shift();if(!e)break;e.update(),e.alive&&o.push(e)}this.particles=o}render(){this.ctx.beginPath(),this.ctx.fillStyle="rgba(0, 0, 0, 0.1)",this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height);for(let e=0;e{const t=e.color+" "+e.d;if(!y[t]){const o=e.d?r:d;if(e.color){const n=e.d?c:u;y[t]=await createImageBitmap(jo.colorizedCanvas(o,n,e.color))}else y[t]=o}return y[t]},v=function(e,t){return t.width=window.innerWidth,t.height=window.innerHeight,e.appendChild(t),window.addEventListener("resize",(()=>{t.width=window.innerWidth,t.height=window.innerHeight,el=!0})),t}(l,jo.createCanvas()),w={final:!1,log:[],logPointer:0,speeds:[.5,1,2,5,10,20,50,100,250,500],speedIdx:1,paused:!1,lastRealTs:0,lastGameTs:0,gameStartTs:0,skipNonActionPhases:!0,dataOffset:0};mo.onConnectionStateChange((e=>{a.setConnectionState(e)}));const b=async e=>{const t=w.dataOffset;w.dataOffset+=1e4;const o=await mo.requestReplayData(e,t);return w.log=w.log.slice(w.logPointer),w.logPointer=0,w.log.push(...o.log),0===o.log.length&&(w.final=!0),o};let x=()=>0;const C=async()=>{if("play"===n){const n=await mo.connect(o,e,t),l=se.decodeGame(n);$n.setGame(l.id,l),x=()=>N()}else{if("replay"!==n)throw"[ 2020-12-22 MODE invalid, must be play|replay ]";{const t=await b(e);if(!t.game)throw"[ 2021-05-29 no game received ]";const o=se.decodeGame(t.game);$n.setGame(o.id,o),w.lastRealTs=N(),w.gameStartTs=parseInt(t.log[0][4],10),w.lastGameTs=w.gameStartTs,x=()=>w.lastGameTs}}el=!0};await C();const k=$n.getPieceDrawOffset(e),A=$n.getPieceDrawSize(e),S=$n.getPuzzleWidth(e),P=$n.getPuzzleHeight(e),T=$n.getTableWidth(e),I=$n.getTableHeight(e),z={x:(T-S)/2,y:(I-P)/2},D={w:S,h:P},E={w:A,h:A},_=await tn.loadPuzzleBitmaps($n.getPuzzle(e)),V=new Yn(v,$n.getRng(e));V.init();const O=v.getContext("2d");v.classList.add("loaded");const U=Lo();U.move(-(T-v.width)/2,-(I-v.height)/2);const B=function(e,t,o,n){let l=[],a=!0,s=!1,i=!1,r=!1,d=!1,c=!1,u=!1,p=!1;const g=(e,t)=>{const n=o.viewportToWorld({x:e,y:t});return[n.x,n.y]},h=e=>g(e.offsetX,e.offsetY),m=()=>g(e.width/2,e.height/2),y=(e,t)=>{a&&("ShiftLeft"===t.code||"ShiftRight"===t.code?p=e:"ArrowUp"===t.code||"KeyW"===t.code?r=e:"ArrowDown"===t.code||"KeyS"===t.code?d=e:"ArrowLeft"===t.code||"KeyA"===t.code?s=e:"ArrowRight"===t.code||"KeyD"===t.code?i=e:"KeyQ"===t.code?u=e:"KeyE"===t.code&&(c=e))};let f=null;e.addEventListener("mousedown",(e=>{f=h(e),0===e.button&&v([$t,...f])})),e.addEventListener("mouseup",(e=>{f=h(e),0===e.button&&v([Lt,...f])})),e.addEventListener("mousemove",(e=>{f=h(e),v([Ft,...f])})),e.addEventListener("wheel",(e=>{if(f=h(e),o.canZoom(e.deltaY<0?"in":"out")){const t=e.deltaY<0?jt:Wt;v([t,...f])}})),t.addEventListener("keydown",(e=>y(!0,e))),t.addEventListener("keyup",(e=>y(!1,e))),t.addEventListener("keypress",(e=>{a&&("Space"===e.code&&v([qt]),"replay"===n&&("KeyI"===e.code&&v([Xt]),"KeyO"===e.code&&v([Jt]),"KeyP"===e.code&&v([Zt])),"KeyF"===e.code&&(Xn=!Xn,el=!0),"KeyG"===e.code&&(Jn=!Jn,el=!0),"KeyM"===e.code&&v([Qt]))}));const v=e=>{l.push(e)};return{addEvent:v,consumeAll:()=>{if(0===l.length)return[];const e=l.slice();return l=[],e},createKeyEvents:()=>{const e=(s?1:0)-(i?1:0),t=(r?1:0)-(d?1:0);if(0!==e||0!==t){const n=(p?24:12)*Math.sqrt(o.getCurrentZoom()),l=o.viewportDimToWorld({w:e*n,h:t*n});v([Gt,l.w,l.h]),f&&(f[0]-=l.w,f[1]-=l.h)}if(c&&u);else if(c){if(o.canZoom("in")){const e=f||m();v([jt,...e])}}else if(u&&o.canZoom("out")){const e=f||m();v([Wt,...e])}},setHotkeys:e=>{a=e}}}(v,window,U,n),R=$n.getImageUrl(e),G=()=>{const t=$n.getStartTs(e),o=$n.getFinishTs(e),n=x();a.setFinished(!!o),a.setDuration((o||n)-t)};G(),a.setPiecesDone($n.getFinishedPiecesCount(e)),a.setPiecesTotal($n.getPieceCount(e));const $=x();a.setActivePlayers($n.getActivePlayers(e,$)),a.setIdlePlayers($n.getIdlePlayers(e,$));const L=!!$n.getFinishTs(e);let F=L;const j=()=>F&&!L,W=()=>{const e=localStorage.getItem("sound_volume");if(null===e)return 100;const t=parseInt(e,10);return isNaN(t)?100:t},H=()=>{const e=localStorage.getItem("sound_enabled");return null!==e&&"1"===e},K=()=>{const e=W();i.volume=e/100,i.play()},Y=()=>"replay"===n?localStorage.getItem("bg_color")||"#222222":$n.getPlayerBgColor(e,t)||localStorage.getItem("bg_color")||"#222222",q=()=>"replay"===n?localStorage.getItem("player_color")||"#ffffff":$n.getPlayerColor(e,t)||localStorage.getItem("player_color")||"#ffffff";let Q="",Z="",X=!1;const J=e=>{X=e;const[t,o]=e?[Q,"grab"]:[Z,"default"];v.style.cursor=`url('${t}') ${g} ${m}, ${o}`},ee=e=>{Q=jo.colorizedCanvas(r,c,e).toDataURL(),Z=jo.colorizedCanvas(d,u,e).toDataURL(),J(X)};ee(q());const te=()=>{a.setReplaySpeed&&a.setReplaySpeed(w.speeds[w.speedIdx]),a.setReplayPaused&&a.setReplayPaused(w.paused)},oe=()=>{w.speedIdx+1{w.speedIdx>=1&&(w.speedIdx--,te())},le=()=>{w.paused=!w.paused,te()},ae=[];let ie;let re;if("play"===n?ae.push(setInterval((()=>{G()}),1e3)):"replay"===n&&te(),"play"===n)mo.onServerChange((o=>{o[0],o[1],o[2];const n=o[3];for(const[l,a]of n)switch(l){case oo:{const o=se.decodePlayer(a);o.id!==t&&($n.setPlayer(e,o.id,o),el=!0)}break;case to:{const t=se.decodePiece(a);$n.setPiece(e,t.idx,t),el=!0}break;case eo:$n.setPuzzleData(e,a),el=!0}F=!!$n.getFinishTs(e)}));else if("replay"===n){const t=(t,o)=>{const n=t;if(n[0]===Ut){const t=n[1];return $n.addPlayer(e,t,o),!0}if(n[0]===Bt){const t=$n.getPlayerIdByIndex(e,n[1]);if(!t)throw"[ 2021-05-17 player not found (update player) ]";return $n.addPlayer(e,t,o),!0}if(n[0]===Rt){const t=$n.getPlayerIdByIndex(e,n[1]);if(!t)throw"[ 2021-05-17 player not found (handle input) ]";const l=n[2];return $n.handleInput(e,t,l,o),!0}return!1};let o=w.lastGameTs;const n=async()=>{w.logPointer+1>=w.log.length&&await b(e);const l=N();if(w.paused)return w.lastRealTs=l,void(ie=setTimeout(n,50));const a=(l-w.lastRealTs)*w.speeds[w.speedIdx];let s=w.lastGameTs+a;for(;;){if(w.paused)break;const e=w.logPointer+1;if(e>=w.log.length)break;const n=w.log[w.logPointer],l=o+n[n.length-1],a=w.log[e],i=a[a.length-1],r=l+i;if(r>s){w.skipNonActionPhases&&s+500*M{let t=!1;const o=e.fps||60,n=e.slow||1,l=e.update,a=e.render,s=window.requestAnimationFrame,i=1/o,r=n*i;let d,c=0,u=window.performance.now();const p=()=>{for(d=window.performance.now(),c+=Math.min(1,(d-u)/1e3);c>r;)c-=r,l(i);a(c/n),u=d,t||s(p)};return s(p),{stop:()=>{t=!0}}})({update:()=>{B.createKeyEvents();for(const o of B.consumeAll())if("play"===n){const n=o[0];if(n===Gt){const e=o[1],t=o[2],n=U.worldDimToViewport({w:e,h:t});el=!0,U.move(n.w,n.h)}else if(n===Ft){if(de&&!$n.getFirstOwnedPiece(e,t)){const e={x:o[1],y:o[2]},t=U.worldToViewport(e),n=Math.round(t.x-de.x),l=Math.round(t.y-de.y);el=!0,U.move(n,l),de=t}}else if(n===Kt)ee(o[1]);else if(n===$t){const e={x:o[1],y:o[2]};de=U.worldToViewport(e),J(!0)}else if(n===Lt)de=null,J(!1);else if(n===jt){const e={x:o[1],y:o[2]};el=!0,U.zoom("in",U.worldToViewport(e))}else if(n===Wt){const e={x:o[1],y:o[2]};el=!0,U.zoom("out",U.worldToViewport(e))}else n===qt?a.togglePreview():n===Qt&&a.toggleSoundsEnabled();const l=x();$n.handleInput(e,t,o,l,(e=>{H()&&K()})).length>0&&(el=!0),mo.sendClientEvent(o)}else if("replay"===n){const e=o[0];if(e===Zt)le();else if(e===Jt)ne();else if(e===Xt)oe();else if(e===Gt){const e=o[1],t=o[2];el=!0,U.move(e,t)}else if(e===Ft){if(de){const e={x:o[1],y:o[2]},t=U.worldToViewport(e),n=Math.round(t.x-de.x),l=Math.round(t.y-de.y);el=!0,U.move(n,l),de=t}}else if(e===Kt)ee(o[1]);else if(e===$t){const e={x:o[1],y:o[2]};de=U.worldToViewport(e),J(!0)}else if(e===Lt)de=null,J(!1);else if(e===jt){const e={x:o[1],y:o[2]};el=!0,U.zoom("in",U.worldToViewport(e))}else if(e===Wt){const e={x:o[1],y:o[2]};el=!0,U.zoom("out",U.worldToViewport(e))}else e===qt&&a.togglePreview()}F=!!$n.getFinishTs(e),j()&&(V.update(),el=!0)},render:async()=>{if(!el)return;const o=x();let l,s,i;window.DEBUG&&Yo(0),O.fillStyle=Y(),O.fillRect(0,0,v.width,v.height),window.DEBUG&&qo("clear done"),l=U.worldToViewportRaw(z),s=U.worldDimToViewportRaw(D),O.fillStyle="rgba(255, 255, 255, .3)",O.fillRect(l.x,l.y,s.w,s.h),window.DEBUG&&qo("board done");const r=$n.getPiecesSortedByZIndex(e);window.DEBUG&&qo("get tiles done"),s=U.worldDimToViewportRaw(E);for(const e of r)(-1===e.owner?Xn:Jn)&&(i=_[e.idx],l=U.worldToViewportRaw({x:k+e.pos.x,y:k+e.pos.y}),O.drawImage(i,0,0,i.width,i.height,l.x,l.y,s.w,s.h));window.DEBUG&&qo("tiles done");const d=[];for(const a of $n.getActivePlayers(e,o))c=a,("replay"===n||c.id!==t)&&(i=await f(a),l=U.worldToViewport(a),O.drawImage(i,l.x-g,l.y-m),d.push([`${a.name} (${a.points})`,l.x,l.y+h]));var c;O.fillStyle="white",O.textAlign="center";for(const[e,t,n]of d)O.fillText(e,t,n);window.DEBUG&&qo("players done"),a.setActivePlayers($n.getActivePlayers(e,o)),a.setIdlePlayers($n.getIdlePlayers(e,o)),a.setPiecesDone($n.getFinishedPiecesCount(e)),window.DEBUG&&qo("HUD done"),j()&&V.render(),el=!1}}),{setHotkeys:e=>{B.setHotkeys(e)},onBgChange:e=>{localStorage.setItem("bg_color",e),B.addEvent([Ht,e])},onColorChange:e=>{localStorage.setItem("player_color",e),B.addEvent([Kt,e])},onNameChange:e=>{localStorage.setItem("player_name",e),B.addEvent([Yt,e])},onSoundsEnabledChange:e=>{localStorage.setItem("sound_enabled",e?"1":"0")},onSoundsVolumeChange:e=>{qn.info("vol changed",e),localStorage.setItem("sound_volume",`${e}`),K()},replayOnSpeedUp:oe,replayOnSpeedDown:ne,replayOnPauseToggle:le,replayOnSkipToggle:()=>{w.skipNonActionPhases=!w.skipNonActionPhases},previewImageUrl:R,player:{background:Y(),color:q(),name:"replay"===n?localStorage.getItem("player_name")||"#ffffff":$n.getPlayerName(e,t)||localStorage.getItem("player_name")||"anon",soundsEnabled:H(),soundsVolume:W()},disconnect:mo.disconnect,connect:C,unload:()=>{ae.forEach((e=>{clearInterval(e)})),ie&&clearTimeout(ie),re&&re.stop()}}}var ol=e({name:"game",components:{PuzzleStatus:bt,Scores:mt,SettingsOverlay:Ct,PreviewOverlay:Et,ConnectionOverlay:yo,HelpOverlay:Co},data:()=>({activePlayers:[],idlePlayers:[],finished:!1,duration:0,piecesDone:0,piecesTotal:0,overlay:"",connectionState:0,g:{player:{background:"",color:"",name:"",soundsEnabled:!1,soundsVolume:100},previewImageUrl:"",setHotkeys:e=>{},onBgChange:e=>{},onColorChange:e=>{},onNameChange:e=>{},onSoundsEnabledChange:e=>{},onSoundsVolumeChange:e=>{},connect:()=>{},disconnect:()=>{},unload:()=>{}}}),async mounted(){this.$route.params.id&&(this.$watch((()=>this.g.player.background),(e=>{this.g.onBgChange(e)})),this.$watch((()=>this.g.player.color),(e=>{this.g.onColorChange(e)})),this.$watch((()=>this.g.player.name),(e=>{this.g.onNameChange(e)})),this.$watch((()=>this.g.player.soundsEnabled),(e=>{this.g.onSoundsEnabledChange(e)})),this.$watch((()=>this.g.player.soundsVolume),(e=>{this.g.onSoundsVolumeChange(e)})),this.g=await tl(`${this.$route.params.id}`,this.$clientId,this.$config.WS_ADDRESS,"play",this.$el,{setActivePlayers:e=>{this.activePlayers=e},setIdlePlayers:e=>{this.idlePlayers=e},setFinished:e=>{this.finished=e},setDuration:e=>{this.duration=e},setPiecesDone:e=>{this.piecesDone=e},setPiecesTotal:e=>{this.piecesTotal=e},togglePreview:()=>{this.toggle("preview",!1)},setConnectionState:e=>{this.connectionState=e},toggleSoundsEnabled:()=>{this.g.player.soundsEnabled=!this.g.player.soundsEnabled}}))},unmounted(){this.g.unload(),this.g.disconnect()},methods:{reconnect(){this.g.connect()},toggle(e,t){""===this.overlay?(this.overlay=e,t&&this.g.setHotkeys(!1)):(this.overlay="",t&&this.g.setHotkeys(!0))}}});const nl={id:"game"},ll={class:"menu"},al={class:"tabs"},sl=i("🧩 Puzzles");ol.render=function(e,l,i,r,d,c){const u=a("settings-overlay"),g=a("preview-overlay"),h=a("help-overlay"),m=a("connection-overlay"),y=a("puzzle-status"),f=a("router-link"),v=a("scores");return s(),t("div",nl,[p(o(u,{onBgclick:l[1]||(l[1]=t=>e.toggle("settings",!0)),modelValue:e.g.player,"onUpdate:modelValue":l[2]||(l[2]=t=>e.g.player=t)},null,8,["modelValue"]),[[C,"settings"===e.overlay]]),p(o(g,{onBgclick:l[3]||(l[3]=t=>e.toggle("preview",!1)),img:e.g.previewImageUrl},null,8,["img"]),[[C,"preview"===e.overlay]]),p(o(h,{onBgclick:l[4]||(l[4]=t=>e.toggle("help",!0))},null,512),[[C,"help"===e.overlay]]),o(m,{connectionState:e.connectionState,onReconnect:e.reconnect},null,8,["connectionState","onReconnect"]),o(y,{finished:e.finished,duration:e.duration,piecesDone:e.piecesDone,piecesTotal:e.piecesTotal},null,8,["finished","duration","piecesDone","piecesTotal"]),o("div",ll,[o("div",al,[o(f,{class:"opener",to:{name:"index"},target:"_blank"},{default:n((()=>[sl])),_:1}),o("div",{class:"opener",onClick:l[5]||(l[5]=t=>e.toggle("preview",!1))},"πŸ–ΌοΈ Preview"),o("div",{class:"opener",onClick:l[6]||(l[6]=t=>e.toggle("settings",!0))},"πŸ› οΈ Settings"),o("div",{class:"opener",onClick:l[7]||(l[7]=t=>e.toggle("help",!0))},"ℹ️ Help")])]),o(v,{activePlayers:e.activePlayers,idlePlayers:e.idlePlayers},null,8,["activePlayers","idlePlayers"])])};var il=e({name:"replay",components:{PuzzleStatus:bt,Scores:mt,SettingsOverlay:Ct,PreviewOverlay:Et,HelpOverlay:Co},data:()=>({activePlayers:[],idlePlayers:[],finished:!1,duration:0,piecesDone:0,piecesTotal:0,skipNoAction:!0,overlay:"",connectionState:0,g:{player:{background:"",color:"",name:"",soundsEnabled:!1,soundsVolume:100},previewImageUrl:"",setHotkeys:e=>{},onBgChange:e=>{},onColorChange:e=>{},onNameChange:e=>{},onSoundsEnabledChange:e=>{},onSoundsVolumeChange:e=>{},replayOnSpeedUp:()=>{},replayOnSpeedDown:()=>{},replayOnPauseToggle:()=>{},replayOnSkipToggle:()=>{},connect:()=>{},disconnect:()=>{},unload:()=>{}},replay:{speed:1,paused:!1}}),async mounted(){this.$route.params.id&&(this.$watch((()=>this.g.player.background),(e=>{this.g.onBgChange(e)})),this.$watch((()=>this.g.player.color),(e=>{this.g.onColorChange(e)})),this.$watch((()=>this.g.player.name),(e=>{this.g.onNameChange(e)})),this.$watch((()=>this.g.player.soundsEnabled),(e=>{this.g.onSoundsEnabledChange(e)})),this.$watch((()=>this.g.player.soundsVolume),(e=>{this.g.onSoundsVolumeChange(e)})),this.g=await tl(`${this.$route.params.id}`,this.$clientId,this.$config.WS_ADDRESS,"replay",this.$el,{setActivePlayers:e=>{this.activePlayers=e},setIdlePlayers:e=>{this.idlePlayers=e},setFinished:e=>{this.finished=e},setDuration:e=>{this.duration=e},setPiecesDone:e=>{this.piecesDone=e},setPiecesTotal:e=>{this.piecesTotal=e},togglePreview:()=>{this.toggle("preview",!1)},setConnectionState:e=>{this.connectionState=e},setReplaySpeed:e=>{this.replay.speed=e},setReplayPaused:e=>{this.replay.paused=e},toggleSoundsEnabled:()=>{this.g.player.soundsEnabled=!this.g.player.soundsEnabled}}))},unmounted(){this.g.unload(),this.g.disconnect()},methods:{toggle(e,t){""===this.overlay?(this.overlay=e,t&&this.g.setHotkeys(!1)):(this.overlay="",t&&this.g.setHotkeys(!0))}},computed:{replayText(){return"Replay-Speed: "+this.replay.speed+"x"+(this.replay.paused?" Paused":"")}}});const rl={id:"replay"},dl=i("Skip no action phases: "),cl={class:"menu"},ul={class:"tabs"},pl=i("🧩 Puzzles");il.render=function(e,l,i,d,c,u){const g=a("settings-overlay"),h=a("preview-overlay"),m=a("help-overlay"),y=a("puzzle-status"),f=a("router-link"),v=a("scores");return s(),t("div",rl,[p(o(g,{onBgclick:l[1]||(l[1]=t=>e.toggle("settings",!0)),modelValue:e.g.player,"onUpdate:modelValue":l[2]||(l[2]=t=>e.g.player=t)},null,8,["modelValue"]),[[C,"settings"===e.overlay]]),p(o(h,{onBgclick:l[3]||(l[3]=t=>e.toggle("preview",!1)),img:e.g.previewImageUrl},null,8,["img"]),[[C,"preview"===e.overlay]]),p(o(m,{onBgclick:l[4]||(l[4]=t=>e.toggle("help",!0))},null,512),[[C,"help"===e.overlay]]),o(y,{finished:e.finished,duration:e.duration,piecesDone:e.piecesDone,piecesTotal:e.piecesTotal},{default:n((()=>[o("div",null,[o("div",null,r(e.replayText),1),o("div",null,[o("label",null,[dl,p(o("input",{type:"checkbox","onUpdate:modelValue":l[5]||(l[5]=t=>e.skipNoAction=t),onChange:l[6]||(l[6]=t=>e.g.replayOnSkipToggle())},null,544),[[x,e.skipNoAction]])])]),o("button",{class:"btn",onClick:l[7]||(l[7]=t=>e.g.replayOnSpeedUp())},"⏫"),o("button",{class:"btn",onClick:l[8]||(l[8]=t=>e.g.replayOnSpeedDown())},"⏬"),o("button",{class:"btn",onClick:l[9]||(l[9]=t=>e.g.replayOnPauseToggle())},"⏸️")])])),_:1},8,["finished","duration","piecesDone","piecesTotal"]),o("div",cl,[o("div",ul,[o(f,{class:"opener",to:{name:"index"},target:"_blank"},{default:n((()=>[pl])),_:1}),o("div",{class:"opener",onClick:l[10]||(l[10]=t=>e.toggle("preview",!1))},"πŸ–ΌοΈ Preview"),o("div",{class:"opener",onClick:l[11]||(l[11]=t=>e.toggle("settings",!0))},"πŸ› οΈ Settings"),o("div",{class:"opener",onClick:l[12]||(l[12]=t=>e.toggle("help",!0))},"ℹ️ Help")])]),o(v,{activePlayers:e.activePlayers,idlePlayers:e.idlePlayers},null,8,["activePlayers","idlePlayers"])])},(async()=>{const e=await fetch("/api/conf"),t=await e.json();const o=k({history:A(),routes:[{name:"index",path:"/",component:j},{name:"new-game",path:"/new-game",component:at},{name:"game",path:"/g/:id",component:ol},{name:"replay",path:"/replay/:id",component:il}]});o.beforeEach(((e,t)=>{t.name&&document.documentElement.classList.remove(`view-${String(t.name)}`),document.documentElement.classList.add(`view-${String(e.name)}`)}));const n=S(P);n.config.globalProperties.$config=t,n.config.globalProperties.$clientId=function(){let e=localStorage.getItem("ID");return e||(e=se.uniqId(),localStorage.setItem("ID",e)),e}(),n.use(o),n.mount("#app")})(); diff --git a/build/public/assets/index.5c03949d.css b/build/public/assets/index.f00a590d.css similarity index 95% rename from build/public/assets/index.5c03949d.css rename to build/public/assets/index.f00a590d.css index 20f51c8..58934f0 100644 --- a/build/public/assets/index.5c03949d.css +++ b/build/public/assets/index.f00a590d.css @@ -1 +1 @@ -:root{--main-color:#c1b19f;--main-darker-color:#4f4e4c;--link-color:#808db0;--link-hover-color:#c5cfeb;--highlight-color:#dd7e7e;--positive-color:#64a756;--input-bg-color:#262523;--bg-color:rgba(0,0,0,.7)}body,html{margin:0;background:#2b2b2b;color:var(--main-color);height:100%}*{font-family:monospace;font-size:15px}h1,h2,h3,h4{font-size:20px}a{color:var(--link-color);text-decoration:none}a:hover{color:var(--link-hover-color)}td,th{vertical-align:top}.btn{display:inline-block;background:var(--input-bg-color);color:var(--link-color);border:solid 1px #000;padding:5px 10px;box-shadow:1px 1px 2px rgba(0,0,0,.5),0 0 1px rgba(150,150,150,.4) inset;border-radius:4px;user-select:none}.btn-big{font-size:1.5em;padding:10px 20px}.btn:hover{background:#2f2e2c;color:var(--link-hover-color);border:solid 1px #111;box-shadow:0 0 1px rgba(150,150,150,.4) inset;cursor:pointer}.btn:disabled{background:#2f2e2c;color:#8c4747!important;border:solid 1px #111;box-shadow:0 0 1px rgba(150,150,150,.4) inset;cursor:not-allowed}input{background:#333230;border-radius:4px;color:var(--main-color);padding:6px 10px;border:solid 1px #000;box-shadow:0 0 3px rgba(0,0,0,.3) inset}input:focus{border:solid 1px #686767;background:var(--input-bg-color)}.scores{position:absolute;right:0;top:0;background:var(--bg-color);padding:5px;border:solid 1px #000;box-shadow:0 0 10px 0 rgba(0,0,0,.7)}.timer{position:absolute;left:0;top:0;background:var(--bg-color);padding:5px;border:solid 1px #000;box-shadow:0 0 10px 0 rgba(0,0,0,.7)}.menu{position:absolute;top:0;left:50%;transform:translateX(-50%);background:var(--bg-color);padding:5px;border:solid 1px #000;box-shadow:0 0 10px 0 rgba(0,0,0,.7);z-index:2}.closed{display:none}.overlay{position:fixed;top:0;left:0;right:0;bottom:0;z-index:10;background:var(--bg-color)}.overlay.transparent{background:0 0}.overlay-content{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);background:var(--bg-color);padding:5px;border:solid 1px #000;box-shadow:0 0 10px 0 rgba(0,0,0,.7);z-index:1}.connection-lost .overlay-content{padding:20px;text-align:center}.preview{position:absolute;top:20px;left:20px;bottom:20px;right:20px}.preview .img{height:100%;width:100%;position:absolute;background-repeat:no-repeat;background-position:center;background-size:contain}.menu .opener{display:inline-block;margin-right:10px;color:var(--link-color)}.menu .opener:last-child{margin-right:0}.menu .opener:hover{color:var(--link-hover-color);cursor:pointer}kbd{background-color:#eee;border-radius:3px;border:1px solid #b4b4b4;box-shadow:0 1px 1px rgba(0,0,0,.2),0 2px 0 0 rgba(255,255,255,.7) inset;color:#333;display:inline-block;font-size:.85em;font-weight:700;line-height:1;padding:2px 4px;white-space:nowrap}.hint{color:var(--main-darker-color)}.bit{background:#3b3737;border-radius:.5em;padding:.25em .5em;display:inline-block;margin:0 .25em .25em 0;cursor:pointer}.bit.on{color:var(--positive-color)}.upload-image-teaser{text-align:center}.upload-image-teaser .btn{margin-bottom:.5em}table label{line-height:32px}.nav{list-style:none;padding:0}.nav li{display:inline-block;margin-right:1em}.image-list{overflow:scroll}.image-list-inner{white-space:nowrap}.imageteaser{width:150px;height:100px;display:inline-block;margin:5px;background-size:contain;background-position:center;background-repeat:no-repeat;background-color:#222;cursor:pointer}.game-teaser-wrap{display:inline-block;width:20%;padding:5px;box-sizing:border-box}.game-teaser{display:block;background-repeat:no-repeat;background-position:center;background-size:contain;position:relative;padding-top:56.25%;width:100%;background-color:#222}.game-info{position:absolute;top:0;left:0;right:0;bottom:0;width:100%;height:100%}.game-info-text{position:absolute;top:0;background:var(--bg-color);padding:5px}.game-replay{position:absolute;top:0;right:0}html.view-game{overflow:hidden}html.view-game body{overflow:hidden}html.view-replay{overflow:hidden}html.view-replay body{overflow:hidden}.imageteaser{position:relative}.imageteaser .edit{display:none;position:absolute}.imageteaser:hover .edit{display:inline-block}.input[data-v-a4fa5e7e]{margin-bottom:.5em}.autocomplete[data-v-a4fa5e7e]{position:relative}.autocomplete ul[data-v-a4fa5e7e]{list-style:none;padding:0;margin:0;position:absolute;left:0;right:0;background:#333230;top:-.5em}.autocomplete ul li[data-v-a4fa5e7e]{position:relative;padding:.5em .5em .5em 1.5em;cursor:pointer}.autocomplete ul li.active[data-v-a4fa5e7e]{color:var(--link-hover-color);background:var(--input-bg-color)}.autocomplete ul li.active[data-v-a4fa5e7e]:before{content:'β–Ά';display:block;position:absolute;left:.5em}.new-image-dialog .overlay-content{display:grid;grid-template-columns:auto 450px;grid-template-rows:auto;grid-template-areas:"image settings" "image buttons";height:90%;width:80%}@media (max-width:1400px){.new-image-dialog .overlay-content{grid-template-columns:auto;grid-template-rows:1fr min-content min-content;grid-template-areas:"image" "settings" "buttons"}.new-image-dialog .overlay-content .area-buttons .btn br{display:none}}.new-image-dialog .area-image{grid-area:image;margin:.5em;border:solid 6px transparent}.new-image-dialog .area-image.no-image{align-content:center;display:grid;text-align:center;border:solid 6px;position:relative}.new-image-dialog .area-image.droppable{border:dashed 6px}.new-image-dialog .area-image .has-image{position:relative;width:100%;height:100%}.new-image-dialog .area-image .has-image .remove{position:absolute;top:.5em;left:.5em}.new-image-dialog .area-settings{grid-area:settings}.new-image-dialog .area-settings table input[type=text]{width:100%;box-sizing:border-box}.new-image-dialog .area-buttons{align-self:end;grid-area:buttons}.new-image-dialog .area-buttons button{width:100%;margin-top:.5em}.new-image-dialog .upload{position:absolute;top:0;left:0;right:0;bottom:0;cursor:pointer}.new-image-dialog .upload .btn{position:absolute;top:50%;transform:translate(-50%,-50%)}.area-image .drop-target{display:none}.area-image.droppable .drop-target{pointer-events:none;position:absolute;top:0;left:0;right:0;bottom:0;z-index:3}.edit-image-dialog .overlay-content{display:grid;grid-template-columns:auto 450px;grid-template-rows:auto;grid-template-areas:"image settings" "image buttons";height:90%;width:80%}.edit-image-dialog .area-image{grid-area:image;margin:20px}.edit-image-dialog .area-image.no-image{align-content:center;display:grid;text-align:center;border:dashed 6px;position:relative}.edit-image-dialog .area-image .has-image{position:relative;width:100%;height:100%}.edit-image-dialog .area-image .has-image .remove{position:absolute;top:.5em;left:.5em}.edit-image-dialog .area-settings{grid-area:settings}.edit-image-dialog .area-settings table input[type=text]{width:100%;box-sizing:border-box}.edit-image-dialog .area-buttons{align-self:end;grid-area:buttons}.edit-image-dialog .area-buttons button{width:100%;margin-top:.5em}.new-game-dialog .overlay-content{display:grid;grid-template-columns:auto 450px;grid-template-rows:auto;grid-template-areas:"image settings" "image buttons";height:90%;width:80%}.new-game-dialog .area-image{grid-area:image;display:grid;grid-template-rows:1fr min-content;grid-template-areas:"image" "image-title";margin-right:1em}@media (max-width:1400px){.new-game-dialog .overlay-content{grid-template-columns:auto;grid-template-rows:1fr min-content min-content;grid-template-areas:"image" "settings" "buttons"}.new-game-dialog .area-image{margin-right:0}}.new-game-dialog .area-settings{grid-area:settings}.new-game-dialog .area-settings table input[type=text]{width:100%;box-sizing:border-box}.new-game-dialog .area-buttons{align-self:end;grid-area:buttons}.new-game-dialog .area-buttons button{width:100%}.new-game-dialog .has-image{box-sizing:border-box;grid-area:image;position:relative;width:100%;height:100%;border:solid 1px}.new-game-dialog .image-title{grid-area:image-title;text-align:center;padding:.5em 0;background:var(--main-color);color:#262523}.new-game-dialog .has-image .remove{position:absolute;top:.5em;left:.5em}.sound-volume span[data-v-a1d1c822]{cursor:pointer;user-select:none}.sound-volume input[data-v-a1d1c822]{vertical-align:middle} \ No newline at end of file +:root{--main-color:#c1b19f;--main-darker-color:#4f4e4c;--link-color:#808db0;--link-hover-color:#c5cfeb;--highlight-color:#dd7e7e;--positive-color:#64a756;--input-bg-color:#262523;--bg-color:rgba(0,0,0,.7)}body,html{margin:0;background:#2b2b2b;color:var(--main-color);height:100%}*{font-family:monospace;font-size:15px}h1,h2,h3,h4{font-size:20px}a{color:var(--link-color);text-decoration:none}a:hover{color:var(--link-hover-color)}td,th{vertical-align:top}.btn{display:inline-block;background:var(--input-bg-color);color:var(--link-color);border:solid 1px #000;padding:5px 10px;box-shadow:1px 1px 2px rgba(0,0,0,.5),0 0 1px rgba(150,150,150,.4) inset;border-radius:4px;user-select:none}.btn-big{font-size:1.5em;padding:10px 20px}.btn:hover{background:#2f2e2c;color:var(--link-hover-color);border:solid 1px #111;box-shadow:0 0 1px rgba(150,150,150,.4) inset;cursor:pointer}.btn:disabled{background:#2f2e2c;color:#8c4747!important;border:solid 1px #111;box-shadow:0 0 1px rgba(150,150,150,.4) inset;cursor:not-allowed}input{background:#333230;border-radius:4px;color:var(--main-color);padding:6px 10px;border:solid 1px #000;box-shadow:0 0 3px rgba(0,0,0,.3) inset}input:focus{border:solid 1px #686767;background:var(--input-bg-color)}.scores{position:absolute;right:0;top:0;background:var(--bg-color);padding:5px;border:solid 1px #000;box-shadow:0 0 10px 0 rgba(0,0,0,.7)}.timer{position:absolute;left:0;top:0;background:var(--bg-color);padding:5px;border:solid 1px #000;box-shadow:0 0 10px 0 rgba(0,0,0,.7)}.menu{position:absolute;top:0;left:50%;transform:translateX(-50%);background:var(--bg-color);padding:5px;border:solid 1px #000;box-shadow:0 0 10px 0 rgba(0,0,0,.7);z-index:2}.closed{display:none}.overlay{position:fixed;top:0;left:0;right:0;bottom:0;z-index:10;background:var(--bg-color)}.overlay.transparent{background:0 0}.overlay-content{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);background:var(--bg-color);padding:5px;border:solid 1px #000;box-shadow:0 0 10px 0 rgba(0,0,0,.7);z-index:1}.connection-lost .overlay-content{padding:20px;text-align:center}.preview{position:absolute;top:20px;left:20px;bottom:20px;right:20px}.preview .img{height:100%;width:100%;position:absolute;background-repeat:no-repeat;background-position:center;background-size:contain}.menu .opener{display:inline-block;margin-right:10px;color:var(--link-color)}.menu .opener:last-child{margin-right:0}.menu .opener:hover{color:var(--link-hover-color);cursor:pointer}kbd{background-color:#eee;border-radius:3px;border:1px solid #b4b4b4;box-shadow:0 1px 1px rgba(0,0,0,.2),0 2px 0 0 rgba(255,255,255,.7) inset;color:#333;display:inline-block;font-size:.85em;font-weight:700;line-height:1;padding:2px 4px;white-space:nowrap}.hint{color:var(--main-darker-color)}.bit{background:#3b3737;border-radius:.5em;padding:.25em .5em;display:inline-block;margin:0 .25em .25em 0;cursor:pointer}.bit.on{color:var(--positive-color)}.upload-image-teaser{text-align:center}.upload-image-teaser .btn{margin-bottom:.5em}table label{line-height:32px}.nav{list-style:none;padding:0}.nav li{display:inline-block;margin-right:1em}.image-list{overflow:scroll}.image-list-inner{white-space:nowrap}.imageteaser{width:150px;height:100px;display:inline-block;margin:5px;background-size:contain;background-position:center;background-repeat:no-repeat;background-color:#222;cursor:pointer}.game-teaser-wrap{display:inline-block;width:20%;padding:5px;box-sizing:border-box}.game-teaser{display:block;background-repeat:no-repeat;background-position:center;background-size:contain;position:relative;padding-top:56.25%;width:100%;background-color:#222}.game-info{position:absolute;top:0;left:0;right:0;bottom:0;width:100%;height:100%}.game-info-text{position:absolute;top:0;background:var(--bg-color);padding:5px}.game-replay{position:absolute;top:0;right:0}html.view-game{overflow:hidden}html.view-game body{overflow:hidden}html.view-replay{overflow:hidden}html.view-replay body{overflow:hidden}.imageteaser{position:relative}.imageteaser .edit{display:none;position:absolute}.imageteaser:hover .edit{display:inline-block}.input[data-v-a4fa5e7e]{margin-bottom:.5em}.autocomplete[data-v-a4fa5e7e]{position:relative}.autocomplete ul[data-v-a4fa5e7e]{list-style:none;padding:0;margin:0;position:absolute;left:0;right:0;background:#333230;top:-.5em}.autocomplete ul li[data-v-a4fa5e7e]{position:relative;padding:.5em .5em .5em 1.5em;cursor:pointer}.autocomplete ul li.active[data-v-a4fa5e7e]{color:var(--link-hover-color);background:var(--input-bg-color)}.autocomplete ul li.active[data-v-a4fa5e7e]:before{content:'β–Ά';display:block;position:absolute;left:.5em}.new-image-dialog .overlay-content{display:grid;grid-template-columns:auto 450px;grid-template-rows:auto;grid-template-areas:"image settings" "image buttons";height:90%;width:80%}@media (max-width:1400px){.new-image-dialog .overlay-content{grid-template-columns:auto;grid-template-rows:1fr min-content min-content;grid-template-areas:"image" "settings" "buttons"}.new-image-dialog .overlay-content .area-buttons .btn br{display:none}}.new-image-dialog .area-image{grid-area:image;margin:.5em;border:solid 6px transparent}.new-image-dialog .area-image.no-image{align-content:center;display:grid;text-align:center;border:solid 6px;position:relative}.new-image-dialog .area-image.droppable{border:dashed 6px}.new-image-dialog .area-image .has-image{position:relative;width:100%;height:100%}.new-image-dialog .area-image .has-image .remove{position:absolute;top:.5em;left:.5em}.new-image-dialog .area-settings{grid-area:settings}.new-image-dialog .area-settings table input[type=text]{width:100%;box-sizing:border-box}.new-image-dialog .area-buttons{align-self:end;grid-area:buttons}.new-image-dialog .area-buttons button{width:100%;margin-top:.5em}.new-image-dialog .upload{position:absolute;top:0;left:0;right:0;bottom:0;cursor:pointer}.new-image-dialog .upload .btn{position:absolute;top:50%;transform:translate(-50%,-50%)}.area-image .drop-target{display:none}.area-image.droppable .drop-target{pointer-events:none;position:absolute;top:0;left:0;right:0;bottom:0;z-index:3}.edit-image-dialog .overlay-content{display:grid;grid-template-columns:auto 450px;grid-template-rows:auto;grid-template-areas:"image settings" "image buttons";height:90%;width:80%}.edit-image-dialog .area-image{grid-area:image;margin:20px}.edit-image-dialog .area-image.no-image{align-content:center;display:grid;text-align:center;border:dashed 6px;position:relative}.edit-image-dialog .area-image .has-image{position:relative;width:100%;height:100%}.edit-image-dialog .area-image .has-image .remove{position:absolute;top:.5em;left:.5em}.edit-image-dialog .area-settings{grid-area:settings}.edit-image-dialog .area-settings table input[type=text]{width:100%;box-sizing:border-box}.edit-image-dialog .area-buttons{align-self:end;grid-area:buttons}.edit-image-dialog .area-buttons button{width:100%;margin-top:.5em}.new-game-dialog .overlay-content{display:grid;grid-template-columns:auto 450px;grid-template-rows:auto;grid-template-areas:"image settings" "image buttons";height:90%;width:80%}.new-game-dialog .area-image{grid-area:image;display:grid;grid-template-rows:1fr min-content;grid-template-areas:"image" "image-title";margin-right:1em}@media (max-width:1400px){.new-game-dialog .overlay-content{grid-template-columns:auto;grid-template-rows:1fr min-content min-content;grid-template-areas:"image" "settings" "buttons"}.new-game-dialog .area-image{margin-right:0}}.new-game-dialog .area-settings{grid-area:settings}.new-game-dialog .area-settings table input[type=text]{width:100%;box-sizing:border-box}.new-game-dialog .area-buttons{align-self:end;grid-area:buttons}.new-game-dialog .area-buttons button{width:100%}.new-game-dialog .has-image{box-sizing:border-box;grid-area:image;position:relative;width:100%;height:100%;border:solid 1px}.new-game-dialog .image-title{grid-area:image-title;text-align:center;padding:.5em 0;background:var(--main-color);color:#262523}.new-game-dialog .has-image .remove{position:absolute;top:.5em;left:.5em}.new-game-dialog .image-title>span{margin-right:.5em}.new-game-dialog .image-title>span:last-child{margin-right:0}.image-title-dim{display:inline-block;white-space:no-wrap}.sound-volume span[data-v-a1d1c822]{cursor:pointer;user-select:none}.sound-volume input[data-v-a1d1c822]{vertical-align:middle} \ No newline at end of file diff --git a/build/public/index.html b/build/public/index.html index 15064ca..46e0e01 100644 --- a/build/public/index.html +++ b/build/public/index.html @@ -4,9 +4,9 @@ 🧩 jigsaw.hyottoko.club - + - +
diff --git a/build/server/main.js b/build/server/main.js index 971e133..7ac8c7c 100644 --- a/build/server/main.js +++ b/build/server/main.js @@ -1410,6 +1410,8 @@ const imageFromDb = (db, imageId) => { title: i.title, tags: getTags(db, i.id), created: i.created * 1000, + width: i.width, + height: i.height, }; }; const allImagesFromDb = (db, tagSlugs, orderBy) => { @@ -1447,8 +1449,13 @@ inner join images i on i.id = ixc.image_id ${where.sql}; title: i.title, tags: getTags(db, i.id), created: i.created * 1000, + width: i.width, + height: i.height, })); }; +/** + * @deprecated old function, now database is used + */ const allImagesFromDisk = (tags, sort) => { let images = fs.readdirSync(UPLOAD_DIR) .filter(f => f.toLowerCase().match(/\.(jpe?g|webp|png)$/)) @@ -1460,6 +1467,8 @@ const allImagesFromDisk = (tags, sort) => { title: f.replace(/\.[a-z]+$/, ''), tags: [], created: fs.statSync(`${UPLOAD_DIR}/${f}`).mtime.getTime(), + width: 0, + height: 0, // may have to fill when the function is used again })); switch (sort) { case 'alpha_asc': diff --git a/src/frontend/components/NewGameDialog.vue b/src/frontend/components/NewGameDialog.vue index 0042ade..e8862bc 100644 --- a/src/frontend/components/NewGameDialog.vue +++ b/src/frontend/components/NewGameDialog.vue @@ -6,7 +6,10 @@
-
"{{image.title}}"
+
+ "{{image.title}}" + ({{image.width}} βœ• {{image.height}}) +
@@ -18,27 +21,34 @@ - +
- + - +
- +
- + - +
- + @@ -192,4 +202,8 @@ export default defineComponent({ top: .5em; left: .5em; } + +.new-game-dialog .image-title > span { margin-right: .5em; } +.new-game-dialog .image-title > span:last-child { margin-right: 0; } +.image-title-dim { display: inline-block; white-space: no-wrap; } diff --git a/src/server/Images.ts b/src/server/Images.ts index 90889bc..8cda19a 100644 --- a/src/server/Images.ts +++ b/src/server/Images.ts @@ -26,7 +26,9 @@ interface ImageInfo url: string title: string tags: Tag[] - created: Timestamp, + created: Timestamp + width: number + height: number } const resizeImage = async (filename: string): Promise => { @@ -109,6 +111,8 @@ const imageFromDb = (db: Db, imageId: number): ImageInfo => { title: i.title, tags: getTags(db, i.id), created: i.created * 1000, + width: i.width, + height: i.height, } } @@ -153,9 +157,14 @@ inner join images i on i.id = ixc.image_id ${where.sql}; title: i.title, tags: getTags(db, i.id), created: i.created * 1000, + width: i.width, + height: i.height, })) } +/** + * @deprecated old function, now database is used + */ const allImagesFromDisk = ( tags: string[], sort: string @@ -170,6 +179,8 @@ const allImagesFromDisk = ( title: f.replace(/\.[a-z]+$/, ''), tags: [] as Tag[], created: fs.statSync(`${UPLOAD_DIR}/${f}`).mtime.getTime(), + width: 0, // may have to fill when the function is used again + height: 0, // may have to fill when the function is used again })) switch (sort) {