MediaWiki:Common.js: Difference between revisions

Jump to navigation Jump to search
add JavaScript-Based Time Zone Detection
(clear)
(add JavaScript-Based Time Zone Detection)
Line 1: Line 1:
/* Any JavaScript here will be loaded for all users on every page load. */
/* Any JavaScript here will be loaded for all users on every page load. */
// Utility function to set/get cookies as a fallback for localStorage
function setCookie(name, value, days) {
    const expires = new Date(Date.now() + days * 864e5).toUTCString();
    document.cookie = `${name}=${encodeURIComponent(value)}; expires=${expires}; path=/`;
}
function getCookie(name) {
    const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));
    return match ? decodeURIComponent(match[1]) : null;
}
mw.hook('wikipage.content').add(function($content) {
    // Only process for anonymous users
    if (mw.user.isAnon()) {
        // Detect user's time zone, with fallback to UTC
        let userTimeZone;
        try {
            userTimeZone = localStorage.getItem('wiki_user_timezone') || getCookie('wiki_user_timezone') || Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';
        } catch (e) {
            console.warn('Failed to detect time zone, defaulting to UTC', e);
            userTimeZone = 'UTC';
        }
        // Cache time zone in localStorage and cookie for persistence
        try {
            localStorage.setItem('wiki_user_timezone', userTimeZone);
            setCookie('wiki_user_timezone', userTimeZone, 365);
        } catch (e) {
            console.warn('Failed to cache time zone', e);
        }
        // Define timestamp selectors, including extension-specific ones
        const timestampSelectors = [
            '.mw-changeslist-date', // Recent changes
            '.mw-contributions-time', // User contributions
            '.timestamp', // General timestamps
            '.mw-history-date', // Page history
            '.mw-last-modified', // Last edited timestamp
            'span.history-user-info span', // User signatures
            '.smw-timestamp', // Semantic MediaWiki
            '.dpl-timestamp' // DynamicPageList
        ].join(',');
        // Optimize by scoping to main content area
        const timestamps = $content.find(timestampSelectors);
        // Get user's interface language for internationalization
        const userLang = mw.config.get('wgUserLanguage') || 'en-US';
        // Debounced timestamp update for performance
        const updateTimestamps = debounce(function() {
            timestamps.each(function() {
                const $element = $(this);
                let utcTime = $element.attr('data-mw-timestamp') || $element.text().trim();
                try {
                    // Parse UTC timestamp with early error handling
                    let date;
                    try {
                        date = new Date(utcTime + (utcTime.includes('Z') ? '' : ' UTC'));
                        if (isNaN(date)) throw new Error('Invalid date');
                    } catch (e) {
                        console.warn(`Failed to parse timestamp: ${utcTime}`, e);
                        return;
                    }
                    // Format options for user's time zone and language
                    const options = {
                        timeZone: userTimeZone,
                        year: 'numeric',
                        month: 'short',
                        day: 'numeric',
                        hour: 'numeric',
                        minute: 'numeric',
                        second: 'numeric',
                        hour12: userLang === 'en-US' // 12-hour for en-US, 24-hour for others
                    };
                    const formattedTime = date.toLocaleString(userLang, options);
                    const timeZoneAbbr = new Intl.DateTimeFormat(userLang, {
                        timeZone: userTimeZone,
                        timeZoneName: 'short'
                    }).formatToParts(date).find(part => part.type === 'timeZoneName')?.value
                        || userTimeZone.split('/').pop().replace('_', ' ') || userTimeZone;
                    // Update timestamp
                    $element.text(`${formattedTime} (${timeZoneAbbr})`);
                    $element.attr('title', `Original UTC: ${utcTime}`);
                } catch (e) {
                    console.warn(`Error processing timestamp: ${utcTime}`, e);
                }
            });
        }, 100);
        updateTimestamps();
    }
});
// Non-intrusive prompt triggered on scroll or time zone mismatch
let prompted = false;
const currentZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const storedZone = localStorage.getItem('wiki_user_timezone') || getCookie('wiki_user_timezone');
const dismissed = localStorage.getItem('wiki_user_timezone_prompt_dismissed') || getCookie('wiki_user_timezone_prompt_dismissed');
if (mw.user.isAnon() && !dismissed && (currentZone !== storedZone || !storedZone)) {
    $(window).one('scroll', () => {
        if (!prompted) {
            mw.notify(
                'Timestamps are shown in your local time zone. Click here to change it.',
                {
                    type: 'info',
                    autoHide: true,
                    autoHideSeconds: 10,
                    tag: 'timezone-prompt',
                    callback: function() {
                        const defaultZone = currentZone || 'UTC';
                        const selectedZone = prompt('Enter your time zone (e.g., America/New_York):', defaultZone);
                        if (selectedZone) {
                            try {
                                localStorage.setItem('wiki_user_timezone', selectedZone);
                                setCookie('wiki_user_timezone', selectedZone, 365);
                                localStorage.setItem('wiki_user_timezone_prompt_dismissed', 'true');
                                setCookie('wiki_user_timezone_prompt_dismissed', 'true', 365);
                                location.reload();
                            } catch (e) {
                                console.warn('Failed to save time zone selection', e);
                            }
                        }
                    }
                }
            );
            prompted = true;
        }
    });
}
// Debounce utility
function debounce(func, wait) {
    let timeout;
    return function() {
        clearTimeout(timeout);
        timeout = setTimeout(func, wait);
    };
}

Navigation menu