UNPKG

wikiparser-node

Version:

A Node.js parser for MediaWiki markup with AST

152 lines (151 loc) 5.11 kB
"use strict"; /* NOT FOR BROWSER */ Object.defineProperty(exports, "__esModule", { value: true }); exports.getId = exports.html = exports.getCommon = void 0; const string_1 = require("./string"); /* NOT FOR BROWSER END */ /** * get common prefix length * @param prefix * @param lastPrefix */ const getCommon = (prefix, lastPrefix) => // eslint-disable-next-line @typescript-eslint/no-misused-spread prefix.startsWith(lastPrefix) ? lastPrefix.length : [...lastPrefix].findIndex((ch, i) => ch !== prefix[i]); exports.getCommon = getCommon; /** * get next list item * @param char list syntax * @param state */ const nextItem = (char, state) => { if (char === '*' || char === '#') { return '</li>\n<li>'; } const close = state.dt[0] ? '</dt>\n' : '</dd>\n'; if (char === ';') { state.dt[0] = true; return `${close}<dt>`; } state.dt[0] = false; return `${close}<dd>`; }; /** * close list item * @param chars list syntax * @param state */ const closeList = (chars, state) => { let result = ''; for (let i = chars.length - 1; i >= 0; i--) { const char = chars[i]; switch (char) { case '*': case '#': result += `</li></${char === '*' ? 'ul' : 'ol'}>`; break; case ':': result += `</${state.dt.shift() ? 'dt' : 'dd'}></dl>`; break; default: // } } return result; }; /** * open list item * @param chars list syntax * @param state */ const openList = (chars, state) => { let result = ''; for (const char of chars) { switch (char) { case '*': case '#': result += `<${char === '*' ? 'ul' : 'ol'}><li>`; break; case ':': state.dt.unshift(false); result += '<dl><dd>'; break; default: state.dt.unshift(true); result += '<dl><dt>'; } } return result; }; /** * convert to HTML * @param childNodes a Token's contents * @param separator delimiter between nodes * @param opt options */ const html = (childNodes, separator = '', opt) => { let lastPrefix = ''; const results = [], state = { dt: [] }; for (let j = 0; j < childNodes.length; j++) { const child = childNodes[j]; let result = child.toHtmlInternal(opt); if (child.is('list-range')) { const { previousSibling } = child, { innerText } = previousSibling; if ((child.length > 0 || /\s$/u.test(innerText)) && previousSibling.is('list') && !/[;#*]/u.test(innerText) && child.closest('ext#poem,list-range')?.is('ext')) { lastPrefix = ''; result = `<span style="display: inline-block; margin-inline-start: ${previousSibling.indent}em;">${result}</span>`; } else { result = result.trim(); const prefix = innerText.trim(), prefix2 = prefix.replaceAll(';', ':'), commonPrefixLength = (0, exports.getCommon)(prefix2, lastPrefix); let pre = closeList(lastPrefix.slice(commonPrefixLength), state); if (prefix.length === commonPrefixLength) { pre += nextItem(prefix.slice(-1), state); } else { if (state.dt[0] && prefix[commonPrefixLength - 1] === ':') { pre += nextItem(':', state); } if (lastPrefix) { pre += '\n'; } pre += openList(prefix.slice(commonPrefixLength), state); } result = pre + result; let { nextSibling } = child; while (nextSibling?.is('dd')) { const next = nextSibling.nextSibling; result += nextItem(':', state) + next.toHtmlInternal(opt).trim(); ({ nextSibling } = next); j += 2; } if (nextSibling?.type === 'text' && nextSibling.data === '\n' && nextSibling.nextVisibleSibling?.is('list')) { j += 2; lastPrefix = prefix2; } else { lastPrefix = ''; result += closeList(prefix2, state); } } } results.push(result); } return results.join(separator); }; exports.html = html; /** * get the id of a section heading * @param tokens inner tokens of a section heading */ const getId = (tokens) => { const opt = { nocc: true }, content = Array.isArray(tokens) ? (0, exports.html)(tokens, '', opt) : tokens.toHtmlInternal(opt), id = (0, string_1.decodeHtml)((0, string_1.sanitizeAlt)(content.replaceAll('_', ' '))) .replace(/[\s_]+/gu, '_'); return id.endsWith('_') ? id.slice(0, -1) : id; }; exports.getId = getId;