This commit is contained in:
309
web/public/js/log.js
Normal file
309
web/public/js/log.js
Normal file
@@ -0,0 +1,309 @@
|
||||
/* line numbers */
|
||||
updateLineNumber(location.hash);
|
||||
|
||||
for (let line of document.querySelectorAll('.line-number')) {
|
||||
line.addEventListener("click", () =>
|
||||
updateLineNumber(line.attributes.getNamedItem("id").value));
|
||||
}
|
||||
|
||||
function updateLineNumber(id) {
|
||||
if (id && id.startsWith('#')) {
|
||||
id = id.substring(1);
|
||||
}
|
||||
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let element = document.getElementById(id);
|
||||
if (element.classList.contains("line-number")) {
|
||||
for (const line of document.querySelectorAll(".line-active")) {
|
||||
line.classList.remove("line-active");
|
||||
}
|
||||
element.closest('.entry').classList.add('line-active');
|
||||
}
|
||||
}
|
||||
|
||||
/* Scroll to top/bottom buttons */
|
||||
const downButton = document.getElementById("down-button");
|
||||
if (downButton) {
|
||||
downButton.addEventListener("click", () => scrollToHeight(document.body.scrollHeight));
|
||||
}
|
||||
|
||||
const upButton = document.getElementById("up-button");
|
||||
if (upButton) {
|
||||
upButton.addEventListener("click", () => scrollToHeight(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll to a specific height
|
||||
* Disable smooth scrolling for large pages
|
||||
* @param {number} top height to scroll to
|
||||
* @param {number} [smoothScrollLimit] only use smooth scrolling if the distance is less than this value
|
||||
*/
|
||||
function scrollToHeight(top, smoothScrollLimit = 10000) {
|
||||
const distance = Math.abs(document.documentElement.scrollTop - top);
|
||||
const behavior = (distance < smoothScrollLimit) ? "smooth" : "instant";
|
||||
window.scrollTo({left: 0, top, behavior});
|
||||
}
|
||||
|
||||
/* error collapse toggle */
|
||||
const toggleErrorsButton = document.getElementById("error-toggle");
|
||||
if (toggleErrorsButton) {
|
||||
toggleErrorsButton.addEventListener("click", toggleErrors);
|
||||
}
|
||||
|
||||
function toggleErrors() {
|
||||
if (toggleErrorsButton.classList.contains("toggled")) {
|
||||
toggleErrorsButton.classList.remove("toggled");
|
||||
uncollapseAllErrors();
|
||||
} else {
|
||||
toggleErrorsButton.classList.add("toggled");
|
||||
collapseAllErrors();
|
||||
}
|
||||
}
|
||||
|
||||
function collapseAllErrors() {
|
||||
let firstNoErrorLine = false;
|
||||
let lines = document.querySelectorAll('.log-inner > .entry');
|
||||
let totalLines = lines.length;
|
||||
for (const [i, line] of lines.entries()) {
|
||||
let lineNumber = line.querySelector(".line-number").innerHTML;
|
||||
if (line.classList.contains("entry-no-error")) {
|
||||
line.style.display = "none";
|
||||
|
||||
if (firstNoErrorLine === false) {
|
||||
firstNoErrorLine = lineNumber;
|
||||
}
|
||||
|
||||
if (i + 1 === totalLines && firstNoErrorLine) {
|
||||
line.insertAdjacentElement("afterend", generateCollapsedLines(firstNoErrorLine, lineNumber));
|
||||
}
|
||||
} else {
|
||||
if (firstNoErrorLine) {
|
||||
line.insertAdjacentElement("beforebegin", generateCollapsedLines(firstNoErrorLine, lineNumber - 1));
|
||||
firstNoErrorLine = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function uncollapseAllErrors() {
|
||||
document.querySelectorAll('.entry-no-error').forEach(line => line.style.removeProperty("display"));
|
||||
document.querySelectorAll('.collapsed-lines').forEach(collapsed => collapsed.remove());
|
||||
}
|
||||
|
||||
function handleCollapsedClick(e) {
|
||||
let collapsed = e.currentTarget;
|
||||
let positionElement = document.getElementById(`L${parseInt(collapsed.dataset.end) + 1}`);
|
||||
let position;
|
||||
if (positionElement) {
|
||||
position = positionElement.getBoundingClientRect().top - window.scrollY;
|
||||
}
|
||||
for (let i = parseInt(collapsed.dataset.start); i <= parseInt(collapsed.dataset.end); i++) {
|
||||
document.getElementById(`L${i}`).parentElement.parentElement.style.removeProperty("display");
|
||||
}
|
||||
if (positionElement) {
|
||||
window.scrollTo({
|
||||
left: 0,
|
||||
top: positionElement.getBoundingClientRect().top - position - collapsed.offsetHeight,
|
||||
behavior: "instant"
|
||||
});
|
||||
}
|
||||
collapsed.remove();
|
||||
}
|
||||
|
||||
function generateCollapsedLines(start, end) {
|
||||
let count = end - start + 1;
|
||||
let string = count === 1 ? "line" : "lines";
|
||||
|
||||
let collapsedRow = document.createElement("div");
|
||||
collapsedRow.classList.add("collapsed-lines");
|
||||
collapsedRow.dataset.start = start;
|
||||
collapsedRow.dataset.end = end;
|
||||
collapsedRow.appendChild(document.createElement("div"));
|
||||
collapsedRow.addEventListener("click", handleCollapsedClick);
|
||||
|
||||
let collapsedLinesCount = document.createElement("div");
|
||||
collapsedLinesCount.classList.add("collapsed-lines-count");
|
||||
let icon = document.createElement("i");
|
||||
icon.classList.add("fa-solid", "fa-angle-up");
|
||||
collapsedLinesCount.appendChild(icon);
|
||||
collapsedLinesCount.append(` ${count} ${string} `);
|
||||
collapsedLinesCount.append(icon.cloneNode());
|
||||
collapsedRow.appendChild(collapsedLinesCount);
|
||||
|
||||
return collapsedRow;
|
||||
}
|
||||
|
||||
/* convert timestamps */
|
||||
let timeElements = document.querySelectorAll('[data-time]');
|
||||
for (const element of timeElements) {
|
||||
const timestamp = parseInt(element.dataset.time);
|
||||
if (isNaN(timestamp)) {
|
||||
continue;
|
||||
}
|
||||
const date = new Date(timestamp * 1000);
|
||||
element.innerHTML = date.toLocaleString();
|
||||
}
|
||||
|
||||
/* settings */
|
||||
const settingCheckboxes = document.querySelectorAll(".setting-checkbox");
|
||||
settingCheckboxes.forEach(checkbox => checkbox.addEventListener("change", handleSettingChange));
|
||||
|
||||
let settingsChannel = null;
|
||||
if (typeof BroadcastChannel !== "undefined") {
|
||||
settingsChannel = new BroadcastChannel("mc-logs-settings");
|
||||
settingsChannel.onmessage = (e) => {
|
||||
if (e.data.type === "settings-updated") {
|
||||
for (const checkbox of settingCheckboxes) {
|
||||
checkbox.checked = !!e.data.settings[checkbox.dataset.key];
|
||||
applySetting(checkbox);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function handleSettingChange(e) {
|
||||
let checkbox = e.target;
|
||||
applySetting(checkbox);
|
||||
saveSettings();
|
||||
if (settingsChannel) {
|
||||
settingsChannel.postMessage({
|
||||
type: "settings-updated",
|
||||
settings: getCurrentSettings()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function applySetting(checkbox) {
|
||||
let bodyClass = checkbox.dataset.bodyClass;
|
||||
if (checkbox.checked) {
|
||||
document.body.classList.add(bodyClass);
|
||||
} else {
|
||||
document.body.classList.remove(bodyClass);
|
||||
}
|
||||
switch (checkbox.dataset.key) {
|
||||
case "floatingScrollbar":
|
||||
initFloatingScrollbar();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function getCurrentSettings() {
|
||||
const data = {};
|
||||
for (const checkbox of settingCheckboxes) {
|
||||
data[checkbox.dataset.key] = checkbox.checked;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
function saveSettings() {
|
||||
const data = {};
|
||||
for (const checkbox of settingCheckboxes) {
|
||||
data[checkbox.dataset.key] = checkbox.checked;
|
||||
}
|
||||
document.cookie = "MCLOGS_SETTINGS=" + encodeURIComponent(JSON.stringify(data)) + ";path=/;expires=" + new Date(new Date().getTime() + 100 * 365 * 24 * 60 * 60 * 1000).toUTCString();
|
||||
}
|
||||
|
||||
/* copy to clipboard */
|
||||
const copyButtons = document.querySelectorAll("[data-clipboard]");
|
||||
copyButtons.forEach(button => button.addEventListener("click", handleCopyButtonClick));
|
||||
const doneClassName = "fa-solid fa-check";
|
||||
|
||||
async function handleCopyButtonClick(e) {
|
||||
const button = e.currentTarget;
|
||||
const data = button.dataset.clipboard;
|
||||
await navigator.clipboard.writeText(data);
|
||||
|
||||
const iconElement = button.querySelector("i");
|
||||
if (!iconElement) {
|
||||
return;
|
||||
}
|
||||
const originalClassName = iconElement.className;
|
||||
if (originalClassName === doneClassName) {
|
||||
return;
|
||||
}
|
||||
iconElement.className = doneClassName;
|
||||
setTimeout(() => {
|
||||
iconElement.className = originalClassName;
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
/* delete button */
|
||||
const deleteButton = document.querySelector(".delete-log-button");
|
||||
const deleteErrorElement = document.querySelector(".delete-overlay .popover-error");
|
||||
if (deleteButton) {
|
||||
deleteButton.addEventListener("click", handleDeleteButtonClick);
|
||||
}
|
||||
|
||||
async function handleDeleteButtonClick() {
|
||||
deleteErrorElement.style.display = "none";
|
||||
const response = await fetch(window.location.href, {
|
||||
method: "DELETE",
|
||||
credentials: "include"
|
||||
});
|
||||
if (!response.ok) {
|
||||
deleteErrorElement.style.display = "block";
|
||||
deleteErrorElement.textContent = `${response.status} (${response.statusText})`;
|
||||
return;
|
||||
}
|
||||
window.location.href = "/";
|
||||
}
|
||||
|
||||
/* floating scroll bar */
|
||||
const browser = getComputedStyle(document.body)
|
||||
.getPropertyValue("--browser")
|
||||
.replaceAll(/['"]/g, '')
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
const floatingScrollbar = document.querySelector(".floating-scrollbar");
|
||||
let logContainer = null;
|
||||
if (browser === "firefox") {
|
||||
logContainer = document.querySelector(".log");
|
||||
} else {
|
||||
logContainer = document.querySelector(".log-inner");
|
||||
}
|
||||
|
||||
if (floatingScrollbar && logContainer) {
|
||||
updateFloatingScrollbarWidths();
|
||||
|
||||
floatingScrollbar.addEventListener("scroll", () => {
|
||||
syncScroll(floatingScrollbar, logContainer);
|
||||
});
|
||||
|
||||
logContainer.addEventListener("scroll", () => {
|
||||
syncScroll(logContainer, floatingScrollbar);
|
||||
});
|
||||
|
||||
const observer = new ResizeObserver(() => {
|
||||
updateFloatingScrollbarWidths();
|
||||
});
|
||||
observer.observe(logContainer);
|
||||
}
|
||||
|
||||
function syncScroll(source, target) {
|
||||
if (Math.abs(source.scrollLeft - target.scrollLeft) > 1) {
|
||||
target.scrollLeft = source.scrollLeft;
|
||||
}
|
||||
}
|
||||
|
||||
function initFloatingScrollbar() {
|
||||
if (!floatingScrollbar || !logContainer) {
|
||||
return;
|
||||
}
|
||||
updateFloatingScrollbarWidths();
|
||||
syncScroll(logContainer, floatingScrollbar);
|
||||
}
|
||||
|
||||
function updateFloatingScrollbarWidths() {
|
||||
floatingScrollbar.style.setProperty(
|
||||
"--floating-scrollbar-width",
|
||||
`${logContainer.clientWidth}px`
|
||||
);
|
||||
|
||||
floatingScrollbar.style.setProperty(
|
||||
"--floating-scrollbar-content-width",
|
||||
`${logContainer.scrollWidth}px`
|
||||
);
|
||||
}
|
||||
365
web/public/js/start.js
Normal file
365
web/public/js/start.js
Normal file
@@ -0,0 +1,365 @@
|
||||
/* Paste area */
|
||||
const source = document.body.dataset.name || location.host;
|
||||
const pasteArea = document.getElementById('paste-text');
|
||||
const pastePlaceholder = document.querySelector('.paste-placeholder');
|
||||
const pasteSaveButtons = document.querySelectorAll('.paste-save');
|
||||
const fileSelectButton = document.getElementById('paste-select-file');
|
||||
const pasteClipboardButton = document.getElementById('paste-clipboard');
|
||||
const pasteError = document.getElementById('paste-error');
|
||||
|
||||
pasteArea.focus();
|
||||
pasteArea.addEventListener('input', reevaluateContentStatus);
|
||||
pasteArea.addEventListener('paste', handlePasteEvent);
|
||||
pasteSaveButtons.forEach(button => button.addEventListener('click', sendLog));
|
||||
fileSelectButton.addEventListener('click', selectLogFile);
|
||||
pasteClipboardButton.addEventListener('click', pasteFromClipboard);
|
||||
|
||||
reevaluateContentStatus();
|
||||
|
||||
document.addEventListener('keydown', event => {
|
||||
if (event.key.toLowerCase() === 's' && (event.ctrlKey || event.metaKey)) {
|
||||
void sendLog();
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
/**
|
||||
* Save the log to the API
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function sendLog() {
|
||||
if (pasteArea.value === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
clearError();
|
||||
pasteSaveButtons.forEach(button => button.classList.add("btn-working"));
|
||||
|
||||
try {
|
||||
let log = pasteArea.value;
|
||||
log = applyFilters(log);
|
||||
|
||||
const bodyData = {
|
||||
"content": log,
|
||||
"source": source,
|
||||
"metadata": Array.isArray(self.METADATA) ? self.METADATA : []
|
||||
};
|
||||
|
||||
let headers = {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
let body = JSON.stringify(bodyData);
|
||||
if (isGzSupported()) {
|
||||
headers["Content-Encoding"] = "gzip";
|
||||
body = await packGz(body);
|
||||
}
|
||||
|
||||
const response = await fetch(`/new`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Content-Encoding": "gzip"
|
||||
},
|
||||
body
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
showError(`${response.status} (${response.statusText})`);
|
||||
return;
|
||||
}
|
||||
|
||||
let data = null;
|
||||
try {
|
||||
data = await response.json();
|
||||
} catch (e) {
|
||||
console.error("Failed to parse JSON returned by API", e);
|
||||
showError("API returned invalid JSON");
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof data === 'object' && !data.success && data.error) {
|
||||
console.error(new Error("API returned an error"), data.error);
|
||||
showError(data.error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof data !== 'object' || !data.success || !data.id) {
|
||||
console.error(new Error("API returned an invalid response"), data);
|
||||
showError("API returned an invalid response");
|
||||
return;
|
||||
}
|
||||
|
||||
location.href = data.url;
|
||||
} catch (e) {
|
||||
showError("Network error");
|
||||
}
|
||||
}
|
||||
|
||||
/* filters */
|
||||
function applyFilters(text) {
|
||||
if (typeof FILTERS === "undefined" || !Array.isArray(FILTERS)) {
|
||||
return text;
|
||||
}
|
||||
for (let filter of FILTERS) {
|
||||
text = applyFilter(text, filter);
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
function applyFilter(text, filter) {
|
||||
switch (filter.type) {
|
||||
case 'trim':
|
||||
return text.trim();
|
||||
case 'limit-bytes':
|
||||
return text.substring(0, filter.data.limit);
|
||||
case 'limit-lines':
|
||||
return text.split('\n').slice(0, filter.data.limit).join('\n');
|
||||
case 'regex':
|
||||
try {
|
||||
for (const pattern of filter.data.patterns) {
|
||||
const regex = new RegExp(pattern.pattern, 'g' + pattern.modifiers.join());
|
||||
text = text.replace(regex, (match) => {
|
||||
for (const exemption of filter.data.exemptions) {
|
||||
if (new RegExp(exemption.pattern, exemption.modifiers.join()).test(match)) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
return pattern.replacement;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error applying regex filter', e);
|
||||
}
|
||||
return text;
|
||||
default:
|
||||
console.error('Unknown filter type', filter.type);
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
async function pasteFromClipboard() {
|
||||
try {
|
||||
let content = await navigator.clipboard.readText();
|
||||
if (!content || content.trim().length === 0) {
|
||||
showError("Clipboard is empty.");
|
||||
return;
|
||||
}
|
||||
pasteArea.value = content;
|
||||
reevaluateContentStatus();
|
||||
} catch (err) {
|
||||
showError("Clipboard is empty or not accessible.");
|
||||
}
|
||||
}
|
||||
|
||||
function reevaluateContentStatus() {
|
||||
clearError();
|
||||
if (pasteArea.value.length > 0) {
|
||||
pastePlaceholder.style.display = 'none';
|
||||
pasteSaveButtons.forEach(button => button.removeAttribute("disabled"));
|
||||
} else {
|
||||
pastePlaceholder.style.display = 'flex';
|
||||
pasteSaveButtons.forEach(button => button.setAttribute("disabled", "disabled"));
|
||||
}
|
||||
}
|
||||
|
||||
function showError(message) {
|
||||
pasteSaveButtons.forEach(button => button.classList.remove("btn-working"));
|
||||
pasteError.innerText = message;
|
||||
pasteError.style.display = 'block';
|
||||
}
|
||||
|
||||
function clearError() {
|
||||
pasteSaveButtons.forEach(button => button.classList.remove("btn-working"));
|
||||
pasteError.innerText = '';
|
||||
pasteError.style.display = 'none';
|
||||
}
|
||||
|
||||
/* File handling */
|
||||
async function handlePasteEvent(e) {
|
||||
if (e.clipboardData?.files?.length > 0) {
|
||||
e.preventDefault();
|
||||
await loadFileContents(e.clipboardData.files[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Blob} file
|
||||
* @return {Promise<Uint8Array>}
|
||||
*/
|
||||
function readFile(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let reader = new FileReader();
|
||||
// noinspection JSCheckFunctionSignatures
|
||||
reader.onload = () => resolve(new Uint8Array(reader.result));
|
||||
reader.onerror = e => reject(e);
|
||||
reader.readAsArrayBuffer(file);
|
||||
});
|
||||
}
|
||||
|
||||
async function loadFileContents(file) {
|
||||
if (file.size > 1024 * 1024 * 100) {
|
||||
showError(`File is too large.`);
|
||||
return;
|
||||
}
|
||||
let content = await readFile(file);
|
||||
if (file.name.endsWith('.gz')) {
|
||||
if (!isGzSupported()) {
|
||||
showError(`Gzip files are not supported in this browser.`);
|
||||
return;
|
||||
}
|
||||
content = await unpackGz(content);
|
||||
}
|
||||
|
||||
if (content.includes(0)) {
|
||||
showError(`This file is not supported.`);
|
||||
return;
|
||||
}
|
||||
|
||||
pasteArea.value = new TextDecoder().decode(content);
|
||||
reevaluateContentStatus();
|
||||
}
|
||||
|
||||
function selectLogFile() {
|
||||
let input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.style.display = 'none';
|
||||
document.body.appendChild(input);
|
||||
input.onchange = async () => {
|
||||
if (input.files.length) {
|
||||
await loadFileContents(input.files[0]);
|
||||
}
|
||||
}
|
||||
input.click();
|
||||
document.body.removeChild(input);
|
||||
}
|
||||
|
||||
/* Gzip compression */
|
||||
function isGzSupported() {
|
||||
return (typeof CompressionStream !== 'undefined') && (typeof DecompressionStream !== 'undefined');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} raw
|
||||
* @returns {Promise<Uint8Array>}
|
||||
*/
|
||||
async function packGz(raw) {
|
||||
let data = new TextEncoder().encode(raw);
|
||||
let inputStream = new ReadableStream({
|
||||
start: (controller) => {
|
||||
controller.enqueue(data);
|
||||
controller.close();
|
||||
}
|
||||
});
|
||||
const cs = new CompressionStream('gzip');
|
||||
const compressedStream = inputStream.pipeThrough(cs);
|
||||
return new Uint8Array(await new Response(compressedStream).arrayBuffer());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} data
|
||||
* @return {Promise<Uint8Array>}
|
||||
*/
|
||||
async function unpackGz(data) {
|
||||
let inputStream = new ReadableStream({
|
||||
start: (controller) => {
|
||||
controller.enqueue(data);
|
||||
controller.close();
|
||||
}
|
||||
});
|
||||
const ds = new DecompressionStream('gzip');
|
||||
const decompressedStream = inputStream.pipeThrough(ds);
|
||||
return new Uint8Array(await new Response(decompressedStream).arrayBuffer());
|
||||
}
|
||||
|
||||
function isDragEventValid(e) {
|
||||
if (!e.dataTransfer) {
|
||||
return false;
|
||||
}
|
||||
let types = Array.from(e.dataTransfer.types);
|
||||
if (types.includes('text/uri-list')) {
|
||||
return false;
|
||||
}
|
||||
return types.includes('Files') || types.includes('text/plain');
|
||||
}
|
||||
|
||||
/* Drag and drop */
|
||||
const dropZone = document.getElementById('dropzone');
|
||||
let windowDragCount = 0;
|
||||
let dropZoneDragCount = 0;
|
||||
|
||||
window.addEventListener('dragover', e => e.preventDefault());
|
||||
window.addEventListener('dragenter', e => {
|
||||
e.preventDefault();
|
||||
if (isDragEventValid(e)) {
|
||||
updateWindowDragCount(1);
|
||||
}
|
||||
});
|
||||
window.addEventListener('dragleave', e => {
|
||||
e.preventDefault();
|
||||
if (isDragEventValid(e)) {
|
||||
updateWindowDragCount(-1);
|
||||
}
|
||||
});
|
||||
window.addEventListener('drop', e => {
|
||||
e.preventDefault();
|
||||
if (isDragEventValid(e)) {
|
||||
updateWindowDragCount(-1);
|
||||
}
|
||||
});
|
||||
|
||||
dropZone.addEventListener('dragenter', e => {
|
||||
e.preventDefault();
|
||||
if (isDragEventValid(e)) {
|
||||
updateDropZoneDragCount(1);
|
||||
}
|
||||
});
|
||||
dropZone.addEventListener('dragleave', e => {
|
||||
e.preventDefault();
|
||||
if (isDragEventValid(e)) {
|
||||
updateDropZoneDragCount(-1);
|
||||
}
|
||||
});
|
||||
dropZone.addEventListener('drop', async e => {
|
||||
e.preventDefault();
|
||||
if (isDragEventValid(e)) {
|
||||
updateDropZoneDragCount(-1);
|
||||
}
|
||||
await handleDropEvent(e);
|
||||
});
|
||||
|
||||
function updateWindowDragCount(amount) {
|
||||
windowDragCount = Math.max(0, windowDragCount + amount);
|
||||
if (windowDragCount > 0) {
|
||||
dropZone.classList.add('window-dragover');
|
||||
} else {
|
||||
dropZone.classList.remove('window-dragover');
|
||||
}
|
||||
}
|
||||
|
||||
function updateDropZoneDragCount(amount) {
|
||||
dropZoneDragCount = Math.max(0, dropZoneDragCount + amount);
|
||||
if (dropZoneDragCount > 0) {
|
||||
dropZone.classList.add('dragover');
|
||||
} else {
|
||||
dropZone.classList.remove('dragover');
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDropEvent(e) {
|
||||
console.log(e.dataTransfer?.types);
|
||||
let files = e.dataTransfer.files;
|
||||
if (files.length !== 1) {
|
||||
if (Array.from(e.dataTransfer.types).includes('text/plain')) {
|
||||
pasteArea.value = e.dataTransfer.getData('text/plain');
|
||||
reevaluateContentStatus();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
await loadFileContents(files[0]);
|
||||
}
|
||||
Reference in New Issue
Block a user