=0;t--)l[t].lifeSpan<0&&l.splice(t,1);0==l.length&&n.clearRect(0,0,s,c)}}(),i=requestAnimationFrame(A)}function m(){e.remove(),cancelAnimationFrame(i),r.removeEventListener("mousemove",g),r.removeEventListener("touchmove",f),r.removeEventListener("touchstart",f),window.addEventListener("resize",p)}function w(t,e,n){const i=Math.floor(60*Math.random()+60);this.initialLifeSpan=i,this.lifeSpan=i,this.velocity={x:(Math.random()<.5?-1:1)*(Math.random()/10),y:-1*Math.random()-.4},this.position={x:t,y:e},this.canv=n,this.baseDimension=4,this.update=function(t){this.position.x+=this.velocity.x,this.position.y+=this.velocity.y,this.velocity.x+=2*(Math.random()<.5?-1:1)/75,this.velocity.y-=Math.random()/600,this.lifeSpan--;const e=.2+(this.initialLifeSpan-this.lifeSpan)/this.initialLifeSpan;t.fillStyle="#e6f1f7",t.strokeStyle="#3a92c5",t.beginPath(),t.arc(this.position.x-this.baseDimension/2*e,this.position.y-this.baseDimension/2,this.baseDimension*e,0,2*Math.PI),t.stroke(),t.fill(),t.closePath()}}return u.onchange=()=>{u.matches?m():d()},d(),{destroy:m}},e.clockCursor=function(t){let e,n,i,o=t&&t.element,r=o||document.body,s=window.innerWidth,c=window.innerHeight,a={x:s/2,y:s/2};const l=t&&t.dateColor||"blue",h=t&&t.faceColor||"black",u=t&&t.secondsColor||"red",d=t&&t.minutesColor||"black",p=t&&t.hoursColor||"black",f=.4;let g=new Date,y=g.getDate(),A=g.getYear()+1900;const m=(" "+["SUNDAY","MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY"][g.getDay()]+" "+y+" "+["JANUARY","FEBRUARY","MARCH","APRIL","MAY","JUNE","JULY","AUGUST","SEPTEMBER","OCTOBER","NOVEMBER","DECEMBER"][g.getMonth()]+" "+A).split(""),w=["3","4","5","6","7","8","9","10","11","12","1","2"],v=w.length,_=["•","•","•"],x=["•","•","•","•"],b=["•","•","•","•","•"],E=360/v,C=360/m.length,M=45/6.5,O=[],j=[],L=[],k=[],B=[],R=[],S=[],D=[],T=[];var P=parseInt(m.length+v+_.length+x.length+b.length)+1;const I=window.matchMedia("(prefers-reduced-motion: reduce)");function W(){if(I.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init"),!1;e=document.createElement("canvas"),n=e.getContext("2d"),e.style.top="0px",e.style.left="0px",e.style.pointerEvents="none",o?(e.style.position="absolute",r.appendChild(e),e.width=r.clientWidth,e.height=r.clientHeight):(e.style.position="fixed",document.body.appendChild(e),e.width=s,e.height=c),n.font="10px sans-serif",n.textAlign="center",n.textBaseline="middle";for(let t=0;t0)if(o){const e=r.getBoundingClientRect();a.x=t.touches[0].clientX-e.left,a.y=t.touches[0].clientY-e.top}else a.x=t.touches[0].clientX,a.y=t.touches[0].clientY}function F(t){if(o){const e=r.getBoundingClientRect();a.x=t.clientX-e.left,a.y=t.clientY-e.top}else a.x=t.clientX,a.y=t.clientY}function X(){!function(){L[0]=Math.round(O[0]+=(a.y-O[0])*f),k[0]=Math.round(j[0]+=(a.x-j[0])*f);for(let t=1;t
=c-80&&(O[t-1]=c-80),j[t-1]>=s-80&&(j[t-1]=s-80)}(),function(){n.clearRect(0,0,s,c);const t=new Date,e=t.getSeconds(),i=Math.PI*(e-15)/30,o=t.getMinutes(),r=Math.PI*(o-15)/30,a=t.getHours(),l=Math.PI*(a-3)/6+Math.PI*parseInt(t.getMinutes())/360;for(let t=0;t{I.matches?U():W()},W(),{destroy:U}},e.emojiCursor=function(t){const e=t&&t.emoji||["😀","😂","😆","😊"];let n=t&&t.element,i=n||document.body,o=window.innerWidth,r=window.innerHeight;const s={x:o/2,y:o/2},c={x:o/2,y:o/2};let a=0;const l=[],h=[];let u,d,p;const f=window.matchMedia("(prefers-reduced-motion: reduce)");function g(){if(f.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init"),!1;u=document.createElement("canvas"),d=u.getContext("2d"),u.style.top="0px",u.style.left="0px",u.style.pointerEvents="none",n?(u.style.position="absolute",i.appendChild(u),u.width=i.clientWidth,u.height=i.clientHeight):(u.style.position="fixed",document.body.appendChild(u),u.width=o,u.height=r),d.font="21px serif",d.textBaseline="middle",d.textAlign="center",e.forEach((t=>{let e=d.measureText(t),n=document.createElement("canvas"),i=n.getContext("2d");n.width=e.width,n.height=2*e.actualBoundingBoxAscent,i.textAlign="center",i.font="21px serif",i.textBaseline="middle",i.fillText(t,n.width/2,e.actualBoundingBoxAscent),h.push(n)})),i.addEventListener("mousemove",m,{passive:!0}),i.addEventListener("touchmove",A,{passive:!0}),i.addEventListener("touchstart",A,{passive:!0}),window.addEventListener("resize",y),v()}function y(t){o=window.innerWidth,r=window.innerHeight,n?(u.width=i.clientWidth,u.height=i.clientHeight):(u.width=o,u.height=r)}function A(t){if(t.touches.length>0)for(let e=0;e{if(n){const e=i.getBoundingClientRect();s.x=t.clientX-e.left,s.y=t.clientY-e.top}else s.x=t.clientX,s.y=t.clientY;Math.hypot(s.x-c.x,s.y-c.y)>1&&(w(s.x,s.y,h[Math.floor(Math.random()*e.length)]),c.x=s.x,c.y=s.y,a=t.timeStamp)}))}function w(t,e,n){l.push(new x(t,e,n))}function v(){!function(){if(0!=l.length){d.clearRect(0,0,o,r);for(let t=0;t=0;t--)l[t].lifeSpan<0&&l.splice(t,1);0==l.length&&d.clearRect(0,0,o,r)}}(),p=requestAnimationFrame(v)}function _(){u.remove(),cancelAnimationFrame(p),i.removeEventListener("mousemove",m),i.removeEventListener("touchmove",A),i.removeEventListener("touchstart",A),window.addEventListener("resize",y)}function x(t,e,n){const i=Math.floor(60*Math.random()+80);this.initialLifeSpan=i,this.lifeSpan=i,this.velocity={x:(Math.random()<.5?-1:1)*(Math.random()/2),y:.4*Math.random()+.8},this.position={x:t,y:e},this.canv=n,this.update=function(t){this.position.x+=this.velocity.x,this.position.y+=this.velocity.y,this.lifeSpan--,this.velocity.y+=.05;const e=Math.max(this.lifeSpan/this.initialLifeSpan,0);t.drawImage(this.canv,this.position.x-this.canv.width/2*e,this.position.y-this.canv.height/2,this.canv.width*e,this.canv.height*e)}}return f.onchange=()=>{f.matches?_():g()},g(),{destroy:_}},e.fairyDustCursor=function(t){let e=t&&t.colors||["#D61C59","#E7D84B","#1B8798"],n=t&&t.element,i=n||document.body,o=window.innerWidth,r=window.innerHeight;const s={x:o/2,y:o/2},c={x:o/2,y:o/2},a=[],l=[];let h,u,d;const p=window.matchMedia("(prefers-reduced-motion: reduce)");function f(){if(p.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init"),!1;h=document.createElement("canvas"),u=h.getContext("2d"),h.style.top="0px",h.style.left="0px",h.style.pointerEvents="none",n?(h.style.position="absolute",i.appendChild(h),h.width=i.clientWidth,h.height=i.clientHeight):(h.style.position="fixed",i.appendChild(h),h.width=o,h.height=r),u.font="21px serif",u.textBaseline="middle",u.textAlign="center",e.forEach((t=>{let e=u.measureText("*"),n=document.createElement("canvas"),i=n.getContext("2d");n.width=e.width,n.height=e.actualBoundingBoxAscent+e.actualBoundingBoxDescent,i.fillStyle=t,i.textAlign="center",i.font="21px serif",i.textBaseline="middle",i.fillText("*",n.width/2,e.actualBoundingBoxAscent),l.push(n)})),i.addEventListener("mousemove",A),i.addEventListener("touchmove",y,{passive:!0}),i.addEventListener("touchstart",y,{passive:!0}),window.addEventListener("resize",g),w()}function g(t){o=window.innerWidth,r=window.innerHeight,n?(h.width=i.clientWidth,h.height=i.clientHeight):(h.width=o,h.height=r)}function y(t){if(t.touches.length>0)for(let e=0;e{if(n){const e=i.getBoundingClientRect();s.x=t.clientX-e.left,s.y=t.clientY-e.top}else s.x=t.clientX,s.y=t.clientY;Math.hypot(s.x-c.x,s.y-c.y)>1.5&&(m(s.x,s.y,l[Math.floor(Math.random()*e.length)]),c.x=s.x,c.y=s.y)}))}function m(t,e,n){a.push(new _(t,e,n))}function w(){!function(){if(0!=a.length){u.clearRect(0,0,o,r);for(let t=0;t=0;t--)a[t].lifeSpan<0&&a.splice(t,1);0==a.length&&u.clearRect(0,0,o,r)}}(),d=requestAnimationFrame(w)}function v(){h.remove(),cancelAnimationFrame(d),i.removeEventListener("mousemove",A),i.removeEventListener("touchmove",y),i.removeEventListener("touchstart",y),window.addEventListener("resize",g)}function _(t,e,n){const i=Math.floor(30*Math.random()+60);this.initialLifeSpan=i,this.lifeSpan=i,this.velocity={x:(Math.random()<.5?-1:1)*(Math.random()/2),y:.7*Math.random()+.9},this.position={x:t,y:e},this.canv=n,this.update=function(t){this.position.x+=this.velocity.x,this.position.y+=this.velocity.y,this.lifeSpan--,this.velocity.y+=.02;const e=Math.max(this.lifeSpan/this.initialLifeSpan,0);t.drawImage(this.canv,this.position.x-this.canv.width/2*e,this.position.y-this.canv.height/2,this.canv.width*e,this.canv.height*e)}}return p.onchange=()=>{p.matches?v():f()},f(),{destroy:v}},e.followingDotCursor=function(t){let e,n,i=t&&t.element,o=i||document.body,r=window.innerWidth,s=window.innerHeight,c={x:r/2,y:r/2},a=new function(t,e,n,i){this.position={x:t,y:e},this.width=10,this.lag=10,this.moveTowards=function(t,e,n){this.position.x+=(t-this.position.x)/this.lag,this.position.y+=(e-this.position.y)/this.lag,n.fillStyle=l,n.beginPath(),n.arc(this.position.x,this.position.y,this.width,0,2*Math.PI),n.fill(),n.closePath()}}(r/2,s/2,10,10),l=t?.color||"#323232a6";const h=window.matchMedia("(prefers-reduced-motion: reduce)");function u(){if(h.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init"),!1;e=document.createElement("canvas"),n=e.getContext("2d"),e.style.top="0px",e.style.left="0px",e.style.pointerEvents="none",i?(e.style.position="absolute",o.appendChild(e),e.width=o.clientWidth,e.height=o.clientHeight):(e.style.position="fixed",document.body.appendChild(e),e.width=r,e.height=s),o.addEventListener("mousemove",p),window.addEventListener("resize",d),f()}function d(t){r=window.innerWidth,s=window.innerHeight,i?(e.width=o.clientWidth,e.height=o.clientHeight):(e.width=r,e.height=s)}function p(t){if(i){const e=o.getBoundingClientRect();c.x=t.clientX-e.left,c.y=t.clientY-e.top}else c.x=t.clientX,c.y=t.clientY}function f(){n.clearRect(0,0,r,s),a.moveTowards(c.x,c.y,n),requestAnimationFrame(f)}function g(){e.remove(),cancelAnimationFrame(f),o.removeEventListener("mousemove",p),window.addEventListener("resize",d)}return h.onchange=()=>{h.matches?g():u()},u(),{destroy:g}},e.ghostCursor=function(t){let e,n,i,o=t&&t.element,r=o||document.body,s=window.innerWidth,c=window.innerHeight,a={x:s/2,y:s/2},l=[],h=new Image;h.src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAATCAYAAACk9eypAAAAAXNSR0IArs4c6QAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADKADAAQAAAABAAAAEwAAAAAChpcNAAAACXBIWXMAAAsTAAALEwEAmpwYAAABWWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpMwidZAAABqElEQVQoFY3SPUvDQBgH8BREpRHExYiDgmLFl6WC+AYmWeyLg4i7buJX8DMpOujgyxGvUYeCgzhUQUSKKLUS0+ZyptXh8Z5Ti621ekPyJHl+uftfomhaf9Ei5JyxXKfynyEA6EYcLHpwyflT958GAQ7DTABNHd8EbtDbEH2BD5QEQmi2mM8P/Iq+A0SzszEg+3sPjDnDdVEtQKQbMUidHD3xVzf6A9UDEmEm+8h9KTqTVUjT+vB53aHrCbAPiceYq1dQI1Aqv4EhMll0jzv+Y0yiRgCnLRSYyDQHVoqUXe4uKL9l+L7GXC4vkMhE6eW/AOJs9k583ORDUyXMZ8F5SVHVVnllmPNKSFagAJ5DofaqGXw/gHBYg51dIldkmknY3tguv3jOtHR4+MqAzaraJXbEhqHhcQlwGSOi5pytVQHZLN5s0WNe8HPrLYlFsO20RPHkImxsbmHdLJFI76th7Z4SeuF53hTeFLvhRCJRCTKZKxgdnRDbW+iozFJbBMw14/ElwGYc0egMBMFzT21f5Rog33Z7dX02GBm7WV5ZfT5Nn5bE3zuCDe9UxdTpNvK+5AAAAABJRU5ErkJggg==";const u=window.matchMedia("(prefers-reduced-motion: reduce)");function d(){if(u.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init"),!1;e=document.createElement("canvas"),n=e.getContext("2d"),e.style.top="0px",e.style.left="0px",e.style.pointerEvents="none",o?(e.style.position="absolute",r.appendChild(e),e.width=r.clientWidth,e.height=r.clientHeight):(e.style.position="fixed",document.body.appendChild(e),e.width=s,e.height=c),r.addEventListener("mousemove",g),r.addEventListener("touchmove",f,{passive:!0}),r.addEventListener("touchstart",f,{passive:!0}),window.addEventListener("resize",p),A()}function p(t){s=window.innerWidth,c=window.innerHeight,o?(e.width=r.clientWidth,e.height=r.clientHeight):(e.width=s,e.height=c)}function f(t){if(t.touches.length>0)for(let e=0;e=0;t--)l[t].lifeSpan<0&&l.splice(t,1);0==l.length&&n.clearRect(0,0,s,c)}}(),i=requestAnimationFrame(A)}function m(){e.remove(),cancelAnimationFrame(i),r.removeEventListener("mousemove",g),r.removeEventListener("touchmove",f),r.removeEventListener("touchstart",f),window.addEventListener("resize",p)}function w(t,e,n){this.initialLifeSpan=40,this.lifeSpan=40,this.position={x:t,y:e},this.image=n,this.update=function(t){this.lifeSpan--;const e=Math.max(this.lifeSpan/this.initialLifeSpan,0);t.globalAlpha=e,t.drawImage(this.image,this.position.x,this.position.y)}}return u.onchange=()=>{u.matches?m():d()},d(),{destroy:m}},e.rainbowCursor=function(t){let e,n,i,o=t&&t.element,r=o||document.body,s=window.innerWidth,c=window.innerHeight,a={x:s/2,y:s/2},l=[];const h=t?.length||20,u=t?.colors||["#FE0000","#FD8C00","#FFE500","#119F0B","#0644B3","#C22EDC"],d=t?.size||3;let p=!1;const f=window.matchMedia("(prefers-reduced-motion: reduce)");function g(){if(f.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init"),!1;e=document.createElement("canvas"),n=e.getContext("2d"),e.style.top="0px",e.style.left="0px",e.style.pointerEvents="none",o?(e.style.position="absolute",r.appendChild(e),e.width=r.clientWidth,e.height=r.clientHeight):(e.style.position="fixed",document.body.appendChild(e),e.width=s,e.height=c),r.addEventListener("mousemove",A),window.addEventListener("resize",y),m()}function y(t){s=window.innerWidth,c=window.innerHeight,o?(e.width=r.clientWidth,e.height=r.clientHeight):(e.width=s,e.height=c)}function A(t){if(o){const e=r.getBoundingClientRect();a.x=t.clientX-e.left,a.y=t.clientY-e.top}else a.x=t.clientX,a.y=t.clientY;if(!1===p){p=!0;for(let t=0;t{n.beginPath(),n.strokeStyle=e,t.length&&n.moveTo(t[0].x,t[0].y+i*(d-1)),t.forEach(((t,e)=>{0!==e&&n.lineTo(t.x,t.y+i*d)})),n.lineWidth=d,n.lineCap="round",n.stroke()}))}(),i=requestAnimationFrame(m)}function w(){e.remove(),cancelAnimationFrame(i),r.removeEventListener("mousemove",A),window.addEventListener("resize",y)}function v(t,e){this.position={x:t,y:e}}return f.onchange=()=>{f.matches?w():g()},g(),{destroy:w}},e.snowflakeCursor=function(t){let e,n,i,o=t&&t.element,r=o||document.body,s=["❄️"],c=window.innerWidth,a=window.innerHeight,l={x:c/2,y:c/2},h=[],u=[];const d=window.matchMedia("(prefers-reduced-motion: reduce)");function p(){if(d.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init"),!1;e=document.createElement("canvas"),n=e.getContext("2d"),e.style.top="0px",e.style.left="0px",e.style.pointerEvents="none",o?(e.style.position="absolute",r.appendChild(e),e.width=r.clientWidth,e.height=r.clientHeight):(e.style.position="fixed",document.body.appendChild(e),e.width=c,e.height=a),n.font="12px serif",n.textBaseline="middle",n.textAlign="center",s.forEach((t=>{let e=n.measureText(t),i=document.createElement("canvas"),o=i.getContext("2d");i.width=e.width,i.height=2*e.actualBoundingBoxAscent,o.textAlign="center",o.font="12px serif",o.textBaseline="middle",o.fillText(t,i.width/2,e.actualBoundingBoxAscent),u.push(i)})),r.addEventListener("mousemove",y),r.addEventListener("touchmove",g,{passive:!0}),r.addEventListener("touchstart",g,{passive:!0}),window.addEventListener("resize",f),m()}function f(t){c=window.innerWidth,a=window.innerHeight,o?(e.width=r.clientWidth,e.height=r.clientHeight):(e.width=c,e.height=a)}function g(t){if(t.touches.length>0)for(let e=0;e=0;t--)h[t].lifeSpan<0&&h.splice(t,1);0==h.length&&n.clearRect(0,0,c,a)}}(),i=requestAnimationFrame(m)}function w(){e.remove(),cancelAnimationFrame(i),r.removeEventListener("mousemove",y),r.removeEventListener("touchmove",g),r.removeEventListener("touchstart",g),window.addEventListener("resize",f)}function v(t,e,n){const i=Math.floor(60*Math.random()+80);this.initialLifeSpan=i,this.lifeSpan=i,this.velocity={x:(Math.random()<.5?-1:1)*(Math.random()/2),y:1+Math.random()},this.position={x:t,y:e},this.canv=n,this.update=function(t){this.position.x+=this.velocity.x,this.position.y+=this.velocity.y,this.lifeSpan--,this.velocity.x+=2*(Math.random()<.5?-1:1)/75,this.velocity.y-=Math.random()/300;const e=Math.max(this.lifeSpan/this.initialLifeSpan,0),n=2*this.lifeSpan*.0174533;t.translate(this.position.x,this.position.y),t.rotate(n),t.drawImage(this.canv,-this.canv.width/2*e,-this.canv.height/2,this.canv.width*e,this.canv.height*e),t.rotate(-n),t.translate(-this.position.x,-this.position.y)}}return d.onchange=()=>{d.matches?w():p()},p(),{destroy:w}},e.springyEmojiCursor=function(t){let e,n,i,o,r=t&&t.emoji||"🤪",s=t&&t.element,c=s||document.body,a=window.innerWidth,l=window.innerHeight,h={x:a/2,y:a/2},u=[];const d=window.matchMedia("(prefers-reduced-motion: reduce)");function p(){if(d.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init"),!1;e=document.createElement("canvas"),n=e.getContext("2d"),e.style.top="0px",e.style.left="0px",e.style.pointerEvents="none",s?(e.style.position="absolute",c.appendChild(e),e.width=c.clientWidth,e.height=c.clientHeight):(e.style.position="fixed",document.body.appendChild(e),e.width=a,e.height=l),n.font="16px serif",n.textBaseline="middle",n.textAlign="center";let t=n.measureText(r),i=document.createElement("canvas"),h=i.getContext("2d");i.width=t.width,i.height=2*t.actualBoundingBoxAscent,h.textAlign="center",h.font="16px serif",h.textBaseline="middle",h.fillText(r,i.width/2,t.actualBoundingBoxAscent),o=i;let p=0;for(p=0;p<7;p++)u[p]=new _(o);c.addEventListener("mousemove",y),c.addEventListener("touchmove",g,{passive:!0}),c.addEventListener("touchstart",g,{passive:!0}),window.addEventListener("resize",f),A()}function f(t){a=window.innerWidth,l=window.innerHeight,s?(e.width=c.clientWidth,e.height=c.clientHeight):(e.width=a,e.height=l)}function g(t){if(t.touches.length>0)if(s){const e=c.getBoundingClientRect();h.x=t.touches[0].clientX-e.left,h.y=t.touches[0].clientY-e.top}else h.x=t.touches[0].clientX,h.y=t.touches[0].clientY}function y(t){if(s){const e=c.getBoundingClientRect();h.x=t.clientX-e.left,h.y=t.clientY-e.top}else h.x=t.clientX,h.y=t.clientY}function A(){!function(){e.width=e.width,u[0].position.x=h.x,u[0].position.y=h.y;for(let t=1;t<7;t++){let i=new w(0,0);t>0&&v(t-1,t,i),t<6&&v(t+1,t,i);let o,r,s=new w(10*-u[t].velocity.x,10*-u[t].velocity.y),c=new w((i.X+s.X)/1,(i.Y+s.Y)/1+50);u[t].velocity.x+=.01*c.X,u[t].velocity.y+=.01*c.Y,Math.abs(u[t].velocity.x)<.1&&Math.abs(u[t].velocity.y)<.1&&Math.abs(c.X)<.1&&Math.abs(c.Y)<.1&&(u[t].velocity.x=0,u[t].velocity.y=0),u[t].position.x+=u[t].velocity.x,u[t].position.y+=u[t].velocity.y,o=e.clientHeight,r=e.clientWidth,u[t].position.y>=o-11-1&&(u[t].velocity.y>0&&(u[t].velocity.y=.7*-u[t].velocity.y),u[t].position.y=o-11-1),u[t].position.x>=r-11&&(u[t].velocity.x>0&&(u[t].velocity.x=.7*-u[t].velocity.x),u[t].position.x=r-11-1),u[t].position.x<0&&(u[t].velocity.x<0&&(u[t].velocity.x=.7*-u[t].velocity.x),u[t].position.x=0),u[t].draw(n)}}(),i=requestAnimationFrame(A)}function m(){e.remove(),cancelAnimationFrame(i),c.removeEventListener("mousemove",y),c.removeEventListener("touchmove",g),c.removeEventListener("touchstart",g),window.addEventListener("resize",f)}function w(t,e){this.X=t,this.Y=e}function v(t,e,n){let i=u[t].position.x-u[e].position.x,o=u[t].position.y-u[e].position.y,r=Math.sqrt(i*i+o*o);if(r>10){let t=10*(r-10);n.X+=i/r*t,n.Y+=o/r*t}}function _(t){this.position={x:h.x,y:h.y},this.velocity={x:0,y:0},this.canv=t,this.draw=function(t){t.drawImage(this.canv,this.position.x-this.canv.width/2,this.position.y-this.canv.height/2,this.canv.width,this.canv.height)}}return d.onchange=()=>{d.matches?m():p()},p(),{destroy:m}},e.textFlag=function(t){let e,n,i,o=t||{},r=t&&t.element,s=r||document.body,c=o.text?" "+t.text:" Your Text Here",a=o.font||"monospace",l=o.textSize||12,h=l+"px "+a,u=o.gap||l+2,d=0,p=[],f=window.innerWidth,g=window.innerHeight,y={x:f/2,y:f/2};for(let t=0;t0;t--)p[t].x=p[t-1].x+u,p[t].y=p[t-1].y,n.font=h,n.fillText(p[t].letter,p[t].x,p[t].y);let i=p[0].x,o=p[0].y;i+=(y.x-i)/5+t+2,o+=(y.y-o)/5+e,p[0].x=i,p[0].y=o}(),i=requestAnimationFrame(_)}function x(){e.remove(),cancelAnimationFrame(i),s.removeEventListener("mousemove",v),window.addEventListener("resize",w)}return A.onchange=()=>{A.matches?x():m()},m(),{destroy:x}},e.trailingCursor=function(t){let e,n,i,o=t&&t.element,r=o||document.body,s=window.innerWidth,c=window.innerHeight,a={x:s/2,y:s/2},l=[];const h=t?.particles||15,u=t?.rate||.4,d=t?.baseImageSrc||"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAATCAYAAACk9eypAAAAAXNSR0IArs4c6QAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADKADAAQAAAABAAAAEwAAAAAChpcNAAAACXBIWXMAAAsTAAALEwEAmpwYAAABWWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpMwidZAAABqElEQVQoFY3SPUvDQBgH8BREpRHExYiDgmLFl6WC+AYmWeyLg4i7buJX8DMpOujgyxGvUYeCgzhUQUSKKLUS0+ZyptXh8Z5Ti621ekPyJHl+uftfomhaf9Ei5JyxXKfynyEA6EYcLHpwyflT958GAQ7DTABNHd8EbtDbEH2BD5QEQmi2mM8P/Iq+A0SzszEg+3sPjDnDdVEtQKQbMUidHD3xVzf6A9UDEmEm+8h9KTqTVUjT+vB53aHrCbAPiceYq1dQI1Aqv4EhMll0jzv+Y0yiRgCnLRSYyDQHVoqUXe4uKL9l+L7GXC4vkMhE6eW/AOJs9k583ORDUyXMZ8F5SVHVVnllmPNKSFagAJ5DofaqGXw/gHBYg51dIldkmknY3tguv3jOtHR4+MqAzaraJXbEhqHhcQlwGSOi5pytVQHZLN5s0WNe8HPrLYlFsO20RPHkImxsbmHdLJFI76th7Z4SeuF53hTeFLvhRCJRCTKZKxgdnRDbW+iozFJbBMw14/ElwGYc0egMBMFzT21f5Rog33Z7dX02GBm7WV5ZfT5Nn5bE3zuCDe9UxdTpNvK+5AAAAABJRU5ErkJggg==";let p=!1,f=new Image;f.src=d;const g=window.matchMedia("(prefers-reduced-motion: reduce)");function y(){if(g.matches)return console.log("This browser has prefers reduced motion turned on, so the cursor did not init"),!1;e=document.createElement("canvas"),n=e.getContext("2d"),e.style.top="0px",e.style.left="0px",e.style.pointerEvents="none",o?(e.style.position="absolute",r.appendChild(e),e.width=r.clientWidth,e.height=r.clientHeight):(e.style.position="fixed",document.body.appendChild(e),e.width=s,e.height=c),r.addEventListener("mousemove",m),window.addEventListener("resize",A),w()}function A(t){s=window.innerWidth,c=window.innerHeight,o?(e.width=r.clientWidth,e.height=r.clientHeight):(e.width=s,e.height=c)}function m(t){if(o){const e=r.getBoundingClientRect();a.x=t.clientX-e.left,a.y=t.clientY-e.top}else a.x=t.clientX,a.y=t.clientY;if(!1===p){p=!0;for(let t=0;t{g.matches?v():y()},y(),{destroy:v}}}},__webpack_module_cache__={};function __webpack_require__(t){var e=__webpack_module_cache__[t];if(void 0!==e)return e.exports;var n=__webpack_module_cache__[t]={exports:{}};return __webpack_modules__[t](n,n.exports,__webpack_require__),n.exports}__webpack_require__.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return __webpack_require__.d(e,{a:e}),e},__webpack_require__.d=(t,e)=>{for(var n in e)__webpack_require__.o(e,n)&&!__webpack_require__.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},__webpack_require__.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e);var __webpack_exports__={};(()=>{var t,e,n=(t=function(e,n){return t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n])},t(e,n)},function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}),i=function(){function t(){}return t.prototype.check=function(t){return Math.random()<.01*t},t.prototype.enable=function(){this.settings.enabled=!0},t.prototype.disable=function(){this.settings.enabled=!1},t.prototype.toggle=function(t){void 0===t&&(t=null),this.settings.enabled=null===t?!this.settings.enabled:t},t}(),o=function(t){function e(){return null!==t&&t.apply(this,arguments)||this}return n(e,t),e}(i),r=__webpack_require__(562),s=function(){var t=function(e,n){return t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n])},t(e,n)};return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),c=function(){function t(){this.ui=new r.d(this),this.core_settings=new a,this.jokes={}}return t.prototype.check=function(t){return Math.random()<.01*t},t.prototype.add=function(t){this.jokes[t.id]=t},t.prototype.addCustom=function(t,e,n){void 0===n&&(n=null);var o=new(function(n){function i(){var e=null!==n&&n.apply(this,arguments)||this;return e.id=t,e.title="Пользовательская шутка",e.description="Для шутки не определено описание",e.settings={chance:10,enabled:!0},e}return s(i,n),i.prototype.start=function(){e.call(this,this.settings)},i}(i));return Object.assign(o.settings,null!=n?n:{}),this.add(o),o},Object.defineProperty(t.prototype,"settings",{get:function(){var t={};t.core=this.core_settings;for(var e=0,n=Object.entries(this.jokes);e=0)return!0;var t=new Date,e=t.getMonth()+1,n=t.getDate();return 4===e&&1===n&&this.check(this.core_settings.chance)},t.prototype.regularStart=function(){this.regularCheck()&&(this.ui.show(),this.startAll())},t}(),a=function(){this.chance=100,this.enabled=!0,this.testers=[]},l=__webpack_require__(311),h=__webpack_require__.n(l),u=function(){var t=function(e,n){return t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n])},t(e,n)};return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),d=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.id="shake",e.title="Форумо-трясение",e.description="Страница форума начинает трястись",e.settings=new p,e.data_name="april-fools-shake",e}return u(e,t),e.prototype.rand=function(t){return t+this.settings.force*(2*Math.random()-1)},e.prototype.start=function(){var t=this;this.items=h()(this.settings.selector),this.items.each((function(e,n){var i={};t.settings.directions.forEach((function(t){i[t]=parseInt(h()(n).css("margin-"+t))})),h()(n).data(t.data_name,i)})),this.interval_id=setInterval((function(){t.items.each((function(e,n){for(var i=h()(n).data(t.data_name),o=0,r=Object.entries(i);o0?"тся":"ться"})))}))},e}(i),y=function(){this.enabled=!0,this.chance=20,this.selector=".post-content"},A=function(){var t=function(e,n){return t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n])},t(e,n)};return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),m=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.id="mirror",e.title="Зеркало",e.description="Элементы форума отражаются (по крайней мере, пытаются)",e.settings=new w,e}return A(e,t),e.prototype.start=function(){h()(".post, .post h3, .post-links, .post-author, .post-content").css("transform","scaleX(-1)").css("-moz-transform","scaleX(-1)").css("-moz-transform","scaleX(-1)").css("filter","FlipH").css("-ms-filter",'"FlipH"'),h()(".post-links, .post-links ul, .post h3 span").css("margin-left","0"),h()(".post h3 span").css("margin-right","25em")},e}(i),w=function(){this.enabled=!1,this.chance=30},v=function(){var t=function(e,n){return t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n])},t(e,n)};return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),_=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.id="carnival",e.title="Карнавал",e.description="Аватарки приобретают интересные цвета",e.settings=new x,e}return v(e,t),e.prototype.start=function(){var t=this;h()(this.settings.selector).each((function(e,n){for(var i=[],o=0,r=Object.entries(t.settings.variants);o0&&h()(n).css({filter:i.join(" ")})}))},e}(i),x=function(){this.enabled=!0,this.chance=30,this.selector='img[src*="/avatars/"]',this.variant_chance=20,this.variants={"sepia({0}%)":100,"saturate({0}%)":100,"hue-rotate({0}deg)":360,"invert({0}%)":100}},b=function(){var t=function(e,n){return t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n])},t(e,n)};return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),E=function(t){function e(){var e=null!==t&&t.apply(this,arguments)||this;return e.id="crazy_letters",e.title="Мама, Я сОшЛа С уМа!",e.description="иМеНа ЗаБоРчИкОм",e.settings=new C,e}return b(e,t),e.prototype.start=function(){var t=this;h()(this.settings.selector).each((function(e,n){for(var i=h()(t).text(),o="",r=0;r0;o--){var r=Math.floor(Math.random()*(o+1));e=[i[r],i[o]],i[o]=e[0],i[r]=e[1]}return O(O([n[0]],i,!0),[n[n.length-1]],!1).join("")},e.prototype.processNode=function(t){var e;t.textContent=null===(e=t.textContent)||void 0===e?void 0:e.replace(/([А-Яа-яA-Za-z]+)/gm,this.shuffleLetters)},e.prototype.start=function(){var t=this;h()(this.settings.selector).find("*").contents().filter((function(t,e){return e.nodeType===Node.TEXT_NODE})).each((function(e,n){return t.processNode(n)}))},e}(i),L=function(){this.enabled=!0,this.chance=20,this.selector=".post-content",this.shuffle_chance=50},k=function(){var t=function(e,n){return t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n])},t(e,n)};return function(e,n){if("function"!=typeof n&&null!==n)throw new TypeError("Class extends value "+String(n)+" is not a constructor or null");function i(){this.constructor=e}t(e,n),e.prototype=null===n?Object.create(n):(i.prototype=n.prototype,new i)}}(),B=__webpack_require__(442),R=function(t){function n(){var e=null!==t&&t.apply(this,arguments)||this;return e.id="cursor_effects",e.title="Красивый курсор",e.description="Эффекты, следующие за курсором",e.settings=new T,e}return k(n,t),n.prototype.enableAll=function(){for(var t=0,e=Object.keys(this.settings.effects);t0&&o[o.length-1])||6!==c[0]&&2!==c[0])){s=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]>>16,2246822507)^Math.imul(o^o>>>13,3266489909);var c=4294967296*(2097151&(o=Math.imul(o^o>>>16,2246822507)^Math.imul(i^i>>>13,3266489909)))+(i>>>0);return Math.floor(Math.pow(Math.sin(c),2)*n)%n},e.prototype.resetSeed=function(){new Date;var t=60*(new Date).getUTCHours()+(new Date).getUTCMinutes();this.seed=Math.floor(t/this.settings.change_frequency),console.log("Seed reset:",t,this.seed)},e.prototype.selectRandom=function(t,e){for(var n=Object.keys(e),i=n.reduce((function(t,n){return t+e[n]}),0),o=this.prng(t,this.seed++,i),r=0,s=0,c=n;s=0,enabled:e.indexOf("!")<0,weight:10*(e.indexOf("$")>=0?.5:1)*(e.indexOf("%")>=0?.5:1)*(e.indexOf("^")>=0?2:1)}))})),n}window.BuildStupdiTitleVariant=q,window.AprilJokes=(S=[d,g,m,_,E,j,R,F],D=new c,S.forEach((function(t){D.add(new t)})),$((function(){D.regularStart()})),D)})()})();
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index b67457f..5023cce 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -26,6 +26,7 @@
"grunt-browserify": "^6.0.0",
"grunt-contrib-uglify": "^5.2.2",
"grunt-jsdoc-to-markdown": "^6.0.0",
+ "html-loader": "^4.2.0",
"jsdoc-babel": "^0.5.0",
"jsdoc-to-markdown": "^8.0.0",
"markdown-it": "^13.0.1",
@@ -3615,6 +3616,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/camel-case": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
+ "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
+ "dev": true,
+ "dependencies": {
+ "pascal-case": "^3.1.2",
+ "tslib": "^2.0.3"
+ }
+ },
"node_modules/caniuse-lite": {
"version": "1.0.30001470",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001470.tgz",
@@ -3716,6 +3727,27 @@
"safe-buffer": "^5.0.1"
}
},
+ "node_modules/clean-css": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.2.0.tgz",
+ "integrity": "sha512-2639sWGa43EMmG7fn8mdVuBSs6HuWaSor+ZPoFWzenBc6oN+td8YhTfghWXZ25G1NiiSvz8bOFBS7PdSbTiqEA==",
+ "dev": true,
+ "dependencies": {
+ "source-map": "~0.6.0"
+ },
+ "engines": {
+ "node": ">= 10.0"
+ }
+ },
+ "node_modules/clean-css/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/clean-stack": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
@@ -4436,6 +4468,16 @@
"npm": ">=1.2"
}
},
+ "node_modules/dot-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
+ "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
+ "dev": true,
+ "dependencies": {
+ "no-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
"node_modules/duplexer": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
@@ -5877,6 +5919,68 @@
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
+ "node_modules/html-loader": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-4.2.0.tgz",
+ "integrity": "sha512-OxCHD3yt+qwqng2vvcaPApCEvbx+nXWu+v69TYHx1FO8bffHn/JjHtE3TTQZmHjwvnJe4xxzuecetDVBrQR1Zg==",
+ "dev": true,
+ "dependencies": {
+ "html-minifier-terser": "^7.0.0",
+ "parse5": "^7.0.0"
+ },
+ "engines": {
+ "node": ">= 14.15.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.0.0"
+ }
+ },
+ "node_modules/html-minifier-terser": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.1.0.tgz",
+ "integrity": "sha512-BvPO2S7Ip0Q5qt+Y8j/27Vclj6uHC6av0TMoDn7/bJPhMWHI2UtR2e/zEgJn3/qYAmxumrGp9q4UHurL6mtW9Q==",
+ "dev": true,
+ "dependencies": {
+ "camel-case": "^4.1.2",
+ "clean-css": "5.2.0",
+ "commander": "^9.4.1",
+ "entities": "^4.4.0",
+ "param-case": "^3.0.4",
+ "relateurl": "^0.2.7",
+ "terser": "^5.15.1"
+ },
+ "bin": {
+ "html-minifier-terser": "cli.js"
+ },
+ "engines": {
+ "node": "^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/html-minifier-terser/node_modules/commander": {
+ "version": "9.5.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
+ "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
+ "dev": true,
+ "engines": {
+ "node": "^12.20.0 || >=14"
+ }
+ },
+ "node_modules/html-minifier-terser/node_modules/entities": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz",
+ "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
"node_modules/htmlescape": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz",
@@ -7142,6 +7246,15 @@
"node": ">=8"
}
},
+ "node_modules/lower-case": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
+ "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
+ "dev": true,
+ "dependencies": {
+ "tslib": "^2.0.3"
+ }
+ },
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -7811,6 +7924,16 @@
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
},
+ "node_modules/no-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
+ "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+ "dev": true,
+ "dependencies": {
+ "lower-case": "^2.0.2",
+ "tslib": "^2.0.3"
+ }
+ },
"node_modules/node-fetch": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz",
@@ -8847,6 +8970,16 @@
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
"dev": true
},
+ "node_modules/param-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
+ "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
+ "dev": true,
+ "dependencies": {
+ "dot-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
"node_modules/parents": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz",
@@ -8918,6 +9051,40 @@
"node": ">=0.10.0"
}
},
+ "node_modules/parse5": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
+ "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
+ "dev": true,
+ "dependencies": {
+ "entities": "^4.4.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/parse5/node_modules/entities": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz",
+ "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/pascal-case": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
+ "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
+ "dev": true,
+ "dependencies": {
+ "no-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
"node_modules/path-browserify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
@@ -9623,6 +9790,15 @@
"jsesc": "bin/jsesc"
}
},
+ "node_modules/relateurl": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+ "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
"node_modules/remove-trailing-separator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
diff --git a/package.json b/package.json
index 95c9db8..abdd919 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"grunt-browserify": "^6.0.0",
"grunt-contrib-uglify": "^5.2.2",
"grunt-jsdoc-to-markdown": "^6.0.0",
+ "html-loader": "^4.2.0",
"jsdoc-babel": "^0.5.0",
"jsdoc-to-markdown": "^8.0.0",
"markdown-it": "^13.0.1",
diff --git a/settings.compact.js b/settings.compact.js
index ff92bb7..f183193 100644
--- a/settings.compact.js
+++ b/settings.compact.js
@@ -16,7 +16,7 @@ window.AprilJokes.settings = {
enabled: false,
chance: 30,
},
-
+
carnival: {
enabled: true,
chance: 30,
@@ -127,4 +127,34 @@ window.AprilJokes.settings = {
},
},
},
+
+ stupid_title: {
+ enabled: true,
+ chance: 20,
+ gender_resolver: null,
+ rude: false,
+ change_frequency: 5,
+ schemas: {
+ '{adj} {noun}{p}': 10,
+ '{noun} {adj}{p}': 3,
+ '{adj}, {adj} {noun}{p}': 10,
+ '{adj} и очень {adj} {noun}{p}': 10,
+ '{adj} {noun} {verb}{p}': 10,
+ '{adj} {noun} {adv} {verb}{p}': 5,
+ '{adj} {noun} {verb} {place}{p}': 5,
+ '{adj} {noun} {adv} {verb} {place}{p}': 5,
+ },
+ variants: {
+ adj: {
+ ...window.AprilJokes.settings.stupid_title.variants.adj,
+ ...BuildStupdiTitleVariant(true, 'скромный, шутливый, !грубый'),
+ ...BuildStupdiTitleVariant(false, 'скромная, шутливая, !грубая'),
+ },
+ noun: { ...window.AprilJokes.settings.stupid_title.variants.noun, },
+ verb: { ...window.AprilJokes.settings.stupid_title.variants.verb, },
+ place: { ...window.AprilJokes.settings.stupid_title.variants.place, },
+ adv: { ...window.AprilJokes.settings.stupid_title.variants.adv, },
+ p: { ...window.AprilJokes.settings.stupid_title.variants.p, },
+ },
+ },
}
\ No newline at end of file
diff --git a/settings.example.js b/settings.example.js
index 4af4661..3aaa951 100644
--- a/settings.example.js
+++ b/settings.example.js
@@ -37,7 +37,7 @@ window.AprilJokes.settings = {
* @author Kozhilya
*/
shake: {
- enabled: true,
+ enabled: false,
chance: 1,
/**
@@ -379,4 +379,74 @@ window.AprilJokes.settings = {
},
},
},
+
+ /**
+ * **Глупые статусы**
+ *
+ * Каждому пользователю даётся новый глупый статус.
+ *
+ * @author Kozhilya
+ */
+ stupid_title: {
+ enabled: true,
+ chance: 20,
+
+ /**
+ * Определение пола по имени.
+ * По умолчанию род устанавливается случайным образом
+ *
+ * Если установлено значение `null`, система попытается загрузить пол, установленный в настройках аккаунта. Если этот пол определить невозможно, то он будет выбран случайно. Эта настройка позволяет изменить это поведение, если ваш форум обладает ролевой системой, в которой пол определяется иным образом.
+ *
+ * @param name Имя персонажа
+ * @returns true, если мужской род; false, если женский род
+ */
+ gender_resolver: null,
+
+ /**
+ * Разрешить возможно оскорбительные слова
+ */
+ rude: false,
+
+ /**
+ * Частота смены статусов (в минутах)
+ */
+ change_frequency: 5,
+
+ /**
+ * Разрешённые схемы формирования статусов
+ *
+ * {adj} - прилагательное
+ * {noun} - имя существительно
+ * {verb} - глагол
+ * {adv} - наречие
+ * {p} - знак препинания
+ */
+ schemas: {
+ '{adj} {noun}{p}': 10,
+ '{noun} {adj}{p}': 3,
+ '{adj}, {adj} {noun}{p}': 10,
+ '{adj} и очень {adj} {noun}{p}': 10,
+ '{adj} {noun} {verb}{p}': 10,
+ '{adj} {noun} {adv} {verb}{p}': 5,
+ '{adj} {noun} {verb} {place}{p}': 5,
+ '{adj} {noun} {adv} {verb} {place}{p}': 5,
+ },
+
+ /**
+ * Вариант слов.
+ * Смотрите раздел описания скрипта, что бы настроить это поле
+ */
+ variants: {
+ adj: {
+ ...window.AprilJokes.settings.stupid_title.variants.adj,
+ ...BuildStupdiTitleVariant(true, 'скромный, шутливый, !грубый'),
+ ...BuildStupdiTitleVariant(false, 'скромная, шутливая, !грубая'),
+ },
+ noun: { ...window.AprilJokes.settings.stupid_title.variants.noun, },
+ verb: { ...window.AprilJokes.settings.stupid_title.variants.verb, },
+ place: { ...window.AprilJokes.settings.stupid_title.variants.place, },
+ adv: { ...window.AprilJokes.settings.stupid_title.variants.adv, },
+ p: { ...window.AprilJokes.settings.stupid_title.variants.p, },
+ },
+ },
}
\ No newline at end of file
diff --git a/src/html.d.ts b/src/html.d.ts
new file mode 100644
index 0000000..56b1935
--- /dev/null
+++ b/src/html.d.ts
@@ -0,0 +1,4 @@
+declare module "*.html" {
+ const content: string;
+ export default content;
+}
\ No newline at end of file
diff --git a/src/html/button.html b/src/html/button.html
new file mode 100644
index 0000000..a3646ae
--- /dev/null
+++ b/src/html/button.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/html/styles.html b/src/html/styles.html
new file mode 100644
index 0000000..14ee2e5
--- /dev/null
+++ b/src/html/styles.html
@@ -0,0 +1,66 @@
+
\ No newline at end of file
diff --git a/src/html/window.html b/src/html/window.html
new file mode 100644
index 0000000..5d4b981
--- /dev/null
+++ b/src/html/window.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/joke.ts b/src/joke.ts
index db5374d..9377908 100644
--- a/src/joke.ts
+++ b/src/joke.ts
@@ -22,6 +22,16 @@ export abstract class Joke {
*/
abstract id: string;
+ /**
+ * Заголовок шутки
+ */
+ abstract title: string;
+
+ /**
+ * Описание шутки
+ */
+ abstract description: string;
+
/**
* Настройки шутки
*/
diff --git a/src/joker.ts b/src/joker.ts
index a0baea0..0452246 100644
--- a/src/joker.ts
+++ b/src/joker.ts
@@ -1,4 +1,6 @@
-import { Joke, JokeSettings } from "./joke";
+import { stat } from "fs";
+import { Joke, JokeSettings, StopableJoke } from "./joke";
+import { JokerUI } from "./ui";
/**
* Блок обработки перво-апрельских шуток
@@ -6,6 +8,8 @@ import { Joke, JokeSettings } from "./joke";
* @author Kozhilya
*/
export class JokerClass {
+ ui: JokerUI = new JokerUI(this);
+
/**
* Проверка вероятности
*
@@ -32,10 +36,14 @@ export class JokerClass {
* @param handler Метод, выполняющий шутку
* @param settings Объект настроек шутки
*/
- addCustom(id: string, handler: Function, settings: JokeSettings|null = null) {
+ addCustom(id: string, handler: Function, settings: JokeSettings|null = null): Joke {
const joke = new class extends Joke {
id = id;
+ title = 'Пользовательская шутка';
+
+ description = 'Для шутки не определено описание';
+
settings: JokeSettings = { chance: 10, enabled: true };
start() {
@@ -46,6 +54,8 @@ export class JokerClass {
Object.assign(joke.settings, settings ?? {});
this.add(joke);
+
+ return joke;
}
/**
@@ -113,6 +123,74 @@ export class JokerClass {
}
}
+ /**
+ * Переключение шутки
+ */
+ toggleJoke(joke_id: string, state: boolean) {
+ const joke = this.jokes[joke_id];
+ joke.settings.enabled = state;
+
+ if (joke instanceof StopableJoke && !state) {
+ joke.stop();
+ }
+
+ this.saveUserStates();
+ }
+
+ /**
+ * Сохранение пользовательских настроек
+ */
+ saveUserStates() {
+ const states: any = {};
+
+ for (const key of Object.keys(this.jokes)) {
+ states[key] = this.jokes[key].settings.enabled;
+ }
+
+ fetch('/api.php?' + $.param({
+ method: 'storage.set',
+ user_id: ( window).UserID,
+ key: 'april-fools',
+ token: ( window).ForumAPITicket,
+ value: JSON.stringify(states),
+ timer: 24 * 60
+ }));
+ }
+
+ /**
+ * Загрузка пользовательских настроек
+ */
+ async loadUserStates() {
+ const response = await fetch('/api.php?' + $.param({
+ method: 'storage.get',
+ user_id: ( window).UserID,
+ key: 'april-fools',
+ }));
+ const json = await response.json();
+
+ if (json.error) {
+ return;
+ }
+
+ const data = JSON.parse(json.response.storage.data['april-fools']);
+
+ for (const key of Object.keys(this.jokes)) {
+ this.jokes[key].settings.enabled = data[key];
+ }
+ }
+
+ async clearUserStates() {
+ await fetch('/api.php?' + $.param({
+ method: 'storage.delete',
+ user_id: ( window).UserID,
+ key: 'april-fools',
+ token: ( window).ForumAPITicket,
+ }));
+
+ return;
+ }
+
+
/**
* Запуск шутки
*
@@ -141,15 +219,33 @@ export class JokerClass {
}
}
- regularStart(): void {
+ regularCheck(): boolean {
+ if (!this.core_settings.enabled) {
+ return false;
+ }
+
+ if (this.core_settings.testers.indexOf(( window).UserID) >= 0) {
+ return true;
+ }
+
const today = new Date();
- const month = today.getMonth() + 1; // Note that getMonth() returns 0-indexed month, so we need to add 1
+ const month = today.getMonth() + 1;
const day = today.getDate();
- if (month !== 4 || day !== 1 || !this.core_settings.enabled || !this.check(this.core_settings.chance)) {
+ if (month !== 4 || day !== 1) {
+ return false;
+ }
+
+ return this.check(this.core_settings.chance);
+ }
+
+ async regularStart() {
+ if (!this.regularCheck()) {
return;
}
+ await this.loadUserStates();
+ this.ui.show();
this.startAll();
}
@@ -181,4 +277,6 @@ interface SettingsMap {
class CoreSettings implements JokeSettings {
chance = 100;
enabled = true;
+
+ testers: number[] = [];
}
\ No newline at end of file
diff --git a/src/jokes/carnival.ts b/src/jokes/carnival.ts
index a0e0435..47873b5 100644
--- a/src/jokes/carnival.ts
+++ b/src/jokes/carnival.ts
@@ -10,7 +10,11 @@ import { JokerClass } from "../joker";
* @author Kozhilya
*/
export class CarnivalJoke extends Joke {
- id = 'carnival'
+ id = 'carnival';
+
+ title = 'Карнавал';
+
+ description = 'Аватарки приобретают интересные цвета';
settings = new CarnivalJokeSettings;
diff --git a/src/jokes/crazy_letters.ts b/src/jokes/crazy_letters.ts
index ea87661..2b0f91e 100644
--- a/src/jokes/crazy_letters.ts
+++ b/src/jokes/crazy_letters.ts
@@ -11,6 +11,10 @@ import $ from "jquery";
export class CrazyLettersJoke extends Joke {
id = 'crazy_letters';
+ title = 'Мама, Я сОшЛа С уМа!';
+
+ description = 'иМеНа ЗаБоРчИкОм';
+
settings = new CrazyLettersJokeSettings;
start(): void {
diff --git a/src/jokes/crazy_punctuation.ts b/src/jokes/crazy_punctuation.ts
deleted file mode 100644
index f8754c9..0000000
--- a/src/jokes/crazy_punctuation.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import { Joke, JokeSettings, StopableJoke } from "../joke";
-import $ from "jquery";
-
-/**
- * **Бешеные запятые**
- *
- * Запятые после слов
- *
- * @author Kozhilya
- */
-export class CrazyPunctuationJoke extends Joke {
- id = 'crazy_punctuation';
-
- settings = new CrazyPunctuationJokeSettings;
-
- punctuationSymbols: string[]|null = null;
-
- private shufflePunctuation(text: string): string {
- if (this.punctuationSymbols === null) {
- this.punctuationSymbols = this.settings.symbols.split('');
- }
- const words = text.split(" ");
-
- const shuffledWords = words.map(word => {
- const characters = word.split("");
-
- // Filter out any non-punctuation characters
- const punctuation = characters.filter(char => this.punctuationSymbols.includes(char));
-
- // Shuffle the punctuation array
- const shuffledPunctuation = punctuation.sort(() => Math.random() - 0.5);
-
- // Loop through each character in the word and replace punctuation with shuffled punctuation
- let index = 0;
- return characters.map(char => {
- if (this.punctuationSymbols.includes(char)) {
- return shuffledPunctuation[index++];
- }
- return char;
- }).join("");
- });
-
- // Join the shuffled words array into a single string with spaces
- return shuffledWords.join(" ");
- }
-
- start(): void {
- const items = $(this.settings.selector).find('*').contents().filter((_, node) => node.nodeType === Node.TEXT_NODE);
-
- items.each((_, node) => {
- node.textContent = this.shufflePunctuation(node.textContent);
- })
- }
-}
-
-export class CrazyPunctuationJokeSettings implements JokeSettings {
- enabled: boolean = true;
-
- chance: number = 20;
-
- /**
- * Селектор всех элементов, которые будут затронуты
- */
- selector: string = '.post-content p';
-
- /**
- * Мигрирующие знаки
- */
- symbols: string = ',.;:?!-()[]{}\'"';
-
-
-}
\ No newline at end of file
diff --git a/src/jokes/cursor.ts b/src/jokes/cursor.ts
index f3c15bc..298c82a 100644
--- a/src/jokes/cursor.ts
+++ b/src/jokes/cursor.ts
@@ -15,6 +15,10 @@ const cursor_effects = require("cursor-effects");
export class CursorEffectsJoke extends StopableJoke {
id = 'cursor_effects';
+ title = 'Красивый курсор';
+
+ description = 'Эффекты, следующие за курсором';
+
settings = new CursorEffectsJokeSettings;
active_cursor_effect: any;
@@ -43,7 +47,7 @@ export class CursorEffectsJoke extends StopableJoke {
}
getRandomEffect(): CursorEffectsEnum {
- const elements = Object.keys(CursorEffectsEnum).map(e => e);
+ const elements = Object.values(CursorEffectsEnum).map(e => e);
const totalWeight = elements.reduce((sum, element) => sum + this.weight(element), 0);
const randomWeight = Math.random() * totalWeight;
diff --git a/src/jokes/humanitarian_hell.ts b/src/jokes/humanitarian_hell.ts
index 3e8e1bf..658de9d 100644
--- a/src/jokes/humanitarian_hell.ts
+++ b/src/jokes/humanitarian_hell.ts
@@ -10,6 +10,10 @@ import $ from "jquery";
*/
export class HumanitarianHellJoke extends Joke {
id = 'humanitarian_hell';
+
+ title = 'Ад гуманитария';
+
+ description = 'Все "тся" заменяются на "ться", а все "ться" на "тся"';
settings = new HumanitarianHellJokeSettings;
diff --git a/src/jokes/letter_chaos.ts b/src/jokes/letter_chaos.ts
index 0defdda..771e023 100644
--- a/src/jokes/letter_chaos.ts
+++ b/src/jokes/letter_chaos.ts
@@ -12,6 +12,10 @@ import { JokerClass } from "../joker";
export class LetterChaosJoke extends Joke {
id = 'letter_chaos';
+ title = 'Буквенный хаос';
+
+ description = 'Все буквы в словах, кроме первой и последней, перемешены.';
+
settings = new LetterChaosJokeSettings;
punctuationSymbols: string[] | null = null;
diff --git a/src/jokes/mirror.ts b/src/jokes/mirror.ts
index 94a098d..53cd7a1 100644
--- a/src/jokes/mirror.ts
+++ b/src/jokes/mirror.ts
@@ -10,6 +10,10 @@ import $ from "jquery";
*/
export class MirrorJoke extends Joke {
id = 'mirror';
+
+ title = 'Зеркало';
+
+ description = 'Элементы форума отражаются (по крайней мере, пытаются)';
settings = new MirrorJokeSettings;
diff --git a/src/jokes/shake.ts b/src/jokes/shake.ts
index 47e3e1d..d3542bc 100644
--- a/src/jokes/shake.ts
+++ b/src/jokes/shake.ts
@@ -11,6 +11,10 @@ import $ from "jquery";
export class ShakeJoke extends StopableJoke {
id = 'shake';
+ title = 'Форумо-трясение';
+
+ description = 'Страница форума начинает трястись';
+
settings = new ShakeJokeSettings;
private interval_id: NodeJS.Timer;
@@ -63,7 +67,7 @@ export class ShakeJoke extends StopableJoke {
}
export class ShakeJokeSettings implements JokeSettings {
- enabled: boolean = true;
+ enabled: boolean = false;
chance: number = 1;
diff --git a/src/jokes/stupid_title.ts b/src/jokes/stupid_title.ts
new file mode 100644
index 0000000..9ef6a3f
--- /dev/null
+++ b/src/jokes/stupid_title.ts
@@ -0,0 +1,312 @@
+import { Joke, JokeSettings, StopableJoke } from "../joke";
+import $ from "jquery";
+
+/**
+ * **Глупые статусы**
+ *
+ * Каждому пользователю даётся новый глупый статус.
+ *
+ * @author Kozhilya
+ */
+export class StupidTitleJoke extends Joke {
+ id = 'stupid_title';
+
+ title = 'Глупые статусы';
+
+ description = 'Каждому пользователю даётся новый глупый статус.';
+
+ settings = new StupidTitleJokeSettings;
+
+ private loadedGenders: any = null;
+
+ async loadGenders(): Promise {
+ const names = $('.pa-author a')
+ .map((_, elem) => elem.innerText).toArray()
+ .reduce((arr: string[], name: string) => {
+ if (arr.indexOf(name) < 0){
+ arr.push(name);
+ }
+ return arr
+ }, []);
+
+ const url = '/api.php?' + $.param({ method: 'users.get', fields: 'username,sex', username: names });
+ const response = await fetch(url);
+ const json = await response.json();
+
+ this.loadedGenders = {};
+
+ if (json.error) {
+ return;
+ }
+
+ json.response.users.forEach((element: any) => {
+ if (element.sex === '0') {
+ return;
+ }
+
+ this.loadedGenders[element.username] = element.sex === '1';
+ });
+ }
+
+ resolveGender(name: string): boolean {
+ if (this.settings.gender_resolver !== null) {
+ return !!this.settings.gender_resolver.call(this, name);
+ }
+
+ if (name in this.loadedGenders) {
+ return !!this.loadedGenders[name];
+ }
+
+ this.resetSeed();
+ return this.selectRandom(name, { 'm': 1, 'f': 1 }) === 'm';
+ }
+
+ async start(): Promise {
+ await this.loadGenders();
+
+ const cache: any = {};
+
+ $('.post-author').each((_, root) => {
+ const name = $('.pa-author a', root).text();
+ if (!name) {
+ return;
+ }
+
+ if (!(name in cache)) {
+ this.resetSeed();
+ cache[name] = this.generatePhrase(name);
+ }
+
+ $('.pa-title', root).text(cache[name]);
+ });
+ }
+
+ private prng(name: string, seed: number, limit: number): number {
+ /*
+ https://github.com/bryc/code/blob/master/jshash/experimental/cyrb53.js
+
+ cyrb53 (c) 2018 bryc (github.com/bryc)
+ A fast and simple hash function with decent collision resistance.
+ Largely inspired by MurmurHash2/3, but with a focus on speed/simplicity.
+ Public domain. Attribution appreciated.
+ */
+ let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
+ for (let i = 0, ch; i < name.length; i++) {
+ ch = name.charCodeAt(i);
+ h1 = Math.imul(h1 ^ ch, 2654435761);
+ h2 = Math.imul(h2 ^ ch, 1597334677);
+ }
+ h1 = Math.imul(h1 ^ (h1>>>16), 2246822507) ^ Math.imul(h2 ^ (h2>>>13), 3266489909);
+ h2 = Math.imul(h2 ^ (h2>>>16), 2246822507) ^ Math.imul(h1 ^ (h1>>>13), 3266489909);
+
+ const value = 4294967296 * (2097151 & h2) + (h1>>>0);
+
+ return Math.floor(Math.pow(Math.sin(value), 2) * limit) % limit;
+ }
+
+ private seed = 0;
+
+ private resetSeed(): void {
+ const date = new Date();
+ const time = new Date().getUTCHours() * 60 + new Date().getUTCMinutes();
+ this.seed = Math.floor(time / this.settings.change_frequency);
+
+ console.log('Seed reset:', time, this.seed);
+ }
+
+ private selectRandom(name: string, variants: StupidTitleWeightedVariants): string {
+ const elements = Object.keys(variants);
+ const totalWeight = elements.reduce((sum, element) => sum + variants[element], 0);
+ const randomWeight = this.prng(name, this.seed++, totalWeight);
+
+ let currentWeight = 0;
+ for (const element of elements) {
+ currentWeight += variants[element];
+ if (randomWeight <= currentWeight) {
+ return element;
+ }
+ }
+
+ throw new Error("Тебя быть тут не должно...");
+ }
+
+ private buildVariants(type: string, gender: boolean): StupidTitleWeightedVariants {
+ const result: StupidTitleWeightedVariants = {};
+ const variants: StupidTitleVariant = this.settings.variants[type];
+
+ for (const iterator of Object.entries(variants)) {
+ const word = iterator[0];
+ const wordSettings = iterator[1];
+
+ if (!wordSettings.enabled || (wordSettings.gender === !gender)) {
+ continue;
+ }
+
+ if (!this.settings.rude && wordSettings.rude) {
+ continue;
+ }
+
+ result[word] = wordSettings.weight;
+ }
+
+ return result;
+ }
+
+ private capitalizeFirst(str: string): string {
+ return str.charAt(0).toUpperCase() + str.slice(1);
+ }
+
+ generatePhrase(name: string): string {
+ const gender = this.resolveGender(name);
+ const prhase = this.selectRandom(name, this.settings.schemas);
+
+ return this.capitalizeFirst(prhase.replace(/\{(.+?)\}/g, (_, type: string) => {
+ const variants = this.buildVariants(type, gender);
+
+ return this.selectRandom(name, variants);
+ }));
+ }
+}
+
+export class StupidTitleJokeSettings implements JokeSettings {
+ enabled: boolean = true;
+ chance: number = 100;
+
+ /**
+ * Определение пола по имени.
+ * По умолчанию род устанавливается случайным образом
+ *
+ * Если установлено значение `null`, система попытается загрузить пол, установленный в настройках аккаунта. Если этот пол определить невозможно, то он будет выбран случайно. Эта настройка позволяет изменить это поведение, если ваш форум обладает ролевой системой, в которой пол определяется иным образом.
+ *
+ * @param name Имя персонажа
+ * @returns true, если мужской род; false, если женский род
+ */
+ gender_resolver: Function | null = null;
+
+ /**
+ * Разрешить возможно оскорбительные варианты
+ */
+ rude: boolean = false;
+
+ /**
+ * Частота смены статусов (в минутах)
+ */
+ change_frequency: number = 5;
+
+ /**
+ * Разрешённые схемы формирования статусов
+ *
+ * {adj} - прилагательное
+ * {noun} - имя существительно
+ * {verb} - глагол
+ * {adv} - наречие
+ * {place} - дополнение места
+ * {p} - знак препинания
+ */
+ schemas: StupidTitleWeightedVariants = {
+ '{adj} {noun}{p}': 10,
+ '{noun} {adj}{p}': 3,
+ '{adj}, {adj} {noun}{p}': 10,
+ '{adj} и очень {adj} {noun}{p}': 10,
+ '{adj} {noun} {verb}{p}': 10,
+ '{adj} {noun} {adv} {verb}{p}': 5,
+ '{adj} {noun} {verb} {place}{p}': 5,
+ '{adj} {noun} {adv} {verb} {place}{p}': 5,
+ };
+
+ /**
+ * Вариант слов
+ * Смотрите раздел описания скрипта, что бы настроить это поле
+ */
+ variants: StupidTitleVariants = {
+ adj: {
+ ...buildVariant(true, 'жесткий,^удачливый,низкий,знакомый,откровенный,^мирный,грамотный,вкусный,виртуальный,пышный,провинциальный,$жирный,заинтересованный,объективный,^оригинальный,пожилой,чистый,особенный,выгодный,громадный,обыкновенный,чужой,ударный,^солнечный,истинный,посторонний,южный,седой,$бесполезный,острый,пестрый,формальный,повседневный,невозможный,$жалкий,целый,$глупый,последний,выходной,серьезный,подземный,небесный,любимый,былой,семейный,далекий,северный,суровый,гигантский,иностранный,родной,конструктивный,показательный,нужный,чрезвычайный,гордый,банальный,открытый,независимый,запасной,мстительный,грубый,алчный,мелочный,хладнокровный,спокойный,терпеливый,креативный,активный,адекватный,застенчивый,опрятный,безнадёжный,уверенный в себе,доброжелательный,ласковый,робкий,воспитанный,самовлюблённый,деятельный,двуличный,верный'),
+ ...buildVariant(false, 'жесткая,^удачливая,низкая,знакомая,откровенная,^мирная,грамотная,вкусная,виртуальная,пышная,провинциальная,$жирная,заинтересованная,объективная,^оригинальная,пожилая,чистая,особенная,выгодная,громадная,обыкновенная,чужая,ударная,^солнечная,истинная,посторонняя,южная,седая,$бесполезная,острая,пестрая,формальная,повседневная,невозможная,$жалкая,целая,$глупая,последняя,выходная,серьезная,подземная,небесная,любимая,былая,семейная,далекая,северная,суровая,гигантская,иностранная,родная,конструктивная,показательная,нужная,чрезвычайная,гордая,банальная,открытая,независимая,запасная,мстительная,грубая,алчная,мелочная,хладнокровная,спокаяная,терпеливая,креативная,активная,адекватная,застенчивая,опрятная,безнадёжная,уверенная в себе,доброжелательная,ласковая,робкая,воспитанная,самовлюблённая,деятельная,двуличная,верная'),
+ },
+ noun: {
+ ...buildVariant(null, ''),
+
+ ...buildVariant(true, 'орёл, ниндзя, манекен, пузырь, бродяга, рыцарь, кентавр, зайц, диван, наследник, сын, джентльмен, басурманин, карасик, $злоумышленник, $раб, хоббит, одиночка, отец, кролик, бруталище, дворф, актёр, %дед, спортсмен, $балбес, %мальчег, лев, ёж, организм, монах, хоббит, борец, тигр, монстр, массажист, мачо, эльф, ледник, жук, кот, зомби, проказник, дракон, носорог, волк, лесоруб, кит, %дедушка, динозавр, бард, кулак, принц, %сынок, мальчик, товарищ, ёжик, сурикат'),
+
+ ...buildVariant(false, 'заноза, волторна, $личинка, детка, %бабушка, простуда, черепашка, мышь, волчица, деревяшка, пиранья, каруселька, красотка, леди, %дочка, орлица, принцесса, $ведьма, травка, актриса, наследница, %деффачка, горгулья, белочка, акула, машинка, конфетка, знаменитость, косточка, спортсменка, панда, мать, львица, эльфийка, $злоумышленница, чертовка, дворфиха, кошка, $рабыня, скамеечка, дочь, монашка, бактерия, рыбка, бусинка, $бабка, девочка, валькирия, килька, бабочка, милашка, крышка, тигрица, рыбка, русалочка'),
+ },
+ verb: {
+ ...buildVariant(null, 'встаёт, снимает, приказывает, указывает, собирается, чувствует опасность, орёт, болеет, поздравляет, освобождает, поздравляет, бежит, берёт и делает, молчит, радуется, прощает, читает, снимает, снимается в фильме, что-то скрывает, думает, имеет план и придерживается его, тянет, работает, меняется, ловит, отказаться куда-то идти, заходит, замирает, оформляет, нарушает, отказывается что-то делать, шепчет, снимается в сериале, скрывается, учит, спрашивает, идёт, отказывается вставать, прибывает, отказывается, забывает, получает, плывёт, отвечает, поглядывает, говорит, просыпается, соединяет, оглядывается'),
+
+ ...buildVariant(true, 'женится, женился, успел, не успел, смог, скрылся, заработался, познал дзен, присел, обиделся, проснулся, взял и сделал, додумался, не спал'),
+
+ ...buildVariant(false, 'выходит замуж, вышла замуж, успела, не успела, смогла, скрылась, заработалась, познала дзен, присела, обиделась, проснулась, взяла и сделала, додумалась, не спала'),
+ },
+ place: {
+ ...buildVariant(null, 'где-то, в пустыне, на пляже, под дождём, %под проливным дождём, в квартире, %в тёплой и уютной квартире, в космосе, в театре, в филармонии, на заснеженной вершине, на конференции, на форуме, в очереди, в лесу, в джунглях, под водой'),
+ },
+ adv: {
+ ...buildVariant(null, 'счастливо, уныло, грустно, позже, немедленно, регулярно, вновь, опять, уже, внезапно, предсказуемо, медленно, быстро, %молниеносно, $грубо, безрассудно, осмысленно, расчётливо'),
+ },
+ p: {
+ '': new StupidTitleSetting({ weight: 10 }),
+ '.': new StupidTitleSetting({ weight: 10 }),
+ '...': new StupidTitleSetting({ weight: 5 }),
+ '!': new StupidTitleSetting({ weight: 5 }),
+ '!!!': new StupidTitleSetting({ weight: 2 }),
+ '?': new StupidTitleSetting({ weight: 2 }),
+ '?!': new StupidTitleSetting({ weight: 1 }),
+ },
+ };
+
+}
+
+interface StupidTitleVariants {
+ [key: string]: StupidTitleVariant
+}
+
+interface StupidTitleVariant {
+ [key: string]: StupidTitleSetting
+}
+
+interface StupidTitleWeightedVariants {
+ [key: string]: number
+}
+
+class StupidTitleSetting {
+ weight: number = 10;
+
+ enabled: boolean = true;
+
+ rude: boolean = false;
+
+ gender: boolean|null = null;
+
+ constructor(options: any = new Object) {
+ for (const iterator of Object.entries(options)) {
+ if (iterator[0] in this) {
+ ( this)[iterator[0]] = iterator[1];
+ }
+ }
+ }
+}
+
+function buildVariant(gender: boolean|null, input: string): StupidTitleVariant
+{
+ const result: StupidTitleVariant = {};
+ const words = input.split(',').map(word => word.trim());
+
+ words.forEach(word => {
+ const clean_word = word.replace(/[^А-ЯЁа-яёA-Za-z\- ]/g, '');
+
+ if (clean_word === '') {
+ return;
+ }
+
+ result[clean_word] = new StupidTitleSetting({
+ gender,
+ rude: word.indexOf('$') >= 0,
+ enabled: word.indexOf('!') < 0,
+ weight: 10 * ((word.indexOf('$') >= 0) ? 0.5 : 1) * ((word.indexOf('%') >= 0) ? 0.5 : 1) * ((word.indexOf('^') >= 0) ? 2 : 1)
+ });
+ });
+
+ return result;
+}
+
+(window as any).BuildStupdiTitleVariant = buildVariant;
\ No newline at end of file
diff --git a/src/main.ts b/src/main.ts
index 9636927..f364710 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -5,9 +5,9 @@ import { HumanitarianHellJoke } from "./jokes/humanitarian_hell";
import { MirrorJoke } from "./jokes/mirror";
import { CarnivalJoke } from "./jokes/carnival";
import { CrazyLettersJoke } from "./jokes/crazy_letters";
-import { CrazyPunctuationJoke } from "./jokes/crazy_punctuation";
import { LetterChaosJoke } from "./jokes/letter_chaos";
-import { CursorEffectsJoke } from "./jokes/cursor"
+import { CursorEffectsJoke } from "./jokes/cursor";
+import { StupidTitleJoke } from "./jokes/stupid_title";
(window as any).AprilJokes = ((jokes: JokeClassList): JokerClass => {
const cl = new JokerClass();
@@ -16,7 +16,7 @@ import { CursorEffectsJoke } from "./jokes/cursor"
cl.add(new joke());
});
- cl.regularStart();
+ $(() => cl.regularStart());
return cl;
})([
@@ -25,9 +25,9 @@ import { CursorEffectsJoke } from "./jokes/cursor"
MirrorJoke,
CarnivalJoke,
CrazyLettersJoke,
- CrazyPunctuationJoke,
LetterChaosJoke,
CursorEffectsJoke,
+ StupidTitleJoke,
]);
interface JokeClassList extends Array Joke> {}
\ No newline at end of file
diff --git a/src/ui.ts b/src/ui.ts
new file mode 100644
index 0000000..7d67cdb
--- /dev/null
+++ b/src/ui.ts
@@ -0,0 +1,62 @@
+///
+import { JokerClass } from "./joker";
+import $ from "jquery";
+import button_html from "./html/button.html";
+import styles_html from "./html/styles.html";
+import window_html from "./html/window.html";
+
+
+export class JokerUI {
+ joker: JokerClass;
+
+ constructor(joker: JokerClass) {
+ this.joker = joker;
+
+ $('link[rel="stylesheet"]').eq(0).before(styles_html);
+
+ const button = this.button;
+
+ button.hide();
+ (button).mybbModal({
+ title: "Настройки первоапрельских шуток",
+ content: () => this.render(),
+ closer: true,
+ noscroll: true,
+ escClose: true,
+ });
+ }
+
+ get button() {
+ let button = $('.april-fools__button');
+
+ if (button.length === 0) {
+ button = $(button_html).appendTo('body');
+ }
+
+ return button;
+ }
+
+ show() {
+ this.button.show();
+ }
+
+ render(): JQuery {
+ const result: JQuery = $('').html(window_html);
+ const row_template = $('#april-fools__window-row', result).html();
+ const joke_container = $('.april-fools__jokes', result);
+
+ for (const joke of Object.values(this.joker.jokes)) {
+ const row = $(eval('`' + row_template + '`'));
+ $('input[type="checkbox"]', row).prop('checked', joke.settings.enabled)
+ .on('click', e => this.joker.toggleJoke(joke.id, (e).currentTarget.checked));
+ joke_container.append(row);
+ }
+
+ $('button', result).on('click', async () => {
+ await this.joker.clearUserStates();
+ window.location.reload();
+ });
+
+ return $('.april-fools__content', result);
+ }
+}
\ No newline at end of file
diff --git a/webpack.config.js b/webpack.config.js
index 4b2ce9b..3a95cfb 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -40,6 +40,10 @@ const config = {
test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i,
type: "asset",
},
+ {
+ test: /\.html$/i,
+ loader: "html-loader",
+ },
// Add your rules for custom modules here
// Learn more about loaders from https://webpack.js.org/loaders/
],
@@ -50,6 +54,9 @@ const config = {
externals: {
jquery: 'jQuery',
},
+ watchOptions: {
+ aggregateTimeout: 20,
+ },
};
module.exports = () => {