User:MonkeysHK/common.js

/** FireworkEffects * Script for displaying fireworks on New Year. */

/** Attribution: Script Referenced: * DHTML Snowstorm! JavaScript-based snow for web pages by Scott Schiller * Copyright (c) 2007, Scott Schiller. All rights reserved. * Code provided under the BSD License * https://github.com/scottschiller/Snowstorm/blob/master/license.txt */

/*jslint nomen: true, plusplus: true, sloppy: true, vars: true, white: true */ /*global window, document, navigator, clearInterval, setInterval */

window.fireworkShow = (new function { // jshint ignore:line    var defaults = {        autoStart: false, // Whether the firework should start automatically or not.        excludeMobile: true, // The flakes are likely to be bad news for mobile phones' CPUs (and batteries.) Enable at your own risk.        rocketsMax: 3, // Make this flakesMaxActive/16 - 2 (warning: this ratio hasn't been tested so don't trust it)        flakesMaxActive: 72, // Limit amount of firework flakes firing at once (less = lower CPU use)        animationInterval: 35, // Theoretical "milliseconds per frame" measurement. 20 = fast + smooth, but high         // CPU use. 50 = more conservative, but slower        useGPU: true, // Enable transform-based hardware acceleration, reduce CPU load.        className: null, // CSS class name for further customization on flake elements        flakeBottom: null, // Integer for Y axis limit, 0 or null for "full-screen" effect starCharacter: '&bull;', // &bull; = bullet, &middot, is square on some systems etc.       targetElement: null, // element which flakes will be appended to (null = document.body) - can be an element // ID eg. 'myDiv', or a DOM node reference useFadeEffect: true, // When recycling fallen flakes (or rarely, when falling), have it "melt" and fade out // if browser supports it       useTwinkleEffect: false, // Allow flakes to randomly "flicker" in and out of view while falling usePositionFixed: true, // true = flakes does not shift vertically when scrolling. May increase CPU load, // disabled by default - if enabled, used only where supported usePixelPosition: false, // Whether to use pixel values for flakes top/left vs. percentages. Auto-enabled if        // body is position:relative or targetElement is specified.

// --- less-used bits --- freezeOnBlur: true, // Only show flakes when the window is in focus (foreground.) Saves CPU. flakeLeftOffset: 0, // Left margin/gutter space on edge of container (eg. browser window.) Bump up these values if seeing horizontal scrollbars. flakeRightOffset: 0, // Right margin/gutter space on edge of container flakeWidth: 8, // Max pixel width reserved for flake element flakeHeight: 8, // Max pixel height reserved for flake element vMaxX: 5, // Maximum X velocity range for a flake vMaxY: 4, // Maximum Y velocity range for a flake zIndex: 2, // CSS stacking order applied to each firework flake windOffset: 1, windMultiplier: 2, flakeTypes: 6, rocketTypes: 3, // 0: Single color. 1: 2 colors. 2: random colors. All of these may be subject to change };

var config = Object.assign(defaults, window.fireworkShow || {});

Object.assign(this, config);

// --- "No user-serviceable parts inside" past this point, yadda yadda --- var show = this, features, // UA sniffing and backCompat rendering mode checks for fixed position, etc.       isIE = navigator.userAgent.match(/msie/i), isIE6 = navigator.userAgent.match(/msie 6/i), isMobile = navigator.userAgent.match(/mobile|opera m(ob|in)/i), isBackCompatIE = (isIE && document.compatMode === 'BackCompat'), noFixed = (isBackCompatIE || isIE6), screenX = null, screenX2 = null, screenY = null, scrollY = null, docHeight = null, vRndX = null, vRndY = null, fixedForEverything = false, targetElementIsRelative = false, opacitySupported = (function {            try {                document.createElement('div').style.opacity = '0.5';            } catch (e) {                return false;            }            return true;        }), didInit = false, docFrag = document.createDocumentFragment;

features = (function {        var getAnimationFrame;

/**        * hat tip: paul irish * https://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ * https://gist.github.com/838785 */       function timeoutShim(callback) { window.setTimeout(callback, 1000 / (show.animationInterval || 20)); }

var _animationFrame = (window.requestAnimationFrame ||           window.webkitRequestAnimationFrame ||            window.mozRequestAnimationFrame ||            window.oRequestAnimationFrame ||            window.msRequestAnimationFrame ||            timeoutShim);

// apply to window, avoid "illegal invocation" errors in Chrome getAnimationFrame = _animationFrame ? function { return _animationFrame.apply(window, arguments); } : null;

var testDiv = document.createElement('div');

function has(prop) { // test for feature support var result = testDiv.style[prop]; return result !== undefined ? prop : null; }

// note local scope. var localFeatures = { transform: { ie: has('-ms-transform'), moz: has('MozTransform'), opera: has('OTransform'), webkit: has('webkitTransform'), w3: has('transform'), prop: null // the normalized property value },

getAnimationFrame: getAnimationFrame };

localFeatures.transform.prop = (           localFeatures.transform.w3 ||            localFeatures.transform.moz ||            localFeatures.transform.webkit ||            localFeatures.transform.ie ||            localFeatures.transform.opera        );

testDiv = null; return localFeatures; });

this.timer = null; this.flakes = []; this.rockets = []; this.disabled = false; this.active = false; this.meltFrameCount = 20; this.meltFrames = [];

this.setXY = function (o, x, y) { if (!o) return false;

if (show.usePixelPosition || targetElementIsRelative) { o.style.left = (x - show.flakeWidth) + 'px'; o.style.top = (y - show.flakeHeight) + 'px'; } else if (noFixed) { o.style.right = (100 - (x / screenX * 100)) + '%'; // avoid creating vertical scrollbars o.style.top = (Math.min(y, docHeight - show.flakeHeight)) + 'px'; } else { if (!show.flakeBottom) { // if not using a fixed bottom coordinate... o.style.right = (100 - (x / screenX * 100)) + '%'; o.style.bottom = (100 - (y / screenY * 100)) + '%'; } else { // absolute top. o.style.right = (100 - (x / screenX * 100)) + '%'; o.style.top = (Math.min(y, docHeight - show.flakeHeight)) + 'px'; }       }    };

this.events = (function {        var old = !window.addEventListener && window.attachEvent,            slice = Array.prototype.slice,            evt = {                add: old ? 'attachEvent' : 'addEventListener',                remove: old ? 'detachEvent' : 'removeEventListener',            };

function getArgs(oArgs) { var args = slice.call(oArgs), len = args.length; if (old) { args[1] = 'on' + args[1]; // prefix if (len > 3) { args.pop; // no capture }           } else if (len === 3) { args.push(false); }           return args; }

function apply(args, sType) { var element = args.shift, method = [evt[sType]]; if (old) { element[method](args[0], args[1]); } else { element[method].apply(element, args); }       }

function addEvent { apply(getArgs(arguments), 'add'); }

function removeEvent { apply(getArgs(arguments), 'remove'); }

return { add: addEvent, remove: removeEvent };   });

function rnd(n, min) { if (isNaN(min)) min = 0;

return (Math.random * n) + min; }

function plusMinus(n) { return parseInt(rnd(2), 10) === 1 ? n * -1 : n;   }

this.scrollHandler = function { var i;       // "attach" firework flakes to bottom of window if no absolute bottom value was given scrollY = (show.flakeBottom ? 0 : parseInt(window.scrollY || document.documentElement.scrollTop || (noFixed ? document.body.scrollTop : 0), 10)); if (isNaN(scrollY)) scrollY = 0; // Netscape 6 scroll fix /* if (!fixedForEverything && !show.flakeBottom && show.flakes) { for(i = 0; i < show.flakes.length; i++) { if (show.flakes[i].active === 0) { show.flakes[i].stick; }       	}        } */    };

this.resizeHandler = function { if (window.innerWidth || window.innerHeight) { screenX = window.innerWidth - 16 - show.flakeRightOffset; screenY = (show.flakeBottom || window.innerHeight); } else { screenX = (document.documentElement.clientWidth || document.body.clientWidth || document.body.scrollWidth) - (!isIE ? 8 : 0) - show.flakeRightOffset; screenY = show.flakeBottom || document.documentElement.clientHeight || document.body.clientHeight || document.body.scrollHeight; }       docHeight = document.body.offsetHeight; screenX2 = parseInt(screenX / 2, 10); };

this.resizeHandlerAlt = function { screenX = show.targetElement.offsetWidth - show.flakeRightOffset; screenY = show.flakeBottom || show.targetElement.offsetHeight; screenX2 = parseInt(screenX / 2, 10); docHeight = document.body.offsetHeight; };

this.freeze = function { // pause animation if (!show.disabled) { show.disabled = 1; } else { return false; }       show.timer = null; };

this.resume = function { if (show.disabled) { show.disabled = 0; } else { return false; }       show.timerInit; };

this.toggleFirework = function { if (!show.flakes.length) { // first run show.start; } else { show.active = !show.active; if (show.active) { show.show; show.resume; } else { show.stop; show.freeze; }       }    };

this.stop = function { var i;       this.freeze; for (i = 0; i < this.flakes.length; i++) { this.flakes[i].o.style.display = 'none'; }       show.events.remove(window, 'scroll', show.scrollHandler); show.events.remove(window, 'resize', show.resizeHandler); if (show.freezeOnBlur) { if (isIE) { show.events.remove(document, 'focusout', show.freeze); show.events.remove(document, 'focusin', show.resume); } else { show.events.remove(window, 'blur', show.freeze); show.events.remove(window, 'focus', show.resume); }       }    };

this.show = function { var i;       for (i = 0; i < this.flakes.length; i++) { this.flakes[i].o.style.display = 'block'; }   };

this.fireworkStar = function { var s = this; this.x = parseInt(rnd(screenX - 20), 10); this.y = -rnd(screenY) - 12; this.vX = null; this.vY = null; this.active = 0; this.fontSize = (30 + Math.floor(Math.random * 5) * 2); this.o = document.createElement('div'); this.o.innerHTML = show.starCharacter; if (show.className) { this.o.setAttribute('class', show.className); }       this.o.style.position = (fixedForEverything ? 'fixed' : 'absolute'); if (show.useGPU && features.transform.prop) { // GPU-accelerated flakes. this.o.style[features.transform.prop] = 'translate3d(0px, 0px, 0px)'; }       this.o.style.color = '#fff'; this.o.style.width = show.flakeWidth + 'px'; this.o.style.height = show.flakeHeight + 'px'; this.o.style.fontFamily = 'arial,verdana'; this.o.style.cursor = 'default'; this.o.style.overflow = 'hidden'; this.o.style.fontWeight = 'normal'; this.o.style.zIndex = show.zIndex; docFrag.appendChild(this.o);

this.refresh = function { if (isNaN(s.x) || isNaN(s.y)) { // safety check return false; }           show.setXY(s.o, s.x, s.y); };

this.stick = function { if (noFixed || (storm.targetElement !== document.documentElement && storm.targetElement !== document.body)) { s.o.style.top = (screenY + scrollY - storm.flakeHeight) + 'px'; } else if (storm.flakeBottom) { s.o.style.top = storm.flakeBottom + 'px'; } else { s.o.style.display = 'none'; s.o.style.top = 'auto'; s.o.style.bottom = '0%'; s.o.style.position = 'fixed'; s.o.style.display = 'block'; }       };

this.move = function { s.x += s.vX; s.y += s.vY; s.vY += 0.5; if (s.vX !== 0) { s.vX -= 0.1 * s.vX / Math.abs(s.vX); }           s.refresh; if (s.x >= screenX || s.x < 0) { // X-axis scroll check s.active = 0; s.o.style.display = 'none'; //console.log('X kill'); }           //console.log('Y = ' + s.y.toString + '. show.height = ' + show.height.toString); if (s.y >= show.height) { s.active = 0; s.o.style.display = 'none'; //console.log('Y kill: Y = ' + s.y.toString + '. show.height = ' + show.height.toString); }       };

this.setOpacity = function (o, opacity) { if (!opacitySupported) { return false; }           o.style.opacity = opacity; };

this.recycle = function (x, y, vX, vY, color) { s.o.style.display = 'none'; s.o.style.position = (fixedForEverything ? 'fixed' : 'absolute'); s.o.style.bottom = 'auto'; s.vX = vX; s.vY = vY; s.o.style.color = color; s.setOpacity(s.o, 1); s.o.style.padding = '0px'; s.o.style.margin = '0px'; s.o.style.fontSize = s.fontSize + 'px'; s.o.style.lineHeight = (show.flakeHeight + 2) + 'px'; s.o.style.textAlign = 'center'; s.o.style.verticalAlign = 'baseline'; s.x = x;           s.y = y;            s.refresh; s.o.style.display = 'block'; s.active = 1; };

this.recycle; // set up x/y coords etc.       this.refresh; s.active = 0; s.setOpacity(s.o, 0); };

this.fireworkRocket = function (type) { var r = this; this.type = type; this.x = parseInt(rnd(screenX - 20), 10); this.y = screenY; this.vX = null; // x velocity this.vY = null; // y velocity this.tY = parseInt(rnd(50), 10) + screenY / 4; // target Y value for rocket to explode at. this.active = 1; this.fontSize = (25); this.o = document.createElement('img'); this.o.src = 'https://static.wikia.nocookie.net/hypixel-skyblock/images/2/25/Firework_Rocket.png/revision/latest/scale-to-width-down/160?cb=20210615224504'; if (show.className) { this.o.setAttribute('class', show.className); }       //this.o.style.color = '#fff'; //this.o.style.position = (fixedForEverything ? 'fixed' : 'absolute'); if (show.useGPU && features.transform.prop) { // GPU-accelerated flakes. this.o.style[features.transform.prop] = 'translate3d(0px, 0px, 0px)'; }       this.o.style.width = '40px'; this.o.style.height = '40px'; this.o.style.zIndex = show.zIndex - 1; docFrag.appendChild(this.o);

this.refresh = function { if (isNaN(r.x) || isNaN(r.y)) { // safety check return false; }           show.setXY(r.o, r.x, r.y); };

this.move = function { r.x += r.vX; r.y -= r.vY; r.vX *= 3 / 4; r.vY *= 7 / 8; r.refresh; if (r.y <= r.targetY || r.vY <= 3) { r.explode(r.x, r.y); }       };

this.explode = function (x, y) { r.active = Math.floor(Math.random * 256) + 128 + 1; r.o.style.display = 'none'; //console.log('Explode! Type: ' + r.type.toString); var inactiveStars = []; var i;           for (i = 0; i < show.flakes.length; i++) { if (show.flakes[i].active === 0) { inactiveStars[inactiveStars.length] = i;               } }           if (inactiveStars.length < 16) { console.log('Add more flakes at initialization.'); }           if (type === 0) { r.color1 = 'rgb(' + Math.floor(Math.random * 256).toString + ',' + Math.floor(Math.random * 256).toString + ',' + Math.floor(Math.random * 256).toString + ')'; for (i = 0; i < 16; i++) { show.flakes[inactiveStars[i]].recycle(x, y, 10 * Math.sin(i * Math.PI / 8), 10 * Math.cos(i * Math.PI / 8) - 11, r.color1); }           } else if (type === 1) { r.colors = [Math.floor(Math.random * 256), Math.floor(Math.random * 256), Math.floor(Math.random * 256)]; // A thing r.color1 = 'rgb(' + r.colors[0].toString + ',' + r.colors[1].toString + ',' + r.colors[2].toString + ')'; r.color2 = 'rgb(' + (255 - r.colors[0]).toString + ',' + (255 - r.colors[1]).toString + ',' + (255 - r.colors[2]).toString + ')'; for (i = 0; i < 16; i++) { show.flakes[inactiveStars[i]].recycle(x, y, 10 * Math.sin(i * Math.PI / 8), 10 * Math.cos(i * Math.PI / 8) - 11, (i % 2 === 1 ? r.color1 : r.color2)); }           } else { for (i = 0; i < 16; i++) { show.flakes[inactiveStars[i]].recycle(x, y, 10 * Math.sin(i * Math.PI / 8), 10 * Math.cos(i * Math.PI / 8) - 11, 'rgb(' + Math.floor(Math.random * 256).toString + ',' + Math.floor(Math.random * 256).toString + ',' + Math.floor(Math.random * 256).toString + ')'); }           }        };

this.setVelocities = function { r.vX = rnd(8, 0.1); r.vY = Math.floor(Math.random * 32) + 65; r.targetY = parseInt(rnd(50), 10) + screenY / 4; };

this.setOpacity = function (o, opacity) { if (!opacitySupported) { return false; }           o.style.opacity = opacity; };

this.recycle = function { r.o.style.display = 'none'; r.o.style.position = (fixedForEverything ? 'fixed' : 'absolute'); r.o.style.bottom = 'auto'; r.setVelocities; r.setOpacity(r.o, 1); r.o.style.padding = '0px'; r.o.style.margin = '0px'; //r.o.style.fontSize = r.fontSize + 'px'; //r.o.style.lineHeight = (show.flakeHeight + 2) + 'px'; //r.o.style.textAlign = 'center'; r.o.style.verticalAlign = 'baseline'; r.x = parseInt(rnd(screenX - show.flakeWidth - 20), 10); r.y = screenY; r.refresh; r.o.style.display = 'block'; r.active = 1; };

this.recycle; // set up x/y coords etc.       this.refresh; };

this.fireworks = function { var active = 0, i, j;       for (i = 0, j = show.flakes.length; i < j; i++) { if (show.flakes[i].active === 1) { show.flakes[i].move; active++; }       }        for (i = 0, j = show.rockets.length; i < j; i++) { if (show.rockets[i].active === 1) { show.rockets[i].move; } else if (show.rockets[i].active === 2) { show.rockets[i].recycle; } else { show.rockets[i].active--; }       }        //console.log(show.rockets); if (show.timer) { features.getAnimationFrame(show.fireworks); }   };

this.createRockets = function (limit) { var i;       for (i = 0; i < limit; i++) { show.rockets[show.rockets.length] = new show.fireworkRocket(Math.floor(Math.random * 3)); }       show.targetElement.appendChild(docFrag); };

this.createStars = function (limit) { var i;       for (i = 0; i < limit; i++) { show.flakes[show.flakes.length] = new show.fireworkStar; }       show.targetElement.appendChild(docFrag); };

this.timerInit = function { show.timer = true; show.fireworks; };

this.init = function { var i;       for (i = 0; i < show.meltFrameCount; i++) { show.meltFrames.push(1 - (i / show.meltFrameCount)); }       show.createRockets(show.rocketsMax); // create initial batch show.createStars(show.flakesMaxActive); show.events.add(window, 'resize', show.resizeHandler); show.events.add(window, 'scroll', show.scrollHandler); if (show.freezeOnBlur) { if (isIE) { show.events.add(document, 'focusout', show.freeze); show.events.add(document, 'focusin', show.resume); } else { show.events.add(window, 'blur', show.freeze); show.events.add(window, 'focus', show.resume); }       }        show.resizeHandler; show.scrollHandler; show.animationInterval = Math.max(20, show.animationInterval); show.timerInit; show.height = Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight); };

this.start = function (bFromOnLoad) { if (!didInit) { didInit = true; } else if (bFromOnLoad) { // already loaded and running return true; }       if (typeof show.targetElement === 'string') { var targetID = show.targetElement; show.targetElement = document.getElementById(targetID); if (!show.targetElement) { throw new Error('Fireworkshow: Unable to get targetElement "' + targetID + '"'); }       }        if (!show.targetElement) { show.targetElement = (document.body || document.documentElement); }       if (show.targetElement !== document.documentElement && show.targetElement !== document.body) { // re-map handler to get element instead of screen dimensions show.resizeHandler = show.resizeHandlerAlt; //and force-enable pixel positioning show.usePixelPosition = true; }       show.resizeHandler; // get bounding box elements show.usePositionFixed = (show.usePositionFixed && !noFixed && !show.flakeBottom); // whether or not position:fixed is to be used if (window.getComputedStyle) { // attempt to determine if body or user-specified flake parent element is relatlively-positioned. try { targetElementIsRelative = (window.getComputedStyle(show.targetElement, null).getPropertyValue('position') === 'relative'); } catch (e) { // oh well targetElementIsRelative = false; }       }        fixedForEverything = show.usePositionFixed; if (screenX && screenY && !show.disabled) { show.init; show.active = true; }   };

function doDelayedStart { window.setTimeout(function {            show.start(true);        }, 20); // event cleanup show.events.remove(isIE ? document : window, 'mousemove', doDelayedStart); }

function doStart { if ((!show.excludeMobile || !isMobile)) { doDelayedStart; }       // event cleanup show.events.remove(window, 'load', doStart); }

function during(date1, date2) { return date1.getFullYear == date2.getFullYear && date1.getMonth == date2.getMonth && date1.getDate == date2.getDate; }

function celebrateDates(year) { var cny_lookup = { 2021: "12 Feb 2021", 2022: "1 Feb 2022", 2023: "22 Jan 2023", 2024: "10 Feb 2024", 2025: "29 Jan 2025", 2026: "17 Feb 2026", 2027: "6 Feb 2027", 2028: "26 Jan 2028", 2029: "13 Feb 2029", 2030: "3 Feb 2030", 2031: "23 Jan 2031", 2032: "11 Feb 2032", };       return [ new Date("1 Jan " + year), // new year (Gregorian calendar) new Date(cny_lookup[year]), // new year (Chinese calendar) ];   }

function waitTillCelebrate { var date = new Date, thisyear = celebrateDates(date.getFullYear), nextyear = celebrateDates(date.getFullYear + 1);

var promise = $.Deferred; if (show.autoStart || during(date, thisyear[0]) || during(date, thisyear[1])) { console.log("[FireworkEffects] Starting celebration!"); return promise.resolve; } else { var one = (date < thisyear[0]) && thisyear[0] || nextyear[0], two = (date < thisyear[1]) && thisyear[1] || nextyear[1], till = Math.min(one - date, two - date); setTimeout(function {                promise.resolve;            }, till); console.log("[FireworkEffects] " + till + "ms till celebration."); }       return promise; }

// hooks for starting the firework show if (mw.config.get("wgAction") !== "view") return; waitTillCelebrate.then(function {        if (document.readyState === 'complete') {            doStart;        } else {            show.events.add(window, 'load', doStart, false);        }    }); });