eaglercraft-1.8/sources/wasm-gc-teavm/js/platformApplication.js

503 lines
15 KiB
JavaScript

/*
* 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");
}