Spaces:
Build error
Build error
/* | |
* Copyright (c) 2024 lax1dude. All Rights Reserved. | |
* | |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, | |
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
* POSSIBILITY OF SUCH DAMAGE. | |
* | |
*/ | |
const platfApplicationName = "platformApplication"; | |
/** | |
* @param {string} str | |
* @param {string} blobUrl | |
* @param {function()|null} blobRevoke | |
*/ | |
function downloadFileImpl(str, blobUrl, blobRevoke) { | |
const el = document.createElement("a"); | |
el.style.position = "absolute"; | |
el.style.left = "0px"; | |
el.style.top = "0px"; | |
el.style.zIndex = "-100"; | |
el.style.color = "transparent"; | |
el.innerText = "Download File"; | |
el.href = blobUrl; | |
el.target = "_blank"; | |
el.download = str; | |
parentElement.appendChild(el); | |
setTimeout(function() { | |
el.click(); | |
setTimeout(function() { | |
parentElement.removeChild(el); | |
}, 50); | |
if(blobRevoke) { | |
setTimeout(blobRevoke, 60000); | |
} | |
}, 50); | |
} | |
/** @type {number} */ | |
const bufferSpoolSize = 256; | |
/** @type {number} */ | |
const windowMaxMessages = 2048; | |
/** @type {number} */ | |
var messageBufferLength = 0; | |
/** @type {Object|null} */ | |
var messageBufferStart = null; | |
/** @type {Object|null} */ | |
var messageBufferEnd = null; | |
/** @type {Window|null} */ | |
var loggerWindow = null; | |
/** @type {function(string, boolean)|null} */ | |
var loggerWindowMsgHandler = null; | |
/** | |
* @param {string} text | |
* @param {boolean} isErr | |
*/ | |
function addLogMessageImpl(text, isErr) { | |
if(!loggerWindow) { | |
var newEl = { | |
"msg": text, | |
"err": isErr, | |
"next": null | |
}; | |
if(messageBufferEnd) { | |
messageBufferEnd["next"] = newEl; | |
} | |
if(!messageBufferStart) { | |
messageBufferStart = newEl; | |
} | |
messageBufferEnd = newEl; | |
++messageBufferLength; | |
while(messageBufferLength > bufferSpoolSize) { | |
--messageBufferLength; | |
if(messageBufferStart) { | |
messageBufferStart = messageBufferStart["next"]; | |
} | |
} | |
}else if(loggerWindowMsgHandler) { | |
loggerWindowMsgHandler(text, isErr); | |
} | |
} | |
/** | |
* @param {Object} applicationImports | |
*/ | |
function initializePlatfApplication(applicationImports) { | |
/** | |
* @param {string} str | |
* @return {boolean} | |
*/ | |
applicationImports["setClipboard"] = function setClipboardImpl(str) { | |
try { | |
if(window.navigator.clipboard) { | |
window.navigator.clipboard.writeText(str); | |
return true; | |
} | |
}catch(ex) { | |
eagError("Failed to set clipboard data!"); | |
} | |
return false; | |
}; | |
/** | |
* @return {Promise<string|null>} | |
*/ | |
async function getClipboardImpl() { | |
var txt = null; | |
try { | |
if(window.navigator.clipboard) { | |
txt = await navigator.clipboard.readText(); | |
} | |
}catch(ex) { | |
eagError("Failed to read clipboard data!"); | |
} | |
return txt; | |
} | |
applicationImports["getClipboard"] = new WebAssembly.Suspending(getClipboardImpl); | |
/** @type {boolean} */ | |
var fileChooserHasResult = false; | |
/** @type {Object|null} */ | |
var fileChooserResultObject = null; | |
/** @type {HTMLInputElement|null} */ | |
var fileChooserElement = null; | |
/** @type {HTMLElement|null} */ | |
var fileChooserMobileElement = null; | |
/** | |
* @param {string} mime | |
* @param {string} ext | |
*/ | |
applicationImports["displayFileChooser"] = function(mime, ext) { | |
clearFileChooserResultImpl(); | |
if(isLikelyMobileBrowser) { | |
const el = fileChooserMobileElement = /** @type {HTMLElement} */ (document.createElement("div")); | |
el.classList.add("_eaglercraftX_mobile_file_chooser_popup"); | |
el.style.position = "absolute"; | |
el.style.backgroundColor = "white"; | |
el.style.fontFamily = "sans-serif"; | |
el.style.top = "10%"; | |
el.style.left = "10%"; | |
el.style.right = "10%"; | |
el.style.border = "5px double black"; | |
el.style.padding = "15px"; | |
el.style.textAlign = "left"; | |
el.style.fontSize = "20px"; | |
el.style.userSelect = "none"; | |
el.style.zIndex = "150"; | |
const fileChooserTitle = document.createElement("h3"); | |
fileChooserTitle.appendChild(document.createTextNode("File Chooser")); | |
el.appendChild(fileChooserTitle); | |
const inputElementContainer = document.createElement("p"); | |
const inputElement = fileChooserElement = /** @type {HTMLInputElement} */ (document.createElement("input")); | |
inputElement.type = "file"; | |
if(mime === null) { | |
inputElement.accept = "." + ext; | |
}else { | |
inputElement.accept = mime; | |
} | |
inputElement.multiple = false; | |
inputElementContainer.appendChild(inputElement); | |
el.appendChild(inputElementContainer); | |
const fileChooserButtons = document.createElement("p"); | |
const fileChooserButtonCancel = document.createElement("button"); | |
fileChooserButtonCancel.classList.add("_eaglercraftX_mobile_file_chooser_btn_cancel"); | |
fileChooserButtonCancel.style.fontSize = "1.0em"; | |
fileChooserButtonCancel.addEventListener("click", function(/** Event */ evt) { | |
if(fileChooserMobileElement === el) { | |
parentElement.removeChild(el); | |
fileChooserMobileElement = null; | |
fileChooserElement = null; | |
} | |
}); | |
fileChooserButtonCancel.appendChild(document.createTextNode("Cancel")); | |
fileChooserButtons.appendChild(fileChooserButtonCancel); | |
fileChooserButtons.appendChild(document.createTextNode(" ")); | |
const fileChooserButtonDone = document.createElement("button"); | |
fileChooserButtonDone.classList.add("_eaglercraftX_mobile_file_chooser_btn_done"); | |
fileChooserButtonDone.style.fontSize = "1.0em"; | |
fileChooserButtonDone.style.fontWeight = "bold"; | |
fileChooserButtonDone.addEventListener("click", function(/** Event */ evt) { | |
if(fileChooserMobileElement === el) { | |
if(inputElement.files.length > 0) { | |
const val = inputElement.files[0]; | |
val.arrayBuffer().then(function(arr){ | |
fileChooserHasResult = true; | |
fileChooserResultObject = { | |
"fileName": val.name, | |
"fileData": arr | |
}; | |
}).catch(function(){ | |
fileChooserHasResult = true; | |
fileChooserResultObject = null; | |
}); | |
}else { | |
fileChooserHasResult = true; | |
fileChooserResultObject = null; | |
} | |
parentElement.removeChild(el); | |
fileChooserMobileElement = null; | |
fileChooserElement = null; | |
} | |
}); | |
fileChooserButtonDone.appendChild(document.createTextNode("Done")); | |
fileChooserButtons.appendChild(fileChooserButtonDone); | |
el.appendChild(fileChooserButtons); | |
parentElement.appendChild(el); | |
}else { | |
const inputElement = fileChooserElement = /** @type {HTMLInputElement} */ (document.createElement("input")); | |
inputElement.type = "file"; | |
inputElement.style.position = "absolute"; | |
inputElement.style.left = "0px"; | |
inputElement.style.top = "0px"; | |
inputElement.style.zIndex = "-100"; | |
if(mime === null) { | |
inputElement.accept = "." + ext; | |
}else { | |
inputElement.accept = mime; | |
} | |
inputElement.multiple = false; | |
inputElement.addEventListener("change", function(/** Event */ evt) { | |
if(fileChooserElement === inputElement) { | |
if(inputElement.files.length > 0) { | |
const val = inputElement.files[0]; | |
val.arrayBuffer().then(function(arr){ | |
fileChooserHasResult = true; | |
fileChooserResultObject = { | |
"fileName": val.name, | |
"fileData": arr | |
}; | |
}).catch(function(){ | |
fileChooserHasResult = true; | |
fileChooserResultObject = null; | |
}); | |
}else { | |
fileChooserHasResult = true; | |
fileChooserResultObject = null; | |
} | |
parentElement.removeChild(inputElement); | |
fileChooserElement = null; | |
} | |
}); | |
parentElement.appendChild(inputElement); | |
window.setTimeout(function() { | |
inputElement.click(); | |
}, 50); | |
} | |
}; | |
/** | |
* @return {boolean} | |
*/ | |
applicationImports["fileChooserHasResult"] = function() { | |
return fileChooserHasResult; | |
}; | |
/** | |
* @return {Object} | |
*/ | |
applicationImports["getFileChooserResult"] = function() { | |
fileChooserHasResult = false; | |
const res = fileChooserResultObject; | |
fileChooserResultObject = null; | |
return res; | |
}; | |
function clearFileChooserResultImpl() { | |
fileChooserHasResult = false; | |
fileChooserResultObject = null; | |
if(fileChooserMobileElement !== null) { | |
parentElement.removeChild(fileChooserMobileElement); | |
fileChooserMobileElement = null; | |
fileChooserElement = null; | |
}else if(fileChooserElement !== null) { | |
parentElement.removeChild(fileChooserElement); | |
fileChooserElement = null; | |
} | |
} | |
applicationImports["clearFileChooserResult"] = clearFileChooserResultImpl; | |
/** | |
* @param {string} str | |
* @param {Uint8Array} dat | |
*/ | |
applicationImports["downloadFileWithNameU8"] = function(str, dat) { | |
const blobUrl = URL.createObjectURL(new Blob([dat], { "type": "application/octet-stream" })); | |
downloadFileImpl(str, blobUrl, function() { | |
URL.revokeObjectURL(blobUrl); | |
}); | |
}; | |
/** | |
* @param {string} str | |
* @param {ArrayBuffer} dat | |
*/ | |
applicationImports["downloadFileWithNameA"] = function(str, dat) { | |
const blobUrl = URL.createObjectURL(new Blob([dat], { "type": "application/octet-stream" })); | |
downloadFileImpl(str, blobUrl, function() { | |
URL.revokeObjectURL(blobUrl); | |
}); | |
}; | |
/** | |
* @param {string} str | |
* @param {HTMLCanvasElement} cvs | |
*/ | |
applicationImports["downloadScreenshot"] = function(str, cvs) { | |
downloadFileImpl(str, cvs.toDataURL("image/png"), null); | |
}; | |
/** @type {HTMLDocument} */ | |
var loggerDoc = null; | |
/** @type {HTMLElement} */ | |
var loggerBody = null; | |
/** @type {HTMLElement} */ | |
var loggerMessageContainer = null; | |
/** @type {string} */ | |
const loggerLocalStorageKey = runtimeOpts.localStorageNamespace + "showDebugConsole"; | |
/** @type {string} */ | |
const loggerWinUnloadEvent = runtimeOpts.fixDebugConsoleUnloadListener ? "beforeunload" : "unload"; | |
function debugConsoleLocalStorageSet(val) { | |
try { | |
if(window.localStorage) { | |
window.localStorage.setItem(loggerLocalStorageKey, val ? "true" : "false"); | |
} | |
}catch(t) { | |
} | |
} | |
function debugConsoleLocalStorageGet() { | |
try { | |
if(window.localStorage) { | |
const val = window.localStorage.getItem(loggerLocalStorageKey); | |
return val && "true" === val.toLowerCase(); | |
}else { | |
return false; | |
} | |
}catch(t) { | |
return false; | |
} | |
} | |
try { | |
window.addEventListener(loggerWinUnloadEvent, (evt) => { | |
destroyWindow(); | |
}); | |
}catch(ex) { | |
} | |
if(runtimeOpts.openDebugConsoleOnLaunch || debugConsoleLocalStorageGet()) { | |
showDebugConsole0(); | |
} | |
function showDebugConsole() { | |
debugConsoleLocalStorageSet(true); | |
showDebugConsole0(); | |
} | |
function showDebugConsole0() { | |
if(!loggerWindow) { | |
const w = Math.round(1000 * window.devicePixelRatio); | |
const h = Math.round(400 * window.devicePixelRatio); | |
const x = Math.round((window.screen.width - w) / 2.0); | |
const y = Math.round((window.screen.height - h) / 2.0); | |
loggerWindow = window.open("", "_blank", "top=" + y + ",left=" + x + ",width=" + w + ",height=" + h + ",menubar=0,status=0,titlebar=0,toolbar=0"); | |
if(!loggerWindow) { | |
eagError("Logger popup was blocked!"); | |
window.alert("ERROR: Popup blocked!\n\nPlease make sure you have popups enabled for this site!"); | |
return; | |
} | |
loggerWindow.focus(); | |
loggerDoc = loggerWindow.document; | |
loggerDoc.write("<!DOCTYPE html><html><head><meta charset=\"UTF-8\" /><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />" | |
+ "<title>Debug Console</title><link type=\"image/png\" rel=\"shortcut icon\" href=\"" + faviconURL + "\" />" | |
+ "</head><body style=\"overflow-x:hidden;overflow-y:scroll;padding:0px;\"><p id=\"loggerMessageContainer\" style=\"overflow-wrap:break-word;white-space:pre-wrap;font:14px monospace;padding:10px;\"></p></body></html>"); | |
loggerDoc.close(); | |
loggerBody = loggerDoc.body; | |
loggerMessageContainer = /** @type {HTMLElement} */ (loggerDoc.getElementById("loggerMessageContainer")); | |
var linkedListEl = messageBufferStart; | |
while(linkedListEl) { | |
appendLogMessage(linkedListEl["msg"] + "\n", linkedListEl["err"] ? "#DD0000" : "#000000"); | |
linkedListEl = linkedListEl["next"]; | |
} | |
messageBufferStart = null; | |
messageBufferEnd = null; | |
messageBufferLength = 0; | |
scrollToEnd0(); | |
const unloadListener = (evt) => { | |
if(loggerWindow != null) { | |
loggerWindow = null; | |
debugConsoleLocalStorageSet(false); | |
} | |
}; | |
loggerWindow.addEventListener("beforeunload", unloadListener); | |
loggerWindow.addEventListener("unload", unloadListener); | |
}else { | |
loggerWindow.focus(); | |
} | |
} | |
/** | |
* @param {string} text | |
* @param {boolean} isErr | |
*/ | |
loggerWindowMsgHandler = function(text, isErr) { | |
appendLogMessageAndScroll(text + "\n", isErr ? "#DD0000" : "#000000"); | |
} | |
function appendLogMessageAndScroll(text, color) { | |
var b = isScrollToEnd(); | |
appendLogMessage(text, color); | |
if(b) { | |
scrollToEnd0(); | |
} | |
} | |
function appendLogMessage(text, color) { | |
var el = loggerDoc.createElement("span"); | |
el.innerText = text; | |
el.style.color = color; | |
loggerMessageContainer.appendChild(el); | |
var children = loggerMessageContainer.children; | |
while(children.length > windowMaxMessages) { | |
children[0].remove(); | |
} | |
} | |
/** | |
* @return {boolean} | |
*/ | |
function isShowingDebugConsole() { | |
return !!loggerWindow; | |
} | |
function destroyWindow() { | |
if(loggerWindow) { | |
var w = loggerWindow; | |
loggerWindow = null; | |
loggerDoc = null; | |
loggerBody = null; | |
loggerMessageContainer = null; | |
w.close(); | |
} | |
} | |
function isScrollToEnd() { | |
return (loggerWindow.innerHeight + loggerWindow.pageYOffset) >= loggerBody.offsetHeight; | |
} | |
function scrollToEnd0() { | |
setTimeout(() => { | |
loggerWindow.scrollTo(0, loggerBody.scrollHeight || loggerBody.clientHeight); | |
}, 1); | |
} | |
applicationImports["showDebugConsole"] = showDebugConsole; | |
applicationImports["addLogMessage"] = addLogMessageImpl; | |
applicationImports["isShowingDebugConsole"] = isShowingDebugConsole; | |
/** | |
* @return {string|null} | |
*/ | |
applicationImports["getFaviconURL"] = function() { | |
return faviconURL; | |
}; | |
} | |
/** | |
* @param {Object} applicationImports | |
*/ | |
function initializeNoPlatfApplication(applicationImports) { | |
setUnsupportedFunc(applicationImports, platfApplicationName, "setClipboard"); | |
setUnsupportedFunc(applicationImports, platfApplicationName, "getClipboard"); | |
setUnsupportedFunc(applicationImports, platfApplicationName, "displayFileChooser"); | |
setUnsupportedFunc(applicationImports, platfApplicationName, "fileChooserHasResult"); | |
setUnsupportedFunc(applicationImports, platfApplicationName, "getFileChooserResult"); | |
setUnsupportedFunc(applicationImports, platfApplicationName, "clearFileChooserResult"); | |
setUnsupportedFunc(applicationImports, platfApplicationName, "downloadFileWithNameU8"); | |
setUnsupportedFunc(applicationImports, platfApplicationName, "downloadFileWithNameA"); | |
setUnsupportedFunc(applicationImports, platfApplicationName, "downloadScreenshot"); | |
setUnsupportedFunc(applicationImports, platfApplicationName, "showDebugConsole"); | |
setUnsupportedFunc(applicationImports, platfApplicationName, "addLogMessage"); | |
setUnsupportedFunc(applicationImports, platfApplicationName, "isShowingDebugConsole"); | |
setUnsupportedFunc(applicationImports, platfApplicationName, "getFaviconURL"); | |
} | |