/**
* Static shell read-more — preview/rest split; full HTML always in DOM for SEO.
* Preview: first N plain-text words. Rest: complete original HTML (collapsed until toggle).
* Override per host: data-read-more-words="55" on .nse-read-more-host
*/
(function () {
'use strict';
var READ_MORE_WORD_LIMIT = 110;
function extractPlainText(html) {
return String(html)
.replace(/<[^>]+>/g, ' ')
.replace(/\s+/g, ' ')
.trim();
}
function countWords(text) {
if (!text) return 0;
return text.split(/\s+/).filter(Boolean).length;
}
function truncateToWords(text, limit) {
var words = text.split(/\s+/).filter(Boolean);
if (words.length <= limit) {
return null;
}
return words.slice(0, limit).join(' ') + '…';
}
function escapeHtml(text) {
return String(text)
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"');
}
function uniqueId() {
return 'nse-rm-' + Math.random().toString(36).slice(2, 9);
}
function resolveWordLimit(host) {
if (host) {
var attr = host.getAttribute('data-read-more-words');
if (attr !== null && attr !== '') {
var parsed = parseInt(attr, 10);
if (!isNaN(parsed) && parsed > 0) {
return Math.min(Math.max(parsed, 10), 200);
}
}
}
return READ_MORE_WORD_LIMIT;
}
/** Split single
with breaks into multiple paragraphs (mirrors NSE PHP). */
function normalizeParagraphMarkup(html) {
html = String(html).trim();
if (!html) return html;
var pCount = (html.match(/<\/p>/gi) || []).length;
if (pCount >= 2) return html;
var pClose = html.toLowerCase().split('
').length - 1;
var pm = html.match(/
]*>([\s\S]*)<\/p>/i);
if (pClose === 1 && pm) {
var inner = pm[1];
if (/ \s* /i.test(inner)) {
var chunks = inner.split(/ \s* /i).map(function (c) {
return c.trim();
}).filter(Boolean);
if (chunks.length >= 2) {
return chunks.map(function (c) {
return '
';
}).join('');
}
} else {
var plain = inner.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
if (plain.length > 360) {
var cut = plain.search(/[.!?]\s+\S/);
if (cut > 99 && cut < plain.length - 40) {
var a = plain.slice(0, cut + 1).trim();
var b = plain.slice(cut + 1).trim();
if (a && b) {
return '
' + escapeHtml(a) + '
' + escapeHtml(b) + '
';
}
}
}
}
}
return html;
}
function buildWrappedMarkup(excerpt, fullHtml) {
fullHtml = String(fullHtml).trim();
if (!fullHtml) return '';
var uid = uniqueId();
var iconPlus =
'';
var iconMinus =
'';
var out = '
';
out += excerpt;
out +=
'
';
out += fullHtml;
out += '
';
out +=
'
';
return out;
}
/** Word-count cutoff — plain-text teaser, full HTML in expandable block (mirrors NSE PHP). */
function wrapReadMoreHtml(html, wordLimit) {
html = String(html).trim();
if (!html || html.indexOf('nse-field-read-more') !== -1) return null;
html = normalizeParagraphMarkup(html);
var plain = extractPlainText(html);
if (countWords(plain) <= wordLimit) return null;
var teaserText = truncateToWords(plain, wordLimit);
if (!teaserText) return null;
var excerpt =
'
' + escapeHtml(teaserText) + '
';
return buildWrappedMarkup(excerpt, html);
}
function syncCollapsedState(wrap) {
if (!wrap) return;
var btn = wrap.querySelector('.nse-field-read-more__btn');
var more = wrap.querySelector('.nse-field-read-more__more');
var label = btn && btn.querySelector('.nse-field-read-more__label');
if (!btn || !more) return;
var lm = btn.getAttribute('data-nse-label-more') || 'Read more';
btn.setAttribute('aria-expanded', 'false');
if (label) label.textContent = lm;
wrap.classList.remove('nse-field-read-more--expanded');
more.classList.add('nse-field-read-more__more--collapsed');
more.style.maxHeight = '';
more.setAttribute('aria-hidden', 'true');
more.removeAttribute('data-nse-rm-anim');
}
function initHosts() {
document.querySelectorAll('.nse-read-more-host').forEach(function (host) {
if (host.querySelector('.nse-field-read-more')) {
syncCollapsedState(host.querySelector('.nse-field-read-more'));
return;
}
var originalHtml = host.innerHTML;
var wrapped = wrapReadMoreHtml(originalHtml, resolveWordLimit(host));
if (wrapped) {
host.innerHTML = wrapped;
syncCollapsedState(host.querySelector('.nse-field-read-more'));
}
});
}
function prefersReducedMotion() {
return (
typeof window.matchMedia === 'function' &&
window.matchMedia('(prefers-reduced-motion: reduce)').matches
);
}
function onceMaxHeightEnd(el, fn) {
function handler(e) {
if (e.propertyName !== 'max-height') return;
el.removeEventListener('transitionend', handler);
fn();
}
el.addEventListener('transitionend', handler);
}
document.addEventListener('click', function (e) {
var btn = e.target.closest('.nse-field-read-more__btn');
if (!btn) return;
var wrap = btn.closest('.nse-field-read-more');
if (!wrap) return;
var more = wrap.querySelector('.nse-field-read-more__more');
if (!more) return;
if (more.getAttribute('data-nse-rm-anim') === '1') return;
var label = btn.querySelector('.nse-field-read-more__label');
var expanded = btn.getAttribute('aria-expanded') === 'true';
var lm = btn.getAttribute('data-nse-label-more') || 'Read more';
var ll = btn.getAttribute('data-nse-label-less') || 'Read less';
var reduced = prefersReducedMotion();
more.setAttribute('data-nse-rm-anim', '1');
function finish() {
more.removeAttribute('data-nse-rm-anim');
}
if (expanded) {
btn.setAttribute('aria-expanded', 'false');
if (label) label.textContent = lm;
wrap.classList.remove('nse-field-read-more--expanded');
if (reduced) {
more.classList.add('nse-field-read-more__more--collapsed');
more.style.maxHeight = '';
more.setAttribute('aria-hidden', 'true');
finish();
return;
}
more.style.maxHeight = more.scrollHeight + 'px';
void more.offsetHeight;
more.style.maxHeight = '0px';
onceMaxHeightEnd(more, function () {
more.classList.add('nse-field-read-more__more--collapsed');
more.style.maxHeight = '';
more.setAttribute('aria-hidden', 'true');
finish();
});
} else {
btn.setAttribute('aria-expanded', 'true');
if (label) label.textContent = ll;
wrap.classList.add('nse-field-read-more--expanded');
if (reduced) {
more.classList.remove('nse-field-read-more__more--collapsed');
more.style.maxHeight = 'none';
more.setAttribute('aria-hidden', 'false');
finish();
return;
}
var xh = more.scrollHeight;
more.style.maxHeight = '0px';
more.classList.remove('nse-field-read-more__more--collapsed');
more.setAttribute('aria-hidden', 'false');
void more.offsetHeight;
more.style.maxHeight = xh + 'px';
onceMaxHeightEnd(more, function () {
more.style.maxHeight = 'none';
finish();
});
}
});
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initHosts);
} else {
initHosts();
}
})();