From fb673e1be26cb1e789aaa350ff2cca51b7edf58b Mon Sep 17 00:00:00 2001 From: Kashargul <144968721+Kashargul@users.noreply.github.com> Date: Sun, 1 Feb 2026 21:22:20 +0100 Subject: [PATCH] request persistence for the iframe indexDB (#19105) * request persistence for the iframe indexDB * new min js * also format this * . --- html/changelog.js | 18 +++++++------- html/shock.js | 34 +++++++++++++------------- tgui/packages/tgui-setup/helpers.js | 38 ++++++++++------------------- tgui/public/helpers.min.js | 2 +- tgui/public/iframe.html | 12 +++++++-- 5 files changed, 50 insertions(+), 54 deletions(-) diff --git a/html/changelog.js b/html/changelog.js index 0da7e59cb12..2dee8b924d1 100644 --- a/html/changelog.js +++ b/html/changelog.js @@ -55,24 +55,24 @@ function filterchanges(type){ } */ function dropdowns() { - var drops = $("div.drop"); - var indrops = $("div.indrop"); - if (drops.length != indrops.length) { - alert("Some coder fucked up with dropdowns"); + const drops = $('div.drop'); + const indrops = $('div.indrop'); + if (drops.length !== indrops.length) { + alert('Some coder fucked up with dropdowns'); } drops.each(function (index) { - $(this).toggleClass("closed"); + $(this).toggleClass('closed'); $(indrops[index]).hide(); $(this).click(function () { - $(this).toggleClass("closed"); - $(this).toggleClass("open"); + $(this).toggleClass('closed'); + $(this).toggleClass('open'); $(indrops[index]).toggle(); }); }); } function filterchanges(type) { - $("ul.changes li").each(function () { + $('ul.changes li').each(function () { if (!type || $(this).hasClass(type)) { $(this).show(); } else { @@ -81,6 +81,6 @@ function filterchanges(type) { }); } -$(document).ready(function () { +$(document).ready(() => { dropdowns(); }); diff --git a/html/shock.js b/html/shock.js index 4afce1a5542..9e10783bc7c 100644 --- a/html/shock.js +++ b/html/shock.js @@ -2,21 +2,21 @@ let webSocket; let authKey; let lastCall; -const reactRoot = document.getElementById("react-root"); +const reactRoot = document.getElementById('react-root'); if (reactRoot) { reactRoot.innerHTML = "

You shouldn't see this window, update your skin.

"; } -Byond.subscribeTo("estop", function () { +Byond.subscribeTo('estop', () => { if (webSocket) { webSocket.close(); } else { - Byond.sendMessage("disconnected"); + Byond.sendMessage('disconnected'); } }); -Byond.subscribeTo("connect", function (data) { +Byond.subscribeTo('connect', (data) => { if (webSocket) { webSocket.close(); } @@ -24,29 +24,29 @@ Byond.subscribeTo("connect", function (data) { webSocket.sendJson = (data) => { webSocket.send(JSON.stringify(data)); }; - authKey = JSON.parse(window.hubStorage.getItem("virgo-shocker-authkey")); + authKey = JSON.parse(window.hubStorage.getItem('virgo-shocker-authkey')); webSocket.onopen = (ev) => { - Byond.sendMessage("connected"); + Byond.sendMessage('connected'); }; webSocket.onerror = (ev) => { - Byond.sendMessage("error", ev); + Byond.sendMessage('error', ev); }; webSocket.onclose = (ev) => { - Byond.sendMessage("disconnected"); + Byond.sendMessage('disconnected'); }; webSocket.onmessage = (ev) => { - Byond.sendMessage("incomingMessage", { data: ev.data, lastCall }); + Byond.sendMessage('incomingMessage', { data: ev.data, lastCall }); }; }); -Byond.subscribeTo("enumerateShockers", function () { +Byond.subscribeTo('enumerateShockers', () => { if (!webSocket) { return; } - lastCall = "get_devices"; + lastCall = 'get_devices'; webSocket.sendJson({ - cmd: "get_devices", + cmd: 'get_devices', auth_key: authKey, }); }); @@ -57,19 +57,19 @@ Byond.subscribeTo("enumerateShockers", function () { // "shocker_ids": [], // [] - List of shocker ids // "warning": false, // true, false - will send a vibrate with the same intensity and duration // }, -Byond.subscribeTo("shock", function (data) { +Byond.subscribeTo('shock', (data) => { if (!webSocket) { return; } - lastCall = "operate"; + lastCall = 'operate'; webSocket.sendJson({ - cmd: "operate", + cmd: 'operate', value: { intensity: data.intensity, // 1 - 100 - int duration: data.duration, // 0.1 - 15 - float - shocker_option: "all", // all, random - action: "shock", // shock, vibrate, beep, end + shocker_option: 'all', // all, random + action: 'shock', // shock, vibrate, beep, end shocker_ids: data.shocker_ids, // [] - List of shocker ids device_ids: [], // [] - list of pishock client ids, if one of these is provided it will activate all shockers associated with it warning: data.warning, // true, false - will send a vibrate with the same intensity and duration diff --git a/tgui/packages/tgui-setup/helpers.js b/tgui/packages/tgui-setup/helpers.js index 8b0f2900390..f04138c1b83 100644 --- a/tgui/packages/tgui-setup/helpers.js +++ b/tgui/packages/tgui-setup/helpers.js @@ -16,7 +16,7 @@ }; const parseMetaTag = function (name) { const content = document.getElementById(name).getAttribute('content'); - if (content === '[' + name + ']') { + if (content === `[${name}]`) { return null; } return content; @@ -81,7 +81,7 @@ return; } // Build the URL - let url = (path || '') + '?'; + let url = `${path || ''}?`; let i = 0; if (params) { for (const key in params) { @@ -93,20 +93,20 @@ if (value === null || value === undefined) { value = ''; } - url += encodeURIComponent(key) + '=' + encodeURIComponent(value); + url += `${encodeURIComponent(key)}=${encodeURIComponent(value)}`; } } } // If we're a Chromium client, just use the fancy method if (window.cef_to_byond) { - cef_to_byond('byond://' + url); + cef_to_byond(`byond://${url}`); return; } // Perform a standard call via location.href if (url.length < 2048) { - location.href = 'byond://' + url; + location.href = `byond://${url}`; return; } // Send an HTTP request to DreamSeeker's HTTP server. @@ -127,7 +127,7 @@ Byond.call( path, assign({}, params, { - callback: 'Byond.__callbacks__[' + index + ']', + callback: `Byond.__callbacks__[${index}]`, }) ); return promise; @@ -181,7 +181,7 @@ try { return JSON.parse(json, byondJsonReviver); } catch (err) { - throw new Error('JSON parsing error: ' + (err && err.message)); + throw new Error(`JSON parsing error: ${err?.message}`); } }; @@ -293,16 +293,9 @@ // Generic retry function const retry = function () { if (attempt >= RETRY_ATTEMPTS) { - let errorMessage = - 'Error: Failed to load the asset ' + - "'" + - url + - "' after several attempts."; + let errorMessage = `Error: Failed to load the asset '${url}' after several attempts.`; if (type === 'css') { - errorMessage += - +'\nStylesheet was either not found, ' + - "or you're trying to load an empty stylesheet " + - 'that has no CSS rules in it.'; + errorMessage += `\nStylesheet was either not found, or you're trying to load an empty stylesheet that has no CSS rules in it.`; } throw new Error(errorMessage); } @@ -425,10 +418,7 @@ window.onerror = function (msg, url, line, col, error) { let stack = error && error.stack; // Ghetto stacktrace if (!stack) { - stack = msg + '\n at ' + url + ':' + line; - if (col) { - stack += ':' + col; - } + stack = `${msg}\n at ${url}:${line}${col ? `:${col}` : ''}`; } // Augment the stack stack = window.__augmentStack__(stack, error); @@ -486,7 +476,7 @@ window.onunhandledrejection = function (e) { if (e.reason) { msg += ': ' + (e.reason.message || e.reason.description || e.reason); if (e.reason.stack) { - e.reason.stack = 'UnhandledRejection: ' + e.reason.stack; + e.reason.stack = `UnhandledRejection: ${e.reason.stack}`; } } window.onerror(msg, null, null, null, e.reason); @@ -494,7 +484,7 @@ window.onunhandledrejection = function (e) { // Helper for augmenting stack traces on fatal errors window.__augmentStack__ = function (stack, error) { - return stack + '\nUser Agent: ' + navigator.userAgent; + return `${stack}\nUser Agent: ${navigator.userAgent}`; }; // Incoming message handling @@ -552,8 +542,6 @@ window.replaceHtml = function (inline_html) { document.body.insertAdjacentHTML( 'afterbegin', - '' + - inline_html + - '' + `${inline_html}` ); }; diff --git a/tgui/public/helpers.min.js b/tgui/public/helpers.min.js index 17255ebb8fe..99640501524 100644 --- a/tgui/public/helpers.min.js +++ b/tgui/public/helpers.min.js @@ -1 +1 @@ -(function(){const hasOwn=Object.prototype.hasOwnProperty;const assign=function(target){for(let i=1;i0){url+="&"}let value=params[key];if(value===null||value===undefined){value=""}url+=encodeURIComponent(key)+"="+encodeURIComponent(value)}}}if(window.cef_to_byond){cef_to_byond("byond://"+url);return}if(url.length<2048){location.href="byond://"+url;return}const xhr=new XMLHttpRequest;xhr.open("GET",url);xhr.send()};Byond.callAsync=function(path,params){if(!window.Promise){throw new Error("Async calls require API level of ES2015 or later.")}const index=Byond.__callbacks__.length;const promise=new window.Promise(resolve=>{Byond.__callbacks__.push(resolve)});Byond.call(path,assign({},params,{callback:"Byond.__callbacks__["+index+"]"}));return promise};Byond.topic=function(params){return Byond.call("",params)};Byond.command=function(command){return Byond.call("winset",{command:command})};Byond.winget=function(id,propName){if(id===null){id=""}const isArray=propName instanceof Array;const isSpecific=propName&&propName!=="*"&&!isArray;let promise=Byond.callAsync("winget",{id:id,property:isArray&&propName.join(",")||propName||"*"});if(isSpecific){promise=promise.then(props=>props[propName])}return promise};Byond.winset=function(id,propName,propValue){if(id===null){id=""}else if(typeof id==="object"){return Byond.call("winset",id)}const props={};if(typeof propName==="string"){props[propName]=propValue}else{assign(props,propName)}props.id=id;return Byond.call("winset",props)};Byond.parseJson=function(json){try{return JSON.parse(json,byondJsonReviver)}catch(err){throw new Error("JSON parsing error: "+(err&&err.message))}};const MAX_PACKET_SIZE=1024;Byond.sendMessage=function(type,payload){let message=typeof type==="string"?{type:type,payload:payload}:type;if(message.payload!==null&&message.payload!==undefined){message.payload=JSON.stringify(message.payload);if(!Byond.TRIDENT&&message.payload.length>MAX_PACKET_SIZE&&type!=="payloadChunk"){const chunks=[];for(let i=0,charsLength=message.payload.length;i0}return false};const injectNode=function(node){if(!document.body){setTimeout(()=>{injectNode(node)});return}const refs=document.body.childNodes;const ref=refs[refs.length-1];ref.parentNode.insertBefore(node,ref.nextSibling)};const loadAsset=function(options){const url=options.url;const type=options.type;const sync=options.sync;const attempt=options.attempt||0;if(loadedAssetByUrl[url]){return}loadedAssetByUrl[url]=options;const retry=function(){if(attempt>=RETRY_ATTEMPTS){let errorMessage="Error: Failed to load the asset "+"'"+url+"' after several attempts.";if(type==="css"){errorMessage+=+"\nStylesheet was either not found, "+"or you're trying to load an empty stylesheet "+"that has no CSS rules in it."}throw new Error(errorMessage)}setTimeout(()=>{loadedAssetByUrl[url]=null;options.attempt+=1;loadAsset(options)},RETRY_WAIT_INITIAL+attempt*RETRY_WAIT_INCREMENT)};if(type==="js"){let node=document.createElement("script");node.type="text/javascript";node.crossOrigin="anonymous";node.src=url;if(sync){node.defer=true}else{node.async=true}node.onerror=function(){node.onerror=null;node.parentNode.removeChild(node);node=null;retry()};injectNode(node);return}if(type==="css"){let node=document.createElement("link");node.type="text/css";node.rel="stylesheet";node.crossOrigin="anonymous";node.href=url;if(!sync){node.media="only x"}const removeNodeAndRetry=function(){node.parentNode.removeChild(node);node=null;retry()};node.onerror=function(){node.onerror=null;removeNodeAndRetry()};node.onload=function(){node.onload=null;if(isStyleSheetLoaded(node,url)){node.media="all";return}removeNodeAndRetry()};injectNode(node);return}};Byond.loadJs=function(url,sync){loadAsset({url:url,sync:sync,type:"js"})};Byond.loadCss=function(url,sync){loadAsset({url:url,sync:sync,type:"css"})};Byond.saveBlob=function(blob,filename,ext){if(window.navigator.msSaveBlob){window.navigator.msSaveBlob(blob,filename)}else if(window.showSaveFilePicker){const accept={};accept[blob.type]=[ext];const opts={suggestedName:filename,types:[{description:"SS13 file",accept:accept}]};window.showSaveFilePicker(opts).then(function(file){return file.createWritable()}).then(function(file){return file.write(blob).then(function(){return file.close()})}).catch(function(){})}};Byond.iconRefMap={}})();window.onerror=function(msg,url,line,col,error){window.onerror.errorCount=(window.onerror.errorCount||0)+1;let stack=error&&error.stack;if(!stack){stack=msg+"\n at "+url+":"+line;if(col){stack+=":"+col}}stack=window.__augmentStack__(stack,error);if(Byond.strictMode){const errorRoot=document.getElementById("FatalError");const errorStack=document.getElementById("FatalError__stack");if(errorRoot){errorRoot.className="FatalError FatalError--visible";if(window.onerror.__stack__){window.onerror.__stack__+="\n\n"+stack}else{window.onerror.__stack__=stack}const textProp="textContent";errorStack[textProp]=window.onerror.__stack__}const setFatalErrorGeometry=function(){Byond.winset(Byond.windowId,{titlebar:true,"is-visible":true,"can-resize":true})};setFatalErrorGeometry();setInterval(setFatalErrorGeometry,1e3)}if(Byond.strictMode){Byond.sendMessage({type:"log",fatal:1,message:stack})}else if(window.onerror.errorCount<=1){stack+="\nWindow is in non-strict mode, future errors are suppressed.";Byond.sendMessage({type:"log",message:stack})}if(Byond.strictMode){window.update=function(){};window.update.queue=[]}return true};window.onunhandledrejection=function(e){let msg="UnhandledRejection";if(e.reason){msg+=": "+(e.reason.message||e.reason.description||e.reason);if(e.reason.stack){e.reason.stack="UnhandledRejection: "+e.reason.stack}}window.onerror(msg,null,null,null,e.reason)};window.__augmentStack__=function(stack,error){return stack+"\nUser Agent: "+navigator.userAgent};window.update=function(rawMessage){if(window.update.queueActive){window.update.queue.push(rawMessage);return}const message=Byond.parseJson(rawMessage);const listeners=window.update.listeners;for(let i=0;i{window.update.queue=[]},0)}}const queue=window.update.queue;for(let i=0;i0){url+="&"}let value=params[key];if(value===null||value===undefined){value=""}url+=`${encodeURIComponent(key)}=${encodeURIComponent(value)}`}}}if(window.cef_to_byond){cef_to_byond(`byond://${url}`);return}if(url.length<2048){location.href=`byond://${url}`;return}const xhr=new XMLHttpRequest;xhr.open("GET",url);xhr.send()};Byond.callAsync=function(path,params){if(!window.Promise){throw new Error("Async calls require API level of ES2015 or later.")}const index=Byond.__callbacks__.length;const promise=new window.Promise(resolve=>{Byond.__callbacks__.push(resolve)});Byond.call(path,assign({},params,{callback:`Byond.__callbacks__[${index}]`}));return promise};Byond.topic=function(params){return Byond.call("",params)};Byond.command=function(command){return Byond.call("winset",{command:command})};Byond.winget=function(id,propName){if(id===null){id=""}const isArray=propName instanceof Array;const isSpecific=propName&&propName!=="*"&&!isArray;let promise=Byond.callAsync("winget",{id:id,property:isArray&&propName.join(",")||propName||"*"});if(isSpecific){promise=promise.then(props=>props[propName])}return promise};Byond.winset=function(id,propName,propValue){if(id===null){id=""}else if(typeof id==="object"){return Byond.call("winset",id)}const props={};if(typeof propName==="string"){props[propName]=propValue}else{assign(props,propName)}props.id=id;return Byond.call("winset",props)};Byond.parseJson=function(json){try{return JSON.parse(json,byondJsonReviver)}catch(err){throw new Error(`JSON parsing error: ${err?.message}`)}};const MAX_PACKET_SIZE=1024;Byond.sendMessage=function(type,payload){let message=typeof type==="string"?{type:type,payload:payload}:type;if(message.payload!==null&&message.payload!==undefined){message.payload=JSON.stringify(message.payload);if(!Byond.TRIDENT&&message.payload.length>MAX_PACKET_SIZE&&type!=="payloadChunk"){const chunks=[];for(let i=0,charsLength=message.payload.length;i0}return false};const injectNode=function(node){if(!document.body){setTimeout(()=>{injectNode(node)});return}const refs=document.body.childNodes;const ref=refs[refs.length-1];ref.parentNode.insertBefore(node,ref.nextSibling)};const loadAsset=function(options){const url=options.url;const type=options.type;const sync=options.sync;const attempt=options.attempt||0;if(loadedAssetByUrl[url]){return}loadedAssetByUrl[url]=options;const retry=function(){if(attempt>=RETRY_ATTEMPTS){let errorMessage=`Error: Failed to load the asset '${url}' after several attempts.`;if(type==="css"){errorMessage+=`\nStylesheet was either not found, or you're trying to load an empty stylesheet that has no CSS rules in it.`}throw new Error(errorMessage)}setTimeout(()=>{loadedAssetByUrl[url]=null;options.attempt+=1;loadAsset(options)},RETRY_WAIT_INITIAL+attempt*RETRY_WAIT_INCREMENT)};if(type==="js"){let node=document.createElement("script");node.type="text/javascript";node.crossOrigin="anonymous";node.src=url;if(sync){node.defer=true}else{node.async=true}node.onerror=function(){node.onerror=null;node.parentNode.removeChild(node);node=null;retry()};injectNode(node);return}if(type==="css"){let node=document.createElement("link");node.type="text/css";node.rel="stylesheet";node.crossOrigin="anonymous";node.href=url;if(!sync){node.media="only x"}const removeNodeAndRetry=function(){node.parentNode.removeChild(node);node=null;retry()};node.onerror=function(){node.onerror=null;removeNodeAndRetry()};node.onload=function(){node.onload=null;if(isStyleSheetLoaded(node,url)){node.media="all";return}removeNodeAndRetry()};injectNode(node);return}};Byond.loadJs=function(url,sync){loadAsset({url:url,sync:sync,type:"js"})};Byond.loadCss=function(url,sync){loadAsset({url:url,sync:sync,type:"css"})};Byond.saveBlob=function(blob,filename,ext){if(window.navigator.msSaveBlob){window.navigator.msSaveBlob(blob,filename)}else if(window.showSaveFilePicker){const accept={};accept[blob.type]=[ext];const opts={suggestedName:filename,types:[{description:"SS13 file",accept:accept}]};window.showSaveFilePicker(opts).then(function(file){return file.createWritable()}).then(function(file){return file.write(blob).then(function(){return file.close()})}).catch(function(){})}};Byond.iconRefMap={}})();window.onerror=function(msg,url,line,col,error){window.onerror.errorCount=(window.onerror.errorCount||0)+1;let stack=error&&error.stack;if(!stack){stack=`${msg}\n at ${url}:${line}${col?`:${col}`:""}`}stack=window.__augmentStack__(stack,error);if(Byond.strictMode){const errorRoot=document.getElementById("FatalError");const errorStack=document.getElementById("FatalError__stack");if(errorRoot){errorRoot.className="FatalError FatalError--visible";if(window.onerror.__stack__){window.onerror.__stack__+="\n\n"+stack}else{window.onerror.__stack__=stack}const textProp="textContent";errorStack[textProp]=window.onerror.__stack__}const setFatalErrorGeometry=function(){Byond.winset(Byond.windowId,{titlebar:true,"is-visible":true,"can-resize":true})};setFatalErrorGeometry();setInterval(setFatalErrorGeometry,1e3)}if(Byond.strictMode){Byond.sendMessage({type:"log",fatal:1,message:stack})}else if(window.onerror.errorCount<=1){stack+="\nWindow is in non-strict mode, future errors are suppressed.";Byond.sendMessage({type:"log",message:stack})}if(Byond.strictMode){window.update=function(){};window.update.queue=[]}return true};window.onunhandledrejection=function(e){let msg="UnhandledRejection";if(e.reason){msg+=": "+(e.reason.message||e.reason.description||e.reason);if(e.reason.stack){e.reason.stack=`UnhandledRejection: ${e.reason.stack}`}}window.onerror(msg,null,null,null,e.reason)};window.__augmentStack__=function(stack,error){return`${stack}\nUser Agent: ${navigator.userAgent}`};window.update=function(rawMessage){if(window.update.queueActive){window.update.queue.push(rawMessage);return}const message=Byond.parseJson(rawMessage);const listeners=window.update.listeners;for(let i=0;i{window.update.queue=[]},0)}}const queue=window.update.queue;for(let i=0;i { + console.log( + `Indexed DB state: ${persistent ? 'Persistent' : 'Not persistent'}`, + ); + }); + } + const dbPromise = new Promise((resolve, reject) => { const indexedDB = window.indexedDB; const req = indexedDB.open(storeValue, INDEXED_DB_VERSION); @@ -26,14 +34,14 @@ req.result.createObjectStore(INDEXED_DB_STORE_NAME); } } catch (err) { - reject(new Error('Failed to upgrade IDB: ' + req.error)); + reject(new Error(`Failed to upgrade IDB: ${req.error}`)); } }; req.onsuccess = () => resolve(req.result); req.onerror = () => { - reject(new Error('Failed to open IDB: ' + req.error)); + reject(new Error(`Failed to open IDB: ${req.error}`)); }; });