mirror of https://gitee.com/godoos/godoos.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1159 lines
61 KiB
1159 lines
61 KiB
class STECardElement extends HTMLElement {
|
|
constructor() {
|
|
super();
|
|
this.defined = false;
|
|
}
|
|
connectedCallback() {
|
|
if (this.defined || !this.isConnected) return;
|
|
this.defined = true;
|
|
this.addEventListener("keydown", event => {
|
|
if (this.getAttribute("data-type") != "dialog" || event.key != "Tab") return;
|
|
var navigable = getNavigableElements({ container: this, scope: true });
|
|
if (!event.shiftKey) {
|
|
if (document.activeElement != navigable[navigable.length - 1]) return;
|
|
event.preventDefault();
|
|
navigable[0].focus();
|
|
} else if (document.activeElement == navigable[0]) {
|
|
event.preventDefault();
|
|
navigable[navigable.length - 1].focus();
|
|
}
|
|
});
|
|
this.type = this.getAttribute("data-type");
|
|
this.header = this.querySelector(".header");
|
|
this.back = document.createElement("button");
|
|
this.back.classList.add("card-back");
|
|
this.back.innerHTML = `<svg><use href="#back_icon"/></svg>`;
|
|
this.back.addEventListener("click", () => document.querySelector(`#${this.header.getAttribute("data-card-parent")}`).open(this));
|
|
this.heading = this.header.querySelector(".heading");
|
|
this.controls = document.createElement("div");
|
|
this.controls.classList.add("card-controls");
|
|
this.controls.minimize = document.createElement("button");
|
|
this.controls.minimize.classList.add("control");
|
|
this.controls.minimize.setAttribute("data-control", "minimize");
|
|
this.controls.minimize.innerHTML = `<svg><use href="#minimize_icon"/></svg>`;
|
|
this.controls.minimize.addEventListener("keydown", event => {
|
|
if (event.key != "Enter") return;
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
this.controls.minimize.click();
|
|
});
|
|
this.controls.minimize.addEventListener("click", () => this.minimize());
|
|
this.controls.close = document.createElement("button");
|
|
this.controls.close.classList.add("control");
|
|
this.controls.close.setAttribute("data-control", "close");
|
|
this.controls.close.innerHTML = `<svg><use href="#close_icon"/></svg>`;
|
|
this.controls.close.addEventListener("click", () => this.close());
|
|
this.header.insertBefore(this.back, this.heading);
|
|
this.controls.appendChild(this.controls.minimize);
|
|
this.controls.appendChild(this.controls.close);
|
|
this.header.appendChild(this.controls);
|
|
if (Editor.environment().macOS_device) {
|
|
this.controls.insertBefore(this.controls.close, this.controls.minimize);
|
|
this.header.insertBefore(this.controls, this.header.firstChild);
|
|
}
|
|
}
|
|
open(previous) {
|
|
if (this.matches(".active") && !this.hasAttribute("data-alert-timeout")) return this.close();
|
|
if (this.type != "alert") {
|
|
document.querySelectorAll(`.card.active`).forEach(card => {
|
|
if (card.type != "dialog" && card.type != this.type) return;
|
|
card.close();
|
|
if (!card.matches(".minimize")) return;
|
|
var transitionDuration = parseInt(getElementStyle({ element: card, property: "transition-duration" }).split(",")[0].replace(/s/g, "") * 1000);
|
|
window.setTimeout(() => card.minimize(), transitionDuration);
|
|
});
|
|
}
|
|
this.classList.add("active");
|
|
if (this.type == "widget" && card_backdrop.matches(".active")) card_backdrop.classList.remove("active");
|
|
if (this.type == "alert") {
|
|
var timeoutIdentifier = Math.random().toString();
|
|
this.setAttribute("data-alert-timeout", timeoutIdentifier);
|
|
window.setTimeout(() => {
|
|
if (this.getAttribute("data-alert-timeout") != timeoutIdentifier) return;
|
|
this.removeAttribute("data-alert-timeout");
|
|
this.close();
|
|
}, 4000);
|
|
}
|
|
if (this.type == "dialog") {
|
|
document.body.addEventListener("keydown", catchCardNavigation);
|
|
card_backdrop.classList.add("active");
|
|
if (!Editor.active_dialog && !Editor.dialog_previous) {
|
|
Editor.dialog_previous = document.activeElement;
|
|
}
|
|
document.querySelectorAll("menu-drop[data-open]").forEach(menu => menu.close());
|
|
var transitionDuration = parseInt(getElementStyle({ element: this, property: "transition-duration" }).split(",")[0].replace(/s/g, "") * 500);
|
|
window.setTimeout(() => {
|
|
document.activeElement.blur();
|
|
if (previous) this.querySelector(`[data-card-previous="${previous.id}"]`).focus();
|
|
}, transitionDuration);
|
|
Editor.active_dialog = this;
|
|
}
|
|
if (this.type == "widget") Editor.active_widget = this;
|
|
}
|
|
minimize() {
|
|
var icon = this.controls.minimize.querySelector("svg use"), main = this.querySelector(".main"), changeIdentifier = Math.random().toString();
|
|
this.setAttribute("data-minimize-change", changeIdentifier);
|
|
workspace_tabs.setAttribute("data-minimize-change", changeIdentifier);
|
|
var transitionDuration = parseInt(getElementStyle({ element: this, property: "transition-duration" }).split(",")[0].replace(/s/g, "") * 1000);
|
|
if (!this.matches(".minimize")) {
|
|
this.classList.add("minimize");
|
|
this.style.setProperty("--card-minimize-width", `${this.controls.minimize.querySelector("svg").clientWidth + parseInt(getElementStyle({ element: this.controls.minimize, property: "--control-padding" }), 10) * 2}px`);
|
|
this.style.setProperty("--card-main-width", `${main.clientWidth}px`);
|
|
this.style.setProperty("--card-main-height", `${main.clientHeight}px`);
|
|
icon.setAttribute("href", "#arrow_icon");
|
|
window.setTimeout(() => {
|
|
workspace_tabs.style.setProperty("--minimize-tab-width", getElementStyle({ element: this, property: "width" }));
|
|
setEditorTabsVisibility();
|
|
}, transitionDuration);
|
|
if (this.contains(document.activeElement) && document.activeElement != this.controls.minimize) this.controls.minimize.focus();
|
|
} else {
|
|
this.classList.remove("minimize");
|
|
window.setTimeout(() => {
|
|
if (this.getAttribute("data-minimize-change") == changeIdentifier) this.style.removeProperty("--card-minimize-width");
|
|
}, transitionDuration);
|
|
this.style.removeProperty("--card-main-width");
|
|
this.style.removeProperty("--card-main-height");
|
|
icon.setAttribute("href", "#minimize_icon");
|
|
workspace_tabs.style.removeProperty("--minimize-tab-width");
|
|
}
|
|
window.setTimeout(() => {
|
|
if (this.getAttribute("data-minimize-change") == changeIdentifier) this.removeAttribute("data-minimize-change");
|
|
if (workspace_tabs.getAttribute("data-minimize-change") == changeIdentifier) workspace_tabs.removeAttribute("data-minimize-change");
|
|
}, transitionDuration);
|
|
}
|
|
close() {
|
|
this.classList.remove("active");
|
|
if (this.matches(".minimize")) {
|
|
var transitionDuration = parseInt(getElementStyle({ element: this, property: "transition-duration" }).split(",")[0].replace(/s/g, "") * 1000);
|
|
window.setTimeout(() => this.minimize(), transitionDuration);
|
|
}
|
|
if (this.type == "dialog") {
|
|
document.body.removeEventListener("keydown", catchCardNavigation);
|
|
card_backdrop.classList.remove("active");
|
|
Editor.active_dialog = null;
|
|
if (Editor.dialog_previous) {
|
|
var hidden = (getElementStyle({ element: Editor.dialog_previous, property: "visibility" }) == "hidden");
|
|
(!workspace_editors.contains(Editor.dialog_previous) && !hidden) ? Editor.dialog_previous.focus({ preventScroll: true }) : Editor.query().container.focus({ preventScroll: true });
|
|
Editor.dialog_previous = null;
|
|
}
|
|
}
|
|
if (this.type == "widget") Editor.active_widget = null;
|
|
}
|
|
}
|
|
function catchCardNavigation() {
|
|
if (!Editor.active_dialog || event.key != "Tab" || document.activeElement != document.body) return;
|
|
var navigable = getNavigableElements({ container: Editor.active_dialog, scope: true });
|
|
event.preventDefault();
|
|
navigable[((!event.shiftKey) ? 0 : navigable.length - 1)].focus();
|
|
}
|
|
window.customElements.define("ste-card", STECardElement);
|
|
|
|
document.querySelectorAll("img").forEach(image => image.draggable = false);
|
|
document.querySelectorAll("num-text").forEach(textarea => applyEditingBehavior({ element: textarea }));
|
|
document.querySelectorAll("input:is([type='text'],[type='url'])").forEach(input => applyEditingBehavior({ element: input }));
|
|
document.querySelectorAll(".checkbox").forEach(checkbox => {
|
|
var input = checkbox.querySelector("input[type='checkbox']");
|
|
checkbox.addEventListener("click", () => input.click());
|
|
checkbox.addEventListener("keydown", event => {
|
|
if (!event.repeat && event.key == "Enter") input.click();
|
|
});
|
|
checkbox.addEventListener("keyup", event => {
|
|
if (event.key == " ") input.click();
|
|
});
|
|
checkbox.tabIndex = 0;
|
|
input.addEventListener("click", event => event.stopPropagation());
|
|
});
|
|
document.querySelectorAll("header .app-omnibox .option").forEach(option => {
|
|
option.tabIndex = -1;
|
|
option.addEventListener("mousedown", event => event.preventDefault());
|
|
});
|
|
window.Tools = {
|
|
replaceText: {
|
|
replace: () => {
|
|
var { container: editor } = Editor.query();
|
|
if (!editor) return;
|
|
var replaced = editor.value.split(replacer_find.value).join(replacer_replace.value);
|
|
if (replaced != editor.value) editor.value = replaced;
|
|
},
|
|
flip: () => [replacer_find.value, replacer_replace.value] = [replacer_replace.value, replacer_find.value],
|
|
clear: () => [replacer_find.value, replacer_replace.value] = ""
|
|
},
|
|
jsonFormatter: {
|
|
format: (spacing = " ") => {
|
|
try {
|
|
var formatted = JSON.stringify(JSON.parse(formatter_input.value), null, spacing);
|
|
if (formatted != formatter_input.value) formatter_input.value = formatted;
|
|
} catch (error) {/* Make matching for "position" optional, as Safari doesn't give JSON parsing error data, it only says that an error occurred. */
|
|
var message = error.toString().match(/^(.+?)position /)[0],
|
|
errorIndex = error.toString().match(/position (\d+)/)[1],
|
|
|
|
errorLine,
|
|
errorLineIndex = (() => {
|
|
var lineIndexes = indexi("\n", formatter_input.value);
|
|
errorLine = formatter_input.value.substring(0, errorIndex).split("\n").length - 1;
|
|
return lineIndexes[errorLine - 1] || 1;
|
|
})(),
|
|
errorPosition = errorIndex - errorLineIndex + 1;
|
|
|
|
alert(`Could not parse JSON, an error occurred.\n${message}line ${errorLine + 1} position ${errorPosition}`);
|
|
}
|
|
|
|
|
|
function indexi(char, str) {
|
|
var list = [], i = -1;
|
|
while ((i = str.indexOf(char, i + 1)) >= 0) list.push(i + 1);
|
|
return list;
|
|
}
|
|
|
|
|
|
},
|
|
collapse: () => {
|
|
Tools.jsonFormatter.format("");
|
|
},
|
|
clear: () => formatter_input.value = ""
|
|
},
|
|
uriEncoder: {
|
|
encode: () => {
|
|
var encodingType = (!encoder_type.checked) ? encodeURI : encodeURIComponent;
|
|
encoder_input.value = encodingType(encoder_input.value);
|
|
},
|
|
decode: () => {
|
|
var decodingType = (!encoder_type.checked) ? decodeURI : decodeURIComponent;
|
|
encoder_input.value = decodingType(encoder_input.value);
|
|
},
|
|
clear: () => encoder_input.value = ""
|
|
},
|
|
uuidGenerator: (() => {
|
|
var lut = [];
|
|
for (var i = 0; i < 256; i++) lut[i] = ((i < 16) ? "0" : "") + i.toString(16);
|
|
return {
|
|
generate: () => {
|
|
var d0 = (Math.random() * 0xffffffff) | 0, d1 = (Math.random() * 0xffffffff) | 0, d2 = (Math.random() * 0xffffffff) | 0, d3 = (Math.random() * 0xffffffff) | 0;
|
|
return `${lut[d0 & 0xff]}${lut[(d0 >> 8) & 0xff]}${lut[(d0 >> 16) & 0xff]}${lut[(d0 >> 24) & 0xff]}-${lut[d1 & 0xff]}${lut[(d1 >> 8) & 0xff]}-${lut[((d1 >> 16) & 0x0f) | 0x40]}${lut[(d1 >> 24) & 0xff]}-${lut[(d2 & 0x3f) | 0x80]}${lut[(d2 >> 8) & 0xff]}-${lut[(d2 >> 16) & 0xff]}${lut[(d2 >> 24) & 0xff]}${lut[d3 & 0xff]}${lut[(d3 >> 8) & 0xff]}${lut[(d3 >> 16) & 0xff]}${lut[(d3 >> 24) & 0xff]}`;
|
|
}
|
|
};
|
|
})(),
|
|
insertTemplate: ({ type } = {}) => {
|
|
var template, editor = Editor.query(), name;
|
|
if (type == "html") {
|
|
var language = navigator.language;
|
|
if (language.includes("-")) language = language.replace(/[^-]+$/g, code => code.toUpperCase());
|
|
name = "index.html";
|
|
template = decodeURI(`%3C!DOCTYPE%20html%3E%0A%3Chtml%20lang=%22${language}%22%3E%0A%0A%3Chead%3E%0A%0A%3Ctitle%3E%3C/title%3E%0A%3Cmeta%20charset=%22UTF-8%22%3E%0A%3Cmeta%20name=%22viewport%22%20content=%22width=device-width,%20initial-scale=1%22%3E%0A%0A%3Cstyle%3E%0A%20%20*,%20*::before,%20*::after%20%7B%0A%20%20%20%20box-sizing:%20border-box;%0A%20%20%7D%0A%20%20body%20%7B%0A%20%20%20%20font-family:%20sans-serif;%0A%20%20%7D%0A%3C/style%3E%0A%0A%3C/head%3E%0A%0A%3Cbody%3E%0A%0A%3Cscript%3E%0A%3C/script%3E%0A%0A%3C/body%3E%0A%0A%3C/html%3E`);
|
|
}
|
|
if (type == "pack-manifest-bedrock") {
|
|
name = "manifest.json";
|
|
template = decodeURI(`%7B%0A%20%20%22format_version%22:%202,%0A%0A%20%20%22header%22:%20%7B%0A%20%20%20%20%22name%22:%20%22Pack%20Manifest%20Template%20-%20Bedrock%20Edition%22,%0A%20%20%20%20%22description%22:%20%22Your%20resource%20pack%20description%22,%0A%20%20%20%20%22uuid%22:%20%22${Tools.uuidGenerator.generate()}%22,%0A%20%20%20%20%22version%22:%20%5B%201,%200,%200%20%5D,%0A%20%20%20%20%22min_engine_version%22:%20%5B%201,%2013,%200%20%5D%0A%20%20%7D,%0A%20%20%22modules%22:%20%5B%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%22description%22:%20%22Your%20resource%20pack%20description%22,%0A%20%20%20%20%20%20%22type%22:%20%22resources%22,%0A%20%20%20%20%20%20%22uuid%22:%20%22${Tools.uuidGenerator.generate()}%22,%0A%20%20%20%20%20%20%22version%22:%20%5B%201,%200,%200%20%5D%0A%20%20%20%20%7D%0A%20%20%5D,%0A%20%20%22metadata%22:%20%7B%0A%20%20%20%20%22authors%22:%20%5B%0A%20%20%20%20%20%20%22Add%20author%20names%20here%20(optional,%20'metadata'%20can%20be%20removed%20altogether)%22%0A%20%20%20%20%5D%0A%20%20%7D%0A%7D`);
|
|
}
|
|
if (!template) return;
|
|
createEditor({ name, value: template });
|
|
if (Editor.view() == "preview") setView({ type: "split" });
|
|
}
|
|
};
|
|
window.addEventListener("load", () => {
|
|
if (Editor.environment().file_protocol) return;
|
|
if (window.location.href.includes("index.html")) history.pushState(null, "", window.location.href.replace(/index.html/, ""));
|
|
if (!("serviceWorker" in navigator) || !Editor.appearance().parent_window) return;
|
|
navigator.serviceWorker.register("service-worker.js").then(() => {
|
|
if ((navigator.serviceWorker.controller) ? (navigator.serviceWorker.controller.state == "activated") : false) activateManifest();
|
|
navigator.serviceWorker.addEventListener("message", event => {
|
|
if (event.data.action == "service-worker-activated") activateManifest();
|
|
if (event.data.action == "clear-site-caches-complete") cleared_cache_card.open();
|
|
if (event.data.action == "share-target") {
|
|
event.data.files.forEach(file => {
|
|
var reader = new FileReader();
|
|
reader.readAsText(file, "UTF-8");
|
|
reader.addEventListener("loadend", () => createEditor({ name: file.name, value: reader.result }));
|
|
});
|
|
}
|
|
});
|
|
document.documentElement.classList.add("service-worker-activated");
|
|
if (queryParameters.get("share-target")) {
|
|
navigator.serviceWorker.controller.postMessage({ action: "share-target" });
|
|
removeQueryParameters(["share-target"]);
|
|
}
|
|
});
|
|
function activateManifest() {
|
|
document.querySelector("link[rel='manifest']").href = "manifest.webmanifest";
|
|
}
|
|
});
|
|
window.addEventListener("beforeinstallprompt", event => {
|
|
event.preventDefault();
|
|
Editor.install_prompt = event;
|
|
document.documentElement.classList.add("install-prompt-available");
|
|
theme_button.childNodes[0].textContent = "Theme";
|
|
});
|
|
window.addEventListener("beforeunload", event => {
|
|
if (Editor.unsaved_work()) return;
|
|
event.preventDefault();
|
|
event.returnValue = "";
|
|
});
|
|
window.addEventListener("unload", () => Editor.child_windows.forEach(window => window.close()));
|
|
window.addEventListener("resize", event => {
|
|
Editor.appearance().refresh_window_controls_overlay();
|
|
Editor.appearance().refresh_device_pixel_ratio();
|
|
if (Editor.view() != "preview") setEditorTabsVisibility();
|
|
if (Editor.view() == "split" && document.body.hasAttribute("data-scaling-active")) setView({ type: "split" });
|
|
});
|
|
window.addEventListener("blur", () => {
|
|
if (Editor.appearance().parent_window) document.querySelectorAll("menu-drop[data-open]").forEach(menu => menu.close());
|
|
});
|
|
document.body.addEventListener("keydown", event => {
|
|
var pressed = key => (event.key.toLowerCase() == key.toLowerCase()), control = (event.ctrlKey && !Editor.environment().apple_device), command = (event.metaKey && Editor.environment().apple_device), shift = (event.shiftKey || ((event.key.toUpperCase() == event.key) && (event.key + event.key == event.key * 2))), controlShift = (control && shift), shiftCommand = (shift && command), controlCommand = (event.ctrlKey && command);
|
|
if (pressed("Escape")) {
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
if (Editor.active_dialog && !document.activeElement.matches("menu-drop[data-open]")) Editor.active_dialog.close();
|
|
}
|
|
if (((control || command) && !shift && pressed("n")) || ((controlShift || shiftCommand) && pressed("x"))) {
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
createEditor({ auto_replace: false });
|
|
}
|
|
if (((control || command) && pressed("w")) || ((controlShift || shiftCommand) && pressed("d"))) {
|
|
if (shift && pressed("w")) return window.close();
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
/* Future feature: If an editor tab is focused, close that editor instead of only the active editor */
|
|
closeEditor();
|
|
}
|
|
if (((controlShift || (event.ctrlKey && shift && !command && Editor.environment().apple_device)) && pressed("Tab")) || ((controlShift || controlCommand) && (pressed("[") || pressed("{")))) {
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
openEditor({ identifier: getPreviousEditor() });
|
|
}
|
|
if (((control || (event.ctrlKey && !command && Editor.environment().apple_device)) && !shift && pressed("Tab")) || ((controlShift || controlCommand) && (pressed("]") || pressed("}")))) {
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
openEditor({ identifier: getNextEditor() });
|
|
}
|
|
if (((controlShift || shiftCommand) && pressed("n")) || ((controlShift || shiftCommand) && pressed("c"))) {
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
createWindow();
|
|
}
|
|
if ((control || command) && !shift && pressed("o")) {
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
openFiles();
|
|
}
|
|
if ((controlShift || shiftCommand) && pressed("r")) {
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
renameEditor();
|
|
}
|
|
if ((control || command) && !shift && pressed("s")) {
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
saveFile();
|
|
}
|
|
if ((controlShift || controlCommand) && (pressed("1") || pressed("!"))) {
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
setView({ type: "code" });
|
|
}
|
|
if ((controlShift || controlCommand) && (pressed("2") || pressed("@"))) {
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
setView({ type: "split" });
|
|
}
|
|
if ((controlShift || controlCommand) && (pressed("3") || pressed("#"))) {
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
setView({ type: "preview" });
|
|
}
|
|
if ((controlShift || controlCommand) && (pressed("4") || pressed("$"))) {
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
setOrientation();
|
|
}
|
|
if ((controlShift || controlCommand) && (pressed("5") || pressed("%"))) {
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
createDisplay();
|
|
}
|
|
if ((controlShift || shiftCommand) && pressed("Enter")) {
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
refreshPreview({ force: true });
|
|
}
|
|
if ((controlShift || shiftCommand) && pressed("B")) {
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
preview_base_card.open();
|
|
}
|
|
if ((controlShift || shiftCommand) && pressed("f")) {
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
replace_text_card.open();
|
|
}/*
|
|
if ((controlShift || shiftCommand) && pressed("k")){
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
color_picker_card.open();
|
|
}*/
|
|
if ((controlShift || shiftCommand) && pressed("g")) {
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
json_formatter_card.open();
|
|
}
|
|
if ((controlShift || shiftCommand) && pressed("y")) {
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
uri_encoder_card.open();
|
|
}
|
|
if ((controlShift || shiftCommand) && pressed("o")) {
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
uuid_generator_card.open();
|
|
}
|
|
if ((controlShift || shiftCommand) && pressed("h")) {
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
Tools.insertTemplate({ type: "html" });
|
|
}
|
|
if ((controlShift || shiftCommand) && pressed("m")) {
|
|
event.preventDefault();
|
|
if (event.repeat || !Editor.active_widget) return;
|
|
if (Editor.active_widget) Editor.active_widget.minimize();
|
|
}
|
|
if ((control || command) && (pressed(",") || pressed("<"))) {
|
|
event.preventDefault();
|
|
if (event.repeat) return;
|
|
settings_card.open();
|
|
}
|
|
}, { capture: true });
|
|
document.body.addEventListener("mousedown", event => {
|
|
if (event.button != 2) return;
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
});
|
|
document.body.addEventListener("contextmenu", event => event.preventDefault());
|
|
document.body.addEventListener("dragover", event => {
|
|
event.preventDefault();
|
|
event.dataTransfer.dropEffect = (event.target.matches("menu-drop, header, .card") || event.target.closest("menu-drop, header, .card")) ? "none" : "copy";
|
|
});
|
|
document.body.addEventListener("drop", event => {
|
|
event.preventDefault();
|
|
document.querySelectorAll("menu-drop[data-open]").forEach(menu => menu.close());
|
|
Array.from(event.dataTransfer.items).forEach(async (item, index) => {
|
|
if (item.kind == "file") {
|
|
if (!Editor.support().file_system || !("getAsFileSystemHandle")) {
|
|
var file = item.getAsFile(), reader = new FileReader();
|
|
reader.readAsText(file, "UTF-8");
|
|
reader.addEventListener("loadend", () => createEditor({ name: file.name, value: reader.result }));
|
|
} else {
|
|
var handle = await item.getAsFileSystemHandle();
|
|
if (handle.kind != "file") return;
|
|
var file = await handle.getFile(), identifier = createEditor({ name: await file.name, value: await file.text() });
|
|
Editor.file_handles[identifier] = await handle;
|
|
}
|
|
} else if (item.kind == "string" && index == 0 && event.dataTransfer.getData("text") != "") createEditor({ value: event.dataTransfer.getData("text") });
|
|
});
|
|
});
|
|
var toolbar = document.querySelector("header .app-menubar");
|
|
toolbar.querySelectorAll("menu-drop").forEach(menu => {
|
|
menu.addEventListener("pointerenter", () => {
|
|
if (event.pointerType != "mouse") return;
|
|
if (toolbar.querySelectorAll("menu-drop:not([data-alternate])[data-open]").length == 0 || menu.matches("[data-alternate]") || menu.matches("[data-open]")) return;
|
|
menu.opener.focus();
|
|
toolbar.querySelectorAll("menu-drop[data-open]").forEach(menu => menu.close());
|
|
menu.open();
|
|
});
|
|
});
|
|
workspace_tabs.addEventListener("keydown", event => {
|
|
if (event.key != "ArrowLeft" && event.key != "ArrowRight") return;
|
|
if (!workspace_tabs.contains(document.activeElement)) return;
|
|
var identifier = document.activeElement.getAttribute("data-editor-identifier"),
|
|
previousEditor = getPreviousEditor({ identifier }),
|
|
nextEditor = getNextEditor({ identifier });
|
|
event.preventDefault();
|
|
if (event.key == "ArrowLeft") Editor.query(previousEditor).tab.focus();
|
|
if (event.key == "ArrowRight") Editor.query(nextEditor).tab.focus();
|
|
});
|
|
create_editor_button.addEventListener("keydown", event => {
|
|
if (event.key != "Enter") return;
|
|
if (event.repeat) event.preventDefault();
|
|
});
|
|
create_editor_button.addEventListener("mousedown", event => event.preventDefault());
|
|
create_editor_button.addEventListener("click", () => createEditor({ auto_replace: false }));
|
|
scaler.addEventListener("mousedown", event => {
|
|
if (event.button != 0) return;
|
|
if (Editor.view() != "split") return;
|
|
event.preventDefault();
|
|
document.body.setAttribute("data-scaling-change", true);
|
|
document.addEventListener("mousemove", setScaling);
|
|
document.addEventListener("mouseup", disableScaling);
|
|
});
|
|
scaler.addEventListener("touchstart", event => {
|
|
if (Editor.view() != "split" || event.touches.length != 1) return;
|
|
document.body.setAttribute("data-scaling-change", true);
|
|
document.addEventListener("touchmove", setScaling, { passive: true });
|
|
document.addEventListener("touchend", disableScaling, { passive: true });
|
|
}, { passive: true });
|
|
card_backdrop.addEventListener("click", () => Editor.active_dialog.close());
|
|
preview_base_input.placeholder = document.baseURI;
|
|
preview_base_input.setWidth = () => preview_base_input.style.setProperty("--input-count", preview_base_input.value.length);
|
|
preview_base_input.setValue = value => {
|
|
preview_base_input.value = value;
|
|
preview_base_input.setWidth();
|
|
};
|
|
preview_base_input.reset = () => {
|
|
preview_base_input.setValue("");
|
|
if (!Editor.settings.has("preview-base")) return;
|
|
Editor.settings.remove("preview-base");
|
|
refreshPreview({ force: true });
|
|
};
|
|
preview_base_input.style.setProperty("--placeholder-count", preview_base_input.placeholder.length);
|
|
preview_base_input.addEventListener("input", event => event.target.setWidth());
|
|
preview_base_input.addEventListener("change", event => {
|
|
var empty = event.target.matches(":placeholder-shown"), valid = event.target.matches(":valid");
|
|
if (empty || !valid) Editor.settings.remove("preview-base");
|
|
if (!empty && valid) Editor.settings.set("preview-base", event.target.value);
|
|
if (empty || valid) refreshPreview({ force: true });
|
|
});
|
|
generator_output.addEventListener("click", () => generator_output.select());
|
|
generator_output.addEventListener("keydown", () => generator_output.click());
|
|
|
|
window.requestAnimationFrame(() => createEditor({ auto_created: true }));
|
|
if (Editor.appearance().parent_window) {
|
|
if (Editor.settings.get("default-orientation")) {
|
|
var value = Editor.settings.get("default-orientation");
|
|
window.requestAnimationFrame(() => default_orientation_setting.select(value));
|
|
setOrientation(value);
|
|
}
|
|
if (Editor.settings.get("syntax-highlighting") != undefined) {
|
|
var state = Editor.settings.get("syntax-highlighting");
|
|
setSyntaxHighlighting(state);
|
|
syntax_highlighting_setting.checked = state;
|
|
}
|
|
if (Editor.settings.get("automatic-refresh") != undefined) automatic_refresh_setting.checked = Editor.settings.get("automatic-refresh");
|
|
if (Editor.settings.get("preview-base")) preview_base_input.setValue(Editor.settings.get("preview-base"));
|
|
window.setTimeout(() => document.documentElement.classList.remove("startup-fade"), 50);
|
|
}
|
|
if (Editor.support().file_handling && Editor.support().file_system) {
|
|
window.launchQueue.setConsumer(params => {
|
|
params.files.forEach(async handle => {
|
|
var file = await handle.getFile(), identifier = createEditor({ name: await file.name, value: await file.text() });
|
|
Editor.file_handles[identifier] = await handle;
|
|
});
|
|
if (!Editor.environment().touch_device) Editor.query().container.focus({ preventScroll: true });
|
|
});
|
|
}
|
|
var queryParameters = new URLSearchParams(window.location.search);
|
|
if (queryParameters.get("template")) {
|
|
Tools.insertTemplate({ type: "html" });
|
|
removeQueryParameters(["template"]);
|
|
}
|
|
if (queryParameters.get("settings")) {
|
|
settings_card.open();
|
|
removeQueryParameters(["settings"]);
|
|
}
|
|
function createEditor({ name = "Untitled.txt", value = "", open = true, auto_created = false, auto_replace = true } = {}) {
|
|
var identifier = Math.random().toString(), tab = document.createElement("button"), editorName = document.createElement("span"), editorClose = document.createElement("button"), container = document.createElement("num-text"), previewOption = document.createElement("li"), focused_override, changeIdentifier = Math.random().toString();
|
|
document.body.setAttribute("data-editor-change", changeIdentifier);
|
|
var transitionDuration = parseInt(getElementStyle({ element: workspace_tabs, property: "transition-duration" }).split(",")[0].replace(/s/g, "") * 1000);
|
|
if (!name.includes(".")) name = `${name}.txt`;
|
|
tab.classList.add("tab");
|
|
tab.setAttribute("data-editor-identifier", identifier);
|
|
if (value) tab.setAttribute("data-editor-refresh", true);
|
|
tab.addEventListener("mousedown", event => {
|
|
if (event.button != 0 || document.activeElement.matches("[data-editor-rename]")) return;
|
|
event.preventDefault();
|
|
if (tab != Editor.query().tab) openEditor({ identifier });
|
|
});
|
|
tab.addEventListener("keydown", event => {
|
|
if (event.key != " " && event.key != "Enter") return;
|
|
event.preventDefault();
|
|
if (tab != Editor.query().tab) openEditor({ identifier });
|
|
});
|
|
tab.addEventListener("contextmenu", event => {
|
|
if (event.target != tab) return;
|
|
var editorRename = tab.querySelector("[data-editor-rename]");
|
|
if (!editorRename) {
|
|
editorRename = document.createElement("input");
|
|
} else return editorRename.blur();
|
|
editorRename.type = "text";
|
|
editorRename.placeholder = Editor.query(identifier).getName();
|
|
editorRename.setAttribute("data-editor-rename", true);
|
|
editorRename.tabIndex = -1;
|
|
editorRename.style.setProperty("--editor-name-width", `${editorName.offsetWidth}px`);
|
|
editorRename.value = Editor.query(identifier).getName();
|
|
editorRename.addEventListener("keydown", event => {
|
|
if (event.key == "Escape") editorRename.blur();
|
|
});
|
|
editorRename.addEventListener("input", () => {
|
|
editorRename.style.width = "0px";
|
|
editorRename.offsetWidth;
|
|
editorRename.style.setProperty("--editor-rename-width", `${editorRename.scrollWidth + 1}px`);
|
|
editorRename.style.removeProperty("width");
|
|
});
|
|
editorRename.addEventListener("change", () => {
|
|
if (editorRename.value) renameEditor({ name: editorRename.value, identifier });
|
|
editorRename.blur();
|
|
});
|
|
editorRename.addEventListener("blur", () => editorRename.remove());
|
|
tab.insertBefore(editorRename, tab.firstChild);
|
|
applyEditingBehavior({ element: editorRename, advanced: false });
|
|
editorRename.focus();
|
|
editorRename.select();
|
|
});
|
|
tab.addEventListener("dragover", event => {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
event.dataTransfer.dropEffect = "copy";
|
|
if (tab != Editor.query().tab) openEditor({ identifier });
|
|
});
|
|
editorName.setAttribute("data-editor-name", name);
|
|
editorName.innerText = name;
|
|
editorClose.classList.add("option");
|
|
editorClose.tabIndex = -1;
|
|
editorClose.innerHTML = "<svg><use href='#close_icon'/></svg>";
|
|
editorClose.addEventListener("mousedown", event => {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
});
|
|
editorClose.addEventListener("click", event => {
|
|
event.stopPropagation();
|
|
closeEditor({ identifier });
|
|
});
|
|
container.classList.add("editor");
|
|
container.setAttribute("data-editor-identifier", identifier);
|
|
container.setAttribute("value", value);
|
|
previewOption.part = "option";
|
|
previewOption.classList.add("option");
|
|
previewOption.setAttribute("data-editor-identifier", identifier);
|
|
previewOption.tabIndex = -1;
|
|
previewOption.innerText = name;
|
|
previewOption.addEventListener("click", () => setPreviewSource({ identifier }));
|
|
if ((Editor.active_editor) ? Editor.query().tab.hasAttribute("data-editor-auto-created") : false) {
|
|
if (document.activeElement == Editor.query().container) focused_override = true;
|
|
if (auto_replace) {
|
|
closeEditor();
|
|
} else Editor.query().tab.removeAttribute("data-editor-auto-created");
|
|
}
|
|
tab.appendChild(editorName);
|
|
tab.appendChild(editorClose);
|
|
workspace_tabs.insertBefore(tab, create_editor_button);
|
|
workspace_editors.appendChild(container);
|
|
container.editor.addEventListener("input", () => {
|
|
if (tab.hasAttribute("data-editor-auto-created")) tab.removeAttribute("data-editor-auto-created");
|
|
if (!tab.hasAttribute("data-editor-refresh")) tab.setAttribute("data-editor-refresh", true);
|
|
if (!tab.hasAttribute("data-editor-unsaved")) tab.setAttribute("data-editor-unsaved", true);
|
|
refreshPreview();
|
|
});
|
|
preview_menu.main.appendChild(previewOption);
|
|
applyEditingBehavior({ element: container });
|
|
if (open || !Editor.active_editor) openEditor({ identifier, auto_created, focused_override });
|
|
container.syntaxLanguage = Editor.query(identifier).getName("extension");
|
|
if ((Editor.settings.get("syntax-highlighting") == true) && (container.syntaxLanguage in Prism.languages)) container.syntaxHighlight.enable();
|
|
window.setTimeout(() => {
|
|
if (document.body.getAttribute("data-editor-change") == changeIdentifier) document.body.removeAttribute("data-editor-change");
|
|
}, transitionDuration);
|
|
return identifier;
|
|
}
|
|
function openEditor({ identifier, auto_created = false, focused_override = false } = {}) {
|
|
if (!identifier) return;
|
|
var { tab, container, textarea, getName } = Editor.query(identifier), focused = (document.activeElement == Editor.query().container) || focused_override;
|
|
if (Editor.query().tab) Editor.query().tab.classList.remove("active");
|
|
if (Editor.query().container) Editor.query().container.classList.remove("active");
|
|
tab.classList.add("active");
|
|
if (auto_created) tab.setAttribute("data-editor-auto-created", true);
|
|
container.classList.add("active");
|
|
Editor.active_editor = identifier;
|
|
setEditorTabsVisibility();
|
|
setTitle({ content: getName() });
|
|
if ((((document.activeElement == document.body && !Editor.active_dialog) || auto_created) && !Editor.environment().touch_device && Editor.appearance().parent_window) || focused) container.focus({ preventScroll: true });
|
|
if (Editor.preview_editor == "active-editor") refreshPreview({ force: (Editor.settings.get("automatic-refresh") != false) });
|
|
}
|
|
function closeEditor({ identifier = Editor.active_editor } = {}) {
|
|
if (!identifier) return;
|
|
var { tab, container, textarea, getName } = Editor.query(identifier), previewOption = preview_menu.main.querySelector(`.option[data-editor-identifier="${identifier}"]`), active = (tab.classList.contains("active")), focused = (document.activeElement == container), editorTabs = Array.from(workspace_tabs.querySelectorAll(".tab:not([data-editor-change])"));
|
|
if (tab.hasAttribute("data-editor-unsaved")) {
|
|
if (!confirm(`Are you sure you would like to close "${getName()}"?\nRecent changes have not yet been saved.`)) return;
|
|
}
|
|
var changeIdentifier = Math.random().toString();
|
|
if (editorTabs.length != 1) {
|
|
document.body.setAttribute("data-editor-change", changeIdentifier);
|
|
tab.style.setProperty("--tab-margin-right", `-${tab.offsetWidth}px`);
|
|
} else if (document.body.hasAttribute("data-editor-change")) {
|
|
document.body.removeAttribute("data-editor-change", changeIdentifier);
|
|
workspace_tabs.querySelectorAll(".tab[data-editor-change]").forEach(tab => workspace_tabs.removeChild(tab));
|
|
}
|
|
var transitionDuration = (document.body.hasAttribute("data-editor-change")) ? parseInt(getElementStyle({ element: workspace_tabs, property: "transition-duration" }).split(",")[0].replace(/s/g, "") * 1000) : 0;
|
|
if (tab == editorTabs[0] && editorTabs.length == 1) {
|
|
Editor.active_editor = null;
|
|
setTitle({ reset: true });
|
|
preview.src = "about:blank";
|
|
}
|
|
if (Editor.preview_editor == identifier) setPreviewSource({ active_editor: true });
|
|
if (tab == editorTabs[0] && editorTabs[1] && tab.classList.contains("active")) openEditor({ identifier: editorTabs[1].getAttribute("data-editor-identifier") });
|
|
if (tab == editorTabs[editorTabs.length - 1] && tab != editorTabs[0] && tab.classList.contains("active")) openEditor({ identifier: editorTabs[editorTabs.length - 2].getAttribute("data-editor-identifier") });
|
|
if (tab != editorTabs[0] && tab.classList.contains("active")) openEditor({ identifier: editorTabs[editorTabs.indexOf(tab) + 1].getAttribute("data-editor-identifier") });
|
|
if (focused && Editor.query().textarea) Editor.query().container.focus({ preventScroll: true });
|
|
tab.setAttribute("data-editor-change", true);
|
|
if (tab == document.activeElement) tab.blur();
|
|
tab.tabIndex = -1;
|
|
if (tab.classList.contains("active")) tab.classList.remove("active");
|
|
workspace_editors.removeChild(container);
|
|
preview_menu.main.removeChild(previewOption);
|
|
(transitionDuration) ? window.setTimeout(removeEditorTab, transitionDuration) : removeEditorTab();
|
|
if (Editor.file_handles[identifier]) delete Editor.file_handles[identifier];
|
|
function removeEditorTab() {
|
|
if (workspace_tabs.contains(tab)) workspace_tabs.removeChild(tab);
|
|
if (document.body.getAttribute("data-editor-change") == changeIdentifier) document.body.removeAttribute("data-editor-change");
|
|
}
|
|
}
|
|
function renameEditor({ name, identifier = Editor.active_editor } = {}) {
|
|
var { tab, container, getName } = Editor.query(identifier), editorName = tab.querySelector("[data-editor-name]"), previewOption = preview_menu.main.querySelector(`.option[data-editor-identifier="${identifier}"]`), currentName = getName(), base = getName("base"), extension = getName("extension"), rename = (name) ? name : prompt(`Enter a new file name for "${currentName}".`, currentName);
|
|
if (!rename) return;
|
|
if (!rename.includes(".")) {
|
|
rename = `${rename}.${extension}`;
|
|
} else if (rename.charAt(0) == ".") rename = `${base}${rename}`;
|
|
editorName.innerText = rename;
|
|
previewOption.innerText = rename;
|
|
var syntaxLanguage = Editor.query(identifier).getName("extension");
|
|
if (syntaxLanguage in Prism.languages) container.syntaxLanguage = syntaxLanguage;
|
|
((Editor.settings.get("syntax-highlighting") == true) && (syntaxLanguage in Prism.languages)) ? container.syntaxHighlight.enable() : container.syntaxHighlight.disable();
|
|
if (syntaxLanguage != container.syntaxLanguage) container.syntaxLanguage = syntaxLanguage;
|
|
if (tab.hasAttribute("data-editor-auto-created")) tab.removeAttribute("data-editor-auto-created");
|
|
if (tab == Editor.query().tab) setTitle({ content: rename });
|
|
if ((Editor.preview_editor == "active-editor" && Editor.active_editor == identifier) || Editor.preview_editor == identifier) refreshPreview({ force: true });
|
|
return rename;
|
|
}
|
|
/* Future feature: Add support to disable the wrapping behavior */
|
|
function getPreviousEditor({ identifier = Editor.active_editor, wrap = true } = {}) {
|
|
var { tab } = Editor.query(identifier), editorTabs = Array.from(workspace_tabs.querySelectorAll(".tab:not([data-editor-change])")), previousTab = editorTabs[(editorTabs.indexOf(tab) || editorTabs.length) - 1], previousEditor = previousTab.getAttribute("data-editor-identifier");
|
|
return previousEditor;
|
|
}
|
|
function getNextEditor({ identifier = Editor.active_editor, wrap = true } = {}) {
|
|
var { tab } = Editor.query(identifier), editorTabs = Array.from(workspace_tabs.querySelectorAll(".tab:not([data-editor-change])")), nextTab = editorTabs[(editorTabs.indexOf(tab) != editorTabs.length - 1) ? editorTabs.indexOf(tab) + 1 : 0], nextEditor = nextTab.getAttribute("data-editor-identifier");
|
|
return nextEditor;
|
|
}
|
|
function setEditorTabsVisibility({ identifier = Editor.active_editor } = {}) {
|
|
if (!Editor.active_editor) return;
|
|
var { tab } = Editor.query(identifier), obstructedLeft = (tab.offsetLeft <= workspace_tabs.scrollLeft), obstructedRight = ((tab.offsetLeft + tab.clientWidth) >= (workspace_tabs.scrollLeft + workspace_tabs.clientWidth)), spacingOffset = 0;
|
|
if ((workspace_tabs.clientWidth < tab.clientWidth) && !obstructedLeft) return;
|
|
if (obstructedLeft) {
|
|
spacingOffset = parseInt(getElementStyle({ element: workspace_tabs, pseudo: "::before", property: "width" }), 10) * 3;
|
|
workspace_tabs.scrollTo(tab.offsetLeft - spacingOffset, 0);
|
|
} else if (obstructedRight) {
|
|
spacingOffset = parseInt(getElementStyle({ element: workspace_tabs, pseudo: "::after", property: "width" }), 10) * 3;
|
|
workspace_tabs.scrollTo(tab.offsetLeft + tab.clientWidth + spacingOffset - workspace_tabs.clientWidth, 0);
|
|
}
|
|
}
|
|
function setView({ type, force = false } = {}) {
|
|
if ((Editor.orientation_change() && !force) || Editor.scaling_change()) return;
|
|
var changeIdentifier = Math.random().toString();
|
|
document.body.setAttribute("data-view-change", changeIdentifier);
|
|
var transitionDuration = parseInt(getElementStyle({ element: workspace, property: "transition-duration" }).split(",")[0].replace(/s/g, "") * 1000);
|
|
document.body.classList.remove(Editor.view());
|
|
document.body.setAttribute("data-view", type);
|
|
document.body.classList.add(Editor.view());
|
|
removeScaling();
|
|
view_menu.select(Editor.view());
|
|
if (type != "preview") window.setTimeout(setEditorTabsVisibility, transitionDuration);
|
|
window.setTimeout(() => {
|
|
if (document.body.getAttribute("data-view-change") == changeIdentifier) document.body.removeAttribute("data-view-change");
|
|
}, transitionDuration);
|
|
refreshPreview();
|
|
}
|
|
function setOrientation(orientation) {
|
|
if (Editor.orientation_change() || Editor.scaling_change()) return;
|
|
document.body.setAttribute("data-orientation-change", true);
|
|
var param = (orientation), transitionDuration = ((Editor.view() != "split") ? 0 : parseInt(getElementStyle({ element: workspace, property: "transition-duration" }).split(",")[0].replace(/s/g, "") * 1000));
|
|
if (!param && Editor.view() == "split") setView({ type: "code", force: true });
|
|
if (!param && Editor.orientation() == "horizontal") orientation = "vertical";
|
|
if (!param && Editor.orientation() == "vertical") orientation = "horizontal";
|
|
window.setTimeout(() => {
|
|
setTransitionDurations("off");
|
|
document.body.classList.remove(Editor.orientation());
|
|
document.body.setAttribute("data-orientation", orientation);
|
|
document.body.classList.add(Editor.orientation());
|
|
workspace.offsetHeight;
|
|
scaler.offsetHeight;
|
|
preview.offsetHeight;
|
|
setTransitionDurations("on");
|
|
if (!param) setView({ type: "split", force: true });
|
|
window.setTimeout(() => document.body.removeAttribute("data-orientation-change"), transitionDuration);
|
|
}, transitionDuration);
|
|
function setTransitionDurations(state) {
|
|
if (state == "on") {
|
|
workspace.style.removeProperty("transition-duration");
|
|
scaler.style.removeProperty("transition-duration");
|
|
preview.style.removeProperty("transition-duration");
|
|
}
|
|
if (state == "off") {
|
|
workspace.style.transitionDuration = "0s";
|
|
scaler.style.transitionDuration = "0s";
|
|
preview.style.transitionDuration = "0s";
|
|
}
|
|
}
|
|
}
|
|
function setPreviewSource({ identifier, active_editor }) {
|
|
if (!identifier && !active_editor) return;
|
|
if ((!identifier && active_editor) || (Editor.preview_editor == identifier)) {
|
|
Editor.preview_editor = "active-editor";
|
|
preview_menu.select("active-editor");
|
|
} else Editor.preview_editor = identifier;
|
|
refreshPreview({ force: true });
|
|
}
|
|
function setSyntaxHighlighting(state) {
|
|
state = (state != undefined) ? state : (Editor.settings.get("syntax-highlighting") != undefined);
|
|
document.querySelectorAll("num-text").forEach(editor => {
|
|
if (editor.syntaxLanguage in Prism.languages) (state) ? editor.syntaxHighlight.enable() : editor.syntaxHighlight.disable();
|
|
});
|
|
Editor.settings.set("syntax-highlighting", state);
|
|
}
|
|
function createWindow() {
|
|
var features = (Editor.appearance().standalone || Editor.appearance().fullscreen) ? "popup" : "", win = window.open(window.location.href, "_blank", features);
|
|
if (Editor.appearance().fullscreen) {
|
|
win.resizeTo(window.screen.width * 2 / 3, window.screen.height * 2 / 3);
|
|
win.moveTo(window.screen.width / 6, window.screen.height / 6);
|
|
} else if (Editor.appearance().standalone) {
|
|
win.resizeTo(window.outerWidth, window.outerHeight);
|
|
win.moveTo(window.screenX, window.screenY);
|
|
}
|
|
}
|
|
async function openFiles() {
|
|
if (!Editor.support().file_system) {
|
|
var input = document.createElement("input");
|
|
input.type = "file";
|
|
input.multiple = true;
|
|
input.addEventListener("change", () => Array.from(input.files).forEach(file => {
|
|
var reader = new FileReader();
|
|
reader.readAsText(file, "UTF-8");
|
|
reader.addEventListener("loadend", () => createEditor({ name: file.name, value: reader.result }));
|
|
}));
|
|
input.click();
|
|
} else {
|
|
var handles = await window.showOpenFilePicker({ multiple: true }).catch(error => {
|
|
if (error.message.toLowerCase().includes("abort")) return;
|
|
});
|
|
if (!handles) return;
|
|
handles.forEach(async handle => {
|
|
var file = await handle.getFile(), identifier = createEditor({ name: await file.name, value: await file.text() });
|
|
Editor.file_handles[identifier] = await handle;
|
|
});
|
|
}
|
|
}
|
|
// var id = _req('id');
|
|
// if (id) {
|
|
// __get('txt/editBefore?id=' + id, res => {
|
|
// if (res.code == 0) {
|
|
// //navigator.serviceWorker.controller.postMessage({ action: "clear-site-caches" });
|
|
// renameEditor({ name: res.data.title });
|
|
// Editor.query().textarea.value = res.data.content;
|
|
// }
|
|
// })
|
|
// }
|
|
window.addEventListener('load', () => {
|
|
window.parent.postMessage({ type: 'initSuccess' }, '*')
|
|
window.addEventListener('message', eventHandler)
|
|
})
|
|
window.addEventListener('unload', () => {
|
|
window.removeEventListener('message', eventHandler)
|
|
})
|
|
const base64ToBlob = (code) => {
|
|
const parts = code.split(";base64,");
|
|
const contentType = parts[0].split(":")[1];
|
|
const raw = window.atob(parts[1]);
|
|
const rawLength = raw.length;
|
|
|
|
const uInt8Array = new Uint8Array(rawLength);
|
|
|
|
for (let i = 0; i < rawLength; ++i) {
|
|
uInt8Array[i] = raw.charCodeAt(i);
|
|
}
|
|
return new Blob([uInt8Array], {
|
|
"type": contentType
|
|
});
|
|
};
|
|
const blobToText = (blob) => {
|
|
return new Promise((resolve, reject) => {
|
|
const reader = new FileReader();
|
|
reader.onload = (e) => {
|
|
resolve(e.target.result);
|
|
};
|
|
reader.onerror = () => {
|
|
reject();
|
|
};
|
|
reader.readAsText(blob);
|
|
});
|
|
};
|
|
function isBase64(str) {
|
|
if (str === '' || str.trim() === '') {
|
|
return false;
|
|
}
|
|
try {
|
|
return btoa(atob(str)) == str;
|
|
} catch (err) {
|
|
return false;
|
|
}
|
|
}
|
|
function decodeBase64(base64String) {
|
|
// 将Base64字符串分成每64个字符一组
|
|
const padding = (base64String.length % 4) === 0 ? 0 : 4 - (base64String.length % 4);
|
|
base64String += '='.repeat(padding);
|
|
|
|
// 使用atob()函数解码Base64字符串
|
|
const binaryString = atob(base64String);
|
|
|
|
// 将二进制字符串转换为TypedArray
|
|
const bytes = new Uint8Array(binaryString.length);
|
|
for (let i = 0; i < binaryString.length; i++) {
|
|
bytes[i] = binaryString.charCodeAt(i);
|
|
}
|
|
|
|
// 将TypedArray转换为字符串
|
|
return new TextDecoder('utf-8').decode(bytes);
|
|
}
|
|
const eventHandler = (e) => {
|
|
const eventData = e.data
|
|
//console.log(eventData)
|
|
if (eventData.type === 'start') {
|
|
renameEditor({ name: eventData.title });
|
|
}
|
|
if (eventData.type === 'init') {
|
|
//console.log(eventData)
|
|
//const data = JSON.parse(eventData.data)
|
|
const data = eventData.data
|
|
if (!data || !data.title) {
|
|
return;
|
|
}
|
|
//console.log(data)
|
|
if(data.ext && data.ext != 'txt') {
|
|
//Editor.query().setName("extension", data.ext);
|
|
data.title = data.title + "." + data.ext;
|
|
}
|
|
//console.log(data.title)
|
|
renameEditor({ name: data.title });
|
|
//console.log(data)
|
|
if (data.content) {
|
|
if(isBase64(data.content)){
|
|
//console.log(data.content)
|
|
data.content = decodeBase64(data.content);
|
|
}
|
|
Editor.query().textarea.value = data.content;
|
|
}
|
|
else if (data.base64) {
|
|
let blob = base64ToBlob(data.base64);
|
|
blobToText(blob).then(res => {
|
|
Editor.query().textarea.value = res;
|
|
})
|
|
}
|
|
|
|
}
|
|
}
|
|
function saveData(extension) {
|
|
let ext = Editor.query().getName("extension"),
|
|
title = Editor.query().getName("base"),
|
|
//title = Editor.query().getName(),
|
|
content = Editor.query().textarea.value;
|
|
//console.log(content)
|
|
const save = {
|
|
data: JSON.stringify({ content, title, ext }),
|
|
type: 'exportText'
|
|
}
|
|
window.parent.postMessage(save, '*')
|
|
|
|
// if (!id) {
|
|
// __post('txt/add', { title, content }, res => {
|
|
// if (res.code == 0) {
|
|
// id = res.data;
|
|
// _msg('保存成功')
|
|
// }
|
|
// })
|
|
// } else {
|
|
// __post('txt/edit', { id, title, content }, res => {
|
|
// if (res.code == 0) {
|
|
// _msg('编辑成功')
|
|
// }
|
|
// })
|
|
// }
|
|
}
|
|
document.getElementById('saveData').addEventListener('click', saveData)
|
|
|
|
/**
|
|
* 保存文件
|
|
* @param {string} extension 文件扩展名
|
|
*/
|
|
async function saveFile(extension) {
|
|
saveData(extension);
|
|
|
|
if (extension || !Editor.support().file_system) {
|
|
// 如果没有指定扩展名或者不支持文件系统
|
|
if (!extension) {
|
|
// 获取文件扩展名
|
|
extension = Editor.query().getName("extension");
|
|
} else {
|
|
// 创建一个a标签并设置下载链接
|
|
var anchor = document.createElement("a"), link = window.URL.createObjectURL(new Blob([Editor.query().textarea.value]));
|
|
anchor.href = link;
|
|
anchor.download = `${Editor.query().getName("base")}.${extension}`;
|
|
anchor.click();
|
|
window.URL.revokeObjectURL(link);
|
|
}
|
|
} else {
|
|
// 如果支持文件系统
|
|
var identifier = Editor.active_editor, handle;
|
|
if (!Editor.file_handles[identifier]) {
|
|
// 显示保存文件选择器
|
|
handle = await window.showSaveFilePicker({ suggestedName: Editor.query().getName(), startIn: (Editor.file_handles[identifier]) ? Editor.file_handles[identifier] : "desktop" }).catch(error => {
|
|
if (error.message.toLowerCase().includes("abort")) return;
|
|
});
|
|
if (!handle) return;
|
|
Editor.file_handles[identifier] = await handle;
|
|
} else handle = Editor.file_handles[identifier];
|
|
var stream = await Editor.file_handles[identifier].createWritable().catch(error => {
|
|
alert(`"${Editor.query().getName()}" 保存失败`);
|
|
if (error.toString().toLowerCase().includes("not allowed")) return;
|
|
});
|
|
if (!stream) return;
|
|
await stream.write(Editor.query().textarea.value);
|
|
await stream.close();
|
|
var currentName = Editor.query().getName(), file = await handle.getFile(), rename = await file.name;
|
|
if (currentName != rename) renameEditor({ name: rename });
|
|
}
|
|
|
|
if (Editor.query().tab.hasAttribute("data-editor-auto-created")) Editor.query().tab.removeAttribute("data-editor-auto-created");
|
|
if (Editor.query().tab.hasAttribute("data-editor-unsaved")) Editor.query().tab.removeAttribute("data-editor-unsaved");
|
|
refreshPreview({ force: true });
|
|
}
|
|
function createDisplay() {
|
|
var width = window.screen.availWidth * 2 / 3,
|
|
height = window.screen.availHeight * 2 / 3,
|
|
left = window.screen.availWidth / 2 + window.screen.availLeft - width / 2,
|
|
top = window.screen.availHeight / 2 + window.screen.availTop - height / 2,
|
|
features = (Editor.appearance().standalone || Editor.appearance().fullscreen) ? "popup" : "",
|
|
link = window.URL.createObjectURL(new Blob([Editor.query().textarea.value], { type: "text/html" })),
|
|
win = window.open(link, "_blank", features);
|
|
window.URL.revokeObjectURL(link);
|
|
win.moveTo(left, top);
|
|
win.resizeTo(width, height);
|
|
Editor.child_windows.push(win);
|
|
window.setTimeout(() => {
|
|
if (!win.document.title) win.document.title = Editor.query().getName();
|
|
}, 20);
|
|
}
|
|
async function callCommand(element, action) {/* I think I may remove this, or do something else with it. document.execCommand() is broken in so many ways, what a shame */
|
|
element.focus({ preventScroll: true });
|
|
if (action == "paste") {
|
|
var clipboard = await navigator.clipboard.readText().catch(error => alert("Could not access the clipboard, please check site permissions for clipboard use."));
|
|
document.execCommand("insertText", false, await clipboard);
|
|
} else document.execCommand(action);
|
|
}
|
|
function getNavigableElements({ container, scope = false } = {}) {
|
|
scope = (scope) ? "" : ":scope > ";
|
|
var navigable = container.querySelectorAll(`${scope}button:not([disabled]), ${scope}textarea:not([disabled]), ${scope}input:not([disabled]), ${scope}select:not([disabled]), ${scope}a[href]:not([disabled]), ${scope}[tabindex]:not([tabindex="-1"])`);
|
|
return Array.from(navigable).filter(element => (getElementStyle({ element, property: "display" }) != "none"));
|
|
}
|
|
function getElementStyle({ element, pseudo = null, property } = {}) {
|
|
return window.getComputedStyle(element, pseudo).getPropertyValue(property);
|
|
}
|
|
function applyEditingBehavior({ element, advanced = true } = {}) {
|
|
var type = element.tagName.toLowerCase();
|
|
element.addEventListener("dragover", event => {
|
|
event.stopPropagation();
|
|
event.dataTransfer.dropEffect = "copy";
|
|
});
|
|
element.addEventListener("drop", event => {
|
|
if (Array.from(event.dataTransfer.items)[0].kind == "file") return;
|
|
event.stopPropagation();
|
|
document.querySelectorAll("menu-drop[data-open]").forEach(menu => menu.close());
|
|
});
|
|
if (type == "input") {
|
|
element.spellcheck = false;
|
|
element.autocomplete = "off";
|
|
element.autocapitalize = "none";
|
|
element.setAttribute("autocorrect", "off");
|
|
}
|
|
if (type == "num-text") {
|
|
element.colorScheme.set("dark");
|
|
element.themes.remove("vanilla-appearance");
|
|
var scrollbarStyles = document.createElement("style");
|
|
scrollbarStyles.textContent = scrollbar_styles.textContent;
|
|
element.shadowRoot.insertBefore(scrollbarStyles, element.container);
|
|
}
|
|
}
|
|
function sendShortcutAction({ control, command, shift, controlShift, shiftCommand, controlCommand, key } = {}) {
|
|
if (!key) return;
|
|
var appleDevice = (Editor.environment().apple_device);
|
|
control = ((control && !appleDevice) || (controlShift && !appleDevice) || (controlCommand && appleDevice)), command = ((command && appleDevice) || (shiftCommand && appleDevice) || (controlCommand && appleDevice)), shift = (shift || (controlShift && !appleDevice) || (shiftCommand && appleDevice)), key = key.toString().toLowerCase();
|
|
document.body.dispatchEvent(new KeyboardEvent("keydown", { ctrlKey: control, metaKey: command, shiftKey: shift, key }));
|
|
}
|
|
function refreshPreview({ force = false } = {}) {
|
|
if (Editor.view() == "code") return;
|
|
var editor = (Editor.preview_editor == "active-editor") ? Editor.query() : Editor.query(Editor.preview_editor);
|
|
if (!editor.textarea) return;
|
|
var change = (editor.tab.hasAttribute("data-editor-refresh") && Editor.settings.get("automatic-refresh") != false);
|
|
if (!change && !force) return;
|
|
var baseURL = Editor.settings.get("preview-base") || null, source = editor.textarea.value;
|
|
if (baseURL) {
|
|
var sourceDOM = new DOMParser().parseFromString(source, "text/html"), baseElement = document.createElement("base");
|
|
sourceDOM.head.prepend(baseElement);
|
|
baseElement.href = baseURL;
|
|
source = new XMLSerializer().serializeToString(sourceDOM);
|
|
}
|
|
preview.addEventListener("load", () => {
|
|
preview.contentWindow.document.open();
|
|
preview.contentWindow.document.write(source);
|
|
preview.contentWindow.document.close();
|
|
}, { once: true });
|
|
preview.src = "about:blank";
|
|
if (change) editor.tab.removeAttribute("data-editor-refresh");
|
|
}
|
|
function setTitle({ content, reset = false } = {}) {
|
|
document.title = `${(content && !reset) ? `${content} - ` : ""}文本编辑器`;
|
|
}
|
|
function addQueryParameters(entries) {
|
|
var parameters = new URLSearchParams(window.location.search);
|
|
entries.forEach(entry => parameters.set(entry, true));
|
|
changeQueryParameters(parameters);
|
|
}
|
|
function removeQueryParameters(entries) {
|
|
var parameters = new URLSearchParams(window.location.search);
|
|
entries.forEach(entry => parameters.delete(entry));
|
|
changeQueryParameters(parameters);
|
|
}
|
|
function changeQueryParameters(parameters) {
|
|
var query = parameters.toString();
|
|
if (query) query = "?" + query;
|
|
var address = window.location.pathname + query;
|
|
history.pushState(null, "", address);
|
|
}
|
|
function showInstallPrompt() {
|
|
Editor.install_prompt.prompt();
|
|
Editor.install_prompt.userChoice.then(result => {
|
|
if (result.outcome != "accepted") return;
|
|
document.documentElement.classList.remove("install-prompt-available");
|
|
theme_button.childNodes[0].textContent = "Customize Theme";
|
|
});
|
|
}
|
|
function clearSiteCaches() {
|
|
if (confirm("你要清除所有缓存吗?\n在Internet连接可用之前,文本编辑器将不再脱机工作。 ")) navigator.serviceWorker.controller.postMessage({ action: "clear-site-caches" });
|
|
}
|
|
function setScaling(event) {
|
|
var { safe_area_insets: safeAreaInsets } = Editor.appearance(), scalingOffset, scalingRange = { minimum: ((Editor.orientation() == "vertical") ? workspace_tabs.offsetHeight : safeAreaInsets.left) + 80, maximum: ((Editor.orientation() == "horizontal") ? window.innerWidth - safeAreaInsets.right : (Editor.orientation() == "vertical") ? (window.innerHeight - header.offsetHeight - safeAreaInsets.bottom) : 0) - 80 }, touchEvent = (Editor.environment().touch_device && event instanceof TouchEvent);
|
|
if (Editor.orientation() == "horizontal") scalingOffset = (!touchEvent) ? event.pageX : event.touches[0].pageX;
|
|
if (Editor.orientation() == "vertical") scalingOffset = (!touchEvent) ? event.pageY - header.offsetHeight : event.touches[0].pageY - header.offsetHeight;
|
|
if (scalingOffset < scalingRange.minimum) scalingOffset = scalingRange.minimum;
|
|
if (scalingOffset > scalingRange.maximum) scalingOffset = scalingRange.maximum;
|
|
document.body.setAttribute("data-scaling-active", true);
|
|
workspace.style.setProperty("--scaling-offset", `${scalingOffset}px`);
|
|
scaler.style.setProperty("--scaling-offset", `${scalingOffset}px`);
|
|
preview.style.setProperty("--scaling-offset", `${scalingOffset}px`);
|
|
}
|
|
function disableScaling(event) {
|
|
var touchEvent = (Editor.environment().touch_device && event instanceof TouchEvent);
|
|
document.removeEventListener((!touchEvent) ? "mousemove" : "touchmove", setScaling);
|
|
document.removeEventListener((!touchEvent) ? "mouseup" : "touchend", disableScaling);
|
|
document.body.removeAttribute("data-scaling-change");
|
|
}
|
|
function removeScaling() {
|
|
if (!document.body.hasAttribute("data-scaling-active")) return;
|
|
document.body.removeAttribute("data-scaling-active");
|
|
workspace.style.removeProperty("--scaling-offset");
|
|
scaler.style.removeProperty("--scaling-offset");
|
|
preview.style.removeProperty("--scaling-offset");
|
|
}
|