firefox addon store bullshit

This commit is contained in:
Zan
2025-12-10 15:51:37 +01:00
parent 75d6cc4676
commit f6a52b73de
15 changed files with 451 additions and 551 deletions

View File

@@ -402,9 +402,9 @@ async function transformAbsencesPage() {
container.className = 'kreta-container'; container.className = 'kreta-container';
const headerDiv = document.createElement('div'); const headerDiv = document.createElement('div');
const parser = new DOMParser(); const template = document.createElement('template');
const doc = parser.parseFromString(await createTemplate.header(), 'text/html'); template.innerHTML = await createTemplate.header();
const tempDiv = doc.body; const tempDiv = template.content;
while (tempDiv.firstChild) { while (tempDiv.firstChild) {
headerDiv.appendChild(tempDiv.firstChild); headerDiv.appendChild(tempDiv.firstChild);
} }

View File

@@ -499,22 +499,22 @@ class DashboardRenderer {
async render() { async render() {
await this.init(); await this.init();
document.body.innerHTML = ''; helper.clearElement(document.body);
const kretaContainer = document.createElement('div'); const kretaContainer = document.createElement('div');
kretaContainer.className = 'kreta-container'; kretaContainer.className = 'kreta-container';
const headerDiv = document.createElement('div'); const headerDiv = document.createElement('div');
const parser = new DOMParser(); const template = document.createElement('template');
const headerDoc = parser.parseFromString(await createTemplate.header(), 'text/html'); template.innerHTML = await createTemplate.header();
const headerContent = headerDoc.body; const headerContent = template.content;
while (headerContent.firstChild) { while (headerContent.firstChild) {
headerDiv.appendChild(headerContent.firstChild); headerDiv.appendChild(headerContent.firstChild);
} }
kretaContainer.appendChild(headerDiv); kretaContainer.appendChild(headerDiv);
const mainContentDiv = document.createElement('div'); const mainContentDiv = document.createElement('div');
const parser2 = new DOMParser(); const template2 = document.createElement('template');
const mainDoc = parser2.parseFromString(this.generateMainContent(), 'text/html'); template2.innerHTML = this.generateMainContent();
const mainContent = mainDoc.body; const mainContent = template2.content;
while (mainContent.firstChild) { while (mainContent.firstChild) {
mainContentDiv.appendChild(mainContent.firstChild); mainContentDiv.appendChild(mainContent.firstChild);
} }

View File

@@ -1,57 +0,0 @@
body.maintenance-mode {
margin:0;
padding:0;
height:100vh;
display:flex;
align-items:center;
justify-content:center;
background-color:var(--background);
color:var(--text-primary);
font-family:'Figtree',sans-serif;
}
body {
background-color:var(--background) !important;
}
.maintenance-container {
text-align:center;
padding:2rem;
border-radius:8px;
background-color:var(--card-card);
box-shadow:0 var(--shadow-blur) 6px var(--accent-shadow);
max-width:600px;
width:90%;
}
.maintenance-logo {
width:128px;
height:128px;
margin:0 auto 2rem;
}
.maintenance-title {
font-size:1.5rem;
font-weight:600;
margin-bottom:1rem;
color:var(--accent-accent);
font-family:'Montserrat',sans-serif;
}
.maintenance-message {
font-size:1rem;
line-height:1.5;
margin-bottom:1.5rem;
color:var(--text-primary);
font-family:'Figtree',sans-serif;
}
.maintenance-footer {
font-size:0.875rem;
color:var(--text-secondary);
margin-top:2rem;
font-family:'Figtree',sans-serif;
}
.maintenance-cactus {
position:fixed;
bottom:0px;
right:20px;
width:120px;
height:120px;
opacity:1;
z-index:1000;
}

View File

@@ -1,290 +0,0 @@
(() => {
// reCAPTCHA functionality removed for security compliance
const loadDependencies = async () => {
// reCAPTCHA functionality removed for security compliance
// Extension now works without external script dependencies
};
const createPageStructure = () => {
// Biztonságos DOM létrehozás innerHTML helyett
document.body.innerHTML = '';
// Biztonságos HTML parsing DOMParser használatával
const parser = new DOMParser();
const doc = parser.parseFromString(`
<div class="forgot-container">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons+Round" rel="stylesheet">
<style>
.g-recaptcha {
margin-top: 5px;
display: flex;
justify-content: center;
}
</style>
<div class="forgot-card">
<header class="forgot-header">
<p class="logo-text">
<img src="${chrome.runtime.getURL("images/firka_logo.png")}" alt="Firka" class="logo">
Firka
</p>
</header>
<h1 class="forgot-title" data-i18n="forgotpassword.title">Elfelejtett jelszó</h1>
<form id="forgotForm" novalidate>
<div class="form-group">
<label class="form-label" for="BejelentkezesiNev" data-i18n="forgotpassword.om_id_label">OM azonosító</label>
<input type="text" id="BejelentkezesiNev" name="BejelentkezesiNev" class="form-control"
data-i18n-attr="placeholder" data-i18n="forgotpassword.om_id_placeholder"
placeholder="Adja meg az OM azonosítóját" required>
<div class="error-message" data-i18n="forgotpassword.om_id_required">Az OM azonosító megadása kötelező</div>
</div>
<div class="form-group">
<label class="form-label" for="EmailCim" data-i18n="forgotpassword.email_label">E-mail cím</label>
<input type="email" id="EmailCim" name="EmailCim" class="form-control"
data-i18n-attr="placeholder" data-i18n="forgotpassword.email_placeholder"
placeholder="Adja meg az e-mail címét" required>
<div class="error-message" data-i18n="forgotpassword.email_required">Az e-mail cím megadása kötelező</div>
</div>
<!-- reCAPTCHA container removed for security compliance -->
<div class="form-actions">
<a href="/Adminisztracio/Login" class="help-link" data-i18n="forgotpassword.back_to_login">
Vissza a bejelentkezéshez
</a>
<button type="submit" class="btn-submit" data-i18n="forgotpassword.reset_button">
Jelszó visszaállítása
</button>
</div>
</form>
</div>
</div>
`, 'text/html');
const tempDiv = doc.body;
// Biztonságos DOM hozzáadás
while (tempDiv.firstChild) {
document.body.appendChild(tempDiv.firstChild);
}
};
const transformForgotPasswordPage = async () => {
await loadDependencies();
const isDarkMode = localStorage.getItem("darkMode") === "true";
document.documentElement.setAttribute(
"data-theme",
isDarkMode ? "dark" : "light",
);
chrome.runtime.onMessage.addListener((message) => {
if (message.action === "toggleTheme") {
document.documentElement.setAttribute(
"data-theme",
message.darkMode ? "dark" : "light",
);
localStorage.setItem("darkMode", message.darkMode);
}
});
createPageStructure();
let attempts = 0;
const maxAttempts = 50;
const waitForLanguageManager = () => {
return new Promise((resolve) => {
const checkLanguageManager = () => {
attempts++;
if (typeof LanguageManager !== "undefined" && LanguageManager.t) {
setTimeout(resolve, 200);
} else if (attempts < maxAttempts) {
setTimeout(checkLanguageManager, 100);
} else {
console.warn("LanguageManager not available, using fallback texts");
resolve();
}
};
checkLanguageManager();
});
};
await waitForLanguageManager();
if (typeof LanguageManager !== "undefined" && LanguageManager.t) {
const elements = document.querySelectorAll("[data-i18n]");
elements.forEach((element) => {
const key = element.getAttribute("data-i18n");
const translation = LanguageManager.t(key);
if (translation && translation !== key) {
const attr = element.getAttribute("data-i18n-attr");
if (attr) {
element.setAttribute(attr, translation);
} else {
element.textContent = translation;
}
}
});
}
// reCAPTCHA rendering removed for security compliance
setupFormValidation();
};
const setupFormValidation = () => {
const form = document.getElementById("forgotForm");
const inputs = form.querySelectorAll(".form-control");
inputs.forEach((input) => {
input.addEventListener("input", () => {
validateInput(input);
});
input.addEventListener("blur", () => {
validateInput(input, true);
});
});
form.addEventListener("submit", handleSubmit);
};
const validateInput = (input, showError = false) => {
const isValid = input.value.trim().length > 0;
const errorElement = input.nextElementSibling;
if (!isValid && showError) {
input.classList.add("error");
errorElement?.classList.add("show");
} else {
input.classList.remove("error");
errorElement?.classList.remove("show");
}
return isValid;
};
const showMessage = (message, isError = false) => {
const existingMessage = document.querySelector(".message");
if (existingMessage) {
existingMessage.remove();
}
const messageDiv = document.createElement("div");
messageDiv.className = `message ${isError ? "error" : "success"}`;
messageDiv.textContent = message;
const form = document.getElementById("forgotForm");
form.insertBefore(messageDiv, form.firstChild);
setTimeout(() => {
if (messageDiv.parentNode) {
messageDiv.remove();
}
}, 5000);
};
const validateEmail = (email) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
const handleSubmit = async (event) => {
event.preventDefault();
const form = event.target;
const inputs = form.querySelectorAll(".form-control[required]");
let isValid = true;
inputs.forEach((input) => {
if (!validateInput(input, true)) {
isValid = false;
}
});
const emailInput = form.querySelector("#EmailCim");
if (emailInput.value && !validateEmail(emailInput.value)) {
emailInput.classList.add("error");
const errorElement = emailInput.nextElementSibling;
if (errorElement) {
errorElement.textContent = LanguageManager.t(
"forgotpassword.invalid_email",
);
errorElement.classList.add("show");
}
isValid = false;
}
// reCAPTCHA validation removed for security compliance
if (!isValid) {
return;
}
const submitButton = form.querySelector(".btn-submit");
const originalText = submitButton.textContent;
submitButton.disabled = true;
submitButton.textContent = LanguageManager.t("loading.text") || "Küldés...";
try {
const formData = new FormData(form);
// reCAPTCHA data removed for security compliance
const response = await fetch(
"/Adminisztracio/ElfelejtettJelszo/LinkKuldes",
{
method: "POST",
body: formData,
headers: {
"X-Requested-With": "XMLHttpRequest",
},
},
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (result.Success) {
showMessage(LanguageManager.t("forgotpassword.success_message"));
form.reset();
// reCAPTCHA reset removed
setTimeout(() => {
window.location.href = "/Adminisztracio/Login";
}, 3000);
} else {
showMessage(
result.Message || LanguageManager.t("forgotpassword.error_message"),
true,
);
// reCAPTCHA reset removed
}
} catch (error) {
console.error("Password reset error:", error);
showMessage(LanguageManager.t("forgotpassword.error_message"), true);
// reCAPTCHA reset removed
} finally {
submitButton.disabled = false;
submitButton.textContent = originalText;
}
};
if (window.location.href.includes("/Adminisztracio/ElfelejtettJelszo")) {
transformForgotPasswordPage().catch(console.error);
}
})();

View File

@@ -45,7 +45,7 @@ function checkMaintenancePage() {
); );
existingStyles.forEach((style) => style.remove()); existingStyles.forEach((style) => style.remove());
body.innerHTML = ""; helper.clearElement(body);
body.classList.add("maintenance-mode"); body.classList.add("maintenance-mode");
body.classList.add("theme-enabled"); body.classList.add("theme-enabled");
body.classList.add("loaded"); body.classList.add("loaded");

View File

@@ -28,17 +28,18 @@
const classAverage = calculateOverallClassAverage(gradesData.subjects); const classAverage = calculateOverallClassAverage(gradesData.subjects);
window.currentGradesData = gradesData; window.currentGradesData = gradesData;
document.body.innerHTML = '';
const parser = new DOMParser(); const htmlString = await generatePageHTML(
const doc = parser.parseFromString(await generatePageHTML(
gradesData, gradesData,
studentAverage, studentAverage,
classAverage, classAverage,
), 'text/html'); );
const tempDiv = doc.body;
while (tempDiv.firstChild) { const template = document.createElement('template');
document.body.appendChild(tempDiv.firstChild); template.innerHTML = htmlString;
}
helper.clearElement(document.body);
document.body.appendChild(template.content);
setupUserDropdown(); setupUserDropdown();

View File

@@ -120,13 +120,11 @@ async function transformLoginPage() {
</div> </div>
`; `;
document.body.innerHTML = ''; const template = document.createElement('template');
const parser = new DOMParser(); template.innerHTML = newHTML;
const doc = parser.parseFromString(newHTML, 'text/html');
const tempDiv = doc.body; helper.clearElement(document.body);
while (tempDiv.firstChild) { document.body.appendChild(template.content);
document.body.appendChild(tempDiv.firstChild);
}
setupEventListeners(); setupEventListeners();
} catch (error) { } catch (error) {

View File

@@ -83,13 +83,12 @@ async function transformTwoFactorPage() {
</div> </div>
`; `;
document.body.innerHTML = ''; const template = document.createElement('template');
const parser = new DOMParser(); template.innerHTML = newHTML;
const doc = parser.parseFromString(newHTML, 'text/html');
const tempDiv = doc.body; helper.clearElement(document.body);
while (tempDiv.firstChild) { document.body.appendChild(template.content);
document.body.appendChild(tempDiv.firstChild);
}
applyTheme(); applyTheme();
setupEventListeners(); setupEventListeners();
if (typeof loadingScreen !== "undefined") { if (typeof loadingScreen !== "undefined") {
@@ -170,7 +169,7 @@ function handleSubmit(event) {
const submitButton = form.querySelector(".btn-kreta"); const submitButton = form.querySelector(".btn-kreta");
if (submitButton) { if (submitButton) {
submitButton.disabled = true; submitButton.disabled = true;
submitButton.innerHTML = ''; helper.clearElement(submitButton);
const spinnerSpan = document.createElement('span'); const spinnerSpan = document.createElement('span');
spinnerSpan.className = 'spinner'; spinnerSpan.className = 'spinner';
const textSpan = document.createElement('span'); const textSpan = document.createElement('span');

View File

@@ -34,10 +34,10 @@
</footer> </footer>
</div> </div>
`; `;
document.body.innerHTML = ''; helper.clearElement(document.body);
const parser = new DOMParser(); const template = document.createElement('template');
const doc = parser.parseFromString(newHTML, 'text/html'); template.innerHTML = newHTML;
const tempDiv = doc.body; const tempDiv = template.content;
while (tempDiv.firstChild) { while (tempDiv.firstChild) {
document.body.appendChild(tempDiv.firstChild); document.body.appendChild(tempDiv.firstChild);
} }

View File

@@ -42,7 +42,7 @@
function sanitizeHTML(html) { function sanitizeHTML(html) {
const div = document.createElement('div'); const div = document.createElement('div');
div.textContent = html; div.textContent = html;
return div.innerHTML; return div.textContent;
} }
class APIManager { class APIManager {
@@ -159,18 +159,32 @@
const modalContent = document.createElement('div'); const modalContent = document.createElement('div');
modalContent.className = 'modal-content'; modalContent.className = 'modal-content';
modalContent.innerHTML = `
<div class="modal-header"> const modalHeader = document.createElement('div');
<h2>Üzenet részletei</h2> modalHeader.className = 'modal-header';
<button class="modal-close">×</button> const modalTitle = document.createElement('h2');
</div> modalTitle.textContent = 'Üzenet részletei';
<div class="modal-body"> const modalClose = document.createElement('button');
<div class="loading-content"> modalClose.className = 'modal-close';
<div class="loading-spinner"></div> modalClose.textContent = '×';
<p>Üzenet betöltése...</p> modalHeader.appendChild(modalTitle);
</div> modalHeader.appendChild(modalClose);
</div>
`; const modalBody = document.createElement('div');
modalBody.className = 'modal-body';
const loadingContent = document.createElement('div');
loadingContent.className = 'loading-content';
const loadingSpinner = document.createElement('div');
loadingSpinner.className = 'loading-spinner';
const loadingText = document.createElement('p');
loadingText.textContent = 'Üzenet betöltése...';
loadingContent.appendChild(loadingSpinner);
loadingContent.appendChild(loadingText);
modalBody.appendChild(loadingContent);
modalContent.appendChild(modalHeader);
modalContent.appendChild(modalBody);
modalOverlay.appendChild(modalContent); modalOverlay.appendChild(modalContent);
document.body.appendChild(modalOverlay); document.body.appendChild(modalOverlay);
@@ -213,13 +227,24 @@
console.error('Error loading message details:', error); console.error('Error loading message details:', error);
const modalContent = document.querySelector('.modal-content'); const modalContent = document.querySelector('.modal-content');
if (modalContent) { if (modalContent) {
modalContent.querySelector('.modal-body').innerHTML = ` const modalBody = modalContent.querySelector('.modal-body');
<div class="error-content"> helper.clearElement(modalBody);
<h3>Hiba történt</h3>
<p>Az üzenet betöltése sikertelen.</p> const errorContent = document.createElement('div');
<button class="retry-btn" onclick="openMessageModal(${messageId})">Újrapróbálás</button> errorContent.className = 'error-content';
</div> const errorTitle = document.createElement('h3');
`; errorTitle.textContent = 'Hiba történt';
const errorText = document.createElement('p');
errorText.textContent = 'Az üzenet betöltése sikertelen.';
const retryBtn = document.createElement('button');
retryBtn.className = 'retry-btn';
retryBtn.textContent = 'Újrapróbálás';
retryBtn.onclick = () => openMessageModal(messageId);
errorContent.appendChild(errorTitle);
errorContent.appendChild(errorText);
errorContent.appendChild(retryBtn);
modalBody.appendChild(errorContent);
} }
} }
} }
@@ -231,38 +256,86 @@
const subject = message.targy || 'Nincs tárgy'; const subject = message.targy || 'Nincs tárgy';
const content = message.szoveg || 'Nincs tartalom'; const content = message.szoveg || 'Nincs tartalom';
modalContent.querySelector('.modal-body').innerHTML = ` const modalBody = modalContent.querySelector('.modal-body');
<div class="message-details"> helper.clearElement(modalBody);
<div class="message-info">
<div class="info-row"> const messageDetails = document.createElement('div');
<span class="info-label">Feladó:</span> messageDetails.className = 'message-details';
<span class="info-value">${sanitizeHTML(sender)}</span>
</div> const messageInfo = document.createElement('div');
<div class="info-row"> messageInfo.className = 'message-info';
<span class="info-label">Dátum:</span>
<span class="info-value">${date}</span> const senderRow = document.createElement('div');
</div> senderRow.className = 'info-row';
<div class="info-row"> const senderLabel = document.createElement('span');
<span class="info-label">Tárgy:</span> senderLabel.className = 'info-label';
<span class="info-value">${sanitizeHTML(subject)}</span> senderLabel.textContent = 'Feladó:';
</div> const senderValue = document.createElement('span');
</div> senderValue.className = 'info-value';
<div class="message-content"> senderValue.textContent = sanitizeHTML(sender);
<h4>Üzenet tartalma:</h4> senderRow.appendChild(senderLabel);
<div class="message-text">${content}</div> senderRow.appendChild(senderValue);
</div> messageInfo.appendChild(senderRow);
${message.csatolmanyok && message.csatolmanyok.length > 0 ? `
<div class="message-attachments"> const dateRow = document.createElement('div');
<h4>Mellékletek:</h4> dateRow.className = 'info-row';
<ul> const dateLabel = document.createElement('span');
${message.csatolmanyok.map(attachment => ` dateLabel.className = 'info-label';
<li><a href="#" onclick="downloadAttachment('${attachment.azonosito}')">${sanitizeHTML(attachment.nev)}</a></li> dateLabel.textContent = 'Dátum:';
`).join('')} const dateValue = document.createElement('span');
</ul> dateValue.className = 'info-value';
</div> dateValue.textContent = date;
` : ''} dateRow.appendChild(dateLabel);
</div> dateRow.appendChild(dateValue);
`; messageInfo.appendChild(dateRow);
const subjectRow = document.createElement('div');
subjectRow.className = 'info-row';
const subjectLabel = document.createElement('span');
subjectLabel.className = 'info-label';
subjectLabel.textContent = 'Tárgy:';
const subjectValue = document.createElement('span');
subjectValue.className = 'info-value';
subjectValue.textContent = sanitizeHTML(subject);
subjectRow.appendChild(subjectLabel);
subjectRow.appendChild(subjectValue);
messageInfo.appendChild(subjectRow);
messageDetails.appendChild(messageInfo);
const messageContent = document.createElement('div');
messageContent.className = 'message-content';
const contentTitle = document.createElement('h4');
contentTitle.textContent = 'Üzenet tartalma:';
messageContent.appendChild(contentTitle);
const messageText = document.createElement('div');
messageText.className = 'message-text';
messageText.textContent = content;
messageContent.appendChild(messageText);
messageDetails.appendChild(messageContent);
if (message.csatolmanyok && message.csatolmanyok.length > 0) {
const messageAttachments = document.createElement('div');
messageAttachments.className = 'message-attachments';
const attachTitle = document.createElement('h4');
attachTitle.textContent = 'Mellékletek:';
messageAttachments.appendChild(attachTitle);
const attachList = document.createElement('ul');
message.csatolmanyok.forEach(attachment => {
const li = document.createElement('li');
const a = document.createElement('a');
a.href = '#';
a.textContent = sanitizeHTML(attachment.nev);
a.onclick = () => downloadAttachment(attachment.azonosito);
li.appendChild(a);
attachList.appendChild(li);
});
messageAttachments.appendChild(attachList);
messageDetails.appendChild(messageAttachments);
}
modalBody.appendChild(messageDetails);
} }
function closeMessageModal() { function closeMessageModal() {
@@ -278,41 +351,58 @@
function createLoadingState() { function createLoadingState() {
const loadingDiv = document.createElement('div'); const loadingDiv = document.createElement('div');
loadingDiv.className = 'loading-state'; loadingDiv.className = 'loading-state';
loadingDiv.innerHTML = `
<div class="loading-content"> const loadingContent = document.createElement('div');
<div class="loading-spinner"></div> loadingContent.className = 'loading-content';
<p>${LanguageManager.t('messages.loading', 'Üzenetek betöltése...')}</p> const spinner = document.createElement('div');
</div> spinner.className = 'loading-spinner';
`; const text = document.createElement('p');
text.textContent = LanguageManager.t('messages.loading', 'Üzenetek betöltése...');
loadingContent.appendChild(spinner);
loadingContent.appendChild(text);
loadingDiv.appendChild(loadingContent);
return loadingDiv; return loadingDiv;
} }
function createErrorState(onRetry) { function createErrorState(onRetry) {
const errorDiv = document.createElement('div'); const errorDiv = document.createElement('div');
errorDiv.className = 'error-state'; errorDiv.className = 'error-state';
errorDiv.innerHTML = `
<div class="error-content">
<h3>${LanguageManager.t('messages.error.title', 'Hiba történt')}</h3>
<p>${LanguageManager.t('messages.error.description', 'Az üzenetek betöltése sikertelen volt.')}</p>
<button class="retry-btn">${LanguageManager.t('messages.error.retry', 'Újrapróbálás')}</button>
</div>
`;
const retryBtn = errorDiv.querySelector('.retry-btn'); const errorContent = document.createElement('div');
errorContent.className = 'error-content';
const title = document.createElement('h3');
title.textContent = LanguageManager.t('messages.error.title', 'Hiba történt');
const desc = document.createElement('p');
desc.textContent = LanguageManager.t('messages.error.description', 'Az üzenetek betöltése sikertelen volt.');
const retryBtn = document.createElement('button');
retryBtn.className = 'retry-btn';
retryBtn.textContent = LanguageManager.t('messages.error.retry', 'Újrapróbálás');
retryBtn.addEventListener('click', onRetry); retryBtn.addEventListener('click', onRetry);
errorContent.appendChild(title);
errorContent.appendChild(desc);
errorContent.appendChild(retryBtn);
errorDiv.appendChild(errorContent);
return errorDiv; return errorDiv;
} }
function createEmptyState() { function createEmptyState() {
const emptyDiv = document.createElement('div'); const emptyDiv = document.createElement('div');
emptyDiv.className = 'empty-state'; emptyDiv.className = 'empty-state';
emptyDiv.innerHTML = `
<div class="empty-content"> const emptyContent = document.createElement('div');
<h3>${LanguageManager.t('messages.empty.title', 'Nincsenek üzenetek')}</h3> emptyContent.className = 'empty-content';
<p>${LanguageManager.t('messages.empty.description', 'Jelenleg nincsenek elérhető üzenetek.')}</p> const title = document.createElement('h3');
</div> title.textContent = LanguageManager.t('messages.empty.title', 'Nincsenek üzenetek');
`; const desc = document.createElement('p');
desc.textContent = LanguageManager.t('messages.empty.description', 'Jelenleg nincsenek elérhető üzenetek.');
emptyContent.appendChild(title);
emptyContent.appendChild(desc);
emptyDiv.appendChild(emptyContent);
return emptyDiv; return emptyDiv;
} }
@@ -332,18 +422,42 @@
const subject = message.uzenetTargy || 'Nincs tárgy'; const subject = message.uzenetTargy || 'Nincs tárgy';
const date = formatDate(message.uzenetKuldesDatum); const date = formatDate(message.uzenetKuldesDatum);
const hasAttachment = message.hasCsatolmany; const hasAttachment = message.hasCsatolmany;
const cardHeader = document.createElement('div');
cardHeader.className = 'message-card-header';
card.innerHTML = ` const senderInfo = document.createElement('div');
<div class="message-card-header"> senderInfo.className = 'sender-info';
<div class="sender-info"> const senderNameSpan = document.createElement('span');
<span class="sender-name">${sanitizeHTML(senderName)}</span> senderNameSpan.className = 'sender-name';
${!message.isElolvasva ? '<span class="unread-indicator"></span>' : ''} senderNameSpan.textContent = sanitizeHTML(senderName);
</div> senderInfo.appendChild(senderNameSpan);
<div class="message-date">${date}</div>
</div> if (!message.isElolvasva) {
<div class="message-subject">${sanitizeHTML(subject)}</div> const unreadIndicator = document.createElement('span');
${hasAttachment ? '<div class="attachment-indicator">📎</div>' : ''} unreadIndicator.className = 'unread-indicator';
`; senderInfo.appendChild(unreadIndicator);
}
const messageDate = document.createElement('div');
messageDate.className = 'message-date';
messageDate.textContent = date;
cardHeader.appendChild(senderInfo);
cardHeader.appendChild(messageDate);
card.appendChild(cardHeader);
const messageSubject = document.createElement('div');
messageSubject.className = 'message-subject';
messageSubject.textContent = sanitizeHTML(subject);
card.appendChild(messageSubject);
if (hasAttachment) {
const attachmentIndicator = document.createElement('div');
attachmentIndicator.className = 'attachment-indicator';
attachmentIndicator.textContent = '📎';
card.appendChild(attachmentIndicator);
}
return card; return card;
@@ -397,41 +511,111 @@
function renderBulkActions(container) { function renderBulkActions(container) {
const bulk = document.createElement('div'); const bulk = document.createElement('div');
bulk.className = 'bulk-actions-card'; bulk.className = 'bulk-actions-card';
bulk.innerHTML = `
<div class="bulk-actions-left"> const bulkActionsLeft = document.createElement('div');
<div class="view-toggle"> bulkActionsLeft.className = 'bulk-actions-left';
<button id="viewInboxBtn" class="${currentView==='inbox'?'active':''}" title="Beérkezett">
<img src="${chrome.runtime.getURL('icons/messages-active.svg')}" alt="Beérkezett"> const viewToggle = document.createElement('div');
</button> viewToggle.className = 'view-toggle';
<button id="viewTrashBtn" class="${currentView==='trash'?'active':''}" title="Törölt">
<img src="${chrome.runtime.getURL('icons/delete.svg')}" alt="Törölt"> const viewInboxBtn = document.createElement('button');
</button> viewInboxBtn.id = 'viewInboxBtn';
</div> viewInboxBtn.className = currentView === 'inbox' ? 'active' : '';
<button id="toggleSelectionModeBtn" class="bulk-btn" title="Kijelölés mód"> viewInboxBtn.title = 'Beérkezett';
<img src="${chrome.runtime.getURL('icons/select.svg')}" alt="Kijelölés mód"> const inboxImg = document.createElement('img');
</button> inboxImg.src = chrome.runtime.getURL('icons/messages-active.svg');
<button id="selectAllBtn" class="bulk-btn" title="Mind kijelöl"> inboxImg.alt = 'Beérkezett';
<img src="${chrome.runtime.getURL('icons/select-all.svg')}" alt="Mind kijelöl"> viewInboxBtn.appendChild(inboxImg);
</button>
<button id="clearSelectionBtn" class="bulk-btn" title="Kijelölés törlése"> const viewTrashBtn = document.createElement('button');
<img src="${chrome.runtime.getURL('icons/select-none.svg')}" alt="Kijelölés törlése"> viewTrashBtn.id = 'viewTrashBtn';
</button> viewTrashBtn.className = currentView === 'trash' ? 'active' : '';
</div> viewTrashBtn.title = 'Törölt';
<div class="bulk-actions-right"> const trashImg = document.createElement('img');
<button id="markReadBtn" class="bulk-btn" title="Olvasott"> trashImg.src = chrome.runtime.getURL('icons/delete.svg');
<img src="${chrome.runtime.getURL('icons/eye-on.svg')}" alt="Olvasott"> trashImg.alt = 'Törölt';
</button> viewTrashBtn.appendChild(trashImg);
<button id="markUnreadBtn" class="bulk-btn" title="Olvasatlan">
<img src="${chrome.runtime.getURL('icons/eye-off.svg')}" alt="Olvasatlan"> viewToggle.appendChild(viewInboxBtn);
</button> viewToggle.appendChild(viewTrashBtn);
<button id="deleteBtn" class="bulk-btn" title="Törlés"> bulkActionsLeft.appendChild(viewToggle);
<img src="${chrome.runtime.getURL('icons/trash.svg')}" alt="Törlés">
</button> const toggleSelBtn = document.createElement('button');
<button id="restoreBtn" class="bulk-btn" title="Visszaállítás"> toggleSelBtn.id = 'toggleSelectionModeBtn';
<img src="${chrome.runtime.getURL('icons/undo.svg')}" alt="Visszaállítás"> toggleSelBtn.className = 'bulk-btn';
</button> toggleSelBtn.title = 'Kijelölés mód';
</div> const selectImg = document.createElement('img');
`; selectImg.src = chrome.runtime.getURL('icons/select.svg');
selectImg.alt = 'Kijelölés mód';
toggleSelBtn.appendChild(selectImg);
bulkActionsLeft.appendChild(toggleSelBtn);
const selectAllBtn = document.createElement('button');
selectAllBtn.id = 'selectAllBtn';
selectAllBtn.className = 'bulk-btn';
selectAllBtn.title = 'Mind kijelöl';
const selectAllImg = document.createElement('img');
selectAllImg.src = chrome.runtime.getURL('icons/select-all.svg');
selectAllImg.alt = 'Mind kijelöl';
selectAllBtn.appendChild(selectAllImg);
bulkActionsLeft.appendChild(selectAllBtn);
const clearSelBtn = document.createElement('button');
clearSelBtn.id = 'clearSelectionBtn';
clearSelBtn.className = 'bulk-btn';
clearSelBtn.title = 'Kijelölés törlése';
const clearSelImg = document.createElement('img');
clearSelImg.src = chrome.runtime.getURL('icons/select-none.svg');
clearSelImg.alt = 'Kijelölés törlése';
clearSelBtn.appendChild(clearSelImg);
bulkActionsLeft.appendChild(clearSelBtn);
bulk.appendChild(bulkActionsLeft);
const bulkActionsRight = document.createElement('div');
bulkActionsRight.className = 'bulk-actions-right';
const markReadBtn = document.createElement('button');
markReadBtn.id = 'markReadBtn';
markReadBtn.className = 'bulk-btn';
markReadBtn.title = 'Olvasott';
const markReadImg = document.createElement('img');
markReadImg.src = chrome.runtime.getURL('icons/eye-on.svg');
markReadImg.alt = 'Olvasott';
markReadBtn.appendChild(markReadImg);
bulkActionsRight.appendChild(markReadBtn);
const markUnreadBtn = document.createElement('button');
markUnreadBtn.id = 'markUnreadBtn';
markUnreadBtn.className = 'bulk-btn';
markUnreadBtn.title = 'Olvasatlan';
const markUnreadImg = document.createElement('img');
markUnreadImg.src = chrome.runtime.getURL('icons/eye-off.svg');
markUnreadImg.alt = 'Olvasatlan';
markUnreadBtn.appendChild(markUnreadImg);
bulkActionsRight.appendChild(markUnreadBtn);
const deleteBtn = document.createElement('button');
deleteBtn.id = 'deleteBtn';
deleteBtn.className = 'bulk-btn';
deleteBtn.title = 'Törlés';
const deleteImg = document.createElement('img');
deleteImg.src = chrome.runtime.getURL('icons/trash.svg');
deleteImg.alt = 'Törlés';
deleteBtn.appendChild(deleteImg);
bulkActionsRight.appendChild(deleteBtn);
const restoreBtn = document.createElement('button');
restoreBtn.id = 'restoreBtn';
restoreBtn.className = 'bulk-btn';
restoreBtn.title = 'Visszaállítás';
const restoreImg = document.createElement('img');
restoreImg.src = chrome.runtime.getURL('icons/undo.svg');
restoreImg.alt = 'Visszaállítás';
restoreBtn.appendChild(restoreImg);
bulkActionsRight.appendChild(restoreBtn);
bulk.appendChild(bulkActionsRight);
container.appendChild(bulk); container.appendChild(bulk);
bulk.querySelector('#viewInboxBtn').addEventListener('click', () => switchView('inbox')); bulk.querySelector('#viewInboxBtn').addEventListener('click', () => switchView('inbox'));
@@ -568,13 +752,13 @@ async function switchView(view) {
async function transformMessagesPage() { async function transformMessagesPage() {
try { try {
await waitForTranslations(); await waitForTranslations();
document.body.innerHTML = ''; helper.clearElement(document.body);
const kretaContainer = document.createElement('div'); const kretaContainer = document.createElement('div');
kretaContainer.className = 'kreta-container'; kretaContainer.className = 'kreta-container';
const headerDiv = document.createElement('div'); const headerDiv = document.createElement('div');
const parser = new DOMParser(); const template = document.createElement('template');
const headerDoc = parser.parseFromString(await createTemplate.header(), 'text/html'); template.innerHTML = await createTemplate.header();
const headerContent = headerDoc.body; const headerContent = template.content;
while (headerContent.firstChild) { while (headerContent.firstChild) {
headerDiv.appendChild(headerContent.firstChild); headerDiv.appendChild(headerContent.firstChild);
} }

View File

@@ -30,7 +30,7 @@
const backButton = document.createElement('button'); const backButton = document.createElement('button');
backButton.id = 'firka-back-button'; backButton.id = 'firka-back-button';
backButton.innerHTML = '← Vissza'; backButton.textContent = '← Vissza';
backButton.style.cssText = ` backButton.style.cssText = `
position: static; position: static;
margin: 20px; margin: 20px;

View File

@@ -154,15 +154,15 @@
if (userName) { if (userName) {
await storageManager.set("userName", userName); await storageManager.set("userName", userName);
} }
document.body.innerHTML = ''; helper.clearElement(document.body);
const parser = new DOMParser(); const template = document.createElement('template');
const doc = parser.parseFromString(createHTML( template.innerHTML = createHTML(
schoolCode, schoolCode,
fullSchoolName, fullSchoolName,
userName, userName,
settings, settings,
), 'text/html'); );
const tempDiv = doc.body; const tempDiv = template.content;
while (tempDiv.firstChild) { while (tempDiv.firstChild) {
document.body.appendChild(tempDiv.firstChild); document.body.appendChild(tempDiv.firstChild);
} }

View File

@@ -242,19 +242,30 @@ document.addEventListener("DOMContentLoaded", async () => {
const settings = pageSettings[pageType] || []; const settings = pageSettings[pageType] || [];
if (settings.length === 0) { if (settings.length === 0) {
container.innerHTML = ` helper.clearElement(container);
<div class="no-settings-placeholder">
<span class="material-icons-round">settings_suggest</span> const placeholder = document.createElement('div');
<p data-i18n="settings.page_settings.no_settings">Ehhez az oldalhoz nincsenek egyéni beállítások.</p> placeholder.className = 'no-settings-placeholder';
</div> const icon = document.createElement('span');
`; icon.className = 'material-icons-round';
icon.textContent = 'settings_suggest';
const text = document.createElement('p');
text.setAttribute('data-i18n', 'settings.page_settings.no_settings');
text.textContent = 'Ehhez az oldalhoz nincsenek egyéni beállítások.';
placeholder.appendChild(icon);
placeholder.appendChild(text);
container.appendChild(placeholder);
if (window.LanguageManager) { if (window.LanguageManager) {
window.LanguageManager.loadTranslationsForPage(); window.LanguageManager.loadTranslationsForPage();
} }
return; return;
} }
container.innerHTML = settings.map(setting => renderSettingItem(setting)).join(""); const template = document.createElement('template');
template.innerHTML = settings.map(setting => renderSettingItem(setting)).join("");
helper.clearElement(container);
container.appendChild(template.content);
initSettingItems(container); initSettingItems(container);
if (window.LanguageManager) { if (window.LanguageManager) {
@@ -362,14 +373,20 @@ document.addEventListener("DOMContentLoaded", async () => {
if (!grid) return; if (!grid) return;
if (customThemes.length === 0) { if (customThemes.length === 0) {
grid.innerHTML = `<div class="no-custom-themes" data-i18n="settings.custom_themes.no_themes">Még nincsenek egyéni témák</div>`; helper.clearElement(grid);
const noThemes = document.createElement('div');
noThemes.className = 'no-custom-themes';
noThemes.setAttribute('data-i18n', 'settings.custom_themes.no_themes');
noThemes.textContent = 'Még nincsenek egyéni témák';
grid.appendChild(noThemes);
if (window.LanguageManager) { if (window.LanguageManager) {
window.LanguageManager.loadTranslationsForPage(); window.LanguageManager.loadTranslationsForPage();
} }
return; return;
} }
grid.innerHTML = customThemes.map(theme => ` const template = document.createElement('template');
template.innerHTML = customThemes.map(theme => `
<button class="theme-option custom-theme-option" data-theme="custom-${theme.id}"> <button class="theme-option custom-theme-option" data-theme="custom-${theme.id}">
<div class="theme-preview" style="background: ${theme.colors.background};"> <div class="theme-preview" style="background: ${theme.colors.background};">
<div class="preview-header" style="background: ${theme.colors.card};"></div> <div class="preview-header" style="background: ${theme.colors.card};"></div>
@@ -393,6 +410,9 @@ document.addEventListener("DOMContentLoaded", async () => {
</div> </div>
</button> </button>
`).join(""); `).join("");
helper.clearElement(grid);
grid.appendChild(template.content);
grid.querySelectorAll(".custom-theme-option").forEach(btn => { grid.querySelectorAll(".custom-theme-option").forEach(btn => {
btn.addEventListener("click", (e) => { btn.addEventListener("click", (e) => {

View File

@@ -197,8 +197,9 @@
} }
const htmlText = await response.text(); const htmlText = await response.text();
const parser = new DOMParser(); const template = document.createElement('template');
const doc = parser.parseFromString(htmlText, 'text/html'); template.innerHTML = htmlText;
const doc = template.content;
const panelBody = doc.querySelector('.panel-body'); const panelBody = doc.querySelector('.panel-body');
const panelFooter = doc.querySelector('.panel-footer'); const panelFooter = doc.querySelector('.panel-footer');
const teacherInfo = doc.querySelector('.panel-heading'); const teacherInfo = doc.querySelector('.panel-heading');
@@ -887,18 +888,27 @@
detailsDiv.className = 'homework-details'; detailsDiv.className = 'homework-details';
const contentP = document.createElement('p'); const contentP = document.createElement('p');
contentP.innerHTML = `<strong>Feladat:</strong> ${homeworkDetails.content}`; const contentStrong = document.createElement('strong');
contentStrong.textContent = 'Feladat: ';
contentP.appendChild(contentStrong);
contentP.appendChild(document.createTextNode(homeworkDetails.content));
detailsDiv.appendChild(contentP); detailsDiv.appendChild(contentP);
if (homeworkDetails.deadline) { if (homeworkDetails.deadline) {
const deadlineP = document.createElement('p'); const deadlineP = document.createElement('p');
deadlineP.innerHTML = `<strong>Határidő:</strong> ${homeworkDetails.deadline}`; const deadlineStrong = document.createElement('strong');
deadlineStrong.textContent = 'Határidő: ';
deadlineP.appendChild(deadlineStrong);
deadlineP.appendChild(document.createTextNode(homeworkDetails.deadline));
detailsDiv.appendChild(deadlineP); detailsDiv.appendChild(deadlineP);
} }
if (homeworkDetails.teacher) { if (homeworkDetails.teacher) {
const teacherP = document.createElement('p'); const teacherP = document.createElement('p');
teacherP.innerHTML = `<strong>Tanár:</strong> ${homeworkDetails.teacher}`; const teacherStrong = document.createElement('strong');
teacherStrong.textContent = 'Tanár: ';
teacherP.appendChild(teacherStrong);
teacherP.appendChild(document.createTextNode(homeworkDetails.teacher));
detailsDiv.appendChild(teacherP); detailsDiv.appendChild(teacherP);
} }
@@ -908,7 +918,9 @@
attachmentsDiv.style.marginTop = '1rem'; attachmentsDiv.style.marginTop = '1rem';
const attachmentsTitle = document.createElement('p'); const attachmentsTitle = document.createElement('p');
attachmentsTitle.innerHTML = '<strong>Csatolmányok:</strong>'; const attachStrong = document.createElement('strong');
attachStrong.textContent = 'Csatolmányok:';
attachmentsTitle.appendChild(attachStrong);
attachmentsTitle.style.marginBottom = '0.5rem'; attachmentsTitle.style.marginBottom = '0.5rem';
attachmentsDiv.appendChild(attachmentsTitle); attachmentsDiv.appendChild(attachmentsTitle);
@@ -1213,15 +1225,24 @@
detailsDiv.className = 'test-details'; detailsDiv.className = 'test-details';
const nameP = document.createElement('p'); const nameP = document.createElement('p');
nameP.innerHTML = `<strong>Megnevezés:</strong> ${testDetails.name}`; const nameStrong = document.createElement('strong');
nameStrong.textContent = 'Megnevezés: ';
nameP.appendChild(nameStrong);
nameP.appendChild(document.createTextNode(testDetails.name));
detailsDiv.appendChild(nameP); detailsDiv.appendChild(nameP);
const typeP = document.createElement('p'); const typeP = document.createElement('p');
typeP.innerHTML = `<strong>Típus:</strong> ${testDetails.type}`; const typeStrong = document.createElement('strong');
typeStrong.textContent = 'Típus: ';
typeP.appendChild(typeStrong);
typeP.appendChild(document.createTextNode(testDetails.type));
detailsDiv.appendChild(typeP); detailsDiv.appendChild(typeP);
const dateP = document.createElement('p'); const dateP = document.createElement('p');
dateP.innerHTML = `<strong>Bejelentés dátuma:</strong> ${testDetails.announceDate}`; const dateStrong = document.createElement('strong');
dateStrong.textContent = 'Bejelentés dátuma: ';
dateP.appendChild(dateStrong);
dateP.appendChild(document.createTextNode(testDetails.announceDate));
detailsDiv.appendChild(dateP); detailsDiv.appendChild(dateP);
testContent.appendChild(detailsDiv); testContent.appendChild(detailsDiv);
@@ -1632,7 +1653,7 @@
modalContent.appendChild(header); modalContent.appendChild(header);
modalContent.appendChild(body); modalContent.appendChild(body);
modal.innerHTML = ''; helper.clearElement(modal);
modal.appendChild(modalContent); modal.appendChild(modalContent);
document.body.appendChild(modal); document.body.appendChild(modal);
@@ -1703,11 +1724,11 @@
if (timetableGrid) { if (timetableGrid) {
const newContent = await generateTimeGrid(allLessons, weekDates); const newContent = await generateTimeGrid(allLessons, weekDates);
timetableGrid.innerHTML = ''; helper.clearElement(timetableGrid);
const parser1 = new DOMParser(); const template = document.createElement('template');
const doc = parser1.parseFromString(`<div>${newContent}</div>`, 'text/html'); template.innerHTML = `<div>${newContent}</div>`;
const tempDiv = doc.querySelector('div'); const tempDiv = template.content.querySelector('div');
while (tempDiv.firstChild) { while (tempDiv.firstChild) {
timetableGrid.appendChild(tempDiv.firstChild); timetableGrid.appendChild(tempDiv.firstChild);
} }
@@ -2186,7 +2207,7 @@
} }
modalGrid.innerHTML = ''; helper.clearElement(modalGrid);
allWeeks.forEach((week) => { allWeeks.forEach((week) => {
const weekCell = document.createElement('div'); const weekCell = document.createElement('div');
weekCell.className = `week-cell ${week.selected ? 'selected' : ''} ${week.selected ? 'current-week' : ''}`; weekCell.className = `week-cell ${week.selected ? 'selected' : ''} ${week.selected ? 'current-week' : ''}`;
@@ -2274,7 +2295,7 @@
const weekGrid = document.getElementById("week-grid"); const weekGrid = document.getElementById("week-grid");
if (weekGrid) { if (weekGrid) {
weekGrid.innerHTML = ''; helper.clearElement(weekGrid);
newWeekOptions.forEach((opt) => { newWeekOptions.forEach((opt) => {
const weekCell = document.createElement('div'); const weekCell = document.createElement('div');
weekCell.className = `week-cell ${opt.selected ? 'selected' : ''}`; weekCell.className = `week-cell ${opt.selected ? 'selected' : ''}`;
@@ -2321,7 +2342,7 @@
}; };
document.body.innerHTML = ''; helper.clearElement(document.body);
const kretaContainer = document.createElement('div'); const kretaContainer = document.createElement('div');
@@ -2330,9 +2351,9 @@
const headerDiv = document.createElement('div'); const headerDiv = document.createElement('div');
const parser2 = new DOMParser(); const template = document.createElement('template');
const headerDoc = parser2.parseFromString(await createTemplate.header(), 'text/html'); template.innerHTML = await createTemplate.header();
const headerContent = headerDoc.body; const headerContent = template.content;
while (headerContent.firstChild) { while (headerContent.firstChild) {
headerDiv.appendChild(headerContent.firstChild); headerDiv.appendChild(headerContent.firstChild);
} }
@@ -2480,9 +2501,9 @@
const gridContent = await generateTimeGrid(data.lessons, data.weekDates); const gridContent = await generateTimeGrid(data.lessons, data.weekDates);
const parser3 = new DOMParser(); const template3 = document.createElement('template');
const doc = parser3.parseFromString(`<div>${gridContent}</div>`, 'text/html'); template3.innerHTML = `<div>${gridContent}</div>`;
const tempDiv = doc.querySelector('div'); const tempDiv = template3.content.querySelector('div');
while (tempDiv.firstChild) { while (tempDiv.firstChild) {
timetableGrid.appendChild(tempDiv.firstChild); timetableGrid.appendChild(tempDiv.firstChild);
} }
@@ -2541,7 +2562,7 @@
} }
weekDisplay.innerHTML = ''; helper.clearElement(weekDisplay);
visibleWeeks.forEach((weekNum, index) => { visibleWeeks.forEach((weekNum, index) => {
const isSelected = index === 2; const isSelected = index === 2;
const isCurrent = weekNum === currentWeekNumber; const isCurrent = weekNum === currentWeekNumber;
@@ -2637,12 +2658,12 @@
const timetableContainer = document.querySelector(".timetable-grid"); const timetableContainer = document.querySelector(".timetable-grid");
if (timetableContainer) { if (timetableContainer) {
timetableContainer.innerHTML = ''; helper.clearElement(timetableContainer);
const gridContent = await generateTimeGrid(lessons, weekDates); const gridContent = await generateTimeGrid(lessons, weekDates);
const parser2 = new DOMParser(); const template2 = document.createElement('template');
const doc = parser2.parseFromString(`<div>${gridContent}</div>`, 'text/html'); template2.innerHTML = `<div>${gridContent}</div>`;
const tempDiv = doc.querySelector('div'); const tempDiv = template2.content.querySelector('div');
while (tempDiv.firstChild) { while (tempDiv.firstChild) {
timetableContainer.appendChild(tempDiv.firstChild); timetableContainer.appendChild(tempDiv.firstChild);
} }
@@ -2670,7 +2691,7 @@
} }
modalGrid.innerHTML = ''; helper.clearElement(modalGrid);
allWeeks.forEach((weekNumber) => { allWeeks.forEach((weekNumber) => {
const isSelected = weekNumber === selectedWeekNumber; const isSelected = weekNumber === selectedWeekNumber;
const isCurrent = weekNumber === currentWeekNumber; const isCurrent = weekNumber === currentWeekNumber;

View File

@@ -37,4 +37,28 @@ const helper = {
const [hours, minutes] = timeStr.split(":").map(Number); const [hours, minutes] = timeStr.split(":").map(Number);
return hours * 60 + minutes; return hours * 60 + minutes;
}, },
createElementFromHTML(htmlString) {
const template = document.createElement('template');
template.innerHTML = htmlString.trim();
return template.content;
},
setTextContent(element, text) {
element.textContent = text;
},
clearElement(element) {
while (element.firstChild) {
element.removeChild(element.firstChild);
}
},
appendChildren(parent, children) {
if (Array.isArray(children)) {
children.forEach(child => parent.appendChild(child));
} else {
parent.appendChild(children);
}
}
}; };