- Created reusable utility modules:
* static/js/utils/stringUtils.js - string operations (esc, norm, escAttr, truncate, getInitials)
* static/js/utils/timing.js - timing utilities (debounce, throttle, delay, poll, once)
* static/js/utils/scroll.js - scroll helpers (smoothScrollTo, scrollToTop, isElementInViewport, etc.)
- Updated static/js/utils/index.js:
* Exports all utilities from a single entry point
* Provides centralized access to all utility functions
- Modified base.html:
* Removed inline esc() and norm() function definitions
* Added ES6 module imports for utilities
* Exported utilities to window for backward compatibility
* All existing code continues to work without changes
Benefits:
- Eliminates code duplication (esc/norm found in 9 templates)
- Centralizes utility functions for easier maintenance
- Enables future improvements (testing, optimization)
- Maintains backward compatibility with existing templates
Tests: All 12 static file tests passing ✅
108 lines
3.4 KiB
JavaScript
108 lines
3.4 KiB
JavaScript
/**
|
||
* String Utilities
|
||
*
|
||
* Утилиты для работы со строками: экранирование, нормализация и т.д.
|
||
*
|
||
* Использование:
|
||
* import { esc, norm } from '{% static "js/utils/stringUtils.js" %}';
|
||
*/
|
||
|
||
/**
|
||
* Экранирование HTML символов для безопасного отображения
|
||
* Защищает от XSS атак при выводе пользовательского контента
|
||
*
|
||
* @param {string|number|null|undefined} s - строка для экранирования
|
||
* @returns {string} - экранированная строка
|
||
*
|
||
* @example
|
||
* esc('<script>alert("XSS")</script>')
|
||
* // => '<script>alert("XSS")</script>'
|
||
*
|
||
* esc("O'Reilly & Sons")
|
||
* // => 'O'Reilly & Sons'
|
||
*/
|
||
export function esc(s) {
|
||
return String(s || '')
|
||
.replace(/&/g, '&')
|
||
.replace(/</g, '<')
|
||
.replace(/>/g, '>')
|
||
.replace(/"/g, '"')
|
||
.replace(/'/g, ''');
|
||
}
|
||
|
||
/**
|
||
* Нормализация строки для поиска и сравнения
|
||
* Приводит к lowercase, заменяет множественные пробелы на один, убирает пробелы с краёв
|
||
*
|
||
* @param {string|null|undefined} s - строка для нормализации
|
||
* @returns {string} - нормализованная строка
|
||
*
|
||
* @example
|
||
* norm(' Иван Петров ')
|
||
* // => 'иван петров'
|
||
*
|
||
* norm('МОСКВА')
|
||
* // => 'москва'
|
||
*/
|
||
export function norm(s) {
|
||
return (s || '').toString().toLowerCase().replace(/\s+/g, ' ').trim();
|
||
}
|
||
|
||
/**
|
||
* Экранирование для использования в атрибутах HTML
|
||
* Более строгое, чем esc() - заменяет больше символов
|
||
*
|
||
* @param {string|null|undefined} s - строка для экранирования
|
||
* @returns {string} - экранированная строка для атрибута
|
||
*
|
||
* @example
|
||
* escAttr('value="test"')
|
||
* // => 'value="test"'
|
||
*/
|
||
export function escAttr(s) {
|
||
return String(s || '')
|
||
.replace(/&/g, '&')
|
||
.replace(/</g, '<')
|
||
.replace(/>/g, '>')
|
||
.replace(/"/g, '"')
|
||
.replace(/'/g, ''')
|
||
.replace(/\//g, '/');
|
||
}
|
||
|
||
/**
|
||
* Усечение строки до указанной длины с добавлением многоточия
|
||
*
|
||
* @param {string} s - строка для усечения
|
||
* @param {number} maxLen - максимальная длина (включая многоточие)
|
||
* @returns {string} - усечённая строка
|
||
*
|
||
* @example
|
||
* truncate('Очень длинная строка текста', 15)
|
||
* // => 'Очень длинн...'
|
||
*/
|
||
export function truncate(s, maxLen) {
|
||
const str = String(s || '');
|
||
if (str.length <= maxLen) return str;
|
||
return str.slice(0, maxLen - 3) + '...';
|
||
}
|
||
|
||
/**
|
||
* Извлечение инициалов из ФИО
|
||
*
|
||
* @param {string} fullName - полное имя
|
||
* @returns {string} - инициалы
|
||
*
|
||
* @example
|
||
* getInitials('Иванов Иван Петрович')
|
||
* // => 'ИИ'
|
||
*
|
||
* getInitials('John Doe')
|
||
* // => 'JD'
|
||
*/
|
||
export function getInitials(fullName) {
|
||
const parts = String(fullName || '').trim().split(/\s+/);
|
||
if (parts.length === 0) return '';
|
||
if (parts.length === 1) return parts[0].charAt(0).toUpperCase();
|
||
return (parts[0].charAt(0) + parts[1].charAt(0)).toUpperCase();
|
||
}
|