Фронт: точки переноса в длинных ссылках (<wbr>), не полагаемся на CSS
Ссылка на онлайн-курс в поле аудитории — одно длинное слово. CSS-фикс (overflow-wrap/word-break) работает в современных движках, но на телефоне пользователя переполнение оставалось: старый движок или закэшированный CSS. Теперь фронтенд сам расставляет <wbr>: после служебных символов ссылок (/, ?, &, =, ., _, -) и не реже, чем каждые 24 символа; экранирование сохраняется. Перенос работает в любом браузере. Проверено в headless Chromium (390px и 320px): переполнений нет как с включёнными CSS-переносами, так и с принудительно отключёнными (эмуляция старого движка).
This commit is contained in:
@@ -80,6 +80,9 @@ nginx (https://schedule.our-place.ru) → 127.0.0.1:8090
|
|||||||
заменяется на «Окно».
|
заменяется на «Окно».
|
||||||
- Подпись недели появляется только там, где недели действительно
|
- Подпись недели появляется только там, где недели действительно
|
||||||
различаются; бейдж «Сейчас» подсвечивает текущую неделю.
|
различаются; бейдж «Сейчас» подсвечивает текущую неделю.
|
||||||
|
- Длинные ссылки в карточке переносятся по служебным символам и каждые
|
||||||
|
24 символа (`<wbr>`), поэтому карточка не вылезает за экран даже в старых
|
||||||
|
браузерах, где нет `overflow-wrap`.
|
||||||
- Приложение можно «установить» на главный экран (манифест + service
|
- Приложение можно «установить» на главный экран (манифест + service
|
||||||
worker + иконки приложения); на iOS иконку берёт `apple-touch-icon`.
|
worker + иконки приложения); на iOS иконку берёт `apple-touch-icon`.
|
||||||
|
|
||||||
|
|||||||
@@ -239,10 +239,10 @@ function entityLessonHTML(parity, entry) {
|
|||||||
<div>
|
<div>
|
||||||
${badge}
|
${badge}
|
||||||
${entry.marker ? `<span class="badge badge-kv">${escapeHTML(entry.marker)}</span>` : ""}
|
${entry.marker ? `<span class="badge badge-kv">${escapeHTML(entry.marker)}</span>` : ""}
|
||||||
<span class="subject">${escapeHTML(entry.subject)}</span>
|
<span class="subject">${withBreaks(entry.subject)}</span>
|
||||||
</div>
|
</div>
|
||||||
${details.length ? `<div class="details">${escapeHTML(details.join(" · "))}</div>` : ""}
|
${details.length ? `<div class="details">${withBreaks(details.join(" · "))}</div>` : ""}
|
||||||
${entry.note ? `<div class="lesson-note">${escapeHTML(entry.note)}</div>` : ""}
|
${entry.note ? `<div class="lesson-note">${withBreaks(entry.note)}</div>` : ""}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,10 +277,10 @@ function singleLessonHTML(badgeKey, lesson, full) {
|
|||||||
<div>
|
<div>
|
||||||
${badge}
|
${badge}
|
||||||
${lesson.marker ? `<span class="badge badge-kv">${escapeHTML(lesson.marker)}</span>` : ""}
|
${lesson.marker ? `<span class="badge badge-kv">${escapeHTML(lesson.marker)}</span>` : ""}
|
||||||
<span class="subject">${escapeHTML(lesson.subject)}</span>
|
<span class="subject">${withBreaks(lesson.subject)}</span>
|
||||||
</div>
|
</div>
|
||||||
${details ? `<div class="details">${escapeHTML(details)}</div>` : ""}
|
${details ? `<div class="details">${withBreaks(details)}</div>` : ""}
|
||||||
${lesson.note ? `<div class="lesson-note">${escapeHTML(lesson.note)}</div>` : ""}
|
${lesson.note ? `<div class="lesson-note">${withBreaks(lesson.note)}</div>` : ""}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -490,6 +490,20 @@ function escapeHTML(s) {
|
|||||||
return s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]));
|
return s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Экранирует текст и расставляет точки переноса <wbr>.
|
||||||
|
* Длинные ссылки (и вообще слова без пробелов) переносятся в любом браузере —
|
||||||
|
* не полагаемся на поддержку overflow-wrap: anywhere старыми движками.
|
||||||
|
*/
|
||||||
|
function withBreaks(text) {
|
||||||
|
const WBR = "\u0001"; // служебный маркер, в тексте таблицы не встречается
|
||||||
|
if (!text) return "";
|
||||||
|
const marked = String(text)
|
||||||
|
.replace(/([/?&=._\-])(?=\S)/g, "$1" + WBR) // после служебных символов ссылок
|
||||||
|
.replace(/(\S{24})(?=\S)/g, "$1" + WBR); // и не реже, чем каждые 24 символа
|
||||||
|
return escapeHTML(marked).split(WBR).join("<wbr>");
|
||||||
|
}
|
||||||
|
|
||||||
function applyTheme(theme) {
|
function applyTheme(theme) {
|
||||||
document.documentElement.setAttribute("data-theme", theme);
|
document.documentElement.setAttribute("data-theme", theme);
|
||||||
localStorage.setItem("theme", theme);
|
localStorage.setItem("theme", theme);
|
||||||
|
|||||||
Reference in New Issue
Block a user