MediaWiki:Common.js: Difference between revisions

From PickiPedia: A knowledge base of bluegrass, old time psychedelic jams, and other public domain music
Jump to navigationJump to search
Created page with "* * 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 ~1..."
 
Change time ago displays to use Ethereum blocks instead of minutes/hours/days (via update-page on MediaWiki MCP Server)
Line 102: Line 102:


/**
/**
  * TimeAgo — shows "X days ago" for transcluded templates.
  * TimeAgo — shows "X blocks ago" for transcluded templates.
  *
  *
  * Usage in wikitext:
  * Usage in wikitext:
Line 108: Line 108:
  *
  *
  * Fetches the last revision timestamp from the API and renders
  * Fetches the last revision timestamp from the API and renders
  * a human-readable relative time string.
  * a block-based relative time string using Ethereum block heights.
  */
  */
(function() {
(function() {
     'use strict';
     'use strict';
    // Post-merge average block time is ~12.12 seconds
    var AVG_BLOCK_TIME = 12.12;


     var spans = document.querySelectorAll('.timeago[data-lastmod-page]');
     var spans = document.querySelectorAll('.timeago[data-lastmod-page]');
     if (!spans.length) return;
     if (!spans.length) return;
    // Get current block from footer (format: "24,328,442")
    function getCurrentBlockFromFooter() {
        var footerLink = document.querySelector('a[href*="etherscan.io/block/"]');
        if (footerLink) {
            var match = footerLink.href.match(/block\/(\d+)/);
            if (match) return parseInt(match[1]);
        }
        return null;
    }
    // Format number with commas (e.g., 1234567 -> "1,234,567")
    function formatNumber(num) {
        return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
    }


     // Collect unique page titles
     // Collect unique page titles
Line 138: Line 156:
             }
             }
         });
         });
        var currentBlock = getCurrentBlockFromFooter();


         spans.forEach(function(span) {
         spans.forEach(function(span) {
Line 146: Line 166:
                 return;
                 return;
             }
             }
             span.textContent = formatTimeAgo(ts);
             span.textContent = formatBlocksAgo(ts, currentBlock);
             span.title = ts.toLocaleString();
             span.title = ts.toLocaleString();
         });
         });
     });
     });


     function formatTimeAgo(date) {
     function formatBlocksAgo(date, currentBlock) {
         var diff = Math.floor((Date.now() - date.getTime()) / 1000);
         var secondsAgo = Math.floor((Date.now() - date.getTime()) / 1000);
         if (diff < 60) return 'just now';
         var blocksAgo = Math.round(secondsAgo / AVG_BLOCK_TIME);
         if (diff < 3600) {
 
             var m = Math.floor(diff / 60);
         if (!currentBlock) {
             return m + (m === 1 ? ' minute ago' : ' minutes ago');
             // Fallback if we can't get the current block
             return '~' + formatNumber(blocksAgo) + ' blocks ago';
         }
         }
         if (diff < 86400) {
 
            var h = Math.floor(diff / 3600);
        // Calculate the estimated block at the time of the edit
             return h + (h === 1 ? ' hour ago' : ' hours ago');
        var editBlock = currentBlock - blocksAgo;
         }
 
        if (diff < 2592000) {
         if (blocksAgo < 1) {
            var d = Math.floor(diff / 86400);
             return 'this block';
             return d + (d === 1 ? ' day ago' : ' days ago');
         } else if (blocksAgo === 1) {
         }
             return '1 block ago (~' + formatNumber(editBlock) + ')';
        if (diff < 31536000) {
         } else {
             var mo = Math.floor(diff / 2592000);
             return formatNumber(blocksAgo) + ' blocks ago (~' + formatNumber(editBlock) + ')';
            return mo + (mo === 1 ? ' month ago' : ' months ago');
         }
         }
        var y = Math.floor(diff / 31536000);
        return y + (y === 1 ? ' year ago' : ' years ago');
     }
     }
})();
})();

Revision as of 17:34, 4 February 2026

/**
* 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 blocks 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 block-based relative time string using Ethereum block heights.
 */
(function() {
    'use strict';

    // Post-merge average block time is ~12.12 seconds
    var AVG_BLOCK_TIME = 12.12;

    var spans = document.querySelectorAll('.timeago[data-lastmod-page]');
    if (!spans.length) return;

    // Get current block from footer (format: "24,328,442")
    function getCurrentBlockFromFooter() {
        var footerLink = document.querySelector('a[href*="etherscan.io/block/"]');
        if (footerLink) {
            var match = footerLink.href.match(/block\/(\d+)/);
            if (match) return parseInt(match[1]);
        }
        return null;
    }

    // Format number with commas (e.g., 1234567 -> "1,234,567")
    function formatNumber(num) {
        return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
    }

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

        var currentBlock = getCurrentBlockFromFooter();

        spans.forEach(function(span) {
            var title = span.getAttribute('data-lastmod-page');
            var ts = timestamps[title];
            if (!ts) {
                span.textContent = '';
                return;
            }
            span.textContent = formatBlocksAgo(ts, currentBlock);
            span.title = ts.toLocaleString();
        });
    });

    function formatBlocksAgo(date, currentBlock) {
        var secondsAgo = Math.floor((Date.now() - date.getTime()) / 1000);
        var blocksAgo = Math.round(secondsAgo / AVG_BLOCK_TIME);

        if (!currentBlock) {
            // Fallback if we can't get the current block
            return '~' + formatNumber(blocksAgo) + ' blocks ago';
        }

        // Calculate the estimated block at the time of the edit
        var editBlock = currentBlock - blocksAgo;

        if (blocksAgo < 1) {
            return 'this block';
        } else if (blocksAgo === 1) {
            return '1 block ago (~' + formatNumber(editBlock) + ')';
        } else {
            return formatNumber(blocksAgo) + ' blocks ago (~' + formatNumber(editBlock) + ')';
        }
    }
})();