68 lines
2.9 KiB
JavaScript
68 lines
2.9 KiB
JavaScript
import { cleanText } from './text.js';
|
||
|
||
// Описание услуги в базе — HTML со старого сайта: абзацы, списки и подзаголовки,
|
||
// набранные жирным span-ом с двоеточием в конце («Когда нужно срочно к врачу:»).
|
||
// Разбираем его на секции и дальше рисуем их компонентами макета — сам HTML
|
||
// в страницу не вставляем, поэтому стили и скрипты из старой вёрстки не протекают.
|
||
|
||
const HEADING_MAX = 90;
|
||
|
||
function capitalize(s) {
|
||
return s ? s[0].toUpperCase() + s.slice(1) : s;
|
||
}
|
||
|
||
export function parseBlocks(blocks) {
|
||
const html = (blocks || [])
|
||
.map((b) => b?.html || '')
|
||
.filter(Boolean)
|
||
.join('\n');
|
||
if (!html.trim() || typeof DOMParser === 'undefined') return [];
|
||
|
||
// An explicit body also makes fragment parsing identical in the build-time
|
||
// DOM implementation and in browsers.
|
||
const doc = new DOMParser().parseFromString(`<!doctype html><html><body>${html}</body></html>`, 'text/html');
|
||
const sections = [{ title: '', paragraphs: [], items: [] }];
|
||
const current = () => sections[sections.length - 1];
|
||
|
||
const visit = (node) => {
|
||
const isElement = node.nodeType === 1;
|
||
if (isElement && /^(SCRIPT|STYLE|NOSCRIPT)$/.test(node.tagName)) return;
|
||
if (isElement && (node.tagName === 'UL' || node.tagName === 'OL')) {
|
||
node.querySelectorAll('li').forEach((li) => {
|
||
const text = cleanText(li.textContent).replace(/[;.,]+$/, '');
|
||
if (text) current().items.push(capitalize(text));
|
||
});
|
||
return;
|
||
}
|
||
// обёртка со списком внутри — разбираем по дочерним узлам
|
||
if (isElement && node.querySelector('ul, ol, h2, h3, h4, h5, h6')) {
|
||
node.childNodes.forEach(visit);
|
||
return;
|
||
}
|
||
|
||
const text = cleanText(node.textContent);
|
||
if (!text) return;
|
||
if (text.length <= HEADING_MAX && text.endsWith(':') && current().title &&
|
||
!current().paragraphs.length && !current().items.length &&
|
||
!(isElement && /^H[2-6]$/.test(node.tagName))) {
|
||
current().paragraphs.push(text);
|
||
return;
|
||
}
|
||
if ((isElement && /^H[2-6]$/.test(node.tagName)) || (text.length <= HEADING_MAX && text.endsWith(':'))) {
|
||
sections.push({ title: text.replace(/:$/, '').trim(), paragraphs: [], items: [] });
|
||
return;
|
||
}
|
||
// текст после списка без нового подзаголовка — отдельная секция, чтобы не склеить порядок
|
||
if (current().items.length) {
|
||
sections.push({ title: '', paragraphs: [text], items: [] });
|
||
return;
|
||
}
|
||
current().paragraphs.push(text);
|
||
};
|
||
|
||
doc.body.childNodes.forEach(visit);
|
||
return sections.filter((s) => s.title || s.paragraphs.length || s.items.length);
|
||
}
|
||
|
||
export const URGENT_TITLE = /срочн/i;
|