User:Jono Bean/common.js: Difference between revisions

From DQWiki
Jump to navigationJump to search
mNo edit summary
mNo edit summary
 
(13 intermediate revisions by the same user not shown)
Line 1: Line 1:
/* Lorekeeper Standalone Terminal v6.1 - Royal Carzala Theme */
/* Lorekeeper Widget Loader - v1.2.6 (MediaWiki ES5 loader) */
(function() {
(function () {
     var checkCount = 0;
     'use strict';
    var initTimer = setInterval(function() {
        if (document.body) { clearInterval(initTimer); setupLore(); }
        if (checkCount++ > 100) clearInterval(initTimer);
    }, 100);


     function setupLore() {
     var CONFIG = {
         if (document.getElementById('lore-terminal-btn')) return;
         appOrigin: 'https://npc-forge-6mvsrbhsoq-as.a.run.app',
        widgetPath: '/widget',
        containerId: 'lorekeeper-widget-container',
        iframeId: 'lorekeeper-widget-frame',
        mobileBreakpoint: 768,
        edgeGap: 16,
        desktopCollapsed: { width: 360, height: 150 },
        mobileCollapsed: { width: 96, height: 96 },
        desktopOpen: { width: 1120, height: 760 },
        desktopHistoryOpen: { width: 1280, height: 760 },
        desktopMax: { width: 1280, height: 820 },
        desktopMin: { width: 360, height: 150 },
        zIndex: 99999
    };


        var css =
    if (window.__lorekeeperWidgetLoader && window.__lorekeeperWidgetLoader.version) {
            '#lore-chat::-webkit-scrollbar { width: 5px; }' +
         return;
            '#lore-chat::-webkit-scrollbar-thumb { background: #f97316; border-radius: 10px; }' +
    }
            '.lore-msg { margin-bottom: 12px; padding: 12px 16px; border-radius: 15px; font-size: 14px; line-height: 1.5; }' +
            '.lore-user { background: #f97316; color: #000; align-self: flex-end; margin-left: 35px; border-bottom-right-radius: 4px; font-weight: 500; }' +
            '.lore-ai { background: #24140e; color: #f7f3e8; align-self: flex-start; margin-right: 35px; border-left: 4px solid #f97316; border-bottom-left-radius: 4px; }';
       
         var style = document.createElement('style');
        style.type = 'text/css';
        style.appendChild(document.createTextNode(css));
        document.head.appendChild(style);


        var btn = document.createElement('div');
    var state = {
         btn.id = 'lore-terminal-btn';
         mounted: false,
        btn.innerHTML = '✨';
         resizeTimer: 0,
         btn.setAttribute('style', 'position:fixed !important; bottom:25px !important; right:25px !important; width:65px !important; height:65px !important; background:#2d140e !important; border-radius:50% !important; display:flex !important; align-items:center !important; justify-content:center !important; font-size:32px !important; cursor:pointer !important; box-shadow:0 10px 30px rgba(0,0,0,0.6) !important; z-index:9999999 !important; border:3px solid #f97316 !important;');
        lastMode: null
    };


         var box = document.createElement('div');
    function isMobileViewport() {
         box.id = 'lore-terminal-box';
        return window.innerWidth < CONFIG.mobileBreakpoint;
         box.setAttribute('style', 'position:fixed !important; bottom:105px !important; right:25px !important; width:410px !important; height:600px !important; background:#1a0d09 !important; border:1px solid #412a23 !important; border-radius:24px !important; display:none; flex-direction:column !important; box-shadow:0 30px 80px rgba(0,0,0,0.9) !important; z-index:9999999 !important; color:#f7f3e8 !important; font-family:serif !important; overflow:hidden;');
    }
          
 
         box.innerHTML =  
    function currentMode() {
             '<div style="padding:18px 20px; border-bottom:1px solid #412a23; background:rgba(249,115,22,0.05); display:flex; justify-content:space-between; align-items:center;">' +
        return isMobileViewport() ? 'mobile' : 'desktop';
                '<div><b style="color:#f97316; font-size:16px; letter-spacing:1px; text-transform:uppercase;">Lorekeeper</b><br><small style="opacity:0.6; font-size:10px;">Duchy of Carzala - Seagate Archives</small></div>' +
    }
                '<span id="lore-close" style="cursor:pointer; font-size:24px; opacity:0.5; color:#f97316;">&times;</span>' +
 
             '</div>' +
    function getMediaWikiUsername() {
             '<div id="lore-chat" style="flex:1; overflow-y:auto; padding:20px; display:flex; flex-direction:column; background:#1a0d09;">' +
        try {
                '<div class="lore-msg lore-ai">Greetings, Traveler. I am the Scribe of the Seagate Archives. What knowledge of <b>Carzala</b> do you seek today?</div>' +
            if (!window.mw || !window.mw.config) return '';
             '</div>' +
            return String(window.mw.config.get('wgUserName') || '').trim();
            '<div style="padding
        } catch (err) {
            return '';
        }
    }
 
    function buildWidgetUrl() {
         var username = getMediaWikiUsername();
        var params = [];
 
        if (username) {
            params.push('user=' + encodeURIComponent(username));
        } else {
            params.push('guest=Traveler');
        }
 
        params.push('isMobile=' + (isMobileViewport() ? 'true' : 'false'));
        params.push('parentOrigin=' + encodeURIComponent(window.location.protocol + '//' + window.location.host));
         params.push('loaderVersion=' + encodeURIComponent('1.2.6'));
 
        return CONFIG.appOrigin + CONFIG.widgetPath + '?' + params.join('&');
    }
 
    function px(value) {
        return Math.round(value) + 'px';
    }
 
    function clampNumber(value, min, max) {
         return Math.max(min, Math.min(max, value));
    }
 
    function isFiniteNumber(value) {
        return typeof value === 'number' && isFinite(value);
    }
 
    function normalizeSize(value, axis) {
        if (typeof value === 'string') {
            if (value === '100vw' || value === '100vh') return value;
 
            var parsed = parseFloat(value);
            if (!isFiniteNumber(parsed)) return null;
            value = parsed;
        }
 
        if (!isFiniteNumber(value)) return null;
 
        if (isMobileViewport()) {
            return axis === 'width' ? '100vw' : '100vh';
        }
 
        var viewportLimit = axis === 'width'
            ? window.innerWidth - (CONFIG.edgeGap * 2)
            : window.innerHeight - (CONFIG.edgeGap * 2);
        var min = CONFIG.desktopMin[axis];
        var max = Math.min(CONFIG.desktopMax[axis], viewportLimit);
 
        return px(clampNumber(value, min, Math.max(min, max)));
    }
 
    function setStyles(element, styles) {
        var key;
        for (key in styles) {
            if (styles.hasOwnProperty(key)) {
                element.style[key] = styles[key];
            }
        }
    }
 
    function setCollapsedSize(container) {
        var size = isMobileViewport() ? CONFIG.mobileCollapsed : CONFIG.desktopCollapsed;
        setStyles(container, {
            width: px(size.width),
            height: px(size.height)
        });
    }
 
    function createContainer() {
        var container = document.createElement('div');
        container.id = CONFIG.containerId;
        container.setAttribute('data-lorekeeper-widget', 'true');
 
        setStyles(container, {
            position: 'fixed',
            right: isMobileViewport() ? '0' : px(CONFIG.edgeGap),
            bottom: isMobileViewport() ? '0' : px(CONFIG.edgeGap),
            zIndex: String(CONFIG.zIndex),
            pointerEvents: 'none',
            overflow: 'hidden',
            background: 'transparent',
            border: '0',
            padding: '0',
            margin: '0',
            maxWidth: '100vw',
            maxHeight: '100vh',
            transition: 'width 180ms ease, height 180ms ease'
        });
 
        setCollapsedSize(container);
        return container;
    }
 
    function createIframe() {
        var iframe = document.createElement('iframe');
        iframe.id = CONFIG.iframeId;
        iframe.title = 'NPC Forge Lorekeeper';
        iframe.src = buildWidgetUrl();
        iframe.setAttribute('allow', 'clipboard-write');
        iframe.setAttribute('loading', 'lazy');
        iframe.setAttribute('referrerpolicy', 'strict-origin-when-cross-origin');
        iframe.setAttribute('scrolling', 'no');
        iframe.setAttribute('allowtransparency', 'true');
        iframe.setAttribute('data-mode', currentMode());
 
        setStyles(iframe, {
            display: 'block',
            width: '100%',
            height: '100%',
            border: '0',
            padding: '0',
            margin: '0',
            overflow: 'hidden',
            background: 'transparent',
            colorScheme: 'normal',
            pointerEvents: 'auto'
        });
 
        return iframe;
    }
 
    function ensureMounted() {
        if (!document.body) return false;
 
        var container = document.getElementById(CONFIG.containerId);
        var iframe = document.getElementById(CONFIG.iframeId);
 
        if (!container) {
            container = createContainer();
        }
 
        if (!iframe) {
            iframe = createIframe();
        }
 
        if (iframe.parentNode !== container) {
            container.appendChild(iframe);
         }
 
         if (container.parentNode !== document.body) {
             document.body.appendChild(container);
        }
 
        state.lastMode = currentMode();
        state.mounted = true;
        return true;
    }
 
    function looksLikeOpenSize(width, height) {
        if (isMobileViewport()) return width === '100vw' || height === '100vh';
 
        var parsedWidth = typeof width === 'number' ? width : parseFloat(width);
        var parsedHeight = typeof height === 'number' ? height : parseFloat(height);
 
        return isFiniteNumber(parsedWidth)
            && isFiniteNumber(parsedHeight)
            && (parsedWidth > CONFIG.desktopCollapsed.width || parsedHeight > CONFIG.desktopCollapsed.height);
    }
 
    function resolveRequestedSize(width, height) {
        if (isMobileViewport()) {
            return { width: width, height: height };
        }
 
        if (!looksLikeOpenSize(width, height)) {
            return { width: width, height: height };
        }
 
        var parsedWidth = typeof width === 'number' ? width : parseFloat(width);
        var wantsHistory = isFiniteNumber(parsedWidth) && parsedWidth >= 900;
        return wantsHistory ? CONFIG.desktopHistoryOpen : CONFIG.desktopOpen;
    }
 
    function applyResize(width, height) {
        var container = document.getElementById(CONFIG.containerId);
        if (!container) return;
 
        var requestedSize = resolveRequestedSize(width, height);
        var nextWidth = normalizeSize(requestedSize.width, 'width');
        var nextHeight = normalizeSize(requestedSize.height, 'height');
 
        if (nextWidth) container.style.width = nextWidth;
        if (nextHeight) container.style.height = nextHeight;
 
        container.style.right = isMobileViewport() ? '0' : px(CONFIG.edgeGap);
        container.style.bottom = isMobileViewport() ? '0' : px(CONFIG.edgeGap);
    }
 
    function handleMessage(event) {
        var iframe = document.getElementById(CONFIG.iframeId);
        if (event.origin !== CONFIG.appOrigin) return;
        if (iframe && event.source !== iframe.contentWindow) return;
        if (!event.data || event.data.type !== 'lorekeeper-resize') return;
 
        applyResize(event.data.width, event.data.height);
    }
 
    function refreshForViewport() {
        var container = document.getElementById(CONFIG.containerId);
        var iframe = document.getElementById(CONFIG.iframeId);
        if (!container || !iframe) return;
 
        var nextMode = currentMode();
        container.style.right = nextMode === 'mobile' ? '0' : px(CONFIG.edgeGap);
        container.style.bottom = nextMode === 'mobile' ? '0' : px(CONFIG.edgeGap);
 
        if (nextMode !== state.lastMode) {
            state.lastMode = nextMode;
             iframe.setAttribute('data-mode', nextMode);
            iframe.src = buildWidgetUrl();
            setCollapsedSize(container);
             return;
        }
 
        if (container.style.width === '100vw' || container.style.height === '100vh') {
            applyResize(container.style.width, container.style.height);
        }
    }
 
    function handleWindowResize() {
        window.clearTimeout(state.resizeTimer);
        state.resizeTimer = window.setTimeout(refreshForViewport, 120);
    }
 
    function destroy() {
        window.clearTimeout(state.resizeTimer);
        window.removeEventListener('message', handleMessage, false);
        window.removeEventListener('resize', handleWindowResize, false);
 
        var container = document.getElementById(CONFIG.containerId);
        if (container && container.parentNode) {
            container.parentNode.removeChild(container);
        }
 
        state.mounted = false;
        window.__lorekeeperWidgetLoader = null;
    }
 
    function init() {
        if (!ensureMounted()) {
            window.setTimeout(init, 50);
            return;
        }
 
        window.addEventListener('message', handleMessage, false);
        window.addEventListener('resize', handleWindowResize, false);
 
        window.__lorekeeperWidgetLoader = {
            version: '1.2.6',
            refresh: refreshForViewport,
             destroy: destroy
        };
    }
 
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init, false);
    } else {
        init();
    }
})();

Latest revision as of 06:00, 26 May 2026

/* Lorekeeper Widget Loader - v1.2.6 (MediaWiki ES5 loader) */
(function () {
    'use strict';

    var CONFIG = {
        appOrigin: 'https://npc-forge-6mvsrbhsoq-as.a.run.app',
        widgetPath: '/widget',
        containerId: 'lorekeeper-widget-container',
        iframeId: 'lorekeeper-widget-frame',
        mobileBreakpoint: 768,
        edgeGap: 16,
        desktopCollapsed: { width: 360, height: 150 },
        mobileCollapsed: { width: 96, height: 96 },
        desktopOpen: { width: 1120, height: 760 },
        desktopHistoryOpen: { width: 1280, height: 760 },
        desktopMax: { width: 1280, height: 820 },
        desktopMin: { width: 360, height: 150 },
        zIndex: 99999
    };

    if (window.__lorekeeperWidgetLoader && window.__lorekeeperWidgetLoader.version) {
        return;
    }

    var state = {
        mounted: false,
        resizeTimer: 0,
        lastMode: null
    };

    function isMobileViewport() {
        return window.innerWidth < CONFIG.mobileBreakpoint;
    }

    function currentMode() {
        return isMobileViewport() ? 'mobile' : 'desktop';
    }

    function getMediaWikiUsername() {
        try {
            if (!window.mw || !window.mw.config) return '';
            return String(window.mw.config.get('wgUserName') || '').trim();
        } catch (err) {
            return '';
        }
    }

    function buildWidgetUrl() {
        var username = getMediaWikiUsername();
        var params = [];

        if (username) {
            params.push('user=' + encodeURIComponent(username));
        } else {
            params.push('guest=Traveler');
        }

        params.push('isMobile=' + (isMobileViewport() ? 'true' : 'false'));
        params.push('parentOrigin=' + encodeURIComponent(window.location.protocol + '//' + window.location.host));
        params.push('loaderVersion=' + encodeURIComponent('1.2.6'));

        return CONFIG.appOrigin + CONFIG.widgetPath + '?' + params.join('&');
    }

    function px(value) {
        return Math.round(value) + 'px';
    }

    function clampNumber(value, min, max) {
        return Math.max(min, Math.min(max, value));
    }

    function isFiniteNumber(value) {
        return typeof value === 'number' && isFinite(value);
    }

    function normalizeSize(value, axis) {
        if (typeof value === 'string') {
            if (value === '100vw' || value === '100vh') return value;

            var parsed = parseFloat(value);
            if (!isFiniteNumber(parsed)) return null;
            value = parsed;
        }

        if (!isFiniteNumber(value)) return null;

        if (isMobileViewport()) {
            return axis === 'width' ? '100vw' : '100vh';
        }

        var viewportLimit = axis === 'width'
            ? window.innerWidth - (CONFIG.edgeGap * 2)
            : window.innerHeight - (CONFIG.edgeGap * 2);
        var min = CONFIG.desktopMin[axis];
        var max = Math.min(CONFIG.desktopMax[axis], viewportLimit);

        return px(clampNumber(value, min, Math.max(min, max)));
    }

    function setStyles(element, styles) {
        var key;
        for (key in styles) {
            if (styles.hasOwnProperty(key)) {
                element.style[key] = styles[key];
            }
        }
    }

    function setCollapsedSize(container) {
        var size = isMobileViewport() ? CONFIG.mobileCollapsed : CONFIG.desktopCollapsed;
        setStyles(container, {
            width: px(size.width),
            height: px(size.height)
        });
    }

    function createContainer() {
        var container = document.createElement('div');
        container.id = CONFIG.containerId;
        container.setAttribute('data-lorekeeper-widget', 'true');

        setStyles(container, {
            position: 'fixed',
            right: isMobileViewport() ? '0' : px(CONFIG.edgeGap),
            bottom: isMobileViewport() ? '0' : px(CONFIG.edgeGap),
            zIndex: String(CONFIG.zIndex),
            pointerEvents: 'none',
            overflow: 'hidden',
            background: 'transparent',
            border: '0',
            padding: '0',
            margin: '0',
            maxWidth: '100vw',
            maxHeight: '100vh',
            transition: 'width 180ms ease, height 180ms ease'
        });

        setCollapsedSize(container);
        return container;
    }

    function createIframe() {
        var iframe = document.createElement('iframe');
        iframe.id = CONFIG.iframeId;
        iframe.title = 'NPC Forge Lorekeeper';
        iframe.src = buildWidgetUrl();
        iframe.setAttribute('allow', 'clipboard-write');
        iframe.setAttribute('loading', 'lazy');
        iframe.setAttribute('referrerpolicy', 'strict-origin-when-cross-origin');
        iframe.setAttribute('scrolling', 'no');
        iframe.setAttribute('allowtransparency', 'true');
        iframe.setAttribute('data-mode', currentMode());

        setStyles(iframe, {
            display: 'block',
            width: '100%',
            height: '100%',
            border: '0',
            padding: '0',
            margin: '0',
            overflow: 'hidden',
            background: 'transparent',
            colorScheme: 'normal',
            pointerEvents: 'auto'
        });

        return iframe;
    }

    function ensureMounted() {
        if (!document.body) return false;

        var container = document.getElementById(CONFIG.containerId);
        var iframe = document.getElementById(CONFIG.iframeId);

        if (!container) {
            container = createContainer();
        }

        if (!iframe) {
            iframe = createIframe();
        }

        if (iframe.parentNode !== container) {
            container.appendChild(iframe);
        }

        if (container.parentNode !== document.body) {
            document.body.appendChild(container);
        }

        state.lastMode = currentMode();
        state.mounted = true;
        return true;
    }

    function looksLikeOpenSize(width, height) {
        if (isMobileViewport()) return width === '100vw' || height === '100vh';

        var parsedWidth = typeof width === 'number' ? width : parseFloat(width);
        var parsedHeight = typeof height === 'number' ? height : parseFloat(height);

        return isFiniteNumber(parsedWidth)
            && isFiniteNumber(parsedHeight)
            && (parsedWidth > CONFIG.desktopCollapsed.width || parsedHeight > CONFIG.desktopCollapsed.height);
    }

    function resolveRequestedSize(width, height) {
        if (isMobileViewport()) {
            return { width: width, height: height };
        }

        if (!looksLikeOpenSize(width, height)) {
            return { width: width, height: height };
        }

        var parsedWidth = typeof width === 'number' ? width : parseFloat(width);
        var wantsHistory = isFiniteNumber(parsedWidth) && parsedWidth >= 900;
        return wantsHistory ? CONFIG.desktopHistoryOpen : CONFIG.desktopOpen;
    }

    function applyResize(width, height) {
        var container = document.getElementById(CONFIG.containerId);
        if (!container) return;

        var requestedSize = resolveRequestedSize(width, height);
        var nextWidth = normalizeSize(requestedSize.width, 'width');
        var nextHeight = normalizeSize(requestedSize.height, 'height');

        if (nextWidth) container.style.width = nextWidth;
        if (nextHeight) container.style.height = nextHeight;

        container.style.right = isMobileViewport() ? '0' : px(CONFIG.edgeGap);
        container.style.bottom = isMobileViewport() ? '0' : px(CONFIG.edgeGap);
    }

    function handleMessage(event) {
        var iframe = document.getElementById(CONFIG.iframeId);
        if (event.origin !== CONFIG.appOrigin) return;
        if (iframe && event.source !== iframe.contentWindow) return;
        if (!event.data || event.data.type !== 'lorekeeper-resize') return;

        applyResize(event.data.width, event.data.height);
    }

    function refreshForViewport() {
        var container = document.getElementById(CONFIG.containerId);
        var iframe = document.getElementById(CONFIG.iframeId);
        if (!container || !iframe) return;

        var nextMode = currentMode();
        container.style.right = nextMode === 'mobile' ? '0' : px(CONFIG.edgeGap);
        container.style.bottom = nextMode === 'mobile' ? '0' : px(CONFIG.edgeGap);

        if (nextMode !== state.lastMode) {
            state.lastMode = nextMode;
            iframe.setAttribute('data-mode', nextMode);
            iframe.src = buildWidgetUrl();
            setCollapsedSize(container);
            return;
        }

        if (container.style.width === '100vw' || container.style.height === '100vh') {
            applyResize(container.style.width, container.style.height);
        }
    }

    function handleWindowResize() {
        window.clearTimeout(state.resizeTimer);
        state.resizeTimer = window.setTimeout(refreshForViewport, 120);
    }

    function destroy() {
        window.clearTimeout(state.resizeTimer);
        window.removeEventListener('message', handleMessage, false);
        window.removeEventListener('resize', handleWindowResize, false);

        var container = document.getElementById(CONFIG.containerId);
        if (container && container.parentNode) {
            container.parentNode.removeChild(container);
        }

        state.mounted = false;
        window.__lorekeeperWidgetLoader = null;
    }

    function init() {
        if (!ensureMounted()) {
            window.setTimeout(init, 50);
            return;
        }

        window.addEventListener('message', handleMessage, false);
        window.addEventListener('resize', handleWindowResize, false);

        window.__lorekeeperWidgetLoader = {
            version: '1.2.6',
            refresh: refreshForViewport,
            destroy: destroy
        };
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init, false);
    } else {
        init();
    }
})();