Mudanças entre as edições de "MediaWiki:Common.js"
Ir para navegação
Ir para pesquisar
Linha 1: | Linha 1: | ||
/* Códigos JavaScript aqui colocados serão carregados por todos aqueles que acessarem alguma página deste wiki */ | /* Códigos JavaScript aqui colocados serão carregados por todos aqueles que acessarem alguma página deste wiki */ | ||
var | // Countdown timer that is accurate to the second and accounts for Daylight Savings Time (DST) unless | ||
// otherwise specified. | |||
// Example: a countdown of loopTime=30 seconds and a delayTime=10 seconds will have a delay timer start a | |||
// count down from 9 to 0 seconds and the actual timer start a count down from 19 to 0 seconds. | |||
// Reference: https://www.w3schools.com/howto/howto_js_countdown.asp | |||
// All of these CSS classes must be present on page in order for countdown timer to function | |||
const COUNTDOWN_CLASSES = ["seedDate", "bText", "bDelayText", "timer", | |||
"aText", "aDelayText", "loopTime", "loopLimit", "endText", | |||
"delayTime", "delayDisplay", "dst", "dateFormat", "dateLabels"]; | |||
Object.freeze(COUNTDOWN_CLASSES); | |||
const BARO_COUNTDOWN_CLASSES = { | |||
PC: "pcPlanet", | |||
PS4: "ps4Planet", | |||
XB1: "xb1Planet", | |||
NSW: "nswPlanet" | |||
}; | |||
Object.freeze(BARO_COUNTDOWN_CLASSES); | |||
// All dictionaries related to time use the abbreviated time units as keys | |||
const TIME_UNIT_ABBR = { | |||
Year: "Y", | |||
Month: "M", | |||
Day: "D", | |||
Hour: "h", | |||
Minute: "m", | |||
Second: "s" | |||
}; | |||
Object.freeze(TIME_UNIT_ABBR); | |||
const TIME_IN_MILLISECONDS = { | |||
Y: 31536000000, // assuming leap years are irrelevant (milliseconds in a day * 365) | |||
M: 2628000000, // average milliseconds per month (milliseconds in a year / 12) | |||
D: 86400000, | |||
h: 3600000, | |||
m: 60000, | |||
s: 1000 | |||
}; | |||
Object.freeze(TIME_IN_MILLISECONDS); | |||
// Mapping relay names to their respective planet | |||
var relayDict = { | |||
Mercury: "Larunda", | |||
Venus: "Vesper", | |||
Earth: "Strata", | |||
Saturn: "Kronia", | |||
Pluto: "Orcus", | |||
Europa: "Leonov", | |||
Eris: "Kuiper" | |||
}; | |||
// Planets are in order of Baro Ki'Teer's rotation | |||
var platformRelayDict = { | |||
PC: ["Earth", "Pluto", "Saturn", "Mercury"], | |||
PS4: ["Earth", "Eris", "Mercury", "Saturn"], | |||
XB1: ["Venus", "Pluto", "Europa", "Earth"], | |||
NSW: ["Europa", "Eris", "Mercury", "Venus"] | |||
}; | |||
var countdownTimers; | |||
// Assume that this code is only invoked by Template:Countdown | |||
if (document.getElementsByClassName("customcountdown").length > 0) { | |||
countdownInit(); | |||
} | |||
/** | |||
* Initializes countdown timers. | |||
*/ | |||
function countdownInit() { | |||
// Stores the innerHTML of elements with the CSS class associated with the key; | |||
// each element contains an object representing all the countdown elements | |||
// for a particular timer. | |||
countdownTimers = getTimersElements(); | |||
console.log("Countdown timer elements recognized."); | |||
updateTimers(); | |||
console.log("Countdown timers started."); | |||
// Update timers every second | |||
setInterval(function() { | setInterval(function() { | ||
var now = new Date(); | updateTimers(); | ||
if (now. | }, 1000); | ||
} | |||
function updateTimers() { | |||
// Create countdown timer for each element with .customcountdown class | |||
for (var i = 0; i < countdownTimers.length; i++) { | |||
updateTimer(countdownTimers[i], i); | |||
} | |||
} | |||
/** | |||
* Calculate time difference and update timer each second. | |||
* @param {*} timerParams - dictionary that contains parameters for countdown timer | |||
* @param {*} num - countdown timer instance | |||
*/ | |||
function updateTimer(timerParams, num) { | |||
var now = new Date(); | |||
// Parameters are stored in innerHTML | |||
var seedDate = new Date((timerParams.seedDate === "") ? "December 3, 2015 00:00:00 UTC" | |||
: timerParams.seedDate); | |||
if (isNaN(seedDate.getTime())) { | |||
throw "ERROR: seedDate is not in a valid date format (e.g. \"December 3, 2015 00:00:00 UTC\")."; | |||
} | |||
// Time between loop iterations (i.e. duration of a loop) | |||
var loopTime = convertTimeToMilliseconds(timerParams.loopTime); | |||
// Maximum number of loop iterations; it loopLimit is less than 0, then effectively | |||
// treat it as infinite number of loops | |||
var loopLimit = (isNaN(timerParams.loopLimit)) ? 0 : | |||
(timerParams.loopLimit < 0) ? Number.MAX_SAFE_INTEGER | |||
: Number(timerParams.loopLimit); | |||
// Splits total loopTime into two time periods, one that is the delayed countdown (i.e. | |||
// a countdown of the countdown) and the other is the actual countdown; timers switch | |||
// after the other timer reaches zero | |||
// (e.g. if delayTime == 20s and loopTime = 60s, the first 20s will be a 20s countdown | |||
// with delay text while the next 40s will be the actual countdown) | |||
var delayTime = convertTimeToMilliseconds(timerParams.delayTime); | |||
// Show delayed countdown if true | |||
var delayDisplay = timerParams.delayDisplay === ""; | |||
// delayTime should always be less than total loopTime | |||
if (delayTime >= loopTime) { | |||
throw "ERROR: Cannot have a delayTime that is larger than total loopTime."; | |||
} | |||
var numLoops = calculateNumLoops(now, seedDate, 0, loopTime, loopLimit); | |||
var numLoopsDelay = calculateNumLoops(now, seedDate, delayTime, loopTime, loopLimit); | |||
var endDate = findEndDate(seedDate, 0, numLoops, loopTime); | |||
var endDateDelay = findEndDate(seedDate, delayTime, numLoopsDelay, loopTime); | |||
// Accounts for Daylight Saving Time (DST) between now and target date | |||
// by default unless otherwise specified | |||
var dstOffset = (timerParams.dst === "") ? | |||
(now.getTimezoneOffset() - endDate.getTimezoneOffset()) * 60 * 1000 : 0; | |||
var dstOffsetDelay = (timerParams.dst === "") ? | |||
(now.getTimezoneOffset() - endDateDelay.getTimezoneOffset()) * 60 * 1000 : 0; | |||
// Total time between now and target date in milliseconds converted | |||
// to certain time period | |||
// (i.e. for 120 minutes: years = 0; months = 0; days = 0; | |||
// hours = 2; minutes = 120; seconds = 7200) | |||
// time string will result in "00021207200" thus far | |||
var timeDiff = calculateTimeDiff(now, endDate, dstOffset); // in milliseconds, rounded to the nearest thousandths place | |||
var timeDiffDelay = calculateTimeDiff(now, endDateDelay, dstOffsetDelay); | |||
// console.log("Loop time: " + loopTime + | |||
// " | Time diff: " + timeDiff + | |||
// " | Delay time diff: " + timeDiffDelay + | |||
// " | Loop time - time diff " + (loopTime - timeDiff) | |||
// ); | |||
var dateFormat = (timerParams.dateFormat === "") ? "YY MM DD hh mm ss" | |||
: timerParams.dateFormat; | |||
// Based on the specified time periods' desired units, gives each time | |||
// period in the string certain units | |||
// (i.e. for 120 minutes & "hh mm ss" & "single": years = 0Y; months = 0M; | |||
// days = 0D; hours = 02h; minutes = 00m; seconds = 00s) | |||
// time string will result in "0Y0M0D02h00m00s" thus far | |||
var timeUnits = getDisplayUnits(timerParams.dateLabels); | |||
// When loop iterations reaches loop limit, hide normal text, hide delay | |||
// text, hide normal/delay time periods, and only show end of loop text | |||
if ((numLoops === loopLimit) && (endDate.getTime() <= now.getTime())) { | |||
document.getElementById("endText_" + num).setAttribute("style", "display:visible"); | |||
document.getElementById("bText_" + num).setAttribute("style", "display:none"); | |||
document.getElementById("aText_" + num).setAttribute("style", "display:none"); | |||
document.getElementById("bDelayText_" + num).setAttribute("style", "display:none"); | |||
document.getElementById("aDelayText_" + num).setAttribute("style", "display:none"); | |||
$("#timer_" + num).html(""); | |||
// While delay time has yet to reach inputted delay time show normal text, | |||
// hide delay text, and only show normal time periods specified by date | |||
// format | |||
// (i.e. for 120 minutes & "hh mm ss" & "single" & " " or " ": | |||
// years = ; months = ; days = ; hours = 02h ; | |||
// minutes = 00m ; seconds = 00s) | |||
// Time string will result in "02h 00m 00s" thus far | |||
} else if (Math.min(timeDiff, timeDiffDelay) === timeDiffDelay) { | |||
document.getElementById("endText_" + num).setAttribute("style", "display:none"); | |||
document.getElementById("bText_" + num).setAttribute("style", "display:visible"); | |||
document.getElementById("aText_" + num).setAttribute("style", "display:visible"); | |||
document.getElementById("aDelayText_" + num).setAttribute("style", "display:none"); | |||
document.getElementById("bDelayText_" + num).setAttribute("style", "display:none"); | |||
// Adding the time values onto the page for "true" countdown | |||
$("#timer_" + num).html(formatTimerNumbers(dateFormat, timeDiffDelay, timeUnits)); | |||
// When delay time reaches inputted delay time show delay text, hide normal | |||
// text, and only show delay time periods specified by date format | |||
} else { | |||
document.getElementById("endText_" + num).setAttribute("style", "display:none"); | |||
document.getElementById("bText_" + num).setAttribute("style", "display:none"); | |||
document.getElementById("aText_" + num).setAttribute("style", "display:none"); | |||
document.getElementById("bDelayText_" + num).setAttribute("style", "display:visible"); | |||
document.getElementById("aDelayText_" + num).setAttribute("style", "display:visible"); | |||
// Adding the time values onto the page for delayed time period | |||
if (delayDisplay) { | |||
$("#timer_" + num).html(formatTimerNumbers(dateFormat, timeDiff, timeUnits)); | |||
} else { | |||
$("#timer_" + num).html(""); | |||
} | } | ||
}, | } | ||
updateBaroTimers(num, numLoops); | |||
} | |||
/** | |||
* Maps countdown timers elements from DOM to individual timers. | |||
* Assuming that when an element with .customcountdown class is present | |||
* all the required elements for timer will be nested under it | |||
*/ | |||
function getTimersElements() { | |||
var count = document.getElementsByClassName("customcountdown"); | |||
countdownTimers = []; | |||
for (var i = 0; i < count.length; i++) { | |||
// Adding new objects to dictionary; each representing an individual timer | |||
countdownTimers[i] = {}; | |||
for (var index in COUNTDOWN_CLASSES) { | |||
var className = COUNTDOWN_CLASSES[index]; | |||
var element = document.getElementsByClassName(className)[i]; | |||
if (element === null) { | |||
throw "ERROR: " + className + " CSS class is missing for countdown timer instance #" + i + "."; | |||
} | |||
// Gives each instance of repeating elements of same class unique ids | |||
// (e.g. #seedDate_1) | |||
element.id = className + "_" + i; | |||
countdownTimers[i][className] = element.innerHTML; | |||
} | |||
} | |||
// Other optional classes related to countdown | |||
for (var platform in BARO_COUNTDOWN_CLASSES) { | |||
var className = BARO_COUNTDOWN_CLASSES[platform]; | |||
if ($("." + className).length > 0) { | |||
var elements = document.getElementsByClassName(className); | |||
for (var i = 0; i < elements.length; i++) { | |||
elements[i].id = className + "_" + i; | |||
} | |||
} | |||
} | |||
return countdownTimers; | |||
} | |||
/** | |||
* Converts time to milliseconds. Ignores sign and decimals. Default unit is seconds ("s") and | |||
* default number is zero. | |||
* @param {*} time = a string with a number and a time unit associated (e.g. "50s" is 50 seconds) | |||
* @returns time in milliseconds | |||
*/ | |||
function convertTimeToMilliseconds(time) { | |||
var number = parseFloat(time); | |||
var unit = time.match(/[A-Za-z]+/); | |||
if (unit === null) { | |||
unit = "s"; | |||
} | |||
if (isNaN(number)) { | |||
number = 0; | |||
} | |||
if (TIME_IN_MILLISECONDS[unit] !== undefined) { | |||
return number * TIME_IN_MILLISECONDS[unit]; | |||
} | |||
throw "ERROR: Invalid time unit (" + unit + ") in a .loopTime and/or .delayTime CSS class. " + | |||
"Valid units: \"Y\", \"M\", \"D\", \"h\", \"m\", \"s\""; | |||
} | |||
/** | |||
* Calculating number of loops between current and initial datetime. | |||
* @param {*} now - Date object representing current datetime | |||
* @param {*} seedDate - Date object that is before now | |||
* @param {*} delayTime - delay in milliseconds | |||
* @param {*} loopTime - loop duration in milliseconds | |||
* @param {*} loopLimit - maximum number of loops countdown timer can run | |||
* @returns the number of loops that countdown timer will run | |||
*/ | |||
function calculateNumLoops(now, seedDate, delayTime, loopTime, loopLimit) { | |||
// Math.ceil() is needed to account for the fact that timer can reach 0 | |||
// during an unfinished loop | |||
var numLoops = Math.ceil((now.getTime() - seedDate.getTime() + delayTime) / loopTime); | |||
if (numLoops > loopLimit) { | |||
return loopLimit; | |||
} | |||
return numLoops; | |||
} | |||
/** | |||
* Determining the end datetime based on initial datetime, | |||
* loop duration, and the number of loops that the timer will cycle through. | |||
* @param {*} seedDate - Date object | |||
* @param {*} delayTime - delay in milliseconds | |||
* @param {*} numLoops - number of countdown loops | |||
* @param {*} loopTime - loop duration in milliseconds | |||
* @returns a Date object representing end date of countdown | |||
*/ | |||
function findEndDate(seedDate, delayTime, numLoops, loopTime) { | |||
return new Date(seedDate.getTime() - delayTime + (numLoops * loopTime)); | |||
} | |||
/** | |||
* Total time between now and target date in milliseconds converted | |||
* to certain time period. | |||
* @param {*} now - Date object | |||
* @param {*} endDate - Date object | |||
* @param {*} dstOffset - DST offset in milliseconds | |||
* @returns time difference in milliseconds, rounded to the nearest thousands | |||
*/ | |||
function calculateTimeDiff(now, endDate, dstOffset) { | |||
// Note that skipping seconds can rarely happen | |||
// especially when counting down to zero | |||
// since function calls are not instantaneous and take time to run | |||
// (example case: 7041 milliseconds => 5999 milliseconds) | |||
var timeDiff = (endDate.getTime() - now.getTime()) + dstOffset; | |||
return timeDiff; | |||
} | |||
/** | |||
* Get display units for each time unit. | |||
* @param {*} dateLabels - a string | |||
* @returns a dictionary that contains display strings per time unit | |||
*/ | |||
function getDisplayUnits(dateLabels) { | |||
var timeUnits = {}; | |||
var unitAbbr; | |||
switch(dateLabels) { | |||
case "full": | |||
for (var unit in TIME_UNIT_ABBR) { | |||
unitAbbr = TIME_UNIT_ABBR[unit]; | |||
timeUnits[unitAbbr] = " " + unit + "s"; | |||
} | |||
break; | |||
case "single": | |||
for (var unit in TIME_UNIT_ABBR) { | |||
unitAbbr = TIME_UNIT_ABBR[unit]; | |||
timeUnits[unitAbbr] = TIME_UNIT_ABBR[unit]; | |||
} | |||
break; | |||
default: | |||
for (var unit in TIME_UNIT_ABBR) { | |||
unitAbbr = TIME_UNIT_ABBR[unit]; | |||
timeUnits[unitAbbr] = ""; | |||
} | |||
break; | |||
} | |||
return timeUnits; | |||
} | |||
/** | |||
* Formats the numbers of the countdown timer text. | |||
* @param {*} dateFormat - string that represents date format; each unit is separated by a non-alphabetical | |||
* character (e.g. "YY-MM-DD hh:mm:ss") | |||
* @param {*} timeDiff - dictionary that contains time differences per time unit | |||
* @param {*} timeUnits - dictionary of display text per time unit | |||
* @returns a string of the formatted text | |||
*/ | |||
function formatTimerNumbers(dateFormat, timeDiff, timeUnits) { | |||
var timerText = dateFormat; | |||
var formatArr = dateFormat.split(/[^A-Za-z]/); // e.g. ["YYYY", "MM", "DD"] | |||
for (var index in formatArr) { | |||
var elem = formatArr[index]; | |||
var unitAbbr = elem.charAt(0); | |||
// calculating how many of a time unit can fit in this time frame | |||
var timeInMilliseconds = TIME_IN_MILLISECONDS[unitAbbr]; | |||
var numTimeUnits = Math.floor(timeDiff / timeInMilliseconds); | |||
timeDiff -= numTimeUnits * timeInMilliseconds; | |||
// building display | |||
var text = numTimeUnits + ""; | |||
text = text.padStart(elem.length, "0"); // padding zeroes for uniformity | |||
text += timeUnits[elem.charAt(0)]; // adding unit display (e.g. "5 Years") | |||
var regex = new RegExp(elem); | |||
timerText = timerText.replace(regex, text); | |||
} | |||
return timerText; | |||
} | |||
/** | |||
* Update Baro Ki'Teer timers. | |||
* @param {*} num - countdown timer instance | |||
* @param {*} numLoops - number of countdown timer loops that have passed | |||
*/ | |||
function updateBaroTimers(num, numLoops) { | |||
for (var platform in BARO_COUNTDOWN_CLASSES) { | |||
var className = BARO_COUNTDOWN_CLASSES[platform]; | |||
if ($("." + className).length > 0) { | |||
$("." + className + "_" + num).html(baroRelayTracker(numLoops, platform)); | |||
} | |||
} | |||
} | |||
/** | |||
* Tracks what relay Baro is currently on. | |||
* @param {*} count | |||
* @param {*} platform - "PC", "PS4", "XB1", or "NSW" | |||
*/ | |||
function baroRelayTracker(count, platform) { | |||
var rotationNum = count % 4; | |||
var planet = platformRelayDict[platform][rotationNum]; | |||
return relayDict[planet] + " Relay, <a href=\"https://warframe.fandom.com/wiki/" + | |||
planet + "\">" + planet + "</a> (" + platform + ")<br/>"; | |||
} | } |
Edição das 07h11min de 17 de julho de 2021
/* Códigos JavaScript aqui colocados serão carregados por todos aqueles que acessarem alguma página deste wiki */ // Countdown timer that is accurate to the second and accounts for Daylight Savings Time (DST) unless // otherwise specified. // Example: a countdown of loopTime=30 seconds and a delayTime=10 seconds will have a delay timer start a // count down from 9 to 0 seconds and the actual timer start a count down from 19 to 0 seconds. // Reference: https://www.w3schools.com/howto/howto_js_countdown.asp // All of these CSS classes must be present on page in order for countdown timer to function const COUNTDOWN_CLASSES = ["seedDate", "bText", "bDelayText", "timer", "aText", "aDelayText", "loopTime", "loopLimit", "endText", "delayTime", "delayDisplay", "dst", "dateFormat", "dateLabels"]; Object.freeze(COUNTDOWN_CLASSES); const BARO_COUNTDOWN_CLASSES = { PC: "pcPlanet", PS4: "ps4Planet", XB1: "xb1Planet", NSW: "nswPlanet" }; Object.freeze(BARO_COUNTDOWN_CLASSES); // All dictionaries related to time use the abbreviated time units as keys const TIME_UNIT_ABBR = { Year: "Y", Month: "M", Day: "D", Hour: "h", Minute: "m", Second: "s" }; Object.freeze(TIME_UNIT_ABBR); const TIME_IN_MILLISECONDS = { Y: 31536000000, // assuming leap years are irrelevant (milliseconds in a day * 365) M: 2628000000, // average milliseconds per month (milliseconds in a year / 12) D: 86400000, h: 3600000, m: 60000, s: 1000 }; Object.freeze(TIME_IN_MILLISECONDS); // Mapping relay names to their respective planet var relayDict = { Mercury: "Larunda", Venus: "Vesper", Earth: "Strata", Saturn: "Kronia", Pluto: "Orcus", Europa: "Leonov", Eris: "Kuiper" }; // Planets are in order of Baro Ki'Teer's rotation var platformRelayDict = { PC: ["Earth", "Pluto", "Saturn", "Mercury"], PS4: ["Earth", "Eris", "Mercury", "Saturn"], XB1: ["Venus", "Pluto", "Europa", "Earth"], NSW: ["Europa", "Eris", "Mercury", "Venus"] }; var countdownTimers; // Assume that this code is only invoked by Template:Countdown if (document.getElementsByClassName("customcountdown").length > 0) { countdownInit(); } /** * Initializes countdown timers. */ function countdownInit() { // Stores the innerHTML of elements with the CSS class associated with the key; // each element contains an object representing all the countdown elements // for a particular timer. countdownTimers = getTimersElements(); console.log("Countdown timer elements recognized."); updateTimers(); console.log("Countdown timers started."); // Update timers every second setInterval(function() { updateTimers(); }, 1000); } function updateTimers() { // Create countdown timer for each element with .customcountdown class for (var i = 0; i < countdownTimers.length; i++) { updateTimer(countdownTimers[i], i); } } /** * Calculate time difference and update timer each second. * @param {*} timerParams - dictionary that contains parameters for countdown timer * @param {*} num - countdown timer instance */ function updateTimer(timerParams, num) { var now = new Date(); // Parameters are stored in innerHTML var seedDate = new Date((timerParams.seedDate === "") ? "December 3, 2015 00:00:00 UTC" : timerParams.seedDate); if (isNaN(seedDate.getTime())) { throw "ERROR: seedDate is not in a valid date format (e.g. \"December 3, 2015 00:00:00 UTC\")."; } // Time between loop iterations (i.e. duration of a loop) var loopTime = convertTimeToMilliseconds(timerParams.loopTime); // Maximum number of loop iterations; it loopLimit is less than 0, then effectively // treat it as infinite number of loops var loopLimit = (isNaN(timerParams.loopLimit)) ? 0 : (timerParams.loopLimit < 0) ? Number.MAX_SAFE_INTEGER : Number(timerParams.loopLimit); // Splits total loopTime into two time periods, one that is the delayed countdown (i.e. // a countdown of the countdown) and the other is the actual countdown; timers switch // after the other timer reaches zero // (e.g. if delayTime == 20s and loopTime = 60s, the first 20s will be a 20s countdown // with delay text while the next 40s will be the actual countdown) var delayTime = convertTimeToMilliseconds(timerParams.delayTime); // Show delayed countdown if true var delayDisplay = timerParams.delayDisplay === ""; // delayTime should always be less than total loopTime if (delayTime >= loopTime) { throw "ERROR: Cannot have a delayTime that is larger than total loopTime."; } var numLoops = calculateNumLoops(now, seedDate, 0, loopTime, loopLimit); var numLoopsDelay = calculateNumLoops(now, seedDate, delayTime, loopTime, loopLimit); var endDate = findEndDate(seedDate, 0, numLoops, loopTime); var endDateDelay = findEndDate(seedDate, delayTime, numLoopsDelay, loopTime); // Accounts for Daylight Saving Time (DST) between now and target date // by default unless otherwise specified var dstOffset = (timerParams.dst === "") ? (now.getTimezoneOffset() - endDate.getTimezoneOffset()) * 60 * 1000 : 0; var dstOffsetDelay = (timerParams.dst === "") ? (now.getTimezoneOffset() - endDateDelay.getTimezoneOffset()) * 60 * 1000 : 0; // Total time between now and target date in milliseconds converted // to certain time period // (i.e. for 120 minutes: years = 0; months = 0; days = 0; // hours = 2; minutes = 120; seconds = 7200) // time string will result in "00021207200" thus far var timeDiff = calculateTimeDiff(now, endDate, dstOffset); // in milliseconds, rounded to the nearest thousandths place var timeDiffDelay = calculateTimeDiff(now, endDateDelay, dstOffsetDelay); // console.log("Loop time: " + loopTime + // " | Time diff: " + timeDiff + // " | Delay time diff: " + timeDiffDelay + // " | Loop time - time diff " + (loopTime - timeDiff) // ); var dateFormat = (timerParams.dateFormat === "") ? "YY MM DD hh mm ss" : timerParams.dateFormat; // Based on the specified time periods' desired units, gives each time // period in the string certain units // (i.e. for 120 minutes & "hh mm ss" & "single": years = 0Y; months = 0M; // days = 0D; hours = 02h; minutes = 00m; seconds = 00s) // time string will result in "0Y0M0D02h00m00s" thus far var timeUnits = getDisplayUnits(timerParams.dateLabels); // When loop iterations reaches loop limit, hide normal text, hide delay // text, hide normal/delay time periods, and only show end of loop text if ((numLoops === loopLimit) && (endDate.getTime() <= now.getTime())) { document.getElementById("endText_" + num).setAttribute("style", "display:visible"); document.getElementById("bText_" + num).setAttribute("style", "display:none"); document.getElementById("aText_" + num).setAttribute("style", "display:none"); document.getElementById("bDelayText_" + num).setAttribute("style", "display:none"); document.getElementById("aDelayText_" + num).setAttribute("style", "display:none"); $("#timer_" + num).html(""); // While delay time has yet to reach inputted delay time show normal text, // hide delay text, and only show normal time periods specified by date // format // (i.e. for 120 minutes & "hh mm ss" & "single" & " " or " ": // years = ; months = ; days = ; hours = 02h ; // minutes = 00m ; seconds = 00s) // Time string will result in "02h 00m 00s" thus far } else if (Math.min(timeDiff, timeDiffDelay) === timeDiffDelay) { document.getElementById("endText_" + num).setAttribute("style", "display:none"); document.getElementById("bText_" + num).setAttribute("style", "display:visible"); document.getElementById("aText_" + num).setAttribute("style", "display:visible"); document.getElementById("aDelayText_" + num).setAttribute("style", "display:none"); document.getElementById("bDelayText_" + num).setAttribute("style", "display:none"); // Adding the time values onto the page for "true" countdown $("#timer_" + num).html(formatTimerNumbers(dateFormat, timeDiffDelay, timeUnits)); // When delay time reaches inputted delay time show delay text, hide normal // text, and only show delay time periods specified by date format } else { document.getElementById("endText_" + num).setAttribute("style", "display:none"); document.getElementById("bText_" + num).setAttribute("style", "display:none"); document.getElementById("aText_" + num).setAttribute("style", "display:none"); document.getElementById("bDelayText_" + num).setAttribute("style", "display:visible"); document.getElementById("aDelayText_" + num).setAttribute("style", "display:visible"); // Adding the time values onto the page for delayed time period if (delayDisplay) { $("#timer_" + num).html(formatTimerNumbers(dateFormat, timeDiff, timeUnits)); } else { $("#timer_" + num).html(""); } } updateBaroTimers(num, numLoops); } /** * Maps countdown timers elements from DOM to individual timers. * Assuming that when an element with .customcountdown class is present * all the required elements for timer will be nested under it */ function getTimersElements() { var count = document.getElementsByClassName("customcountdown"); countdownTimers = []; for (var i = 0; i < count.length; i++) { // Adding new objects to dictionary; each representing an individual timer countdownTimers[i] = {}; for (var index in COUNTDOWN_CLASSES) { var className = COUNTDOWN_CLASSES[index]; var element = document.getElementsByClassName(className)[i]; if (element === null) { throw "ERROR: " + className + " CSS class is missing for countdown timer instance #" + i + "."; } // Gives each instance of repeating elements of same class unique ids // (e.g. #seedDate_1) element.id = className + "_" + i; countdownTimers[i][className] = element.innerHTML; } } // Other optional classes related to countdown for (var platform in BARO_COUNTDOWN_CLASSES) { var className = BARO_COUNTDOWN_CLASSES[platform]; if ($("." + className).length > 0) { var elements = document.getElementsByClassName(className); for (var i = 0; i < elements.length; i++) { elements[i].id = className + "_" + i; } } } return countdownTimers; } /** * Converts time to milliseconds. Ignores sign and decimals. Default unit is seconds ("s") and * default number is zero. * @param {*} time = a string with a number and a time unit associated (e.g. "50s" is 50 seconds) * @returns time in milliseconds */ function convertTimeToMilliseconds(time) { var number = parseFloat(time); var unit = time.match(/[A-Za-z]+/); if (unit === null) { unit = "s"; } if (isNaN(number)) { number = 0; } if (TIME_IN_MILLISECONDS[unit] !== undefined) { return number * TIME_IN_MILLISECONDS[unit]; } throw "ERROR: Invalid time unit (" + unit + ") in a .loopTime and/or .delayTime CSS class. " + "Valid units: \"Y\", \"M\", \"D\", \"h\", \"m\", \"s\""; } /** * Calculating number of loops between current and initial datetime. * @param {*} now - Date object representing current datetime * @param {*} seedDate - Date object that is before now * @param {*} delayTime - delay in milliseconds * @param {*} loopTime - loop duration in milliseconds * @param {*} loopLimit - maximum number of loops countdown timer can run * @returns the number of loops that countdown timer will run */ function calculateNumLoops(now, seedDate, delayTime, loopTime, loopLimit) { // Math.ceil() is needed to account for the fact that timer can reach 0 // during an unfinished loop var numLoops = Math.ceil((now.getTime() - seedDate.getTime() + delayTime) / loopTime); if (numLoops > loopLimit) { return loopLimit; } return numLoops; } /** * Determining the end datetime based on initial datetime, * loop duration, and the number of loops that the timer will cycle through. * @param {*} seedDate - Date object * @param {*} delayTime - delay in milliseconds * @param {*} numLoops - number of countdown loops * @param {*} loopTime - loop duration in milliseconds * @returns a Date object representing end date of countdown */ function findEndDate(seedDate, delayTime, numLoops, loopTime) { return new Date(seedDate.getTime() - delayTime + (numLoops * loopTime)); } /** * Total time between now and target date in milliseconds converted * to certain time period. * @param {*} now - Date object * @param {*} endDate - Date object * @param {*} dstOffset - DST offset in milliseconds * @returns time difference in milliseconds, rounded to the nearest thousands */ function calculateTimeDiff(now, endDate, dstOffset) { // Note that skipping seconds can rarely happen // especially when counting down to zero // since function calls are not instantaneous and take time to run // (example case: 7041 milliseconds => 5999 milliseconds) var timeDiff = (endDate.getTime() - now.getTime()) + dstOffset; return timeDiff; } /** * Get display units for each time unit. * @param {*} dateLabels - a string * @returns a dictionary that contains display strings per time unit */ function getDisplayUnits(dateLabels) { var timeUnits = {}; var unitAbbr; switch(dateLabels) { case "full": for (var unit in TIME_UNIT_ABBR) { unitAbbr = TIME_UNIT_ABBR[unit]; timeUnits[unitAbbr] = " " + unit + "s"; } break; case "single": for (var unit in TIME_UNIT_ABBR) { unitAbbr = TIME_UNIT_ABBR[unit]; timeUnits[unitAbbr] = TIME_UNIT_ABBR[unit]; } break; default: for (var unit in TIME_UNIT_ABBR) { unitAbbr = TIME_UNIT_ABBR[unit]; timeUnits[unitAbbr] = ""; } break; } return timeUnits; } /** * Formats the numbers of the countdown timer text. * @param {*} dateFormat - string that represents date format; each unit is separated by a non-alphabetical * character (e.g. "YY-MM-DD hh:mm:ss") * @param {*} timeDiff - dictionary that contains time differences per time unit * @param {*} timeUnits - dictionary of display text per time unit * @returns a string of the formatted text */ function formatTimerNumbers(dateFormat, timeDiff, timeUnits) { var timerText = dateFormat; var formatArr = dateFormat.split(/[^A-Za-z]/); // e.g. ["YYYY", "MM", "DD"] for (var index in formatArr) { var elem = formatArr[index]; var unitAbbr = elem.charAt(0); // calculating how many of a time unit can fit in this time frame var timeInMilliseconds = TIME_IN_MILLISECONDS[unitAbbr]; var numTimeUnits = Math.floor(timeDiff / timeInMilliseconds); timeDiff -= numTimeUnits * timeInMilliseconds; // building display var text = numTimeUnits + ""; text = text.padStart(elem.length, "0"); // padding zeroes for uniformity text += timeUnits[elem.charAt(0)]; // adding unit display (e.g. "5 Years") var regex = new RegExp(elem); timerText = timerText.replace(regex, text); } return timerText; } /** * Update Baro Ki'Teer timers. * @param {*} num - countdown timer instance * @param {*} numLoops - number of countdown timer loops that have passed */ function updateBaroTimers(num, numLoops) { for (var platform in BARO_COUNTDOWN_CLASSES) { var className = BARO_COUNTDOWN_CLASSES[platform]; if ($("." + className).length > 0) { $("." + className + "_" + num).html(baroRelayTracker(numLoops, platform)); } } } /** * Tracks what relay Baro is currently on. * @param {*} count * @param {*} platform - "PC", "PS4", "XB1", or "NSW" */ function baroRelayTracker(count, platform) { var rotationNum = count % 4; var planet = platformRelayDict[platform][rotationNum]; return relayDict[planet] + " Relay, <a href=\"https://warframe.fandom.com/wiki/" + planet + "\">" + planet + "</a> (" + platform + ")<br/>"; }