style: apply linter suggestions
This commit is contained in:
@@ -19,7 +19,7 @@ function getCsrfToken() {
|
||||
return match ? match.split('=')[1] : '';
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
// biome-ignore lint/correctness/noUnusedVariables: global function
|
||||
function getCsrfHeaders() {
|
||||
const token = getCsrfToken();
|
||||
return token ? { 'X-CSRF-Token': token } : {};
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
function escapeHtml(str) {
|
||||
if (typeof str !== 'string') return '';
|
||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"').replace(/'/g, ''');
|
||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function formatFileSize(bytes) {
|
||||
@@ -15,7 +15,7 @@ function formatFileSize(bytes) {
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||
return `${parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;
|
||||
}
|
||||
|
||||
/// Formats a byte count for quota display. When bytes is 0, returns "∞" (unlimited).
|
||||
@@ -34,14 +34,14 @@ function formatDateTime(value) {
|
||||
} else {
|
||||
dateValue = new Date(value);
|
||||
}
|
||||
if (isNaN(dateValue.getTime())) return String(value);
|
||||
return dateValue.toLocaleDateString() + ' ' + dateValue.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
||||
if (Number.isNaN(dateValue.getTime())) return String(value);
|
||||
return `${dateValue.toLocaleDateString()} ${dateValue.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`;
|
||||
}
|
||||
|
||||
function formatDateShort(value) {
|
||||
if (!value) return 'N/A';
|
||||
const dateValue = typeof value === 'number' ? new Date(value * 1000) : new Date(value);
|
||||
if (isNaN(dateValue.getTime())) return String(value);
|
||||
if (Number.isNaN(dateValue.getTime())) return String(value);
|
||||
return dateValue.toLocaleDateString(undefined, {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
// Current locale code (default to browser locale if available, fallback to English)
|
||||
let currentLocale = (navigator.language && navigator.language.substring(0, 2)) || (navigator.userLanguage && navigator.userLanguage.substring(0, 2)) || 'en';
|
||||
let currentLocale = navigator.language?.substring(0, 2) || navigator.userLanguage?.substring(0, 2) || 'en';
|
||||
|
||||
// Supported locales (languages that have locale files on the server)
|
||||
// When a locale file is not found, the system gracefully falls back to English
|
||||
@@ -93,6 +93,8 @@ function getNestedValue(obj, path) {
|
||||
* @param {object} params - Parameters to replace in the translation (e.g., {name: 'John'})
|
||||
* @returns {string} - The translated string or the key itself if not found
|
||||
*/
|
||||
|
||||
// biome-ignore lint/correctness/noUnusedVariables: global function
|
||||
function t(key, params = {}) {
|
||||
// Get translation from cache
|
||||
const localeData = translations[currentLocale];
|
||||
@@ -131,8 +133,8 @@ function t(key, params = {}) {
|
||||
|
||||
if (!value) {
|
||||
// Try fallback to English
|
||||
if (currentLocale !== 'en' && translations['en']) {
|
||||
let fallbackValue = getNestedValue(translations['en'], key);
|
||||
if (currentLocale !== 'en' && translations.en) {
|
||||
let fallbackValue = getNestedValue(translations.en, key);
|
||||
|
||||
if (!fallbackValue) {
|
||||
const aliasMap = {
|
||||
@@ -143,7 +145,7 @@ function t(key, params = {}) {
|
||||
};
|
||||
const aliasKey = aliasMap[key];
|
||||
if (aliasKey) {
|
||||
fallbackValue = getNestedValue(translations['en'], aliasKey);
|
||||
fallbackValue = getNestedValue(translations.en, aliasKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,8 +306,8 @@ function safeT(key, params = {}) {
|
||||
let value = getNestedValue(localeData, key);
|
||||
|
||||
// Fallback to English
|
||||
if (!value && currentLocale !== 'en' && translations['en']) {
|
||||
value = getNestedValue(translations['en'], key);
|
||||
if (!value && currentLocale !== 'en' && translations.en) {
|
||||
value = getNestedValue(translations.en, key);
|
||||
}
|
||||
|
||||
if (!value) return key;
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
* https://fontawesome.com/license/free
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// ── Icon registry ──────────────────────────────────────────────
|
||||
// Each entry: [viewBox-width, svg-path-d]
|
||||
// All icons use viewBox="0 0 {width} 512" and fill="currentColor".
|
||||
@@ -379,7 +377,7 @@ function oxiIcon(name, extraClass) {
|
||||
const entry = _ICONS[name];
|
||||
if (!entry) return '';
|
||||
const [w, d] = entry;
|
||||
const cls = extraClass ? 'oxi-icon ' + extraClass : 'oxi-icon';
|
||||
const cls = extraClass ? `oxi-icon ${extraClass}` : 'oxi-icon';
|
||||
return `<svg class="${cls}" viewBox="0 0 ${w} 512" aria-hidden="true"><path fill="currentColor" d="${d}"/></svg>`;
|
||||
}
|
||||
|
||||
@@ -423,8 +421,8 @@ function replaceIconsInElement(container) {
|
||||
}
|
||||
|
||||
// Use outline variant if available and element uses "far"
|
||||
if (isRegular && _ICONS[iconName + '-outline']) {
|
||||
iconName = iconName + '-outline';
|
||||
if (isRegular && _ICONS[`${iconName}-outline`]) {
|
||||
iconName = `${iconName}-outline`;
|
||||
}
|
||||
|
||||
if (!iconName || !_ICONS[iconName]) continue;
|
||||
@@ -433,12 +431,12 @@ function replaceIconsInElement(container) {
|
||||
|
||||
// Build SVG element
|
||||
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
svg.setAttribute('viewBox', '0 0 ' + w + ' 512');
|
||||
svg.setAttribute('viewBox', `0 0 ${w} 512`);
|
||||
svg.setAttribute('aria-hidden', 'true');
|
||||
|
||||
let svgClass = 'oxi-icon';
|
||||
if (isSpin) svgClass += ' oxi-icon-spin';
|
||||
if (extraClasses.length) svgClass += ' ' + extraClasses.join(' ');
|
||||
if (extraClasses.length) svgClass += ` ${extraClasses.join(' ')}`;
|
||||
svg.setAttribute('class', svgClass);
|
||||
|
||||
// Copy inline style if present
|
||||
@@ -479,7 +477,7 @@ window.OxiIcons = _ICONS;
|
||||
}
|
||||
|
||||
// Observe future mutations (dynamic renders, modals, etc.)
|
||||
new MutationObserver(function (mutations) {
|
||||
new MutationObserver((mutations) => {
|
||||
if (raf) return;
|
||||
for (let i = 0; i < mutations.length; i++) {
|
||||
if (mutations[i].addedNodes.length) {
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
*/
|
||||
|
||||
const notifications = (() => {
|
||||
'use strict';
|
||||
|
||||
/* ── state ──────────────────────────────────────────────── */
|
||||
let _badgeCount = 0;
|
||||
let _batchSeq = 0;
|
||||
@@ -126,7 +124,7 @@ const notifications = (() => {
|
||||
|
||||
// If panel is closed, bump badge
|
||||
const wrapper = $('notif-wrapper');
|
||||
if (!wrapper || !wrapper.classList.contains('open')) {
|
||||
if (!wrapper?.classList.contains('open')) {
|
||||
_incrementBadge();
|
||||
}
|
||||
_showEmptyIfNeeded();
|
||||
@@ -141,7 +139,7 @@ const notifications = (() => {
|
||||
* @param {string} [folderName] root folder name (for folder uploads)
|
||||
*/
|
||||
function addUploadBatch(totalFiles, folderName) {
|
||||
const batchId = 'batch-' + ++_batchSeq;
|
||||
const batchId = `batch-${++_batchSeq}`;
|
||||
const body = $('notif-panel-body');
|
||||
if (!body) return batchId;
|
||||
|
||||
@@ -217,9 +215,9 @@ const notifications = (() => {
|
||||
if (!shouldUpdate) return;
|
||||
|
||||
// Show just the file name being uploaded (truncate long paths)
|
||||
const curEl = $(batchId + '-current');
|
||||
const curEl = $(`${batchId}-current`);
|
||||
if (curEl) {
|
||||
const shortName = fileName.length > 50 ? '…' + fileName.slice(-49) : fileName;
|
||||
const shortName = fileName.length > 50 ? `…${fileName.slice(-49)}` : fileName;
|
||||
curEl.textContent = shortName;
|
||||
}
|
||||
batch.lastLabelFile = fileName;
|
||||
@@ -228,10 +226,10 @@ const notifications = (() => {
|
||||
// Update progress bar with per-file granularity:
|
||||
// overall% = (completed_files + current_file_fraction) / total_files
|
||||
const overallPct = Math.round(((batch.completed + pct / 100) / batch.totalFiles) * 100);
|
||||
const fillEl = $(batchId + '-fill');
|
||||
const pctEl = $(batchId + '-pct');
|
||||
if (fillEl) fillEl.style.width = overallPct + '%';
|
||||
if (pctEl) pctEl.textContent = overallPct + '%';
|
||||
const fillEl = $(`${batchId}-fill`);
|
||||
const pctEl = $(`${batchId}-pct`);
|
||||
if (fillEl) fillEl.style.width = `${overallPct}%`;
|
||||
if (pctEl) pctEl.textContent = `${overallPct}%`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,15 +248,15 @@ const notifications = (() => {
|
||||
if (!isLast && batch.completed % 5 !== 0) return;
|
||||
|
||||
const pctVal = Math.round((batch.completed / batch.totalFiles) * 100);
|
||||
const fillEl = $(batchId + '-fill');
|
||||
const pctEl = $(batchId + '-pct');
|
||||
const statsEl = $(batchId + '-stats');
|
||||
const fillEl = $(`${batchId}-fill`);
|
||||
const pctEl = $(`${batchId}-pct`);
|
||||
const statsEl = $(`${batchId}-stats`);
|
||||
|
||||
const t = window.i18n?.t || ((k) => k);
|
||||
const filesLabel = t('upload.files');
|
||||
|
||||
if (fillEl) fillEl.style.width = pctVal + '%';
|
||||
if (pctEl) pctEl.textContent = pctVal + '%';
|
||||
if (fillEl) fillEl.style.width = `${pctVal}%`;
|
||||
if (pctEl) pctEl.textContent = `${pctVal}%`;
|
||||
if (statsEl) statsEl.textContent = `${batch.completed} / ${batch.totalFiles} ${filesLabel}`;
|
||||
}
|
||||
|
||||
@@ -269,7 +267,7 @@ const notifications = (() => {
|
||||
const batch = _batches[batchId];
|
||||
if (!batch) return;
|
||||
|
||||
const fillEl = $(batchId + '-fill');
|
||||
const fillEl = $(`${batchId}-fill`);
|
||||
if (fillEl) {
|
||||
fillEl.style.width = '100%';
|
||||
fillEl.classList.add(successCount === totalFiles ? 'done' : 'error');
|
||||
@@ -279,11 +277,10 @@ const notifications = (() => {
|
||||
const iconEl = batch.el.querySelector('.notif-item-icon');
|
||||
|
||||
// Clear the current-file label
|
||||
const curEl = $(batchId + '-current');
|
||||
const curEl = $(`${batchId}-current`);
|
||||
if (curEl) curEl.textContent = '';
|
||||
|
||||
const t = window.i18n?.t || ((k) => k);
|
||||
const filesLabel = t('upload.files');
|
||||
const completeText = t('upload.complete', {
|
||||
count: successCount,
|
||||
total: totalFiles
|
||||
@@ -302,7 +299,7 @@ const notifications = (() => {
|
||||
|
||||
// If the panel is closed, bump badge
|
||||
const wrapper = $('notif-wrapper');
|
||||
if (!wrapper || !wrapper.classList.contains('open')) {
|
||||
if (!wrapper?.classList.contains('open')) {
|
||||
_incrementBadge();
|
||||
}
|
||||
}
|
||||
@@ -312,7 +309,9 @@ const notifications = (() => {
|
||||
const body = $('notif-panel-body');
|
||||
if (!body) return;
|
||||
// Remove all notif-items
|
||||
body.querySelectorAll('.notif-item').forEach((el) => el.remove());
|
||||
body.querySelectorAll('.notif-item').forEach((el) => {
|
||||
el.remove();
|
||||
});
|
||||
_clearBadge();
|
||||
_showEmptyIfNeeded();
|
||||
// automatically close notification center on clear
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// Service Worker registration — runs after page load.
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', function () {
|
||||
window.addEventListener('load', () => {
|
||||
navigator.serviceWorker
|
||||
.register('/sw.js')
|
||||
.then(function () {
|
||||
.then(() => {
|
||||
/* registered */
|
||||
})
|
||||
.catch(function (err) {
|
||||
.catch((err) => {
|
||||
console.log('Service Worker registration failed:', err);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user