@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,329 @@
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 {
7
+ arrayfromJSONorString,
8
+ datefromJSON,
9
+ positiveNumberfromJSON,
10
+ } from '../util/JSONParse';
11
+ import { BelongsTo } from './BelongsTo';
12
+ import { Contributors } from './Contributor';
13
+ import { LocalizedString } from './LocalizedString';
14
+ import { ReadingProgression } from './ReadingProgression';
15
+ import { Subjects } from './Subject';
16
+
17
+ /**
18
+ * https://readium.org/webpub-manifest/schema/metadata.schema.json
19
+ *
20
+ * readingProgression : This contains the reading progression as declared in the
21
+ * publication, so it might be [auto]. To lay out the content, use [effectiveReadingProgression]
22
+ * to get the calculated reading progression from the declared direction and the language.
23
+ * otherMetadata Additional metadata for extensions, as a JSON dictionary.
24
+ */
25
+ export class Metadata {
26
+ public title: LocalizedString;
27
+ public typeUri?: string;
28
+ public identifier?: string;
29
+ public subtitle?: LocalizedString;
30
+ public sortAs?: LocalizedString;
31
+ public artists?: Contributors;
32
+ public authors?: Contributors;
33
+ public colorists?: Contributors;
34
+ public contributors?: Contributors;
35
+ public editors?: Contributors;
36
+ public illustrators?: Contributors;
37
+ public inkers?: Contributors;
38
+ public letterers?: Contributors;
39
+ public narrators?: Contributors;
40
+ public pencilers?: Contributors;
41
+ public translators?: Contributors;
42
+ public languages?: Array<string>;
43
+ public description?: string;
44
+ public publishers?: Contributors;
45
+ public imprints?: Contributors;
46
+ public published?: Date;
47
+ public modified?: Date;
48
+ public subjects?: Subjects;
49
+ public belongsTo?: BelongsTo;
50
+ public belongsToCollections?: Contributors;
51
+ public belongsToSeries?: Contributors;
52
+ public readingProgression?: ReadingProgression;
53
+ public duration?: number;
54
+ public numberOfPages?: number;
55
+ public otherMetadata?: { [key: string]: any };
56
+
57
+ /**All metadata not in otherMetadata */
58
+ private static readonly mappedProperties = [
59
+ 'title',
60
+ '@type',
61
+ 'identifier',
62
+ 'subtitle',
63
+ 'sortAs',
64
+ 'artist',
65
+ 'author',
66
+ 'colorist',
67
+ 'contributor',
68
+ 'editor',
69
+ 'illustrator',
70
+ 'inker',
71
+ 'letterer',
72
+ 'narrator',
73
+ 'penciler',
74
+ 'translator',
75
+ 'language',
76
+ 'description',
77
+ 'publisher',
78
+ 'imprint',
79
+ 'published',
80
+ 'modified',
81
+ 'subject',
82
+ 'belongsTo',
83
+ 'readingProgression',
84
+ 'duration',
85
+ 'numberOfPages',
86
+ ];
87
+
88
+ /** Creates [Metadata] object */
89
+ constructor(values: {
90
+ title: LocalizedString;
91
+ typeUri?: string;
92
+ identifier?: string;
93
+ subtitle?: LocalizedString;
94
+ sortAs?: LocalizedString;
95
+ artists?: Contributors;
96
+ authors?: Contributors;
97
+ colorists?: Contributors;
98
+ contributors?: Contributors;
99
+ editors?: Contributors;
100
+ illustrators?: Contributors;
101
+ inkers?: Contributors;
102
+ letterers?: Contributors;
103
+ narrators?: Contributors;
104
+ pencilers?: Contributors;
105
+ translators?: Contributors;
106
+ languages?: Array<string>;
107
+ description?: string;
108
+ publishers?: Contributors;
109
+ imprints?: Contributors;
110
+ published?: Date;
111
+ modified?: Date;
112
+ subjects?: Subjects;
113
+ belongsTo?: BelongsTo;
114
+ belongsToCollections?: Contributors;
115
+ belongsToSeries?: Contributors;
116
+ readingProgression?: ReadingProgression;
117
+ duration?: number;
118
+ numberOfPages?: number;
119
+ otherMetadata?: { [key: string]: any };
120
+ }) {
121
+ //title always required
122
+ this.title = values.title as LocalizedString;
123
+ this.typeUri = values.typeUri;
124
+ this.identifier = values.identifier;
125
+ this.subtitle = values.subtitle;
126
+ this.sortAs = values.sortAs;
127
+ this.artists = values.artists;
128
+ this.authors = values.authors;
129
+ this.colorists = values.colorists;
130
+ this.contributors = values.contributors;
131
+ this.editors = values.editors;
132
+ this.illustrators = values.illustrators;
133
+ this.inkers = values.inkers;
134
+ this.letterers = values.letterers;
135
+ this.narrators = values.narrators;
136
+ this.pencilers = values.pencilers;
137
+ this.translators = values.translators;
138
+ this.languages = values.languages;
139
+ this.description = values.description;
140
+ this.publishers = values.publishers;
141
+ this.imprints = values.imprints;
142
+ this.published = values.published;
143
+ this.modified = values.modified;
144
+ this.subjects = values.subjects;
145
+ this.belongsTo = values.belongsTo;
146
+ this.belongsToCollections = values.belongsToCollections;
147
+ this.belongsToSeries = values.belongsToSeries;
148
+
149
+ if (
150
+ this.belongsToCollections &&
151
+ this.belongsToCollections.items.length > 0
152
+ ) {
153
+ if (!this.belongsTo) {
154
+ this.belongsTo = new BelongsTo();
155
+ }
156
+ this.belongsTo.items.set('collection', this.belongsToCollections);
157
+ }
158
+
159
+ if (this.belongsToSeries && this.belongsToSeries.items.length > 0) {
160
+ if (!this.belongsTo) {
161
+ this.belongsTo = new BelongsTo();
162
+ }
163
+ this.belongsTo.items.set('series', this.belongsToSeries);
164
+ }
165
+
166
+ this.readingProgression = values.readingProgression;
167
+ this.duration = values.duration;
168
+ this.numberOfPages = values.numberOfPages;
169
+ this.otherMetadata = values.otherMetadata;
170
+ }
171
+
172
+ /**
173
+ * Parses a [Metadata] from its RWPM JSON representation.
174
+ *
175
+ * If the metadata can't be parsed, a warning will be logged with [warnings].
176
+ */
177
+ public static deserialize(json: any): Metadata | undefined {
178
+ if (!(json && json.title)) return;
179
+
180
+ const title = LocalizedString.deserialize(json.title) as LocalizedString;
181
+ const typeUri = json['@type'];
182
+ const identifier = json.identifier;
183
+ const subtitle = LocalizedString.deserialize(json.subtitle);
184
+ const sortAs = LocalizedString.deserialize(json.sortAs);
185
+ const artists = Contributors.deserialize(json.artist);
186
+ const authors = Contributors.deserialize(json.author);
187
+ const colorists = Contributors.deserialize(json.colorist);
188
+ const contributors = Contributors.deserialize(json.contributor);
189
+ const editors = Contributors.deserialize(json.editor);
190
+ const illustrators = Contributors.deserialize(json.illustrator);
191
+ const inkers = Contributors.deserialize(json.inker);
192
+ const letterers = Contributors.deserialize(json.letterer);
193
+ const narrators = Contributors.deserialize(json.narrator);
194
+ const pencilers = Contributors.deserialize(json.penciler);
195
+ const translators = Contributors.deserialize(json.translator);
196
+ const languages = arrayfromJSONorString(json.language);
197
+ const description = json.description;
198
+ const publishers = Contributors.deserialize(json.publisher);
199
+ const imprints = Contributors.deserialize(json.imprint);
200
+ const published = datefromJSON(json.published);
201
+ const modified = datefromJSON(json.modified);
202
+ const subjects = Subjects.deserialize(json.subject);
203
+ const belongsTo = BelongsTo.deserialize(json.belongsTo);
204
+ const readingProgression = json.readingProgression;
205
+ const duration = positiveNumberfromJSON(json.duration);
206
+ const numberOfPages = positiveNumberfromJSON(json.numberOfPages);
207
+
208
+ let otherMetadata = Object.assign({}, json);
209
+ Metadata.mappedProperties.forEach(x => delete otherMetadata[x]);
210
+ if (Object.keys(otherMetadata).length === 0) {
211
+ otherMetadata = undefined;
212
+ }
213
+
214
+ return new Metadata({
215
+ title,
216
+ typeUri,
217
+ identifier,
218
+ subtitle,
219
+ sortAs,
220
+ artists,
221
+ authors,
222
+ colorists,
223
+ contributors,
224
+ editors,
225
+ illustrators,
226
+ inkers,
227
+ letterers,
228
+ narrators,
229
+ pencilers,
230
+ translators,
231
+ languages,
232
+ description,
233
+ publishers,
234
+ imprints,
235
+ published,
236
+ modified,
237
+ subjects,
238
+ belongsTo,
239
+ readingProgression,
240
+ duration,
241
+ numberOfPages,
242
+ otherMetadata,
243
+ });
244
+ }
245
+
246
+ /**
247
+ * Serializes a [Metadata] to its RWPM JSON representation.
248
+ */
249
+ public serialize(): any {
250
+ const json: any = { title: this.title.serialize() };
251
+ if (this.typeUri !== undefined) json['@type'] = this.typeUri;
252
+ if (this.identifier !== undefined) json.identifier = this.identifier;
253
+ if (this.subtitle) json.subtitle = this.subtitle.serialize();
254
+ if (this.sortAs) json.sortAs = this.sortAs.serialize();
255
+ if (this.editors) json.editor = this.editors.serialize();
256
+ if (this.artists) json.artist = this.artists.serialize();
257
+ if (this.authors) json.author = this.authors.serialize();
258
+ if (this.colorists) json.colorist = this.colorists.serialize();
259
+ if (this.contributors) json.contributor = this.contributors.serialize();
260
+ if (this.illustrators) json.illustrator = this.illustrators.serialize();
261
+ if (this.letterers) json.letterer = this.letterers.serialize();
262
+ if (this.narrators) json.narrator = this.narrators.serialize();
263
+ if (this.pencilers) json.penciler = this.pencilers.serialize();
264
+ if (this.translators) json.translator = this.translators.serialize();
265
+ if (this.inkers) json.inker = this.inkers.serialize();
266
+ if (this.languages) json.language = this.languages;
267
+ if (this.description !== undefined) json.description = this.description;
268
+ if (this.publishers) json.publisher = this.publishers.serialize();
269
+ if (this.imprints) json.imprint = this.imprints.serialize();
270
+ if (this.published !== undefined)
271
+ json.published = this.published.toISOString();
272
+ if (this.modified !== undefined)
273
+ json.modified = this.modified.toISOString();
274
+ if (this.subjects) json.subject = this.subjects.serialize();
275
+ if (this.belongsTo) json.belongsTo = this.belongsTo.serialize();
276
+ if (this.readingProgression)
277
+ json.readingProgression = this.readingProgression;
278
+ if (this.duration !== undefined) json.duration = this.duration;
279
+ if (this.numberOfPages !== undefined)
280
+ json.numberOfPages = this.numberOfPages;
281
+
282
+ if (this.otherMetadata) {
283
+ const metadata = this.otherMetadata;
284
+ Object.keys(metadata).forEach(x => (json[x] = metadata[x]));
285
+ }
286
+
287
+ return json;
288
+ }
289
+
290
+ /**
291
+ * Computes a [ReadingProgression] when the value of [readingProgression] is set to
292
+ * auto, using the publication language.
293
+ *
294
+ * See this issue for more details: https://github.com/readium/architecture/issues/113
295
+ */
296
+ public get effectiveReadingProgression(): ReadingProgression {
297
+ if (
298
+ this.readingProgression &&
299
+ this.readingProgression !== ReadingProgression.auto
300
+ ) {
301
+ return this.readingProgression;
302
+ }
303
+
304
+ // https://github.com/readium/readium-css/blob/develop/docs/CSS16-internationalization.md#missing-page-progression-direction
305
+ if (this.languages?.length !== 1) {
306
+ return ReadingProgression.ltr;
307
+ }
308
+
309
+ const primaryLang = this.languages[0].toLowerCase();
310
+
311
+ if (primaryLang === 'zh-hant' || primaryLang === 'zh-tw') {
312
+ return ReadingProgression.rtl;
313
+ }
314
+
315
+ // The region is ignored for ar, fa and he.
316
+ const lang = primaryLang.split('-')[0];
317
+
318
+ switch (lang) {
319
+ case 'ar':
320
+ return ReadingProgression.rtl;
321
+ case 'fa':
322
+ return ReadingProgression.rtl;
323
+ case 'he':
324
+ return ReadingProgression.rtl;
325
+ default:
326
+ return ReadingProgression.ltr;
327
+ }
328
+ }
329
+ }
@@ -0,0 +1,44 @@
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
+ * Properties associated to the linked resource.
8
+ *
9
+ * This is opened for extensions.
10
+ * https://readium.org/webpub-manifest/schema/link.schema.json
11
+ */
12
+ export class Properties {
13
+ public otherProperties: { [key: string]: any };
14
+
15
+ constructor(values: { [key: string]: any }) {
16
+ this.otherProperties = values;
17
+ }
18
+
19
+ /**
20
+ * Creates a [Properties] from its RWPM JSON representation.
21
+ */
22
+ public static deserialize(json: any): Properties | undefined {
23
+ if (!json) return;
24
+ return new Properties(json);
25
+ }
26
+
27
+ /**
28
+ * Serializes a [Properties] to its RWPM JSON representation.
29
+ */
30
+ public serialize(): any {
31
+ return this.otherProperties;
32
+ }
33
+
34
+ /**
35
+ * Makes a copy of this [Properties] after merging in the given additional other [properties].
36
+ */
37
+ public add(properties: { [key: string]: any }): Properties {
38
+ const _properties = Object.assign({}, this.otherProperties);
39
+ for (const property in properties) {
40
+ _properties[property] = properties[property];
41
+ }
42
+ return new Properties(_properties);
43
+ }
44
+ }
@@ -0,0 +1,148 @@
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 { Link, Links } from './Link';
7
+ import { Locator } from './Locator';
8
+ import { Manifest } from './Manifest';
9
+ import { Metadata } from './Metadata';
10
+ import { EmptyFetcher, Fetcher } from '../fetcher/Fetcher';
11
+ import { PublicationCollection } from './PublicationCollection';
12
+ import { Resource } from '../fetcher/Resource';
13
+ import { GuidedNavigationDocument } from "./GuidedNavigation";
14
+ import { URITemplate } from "../util";
15
+
16
+ export type ServiceFactory = () => null;
17
+
18
+ /** Shared model for a Readium Publication. */
19
+ export class Publication {
20
+ /** The manifest holding the publication metadata extracted from the publication file */
21
+ public manifest: Manifest;
22
+ private readonly fetcher: Fetcher = new EmptyFetcher();
23
+
24
+ // Shortcuts to manifest properties
25
+ public readonly context?: Array<string>;
26
+ public readonly metadata: Metadata;
27
+ public readonly links: Links;
28
+ /** Identifies a list of resources in reading order for the publication. */
29
+ public readonly readingOrder: Links;
30
+ /** Identifies resources that are necessary for rendering the publication. */
31
+ public readonly resources?: Links;
32
+ /** Identifies the collection that contains a table of contents. */
33
+ public readonly tableOfContents?: Links;
34
+ /** Identifies the collection that contains sub collections. */
35
+ public readonly subcollections?: Map<string, Array<PublicationCollection>>;
36
+
37
+ constructor(values: { manifest: Manifest; fetcher?: Fetcher }) {
38
+ if (values.fetcher) this.fetcher = values.fetcher;
39
+ this.manifest = values.manifest;
40
+ this.context = values.manifest.context;
41
+ this.metadata = values.manifest.metadata;
42
+ this.links = values.manifest.links;
43
+ this.readingOrder = values.manifest.readingOrder;
44
+ this.resources = values.manifest.resources;
45
+ this.tableOfContents = values.manifest.tableOfContents;
46
+ this.subcollections = values.manifest.subcollections;
47
+ }
48
+
49
+ /** The URL where this publication is served, computed from the `Link` with `self` relation.
50
+ * e.g. https://provider.com/pub1293/manifest.json gives https://provider.com/pub1293/
51
+ */
52
+ public get baseURL(): string | undefined {
53
+ return this.manifest.baseURL;
54
+ }
55
+
56
+ /** Finds the first Link having the given `href` in the publication's links. */
57
+ public linkWithHref(href: string): Link | undefined {
58
+ return this.manifest.linkWithHref(href);
59
+ }
60
+
61
+ /**
62
+ * Returns the [links] of the first child [PublicationCollection] with the given role, or an
63
+ * empty list.
64
+ */
65
+ public linksWithRole(role: string): Links | undefined {
66
+ const list = this.subcollections?.get(role);
67
+ return list && list.length > 0 ? list[0].links : undefined;
68
+ }
69
+
70
+ /** Finds all the links with the given relation in the publication's links. */
71
+ public linksWithRel(rel: string): Array<Link> {
72
+ return this.manifest.linksWithRel(rel);
73
+ }
74
+
75
+ /**
76
+ * Finds the first [Link] having the given [rel] in the publications's links.
77
+ */
78
+ public linkWithRel(rel: string): Link | undefined {
79
+ return this.manifest.linkWithRel(rel);
80
+ }
81
+
82
+ public async positionsFromManifest(): Promise<Locator[]> {
83
+ const positionListLink = this.manifest.links.findWithMediaType(
84
+ 'application/vnd.readium.position-list+json'
85
+ );
86
+ if (positionListLink === undefined) return [];
87
+ const positionListJSON = (await this.get(
88
+ positionListLink
89
+ ).readAsJSON()) as { positions: unknown[] | null, total: number };
90
+ if(!positionListJSON['total']) return [];
91
+ return (positionListJSON['positions'] as unknown[]) // Get the array for the positions key
92
+ .map(pos => Locator.deserialize(pos)) // Parse locators
93
+ .filter(l => l !== undefined) as Locator[]; // Filter out failures
94
+ }
95
+
96
+ public async guideForLink(link: Link): Promise<GuidedNavigationDocument | undefined> {
97
+ const findGNLink = (l: Link): Link | undefined => l.alternates?.findWithMediaType(
98
+ 'application/guided-navigation+json'
99
+ );
100
+
101
+ let guidedNavigationLink = findGNLink(link);
102
+ if(!guidedNavigationLink) {
103
+ // If provided link doesn't have a guided navigation link,
104
+ // search through the manfiest for a matching Link based on the `href`
105
+ const foundLink = this.linkWithHref(link.href);
106
+ if (foundLink !== undefined) {
107
+ // A Link was found, attempt to find a guided navigation link
108
+ guidedNavigationLink = findGNLink(foundLink);
109
+ }
110
+ }
111
+
112
+ if(!guidedNavigationLink) {
113
+ // Still unable to find a guided navigation link, try to use the manifest's global document
114
+ guidedNavigationLink = this.manifest.links.findWithMediaType(
115
+ 'application/guided-navigation+json'
116
+ );
117
+ }
118
+
119
+ // Unable to find any guided navigation Link, give up
120
+ if(!guidedNavigationLink) return;
121
+
122
+ let href = guidedNavigationLink.href;
123
+ if(guidedNavigationLink.templated) {
124
+ // The manifest's guided navigation link is templated, expand it
125
+ const template = new URITemplate(href);
126
+ const params: { [param: string]: string } = {};
127
+ if(template.parameters.has('ref')) {
128
+ // The template has a `ref` parameter that could be used to reduce the returned doc's size
129
+ params['ref'] = link.href;
130
+ }
131
+ href = new URITemplate(href).expand(params);
132
+ }
133
+
134
+ // Fetch the guided navigation document
135
+ const guidedNavigationJSON = (await this.get(new Link({
136
+ href,
137
+ })).readAsJSON());
138
+ return GuidedNavigationDocument.deserialize(guidedNavigationJSON);
139
+ }
140
+
141
+ /**
142
+ * Returns the resource targeted by the given non-templated [link].
143
+ */
144
+ public get(link: Link): Resource {
145
+ // TODO warn about expanding templated links
146
+ return this.fetcher.get(link);
147
+ }
148
+ }
@@ -0,0 +1,144 @@
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 { Links } from './Link';
7
+
8
+ /**
9
+ * Core Collection Model
10
+ *
11
+ * https://readium.org/webpub-manifest/schema/subcollection.schema.json
12
+ * Can be used as extension point in the Readium Web Publication Manifest.
13
+ */
14
+ export class PublicationCollection {
15
+ public metadata?: Map<string, any>;
16
+ public links: Links;
17
+ public subcollections?: Map<string, Array<PublicationCollection>>;
18
+
19
+ /**
20
+ * Creates a [PublicationCollection].
21
+ */
22
+ constructor(values: {
23
+ metadata?: Map<string, any>;
24
+ links: Links;
25
+ subcollections?: Map<string, Array<PublicationCollection>>;
26
+ }) {
27
+ this.metadata = values.metadata;
28
+ this.links = values.links;
29
+ this.subcollections = values.subcollections;
30
+ }
31
+
32
+ /**
33
+ * Parses a [PublicationCollection] from its RWPM JSON representation.
34
+ */
35
+ public static deserialize(json: any): PublicationCollection | undefined {
36
+ if (!json) return;
37
+
38
+ let links: Links | undefined;
39
+ let metadata: Map<string, any> | undefined;
40
+ let subcollections: Map<string, Array<PublicationCollection>> | undefined;
41
+
42
+ if (json instanceof Array) {
43
+ // Parses an array of links.
44
+ links = Links.deserialize(json);
45
+ } else if (json instanceof Object) {
46
+ // Parses a sub-collection object.
47
+ links = Links.deserialize(json.links);
48
+
49
+ metadata = new Map<string, any>();
50
+
51
+ if (json.metadata) {
52
+ Object.entries(json.metadata).forEach(([key, value]) => {
53
+ metadata?.set(key, value);
54
+ });
55
+ }
56
+
57
+ subcollections = PublicationCollection.deserializeCollections(json);
58
+ } else {
59
+ return;
60
+ }
61
+
62
+ if (!links || links.items.length === 0) return;
63
+
64
+ return new PublicationCollection({
65
+ metadata: metadata?.size ? metadata : undefined,
66
+ links,
67
+ subcollections: subcollections?.size ? subcollections : undefined,
68
+ });
69
+ }
70
+
71
+ /**
72
+ * Parses a map of [PublicationCollection] indexed by their roles from its RWPM JSON representation.
73
+ *
74
+ */
75
+ public static deserializeCollections(
76
+ json: any
77
+ ): Map<string, Array<PublicationCollection>> | undefined {
78
+ if (!json) return;
79
+ const collections = new Map<string, Array<PublicationCollection>>();
80
+ Object.entries(json).forEach(([role, subJSON]) => {
81
+ if (role !== 'links' && role !== 'metadata') {
82
+ // Parses a list of links or a single collection object.
83
+ const collection = PublicationCollection.deserialize(subJSON);
84
+
85
+ if (collection) {
86
+ const list = new Array<PublicationCollection>();
87
+ list.push(collection);
88
+ collections.set(role, list);
89
+ } else if (subJSON instanceof Array) {
90
+ // Parses a list of collection objects.
91
+ const list = subJSON
92
+ .map<PublicationCollection>(
93
+ item =>
94
+ PublicationCollection.deserialize(item) as PublicationCollection
95
+ )
96
+ .filter(x => x !== undefined);
97
+
98
+ collections.set(role, list);
99
+ }
100
+ }
101
+ });
102
+
103
+ return collections.size ? collections : undefined;
104
+ }
105
+
106
+ /**
107
+ * Serializes a [PublicationCollection] to its RWPM JSON representation.
108
+ */
109
+ public serialize(): any {
110
+ const json: any = {};
111
+
112
+ if (this.metadata) {
113
+ json.metadata = {};
114
+ this.metadata.forEach(
115
+ (value: any, key: string) => (json.metadata[key] = value)
116
+ );
117
+ }
118
+
119
+ if (this.links.items.length) {
120
+ json.links = this.links.serialize();
121
+ }
122
+
123
+ PublicationCollection.serializeCollection(json, this.subcollections);
124
+
125
+ return json;
126
+ }
127
+
128
+ public static serializeCollection(
129
+ json: any,
130
+ subcollections?: Map<string, Array<PublicationCollection>>
131
+ ) {
132
+ if (subcollections && subcollections.size > 0) {
133
+ subcollections.forEach(
134
+ (value: Array<PublicationCollection>, key: string) => {
135
+ if (value.length === 1) {
136
+ json[key] = value[0].serialize();
137
+ } else {
138
+ json[key] = value.map(x => x.serialize());
139
+ }
140
+ }
141
+ );
142
+ }
143
+ }
144
+ }
@@ -0,0 +1,28 @@
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 { Page } from './presentation/Presentation';
7
+
8
+ export enum ReadingProgression {
9
+ auto = 'auto',
10
+ btt = 'btt',
11
+ ltr = 'ltr',
12
+ rtl = 'rtl',
13
+ ttb = 'ttb',
14
+ }
15
+
16
+ export function leadingPage(readingProgression: ReadingProgression): Page {
17
+ switch (readingProgression) {
18
+ case ReadingProgression.auto:
19
+ case ReadingProgression.ttb:
20
+ case ReadingProgression.ltr:
21
+ return Page.left;
22
+ case ReadingProgression.rtl:
23
+ case ReadingProgression.btt:
24
+ return Page.right;
25
+ default:
26
+ return Page.left;
27
+ }
28
+ }