glossarist 0.4.38 → 0.4.40

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.
Files changed (41) hide show
  1. package/package.json +1 -1
  2. package/src/models/base.ts +18 -1
  3. package/src/models/citation.ts +98 -0
  4. package/src/models/{concept-reference.js → concept-reference.ts} +21 -7
  5. package/src/models/designation-relationship.ts +39 -0
  6. package/src/models/designation.ts +297 -0
  7. package/src/models/figure.ts +110 -0
  8. package/src/models/formula.ts +36 -0
  9. package/src/models/gcr-metadata.ts +118 -0
  10. package/src/models/gcr-statistics.ts +76 -0
  11. package/src/models/hyperedge-colors.ts +87 -0
  12. package/src/models/non-verb-rep.ts +48 -0
  13. package/src/models/non-verbal-entity.ts +65 -0
  14. package/src/models/non-verbal-reference.ts +73 -0
  15. package/src/models/non-verbal-references.ts +29 -0
  16. package/src/models/register.ts +315 -0
  17. package/src/models/registrable.ts +48 -0
  18. package/src/models/{related-concept.js → related-concept.ts} +49 -25
  19. package/src/models/relation-categories.ts +167 -0
  20. package/src/models/{relation-colors.js → relation-colors.ts} +36 -23
  21. package/src/models/section.ts +61 -0
  22. package/src/models/shared-non-verbal-entity.ts +36 -0
  23. package/src/models/table.ts +36 -0
  24. package/src/models/citation.js +0 -68
  25. package/src/models/designation-relationship.js +0 -27
  26. package/src/models/designation.js +0 -213
  27. package/src/models/figure.js +0 -75
  28. package/src/models/formula.js +0 -25
  29. package/src/models/gcr-metadata.js +0 -68
  30. package/src/models/gcr-statistics.js +0 -51
  31. package/src/models/hyperedge-colors.js +0 -71
  32. package/src/models/non-verb-rep.js +0 -33
  33. package/src/models/non-verbal-entity.js +0 -46
  34. package/src/models/non-verbal-reference.js +0 -35
  35. package/src/models/non-verbal-references.js +0 -16
  36. package/src/models/register.js +0 -253
  37. package/src/models/registrable.js +0 -25
  38. package/src/models/relation-categories.js +0 -182
  39. package/src/models/section.js +0 -44
  40. package/src/models/shared-non-verbal-entity.js +0 -28
  41. package/src/models/table.js +0 -25
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glossarist",
3
- "version": "0.4.38",
3
+ "version": "0.4.40",
4
4
  "description": "JavaScript SDK for Glossarist GCR packages — read, write, validate, and manage terminology concepts",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -7,9 +7,26 @@
7
7
  * override for performance.
8
8
  */
9
9
 
10
+ /**
11
+ * Any value that can be the output of `JSON.stringify`. Used as the
12
+ * return type of {@link GlossaristModel.toJSON} so subclasses with
13
+ * non-object JSON output (e.g. `NonVerbalReference.toJSON()` returns
14
+ * a bare string for the no-display case) can override without
15
+ * breaking variance. The object case accepts any object to avoid
16
+ * forcing `<Model>Json` interfaces to declare explicit string index
17
+ * signatures (TS interfaces don't have implicit index signatures).
18
+ */
19
+ export type JsonValue =
20
+ | string
21
+ | number
22
+ | boolean
23
+ | null
24
+ | JsonValue[]
25
+ | object;
26
+
10
27
  export abstract class GlossaristModel {
11
28
  /** Wire-shape serialization. Subclasses MUST override. */
12
- toJSON(): object {
29
+ toJSON(): JsonValue {
13
30
  throw new Error(`${this.constructor.name} must implement toJSON()`);
14
31
  }
15
32
 
@@ -0,0 +1,98 @@
1
+ import { GlossaristModel } from './base.js';
2
+ import { Locality } from './locality.js';
3
+ import type { LocalityJson } from './locality.js';
4
+
5
+ export interface CitationRefJson {
6
+ source?: string | null;
7
+ id?: string | null;
8
+ version?: string | null;
9
+ }
10
+
11
+ export interface CitationJson {
12
+ ref?: CitationRefJson | CitationRef | null;
13
+ locality?: LocalityJson | Locality | null;
14
+ link?: string | null;
15
+ original?: string | null;
16
+ custom_locality?: string | null;
17
+ customLocality?: string | null;
18
+ }
19
+
20
+ export class CitationRef extends GlossaristModel {
21
+ readonly source: string | null;
22
+ readonly id: string | null;
23
+ readonly version: string | null;
24
+
25
+ constructor(data: CitationRefJson = {}) {
26
+ super();
27
+ this.source = data.source ?? null;
28
+ this.id = data.id ?? null;
29
+ this.version = data.version ?? null;
30
+ }
31
+
32
+ override toString(): string {
33
+ const parts: string[] = [];
34
+ if (this.source) parts.push(this.source);
35
+ if (this.id) parts.push(this.id);
36
+ return parts.join(' ');
37
+ }
38
+
39
+ override toJSON(): CitationRefJson {
40
+ const obj: CitationRefJson = {};
41
+ if (this.source != null) obj.source = this.source;
42
+ if (this.id != null) obj.id = this.id;
43
+ if (this.version != null) obj.version = this.version;
44
+ return obj;
45
+ }
46
+
47
+ static override fromJSON(data: CitationRefJson): CitationRef {
48
+ return new CitationRef(data);
49
+ }
50
+ }
51
+
52
+ export class Citation extends GlossaristModel {
53
+ /** Backward-compat alias for {@link CitationRef}. Original codebase
54
+ * exposed the Ref class via `Citation.Ref`; keep that working. */
55
+ static readonly Ref = CitationRef;
56
+
57
+ readonly ref: CitationRef | null;
58
+ readonly locality: Locality | null;
59
+ readonly link: string | null;
60
+ readonly original: string | null;
61
+ readonly customLocality: string | null;
62
+
63
+ constructor(data: CitationJson | null = {}) {
64
+ super();
65
+ const d = data ?? {};
66
+ this.ref = d.ref
67
+ ? d.ref instanceof CitationRef
68
+ ? d.ref
69
+ : new CitationRef(d.ref)
70
+ : null;
71
+ this.locality = d.locality
72
+ ? d.locality instanceof Locality
73
+ ? d.locality
74
+ : new Locality(d.locality)
75
+ : null;
76
+ this.link = d.link ?? null;
77
+ this.original = d.original ?? null;
78
+ this.customLocality = d.custom_locality ?? d.customLocality ?? null;
79
+ }
80
+
81
+ override toString(): string {
82
+ return this.ref ? this.ref.toString() : '';
83
+ }
84
+
85
+ override toJSON(): CitationJson {
86
+ const obj: CitationJson = {};
87
+ if (this.ref != null) obj.ref = this.ref.toJSON();
88
+ if (this.locality != null) obj.locality = this.locality.toJSON();
89
+ if (this.link != null) obj.link = this.link;
90
+ if (this.original != null) obj.original = this.original;
91
+ if (this.customLocality != null) obj.custom_locality = this.customLocality;
92
+ return obj;
93
+ }
94
+
95
+ static override fromJSON(data: CitationJson): Citation {
96
+ return new Citation(data);
97
+ }
98
+ }
@@ -1,7 +1,21 @@
1
1
  import { GlossaristModel } from './base.js';
2
2
 
3
+ export interface ConceptReferenceJson {
4
+ concept_id?: string | null;
5
+ conceptId?: string | null;
6
+ ref_type?: string | null;
7
+ refType?: string | null;
8
+ source?: string | null;
9
+ urn?: string | null;
10
+ }
11
+
3
12
  export class ConceptReference extends GlossaristModel {
4
- constructor(data = {}) {
13
+ readonly conceptId: string | null;
14
+ readonly refType: string | null;
15
+ readonly source: string | null;
16
+ readonly urn: string | null;
17
+
18
+ constructor(data: ConceptReferenceJson = {}) {
5
19
  super();
6
20
  this.conceptId = data.concept_id ?? data.conceptId ?? null;
7
21
  this.refType = data.ref_type ?? data.refType ?? null;
@@ -9,20 +23,20 @@ export class ConceptReference extends GlossaristModel {
9
23
  this.urn = data.urn ?? null;
10
24
  }
11
25
 
12
- get isLocal() {
26
+ get isLocal(): boolean {
13
27
  return this.urn == null && this.source == null;
14
28
  }
15
29
 
16
- get isExternal() {
30
+ get isExternal(): boolean {
17
31
  return !this.isLocal;
18
32
  }
19
33
 
20
- static domain(conceptId) {
34
+ static domain(conceptId: string): ConceptReference {
21
35
  return new ConceptReference({ concept_id: conceptId, ref_type: 'domain' });
22
36
  }
23
37
 
24
- toJSON() {
25
- const obj = {};
38
+ override toJSON(): ConceptReferenceJson {
39
+ const obj: ConceptReferenceJson = {};
26
40
  if (this.conceptId != null) obj.concept_id = this.conceptId;
27
41
  if (this.refType != null) obj.ref_type = this.refType;
28
42
  if (this.source != null) obj.source = this.source;
@@ -30,7 +44,7 @@ export class ConceptReference extends GlossaristModel {
30
44
  return obj;
31
45
  }
32
46
 
33
- static fromJSON(data) {
47
+ static override fromJSON(data: ConceptReferenceJson): ConceptReference {
34
48
  return new ConceptReference(data);
35
49
  }
36
50
  }
@@ -0,0 +1,39 @@
1
+ import { GlossaristModel } from './base.js';
2
+
3
+ export const DESIGNATION_RELATIONSHIP_TYPES: ReadonlyArray<string> = Object.freeze([
4
+ // TBX (ISO 30042) / ISO 12620 term-level relationships
5
+ 'abbreviated_form_for', 'short_form_for',
6
+ ]);
7
+
8
+ export type DesignationRelationshipType = string;
9
+
10
+ export interface DesignationRelationshipJson {
11
+ type?: DesignationRelationshipType | null;
12
+ content?: string | null;
13
+ target?: string | null;
14
+ }
15
+
16
+ export class DesignationRelationship extends GlossaristModel {
17
+ readonly type: DesignationRelationshipType | null;
18
+ readonly content: string | null;
19
+ readonly target: string | null;
20
+
21
+ constructor(data: DesignationRelationshipJson = {}) {
22
+ super();
23
+ this.type = data.type ?? null;
24
+ this.content = data.content ?? null;
25
+ this.target = data.target ?? null;
26
+ }
27
+
28
+ override toJSON(): DesignationRelationshipJson {
29
+ const obj: DesignationRelationshipJson = {};
30
+ if (this.type != null) obj.type = this.type;
31
+ if (this.content != null) obj.content = this.content;
32
+ if (this.target != null) obj.target = this.target;
33
+ return obj;
34
+ }
35
+
36
+ static override fromJSON(data: DesignationRelationshipJson): DesignationRelationship {
37
+ return new DesignationRelationship(data);
38
+ }
39
+ }
@@ -0,0 +1,297 @@
1
+ import { RegistrableModel } from './registrable.js';
2
+ import { ConceptSource } from './concept-source.js';
3
+ import type { ConceptSourceJson } from './concept-source.js';
4
+ import { Pronunciation } from './pronunciation.js';
5
+ import type { PronunciationJson } from './pronunciation.js';
6
+ import { GrammarInfo } from './grammar-info.js';
7
+ import type { GrammarInfoJson } from './grammar-info.js';
8
+ import { RelatedConcept } from './related-concept.js';
9
+ import type { RelatedConceptJson } from './related-concept.js';
10
+ import {
11
+ DesignationRelationship,
12
+ DESIGNATION_RELATIONSHIP_TYPES,
13
+ } from './designation-relationship.js';
14
+ import type { DesignationRelationshipJson } from './designation-relationship.js';
15
+
16
+ export type DesignationType =
17
+ | 'expression'
18
+ | 'abbreviation'
19
+ | 'symbol'
20
+ | 'letter_symbol'
21
+ | 'graphical symbol'
22
+ | 'graphical_symbol'
23
+ | string;
24
+
25
+ export type NormativeStatus = 'preferred' | 'deprecated' | 'admitted' | 'deprecated_' | string | null;
26
+
27
+ const SKOS_LABEL_BY_NORMATIVE_STATUS: Readonly<Record<string, string>> = Object.freeze({
28
+ preferred: 'prefLabel',
29
+ deprecated: 'hiddenLabel',
30
+ admitted: 'altLabel',
31
+ deprecated_: 'altLabel',
32
+ });
33
+
34
+ export interface DesignationJson {
35
+ type?: DesignationType;
36
+ designation?: string;
37
+ normative_status?: NormativeStatus;
38
+ normativeStatus?: NormativeStatus;
39
+ absent?: boolean | null;
40
+ field_of_application?: string | null;
41
+ fieldOfApplication?: string | null;
42
+ usage_info?: string | null;
43
+ usageInfo?: string | null;
44
+ geographical_area?: string | null;
45
+ geographicalArea?: string | null;
46
+ language?: string | null;
47
+ script?: string | null;
48
+ system?: string | null;
49
+ international?: boolean | null;
50
+ term_type?: string | null;
51
+ termType?: string | null;
52
+ pronunciation?: ReadonlyArray<PronunciationJson | Pronunciation>;
53
+ sources?: ReadonlyArray<ConceptSourceJson | ConceptSource>;
54
+ related?: ReadonlyArray<DesignationRelationshipJson | DesignationRelationship | RelatedConceptJson | RelatedConcept>;
55
+ prefix?: string | null;
56
+ grammar_info?: ReadonlyArray<GrammarInfoJson | GrammarInfo>;
57
+ acronym?: boolean;
58
+ initialism?: boolean;
59
+ truncation?: boolean;
60
+ text?: string | null;
61
+ image?: string | null;
62
+ }
63
+
64
+ export class Designation extends RegistrableModel {
65
+ readonly designation: string;
66
+ readonly type: DesignationType;
67
+ readonly normativeStatus: NormativeStatus;
68
+ readonly absent: boolean | null;
69
+ readonly fieldOfApplication: string | null;
70
+ readonly usageInfo: string | null;
71
+ readonly geographicalArea: string | null;
72
+ readonly language: string | null;
73
+ readonly script: string | null;
74
+ readonly system: string | null;
75
+ readonly international: boolean | null;
76
+ readonly termType: string | null;
77
+ readonly pronunciations: ReadonlyArray<Pronunciation>;
78
+ readonly sources: ReadonlyArray<ConceptSource>;
79
+ readonly related: ReadonlyArray<DesignationRelationship | RelatedConcept>;
80
+
81
+ constructor(data: DesignationJson = {}) {
82
+ super();
83
+ this.designation = data.designation ?? '';
84
+ this.type = data.type ?? 'expression';
85
+ this.normativeStatus = data.normative_status ?? data.normativeStatus ?? null;
86
+ this.absent = data.absent ?? null;
87
+ this.fieldOfApplication = data.field_of_application ?? data.fieldOfApplication ?? null;
88
+ this.usageInfo = data.usage_info ?? data.usageInfo ?? null;
89
+ this.geographicalArea = data.geographical_area ?? data.geographicalArea ?? null;
90
+ this.language = data.language ?? null;
91
+ this.script = data.script ?? null;
92
+ this.system = data.system ?? null;
93
+ this.international = data.international ?? null;
94
+ this.termType = data.term_type ?? data.termType ?? null;
95
+ this.pronunciations = (data.pronunciation ?? []).map((p) =>
96
+ p instanceof Pronunciation ? p : new Pronunciation(p as PronunciationJson),
97
+ );
98
+ this.sources = (data.sources ?? []).map((s) =>
99
+ s instanceof ConceptSource ? s : new ConceptSource(s as ConceptSourceJson),
100
+ );
101
+ this.related = (data.related ?? []).map((r) => {
102
+ if (r instanceof DesignationRelationship || r instanceof RelatedConcept) return r;
103
+ const hash = r as { type?: string };
104
+ if (hash?.type && DESIGNATION_RELATIONSHIP_TYPES.includes(hash.type)) {
105
+ return DesignationRelationship.fromJSON(hash as DesignationRelationshipJson);
106
+ }
107
+ return RelatedConcept.fromJSON(hash as RelatedConceptJson);
108
+ });
109
+ }
110
+
111
+ override toJSON(): DesignationJson {
112
+ const obj: DesignationJson = { type: this.type, designation: this.designation };
113
+ if (this.normativeStatus != null) obj.normative_status = this.normativeStatus;
114
+ if (this.absent != null) obj.absent = this.absent;
115
+ if (this.fieldOfApplication != null) obj.field_of_application = this.fieldOfApplication;
116
+ if (this.usageInfo != null) obj.usage_info = this.usageInfo;
117
+ if (this.geographicalArea != null) obj.geographical_area = this.geographicalArea;
118
+ if (this.language != null) obj.language = this.language;
119
+ if (this.script != null) obj.script = this.script;
120
+ if (this.system != null) obj.system = this.system;
121
+ if (this.international != null) obj.international = this.international;
122
+ if (this.termType != null) obj.term_type = this.termType;
123
+ if (this.pronunciations.length > 0) {
124
+ obj.pronunciation = this.pronunciations.map((p) => p.toJSON());
125
+ }
126
+ if (this.sources.length > 0) {
127
+ obj.sources = this.sources.map((s) => s.toJSON());
128
+ }
129
+ if (this.related.length > 0) {
130
+ obj.related = this.related.map((r) => r.toJSON() as never);
131
+ }
132
+ return obj;
133
+ }
134
+
135
+ /** RDF class local-name for this designation subtype. */
136
+ rdfClass(): string {
137
+ return 'Expression';
138
+ }
139
+
140
+ /** SKOS label predicate URI appropriate for this designation's normative status. */
141
+ skosLabelPredicate(skosNs: string): string {
142
+ return `${skosNs}${this._skosLabelLocalName()}`;
143
+ }
144
+
145
+ /** SKOS-XL label predicate URI. */
146
+ skosxlLabelPredicate(skosxlNs: string): string {
147
+ return `${skosxlNs}${this._skosLabelLocalName()}`;
148
+ }
149
+
150
+ protected _skosLabelLocalName(): string {
151
+ const statusVal = this.normativeStatus == null ? '' : String(this.normativeStatus);
152
+ const status = statusVal
153
+ .split(/[/#]/)
154
+ .pop() ?? '';
155
+ return SKOS_LABEL_BY_NORMATIVE_STATUS[status] ?? 'altLabel';
156
+ }
157
+
158
+ static identityOf(value: { type?: string; designation?: string } | null | undefined): string {
159
+ const v = value ?? {};
160
+ const type = v.type ?? 'expression';
161
+ const text = String(v.designation ?? '').toLowerCase().trim();
162
+ return `${type}|${text}`;
163
+ }
164
+
165
+ override identity(): string {
166
+ return Designation.identityOf(this);
167
+ }
168
+
169
+ static override fromJSON(data: DesignationJson): Designation {
170
+ return Designation.fromData(data);
171
+ }
172
+ }
173
+
174
+ export class Expression extends Designation {
175
+ readonly prefix: string | null;
176
+ readonly grammarInfo: ReadonlyArray<GrammarInfo>;
177
+
178
+ constructor(data: DesignationJson = {}) {
179
+ super(data);
180
+ this.prefix = data.prefix ?? null;
181
+ this.grammarInfo = (data.grammar_info ?? []).map((g) =>
182
+ g instanceof GrammarInfo ? g : new GrammarInfo(g as GrammarInfoJson),
183
+ );
184
+ }
185
+
186
+ override toJSON(): DesignationJson {
187
+ const obj = super.toJSON();
188
+ if (this.prefix != null) obj.prefix = this.prefix;
189
+ if (this.grammarInfo.length > 0) {
190
+ obj.grammar_info = this.grammarInfo.map((g) => g.toJSON());
191
+ }
192
+ return obj;
193
+ }
194
+
195
+ static override fromJSON(data: DesignationJson): Expression {
196
+ return new Expression(data);
197
+ }
198
+ }
199
+
200
+ Designation.register('expression', Expression);
201
+
202
+ export class Abbreviation extends Expression {
203
+ readonly acronym: boolean;
204
+ readonly initialism: boolean;
205
+ readonly truncation: boolean;
206
+
207
+ constructor(data: DesignationJson = {}) {
208
+ super(data);
209
+ this.acronym = data.acronym ?? false;
210
+ this.initialism = data.initialism ?? false;
211
+ this.truncation = data.truncation ?? false;
212
+ }
213
+
214
+ override rdfClass(): string {
215
+ return 'Abbreviation';
216
+ }
217
+
218
+ override toJSON(): DesignationJson {
219
+ const obj = super.toJSON();
220
+ if (this.acronym) obj.acronym = true;
221
+ if (this.initialism) obj.initialism = true;
222
+ if (this.truncation) obj.truncation = true;
223
+ return obj;
224
+ }
225
+
226
+ static override fromJSON(data: DesignationJson): Abbreviation {
227
+ return new Abbreviation(data);
228
+ }
229
+ }
230
+
231
+ Designation.register('abbreviation', Abbreviation);
232
+
233
+ export class Symbol extends Designation {
234
+ override rdfClass(): string {
235
+ return 'Symbol';
236
+ }
237
+
238
+ static override fromJSON(data: DesignationJson): Symbol {
239
+ return new Symbol(data);
240
+ }
241
+ }
242
+
243
+ Designation.register('symbol', Symbol);
244
+
245
+ export class LetterSymbol extends Symbol {
246
+ readonly text: string | null;
247
+
248
+ constructor(data: DesignationJson = {}) {
249
+ super(data);
250
+ this.text = data.text ?? null;
251
+ }
252
+
253
+ override rdfClass(): string {
254
+ return 'LetterSymbol';
255
+ }
256
+
257
+ override toJSON(): DesignationJson {
258
+ const obj = super.toJSON();
259
+ if (this.text != null) obj.text = this.text;
260
+ return obj;
261
+ }
262
+
263
+ static override fromJSON(data: DesignationJson): LetterSymbol {
264
+ return new LetterSymbol(data);
265
+ }
266
+ }
267
+
268
+ Designation.register('letter_symbol', LetterSymbol);
269
+
270
+ export class GraphicalSymbol extends Symbol {
271
+ readonly text: string | null;
272
+ readonly image: string | null;
273
+
274
+ constructor(data: DesignationJson = {}) {
275
+ super(data);
276
+ this.text = data.text ?? null;
277
+ this.image = data.image ?? null;
278
+ }
279
+
280
+ override rdfClass(): string {
281
+ return 'GraphicalSymbol';
282
+ }
283
+
284
+ override toJSON(): DesignationJson {
285
+ const obj = super.toJSON();
286
+ if (this.text != null) obj.text = this.text;
287
+ if (this.image != null) obj.image = this.image;
288
+ return obj;
289
+ }
290
+
291
+ static override fromJSON(data: DesignationJson): GraphicalSymbol {
292
+ return new GraphicalSymbol(data);
293
+ }
294
+ }
295
+
296
+ Designation.register('graphical symbol', GraphicalSymbol);
297
+ Designation.register('graphical_symbol', GraphicalSymbol);
@@ -0,0 +1,110 @@
1
+ import { SharedNonVerbalEntity } from './shared-non-verbal-entity.js';
2
+ import type { SharedNonVerbalEntityJson } from './shared-non-verbal-entity.js';
3
+ import { NonVerbalEntity } from './non-verbal-entity.js';
4
+
5
+ export interface FigureImageJson {
6
+ src?: string | null;
7
+ format?: string | null;
8
+ role?: string | null;
9
+ width?: number | string | null;
10
+ height?: number | string | null;
11
+ scale?: number | string | null;
12
+ }
13
+
14
+ export class FigureImage {
15
+ readonly src: string | null;
16
+ readonly format: string | null;
17
+ readonly role: string | null;
18
+ readonly width: number | string | null;
19
+ readonly height: number | string | null;
20
+ readonly scale: number | string | null;
21
+
22
+ constructor(data: FigureImageJson = {}) {
23
+ this.src = data.src ?? null;
24
+ this.format = data.format ?? null;
25
+ this.role = data.role ?? null;
26
+ this.width = data.width ?? null;
27
+ this.height = data.height ?? null;
28
+ this.scale = data.scale ?? null;
29
+ }
30
+
31
+ toJSON(): FigureImageJson {
32
+ const obj: FigureImageJson = {};
33
+ if (this.src != null) obj.src = this.src;
34
+ if (this.format != null) obj.format = this.format;
35
+ if (this.role != null) obj.role = this.role;
36
+ if (this.width != null) obj.width = this.width;
37
+ if (this.height != null) obj.height = this.height;
38
+ if (this.scale != null) obj.scale = this.scale;
39
+ return obj;
40
+ }
41
+
42
+ static fromJSON(data: FigureImageJson): FigureImage {
43
+ return new FigureImage(data);
44
+ }
45
+ }
46
+
47
+ export interface FigureJson extends SharedNonVerbalEntityJson {
48
+ images?: ReadonlyArray<FigureImageJson>;
49
+ subfigures?: ReadonlyArray<FigureJson | Figure>;
50
+ }
51
+
52
+ export class Figure extends SharedNonVerbalEntity {
53
+ protected readonly _rawImages: ReadonlyArray<FigureImageJson | FigureImage>;
54
+ protected readonly _rawSubfigures: ReadonlyArray<FigureJson | Figure>;
55
+ protected _images: ReadonlyArray<FigureImage> | null = null;
56
+ protected _subfigures: ReadonlyArray<Figure> | null = null;
57
+
58
+ constructor(data: FigureJson = {}) {
59
+ super(data);
60
+ this._rawImages = data.images ?? [];
61
+ this._rawSubfigures = data.subfigures ?? [];
62
+ }
63
+
64
+ override rdfClass(): string {
65
+ return 'Figure';
66
+ }
67
+
68
+ get images(): ReadonlyArray<FigureImage> {
69
+ return this._lazy<FigureImage>(
70
+ '_images',
71
+ '_rawImages',
72
+ (i) => (i instanceof FigureImage ? i : new FigureImage(i as FigureImageJson)),
73
+ );
74
+ }
75
+
76
+ get subfigures(): ReadonlyArray<Figure> {
77
+ return this._lazy<Figure>(
78
+ '_subfigures',
79
+ '_rawSubfigures',
80
+ (s) => (s instanceof Figure ? s : new Figure(s as FigureJson)),
81
+ );
82
+ }
83
+
84
+ override findById(targetId: string): this | null {
85
+ if (this.id === targetId) return this;
86
+ for (const sub of this.subfigures) {
87
+ const found = sub.findById(targetId);
88
+ if (found) return found as this;
89
+ }
90
+ return null;
91
+ }
92
+
93
+ override allIds(): string[] {
94
+ const ids = this.id != null ? [this.id] : [];
95
+ return [...ids, ...this.subfigures.flatMap((s) => s.allIds())];
96
+ }
97
+
98
+ override toJSON(): FigureJson {
99
+ const obj = super.toJSON() as FigureJson;
100
+ this._serialize(obj as unknown as Record<string, unknown>, 'images', '_images', '_rawImages');
101
+ this._serialize(obj as unknown as Record<string, unknown>, 'subfigures', '_subfigures', '_rawSubfigures');
102
+ return obj;
103
+ }
104
+
105
+ static override fromJSON(data: FigureJson): Figure {
106
+ return new Figure(data);
107
+ }
108
+ }
109
+
110
+ NonVerbalEntity.register('figure', Figure);
@@ -0,0 +1,36 @@
1
+ import { SharedNonVerbalEntity } from './shared-non-verbal-entity.js';
2
+ import type { SharedNonVerbalEntityJson } from './shared-non-verbal-entity.js';
3
+ import { NonVerbalEntity } from './non-verbal-entity.js';
4
+
5
+ export interface FormulaJson extends SharedNonVerbalEntityJson {
6
+ expression?: string | null;
7
+ notation?: string | null;
8
+ }
9
+
10
+ export class Formula extends SharedNonVerbalEntity {
11
+ readonly expression: string | null;
12
+ readonly notation: string | null;
13
+
14
+ constructor(data: FormulaJson = {}) {
15
+ super(data);
16
+ this.expression = data.expression ?? null;
17
+ this.notation = data.notation ?? null;
18
+ }
19
+
20
+ override rdfClass(): string {
21
+ return 'Formula';
22
+ }
23
+
24
+ override toJSON(): FormulaJson {
25
+ const obj = super.toJSON() as FormulaJson;
26
+ if (this.expression != null) obj.expression = this.expression;
27
+ if (this.notation != null) obj.notation = this.notation;
28
+ return obj;
29
+ }
30
+
31
+ static override fromJSON(data: FormulaJson): Formula {
32
+ return new Formula(data);
33
+ }
34
+ }
35
+
36
+ NonVerbalEntity.register('formula', Formula);