Jump to content

User:Whatback11/WikiVault.js

From Wikipedia, the free encyclopedia
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
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() {
    if ($("#wikivault-floating-button").length) return;

    const WIKI_VAULT = {
        MAIN_URI: 'https://tedbot.toolforge.org',
        JS_CONTENT_API: '/api/get-js-content',
        INIT_API: '/api/init',
        IS_DEMO: false,
        STORAGE_KEYS: {
            LAST_MODIFIED: 'wikivault_last_modified',
            CACHED_CODE: 'wikivault_cached_code',
            BUTTON_POSITION: 'wikivault_button_position'
        }
    };

    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)" 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)"></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)"></path>' +
        '</svg>';

    const BUTTON_STYLES = {
        position: 'fixed',
        right: '20px',
        top: '50%',
        transform: 'translateY(-50%)',
        width: '50px',
        height: '50px',
        'background-color': '#b0c4de',
        color: 'white',
        display: 'flex',
        'justify-content': 'center',
        'align-items': 'center',
        'border-radius': '50%',
        cursor: 'not-allowed',
        opacity: '1.0',
        'box-shadow': '0 2px 3px rgba(0,0,0,0.1)',
        'z-index': '1000',
        'touch-action': 'none'
    };

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

        // Load saved position
        const savedPosition = localStorage.getItem(WIKI_VAULT.STORAGE_KEYS.BUTTON_POSITION);
        if (savedPosition) {
            try {
                const pos = JSON.parse(savedPosition);
                button.css({
                    position: 'fixed',
                    left: pos.left + 'px',
                    top: pos.top + 'px',
                    right: '',
                    transform: ''
                });
            } catch (e) {
                console.error("WikiVault: Failed to parse saved button position:", e);
                localStorage.removeItem(WIKI_VAULT.STORAGE_KEYS.BUTTON_POSITION);
            }
        }

        // Make the button draggable
        var isDragging = false;
        var offsetX, offsetY;
        var startX, startY;
        var DRAG_THRESHOLD = 5; // pixels

        function startDrag(e) {
            if (e.button === 2) return; // Prevent right-click drag
            isDragging = false; // Reset dragging state
            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;
            
            // Calculate distance from start point
            const dx = event.clientX - startX;
            const dy = event.clientY - startY;
            const distance = Math.sqrt(dx * dx + dy * dy);
            
            // Only start dragging if we've moved beyond the threshold
            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();

            var newLeft = event.clientX - offsetX;
            var newTop = event.clientY - offsetY;

            // Ensure button stays within viewport
            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) {
                // console.log("Button clicked");
            }
            
            isDragging = false;
            button.data('dragging', false);
            startX = null;
            startY = null;

            const pos = {
                left: parseInt(button.css('left')),
                top: parseInt(button.css('top'))
            };
            localStorage.setItem(WIKI_VAULT.STORAGE_KEYS.BUTTON_POSITION, JSON.stringify(pos));

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

        // Event listeners
        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();

                    // Ensure button stays within viewport
                    const newLeft = Math.min(pos.left, viewportWidth - buttonWidth);
                    const newTop = Math.min(pos.top, viewportHeight - buttonHeight);

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

                    // Update saved position if it was 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);
                }
            }
        });
        window.dispatchEvent(new Event('resize'));
    }

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

    function decodeResponse(response) {
        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("Script Execution Error:", e);
        }
    }

    function loadAndExecuteScript() {
        if (WIKI_VAULT.IS_DEMO) return;

        $.ajax({
            url: getWikiVaultUri(WIKI_VAULT.INIT_API),
            type: 'GET',
            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);

                if (cachedCode && cachedLastModified && serverLastModified === Number(cachedLastModified)) {
                    executeCode(cachedCode);
                    return;
                }

                $.ajax({
                    url: getWikiVaultUri(WIKI_VAULT.JS_CONTENT_API),
                    type: 'GET',
                    dataType: 'text',
                    success: function(response) {
                        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("Script Fetch Failed:", status, error);
                        if (cachedCode) {
                            executeCode(cachedCode);
                        }
                    }
                });
            },
            error: function(xhr, status, error) {
                console.error("Init Request Failed:", status, error);
                const cachedCode = localStorage.getItem(WIKI_VAULT.STORAGE_KEYS.CACHED_CODE);
                if (cachedCode) {
                    executeCode(cachedCode);
                }
            }
        });
    }

    createFloatingButton();
    loadAndExecuteScript();
});