1 | "use strict";
|
2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
4 | return new (P || (P = Promise))(function (resolve, reject) {
|
5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
8 | step((generator = generator.apply(thisArg, _arguments || [])).next());
|
9 | });
|
10 | };
|
11 | Object.defineProperty(exports, "__esModule", { value: true });
|
12 | const Builder_1 = require("./Builder");
|
13 | const utils_1 = require("./utils");
|
14 |
|
15 | class Xref extends Builder_1.default {
|
16 | constructor(spec, node, clause, namespace, href, aoid) {
|
17 | super(spec, node);
|
18 | this.namespace = namespace;
|
19 | this.href = href;
|
20 | this.aoid = aoid;
|
21 | this.clause = clause;
|
22 | this.id = node.getAttribute('id');
|
23 | this.isInvocation = node.hasAttribute('is-invocation');
|
24 | node.removeAttribute('is-invocation');
|
25 |
|
26 | this.addEffects = null;
|
27 | this.suppressEffects = null;
|
28 | if (node.parentElement && node.parentElement.tagName === 'EMU-META') {
|
29 | if (node.parentElement.hasAttribute('effects')) {
|
30 | const addEffects = node.parentElement.getAttribute('effects').split(',');
|
31 | if (addEffects.length !== 0) {
|
32 | this.addEffects = utils_1.validateEffects(spec, addEffects, node.parentElement);
|
33 | }
|
34 | }
|
35 | if (node.parentElement.hasAttribute('suppress-effects')) {
|
36 | const suppressEffects = node.parentElement.getAttribute('suppress-effects').split(',');
|
37 | if (suppressEffects.length !== 0) {
|
38 | this.suppressEffects = utils_1.validateEffects(spec, suppressEffects, node.parentElement);
|
39 | }
|
40 | }
|
41 | if (this.addEffects !== null && this.suppressEffects !== null) {
|
42 | for (const e of this.addEffects) {
|
43 | if (this.suppressEffects.includes(e)) {
|
44 | throw new Error('effect suppression is contradictory');
|
45 | }
|
46 | }
|
47 | for (const e of this.suppressEffects) {
|
48 | if (this.addEffects.includes(e)) {
|
49 | throw new Error('effect suppression is contradictory');
|
50 | }
|
51 | }
|
52 | }
|
53 |
|
54 | const children = node.parentElement.childNodes;
|
55 | node.parentElement.replaceWith(...children);
|
56 | }
|
57 | }
|
58 | canHaveEffect(effectName) {
|
59 | if (!this.isInvocation)
|
60 | return false;
|
61 | if (this.clause && !this.clause.canHaveEffect(effectName)) {
|
62 | return false;
|
63 | }
|
64 | if (this.suppressEffects !== null) {
|
65 | return !this.suppressEffects.includes(effectName);
|
66 | }
|
67 | return true;
|
68 | }
|
69 | hasAddedEffect(effectName) {
|
70 | if (!this.isInvocation)
|
71 | return false;
|
72 | if (this.addEffects !== null) {
|
73 | return this.addEffects.includes(effectName);
|
74 | }
|
75 | return false;
|
76 | }
|
77 | static enter({ node, spec, clauseStack }) {
|
78 | return __awaiter(this, void 0, void 0, function* () {
|
79 | const href = node.getAttribute('href');
|
80 | const aoid = node.getAttribute('aoid');
|
81 | const parentClause = clauseStack[clauseStack.length - 1];
|
82 | let namespace;
|
83 | if (node.hasAttribute('namespace')) {
|
84 | namespace = node.getAttribute('namespace');
|
85 | }
|
86 | else {
|
87 | namespace = parentClause ? parentClause.namespace : spec.namespace;
|
88 | }
|
89 | if (href && aoid) {
|
90 | spec.warn({
|
91 | type: 'node',
|
92 | ruleId: 'invalid-xref',
|
93 | message: "xref can't have both href and aoid",
|
94 | node,
|
95 | });
|
96 | return;
|
97 | }
|
98 | if (!href && !aoid) {
|
99 | spec.warn({
|
100 | type: 'node',
|
101 | ruleId: 'invalid-xref',
|
102 | message: 'xref has neither href nor aoid',
|
103 | node,
|
104 | });
|
105 | return;
|
106 | }
|
107 | const xref = new Xref(spec, node, parentClause, namespace, href, aoid);
|
108 | spec._xrefs.push(xref);
|
109 | });
|
110 | }
|
111 | build() {
|
112 | const spec = this.spec;
|
113 | const href = this.href;
|
114 | const node = this.node;
|
115 | const aoid = this.aoid;
|
116 | const namespace = this.namespace;
|
117 | if (href) {
|
118 | if (href[0] !== '#') {
|
119 | spec.warn({
|
120 | type: 'attr-value',
|
121 | attr: 'href',
|
122 | ruleId: 'invalid-xref',
|
123 | message: `xref to anything other than a fragment id is not supported (is ${JSON.stringify(href)}). try href="#sec-id" instead`,
|
124 | node: this.node,
|
125 | });
|
126 | return;
|
127 | }
|
128 | const id = href.slice(1);
|
129 | this.entry = spec.biblio.byId(id);
|
130 | if (!this.entry) {
|
131 | spec.warn({
|
132 | type: 'attr-value',
|
133 | attr: 'href',
|
134 | ruleId: 'xref-not-found',
|
135 | message: `can't find clause, production, note or example with id ${JSON.stringify(id)}`,
|
136 | node: this.node,
|
137 | });
|
138 | return;
|
139 | }
|
140 | switch (this.entry.type) {
|
141 | case 'clause':
|
142 | buildClauseLink(node, this.entry);
|
143 | break;
|
144 | case 'production':
|
145 | buildProductionLink(node, this.entry);
|
146 | break;
|
147 | case 'example':
|
148 | buildFigureLink(spec, this.clause, node, this.entry, 'Example');
|
149 | break;
|
150 | case 'note':
|
151 | buildFigureLink(spec, this.clause, node, this.entry, 'Note');
|
152 | break;
|
153 | case 'table':
|
154 | buildFigureLink(spec, this.clause, node, this.entry, 'Table');
|
155 | break;
|
156 | case 'figure':
|
157 | buildFigureLink(spec, this.clause, node, this.entry, 'Figure');
|
158 | break;
|
159 | case 'term':
|
160 | buildTermLink(node, this.entry);
|
161 | break;
|
162 | case 'step':
|
163 | buildStepLink(spec, node, this.entry);
|
164 | break;
|
165 | default: {
|
166 | spec.warn({
|
167 | type: 'node',
|
168 | ruleId: 'unknown-biblio',
|
169 | message: `found unknown biblio entry ${this.entry.type} (this is a bug, please file it with ecmarkup)`,
|
170 | node: this.node,
|
171 | });
|
172 | }
|
173 | }
|
174 | }
|
175 | else if (aoid) {
|
176 | this.entry = spec.biblio.byAoid(aoid, namespace);
|
177 | if (this.entry) {
|
178 | let effects = null;
|
179 | let classNames = null;
|
180 | if (this.isInvocation) {
|
181 | effects = spec.getEffectsByAoid(aoid);
|
182 | }
|
183 | if (this.addEffects !== null) {
|
184 | if (effects !== null) {
|
185 | effects.push(...this.addEffects);
|
186 | }
|
187 | else {
|
188 | effects = this.addEffects;
|
189 | }
|
190 | }
|
191 | if (effects) {
|
192 | if (this.suppressEffects !== null) {
|
193 | effects = effects.filter(e => !this.suppressEffects.includes(e));
|
194 | }
|
195 | if (effects.length !== 0) {
|
196 | const parentClause = this.clause;
|
197 | effects = parentClause ? effects.filter(e => parentClause.canHaveEffect(e)) : effects;
|
198 | if (effects.length !== 0) {
|
199 | classNames = effects.map(e => `e-${e}`).join(' ');
|
200 | }
|
201 | }
|
202 | }
|
203 | buildAOLink(node, this.entry, classNames);
|
204 | return;
|
205 | }
|
206 | const namespaceSuffix = namespace === '<no location>' ? '' : ` in namespace ${JSON.stringify(namespace)}`;
|
207 | spec.warn({
|
208 | type: 'attr-value',
|
209 | attr: 'aoid',
|
210 | ruleId: 'xref-not-found',
|
211 | message: `can't find abstract op with aoid ${JSON.stringify(aoid)}` + namespaceSuffix,
|
212 | node: this.node,
|
213 | });
|
214 | }
|
215 | }
|
216 | }
|
217 | exports.default = Xref;
|
218 | Xref.elements = ['EMU-XREF'];
|
219 | function buildClauseLink(xref, entry) {
|
220 | if (xref.textContent.trim() === '') {
|
221 | if (xref.hasAttribute('title')) {
|
222 |
|
223 | xref.innerHTML = buildXrefLink(entry, entry.titleHTML || entry.title);
|
224 | }
|
225 | else {
|
226 | xref.innerHTML = buildXrefLink(entry, entry.number);
|
227 | }
|
228 | }
|
229 | else {
|
230 | xref.innerHTML = buildXrefLink(entry, xref.innerHTML);
|
231 | }
|
232 | }
|
233 | function buildProductionLink(xref, entry) {
|
234 | if (xref.textContent.trim() === '') {
|
235 | xref.innerHTML = buildXrefLink(entry, '<emu-nt>' + entry.name + '</emu-nt>');
|
236 | }
|
237 | else {
|
238 | xref.innerHTML = buildXrefLink(entry, xref.innerHTML);
|
239 | }
|
240 | }
|
241 | function buildAOLink(xref, entry, classNames) {
|
242 | if (xref.textContent.trim() === '') {
|
243 | xref.innerHTML = buildXrefLink(entry, xref.getAttribute('aoid'), classNames);
|
244 | }
|
245 | else {
|
246 | xref.innerHTML = buildXrefLink(entry, xref.innerHTML, classNames);
|
247 | }
|
248 | }
|
249 | function buildTermLink(xref, entry) {
|
250 | if (xref.textContent.trim() === '') {
|
251 | xref.innerHTML = buildXrefLink(entry, entry.term);
|
252 | }
|
253 | else {
|
254 | xref.innerHTML = buildXrefLink(entry, xref.innerHTML);
|
255 | }
|
256 | }
|
257 | function buildFigureLink(spec, parentClause, xref, entry, type) {
|
258 | if (xref.textContent.trim() === '') {
|
259 | if (entry.clauseId) {
|
260 |
|
261 | const clauseEntry = spec.biblio.byId(entry.clauseId);
|
262 | if (clauseEntry.type !== 'clause') {
|
263 | spec.warn({
|
264 | type: 'node',
|
265 | ruleId: 'invalid-xref',
|
266 | message: `could not find parent clause for ${type} id ${entry.id}`,
|
267 | node: entry.node,
|
268 | });
|
269 | return;
|
270 | }
|
271 | if (parentClause && parentClause.id === clauseEntry.id) {
|
272 | xref.innerHTML = buildXrefLink(entry, type + ' ' + entry.number);
|
273 | }
|
274 | else {
|
275 | if (xref.hasAttribute('title')) {
|
276 | xref.innerHTML = buildXrefLink(entry, clauseEntry.title + ' ' + type + ' ' + entry.number);
|
277 | }
|
278 | else {
|
279 | xref.innerHTML = buildXrefLink(entry, clauseEntry.number + ' ' + type + ' ' + entry.number);
|
280 | }
|
281 | }
|
282 | }
|
283 | else {
|
284 | xref.innerHTML = buildXrefLink(entry, type + ' ' + entry.number);
|
285 | }
|
286 | }
|
287 | else {
|
288 | xref.innerHTML = buildXrefLink(entry, xref.innerHTML);
|
289 | }
|
290 | }
|
291 | const decimalBullet = Array.from({ length: 100 }).map((a, i) => '' + (i + 1));
|
292 | const alphaBullet = Array.from({ length: 26 }).map((a, i) => String.fromCharCode('a'.charCodeAt(0) + i));
|
293 |
|
294 | const romanBullet = ['i', 'ii', 'iii', 'iv', 'v', 'vi', 'vii', 'viii', 'ix', 'x', 'xi', 'xii', 'xiii', 'xiv', 'xv', 'xvi', 'xvii', 'xviii', 'xix', 'xx', 'xxi', 'xxii', 'xxiii', 'xxiv', 'xxv'];
|
295 | const bullets = [decimalBullet, alphaBullet, romanBullet, decimalBullet, alphaBullet, romanBullet];
|
296 | function buildStepLink(spec, xref, entry) {
|
297 | if (xref.innerHTML !== '') {
|
298 | spec.warn({
|
299 | type: 'contents',
|
300 | ruleId: 'step-xref-contents',
|
301 | message: 'the contents of emu-xrefs to steps are ignored',
|
302 | node: xref,
|
303 | nodeRelativeLine: 1,
|
304 | nodeRelativeColumn: 1,
|
305 | });
|
306 | }
|
307 | const stepBullets = entry.stepNumbers.map((s, i) => {
|
308 | const applicable = bullets[Math.min(i, 5)];
|
309 | if (s > applicable.length) {
|
310 | spec.warn({
|
311 | type: 'attr-value',
|
312 | ruleId: 'high-step-number',
|
313 | message: `ecmarkup does not know how to deal with step numbers as high as ${s}; if you need this, open an issue on ecmarkup`,
|
314 | node: xref,
|
315 | attr: 'href',
|
316 | });
|
317 | return '?';
|
318 | }
|
319 | return applicable[s - 1];
|
320 | });
|
321 | const text = stepBullets.join('.');
|
322 | xref.innerHTML = buildXrefLink(entry, text);
|
323 | }
|
324 | function buildXrefLink(entry, contents, classNames = null) {
|
325 | const classSnippet = classNames == null ? '' : ' class="' + classNames + '"';
|
326 | return `<a href="${entry.location}#${entry.id || entry.refId}"${classSnippet}>${contents}</a>`;
|
327 | }
|