@finbheara/domain 0.5.0 → 0.6.2
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 +41 -2
- package/dist/age.js +143 -24
- package/dist/chunks/{place-0j6k50wf.js → place-h7tvdfyp.js} +548 -29
- package/dist/place.js +1 -1
- package/dist/promotion.js +22 -2
- package/dist/school.js +12 -128
- package/dist/types/age-title.d.ts +12 -0
- package/dist/types/age-types.d.ts +5 -3
- package/dist/types/age.d.ts +11 -2
- package/dist/types/place-types.d.ts +5 -2
- package/dist/types/promotion.d.ts +2 -0
- package/dist/types/school-location.d.ts +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ ESM, Node ≥ 22 or Bun ≥ 1.4.2. MIT. Not affiliated with or endorsed by any d
|
|
|
39
39
|
| `@finbheara/domain/tune` | `tune()`, `tuneRelation`, `tempoLegal`, `settingAt`, `TUNE_NODES`: the CLRG set dance list, each tune with its time, speeds and bars |
|
|
40
40
|
| `@finbheara/domain/ceili` | `ceili()`, `ceiliRelation`, `CEILI_NODES`: the céilí handbook dances |
|
|
41
41
|
| `@finbheara/domain/dance` | `dance()`: a print read as a tune and a céilí dance, narrowed by its round |
|
|
42
|
-
| `@finbheara/domain/age` | `Age.from`, `Age.fromYOB`, `ageRelation`, `canOverlap`, `averageAge`, `isAveraged`, `intersectBirthYears`, `validYOB`, the age constants |
|
|
42
|
+
| `@finbheara/domain/age` | `Age.from`, `Age.fromYOB`, `Age.fromTitle`, `ageTitleToken`, `ageRelation`, `canOverlap`, `averageAge`, `isAveraged`, `intersectBirthYears`, `validYOB`, the age constants |
|
|
43
43
|
| `@finbheara/domain/noun-index` | `createIndex`, `indexSchema`, `personEncoder`, and the `SqlHandle`/`NounEncoder` types: a database-backed name map (below) |
|
|
44
44
|
| `@finbheara/domain/dance-types` | `danceTypes()`, `DANCE_TYPES`, `rhythmOfTime`, and the shared `DanceMatch`/`DanceRelation`/`SetRhythm`/`TimeSignature` types |
|
|
45
45
|
| `@finbheara/domain/promotion` | `samePromotedRow`, `samePromotedSchool`, `samePromotedHeir`, `samePromotedName`, `questionMarksRead`, `foldCharSynonyms`: is a row the same row after a promotion (below) |
|
|
@@ -56,7 +56,7 @@ questions:
|
|
|
56
56
|
| method | true when |
|
|
57
57
|
|---|---|
|
|
58
58
|
| `isEmpty()` | nothing was printed: an empty or blank cell, no dance named |
|
|
59
|
-
| `isRefused()` | something was printed and the type declines it: a placeholder, a school in a place field, `U2`, a `1900` year of birth |
|
|
59
|
+
| `isRefused()` | something was printed and the type declines it: a placeholder, a school in a place field, a URL or an online event in a place field, `U2`, a `1900` year of birth |
|
|
60
60
|
| `isValid()` | neither: `!isEmpty() && !isRefused()` |
|
|
61
61
|
|
|
62
62
|
`invalidReason()` is `null` when the value is valid, `"empty"` when it is empty, and the
|
|
@@ -71,6 +71,7 @@ Valid does not mean usable. A print the reader does not know but cannot rule out
|
|
|
71
71
|
place("").invalidReason(); // "empty"
|
|
72
72
|
place("n/a").invalidReason(); // "placeholder"
|
|
73
73
|
place("Ardmore").isValid(); // true: valid, unknown
|
|
74
|
+
place("Video Feis").invalidReason(); // "online"
|
|
74
75
|
DEFAULT_SCHOOL_NAME_READER.schoolName("N/A").isRefused(); // true
|
|
75
76
|
Age.fromYOB(1900).invalidReason(); // "year-out-of-range"
|
|
76
77
|
tune("").isEmpty(); // true
|
|
@@ -102,6 +103,13 @@ A `Place` is one of four arms: `resolved`, `plural` (a print naming more than on
|
|
|
102
103
|
`ambiguous` (one print, several readings) and `unread`. Only `conflict` between two readings
|
|
103
104
|
says they cannot be one place; `same`, `contains` and `overlaps` do not.
|
|
104
105
|
|
|
106
|
+
A print that is clearly not a place is refused, with its reason: a placeholder
|
|
107
|
+
(`[address withheld]`, `Other`), a URL anywhere in the print (`web`), an online event in the
|
|
108
|
+
venue field (`online`), a change-of-venue notice (`notice`), or a replacement character where
|
|
109
|
+
the source lost bytes (`damaged`). These are asked only where the store reads nothing, so a
|
|
110
|
+
refusal never takes a reading away. Mojibake that can be decoded back is not refused: repair it
|
|
111
|
+
before reading.
|
|
112
|
+
|
|
105
113
|
## Find a CLRG region
|
|
106
114
|
|
|
107
115
|
```ts
|
|
@@ -234,8 +242,39 @@ Age.fromYOB(1999).fitsIn(Age.from("U18", { year: 2025 })); // fals
|
|
|
234
242
|
Age.fromYOB(1999).fitsIn(Age.from("Over 21", { year: 2026, top: true })); // true
|
|
235
243
|
|
|
236
244
|
canOverlap(u12, Age.from("U14", { year: 2026 }), dancer); // true
|
|
245
|
+
|
|
246
|
+
Age.from("13 & Over", { year: 2026 }).interval(); // { lo: 13, hi: Infinity }, as "Over 13"
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
A competition title usually carries its age group in its words. `Age.fromTitle` finds the
|
|
250
|
+
phrase, reads it as `Age.from` would, and says where it was. A title that names no age group
|
|
251
|
+
is empty, not refused. Competition numbers are never read as ages.
|
|
252
|
+
|
|
253
|
+
```ts
|
|
254
|
+
const t = Age.fromTitle("Novice Hornpipe Under 10", { year: 2026 });
|
|
255
|
+
t.token; // "u10"
|
|
256
|
+
t.span; // { start: 16, end: 24, text: "Under 10" }
|
|
257
|
+
t.age.birthYears(); // { from: 2016, to: 2023 }
|
|
258
|
+
|
|
259
|
+
Age.fromTitle("Open Championship 15-16", { year: 2026 }).token; // "15-16"
|
|
260
|
+
Age.fromTitle("Competition 719OC - Open Championship (O18)").token; // "o18", not "o9"
|
|
261
|
+
Age.fromTitle("Treble Reel Special", { year: 2026 }).age.isEmpty(); // true
|
|
262
|
+
Age.fromTitle("Mixed Reel 7 & Under").token; // "u8": a 7-year-old dances in it
|
|
263
|
+
Age.fromTitle("Girls 8 and under 9 years").token; // "8-9"
|
|
264
|
+
Age.fromTitle("Junior Boys 12 Years").token; // "u13"
|
|
265
|
+
Age.fromTitle("10-11 Single Jig").token; // "10-11", a band, not a number
|
|
266
|
+
Age.fromTitle("Competition 001-013 - Reel").token; // "", a competition number
|
|
267
|
+
Age.fromTitle("Senior Boys Reel 15&16").token; // "15-16": a range beats the word
|
|
268
|
+
Age.fromTitle("Beginner Adults Reel U40").token; // "adult": a split of the adult band
|
|
269
|
+
Age.fromTitle("Adult / Child Reel Under 10").token; // "u10": no adult dances in it
|
|
237
270
|
```
|
|
238
271
|
|
|
272
|
+
An adult word beside an `Under N` at 18 or above reads `adult`: Adult is a band with an 18
|
|
273
|
+
floor, and the `Under 40` splits it. Below 18 the youth band stands.
|
|
274
|
+
|
|
275
|
+
An age group is the age on the first of January, so a title that admits the dancer who is
|
|
276
|
+
`N` (`7 & Under`, `7/U`, `7 Years`) is the `Under N+1` group.
|
|
277
|
+
|
|
239
278
|
A value the reader cannot stand behind is returned, not thrown. An `Age` is `Validated` (above):
|
|
240
279
|
an age group under `U3` admits nobody old enough to dance, and a year of birth outside 1901 to
|
|
241
280
|
2099 is not one, so the placeholders `1000` and `1900` are refused. An invalid `Age` has no
|
package/dist/age.js
CHANGED
|
@@ -2,6 +2,111 @@ import {
|
|
|
2
2
|
validity
|
|
3
3
|
} from "./chunks/place-q9kwsmzd.js";
|
|
4
4
|
|
|
5
|
+
var LEAD_ID = /^\s*(?:comp(?:etition)?\.?\s*)?\d+[a-z]*(?:[/-]\d*[a-z]*)*\s*[-:.]?\s*/i;
|
|
6
|
+
var PAREN = /\(([^)]*)\)/g;
|
|
7
|
+
var AND_OVER = /(?<![a-z0-9])(\d{1,2})\s*(?:years?\s*)?(?:&|\+|\band\b)\s*(?:over|older|up|o)(?![a-z])/i;
|
|
8
|
+
var OVER_WORD = /(?:\bover\s*|\bos\s+cionn\s*|(?<![a-z0-9])o\.?\s*)(\d{1,2})(?![a-z0-9])/i;
|
|
9
|
+
var OVER_GLUED = /(?<![a-z0-9])(\d{1,2})\s*o(?![a-z'’])/i;
|
|
10
|
+
var OVER_PLUS = /(?<![a-z0-9])(\d{1,2})\s*\+/;
|
|
11
|
+
var UNDER = /(?:\bunder\s*|\bfaoi\s*|(?<![a-z0-9])u\.?\s*)(\d{1,2})(?![a-z0-9])/i;
|
|
12
|
+
var AND_UNDER = /(?<![a-z0-9])(\d{1,2})\s*(?:years?\s*)?(?:&|\/|\band\b)\s*u(?:nder)?(?![a-z])(?!\s*\d)/i;
|
|
13
|
+
var AND_UNDER_RANGE = /(?<![a-z0-9])(\d{1,2})\s*(?:&|\band\b)\s*under\s*(\d{1,2})(?![a-z0-9])/i;
|
|
14
|
+
var AND_RANGE = /(?<![a-z0-9])(?<!\b(?:under|und|over|faoi|cionn|u|o)\.?\s*)(?<!\d\s*,\s*)(?<!\(\s*)(\d{1,2})\s*(?:&|\band\b)\s*(\d{1,2})(?![a-z0-9])(?!\s*-?\s*hand)(?!\s*(?:&|\+|\/|,|\)|\band\b|\bor\b))/i;
|
|
15
|
+
var YEARS = /(?<![a-z0-9])(?<!\d\s*(?:&|\/|\band\b|[-\u0096–—]|\bto\b)\s*)(\d{1,2})\s*(?:years?|yrs?)(?![a-z0-9])(?!\s*(?:&|\+|\/|\band\b|\bor\b|old))/i;
|
|
16
|
+
var RANGE_DASH = String.raw`(?:\u00c2?[-\u0096–—]|\bto\b)`;
|
|
17
|
+
var RANGE = new RegExp(String.raw`(?<![0-9])(?<![0-9][a-z]*)(?<!(?:^|[^a-z0-9])[a-z])(?:u\s*)?(\d{1,2})\s*${RANGE_DASH}\s*(\d{1,2})(?![a-z0-9])`, "i");
|
|
18
|
+
var LEAD_RANGE = new RegExp(String.raw`^\s*([1-9]\d?)\s*${RANGE_DASH}\s*([1-9]\d?)(?![a-z0-9])(?!\s*[/&])`, "i");
|
|
19
|
+
var ADULT_CODE = /^\s*a(?:\d{1,2}(?:u|o|&o))?\s*$/i;
|
|
20
|
+
var ADULT = /(?<!non[\s-])(?<!-)\b(?:adults?|advanced\s+adults?|masters?)\b/i;
|
|
21
|
+
var SENIOR = /(?<!-)\bseniors?\b/i;
|
|
22
|
+
var TITLE_RANGE_MIN = 3;
|
|
23
|
+
var TITLE_RANGE_MAX = 40;
|
|
24
|
+
var NONE = Object.freeze({ token: "", span: null });
|
|
25
|
+
function at(title, offset, start, end, token) {
|
|
26
|
+
const a = offset + start;
|
|
27
|
+
const b = offset + end;
|
|
28
|
+
return { token, span: { start: a, end: b, text: title.slice(a, b) } };
|
|
29
|
+
}
|
|
30
|
+
function inFence(a, b) {
|
|
31
|
+
const lo = Number(a);
|
|
32
|
+
const hi = Number(b);
|
|
33
|
+
return lo >= TITLE_RANGE_MIN && hi <= TITLE_RANGE_MAX && lo < hi;
|
|
34
|
+
}
|
|
35
|
+
function readAnchored(title, s, offset, aside = false) {
|
|
36
|
+
for (const re of [AND_OVER, OVER_WORD, OVER_GLUED]) {
|
|
37
|
+
const m = re.exec(s);
|
|
38
|
+
if (m?.[1] !== undefined)
|
|
39
|
+
return at(title, offset, m.index, m.index + m[0].length, `o${Number(m[1])}`);
|
|
40
|
+
}
|
|
41
|
+
const u = AND_UNDER_RANGE.exec(s);
|
|
42
|
+
if (u?.[1] !== undefined && u[2] !== undefined && inFence(u[1], u[2]))
|
|
43
|
+
return at(title, offset, u.index, u.index + u[0].length, `${Number(u[1])}-${Number(u[2])}`);
|
|
44
|
+
const j = aside ? null : AND_RANGE.exec(s);
|
|
45
|
+
if (j?.[1] !== undefined && j[2] !== undefined && inFence(j[1], j[2]))
|
|
46
|
+
return at(title, offset, j.index, j.index + j[0].length, `${Number(j[1])}-${Number(j[2])}`);
|
|
47
|
+
const l = LEAD_RANGE.exec(s);
|
|
48
|
+
if (l?.[1] !== undefined && l[2] !== undefined && inFence(l[1], l[2])) {
|
|
49
|
+
const lead = l[0].length - l[0].trimStart().length;
|
|
50
|
+
return at(title, offset, lead, l[0].length, `${Number(l[1])}-${Number(l[2])}`);
|
|
51
|
+
}
|
|
52
|
+
const stripped = s.replace(LEAD_ID, "");
|
|
53
|
+
const shift = s.length - stripped.length;
|
|
54
|
+
const r = RANGE.exec(stripped);
|
|
55
|
+
if (r?.[1] !== undefined && r[2] !== undefined && inFence(r[1], r[2]))
|
|
56
|
+
return at(title, offset + shift, r.index, r.index + r[0].length, `${Number(r[1])}-${Number(r[2])}`);
|
|
57
|
+
const au = AND_UNDER.exec(s);
|
|
58
|
+
if (au?.[1] !== undefined)
|
|
59
|
+
return at(title, offset, au.index, au.index + au[0].length, `u${Number(au[1]) + 1}`);
|
|
60
|
+
const m = UNDER.exec(s);
|
|
61
|
+
if (m?.[1] !== undefined)
|
|
62
|
+
return at(title, offset, m.index, m.index + m[0].length, `u${Number(m[1])}`);
|
|
63
|
+
const y = YEARS.exec(s);
|
|
64
|
+
if (y?.[1] !== undefined)
|
|
65
|
+
return at(title, offset, y.index, y.index + y[0].length, `u${Number(y[1]) + 1}`);
|
|
66
|
+
const p = OVER_PLUS.exec(s);
|
|
67
|
+
if (p?.[1] !== undefined)
|
|
68
|
+
return at(title, offset, p.index, p.index + p[0].length, `o${Number(p[1])}`);
|
|
69
|
+
const a = ADULT.exec(s);
|
|
70
|
+
if (a !== null)
|
|
71
|
+
return at(title, offset, a.index, a.index + a[0].length, "adult");
|
|
72
|
+
const n = SENIOR.exec(s);
|
|
73
|
+
if (n !== null)
|
|
74
|
+
return at(title, offset, n.index, n.index + n[0].length, "senior");
|
|
75
|
+
return NONE;
|
|
76
|
+
}
|
|
77
|
+
function ageTitleToken(title) {
|
|
78
|
+
const s = title.trim();
|
|
79
|
+
if (s === "")
|
|
80
|
+
return NONE;
|
|
81
|
+
const lead = title.length - title.trimStart().length;
|
|
82
|
+
PAREN.lastIndex = 0;
|
|
83
|
+
let adultAside = null;
|
|
84
|
+
for (const m of s.matchAll(PAREN)) {
|
|
85
|
+
const inner = m[1] ?? "";
|
|
86
|
+
const from = lead + (m.index ?? 0) + 1;
|
|
87
|
+
const i = m.index ?? 0;
|
|
88
|
+
const tail = i + m[0].length === s.length && !/[a-z0-9]/i.test(s[i - 1] ?? "");
|
|
89
|
+
if (tail && ADULT_CODE.test(inner)) {
|
|
90
|
+
const a = inner.length - inner.trimStart().length;
|
|
91
|
+
adultAside = at(title, from, a, inner.trimEnd().length, "adult");
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const t = readAnchored(title, inner, from, true);
|
|
95
|
+
if (t.token !== "")
|
|
96
|
+
return adultOverUnder(title, s, lead, t);
|
|
97
|
+
}
|
|
98
|
+
const words = readAnchored(title, s, lead);
|
|
99
|
+
return words.token === "" && adultAside !== null ? adultAside : adultOverUnder(title, s, lead, words);
|
|
100
|
+
}
|
|
101
|
+
var ADULT_UNDER_MIN = 18;
|
|
102
|
+
function adultOverUnder(title, s, lead, t) {
|
|
103
|
+
const n = /^u(\d+)$/.exec(t.token)?.[1];
|
|
104
|
+
if (n === undefined || Number(n) < ADULT_UNDER_MIN)
|
|
105
|
+
return t;
|
|
106
|
+
const a = ADULT.exec(s);
|
|
107
|
+
return a === null ? t : at(title, lead, a.index, a.index + a[0].length, "adult");
|
|
108
|
+
}
|
|
109
|
+
|
|
5
110
|
var YOUNGEST_FLOOR = 0;
|
|
6
111
|
var ADULT_FLOOR = 18;
|
|
7
112
|
var MAJORS_FLOOR_YEARS = 3;
|
|
@@ -15,9 +120,10 @@ var YOB_RANGE = Object.freeze({ first: 1901, last: 2099 });
|
|
|
15
120
|
var DANCE_UP_YEARS = 1;
|
|
16
121
|
var ageOnJanuary1 = (year, born) => year - born - 1;
|
|
17
122
|
var yearsSinceBirth = (year, born) => year - born;
|
|
18
|
-
var
|
|
123
|
+
var UNDER2 = /^(?:u|under\s*)(\d+)$/i;
|
|
19
124
|
var OVER = /^(?:o|over\s*)(\d+)$/i;
|
|
20
|
-
var
|
|
125
|
+
var AND_OVER2 = /^(\d+)\s*(?:&|and\b)\s*(?:over|o)$/i;
|
|
126
|
+
var RANGE2 = /^u?(\d+)\s*[-–]\s*(\d+)$/i;
|
|
21
127
|
var ADULT_WORD = /^(?:adults?|ad|advanced|masters?)$/i;
|
|
22
128
|
var SENIOR_WORD = /^seniors?$/i;
|
|
23
129
|
var NO_LADDER = Object.freeze({ caps: [], overs: [], ranges: [] });
|
|
@@ -44,29 +150,36 @@ function readRange(a, b, ladder) {
|
|
|
44
150
|
clauses: [],
|
|
45
151
|
parts: [a, b]
|
|
46
152
|
};
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
153
|
+
if (ladder.ranges.some(([c, d]) => (c !== a || d !== b) && c < b && a < d))
|
|
154
|
+
return {
|
|
155
|
+
kind: "range",
|
|
156
|
+
interval: null,
|
|
157
|
+
refusal: "overlapping-ranges",
|
|
158
|
+
clauses: ["range-overlapping"],
|
|
159
|
+
parts: [a, b]
|
|
160
|
+
};
|
|
161
|
+
const below = maxWhere(ladder.caps, (c) => c < b);
|
|
162
|
+
const adultFloor = below === undefined && ladder.adult === true && b > ADULT_FLOOR;
|
|
163
|
+
const meetsOver = ladder.overs.includes(b + 1) && !ladder.caps.some((c) => c > b);
|
|
164
|
+
const clauses = [
|
|
165
|
+
"range-as-under",
|
|
166
|
+
below !== undefined ? "under-floored-on-rung" : adultFloor ? "range-adult-floor" : "youngest-absorbs"
|
|
167
|
+
];
|
|
168
|
+
if (meetsOver)
|
|
169
|
+
clauses.push("range-top-meets-over");
|
|
60
170
|
return {
|
|
61
171
|
kind: "range",
|
|
62
|
-
interval: {
|
|
172
|
+
interval: {
|
|
173
|
+
lo: below ?? (adultFloor ? ADULT_FLOOR : YOUNGEST_FLOOR),
|
|
174
|
+
hi: meetsOver ? b + 1 : b
|
|
175
|
+
},
|
|
63
176
|
refusal: null,
|
|
64
177
|
clauses,
|
|
65
178
|
parts: [a, b]
|
|
66
179
|
};
|
|
67
180
|
}
|
|
68
181
|
function read(token, ladder, top) {
|
|
69
|
-
const r =
|
|
182
|
+
const r = RANGE2.exec(token);
|
|
70
183
|
if (r !== null)
|
|
71
184
|
return readRange(Number(r[1]), Number(r[2]), ladder);
|
|
72
185
|
if (ADULT_WORD.test(token))
|
|
@@ -87,7 +200,7 @@ function read(token, ladder, top) {
|
|
|
87
200
|
parts: []
|
|
88
201
|
};
|
|
89
202
|
}
|
|
90
|
-
const u =
|
|
203
|
+
const u = UNDER2.exec(token);
|
|
91
204
|
if (u !== null) {
|
|
92
205
|
const n = Number(u[1]);
|
|
93
206
|
const below = maxWhere(ladder.caps, (c) => c < n);
|
|
@@ -99,7 +212,7 @@ function read(token, ladder, top) {
|
|
|
99
212
|
parts: [n]
|
|
100
213
|
};
|
|
101
214
|
}
|
|
102
|
-
const o = OVER.exec(token);
|
|
215
|
+
const o = OVER.exec(token) ?? AND_OVER2.exec(token);
|
|
103
216
|
if (o !== null) {
|
|
104
217
|
const n = Number(o[1]);
|
|
105
218
|
const above = top ? undefined : minWhere(ladder.overs, (c) => c > n);
|
|
@@ -194,7 +307,7 @@ function fromPrint(print, context = {}) {
|
|
|
194
307
|
throw new Error(`Age.from(${JSON.stringify(print)}): birthYears needs a competition year`);
|
|
195
308
|
return birthYearsOfInterval(iv, at);
|
|
196
309
|
},
|
|
197
|
-
floorObserved: () => reading.kind !== "under" || iv === null ? null : reading.clauses.includes("under-floored-on-rung")
|
|
310
|
+
floorObserved: () => reading.kind !== "under" && reading.kind !== "range" || iv === null ? null : reading.clauses.includes("under-floored-on-rung") || reading.clauses.includes("range-adult-floor")
|
|
198
311
|
});
|
|
199
312
|
}
|
|
200
313
|
function fromYOB(from, to = from, context = {}) {
|
|
@@ -237,21 +350,24 @@ function fromYOB(from, to = from, context = {}) {
|
|
|
237
350
|
floorObserved: () => null
|
|
238
351
|
});
|
|
239
352
|
}
|
|
240
|
-
|
|
353
|
+
function fromTitle(title, context = {}) {
|
|
354
|
+
const t = ageTitleToken(title);
|
|
355
|
+
return Object.freeze({ age: fromPrint(t.token, context), token: t.token, span: t.span });
|
|
356
|
+
}
|
|
357
|
+
var Age = Object.freeze({ from: fromPrint, fromYOB, fromTitle });
|
|
241
358
|
function sourceOf(r) {
|
|
242
359
|
const iv = r.interval;
|
|
243
360
|
if (iv === null)
|
|
244
361
|
return "open";
|
|
245
362
|
switch (r.kind) {
|
|
246
363
|
case "range":
|
|
247
|
-
|
|
364
|
+
case "under":
|
|
365
|
+
return iv.lo > YOUNGEST_FLOOR ? "sibling-narrowed" : "printed-band";
|
|
248
366
|
case "over":
|
|
249
367
|
return iv.hi === Number.POSITIVE_INFINITY ? "printed-band" : "sibling-narrowed";
|
|
250
368
|
case "adult":
|
|
251
369
|
case "senior":
|
|
252
370
|
return "printed-band";
|
|
253
|
-
case "under":
|
|
254
|
-
return iv.lo > YOUNGEST_FLOOR ? "sibling-narrowed" : "printed-band";
|
|
255
371
|
default:
|
|
256
372
|
return "open";
|
|
257
373
|
}
|
|
@@ -435,6 +551,8 @@ export {
|
|
|
435
551
|
MAJORS_FLOOR_YEARS,
|
|
436
552
|
MIN_FEIS_AGE,
|
|
437
553
|
MIN_MAJOR_AGE,
|
|
554
|
+
TITLE_RANGE_MAX,
|
|
555
|
+
TITLE_RANGE_MIN,
|
|
438
556
|
UNDER_CAP,
|
|
439
557
|
UNDER_CAP_MAX_TOP,
|
|
440
558
|
YOB_RANGE,
|
|
@@ -443,6 +561,7 @@ export {
|
|
|
443
561
|
YOUNG_FLOOR,
|
|
444
562
|
ageOnJanuary1,
|
|
445
563
|
ageRelation,
|
|
564
|
+
ageTitleToken,
|
|
446
565
|
averageAge,
|
|
447
566
|
birthYearsDisjoint,
|
|
448
567
|
birthYearsMeet,
|