MediaWiki:Gadget-fix-schedule-times.js

// vim: ts=4 sw=4 et ai si // HOW TO USE: you really should know at least a little javascript for this... (find someone who does if not). Copy this page to your wiki. The title must be the same. Or you can use a different title, but it MUST start with "MediaWiki:" and end in ".js", and you'll have to edit the snippet. Then, on your wiki's MediaWiki:Common.js page (VERY DANGEROUS), determine the page ID of your schedule (click "Page information" under "Tools" in the sidebar), then insert this snippet onto that MediaWiki:Common.js page (don't include the slash and star on their own lines at the beginning and end... actually, you should only be doing this if you have at least minimal JavaScript knowledge...). Make sure to replace BOTH occurrences of REPLACE_ME_WITH_THE_PAGE_ID_OF_YOUR_SCHEDULE_PAGE with that page ID you found. The snippet loads in this gadget and also automatically reloads the page when the schedule changes. (It checks every 2 minutes.) You must also update CONFERENCE_DAYS and WRITTEN_SCHEDULE_OFFSET_SECONDS in this page with the appropriate values for your conference. You will also want to, in your schedule, include a link to the schedule page itself but with the text ?dontFixSchedule at the end of the URL right after the title of the schedule page. That disables these two features and is useful when you're working on the schedule itself. The schedule is assumed to USE 24-HOUR TIME with TWO DIGITS ALWAYS FOR THE HOUR. If you DON'T DO THAT, it WILL NOT WORK.: /* DELETE THIS LINE WHEN COPYING. This is the start of the stuff that goes in MediaWiki:Common.js if ( ( mw.config.get( "wgArticleId" ) === REPLACE_ME_WITH_THE_PAGE_ID_OF_YOUR_SCHEDULE_PAGE ) && ( mw.config.get( 'wgAction' ) === 'view' ) ) { mw.loader.load( '/index.php?title=MediaWiki%3AGadget-fix-schedule-times.js&action=raw&ctype=text/javascript' ); mw.loader.using( ['mediawiki.api'], function { if(window.location.search.indexOf("dontFixSchedule")>=0)return;    var api = new mw.Api;    window.setInterval(function{ api.get({           action:'query',            pageids:REPLACE_ME_WITH_THE_PAGE_ID_OF_YOUR_SCHEDULE_PAGE,            prop:'revisions',            rvprop:'ids',            uselang:'content',            format:'json',            formatversion:'2'        }).then(function(res){            var revid = res.query.pages[0].revisions[0].revid;            if(revid !== mw.config.get('wgRevisionId')){                mw.notify('Schedule changed. Reloading.');               window.location.reload;            }        }); },1000 * 60 * 2); // check every 2 minutes. if you wanted 5 minutes, replace the 2 with a 5. }); } DELETE THIS LINE WHEN COPYING. This is the end of the stuff that goes in MediaWiki:Common.js */ $.when(   $.ready ).then( function  { // quit immediately if dontFixSchedule is in the URL after the ? if(window.location.search.indexOf("dontFixSchedule")>=0)return;

// Configuration var CONFERENCE_DAYS = { "Friday, October 8": Date.UTC( 2021, /* month index, not month number! */ 9, 8 ), "Saturday, October 9": Date.UTC( 2021, 9, 9 ), "Sunday, October 10": Date.UTC( 2021, 9, 10 ) };   var WRITTEN_SCHEDULE_OFFSET_SECONDS = -4 * 3600; // written schedule for WCNA 2021 is Eastern Time which is UTC-4

// Other constants var TIME_REGEX = /(\d\d):(\d\d)( - (\d\d):(\d\d)|\+)/;

function pl( n ) { return ( Math.floor( n ) === 1 ) ? "" : "s"; }

function textDescribingRelativeTime( minutesUntilStart, minutesUntilEnd, countdownDuringEvent ) { if ( minutesUntilStart > /* one day */ 1440 ) { return "in " + Math.floor( minutesUntilStart / 1440 ).toFixed( 0 ) + " day" + pl( minutesUntilStart / 1440 ); } else if ( minutesUntilStart > /* 3 hours */ 180 ) { return "in " + Math.floor( minutesUntilStart / 60 ).toFixed( 0 ) + " hour" + pl( minutesUntilStart / 60 ); } else if ( minutesUntilStart > 120 ) { return "in 2 hours and " + Math.floor( minutesUntilStart % 60 ).toFixed( 0 ) + " minute" + pl( minutesUntilStart % 60 ); } else if ( minutesUntilStart > 60 ) { return "in an hour and " + Math.floor( minutesUntilStart % 60 ).toFixed( 0 ) + " minute" + pl( minutesUntilStart % 60 ); } else if ( minutesUntilStart > 1 ) { return "in " + Math.floor( minutesUntilStart ).toFixed( 0 ) + " minute" + pl( minutesUntilStart ); } else if ( minutesUntilStart > 0 ) { return "in less than a minute"; } else if ( minutesUntilStart > -1 ) { return "started just now"; } else if ( minutesUntilStart > -5 ) { return "started " + Math.floor( -minutesUntilStart ).toFixed( 0 ) + " minute" + pl( -minutesUntilStart ) + " ago"; } else if ( minutesUntilEnd > 1 ) { if ( countdownDuringEvent ) { return "ends in " + Math.floor( minutesUntilEnd ).toFixed( 0 ) + " minute" + pl( minutesUntilEnd ); } else { if ( minutesUntilStart > -60 ) { return "started " + Math.floor( -minutesUntilStart ).toFixed( 0 ) + " minute" + pl( -minutesUntilStart ) + " ago"; } else { return "started " + Math.floor( -minutesUntilStart / 60 ).toFixed( 0 ) + " hour" + pl( -minutesUntilStart / 60 ) + " ago"; }           }        } else if ( minutesUntilEnd > 0 ) { return "ends in one minute"; } else if ( minutesUntilEnd > -1 ) { return "ended just now"; } else if ( minutesUntilEnd > -60 ) { return "ended " + Math.floor( -minutesUntilEnd ).toFixed( 0 ) + " minute" + pl( -minutesUntilEnd ) + " ago"; } else if ( minutesUntilEnd > -1440 ) { return "ended " + Math.floor( -minutesUntilEnd / 60 ).toFixed( 0 ) + " hour" + pl( -minutesUntilEnd / 60 ) + " ago"; } else { return "ended " + Math.floor( -minutesUntilEnd / 1440 ).toFixed( 0 ) + " day" + pl( -minutesUntilEnd / 1440 ) + " ago"; }   }

function formatRelativeTime( minutesUntilStart, minutesUntilEnd, countdownDuringEvent ) { var text = textDescribingRelativeTime( minutesUntilStart, minutesUntilEnd, countdownDuringEvent ); if ( minutesUntilStart > 0 ) { return "(" + text + ")"; } else if ( minutesUntilEnd > 0 ) { return "(" + text + ")"; } else { return "(" + text + ")"; }   }

function formatTime( unixTimestamp ) { return new Date( unixTimestamp * 1000 ).toLocaleTimeString.replace( /(\d\d?:\d\d):00/, "$1" ).replace( / /g, " " ); }

function makeHtml( originalTimeMatch, tableCell ) { var dayHeading = $( tableCell ).closest( "table" ).prev.prev; // skip icon key var dayHeadingText = dayHeading.find( ".mw-headline" ).text; var baseDateUnix = Math.floor( CONFERENCE_DAYS[dayHeadingText] / 1000 ); var isSoftEndingTime = !originalTimeMatch[4]; // if there's no group-4 match, no ending time was specified

// First, get the actual timestamps var startTimeUnix = baseDateUnix + parseInt( originalTimeMatch[1], 10 ) * 3600 + parseInt( originalTimeMatch[2], 10 ) * 60 - WRITTEN_SCHEDULE_OFFSET_SECONDS; var endTimeUnix = isSoftEndingTime ? ( startTimeUnix + 3600 * 5 ) : ( baseDateUnix + parseInt( originalTimeMatch[4], 10 ) * 3600 +               parseInt( originalTimeMatch[5], 10 ) * 60 - WRITTEN_SCHEDULE_OFFSET_SECONDS );

// Now, get some text like "Starts in 10 minutes". var nowUnix = Math.floor( new Date.getTime / 1000 ); var relativeTimeFormatted = formatRelativeTime(           ( startTimeUnix - nowUnix ) / 60,            ( endTimeUnix - nowUnix ) / 60,            /* countdownDuringEvent */ !isSoftEndingTime,        ); return formatTime( startTimeUnix ) + ( isSoftEndingTime ? "+" : ( " - " + formatTime( endTimeUnix ) ) ) + " " + relativeTimeFormatted; }

$( "small" ).each( function {        if ( this.textContent === "(US Eastern Time)" ) { try{            $( this ).text( "In " + Intl.DateTimeFormat.resolvedOptions.timeZone.replace( /_/g, " " ) + " time" ); }catch(e){ console.error(e); $(this).text("In your local timezone (hover to see Eastern U.S. time)");        }        }    } );

$( "td" ).each( function {        var match = TIME_REGEX.exec( this.textContent );        if ( match ) {            this.innerHTML = this.innerHTML.replace( match[0], "" + match[0] + " " );        }    } );

function go { $( "span.fixed" ).each( function {            $( this ).html( makeHtml( TIME_REGEX.exec( this.dataset.original ), this.parentNode ) );        } ); }

go; window.setInterval( go, 60 * 1000 ); } );