1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.replacerForNamespace = exports.autolink = exports.YES_CLAUSE_AUTOLINK = exports.NO_CLAUSE_AUTOLINK = void 0;
|
4 | const Xref_1 = require("./Xref");
|
5 | const utils = require("./utils");
|
6 | const escape = require('html-escape');
|
7 | exports.NO_CLAUSE_AUTOLINK = new Set([
|
8 | 'PRE',
|
9 | 'CODE',
|
10 | 'EMU-CONST',
|
11 | 'EMU-PRODUCTION',
|
12 | 'EMU-GRAMMAR',
|
13 | 'EMU-XREF',
|
14 | 'H1',
|
15 | 'H2',
|
16 | 'H3',
|
17 | 'H4',
|
18 | 'H5',
|
19 | 'H6',
|
20 | 'EMU-VAR',
|
21 | 'EMU-VAL',
|
22 | 'VAR',
|
23 | 'A',
|
24 | 'DFN',
|
25 | 'SUB',
|
26 | 'EMU-NOT-REF',
|
27 | ]);
|
28 | exports.YES_CLAUSE_AUTOLINK = new Set(['EMU-GMOD']);
|
29 | function autolink(node, replacer, autolinkmap, clause, currentId, allowSameId) {
|
30 | const spec = clause.spec;
|
31 | const template = spec.doc.createElement('template');
|
32 | const content = escape(node.textContent);
|
33 | const autolinked = content.replace(replacer, (match, offset) => {
|
34 | const entry = autolinkmap[narrowSpace(match)];
|
35 | if (!entry) {
|
36 | return match;
|
37 | }
|
38 | const entryId = entry.id || entry.refId;
|
39 | const skipLinking = !allowSameId && currentId && entryId === currentId;
|
40 | if (skipLinking) {
|
41 | return match;
|
42 | }
|
43 | if (entry.aoid) {
|
44 | let isInvocationAttribute = '';
|
45 |
|
46 |
|
47 | if (content[offset + match.length] === '(' ||
|
48 | (content[offset + match.length] === ' ' &&
|
49 | content[offset + match.length + 1] === 'o' &&
|
50 | content[offset + match.length + 2] === 'f')) {
|
51 | isInvocationAttribute = ' is-invocation';
|
52 | }
|
53 | return `<emu-xref aoid="${entry.aoid}"${isInvocationAttribute}>${match}</emu-xref>`;
|
54 | }
|
55 | else {
|
56 | return `<emu-xref href="#${entry.id || entry.refId}">${match}</emu-xref>`;
|
57 | }
|
58 | });
|
59 | if (autolinked !== content) {
|
60 | template.innerHTML = autolinked;
|
61 | const newXrefNodes = utils.replaceTextNode(node, template.content);
|
62 | const newXrefs = newXrefNodes.map(node => new Xref_1.default(spec, node, clause, clause.namespace, node.getAttribute('href'), node.getAttribute('aoid')));
|
63 | spec._xrefs = spec._xrefs.concat(newXrefs);
|
64 | }
|
65 | }
|
66 | exports.autolink = autolink;
|
67 | function replacerForNamespace(namespace, biblio) {
|
68 | const autolinkmap = biblio.getDefinedWords(namespace);
|
69 | const replacer = new RegExp(regexpPatternForAutolinkKeys(autolinkmap, Object.keys(autolinkmap), 0), 'g');
|
70 | return { replacer, autolinkmap };
|
71 | }
|
72 | exports.replacerForNamespace = replacerForNamespace;
|
73 | function isCommonAbstractOp(op) {
|
74 | return (op === 'Call' || op === 'Set' || op === 'Type' || op === 'UTC' || op === 'min' || op === 'max');
|
75 | }
|
76 | function lookAheadBeyond(key, entry) {
|
77 | if (isCommonAbstractOp(key)) {
|
78 |
|
79 | return '\\b(?=\\()';
|
80 | }
|
81 | if (entry.type !== 'term' || /^\w/.test(key)) {
|
82 |
|
83 | return '\\b(?!\\.\\w|%%|\\]\\])';
|
84 | }
|
85 | return '';
|
86 | }
|
87 |
|
88 | function widenSpace(str) {
|
89 | return str.replace(/\s+/g, '\\s+');
|
90 | }
|
91 |
|
92 | function narrowSpace(str) {
|
93 | return str.replace(/\s+/g, ' ');
|
94 | }
|
95 | function regexpEscape(str) {
|
96 | return str.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
|
97 | }
|
98 | function regexpUnion(alternatives) {
|
99 | var _a;
|
100 | if (alternatives.length < 2) {
|
101 | return `${(_a = alternatives[0]) !== null && _a !== void 0 ? _a : ''}`;
|
102 | }
|
103 | return `(?:${alternatives.join('|')})`;
|
104 | }
|
105 |
|
106 |
|
107 |
|
108 | function longestCommonPrefix(items, beginIndex = 0) {
|
109 | let endIndex = beginIndex;
|
110 | OUTER: while (endIndex < items[0].length) {
|
111 | const char = items[0][endIndex];
|
112 | for (let i = 1; i < items.length; ++i) {
|
113 | if (char !== items[i][endIndex]) {
|
114 | break OUTER;
|
115 | }
|
116 | }
|
117 | ++endIndex;
|
118 | }
|
119 | return items[0].slice(beginIndex, endIndex);
|
120 | }
|
121 | function regexpPatternForAutolinkKeys(autolinkmap, subsetKeys, initialCommonLength) {
|
122 | var _a;
|
123 | const resultAlternatives = [];
|
124 | const wordStartAlternatives = [];
|
125 | const groups = {};
|
126 | for (const key of subsetKeys) {
|
127 | const char = key[initialCommonLength];
|
128 | const group = ((_a = groups[char]) !== null && _a !== void 0 ? _a : (groups[char] = []));
|
129 | group.push(key);
|
130 | }
|
131 | const matchEmpty = '' in groups;
|
132 | if (matchEmpty) {
|
133 | delete groups[''];
|
134 | }
|
135 | const longestFirst = (a, b) => b.length - a.length;
|
136 | for (const groupChar of Object.keys(groups).sort()) {
|
137 |
|
138 | const groupItems = groups[groupChar].sort(longestFirst);
|
139 | const prefix = longestCommonPrefix(groupItems, initialCommonLength);
|
140 | const prefixRegex = widenSpace(regexpEscape(prefix));
|
141 | const suffixPos = initialCommonLength + prefix.length;
|
142 | let suffixRegex;
|
143 | if (groupItems.length > 5) {
|
144 |
|
145 | suffixRegex = regexpPatternForAutolinkKeys(autolinkmap, groupItems, suffixPos);
|
146 | }
|
147 | else {
|
148 | suffixRegex = regexpUnion(groupItems.map(k => {
|
149 | const item = widenSpace(regexpEscape(k.slice(suffixPos)));
|
150 | return item + lookAheadBeyond(k, autolinkmap[k]);
|
151 | }));
|
152 | }
|
153 | if (initialCommonLength === 0 && /^\w/.test(prefix)) {
|
154 | wordStartAlternatives.push(prefixRegex + suffixRegex);
|
155 | }
|
156 | else {
|
157 | resultAlternatives.push(prefixRegex + suffixRegex);
|
158 | }
|
159 | }
|
160 | if (matchEmpty) {
|
161 | resultAlternatives.push('');
|
162 | }
|
163 | if (wordStartAlternatives.length) {
|
164 | resultAlternatives.unshift('\\b' + regexpUnion(wordStartAlternatives));
|
165 | }
|
166 | return regexpUnion(resultAlternatives);
|
167 | }
|