Corp/backend/static/js/utils/stringUtils.js
Игорь Надеин 76f6f4ea32 refactor: extract JavaScript utilities to separate modules (Phase 2)
- 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 ✅
2025-10-28 08:37:02 +03:00

108 lines
3.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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>')
* // => '&lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;'
*
* esc("O'Reilly & Sons")
* // => 'O&#39;Reilly &amp; Sons'
*/
export function esc(s) {
return String(s || '')
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
/**
* Нормализация строки для поиска и сравнения
* Приводит к 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=&quot;test&quot;'
*/
export function escAttr(s) {
return String(s || '')
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#x27;')
.replace(/\//g, '&#x2F;');
}
/**
* Усечение строки до указанной длины с добавлением многоточия
*
* @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();
}