MediaWiki:Common.js
From PickiPedia: A knowledge base of bluegrass, old time psychedelic jams, and other public domain music
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/**
* Blue Railroad Submission - Date to Block Height Converter
* Adds a datepicker that converts dates to Ethereum block heights
* Also shows time ago for news templates
*/
(function() {
'use strict';
// Only run on the Blue Railroad Submission form
if (!document.querySelector('input[name="Blue Railroad Submission[block_height]"]')) {
return;
}
// Reference point: known block and timestamp
// Post-merge average block time is ~12.12 seconds
const AVG_BLOCK_TIME = 12.12;
// Get current block from footer (format: "24,328,442")
function getCurrentBlockFromFooter() {
const footerLink = document.querySelector('a[href*="etherscan.io/block/"]');
if (footerLink) {
const match = footerLink.href.match(/block\/(\d+)/);
if (match) return parseInt(match[1]);
}
return null;
}
// Calculate block height from date
function dateToBlockHeight(targetDate, refBlock, refTimestamp) {
const targetTimestamp = targetDate.getTime() / 1000;
const secondsDiff = refTimestamp - targetTimestamp;
const blocksDiff = Math.round(secondsDiff / AVG_BLOCK_TIME);
return refBlock - blocksDiff;
}
// Add the datepicker UI
function addDatePicker() {
const blockInput = document.querySelector('input[name="Blue Railroad Submission[block_height]"]');
if (!blockInput) return;
const container = document.createElement('div');
container.style.marginTop = '8px';
container.innerHTML = `
<label style="display: block; margin-bottom: 4px; font-size: 0.9em;">
Or pick a date/time:
</label>
<input type="datetime-local" id="br-datepicker" style="padding: 4px; margin-right: 8px;">
<button type="button" id="br-convert-btn" style="padding: 4px 12px; cursor: pointer;">
Convert to Block
</button>
<span id="br-status" style="margin-left: 8px; font-size: 0.9em; color: #666;"></span>
`;
blockInput.parentNode.appendChild(container);
const datePicker = document.getElementById('br-datepicker');
const convertBtn = document.getElementById('br-convert-btn');
const status = document.getElementById('br-status');
// Set default to now
const now = new Date();
datePicker.value = now.toISOString().slice(0, 16);
convertBtn.addEventListener('click', function() {
const selectedDate = new Date(datePicker.value);
if (isNaN(selectedDate.getTime())) {
status.textContent = 'Invalid date';
status.style.color = 'red';
return;
}
const currentBlock = getCurrentBlockFromFooter();
if (!currentBlock) {
status.textContent = 'Could not find reference block';
status.style.color = 'red';
return;
}
const currentTimestamp = Date.now() / 1000;
const estimatedBlock = dateToBlockHeight(selectedDate, currentBlock, currentTimestamp);
if (estimatedBlock > currentBlock) {
status.textContent = 'Date is in the future!';
status.style.color = 'orange';
} else {
status.textContent = '~estimated';
status.style.color = 'green';
}
blockInput.value = estimatedBlock;
});
}
// Run when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', addDatePicker);
} else {
addDatePicker();
}
})();
/**
* TimeAgo — shows "X days ago" for transcluded templates.
*
* Usage in wikitext:
* <span class="timeago" data-lastmod-page="Template:NewsShorts"></span>
*
* Fetches the last revision timestamp from the API and renders
* a human-readable relative time string.
*/
(function() {
'use strict';
var spans = document.querySelectorAll('.timeago[data-lastmod-page]');
if (!spans.length) return;
// Collect unique page titles
var titles = [];
spans.forEach(function(span) {
var t = span.getAttribute('data-lastmod-page');
if (t && titles.indexOf(t) === -1) titles.push(t);
});
// Batch query the API (up to 50 titles per request)
var api = mw.config.get('wgScriptPath') + '/api.php';
var url = api + '?action=query&prop=revisions&rvprop=timestamp&format=json&titles=' +
encodeURIComponent(titles.join('|'));
fetch(url).then(function(r) { return r.json(); }).then(function(data) {
var pages = data.query && data.query.pages || {};
var timestamps = {};
Object.keys(pages).forEach(function(id) {
var page = pages[id];
if (page.revisions && page.revisions[0]) {
timestamps[page.title] = new Date(page.revisions[0].timestamp);
}
});
spans.forEach(function(span) {
var title = span.getAttribute('data-lastmod-page');
var ts = timestamps[title];
if (!ts) {
span.textContent = '';
return;
}
span.textContent = formatTimeAgo(ts);
span.title = ts.toLocaleString();
});
});
function formatTimeAgo(date) {
var diff = Math.floor((Date.now() - date.getTime()) / 1000);
if (diff < 60) return 'just now';
if (diff < 3600) {
var m = Math.floor(diff / 60);
return m + (m === 1 ? ' minute ago' : ' minutes ago');
}
if (diff < 86400) {
var h = Math.floor(diff / 3600);
return h + (h === 1 ? ' hour ago' : ' hours ago');
}
if (diff < 2592000) {
var d = Math.floor(diff / 86400);
return d + (d === 1 ? ' day ago' : ' days ago');
}
if (diff < 31536000) {
var mo = Math.floor(diff / 2592000);
return mo + (mo === 1 ? ' month ago' : ' months ago');
}
var y = Math.floor(diff / 31536000);
return y + (y === 1 ? ' year ago' : ' years ago');
}
})();