UNPKG

ecmarkup

Version:

Custom element definitions and core utilities for markup that specifies ECMAScript and related technologies.

144 lines (143 loc) 6.17 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const Builder_1 = require("./Builder"); const Clause_1 = require("./Clause"); const utils_1 = require("./utils"); const utils_2 = require("./lint/utils"); const emd = require("ecmarkdown"); function findLabeledSteps(root) { const steps = []; emd.visit(root, { enter(node) { if (node.name === 'ordered-list-item' && node.attrs.some(a => a.key === 'id')) { steps.push(node); } }, }); return steps; } const kindSelector = Clause_1.SPECIAL_KINDS.map(kind => `li[${kind}]`).join(','); class Algorithm extends Builder_1.default { static async enter(context) { context.inAlg = true; const { spec, node, clauseStack } = context; let emdTree = null; let innerHTML; if ('ecmarkdownTree' in node) { emdTree = node.ecmarkdownTree; innerHTML = node.originalHtml; } else { const location = spec.locate(node); const source = (location === null || location === void 0 ? void 0 : location.source) == null || location.endTag == null ? node.innerHTML : location.source.slice(location.startTag.endOffset, location.endTag.startOffset); innerHTML = source; try { emdTree = emd.parseAlgorithm(source); node.ecmarkdownTree = emdTree; node.originalHtml = source; } catch (e) { (0, utils_1.warnEmdFailure)(spec.warn, node, e); } } if (emdTree == null) { node.innerHTML = (0, utils_1.wrapEmdFailure)(innerHTML); return; } if (spec.opts.lintSpec && spec.locate(node) != null && !node.hasAttribute('example')) { const clause = clauseStack[clauseStack.length - 1]; const namespace = clause ? clause.namespace : spec.namespace; const nonterminals = (0, utils_2.collectNonterminalsFromEmd)(emdTree).map(({ name, loc }) => ({ name, loc, node, namespace, })); spec._ntStringRefs = spec._ntStringRefs.concat(nonterminals); } const rawHtml = emd.emit(emdTree); // replace spaces after !/? with   to prevent bad line breaking let html = rawHtml.replace(/((?:\s+|>)[!?])[ \t]+/g, '$1 '); // replace spaces before »/} with   to prevent bad line breaking html = html.replace(/[ \t]+([»}])/g, ' $1'); node.innerHTML = html; const labeledStepEntries = []; const replaces = node.getAttribute('replaces-step'); if (replaces) { context.spec.replacementAlgorithms.push({ element: node, target: replaces, }); context.spec.replacementAlgorithmToContainedLabeledStepEntries.set(node, labeledStepEntries); } if (replaces && node.firstElementChild.children.length > 1) { const labeledSteps = findLabeledSteps(emdTree); for (const step of labeledSteps) { const itemSource = innerHTML.slice(step.location.start.offset, step.location.end.offset); const offset = itemSource.match(/^.*?[ ,[]id *= *"/)[0].length; spec.warn({ type: 'contents', ruleId: 'labeled-step-in-replacement', message: 'labeling a step in a replacement algorithm which has multiple top-level steps is unsupported because the resulting step number would be ambiguous', node, nodeRelativeLine: step.location.start.line, nodeRelativeColumn: step.location.start.column + offset, }); } } for (const step of node.querySelectorAll('li[id]')) { const entry = { type: 'step', id: step.id, stepNumbers: getStepNumbers(step), }; context.spec.biblio.add(entry); if (replaces) { // The biblio entries for labeled steps in replacement algorithms will be modified in-place by a subsequent pass labeledStepEntries.push(entry); context.spec.labeledStepsToBeRectified.add(step.id); } } for (const step of node.querySelectorAll(kindSelector)) { // prettier-ignore const attributes = Clause_1.SPECIAL_KINDS .filter(kind => step.hasAttribute(kind)) .map(kind => Clause_1.SPECIAL_KINDS_MAP.get(kind)); const tag = spec.doc.createElement('div'); tag.className = 'attributes-tag'; const text = attributes.join(', '); const contents = spec.doc.createTextNode(text); tag.append(contents); step.prepend(tag); // we've already walked past the text node, so it won't get picked up by the usual process for autolinking const clause = clauseStack[clauseStack.length - 1]; if (clause != null) { // the `== null` case only happens if you put an algorithm at the top level of your document spec._textNodes[clause.namespace] = spec._textNodes[clause.namespace] || []; spec._textNodes[clause.namespace].push({ node: contents, clause, inAlg: true, currentId: context.currentId, }); } } } static exit(context) { context.inAlg = false; } } Algorithm.elements = ['EMU-ALG']; exports.default = Algorithm; function getStepNumbers(item) { var _a; const { indexOf } = Array.prototype; const counts = []; while (((_a = item.parentElement) === null || _a === void 0 ? void 0 : _a.tagName) === 'OL') { counts.unshift(1 + indexOf.call(item.parentElement.children, item)); item = item.parentElement.parentElement; } return counts; }