User:TheTrueShaman/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 */

var height = Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight); var width = Math.max(document.body.scrollWidth, document.body.offsetWidth, document.documentElement.clientWidth, document.documentElement.scrollWidth, document.documentElement.offsetWidth);

console.log("Height:" + height.toString); console.log("Width:" + width.toString);

var scriptLoaded = !!(window.fireworkShow && window.fireworkShow.Loaded);

window.fireworkShow = window.fireworkShow || {}; window.fireworkShow.Loaded = true;

mw.loader.using(["mediawiki.util", "mediawiki.Uri"], function {	if ((mw.config.get("wgAction") !== "view") || scriptLoaded) return;

console.log("[FireworkEffects] Script Loading.");

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: 5, // Make this flakesMaxActive/16 - 2 (warning: this ratio hasn't been tested so don't trust it)			flakesMaxActive: 50, // 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: "fireworkeffect-flakes", // 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.

// Specific Variables For Firework Custom Script celebrateOnNewYear: true, // If enabled, even if autoStart is turned off, celebrateOnSpecificDates: [], // Sets custom dates in the format of, for example, "31 December". On those days, the firework show will start automatically even if this.autoStart is turned off fireworksHideToBackground: true, // If enabled, this.zIndex will be set to -1 after the timeout fireworksHideTimeout: 4000, // Sets the timeout after which the firework will hide in the background

// --- 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 };

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; canvas = document.createElement("canvas"); canvas.style.height = height; canvas.style.width = width;

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"; }		};

//rocket = "https://static.wikia.nocookie.net/hypixel-skyblock/images/2/25/Firework_Rocket.png/revision/latest/scale-to-width-down/160?cb=20210615224504";

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.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 (show.fireworksHideToBackground) { setTimeout(function {					show.zIndex = -1;					setTimeout(function  { var res = $("." + show.className).css("z-index", -2); console.log("[FireworkEffects] Show opening is over. The firework show will continue in background.", res.length); }, 1000);				}, show.fireworksHideTimeout); }			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(date0, date1) { return date0.getFullYear == date1.getFullYear && date0.getMonth == date1.getMonth && date0.getDate == date1.getDate; }

function duringAny(date0, dates) { return dates.some(function (d) {				return during(date0, d);			}); }

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 show.celebrateOnSpecificDates.map(function (d) {				return new Date(d + " " + year);			}).concat(show.celebrateOnNewYear && [				new Date("1 January " + 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 || duringAny(date, thisyear)) { console.log("[FireworkEffects] Starting celebration!"); promise.resolve(true); } else if (!thisyear.length) { promise.resolve(false); } else { var till = Math.min.apply(this, thisyear.map(function (v, i) { return ((date < v) && v || nextyear[i]) - date; }));				if (till < 86400000) { // Celebration in less than 1 day setTimeout(function {						console.log("[FireworkEffects] Starting celebration!");						promise.resolve(true);					}, till); } else promise.resolve(false); console.log("[FireworkEffects] " + till + "ms till celebration."); }			return promise; }

// hooks for starting the firework show waitTillCelebrate.then(function (toCelebrate) {			if (toCelebrate) {				if (document.readyState === "complete")					doStart;				else					show.events.add(window, "load", doStart, false);			}		}); }); });