Ronbun/ui/static/index.js

164 lines
5.3 KiB
JavaScript

// Focus-Aware Keystroke Pattern Detector with Redirects
// This script detects keyboard shortcuts only when NOT focusing on input elements
document.addEventListener('DOMContentLoaded', function () {
// Variables to store keystroke history
let keystrokeHistory = '';
// Define patterns and their corresponding redirect URLs
const shortcuts = {
'uli': '/user/login',
'ulo': '/user/logout',
'usu': '/user/signup',
'nnn': '/page/create',
'eee': '/page/edit',
'ddd': '/page/delete',
'hhh': '/',
'aaa': '/user/account',
};
// Maximum length of patterns to track
const maxPatternLength = Object.keys(shortcuts).reduce((max, pattern) =>
Math.max(max, pattern.length), 0);
// Check if user is currently focused on an input element
function isUserTypingInFormElement() {
const activeElement = document.activeElement;
const inputTags = ['INPUT', 'TEXTAREA', 'SELECT'];
const isContentEditable = activeElement.hasAttribute('contenteditable') &&
activeElement.getAttribute('contenteditable') !== 'false';
return inputTags.includes(activeElement.tagName) || isContentEditable;
}
// Listen for keydown events on the document
document.addEventListener('keydown', function (event) {
// Skip if the user is typing in an input field or textarea
if (isUserTypingInFormElement()) {
return;
}
// Get the pressed key
const key = event.key;
// Add the key to keystroke history
keystrokeHistory += key;
// Keep only the last N characters where N is the max pattern length
if (keystrokeHistory.length > maxPatternLength) {
keystrokeHistory = keystrokeHistory.slice(-maxPatternLength);
}
// Check if any pattern matches the end of keystroke history
for (const [pattern, url] of Object.entries(shortcuts)) {
if (keystrokeHistory.endsWith(pattern)) {
if (pattern === "eee") {
const path = window.location.pathname;
const cleanPath = path.endsWith('/') ? path.slice(0, -1) : path;
const parts = cleanPath.split('/');
window.location.href = url + "/" + parts[1]
} else {
window.location.href = url;
}
// Reset keystroke history
keystrokeHistory = '';
break;
}
}
});
});
let debounceTimer;
function triggerDemo() {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(async () => {
const oldHtml = formated.innerHTML
const newHtml = await previewText(textarea.value)
if (oldHtml !== newHtml) {
formated.innerHTML = ""
formated.innerHTML = newHtml;
}
}, 500);
}
// Insert text at saved cursor position (works even when not focused)
function insertMedia(slug, extension, reposition, event) {
event.preventDefault()
const value = textarea.value;
let textInsert = ""
if (extension === ".mp4") {
textInsert = `(video: ${slug} autoplay figcaption: )`
}
else if (extension === ".mp3") {
textInsert = `(audio: ${slug})`
} else {
textInsert = `(image: ${slug} alt: )`
}
textarea.value = value.slice(0, savedCursor) + textInsert + value.slice(savedCursor);
if (reposition) {
savedCursor += textInsert.length - 1;
} else {
savedCursor += textInsert.length;
}
// If textarea is focused, update the actual cursor position
if (document.activeElement === textarea) {
textarea.setSelectionRange(savedCursor, savedCursor);
}
// togglePreview()
setTimeout(() => {
triggerDemo()
}, 400)
}
function deleteMedia(filename, event) {
event.preventDefault()
if (!confirm(`Delete "${filename}"?`)) return;
const csrfToken = document.querySelector('#csrf_token')?.value;
fetch(`/files/delete/${encodeURIComponent(filename)}`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: `csrf_token=${encodeURIComponent(csrfToken)}`
})
.then(async response => {
const responseText = await response.text();
if (response.ok) {
const mediaItem = document.querySelector(`#${filename.split('.').slice(0, -1)[0]}`)
if (mediaItem) {
mediaItem.remove()
}
return
}
// Handle error response
const errorMessage = responseText || `Request failed (${response.status})`;
throw new Error(errorMessage);
})
.catch(error => {
console.error('Delete failed:', error);
alert(`Failed to delete file: ${error.message}`);
});
}
async function previewText(pageText) {
const csrfToken = document.querySelector('#csrf_token')?.value;
const res = await fetch("/page/preview", {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken
},
body: pageText
})
const text = await res.text()
return text
}