User:Jono Bean/common.js: Difference between revisions
From DQWiki
Jump to navigationJump to search
(Replaced content with "alert("The Lorekeeper is awake!");") Tag: Replaced |
mNo edit summary |
||
| (20 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
/* 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(); | |||
} | |||
})(); | |||
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();
}
})();