@readium/shared 1.2.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.
Files changed (150) hide show
  1. package/LICENSE +28 -0
  2. package/README.MD +27 -0
  3. package/dist/index.js +2536 -0
  4. package/dist/index.umd.cjs +2 -0
  5. package/package.json +73 -0
  6. package/src/fetcher/Fetcher.ts +37 -0
  7. package/src/fetcher/HttpFetcher.ts +122 -0
  8. package/src/fetcher/Resource.ts +37 -0
  9. package/src/fetcher/index.ts +2 -0
  10. package/src/index.ts +4 -0
  11. package/src/opds/Acquisition.ts +54 -0
  12. package/src/opds/Availability.ts +61 -0
  13. package/src/opds/Copies.ts +43 -0
  14. package/src/opds/Holds.ts +43 -0
  15. package/src/opds/Price.ts +51 -0
  16. package/src/opds/index.ts +5 -0
  17. package/src/publication/BelongsTo.ts +51 -0
  18. package/src/publication/Contributor.ts +129 -0
  19. package/src/publication/GuidedNavigation.ts +176 -0
  20. package/src/publication/Link.ts +332 -0
  21. package/src/publication/LocalizedString.ts +74 -0
  22. package/src/publication/Locator.ts +225 -0
  23. package/src/publication/LocatorCollection.ts +133 -0
  24. package/src/publication/Manifest.ts +256 -0
  25. package/src/publication/Metadata.ts +329 -0
  26. package/src/publication/Properties.ts +44 -0
  27. package/src/publication/Publication.ts +148 -0
  28. package/src/publication/PublicationCollection.ts +144 -0
  29. package/src/publication/ReadingProgression.ts +28 -0
  30. package/src/publication/Subject.ts +115 -0
  31. package/src/publication/encryption/Encryption.ts +68 -0
  32. package/src/publication/encryption/Properties.ts +20 -0
  33. package/src/publication/encryption/index.ts +2 -0
  34. package/src/publication/epub/EPUBLayout.ts +10 -0
  35. package/src/publication/epub/Presentation.ts +16 -0
  36. package/src/publication/epub/Properties.ts +28 -0
  37. package/src/publication/epub/Publication.ts +51 -0
  38. package/src/publication/epub/index.ts +4 -0
  39. package/src/publication/html/DomRange.ts +63 -0
  40. package/src/publication/html/DomRangePoint.ts +64 -0
  41. package/src/publication/html/Locations.ts +129 -0
  42. package/src/publication/html/index.ts +3 -0
  43. package/src/publication/index.ts +19 -0
  44. package/src/publication/opds/Properties.ts +84 -0
  45. package/src/publication/opds/Publication.ts +14 -0
  46. package/src/publication/opds/index.ts +2 -0
  47. package/src/publication/presentation/Metadata.ts +19 -0
  48. package/src/publication/presentation/Presentation.ts +155 -0
  49. package/src/publication/presentation/Properties.ts +70 -0
  50. package/src/publication/presentation/index.ts +3 -0
  51. package/src/publication/services/content/Content.ts +36 -0
  52. package/src/publication/services/content/ContentTokenizer.ts +62 -0
  53. package/src/publication/services/content/Iterator.ts +46 -0
  54. package/src/publication/services/content/element/attributes.ts +44 -0
  55. package/src/publication/services/content/element/element.ts +118 -0
  56. package/src/publication/services/content/element/index.ts +3 -0
  57. package/src/publication/services/content/element/text_role.ts +60 -0
  58. package/src/publication/services/content/index.ts +5 -0
  59. package/src/publication/services/content/iterators/HTMLResourceContentIterator.ts +571 -0
  60. package/src/publication/services/content/iterators/PDFTextContentIterator.ts +184 -0
  61. package/src/publication/services/content/iterators/PublicationContentIterator.ts +144 -0
  62. package/src/publication/services/content/iterators/helpers.ts +146 -0
  63. package/src/publication/services/content/iterators/index.ts +3 -0
  64. package/src/publication/services/index.ts +1 -0
  65. package/src/util/JSONParse.ts +31 -0
  66. package/src/util/Language.ts +5 -0
  67. package/src/util/URITemplate.ts +83 -0
  68. package/src/util/index.ts +5 -0
  69. package/src/util/mediatype/MediaType.ts +563 -0
  70. package/src/util/mediatype/index.ts +1 -0
  71. package/src/util/tokenizer/TextTokenizer.ts +126 -0
  72. package/src/util/tokenizer/Tokenizer.ts +8 -0
  73. package/src/util/tokenizer/index.ts +2 -0
  74. package/src/util/tokenizer/tokenize-english/README.md +7 -0
  75. package/src/util/tokenizer/tokenize-english/abbreviations.js +52 -0
  76. package/src/util/tokenizer/tokenize-english/index.d.ts +3 -0
  77. package/src/util/tokenizer/tokenize-english/index.js +243 -0
  78. package/src/util/tokenizer/tokenize-english/utils.js +16 -0
  79. package/src/util/tokenizer/tokenize-text/README.MD +6 -0
  80. package/src/util/tokenizer/tokenize-text/index.d.ts +52 -0
  81. package/src/util/tokenizer/tokenize-text/index.js +256 -0
  82. package/src/util/tokenizer/tokenize-text/tokens.js +100 -0
  83. package/types/src/fetcher/Fetcher.d.ts +27 -0
  84. package/types/src/fetcher/HttpFetcher.d.ts +28 -0
  85. package/types/src/fetcher/Resource.d.ts +15 -0
  86. package/types/src/fetcher/index.d.ts +2 -0
  87. package/types/src/index.d.ts +4 -0
  88. package/types/src/opds/Acquisition.d.ts +26 -0
  89. package/types/src/opds/Availability.d.ts +32 -0
  90. package/types/src/opds/Copies.d.ts +22 -0
  91. package/types/src/opds/Holds.d.ts +22 -0
  92. package/types/src/opds/Price.d.ts +29 -0
  93. package/types/src/opds/index.d.ts +5 -0
  94. package/types/src/publication/BelongsTo.d.ts +23 -0
  95. package/types/src/publication/Contributor.d.ts +60 -0
  96. package/types/src/publication/GuidedNavigation.d.ts +70 -0
  97. package/types/src/publication/Link.d.ts +128 -0
  98. package/types/src/publication/LocalizedString.d.ts +48 -0
  99. package/types/src/publication/Locator.d.ts +101 -0
  100. package/types/src/publication/LocatorCollection.d.ts +54 -0
  101. package/types/src/publication/Manifest.d.ts +59 -0
  102. package/types/src/publication/Metadata.d.ts +101 -0
  103. package/types/src/publication/Properties.d.ts +28 -0
  104. package/types/src/publication/Publication.d.ts +53 -0
  105. package/types/src/publication/PublicationCollection.d.ts +34 -0
  106. package/types/src/publication/ReadingProgression.d.ts +9 -0
  107. package/types/src/publication/Subject.d.ts +56 -0
  108. package/types/src/publication/encryption/Encryption.d.ts +33 -0
  109. package/types/src/publication/encryption/Properties.d.ts +10 -0
  110. package/types/src/publication/encryption/index.d.ts +2 -0
  111. package/types/src/publication/epub/EPUBLayout.d.ts +5 -0
  112. package/types/src/publication/epub/Presentation.d.ts +7 -0
  113. package/types/src/publication/epub/Properties.d.ts +14 -0
  114. package/types/src/publication/epub/Publication.d.ts +19 -0
  115. package/types/src/publication/epub/index.d.ts +4 -0
  116. package/types/src/publication/html/DomRange.d.ts +40 -0
  117. package/types/src/publication/html/DomRangePoint.d.ts +36 -0
  118. package/types/src/publication/html/Locations.d.ts +45 -0
  119. package/types/src/publication/html/index.d.ts +3 -0
  120. package/types/src/publication/index.d.ts +19 -0
  121. package/types/src/publication/opds/Properties.d.ts +38 -0
  122. package/types/src/publication/opds/Publication.d.ts +6 -0
  123. package/types/src/publication/opds/index.d.ts +2 -0
  124. package/types/src/publication/presentation/Metadata.d.ts +6 -0
  125. package/types/src/publication/presentation/Presentation.d.ts +95 -0
  126. package/types/src/publication/presentation/Properties.d.ts +32 -0
  127. package/types/src/publication/presentation/index.d.ts +3 -0
  128. package/types/src/publication/services/content/Content.d.ts +20 -0
  129. package/types/src/publication/services/content/ContentTokenizer.d.ts +19 -0
  130. package/types/src/publication/services/content/Iterator.d.ts +38 -0
  131. package/types/src/publication/services/content/element/attributes.d.ts +31 -0
  132. package/types/src/publication/services/content/element/element.d.ts +107 -0
  133. package/types/src/publication/services/content/element/index.d.ts +3 -0
  134. package/types/src/publication/services/content/element/text_role.d.ts +53 -0
  135. package/types/src/publication/services/content/index.d.ts +5 -0
  136. package/types/src/publication/services/content/iterators/HTMLResourceContentIterator.d.ts +30 -0
  137. package/types/src/publication/services/content/iterators/PDFTextContentIterator.d.ts +60 -0
  138. package/types/src/publication/services/content/iterators/PublicationContentIterator.d.ts +59 -0
  139. package/types/src/publication/services/content/iterators/helpers.d.ts +10 -0
  140. package/types/src/publication/services/content/iterators/index.d.ts +3 -0
  141. package/types/src/publication/services/index.d.ts +1 -0
  142. package/types/src/util/JSONParse.d.ts +11 -0
  143. package/types/src/util/Language.d.ts +1 -0
  144. package/types/src/util/URITemplate.d.ts +24 -0
  145. package/types/src/util/index.d.ts +5 -0
  146. package/types/src/util/mediatype/MediaType.d.ts +148 -0
  147. package/types/src/util/mediatype/index.d.ts +1 -0
  148. package/types/src/util/tokenizer/TextTokenizer.d.ts +39 -0
  149. package/types/src/util/tokenizer/Tokenizer.d.ts +6 -0
  150. package/types/src/util/tokenizer/index.d.ts +2 -0
@@ -0,0 +1,74 @@
1
+ /* Copyright 2021 Readium Foundation. All rights reserved.
2
+ * Use of this source code is governed by a BSD-style license,
3
+ * available in the LICENSE file present in the Github repository of the project.
4
+ */
5
+
6
+ /**
7
+ * Represents a string with multiple translations indexed by a BCP 47 language tag.
8
+ */
9
+ export class LocalizedString {
10
+ public readonly translations: { [key: string]: string };
11
+
12
+ public static readonly UNDEFINED_LANGUAGE = 'undefined';
13
+ public static readonly LANGUAGE_EN = 'en';
14
+
15
+ /**
16
+ * translations can be a string or a collection of language/translation pairs,
17
+ * for a single string the language is assumed to be undefined
18
+ */
19
+ constructor(translations: string | { [key: string]: string }) {
20
+ this.translations =
21
+ typeof translations === 'string'
22
+ ? { [LocalizedString.UNDEFINED_LANGUAGE]: translations }
23
+ : translations;
24
+ }
25
+
26
+ /**
27
+ * Parses a [LocalizedString] from its RWPM JSON representation.
28
+ *
29
+ * "anyOf": [
30
+ * {
31
+ * "type": "string"
32
+ * },
33
+ * {
34
+ * "description": "The language in a language map must be a valid BCP 47 tag.",
35
+ * "type": "object",
36
+ * "patternProperties": {
37
+ * "^((?<grandfathered>(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))|((?<language>([A-Za-z]{2,3}(-(?<extlang>[A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-(?<script>[A-Za-z]{4}))?(-(?<region>[A-Za-z]{2}|[0-9]{3}))?(-(?<variant>[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-(?<extension>[0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(?<privateUse>x(-[A-Za-z0-9]{1,8})+))?)|(?<privateUse2>x(-[A-Za-z0-9]{1,8})+))$": {
38
+ * "type": "string"
39
+ * }
40
+ * },
41
+ * "additionalProperties": false,
42
+ * "minProperties": 1
43
+ * }
44
+ * ]
45
+ */
46
+ public static deserialize(json: any): undefined | LocalizedString {
47
+ if (!(json && (typeof json === 'string' || json.constructor === Object)))
48
+ return;
49
+ return new LocalizedString(json);
50
+ }
51
+
52
+ public serialize(): any {
53
+ return this.translations;
54
+ }
55
+
56
+ /**
57
+ * Returns the first translation for the given [language] BCP–47 tag.
58
+ * If not found, then fallback:
59
+ * 1. on the undefined language
60
+ * 2. on the English language
61
+ * 3. the first translation found
62
+ * 4. returns empty string
63
+ */
64
+ public getTranslation(language?: string): string {
65
+ return (
66
+ this.translations[language || LocalizedString.UNDEFINED_LANGUAGE] ||
67
+ this.translations[LocalizedString.UNDEFINED_LANGUAGE] ||
68
+ this.translations[LocalizedString.LANGUAGE_EN] ||
69
+ (Object.values(this.translations).length === 0
70
+ ? ''
71
+ : Object.values(this.translations)[0])
72
+ );
73
+ }
74
+ }
@@ -0,0 +1,225 @@
1
+ /* Copyright 2021 Readium Foundation. All rights reserved.
2
+ * Use of this source code is governed by a BSD-style license,
3
+ * available in the LICENSE file present in the Github repository of the project.
4
+ */
5
+
6
+ import { arrayfromJSONorString, numberfromJSON } from '../util/JSONParse';
7
+
8
+ /**
9
+ * One or more alternative expressions of the location.
10
+ * https://github.com/readium/architecture/tree/master/models/locators#the-location-object
11
+ */
12
+ export class LocatorLocations {
13
+ /** Contains one or more fragment in the resource referenced by the `Locator`. */
14
+ public readonly fragments: Array<string>;
15
+
16
+ /** Progression in the resource expressed as a percentage (between 0 and 1). */
17
+ public readonly progression?: number;
18
+
19
+ /** Progression in the publication expressed as a percentage (between 0 and 1). */
20
+ public readonly totalProgression?: number;
21
+
22
+ /** An index in the publication (>= 1).*/
23
+ public readonly position?: number;
24
+
25
+ /** Additional locations for extensions. */
26
+ public readonly otherLocations?: Map<string, any>;
27
+
28
+ /**
29
+ * Creates a [Locations].
30
+ */
31
+ constructor(values: {
32
+ fragments?: Array<string>;
33
+ progression?: number;
34
+ totalProgression?: number;
35
+ position?: number;
36
+ otherLocations?: Map<string, any>;
37
+ }) {
38
+ this.fragments = values.fragments ? values.fragments : new Array<string>();
39
+ this.progression = values.progression;
40
+ this.totalProgression = values.totalProgression;
41
+ this.position = values.position;
42
+ this.otherLocations = values.otherLocations;
43
+ }
44
+
45
+ /**
46
+ * Parses a [Locations] from its RWPM JSON representation.
47
+ */
48
+ public static deserialize(json: any): LocatorLocations | undefined {
49
+ if (!json) return;
50
+ const progression = numberfromJSON(json.progression);
51
+ const totalProgression = numberfromJSON(json.totalProgression);
52
+ const position = numberfromJSON(json.position);
53
+
54
+ const otherLocations = new Map<string, any>();
55
+
56
+ const reservedKeys = new Set([
57
+ 'fragment',
58
+ 'fragments',
59
+ 'progression',
60
+ 'totalProgression',
61
+ 'position',
62
+ ]);
63
+ Object.entries(json).forEach(([key, value]) => {
64
+ if (!reservedKeys.has(key)) {
65
+ otherLocations.set(key, value);
66
+ }
67
+ });
68
+
69
+ return new LocatorLocations({
70
+ fragments: arrayfromJSONorString(json.fragments || json.fragment),
71
+ progression:
72
+ progression !== undefined && progression >= 0 && progression <= 1
73
+ ? progression
74
+ : undefined,
75
+ totalProgression:
76
+ totalProgression !== undefined &&
77
+ totalProgression >= 0 &&
78
+ totalProgression <= 1
79
+ ? totalProgression
80
+ : undefined,
81
+ position: position !== undefined && position > 0 ? position : undefined,
82
+ otherLocations: otherLocations.size === 0 ? undefined : otherLocations,
83
+ });
84
+ }
85
+
86
+ /**
87
+ * Serializes a [Locations] to its RWPM JSON representation.
88
+ */
89
+ public serialize(): any {
90
+ const json: any = {};
91
+ if (this.fragments) json.fragments = this.fragments;
92
+ if (this.progression !== undefined) json.progression = this.progression;
93
+ if (this.totalProgression !== undefined)
94
+ json.totalProgression = this.totalProgression;
95
+ if (this.position !== undefined) json.position = this.position;
96
+ if (this.otherLocations) {
97
+ this.otherLocations.forEach((value, key) => (json[key] = value));
98
+ }
99
+ return json;
100
+ }
101
+ }
102
+
103
+ export class LocatorText {
104
+ public readonly after?: string;
105
+ public readonly before?: string;
106
+ public readonly highlight?: string;
107
+
108
+ /**
109
+ * Creates a [Text].
110
+ */
111
+ constructor(values: { after?: string; before?: string; highlight?: string }) {
112
+ this.after = values.after;
113
+ this.before = values.before;
114
+ this.highlight = values.highlight;
115
+ }
116
+
117
+ /**
118
+ * Parses a [Locations] from its RWPM JSON representation.
119
+ */
120
+ public static deserialize(json: any): LocatorText | undefined {
121
+ if (!json) return;
122
+ return new LocatorText({
123
+ after: json.after,
124
+ before: json.before,
125
+ highlight: json.highlight,
126
+ });
127
+ }
128
+
129
+ /**
130
+ * Serializes a [Locations] to its RWPM JSON representation.
131
+ */
132
+ public serialize(): any {
133
+ const json: any = {};
134
+ if (this.after !== undefined) json.after = this.after;
135
+ if (this.before !== undefined) json.before = this.before;
136
+ if (this.highlight !== undefined) json.highlight = this.highlight;
137
+ return json;
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Provides a precise location in a publication in a format that can be stored and shared.
143
+ *
144
+ * There are many different use cases for locators:
145
+ * - getting back to the last position in a publication
146
+ * - bookmarks
147
+ * - highlights & annotations
148
+ * - search results
149
+ * - human-readable (and shareable) reference in a publication
150
+ *
151
+ * https://github.com/readium/architecture/tree/master/locators
152
+ */
153
+ export class Locator {
154
+ /** The URI of the resource that the Locator Object points to. */
155
+ public readonly href: string;
156
+
157
+ /** The media type of the resource that the Locator Object points to. */
158
+ public readonly type: string;
159
+
160
+ /** The title of the chapter or section which is more relevant in the context of this locator. */
161
+ public readonly title?: string;
162
+
163
+ /** One or more alternative expressions of the location. */
164
+ public readonly locations: LocatorLocations;
165
+
166
+ /** Textual context of the locator. */
167
+ public readonly text?: LocatorText;
168
+
169
+ /**
170
+ * Creates a [Locator].
171
+ */
172
+ constructor(values: {
173
+ href: string;
174
+ type: string;
175
+ title?: string;
176
+ locations?: LocatorLocations;
177
+ text?: LocatorText;
178
+ }) {
179
+ this.href = values.href;
180
+ this.type = values.type;
181
+ this.title = values.title;
182
+ this.locations = values.locations
183
+ ? values.locations
184
+ : new LocatorLocations({});
185
+ this.text = values.text;
186
+ }
187
+
188
+ /**
189
+ * Parses a [Link] from its RWPM JSON representation.
190
+ */
191
+ public static deserialize(json: any): Locator | undefined {
192
+ if (!(json && json.href && json.type)) return;
193
+ return new Locator({
194
+ href: json.href,
195
+ type: json.type,
196
+ title: json.title,
197
+ locations: LocatorLocations.deserialize(json.locations),
198
+ text: LocatorText.deserialize(json.text),
199
+ });
200
+ }
201
+
202
+ /**
203
+ * Serializes a [Link] to its RWPM JSON representation.
204
+ */
205
+ public serialize(): any {
206
+ const json: any = { href: this.href, type: this.type };
207
+ if (this.title !== undefined) json.title = this.title;
208
+ if (this.locations) json.locations = this.locations.serialize();
209
+ if (this.text) json.text = this.text.serialize();
210
+ return json;
211
+ }
212
+
213
+ /**
214
+ * Shortcut to get a copy of the [Locator] with different [Locations] sub-properties.
215
+ */
216
+ public copyWithLocations(values: any): Locator {
217
+ return new Locator({
218
+ href: this.href,
219
+ type: this.type,
220
+ title: this.title,
221
+ text: this.text,
222
+ locations: new LocatorLocations({ ...this.locations, ...values }),
223
+ });
224
+ }
225
+ }
@@ -0,0 +1,133 @@
1
+ /* Copyright 2021 Readium Foundation. All rights reserved.
2
+ * Use of this source code is governed by a BSD-style license,
3
+ * available in the LICENSE file present in the Github repository of the project.
4
+ */
5
+
6
+ import { numberfromJSON } from '../util/JSONParse';
7
+ import { Links } from './Link';
8
+ import { LocalizedString } from './LocalizedString';
9
+ import { Locator } from './Locator';
10
+
11
+ /**
12
+ * Holds the metadata of a `LocatorCollection`.
13
+ */
14
+ export class LocatorCollectionMetadata {
15
+ public title?: LocalizedString;
16
+
17
+ /** numberOfItems Indicates the total number of locators in the collection. */
18
+ public numberOfItems?: number;
19
+
20
+ public otherMetadata?: Map<string, any>;
21
+
22
+ /**
23
+ * Creates a [LocatorMetadata].
24
+ */
25
+ constructor(values: {
26
+ title?: LocalizedString;
27
+ numberOfItems?: number;
28
+ otherMetadata?: Map<string, any>;
29
+ }) {
30
+ this.title = values.title;
31
+ this.numberOfItems = values.numberOfItems;
32
+ this.otherMetadata = values.otherMetadata;
33
+ }
34
+
35
+ /**
36
+ * Parses a [LocatorMetadata] from its RWPM JSON representation.
37
+ */
38
+ public static deserialize(json: any): LocatorCollectionMetadata | undefined {
39
+ if (!json) return;
40
+
41
+ const otherMetadata = new Map<string, any>();
42
+
43
+ const reservedKeys = new Set(['title', 'numberOfItems']);
44
+ Object.entries(json).forEach(([key, value]) => {
45
+ if (!reservedKeys.has(key)) {
46
+ otherMetadata.set(key, value);
47
+ }
48
+ });
49
+
50
+ return new LocatorCollectionMetadata({
51
+ title: LocalizedString.deserialize(json.title),
52
+ numberOfItems: numberfromJSON(json.numberOfItems),
53
+ otherMetadata: otherMetadata.size === 0 ? undefined : otherMetadata,
54
+ });
55
+ }
56
+
57
+ /**
58
+ * Serializes a [LocatorMetadata] to its RWPM JSON representation.
59
+ */
60
+ public serialize(): any {
61
+ const json: any = {};
62
+ if (this.title) json.title = this.title.serialize();
63
+ if (this.numberOfItems !== undefined)
64
+ json.numberOfItems = this.numberOfItems;
65
+ if (this.otherMetadata) {
66
+ this.otherMetadata.forEach((value, key) => (json[key] = value));
67
+ }
68
+ return json;
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Represents a sequential list of `Locator` objects.
74
+ *
75
+ * For example, a search result or a list of positions.
76
+ */
77
+ export class LocatorCollection {
78
+ public metadata: LocatorCollectionMetadata;
79
+ public links: Links;
80
+ public locators: Array<Locator>;
81
+
82
+ /**
83
+ * Creates a [LocatorCollection].
84
+ */
85
+ constructor(values: {
86
+ metadata?: LocatorCollectionMetadata;
87
+ links?: Links;
88
+ locators?: Array<Locator>;
89
+ }) {
90
+ this.metadata = values.metadata ?? new LocatorCollectionMetadata({});
91
+ this.links = values.links ?? new Links([]);
92
+ this.locators = values.locators ?? [];
93
+ }
94
+
95
+ /**
96
+ * Parses a [LocatorCollection] from its RWPM JSON representation.
97
+ */
98
+ public static deserialize(json: any): LocatorCollection | undefined {
99
+ if (!json) return;
100
+
101
+ let locators: Array<Locator> | undefined;
102
+
103
+ let jsonList = json.locators;
104
+
105
+ if (jsonList && jsonList instanceof Array) {
106
+ locators = jsonList
107
+ .map<Locator>(x => Locator.deserialize(x) as Locator)
108
+ .filter(x => x !== undefined);
109
+ }
110
+
111
+ return new LocatorCollection({
112
+ metadata: LocatorCollectionMetadata.deserialize(json.metadata),
113
+ links: Links.deserialize(json.links),
114
+ locators,
115
+ });
116
+ }
117
+
118
+ /**
119
+ * Serializes a [LocatorCollection] to its RWPM JSON representation.
120
+ */
121
+ public serialize(): any {
122
+ const json: any = {};
123
+ if (this.metadata) {
124
+ let serializedMetadata = this.metadata.serialize();
125
+ if (serializedMetadata && Object.keys(serializedMetadata).length > 0)
126
+ json.metadata = serializedMetadata;
127
+ }
128
+ if (this.links && this.links.items.length > 0)
129
+ json.links = this.links.serialize();
130
+ if (this.locators) json.locators = this.locators.map(x => x.serialize());
131
+ return json;
132
+ }
133
+ }
@@ -0,0 +1,256 @@
1
+ /* Copyright 2021 Readium Foundation. All rights reserved.
2
+ * Use of this source code is governed by a BSD-style license,
3
+ * available in the LICENSE file present in the Github repository of the project.
4
+ */
5
+
6
+ import { Metadata } from './Metadata';
7
+ import { Link, Links } from './Link';
8
+ import { arrayfromJSONorString } from '../util/JSONParse';
9
+ import { PublicationCollection } from './PublicationCollection';
10
+ import { MediaType } from '../util/mediatype/MediaType';
11
+ import { Locator, LocatorLocations } from "./Locator";
12
+
13
+ /** Holds the metadata of a Readium publication, as described in
14
+ * the Readium Web Publication Manifest.
15
+ * See. https://readium.org/webpub-manifest/
16
+ */
17
+ export class Manifest {
18
+ public readonly context?: Array<string>;
19
+
20
+ public readonly metadata: Metadata;
21
+
22
+ public readonly links: Links;
23
+
24
+ /** Identifies a list of resources in reading order for the publication. */
25
+ public readonly readingOrder: Links;
26
+
27
+ /** Identifies resources that are necessary for rendering the publication. */
28
+ public readonly resources?: Links;
29
+
30
+ /** Identifies the collection that contains a table of contents. */
31
+ public readonly tableOfContents?: Links;
32
+
33
+ public readonly subcollections?: Map<string, Array<PublicationCollection>>;
34
+
35
+ constructor(values: {
36
+ context?: Array<string>;
37
+ metadata: Metadata;
38
+ links: Links;
39
+ readingOrder: Links;
40
+ resources?: Links;
41
+ tableOfContents?: Links;
42
+ subcollections?: Map<string, Array<PublicationCollection>>;
43
+ }) {
44
+ this.context = values.context;
45
+ this.metadata = values.metadata;
46
+ this.links = values.links;
47
+ this.readingOrder = values.readingOrder;
48
+ this.resources = values.resources;
49
+ this.tableOfContents = values.tableOfContents;
50
+ this.subcollections = values.subcollections;
51
+ }
52
+
53
+ /**
54
+ * Parses a [Publication] from its RWPM JSON representation.
55
+ *
56
+ * https://readium.org/webpub-manifest/
57
+ * https://readium.org/webpub-manifest/schema/publication.schema.json
58
+ */
59
+ public static deserialize(json: any): Manifest | undefined {
60
+ if (!json) return;
61
+
62
+ const metadata = Metadata.deserialize(json.metadata);
63
+
64
+ if (!metadata) return;
65
+
66
+ const links = Links.deserialize(json.links);
67
+
68
+ if (!links) return;
69
+
70
+ const readingOrder = Links.deserialize(
71
+ json.readingOrder ? json.readingOrder : json.spine
72
+ );
73
+
74
+ if (!readingOrder) return;
75
+
76
+ return new Manifest({
77
+ context: arrayfromJSONorString(json['@context']),
78
+ metadata,
79
+ links,
80
+ readingOrder,
81
+ resources: Links.deserialize(json.resources),
82
+ tableOfContents: Links.deserialize(json.toc),
83
+ subcollections: PublicationCollection.deserializeCollections({
84
+ sub: json.sub,
85
+ }),
86
+ });
87
+ }
88
+
89
+ /**
90
+ * Serializes a [Publication] to its RWPM JSON representation.
91
+ */
92
+ public serialize(): any {
93
+ const json: any = {};
94
+ if (this.context !== undefined) json['@context'] = this.context;
95
+ json.metadata = this.metadata.serialize();
96
+ json.links = this.links.serialize();
97
+ json.readingOrder = this.readingOrder.serialize();
98
+ if (this.resources) json.resources = this.resources.serialize();
99
+ if (this.tableOfContents) json.toc = this.tableOfContents.serialize();
100
+ PublicationCollection.serializeCollection(json, this.subcollections);
101
+ return json;
102
+ }
103
+
104
+ /** Finds the first link with the given relation in the manifest's links. */
105
+ public linkWithRel(rel: string): Link | undefined {
106
+ const links = new Array<Links>();
107
+ links.push(this.readingOrder);
108
+ if (this.resources) {
109
+ links.push(this.resources);
110
+ }
111
+ links.push(this.links);
112
+
113
+ let result: Link | undefined;
114
+
115
+ for (const collection of links) {
116
+ result = collection.findWithRel(rel);
117
+ if (result !== undefined) {
118
+ return result;
119
+ }
120
+ }
121
+
122
+ return result;
123
+ }
124
+
125
+ /** Finds all the links with the given relation in the manifest's links. */
126
+ public linksWithRel(rel: string): Array<Link> {
127
+ const result = [];
128
+ result.push(this.readingOrder.filterByRel(rel));
129
+ if (this.resources) {
130
+ result.push(this.resources.filterByRel(rel));
131
+ }
132
+ result.push(this.links.filterByRel(rel));
133
+
134
+ return result.reduce((acc, val) => acc.concat(val), []);
135
+ }
136
+
137
+ /**
138
+ * Creates a new [Locator] object from a [Link] to a resource of this manifest.
139
+ * Returns null if the resource is not found in this manifest.
140
+ */
141
+ public locatorFromLink(link: Link): Locator | undefined {
142
+ const components = link.href.split("#");
143
+ const href = components.length == 2 ? components[0] : link.href;
144
+ const resourceLink = this.linkWithHref(href);
145
+ if(!resourceLink) return undefined;
146
+ const type = resourceLink.type;
147
+ if(!type) return undefined;
148
+ const fragment = components.length == 2 ? components[1] : undefined;
149
+
150
+ return new Locator({
151
+ href,
152
+ type,
153
+ title: resourceLink.title ?? link.title,
154
+ locations: new LocatorLocations({
155
+ fragments: fragment ? [fragment] : undefined,
156
+ progression: fragment ? undefined : 0.0,
157
+ })
158
+ });
159
+ }
160
+
161
+ /** Finds the first Link having the given `href` in the publication's links. */
162
+ public linkWithHref(href: string): Link | undefined {
163
+ const find = (links: Array<Links>): Link | undefined => {
164
+ let result = undefined;
165
+
166
+ for (const collection of links) {
167
+ result = collection.findWithHref(href);
168
+ if (result !== undefined) {
169
+ return result;
170
+ }
171
+ }
172
+
173
+ const children = new Array<Links>();
174
+
175
+ links.forEach(item => {
176
+ const arr = [];
177
+ for (const link of item.items) {
178
+ if (link.alternates) {
179
+ arr.push(link.alternates);
180
+ }
181
+ if (link.children) {
182
+ arr.push(link.children);
183
+ }
184
+ }
185
+ children.push(...arr);
186
+ });
187
+
188
+ if (children.length > 0) {
189
+ result = find(children);
190
+ }
191
+
192
+ return result;
193
+ };
194
+
195
+ const links: Array<Links> = [];
196
+ links.push(this.readingOrder);
197
+ if (this.resources) {
198
+ links.push(this.resources);
199
+ }
200
+ links.push(this.links);
201
+
202
+ const link = find(links);
203
+
204
+ if (link !== undefined) {
205
+ return link;
206
+ }
207
+
208
+ const parts = href.split(/[#]/);
209
+
210
+ if (parts.length < 2) return;
211
+
212
+ const shortHref = parts[0];
213
+
214
+ return this.linkWithHref(shortHref);
215
+ }
216
+
217
+ /** The URL where this publication is served, computed from the `Link` with `self` relation.
218
+ * e.g. https://provider.com/pub1293/manifest.json gives https://provider.com/pub1293/
219
+ */
220
+ public get baseURL(): string | undefined {
221
+ const selfLink = this.links.items.find(
222
+ el => el.rels && el.rels.has('self')
223
+ );
224
+ if (selfLink) {
225
+ let href = selfLink.href;
226
+ if (href) {
227
+ const li = href.lastIndexOf('/');
228
+ const lastpart = li === -1 ? undefined : href.substring(li + 1);
229
+
230
+ href = href.replace(new RegExp('/?$query$'), '');
231
+ href = href.replace(new RegExp('//$'), '');
232
+ if (lastpart) {
233
+ href = href.replace(new RegExp(lastpart + '$'), '');
234
+ }
235
+ }
236
+ return href;
237
+ }
238
+ return undefined;
239
+ }
240
+
241
+ /**
242
+ * Sets the URL where this [Publication]'s RWPM manifest is served.
243
+ */
244
+ public setSelfLink(href: string): void {
245
+ this.links.items = this.links.items.filter(
246
+ x => x.rels === undefined || !x.rels?.has('self')
247
+ );
248
+ this.links.items.push(
249
+ new Link({
250
+ href,
251
+ type: MediaType.READIUM_WEBPUB_MANIFEST.string,
252
+ rels: new Set<string>(['self']),
253
+ })
254
+ );
255
+ }
256
+ }