@finbheara/domain 0.31.0 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -0
- package/dist/age.js +73 -688
- package/dist/chunks/place-8k74cr5j.js +655 -0
- package/dist/chunks/place-bhedwqd1.js +139 -0
- package/dist/schedule.js +70 -0
- package/dist/title.js +19 -0
- package/dist/types/schedule.d.ts +21 -0
- package/dist/types/title.d.ts +45 -0
- package/package.json +10 -2
package/dist/age.js
CHANGED
|
@@ -1,691 +1,76 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return false;
|
|
40
|
-
const lo = Number(a);
|
|
41
|
-
const hi = Number(b);
|
|
42
|
-
return lo >= TITLE_RANGE_MIN && hi <= TITLE_RANGE_MAX && lo < hi;
|
|
43
|
-
}
|
|
44
|
-
function readJoined(title, s, offset) {
|
|
45
|
-
const j = JOINED_CAPS.exec(s);
|
|
46
|
-
if (j === null)
|
|
47
|
-
return null;
|
|
48
|
-
const end = j.index + j[0].length;
|
|
49
|
-
const tail = s.slice(end);
|
|
50
|
-
if (JOINED_NOT.test(tail))
|
|
51
|
-
return null;
|
|
52
|
-
const caps = [...j[0].matchAll(/\d{1,2}/g)].map((d) => Number(d[0]));
|
|
53
|
-
if (caps.some((c, i) => i > 0 && c < (caps[i - 1] ?? 0)))
|
|
54
|
-
return null;
|
|
55
|
-
if (JOINED_OPEN.test(tail))
|
|
56
|
-
return NONE;
|
|
57
|
-
return at(title, offset, j.index, end, `u${Math.max(...caps)}`);
|
|
58
|
-
}
|
|
59
|
-
function readAnchored(title, s, offset, aside = false) {
|
|
60
|
-
const joined = readJoined(title, s, offset);
|
|
61
|
-
if (joined !== null)
|
|
62
|
-
return joined;
|
|
63
|
-
for (const re of [AND_OVER, OVER_WORD, OVER_GLUED]) {
|
|
64
|
-
const m = re.exec(s);
|
|
65
|
-
if (m?.[1] !== undefined)
|
|
66
|
-
return at(title, offset, m.index, m.index + m[0].length, `o${Number(m[1])}`);
|
|
67
|
-
}
|
|
68
|
-
const u = AND_UNDER_RANGE.exec(s);
|
|
69
|
-
if (u?.[1] !== undefined && u[2] !== undefined && inFence(u[1], u[2]))
|
|
70
|
-
return at(title, offset, u.index, u.index + u[0].length, `${Number(u[1])}-${Number(u[2])}`);
|
|
71
|
-
const j = aside ? null : AND_RANGE.exec(s);
|
|
72
|
-
if (j?.[1] !== undefined && j[2] !== undefined && inFence(j[1], j[2]))
|
|
73
|
-
return at(title, offset, j.index, j.index + j[0].length, `${Number(j[1])}-${Number(j[2])}`);
|
|
74
|
-
const sl = aside ? null : SLASH_RANGE.exec(s);
|
|
75
|
-
if (sl?.[1] !== undefined && sl[2] !== undefined && inFence(sl[1], sl[2]) && Number(sl[2]) === Number(sl[1]) + 1)
|
|
76
|
-
return at(title, offset, sl.index, sl.index + sl[0].length, `${Number(sl[1])}-${Number(sl[2])}`);
|
|
77
|
-
const l = LEAD_RANGE.exec(s);
|
|
78
|
-
if (l?.[1] !== undefined && l[2] !== undefined && inFence(l[1], l[2])) {
|
|
79
|
-
const lead = l[0].length - l[0].trimStart().length;
|
|
80
|
-
return at(title, offset, lead, l[0].length, `${Number(l[1])}-${Number(l[2])}`);
|
|
81
|
-
}
|
|
82
|
-
const stripped = s.replace(LEAD_ID, "");
|
|
83
|
-
const shift = s.length - stripped.length;
|
|
84
|
-
const r = RANGE.exec(stripped);
|
|
85
|
-
if (r?.[1] !== undefined && r[2] !== undefined && inFence(r[1], r[2]))
|
|
86
|
-
return at(title, offset + shift, r.index, r.index + r[0].length, `${Number(r[1])}-${Number(r[2])}`);
|
|
87
|
-
const au = AND_UNDER.exec(s);
|
|
88
|
-
if (au?.[1] !== undefined)
|
|
89
|
-
return at(title, offset, au.index, au.index + au[0].length, `u${Number(au[1]) + 1}`);
|
|
90
|
-
const m = UNDER.exec(s);
|
|
91
|
-
if (m?.[1] !== undefined) {
|
|
92
|
-
const end = m.index + m[0].length;
|
|
93
|
-
const k = UNDER_STRIDE.exec(s.slice(end));
|
|
94
|
-
if (k?.[1] !== undefined)
|
|
95
|
-
return at(title, offset, m.index, end + k[0].length, `u${Number(m[1])}/${Number(k[1])}yr`);
|
|
96
|
-
return at(title, offset, m.index, end, `u${Number(m[1])}`);
|
|
97
|
-
}
|
|
98
|
-
const y = YEARS.exec(s) ?? AGE_N.exec(s);
|
|
99
|
-
if (y?.[1] !== undefined)
|
|
100
|
-
return at(title, offset, y.index, y.index + y[0].length, `u${Number(y[1]) + 1}`);
|
|
101
|
-
const p = OVER_PLUS.exec(s);
|
|
102
|
-
if (p?.[1] !== undefined)
|
|
103
|
-
return at(title, offset, p.index, p.index + p[0].length, `o${Number(p[1])}`);
|
|
104
|
-
const a = ADULT.exec(s);
|
|
105
|
-
if (a !== null)
|
|
106
|
-
return at(title, offset, a.index, a.index + a[0].length, "adult");
|
|
107
|
-
const n = SENIOR.exec(s);
|
|
108
|
-
if (n !== null)
|
|
109
|
-
return at(title, offset, n.index, n.index + n[0].length, "senior");
|
|
110
|
-
return NONE;
|
|
111
|
-
}
|
|
112
|
-
function ageTitleToken(title) {
|
|
113
|
-
const s = title.trim();
|
|
114
|
-
if (s === "")
|
|
115
|
-
return NONE;
|
|
116
|
-
const lead = title.length - title.trimStart().length;
|
|
117
|
-
PAREN.lastIndex = 0;
|
|
118
|
-
let adultAside = null;
|
|
119
|
-
let adultWord = null;
|
|
120
|
-
for (const m of s.matchAll(PAREN)) {
|
|
121
|
-
const inner = m[1] ?? "";
|
|
122
|
-
const from = lead + (m.index ?? 0) + 1;
|
|
123
|
-
const i = m.index ?? 0;
|
|
124
|
-
const tail = i + m[0].length === s.length && !/[a-z0-9]/i.test(s[i - 1] ?? "");
|
|
125
|
-
const code = tail ? ADULT_CODE.exec(inner) : null;
|
|
126
|
-
if (code !== null) {
|
|
127
|
-
const split = code[1] === undefined ? "" : `${/u/i.test(code[2] ?? "") ? "u" : "o"}${code[1]}`;
|
|
128
|
-
const a = inner.length - inner.trimStart().length;
|
|
129
|
-
adultAside = at(title, from, a, inner.trimEnd().length, adultBeside(split) ?? "adult");
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
if (adultWord !== null)
|
|
133
|
-
continue;
|
|
134
|
-
const t = readAnchored(title, inner, from, true);
|
|
135
|
-
if (t.token === "")
|
|
136
|
-
continue;
|
|
137
|
-
if (t.token === "adult") {
|
|
138
|
-
adultWord = t;
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
return adultOverUnder(title, s, lead, t);
|
|
142
|
-
}
|
|
143
|
-
if (adultWord !== null)
|
|
144
|
-
return adultAside ?? adultWord;
|
|
145
|
-
const words = readAnchored(title, s, lead);
|
|
146
|
-
return adultAside !== null && (words.token === "" || words.token === "adult") ? adultAside : adultOverUnder(title, s, lead, words);
|
|
147
|
-
}
|
|
148
|
-
var ADULT_UNDER_MIN = 18;
|
|
149
|
-
var SENIOR_ADULT_OVER = /\bsenior\s+adult\b/i;
|
|
150
|
-
function adultBeside(band) {
|
|
151
|
-
const b = band.trim().toLowerCase();
|
|
152
|
-
if (b === "")
|
|
153
|
-
return "adult";
|
|
154
|
-
const u = /^u(\d+)(\/\d+yr)?$/.exec(b);
|
|
155
|
-
const n = u?.[1];
|
|
156
|
-
if (n !== undefined) {
|
|
157
|
-
if (Number(n) < ADULT_UNDER_MIN)
|
|
158
|
-
return;
|
|
159
|
-
return Number(n) > ADULT_UNDER_MIN ? `adult u${Number(n)}${u?.[2] ?? ""}` : "adult";
|
|
160
|
-
}
|
|
161
|
-
const floor = /^o(\d+)$/.exec(b)?.[1] ?? /^(\d+)-\d+$/.exec(b)?.[1];
|
|
162
|
-
if (floor === undefined)
|
|
163
|
-
return band;
|
|
164
|
-
return Number(floor) < ADULT_UNDER_MIN ? undefined : `adult ${b}`;
|
|
165
|
-
}
|
|
166
|
-
function adultOverUnder(title, s, lead, t) {
|
|
167
|
-
if (t.token === "")
|
|
168
|
-
return t;
|
|
169
|
-
const a = ADULT.exec(s);
|
|
170
|
-
if (a === null)
|
|
171
|
-
return t;
|
|
172
|
-
if (/^o\d+$/.test(t.token) && SENIOR_ADULT_OVER.test(s))
|
|
173
|
-
return t;
|
|
174
|
-
const token = adultBeside(t.token);
|
|
175
|
-
if (token === undefined || token === t.token)
|
|
176
|
-
return t;
|
|
177
|
-
if (/^u\d+(?:\/\d+yr)?$/.test(t.token))
|
|
178
|
-
return at(title, lead, a.index, a.index + a[0].length, token);
|
|
179
|
-
return { token, span: t.span };
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
var YOUNGEST_FLOOR = 0;
|
|
183
|
-
var ADULT_FLOOR = 18;
|
|
184
|
-
var MAJORS_FLOOR_YEARS = 3;
|
|
185
|
-
var UNDER_CAP = Object.freeze({ plain: 2, exceptional: 4 });
|
|
186
|
-
var UNDER_CAP_MAX_TOP = 21;
|
|
187
|
-
var YOUNG_FLOOR = Object.freeze({ hard: 2, soft: 4 });
|
|
188
|
-
var MIN_MAJOR_AGE = 4;
|
|
189
|
-
var MIN_FEIS_AGE = 3;
|
|
190
|
-
var YOUNGEST_BAND_TOP = MAJORS_FLOOR_YEARS;
|
|
191
|
-
var YOB_RANGE = Object.freeze({ first: 1901, last: 2099 });
|
|
192
|
-
var DANCE_UP_YEARS = 1;
|
|
193
|
-
var ageOnJanuary1 = (year, born) => year - born - 1;
|
|
194
|
-
var yearsSinceBirth = (year, born) => year - born;
|
|
195
|
-
var UNDER2 = /^(?:u|under\s*)(\d+)(?:\/([1-9]\d*)yr)?$/i;
|
|
196
|
-
var OVER = /^(?:o|over\s*)(\d+)$/i;
|
|
197
|
-
var AND_OVER2 = /^(\d+)\s*(?:&|and\b)\s*(?:over|o)$/i;
|
|
198
|
-
var RANGE2 = /^u?(\d+)\s*[-–]\s*(\d+)$/i;
|
|
199
|
-
var ADULT_WORD = /^(?:adults?|ad|advanced|masters?)$/i;
|
|
200
|
-
var ADULT_UNDER = /^(?:adults?|ad)[\s-]*(?:u|under\s*)(\d+)(?:\/([1-9]\d*)yr)?$/i;
|
|
201
|
-
var ADULT_BAND = /^(?:adults?|ad)[\s-]+(\S.*)$/i;
|
|
202
|
-
var SENIOR_WORD = /^seniors?$/i;
|
|
203
|
-
var NO_LADDER = Object.freeze({ caps: [], overs: [], ranges: [] });
|
|
204
|
-
function maxWhere(xs, pred) {
|
|
205
|
-
let best;
|
|
206
|
-
for (const x of xs)
|
|
207
|
-
if (pred(x) && (best === undefined || x > best))
|
|
208
|
-
best = x;
|
|
209
|
-
return best;
|
|
210
|
-
}
|
|
211
|
-
function minWhere(xs, pred) {
|
|
212
|
-
let best;
|
|
213
|
-
for (const x of xs)
|
|
214
|
-
if (pred(x) && (best === undefined || x < best))
|
|
215
|
-
best = x;
|
|
216
|
-
return best;
|
|
217
|
-
}
|
|
218
|
-
function readRange(a, b, ladder) {
|
|
219
|
-
if (!Number.isFinite(a) || !Number.isFinite(b) || b <= a)
|
|
220
|
-
return {
|
|
221
|
-
kind: "range",
|
|
222
|
-
interval: null,
|
|
223
|
-
refusal: "backwards-range",
|
|
224
|
-
clauses: [],
|
|
225
|
-
parts: [a, b]
|
|
226
|
-
};
|
|
227
|
-
if (ladder.ranges.some(([c, d]) => (c !== a || d !== b) && c < b && a < d))
|
|
228
|
-
return {
|
|
229
|
-
kind: "range",
|
|
230
|
-
interval: null,
|
|
231
|
-
refusal: "overlapping-ranges",
|
|
232
|
-
clauses: ["range-overlapping"],
|
|
233
|
-
parts: [a, b]
|
|
234
|
-
};
|
|
235
|
-
const below = maxWhere(ladder.caps, (c) => c < b);
|
|
236
|
-
const adultFloor = below === undefined && ladder.adult === true && b > ADULT_FLOOR;
|
|
237
|
-
const meetsOver = ladder.overs.includes(b + 1) && !ladder.caps.some((c) => c > b);
|
|
238
|
-
const clauses = [
|
|
239
|
-
"range-as-under",
|
|
240
|
-
below !== undefined ? "under-floored-on-rung" : adultFloor ? "range-adult-floor" : "youngest-absorbs"
|
|
241
|
-
];
|
|
242
|
-
if (meetsOver)
|
|
243
|
-
clauses.push("range-top-meets-over");
|
|
244
|
-
return {
|
|
245
|
-
kind: "range",
|
|
246
|
-
interval: {
|
|
247
|
-
lo: below ?? (adultFloor ? ADULT_FLOOR : YOUNGEST_FLOOR),
|
|
248
|
-
hi: meetsOver ? b + 1 : b
|
|
249
|
-
},
|
|
250
|
-
refusal: null,
|
|
251
|
-
clauses,
|
|
252
|
-
parts: [a, b]
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
function strideOf(k) {
|
|
256
|
-
if (k === undefined)
|
|
257
|
-
return null;
|
|
258
|
-
const n = Number(k);
|
|
259
|
-
return Number.isInteger(n) && n >= 1 ? n : null;
|
|
260
|
-
}
|
|
261
|
-
function read(token, ladder, top) {
|
|
262
|
-
const r = RANGE2.exec(token);
|
|
263
|
-
if (r !== null)
|
|
264
|
-
return readRange(Number(r[1]), Number(r[2]), ladder);
|
|
265
|
-
const au = ADULT_UNDER.exec(token);
|
|
266
|
-
if (au !== null && Number(au[1]) > ADULT_FLOOR) {
|
|
267
|
-
const k = strideOf(au[2]);
|
|
268
|
-
const hi = Number(au[1]);
|
|
269
|
-
return {
|
|
270
|
-
kind: "adult",
|
|
271
|
-
interval: { lo: k === null ? ADULT_FLOOR : Math.max(ADULT_FLOOR, hi - k), hi },
|
|
272
|
-
refusal: null,
|
|
273
|
-
clauses: k === null ? ["adult-under-ceiling"] : ["adult-under-ceiling", "under-stride"],
|
|
274
|
-
parts: k === null ? [hi] : [hi, k]
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
const ab = au === null ? ADULT_BAND.exec(token) : null;
|
|
278
|
-
if (ab !== null) {
|
|
279
|
-
const inner = read(ab[1].trim(), { ...ladder, adult: true }, top);
|
|
280
|
-
if (inner.kind === "over" || inner.kind === "range")
|
|
281
|
-
return { ...inner, clauses: [...inner.clauses, "adult-band"], adult: true };
|
|
282
|
-
}
|
|
283
|
-
if (au !== null || ADULT_WORD.test(token))
|
|
284
|
-
return {
|
|
285
|
-
kind: "adult",
|
|
286
|
-
interval: { lo: ADULT_FLOOR, hi: Number.POSITIVE_INFINITY },
|
|
287
|
-
refusal: null,
|
|
288
|
-
clauses: ["and-over-no-ceiling"],
|
|
289
|
-
parts: []
|
|
290
|
-
};
|
|
291
|
-
if (SENIOR_WORD.test(token)) {
|
|
292
|
-
const top = maxWhere(ladder.caps, () => true);
|
|
293
|
-
return top === undefined ? { kind: "senior", interval: null, refusal: "senior-without-ladder", clauses: [], parts: [] } : {
|
|
294
|
-
kind: "senior",
|
|
295
|
-
interval: { lo: top, hi: Number.POSITIVE_INFINITY },
|
|
296
|
-
refusal: null,
|
|
297
|
-
clauses: ["and-over-no-ceiling"],
|
|
298
|
-
parts: []
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
const u = UNDER2.exec(token);
|
|
302
|
-
if (u !== null) {
|
|
303
|
-
const n = Number(u[1]);
|
|
304
|
-
const k = strideOf(u[2]);
|
|
305
|
-
if (k !== null)
|
|
306
|
-
return {
|
|
307
|
-
kind: "under",
|
|
308
|
-
interval: { lo: Math.max(YOUNGEST_FLOOR, n - k), hi: n },
|
|
309
|
-
refusal: null,
|
|
310
|
-
clauses: ["under-stride"],
|
|
311
|
-
parts: [n, k]
|
|
312
|
-
};
|
|
313
|
-
const below = maxWhere(ladder.caps, (c) => c < n);
|
|
314
|
-
return {
|
|
315
|
-
kind: "under",
|
|
316
|
-
interval: { lo: below ?? YOUNGEST_FLOOR, hi: n },
|
|
317
|
-
refusal: null,
|
|
318
|
-
clauses: [below === undefined ? "youngest-absorbs" : "under-floored-on-rung"],
|
|
319
|
-
parts: [n]
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
const o = OVER.exec(token) ?? AND_OVER2.exec(token);
|
|
323
|
-
if (o !== null) {
|
|
324
|
-
const n = Number(o[1]);
|
|
325
|
-
const above = top ? undefined : minWhere(ladder.overs, (c) => c > n);
|
|
326
|
-
if (above === undefined)
|
|
327
|
-
return {
|
|
328
|
-
kind: "over",
|
|
329
|
-
interval: { lo: n, hi: Number.POSITIVE_INFINITY },
|
|
330
|
-
refusal: null,
|
|
331
|
-
clauses: ["and-over-no-ceiling"],
|
|
332
|
-
parts: [n]
|
|
333
|
-
};
|
|
334
|
-
const gapped = ladder.caps.length > 0 && !ladder.caps.some((c) => c > n && c < above);
|
|
335
|
-
return {
|
|
336
|
-
kind: "over",
|
|
337
|
-
interval: { lo: n, hi: gapped ? Number.POSITIVE_INFINITY : above },
|
|
338
|
-
refusal: null,
|
|
339
|
-
clauses: [gapped ? "over-ceiling-refused-gap" : "over-ceiling-from-next-over"],
|
|
340
|
-
parts: [n]
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
return { kind: null, interval: null, refusal: "not-a-band", clauses: [], parts: [] };
|
|
344
|
-
}
|
|
345
|
-
function refuseBelowYoungest(r) {
|
|
346
|
-
if (r.interval === null || r.interval.hi >= YOUNGEST_BAND_TOP)
|
|
347
|
-
return r;
|
|
348
|
-
return { ...r, interval: null, refusal: "below-youngest-band", clauses: [] };
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
class AgeMisuseError extends Error {
|
|
352
|
-
misuse;
|
|
353
|
-
constructor(misuse, detail) {
|
|
354
|
-
super(`age: ${misuse}: ${detail}`);
|
|
355
|
-
this.name = "AgeMisuseError";
|
|
356
|
-
this.misuse = misuse;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
var display = (iv) => `[${iv.lo},${iv.hi === Number.POSITIVE_INFINITY ? "∞" : iv.hi})`;
|
|
360
|
-
var currentYear = () => new Date().getUTCFullYear();
|
|
361
|
-
function withRelations(a) {
|
|
362
|
-
const self = a;
|
|
363
|
-
return Object.assign(self, {
|
|
364
|
-
fitsIn: (container) => {
|
|
365
|
-
if (isAveraged(container))
|
|
366
|
-
throw new AgeMisuseError("average-as-container", `${self.ag2} cannot fit inside an average age (${String(container)}); an average is not a dancer`);
|
|
367
|
-
const r = ageRelation(self, container);
|
|
368
|
-
return r.relation === "same" || r.reason === "b-contains-a";
|
|
369
|
-
},
|
|
370
|
-
overlaps: (other) => {
|
|
371
|
-
const r = ageRelation(self, other).relation;
|
|
372
|
-
return r === "same" || r === "contains" || r === "overlaps";
|
|
373
|
-
}
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
function fromPrint(print, context = {}) {
|
|
377
|
-
const ladder = context.ladder ?? NO_LADDER;
|
|
378
|
-
const year = context.year === undefined ? currentYear() : context.year;
|
|
379
|
-
const ag1 = print.trim().toLowerCase();
|
|
380
|
-
const reading = ag1 === "" ? { kind: null, interval: null, refusal: null, clauses: [], parts: [] } : refuseBelowYoungest(read(print.trim(), ladder, context.top === true));
|
|
381
|
-
const iv = reading.interval;
|
|
382
|
-
const arm = ag1 === "" ? "empty" : iv === null ? "open" : "band";
|
|
383
|
-
const ag2 = iv !== null ? display(iv) : ag1 === "" ? "" : `${ag1}?`;
|
|
384
|
-
const source = sourceOf(reading);
|
|
385
|
-
return withRelations({
|
|
386
|
-
[Symbol.toStringTag]: "Age",
|
|
387
|
-
toString: () => ag2,
|
|
388
|
-
arm,
|
|
389
|
-
kind: reading.kind,
|
|
390
|
-
ag0: print,
|
|
391
|
-
ag1,
|
|
392
|
-
ag2,
|
|
393
|
-
parts: reading.parts,
|
|
394
|
-
length: ag2.length,
|
|
395
|
-
refusal: reading.refusal,
|
|
396
|
-
...validity(arm === "empty", reading.refusal),
|
|
397
|
-
source,
|
|
398
|
-
clauses: reading.clauses,
|
|
399
|
-
ladder,
|
|
400
|
-
year,
|
|
401
|
-
isAge: () => iv !== null,
|
|
402
|
-
isExact: () => {
|
|
403
|
-
if (iv === null || year === null)
|
|
404
|
-
return false;
|
|
405
|
-
const b = birthYearsOfInterval(iv, year);
|
|
406
|
-
return b.from !== null && b.from === b.to;
|
|
407
|
-
},
|
|
408
|
-
isAndOver: () => iv !== null && iv.hi === Number.POSITIVE_INFINITY,
|
|
409
|
-
isAdult: () => reading.kind === "adult" || reading.adult === true,
|
|
410
|
-
interval: () => iv,
|
|
411
|
-
birthYears: (y) => {
|
|
412
|
-
const at = y ?? year;
|
|
413
|
-
if (at === null)
|
|
414
|
-
throw new Error(`Age.from(${JSON.stringify(print)}): birthYears needs a competition year`);
|
|
415
|
-
return birthYearsOfInterval(iv, at);
|
|
416
|
-
},
|
|
417
|
-
floorObserved: () => reading.kind !== "under" && reading.kind !== "range" || iv === null ? null : reading.clauses.includes("under-floored-on-rung") || reading.clauses.includes("range-adult-floor") || reading.clauses.includes("under-stride")
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
|
-
function fromYOB(from, to = from, context = {}) {
|
|
421
|
-
for (const y of [from, to])
|
|
422
|
-
if (y !== null && !Number.isInteger(y))
|
|
423
|
-
throw new RangeError(`Age.fromYOB(${from}, ${to}): a year of birth is a whole number`);
|
|
424
|
-
if (from === null && to === null)
|
|
425
|
-
throw new RangeError("Age.fromYOB(null, null): at least one end is a year");
|
|
426
|
-
if (from !== null && to !== null && from > to)
|
|
427
|
-
throw new RangeError(`Age.fromYOB(${from}, ${to}): the earliest year comes first`);
|
|
428
|
-
const at = context.year ?? null;
|
|
429
|
-
const valid = [from, to].every((y) => y === null || y >= YOB_RANGE.first && y <= YOB_RANGE.last) && (at === null || to === null || yearsSinceBirth(at, to) >= YOUNG_FLOOR.hard);
|
|
430
|
-
const refusal = valid ? null : "year-out-of-range";
|
|
431
|
-
const exact = valid && from !== null && from === to;
|
|
432
|
-
const ag0 = from !== null && from === to ? String(from) : `${from ?? ""}..${to ?? ""}`;
|
|
433
|
-
const ag2 = valid ? `b${ag0}` : `b${ag0}?`;
|
|
434
|
-
const years = Object.freeze(valid ? { from, to } : { from: null, to: null });
|
|
435
|
-
return withRelations({
|
|
436
|
-
[Symbol.toStringTag]: "Age",
|
|
437
|
-
toString: () => ag2,
|
|
438
|
-
arm: valid ? "yob" : "open",
|
|
439
|
-
kind: "yob",
|
|
440
|
-
ag0,
|
|
441
|
-
ag1: ag0,
|
|
442
|
-
ag2,
|
|
443
|
-
parts: from !== null && from === to ? [from] : [from, to].filter((y) => y !== null),
|
|
444
|
-
length: ag2.length,
|
|
445
|
-
refusal,
|
|
446
|
-
...validity(false, refusal),
|
|
447
|
-
source: valid ? "printed-yob" : "open",
|
|
448
|
-
clauses: [],
|
|
449
|
-
ladder: NO_LADDER,
|
|
450
|
-
year: null,
|
|
451
|
-
isAge: () => valid,
|
|
452
|
-
isExact: () => exact,
|
|
453
|
-
isAndOver: () => valid && from === null,
|
|
454
|
-
isAdult: () => false,
|
|
455
|
-
interval: () => null,
|
|
456
|
-
birthYears: () => years,
|
|
457
|
-
floorObserved: () => null
|
|
458
|
-
});
|
|
459
|
-
}
|
|
460
|
-
function fromTitle(title, context = {}) {
|
|
461
|
-
const t = ageTitleToken(title);
|
|
462
|
-
return Object.freeze({ age: fromPrint(t.token, context), token: t.token, span: t.span });
|
|
463
|
-
}
|
|
464
|
-
var Age = Object.freeze({ from: fromPrint, fromYOB, fromTitle });
|
|
465
|
-
function sourceOf(r) {
|
|
466
|
-
const iv = r.interval;
|
|
467
|
-
if (iv === null)
|
|
468
|
-
return "open";
|
|
469
|
-
switch (r.kind) {
|
|
470
|
-
case "range":
|
|
471
|
-
case "under":
|
|
472
|
-
if (r.clauses.includes("under-stride"))
|
|
473
|
-
return "printed-band";
|
|
474
|
-
return iv.lo > YOUNGEST_FLOOR ? "sibling-narrowed" : "printed-band";
|
|
475
|
-
case "over":
|
|
476
|
-
return iv.hi === Number.POSITIVE_INFINITY ? "printed-band" : "sibling-narrowed";
|
|
477
|
-
case "adult":
|
|
478
|
-
case "senior":
|
|
479
|
-
return "printed-band";
|
|
480
|
-
default:
|
|
481
|
-
return "open";
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
function birthYearsOfInterval(iv, year) {
|
|
485
|
-
if (iv === null)
|
|
486
|
-
return { from: null, to: null };
|
|
487
|
-
return {
|
|
488
|
-
from: iv.hi === Number.POSITIVE_INFINITY ? null : year - iv.hi,
|
|
489
|
-
to: Math.min(year - iv.lo - 1, year - MAJORS_FLOOR_YEARS)
|
|
490
|
-
};
|
|
491
|
-
}
|
|
492
|
-
var inBirthYears = (a) => a.kind === "yob" || a.year !== null;
|
|
493
|
-
function spanOf(a) {
|
|
494
|
-
if (!a.isAge())
|
|
495
|
-
return null;
|
|
496
|
-
if (inBirthYears(a)) {
|
|
497
|
-
const b = a.birthYears();
|
|
498
|
-
return {
|
|
499
|
-
lo: b.from ?? Number.NEGATIVE_INFINITY,
|
|
500
|
-
hi: b.to ?? Number.POSITIVE_INFINITY
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
const iv = a.interval();
|
|
504
|
-
return { lo: iv.lo, hi: iv.hi - 1 };
|
|
505
|
-
}
|
|
506
|
-
function ageRelation(a, b) {
|
|
507
|
-
for (const x of [a, b])
|
|
508
|
-
if (isAveraged(x))
|
|
509
|
-
throw new AgeMisuseError("average-in-relation", `${String(x)} is an average, not a dancer's birth years; ask its fitsIn`);
|
|
510
|
-
if (inBirthYears(a) !== inBirthYears(b))
|
|
511
|
-
throw new Error("ageRelation: one reading is in birth years and the other has no competition year; read both with a year, or neither");
|
|
512
|
-
if (!a.isAge())
|
|
513
|
-
return { relation: "silent", reason: "a-not-a-band" };
|
|
514
|
-
if (!b.isAge())
|
|
515
|
-
return { relation: "silent", reason: "b-not-a-band" };
|
|
516
|
-
const sa = spanOf(a);
|
|
517
|
-
const sb = spanOf(b);
|
|
518
|
-
const years = inBirthYears(a);
|
|
519
|
-
if (sa.hi < sb.lo)
|
|
520
|
-
return { relation: "disjoint", reason: years ? "a-older" : "a-younger" };
|
|
521
|
-
if (sb.hi < sa.lo)
|
|
522
|
-
return { relation: "disjoint", reason: years ? "a-younger" : "a-older" };
|
|
523
|
-
if (sa.lo === sb.lo && sa.hi === sb.hi)
|
|
524
|
-
return { relation: "same", reason: "equal" };
|
|
525
|
-
if (sa.lo <= sb.lo && sb.hi <= sa.hi)
|
|
526
|
-
return { relation: "contains", reason: "a-contains-b" };
|
|
527
|
-
if (sb.lo <= sa.lo && sa.hi <= sb.hi)
|
|
528
|
-
return { relation: "contains", reason: "b-contains-a" };
|
|
529
|
-
return { relation: "overlaps", reason: "partial" };
|
|
530
|
-
}
|
|
531
|
-
function canOverlap(...ages) {
|
|
532
|
-
for (let i = 0;i < ages.length; i++)
|
|
533
|
-
for (let j = i + 1;j < ages.length; j++)
|
|
534
|
-
if (ageRelation(ages[i], ages[j]).relation === "disjoint")
|
|
535
|
-
return false;
|
|
536
|
-
return true;
|
|
537
|
-
}
|
|
538
|
-
var AVERAGE_AGE_DECIMALS = 2;
|
|
539
|
-
var HUNDREDTHS = 10 ** AVERAGE_AGE_DECIMALS;
|
|
540
|
-
function roundedMeanHundredths(k, n) {
|
|
541
|
-
if (!Number.isInteger(k) || !Number.isInteger(n) || n <= 0)
|
|
542
|
-
throw new RangeError(`roundedMeanHundredths(${k}, ${n}): whole numbers, n > 0`);
|
|
543
|
-
return Math.floor((2 * HUNDREDTHS * k + n) / (2 * n));
|
|
544
|
-
}
|
|
545
|
-
function isAveraged(a) {
|
|
546
|
-
return a[Symbol.toStringTag] === "AverageAge";
|
|
547
|
-
}
|
|
548
|
-
function averageAge(ages) {
|
|
549
|
-
if (ages.length === 0)
|
|
550
|
-
throw new AgeMisuseError("empty-average", "averageAge needs at least one exact age");
|
|
551
|
-
let sum = 0;
|
|
552
|
-
for (const [i, a] of ages.entries()) {
|
|
553
|
-
if (isAveraged(a))
|
|
554
|
-
throw new AgeMisuseError("average-of-averages", `input ${i} (${String(a)}) is already an average; average the members instead`);
|
|
555
|
-
if (!a.isExact())
|
|
556
|
-
throw new AgeMisuseError("not-exact", `input ${i} (${String(a)}) is not one birth year; an average takes exact ages only`);
|
|
557
|
-
sum += a.birthYears().from;
|
|
558
|
-
}
|
|
559
|
-
const n = ages.length;
|
|
560
|
-
const members = Object.freeze([...ages]);
|
|
561
|
-
const hundredths = (year) => roundedMeanHundredths(n * (year - 1) - sum, n);
|
|
562
|
-
const self = {
|
|
563
|
-
[Symbol.toStringTag]: "AverageAge",
|
|
564
|
-
provenance: `average of ${n}`,
|
|
565
|
-
members,
|
|
566
|
-
n,
|
|
567
|
-
birthYearSum: sum,
|
|
568
|
-
meanBirthYear: sum / n,
|
|
569
|
-
toString: () => `avg(${n}):b${sum / n}`,
|
|
570
|
-
isExact: () => false,
|
|
571
|
-
meanAgeHundredths: hundredths,
|
|
572
|
-
meanAge: (year) => hundredths(year) / HUNDREDTHS,
|
|
573
|
-
fitsIn: (container) => {
|
|
574
|
-
if (isAveraged(container))
|
|
575
|
-
throw new AgeMisuseError("average-as-container", `nothing fits inside an average age (${String(container)})`);
|
|
576
|
-
if (container.year === null)
|
|
577
|
-
throw new AgeMisuseError("average-container-no-year", `${String(container)} has no competition year to take the average age in`);
|
|
578
|
-
const iv = container.interval();
|
|
579
|
-
if (iv === null)
|
|
580
|
-
return false;
|
|
581
|
-
const h = hundredths(container.year);
|
|
582
|
-
return iv.lo * HUNDREDTHS <= h && h < iv.hi * HUNDREDTHS;
|
|
583
|
-
},
|
|
584
|
-
birthYears: () => {
|
|
585
|
-
throw new AgeMisuseError("average-birth-years", `an average of ${n} has no year of birth; read meanBirthYear, which is not a dancer's`);
|
|
586
|
-
}
|
|
587
|
-
};
|
|
588
|
-
return self;
|
|
589
|
-
}
|
|
590
|
-
function birthYearsDisjoint(a, b) {
|
|
591
|
-
if (a.to !== null && b.from !== null && a.to < b.from)
|
|
592
|
-
return true;
|
|
593
|
-
if (b.to !== null && a.from !== null && b.to < a.from)
|
|
594
|
-
return true;
|
|
595
|
-
return false;
|
|
596
|
-
}
|
|
597
|
-
function intersectBirthYears(windows) {
|
|
598
|
-
let from = null;
|
|
599
|
-
let to = null;
|
|
600
|
-
let n = 0;
|
|
601
|
-
for (const w of windows) {
|
|
602
|
-
if (w.from === null && w.to === null)
|
|
603
|
-
continue;
|
|
604
|
-
n++;
|
|
605
|
-
if (w.from !== null)
|
|
606
|
-
from = from === null ? w.from : Math.max(from, w.from);
|
|
607
|
-
if (w.to !== null)
|
|
608
|
-
to = to === null ? w.to : Math.min(to, w.to);
|
|
609
|
-
}
|
|
610
|
-
if (n === 0)
|
|
611
|
-
return { from: null, to: null, n: 0 };
|
|
612
|
-
if (from !== null && to !== null && from > to)
|
|
613
|
-
return null;
|
|
614
|
-
return { from, to, n };
|
|
615
|
-
}
|
|
616
|
-
function birthYearsMeet(card, other, slack = 0) {
|
|
617
|
-
if (card.from !== null && other.to !== null && card.from > other.to)
|
|
618
|
-
return false;
|
|
619
|
-
if (card.to !== null && other.from !== null && card.to + slack < other.from)
|
|
620
|
-
return false;
|
|
621
|
-
return true;
|
|
622
|
-
}
|
|
623
|
-
function underCapApplies(bandFrom, year) {
|
|
624
|
-
return bandFrom !== null && year - bandFrom <= UNDER_CAP_MAX_TOP;
|
|
625
|
-
}
|
|
626
|
-
function underCappedYears(b, year, n = UNDER_CAP.plain) {
|
|
627
|
-
if (n > UNDER_CAP.exceptional)
|
|
628
|
-
throw new Error(`under-cap: ${n} is over the exceptional cap of ${UNDER_CAP.exceptional}`);
|
|
629
|
-
if (!underCapApplies(b.from, year))
|
|
630
|
-
return b;
|
|
631
|
-
const cap = b.from + n;
|
|
632
|
-
if (b.to !== null && b.to <= cap)
|
|
633
|
-
return b;
|
|
634
|
-
return { from: b.from, to: cap };
|
|
635
|
-
}
|
|
636
|
-
function underCapRefuses(bandFrom, identityFrom, n) {
|
|
637
|
-
return identityFrom > bandFrom + n;
|
|
638
|
-
}
|
|
639
|
-
function validYOB(y, year) {
|
|
640
|
-
if (y === null || y === undefined || !Number.isInteger(y))
|
|
641
|
-
return null;
|
|
642
|
-
return Age.fromYOB(y, y, { year: year ?? null }).isValid() ? y : null;
|
|
643
|
-
}
|
|
644
|
-
function youngFloor(year, oldest) {
|
|
645
|
-
const s = yearsSinceBirth(year, oldest);
|
|
646
|
-
if (s >= YOUNG_FLOOR.soft)
|
|
647
|
-
return "clear";
|
|
648
|
-
if (s < YOUNG_FLOOR.hard)
|
|
649
|
-
return "too-young";
|
|
650
|
-
return "same-school-only";
|
|
651
|
-
}
|
|
652
|
-
var tooYoungForMajor = (year, oldest) => yearsSinceBirth(year, oldest) < MIN_MAJOR_AGE;
|
|
653
|
-
var tooYoungForFeis = (year, oldest) => yearsSinceBirth(year, oldest) < MIN_FEIS_AGE;
|
|
2
|
+
TITLE_RANGE_MIN2,
|
|
3
|
+
TITLE_RANGE_MAX2,
|
|
4
|
+
ageTitleToken2,
|
|
5
|
+
adultBeside2,
|
|
6
|
+
YOUNGEST_FLOOR2,
|
|
7
|
+
ADULT_FLOOR2,
|
|
8
|
+
MAJORS_FLOOR_YEARS2,
|
|
9
|
+
UNDER_CAP2,
|
|
10
|
+
UNDER_CAP_MAX_TOP2,
|
|
11
|
+
YOUNG_FLOOR2,
|
|
12
|
+
MIN_MAJOR_AGE2,
|
|
13
|
+
MIN_FEIS_AGE2,
|
|
14
|
+
YOUNGEST_BAND_TOP2,
|
|
15
|
+
YOB_RANGE2,
|
|
16
|
+
DANCE_UP_YEARS2,
|
|
17
|
+
ageOnJanuary12,
|
|
18
|
+
yearsSinceBirth2,
|
|
19
|
+
AgeMisuseError2,
|
|
20
|
+
Age2,
|
|
21
|
+
birthYearsOfInterval2,
|
|
22
|
+
ageRelation2,
|
|
23
|
+
canOverlap2,
|
|
24
|
+
AVERAGE_AGE_DECIMALS2,
|
|
25
|
+
roundedMeanHundredths2,
|
|
26
|
+
isAveraged2,
|
|
27
|
+
averageAge2,
|
|
28
|
+
birthYearsDisjoint2,
|
|
29
|
+
intersectBirthYears2,
|
|
30
|
+
birthYearsMeet2,
|
|
31
|
+
underCapApplies2,
|
|
32
|
+
underCappedYears2,
|
|
33
|
+
underCapRefuses2,
|
|
34
|
+
validYOB2,
|
|
35
|
+
youngFloor2,
|
|
36
|
+
tooYoungForMajor2,
|
|
37
|
+
tooYoungForFeis2
|
|
38
|
+
} from "./chunks/place-8k74cr5j.js";
|
|
654
39
|
export {
|
|
655
|
-
ADULT_FLOOR,
|
|
656
|
-
AVERAGE_AGE_DECIMALS,
|
|
657
|
-
Age,
|
|
658
|
-
AgeMisuseError,
|
|
659
|
-
DANCE_UP_YEARS,
|
|
660
|
-
MAJORS_FLOOR_YEARS,
|
|
661
|
-
MIN_FEIS_AGE,
|
|
662
|
-
MIN_MAJOR_AGE,
|
|
663
|
-
TITLE_RANGE_MAX,
|
|
664
|
-
TITLE_RANGE_MIN,
|
|
665
|
-
UNDER_CAP,
|
|
666
|
-
UNDER_CAP_MAX_TOP,
|
|
667
|
-
YOB_RANGE,
|
|
668
|
-
YOUNGEST_BAND_TOP,
|
|
669
|
-
YOUNGEST_FLOOR,
|
|
670
|
-
YOUNG_FLOOR,
|
|
671
|
-
adultBeside,
|
|
672
|
-
ageOnJanuary1,
|
|
673
|
-
ageRelation,
|
|
674
|
-
ageTitleToken,
|
|
675
|
-
averageAge,
|
|
676
|
-
birthYearsDisjoint,
|
|
677
|
-
birthYearsMeet,
|
|
678
|
-
birthYearsOfInterval,
|
|
679
|
-
canOverlap,
|
|
680
|
-
intersectBirthYears,
|
|
681
|
-
isAveraged,
|
|
682
|
-
roundedMeanHundredths,
|
|
683
|
-
tooYoungForFeis,
|
|
684
|
-
tooYoungForMajor,
|
|
685
|
-
underCapApplies,
|
|
686
|
-
underCapRefuses,
|
|
687
|
-
underCappedYears,
|
|
688
|
-
validYOB,
|
|
689
|
-
yearsSinceBirth,
|
|
690
|
-
youngFloor
|
|
40
|
+
ADULT_FLOOR2 as ADULT_FLOOR,
|
|
41
|
+
AVERAGE_AGE_DECIMALS2 as AVERAGE_AGE_DECIMALS,
|
|
42
|
+
Age2 as Age,
|
|
43
|
+
AgeMisuseError2 as AgeMisuseError,
|
|
44
|
+
DANCE_UP_YEARS2 as DANCE_UP_YEARS,
|
|
45
|
+
MAJORS_FLOOR_YEARS2 as MAJORS_FLOOR_YEARS,
|
|
46
|
+
MIN_FEIS_AGE2 as MIN_FEIS_AGE,
|
|
47
|
+
MIN_MAJOR_AGE2 as MIN_MAJOR_AGE,
|
|
48
|
+
TITLE_RANGE_MAX2 as TITLE_RANGE_MAX,
|
|
49
|
+
TITLE_RANGE_MIN2 as TITLE_RANGE_MIN,
|
|
50
|
+
UNDER_CAP2 as UNDER_CAP,
|
|
51
|
+
UNDER_CAP_MAX_TOP2 as UNDER_CAP_MAX_TOP,
|
|
52
|
+
YOB_RANGE2 as YOB_RANGE,
|
|
53
|
+
YOUNGEST_BAND_TOP2 as YOUNGEST_BAND_TOP,
|
|
54
|
+
YOUNGEST_FLOOR2 as YOUNGEST_FLOOR,
|
|
55
|
+
YOUNG_FLOOR2 as YOUNG_FLOOR,
|
|
56
|
+
adultBeside2 as adultBeside,
|
|
57
|
+
ageOnJanuary12 as ageOnJanuary1,
|
|
58
|
+
ageRelation2 as ageRelation,
|
|
59
|
+
ageTitleToken2 as ageTitleToken,
|
|
60
|
+
averageAge2 as averageAge,
|
|
61
|
+
birthYearsDisjoint2 as birthYearsDisjoint,
|
|
62
|
+
birthYearsMeet2 as birthYearsMeet,
|
|
63
|
+
birthYearsOfInterval2 as birthYearsOfInterval,
|
|
64
|
+
canOverlap2 as canOverlap,
|
|
65
|
+
intersectBirthYears2 as intersectBirthYears,
|
|
66
|
+
isAveraged2 as isAveraged,
|
|
67
|
+
roundedMeanHundredths2 as roundedMeanHundredths,
|
|
68
|
+
tooYoungForFeis2 as tooYoungForFeis,
|
|
69
|
+
tooYoungForMajor2 as tooYoungForMajor,
|
|
70
|
+
underCapApplies2 as underCapApplies,
|
|
71
|
+
underCapRefuses2 as underCapRefuses,
|
|
72
|
+
underCappedYears2 as underCappedYears,
|
|
73
|
+
validYOB2 as validYOB,
|
|
74
|
+
yearsSinceBirth2 as yearsSinceBirth,
|
|
75
|
+
youngFloor2 as youngFloor
|
|
691
76
|
};
|