aiiinotate 0.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 (88) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +61 -0
  3. package/cli/import.js +142 -0
  4. package/cli/index.js +26 -0
  5. package/cli/io.js +105 -0
  6. package/cli/migrate.js +123 -0
  7. package/cli/mongoClient.js +11 -0
  8. package/docs/architecture.md +88 -0
  9. package/docs/db.md +38 -0
  10. package/docs/dev_iiif_compatibility.md +43 -0
  11. package/docs/endpoints.md +48 -0
  12. package/docs/progress.md +159 -0
  13. package/docs/specifications/0_w3c_open_annotations.md +332 -0
  14. package/docs/specifications/1_w3c_web_annotations.md +577 -0
  15. package/docs/specifications/2_iiif_apis.md +396 -0
  16. package/docs/specifications/3_iiif_annotations.md +103 -0
  17. package/docs/specifications/4_search_api.md +135 -0
  18. package/docs/specifications/5_sas.md +119 -0
  19. package/docs/specifications/6_mirador.md +119 -0
  20. package/docs/specifications/7_aikon.md +137 -0
  21. package/docs/specifications/include/presentation_2.0.webp +0 -0
  22. package/docs/specifications/include/presentation_2.0_white.png +0 -0
  23. package/docs/specifications/include/presentation_3.0.png +0 -0
  24. package/docs/specifications/include/presentation_3.0_resize.png +0 -0
  25. package/eslint.config.js +27 -0
  26. package/migrations/baseConfig.js +56 -0
  27. package/migrations/manageIndex.js +55 -0
  28. package/migrations/migrate-mongo-config-main.js +8 -0
  29. package/migrations/migrate-mongo-config-test.js +8 -0
  30. package/migrations/migrationScripts/20250825185706-collections.js +41 -0
  31. package/migrations/migrationScripts/20250826194832-annotations2-canvas-index.js +31 -0
  32. package/migrations/migrationScripts/20250904080710-annotations2-schema.js +42 -0
  33. package/migrations/migrationScripts/20251002141951-manifest2-schema.js +43 -0
  34. package/migrations/migrationScripts/20251006212110-manifest-unique-index.js +29 -0
  35. package/migrations/migrationScripts/20251028115614-annotations2-id-index.js +27 -0
  36. package/migrations/migrationTemplate.js +25 -0
  37. package/package.json +78 -0
  38. package/run.sh +70 -0
  39. package/scripts/_migrations.sh +79 -0
  40. package/scripts/_setup.js +31 -0
  41. package/scripts/setup_mongodb.sh +61 -0
  42. package/scripts/setup_mongodb_migrate.sh +17 -0
  43. package/scripts/setup_node.sh +15 -0
  44. package/scripts/utils.sh +192 -0
  45. package/setup.sh +20 -0
  46. package/src/app.js +113 -0
  47. package/src/config/.env.template +22 -0
  48. package/src/data/annotations/annotations2.js +419 -0
  49. package/src/data/annotations/annotations3.js +32 -0
  50. package/src/data/annotations/routes.js +271 -0
  51. package/src/data/annotations/routes.test.js +180 -0
  52. package/src/data/collectionAbstract.js +270 -0
  53. package/src/data/index.js +29 -0
  54. package/src/data/manifests/manifests2.js +305 -0
  55. package/src/data/manifests/manifests2.test.js +53 -0
  56. package/src/data/manifests/manifests3.js +23 -0
  57. package/src/data/manifests/routes.js +95 -0
  58. package/src/data/manifests/routes.test.js +69 -0
  59. package/src/data/routes.js +141 -0
  60. package/src/data/routes.test.js +117 -0
  61. package/src/data/utils/iiif2Utils.js +196 -0
  62. package/src/data/utils/iiif2Utils.test.js +98 -0
  63. package/src/data/utils/iiif3Utils.js +0 -0
  64. package/src/data/utils/iiifUtils.js +18 -0
  65. package/src/data/utils/routeUtils.js +109 -0
  66. package/src/data/utils/testUtils.js +253 -0
  67. package/src/data/utils/utils.js +231 -0
  68. package/src/db/index.js +48 -0
  69. package/src/fileServer/annotations.js +39 -0
  70. package/src/fileServer/data/annotationList_aikon_wit9_man11_anno165_all.jsonld +827 -0
  71. package/src/fileServer/data/annotationList_vhs_wit250_man250_anno250_all.jsonld +37514 -0
  72. package/src/fileServer/data/annotationList_vhs_wit253_man253_anno253_all.jsonld +20111 -0
  73. package/src/fileServer/data/annotations2Invalid.jsonld +39 -0
  74. package/src/fileServer/data/annotations2Valid.jsonld +39 -0
  75. package/src/fileServer/data/bnf_invalid_manifest.json +2806 -0
  76. package/src/fileServer/data/bnf_valid_manifest.json +2817 -0
  77. package/src/fileServer/data/vhs_wit253_man253_anno253_anno-24.json +1 -0
  78. package/src/fileServer/index.js +64 -0
  79. package/src/fileServer/manifests.js +14 -0
  80. package/src/fileServer/utils.js +35 -0
  81. package/src/schemas/index.js +20 -0
  82. package/src/schemas/schemasBase.js +47 -0
  83. package/src/schemas/schemasPresentation2.js +417 -0
  84. package/src/schemas/schemasPresentation3.js +57 -0
  85. package/src/schemas/schemasResolver.js +71 -0
  86. package/src/schemas/schemasRoutes.js +277 -0
  87. package/src/server.js +22 -0
  88. package/src/types.js +93 -0
@@ -0,0 +1,577 @@
1
+ # Web annotation data model
2
+
3
+ [https://www.w3.org/TR/annotation-model/](https://www.w3.org/TR/annotation-model/)
4
+
5
+ The IIIF format is an implementation of the W3C's Web Annotation Data Model
6
+
7
+ ---
8
+
9
+ ## In general
10
+
11
+ - an annotation is composed of 2 connected resourrces:
12
+ - a body: the annotation content
13
+ - a target: the Web resource being annotated
14
+ - the Web annotation format is a single data model that satisfies most annotation use cases.
15
+ - it is implemenation-independant
16
+ - its design is based on linked data, but can be used outside of that context
17
+ - annotations are expressed in JSON-LD
18
+
19
+ A basic annotation can look like:
20
+
21
+ ```js
22
+ {
23
+ "@context": "http://www.w3.org/ns/anno.jsonld",
24
+ "id": "http://example.org/anno1",
25
+ "type": "Annotation",
26
+ "body": "http://example.org/post1",
27
+ "target": "http://example.com/page1"
28
+ }
29
+ ```
30
+ ---
31
+
32
+ ## Vocabulary
33
+
34
+ - **IRI**: like URIs except that IRIs allow for unicode characters, whereas URIs are limited to ASCII
35
+ - **Resource**: an item of interest that MAY be identified by an URI
36
+ - **Web resource**: a Resource that MUST be identified by an IRI
37
+ - **External Web Resource (EWR)**: a Web resource that is outside of the current Resource, to which an IRI points. for example, the Target of an Annotation is an EWR.
38
+ - **Property**: a feature of a Resource, often with a specific data type, that is not a Relationship
39
+ - **Relationship**: a specific Property that is a reference to another resource (using its IRI, or describing it)
40
+ - **Class**: a group of resources. a class is a Web resource itself (identified by an IRI)
41
+ - **Instance**: a resource that belongs to a specific class
42
+ - **Type**: a Relationship that associates a class Instance to the class itself
43
+ - **SpecificResource**: a Target or Body that is more specific than the Resource identified by an IRI:
44
+ - the SpecificResource refers to the source resource and the constraints that make it more specific.
45
+ - examples: a specific segment or state, a T or B that plays a specific role in the annotation...
46
+ - **AnnotationCollection**: an ordered list of annotations
47
+
48
+ ---
49
+
50
+ ## Annotations
51
+
52
+ ### Description
53
+
54
+ - an Annotation is directed graph describing a relationship between 2 types of resources:
55
+ - Bodies
56
+ - Targets
57
+ - an Annotation has 0..n Bodies and 1..n Targets. it typically has 1 Body and 1 Target.
58
+ - Annotations, Bodies and Targets MAY have their own properties and relationships
59
+
60
+ ### Annotations data model
61
+
62
+ - `@context`: `IRI | IRI[]`
63
+ - `"http://www.w3.org/ns/anno.jsonld"` MUST be in the context, or the entire context. if there's only this value, it MUST be provided as a string
64
+ - `id`: `string`
65
+ - the IRI identifying the resource
66
+ - `type`: `string | string[]`
67
+ - Relationship describing the type of the annotation.
68
+ - accepts 1+ values. if it has only 1 value, it MUST be `"Annotation"`
69
+ - `body`: `IRI | Object`
70
+ - accepts 0..n values, but there SHOULD be at least 1 body
71
+ - the Body MAY be embedded in the annotation, or it is an IRI pointing to an EWR
72
+ - `target`: `Object | IRI`
73
+ - accepts 0..n values, but there SHOULD be at least 1 body
74
+ - the Target MUST be an IRI pointing to an EWR, or a SpecificResource
75
+
76
+ ```js
77
+ // a complete annotation
78
+ {
79
+ "@context": "http://www.w3.org/ns/anno.jsonld",
80
+ "id": "http://example.org/anno1",
81
+ "type": "Annotation",
82
+ "body": "http://example.org/post1",
83
+ "target": "http://example.com/page1"
84
+ }
85
+ ```
86
+
87
+ ---
88
+
89
+ ## Bodies and targets
90
+
91
+ ### External Web Resources
92
+
93
+ An EWR is identified by an IRI and has properties. It is a pattern used to reference content external to the annotation JSON.
94
+
95
+ Targets MUST be EWRs and Bodies MAY be EWRs.
96
+ - they MUST be identified by an IRI
97
+ - the Resource described by a Target or Body MAY be more specific than its IRI: a T or B can be:
98
+ - a segment of a resource,
99
+ - a specific state of it,
100
+ - a T or B can have a specific role in an annotation
101
+ - for specific ways to target part of a Resource, see [Specific Resources](https://www.w3.org/TR/annotation-model/#specific-resources), which allows to specify CSS selectors, XPATH...
102
+
103
+ All properties of an EWR MAY be contained within the annotation itself. In the example below, the IRIs point to the EWR to access, while all other properties are described in the annotation's body.
104
+
105
+ ```js
106
+ // Beatrice records a long analysis of a patent, and publishes the audio on her website as an mp3. She then creates an Annotation with the mp3 as the body, and the PDF of the patent as the target.
107
+ // here, the properties of the Body and Target are recorded within the Annotation itself
108
+ {
109
+ "@context": "http://www.w3.org/ns/anno.jsonld",
110
+ "id": "http://example.org/anno2",
111
+ "type": "Annotation",
112
+ "body": {
113
+ "id": "http://example.org/analysis1.mp3",
114
+ "format": "audio/mpeg",
115
+ "language": "fr"
116
+ },
117
+ "target": {
118
+ "id": "http://example.gov/patent1.pdf",
119
+ "format": "application/pdf",
120
+ "language": ["en", "ar"],
121
+ "textDirection": "ltr",
122
+ "processingLanguage": "en"
123
+ }
124
+ }
125
+ ```
126
+
127
+ ### General attributes of a Body or Target
128
+
129
+ - `id`: `IRI`
130
+ - the IRI identifying the body or target.
131
+ - if the `id` identifies a segment of an EWR (i.e., part of an image), the IRI's `fragment` should identify the specific part of the resource that is being targeted. Example: `{"id": "http://example.com/image1#xywh=100,100,300,300"}`
132
+ - for more fine-grained ways to retrieve part of a Resource, see [Specific Resources](https://www.w3.org/TR/annotation-model/#specific-resources), which allows to specify CSS selectors, XPATH...
133
+ - `type`: `string`
134
+ - the type attribute of a target or body describes the class it belongs to.
135
+ - allowed values are: `Dataset | Image | Video | Sound | Text`
136
+ - `format`: `mimetype`
137
+ - the mimetype of the resource. not to be confused with `type`, that describes the general class of the EWR
138
+
139
+ ### String body (`bodyValue`)
140
+
141
+ In a lot of cases, the annotation's body is just an HTML string. In those cases, the Body may not be an EWR and can be included directly in the annotation, to avoid fetching content from external sources.
142
+
143
+ The simplest body is a plain string. It is expressed using `bodyValue` attribute.
144
+ - it MUST NOT have any metadata associated with it. For metadata (`format`, `language`...), use `TextualBody`
145
+ - it MUST be interpretable as `text/plain` (not HTML)
146
+
147
+ ```js
148
+ // complete annotation with a string body
149
+ {
150
+ "@context": "http://www.w3.org/ns/anno.jsonld",
151
+ "id": "http://example.org/anno6",
152
+ "type": "Annotation",
153
+ "bodyValue": "Comment text",
154
+ "target": "http://example.org/target1"
155
+ }
156
+ ```
157
+
158
+ ### Embedded textual body (`TextualBody`)
159
+
160
+ To include metafdata with an embedded body, use the Embedded textual body construct. Embedded textual body attributes are:
161
+
162
+ - `id`: `IRI`
163
+ - the IRI of the body. this attribute MAY be used to identify the textual body
164
+ - `type`: `string | string[]`
165
+ - it SHOULD have the value `"TextualBody"` and may have other classes
166
+ - the class `TextualBody` indicates that the body is embedded within the annotation
167
+ - `value`: `string`
168
+ - the value MUST be used. it contains the content of the body.
169
+
170
+ `TextualBody` should be preffered over `bodyValue`.
171
+
172
+ ```js
173
+ {
174
+ "@context": "http://www.w3.org/ns/anno.jsonld",
175
+ "id": "http://example.org/anno5",
176
+ "type": "Annotation",
177
+ "body": {
178
+ "type" : "TextualBody",
179
+ "value" : "<p>j'adore !</p>",
180
+ "format" : "text/html",
181
+ "language" : "fr"
182
+ },
183
+ "target": "http://example.org/photo1"
184
+ }
185
+ ```
186
+
187
+ And the equivalent in `bodyValue` (note that HTML markup is removed)
188
+
189
+ ```js
190
+ {
191
+ "@context": "http://www.w3.org/ns/anno.jsonld",
192
+ "id": "http://example.org/anno5",
193
+ "type": "Annotation",
194
+ "bodyValue": "j'adore !,
195
+ "target": "http://example.org/photo1"
196
+ }
197
+ ```
198
+
199
+ ### Cardinality (number of bodies and targets)
200
+
201
+ An annotation can have 0+ bodies and 1+ targets.
202
+ - when there is 0 body, the attribute `body | bodyValue` is omitted. an annotation with no body is semantically similar to highlighting part of a resource.
203
+ - when there are 2+ bodies or targets, the `body` and `target` attributes are mapped to an array.
204
+ - when there are several bodies/targets, each body is related to each target individually
205
+
206
+
207
+ ```js
208
+ // annotation without a body.
209
+ {
210
+ "@context": "http://www.w3.org/ns/anno.jsonld",
211
+ "id": "http://example.org/anno8",
212
+ "type": "Annotation",
213
+ "target": "http://example.org/img.jpg#xywh=100,100,300,300"
214
+ }
215
+ ```
216
+
217
+ ```js
218
+ // annotation with several bodies and targets
219
+ {
220
+ "@context": "http://www.w3.org/ns/anno.jsonld",
221
+ "id": "http://example.org/anno9",
222
+ "type": "Annotation",
223
+ "body": [
224
+ "http://example.org/description1",
225
+ {
226
+ "type": "TextualBody",
227
+ "value": "tag1"
228
+ }
229
+ ],
230
+ "target": [
231
+ "http://example.org/image1",
232
+ "http://example.org/image2"
233
+ ]
234
+ }
235
+ ```
236
+
237
+ ### Choice
238
+
239
+ The class `Choice` allows to select only one resourrce from an array. The client MAY use any algorithm to decide which resource to use. Otherwise, it MAY require the uer to make the decision.
240
+
241
+ Attribures are:
242
+ - `id`: `string`
243
+ - the IRI of the Choice
244
+ - `type`: `Choice`.
245
+ - A Choice resourrce MUST have only 1 value and the only allowed value is `Choice`
246
+ - `items`: `[]`
247
+ - the list of resources to choose from. The 1st option is the default.
248
+
249
+ ```js
250
+ {
251
+ "@context": "http://www.w3.org/ns/anno.jsonld",
252
+ "id": "http://example.org/anno10",
253
+ "type": "Annotation",
254
+ "body": {
255
+ "type": "Choice",
256
+ "items": [
257
+ {
258
+ "id": "http://example.org/note1",
259
+ "language": "en"
260
+ },
261
+ {
262
+ "id": "http://example.org/note2",
263
+ "language": "fr"
264
+ }
265
+ ]
266
+ },
267
+ "target": "http://example.org/website1"
268
+ }
269
+ ```
270
+
271
+ ---
272
+
273
+ ## Specific Resources
274
+
275
+ A `SpecificResource` allows to target a section with more precision than just an IRI and its fragment: CSS selectors, XPATH, text range...
276
+ - it is used in between the Body and Target
277
+ - they MAY be EWRs with their IRIs, but it is better to include them inside the annotation to avoid extra queries
278
+ - possible types of specificities (categories of Specific Resources) are: `Purpose | Selector | State | Style | Rendering | Scope`
279
+
280
+ ### Specific resources data model
281
+
282
+ - `id`: `IRI`
283
+ - the SpecificResource's IRI (optional)
284
+ - `type`: `SpecificResource`
285
+ - it SHOULD have `SpecificResource` value and MAY have other values too
286
+ - `source`: `IRI | Object`
287
+ - the relationship between SecificResource and the Resource it represents.
288
+ - there MUST be exactly 1 source, described in detail or identified by its IRI.
289
+ - `purpose`: optional
290
+ - describe the motivation for the annotation
291
+ - `selector`: `string | string[] | object | object[]`
292
+ - may be a string, an object or an array of strings or objects
293
+ - custom selectors for the annotation
294
+ - there MAY be 0+ selectors
295
+ - multiple selectors SHOULD select the same content
296
+
297
+ ```js
298
+ // a SpecificResource with "tagging" purposes
299
+
300
+
301
+ {
302
+ "@context": "http://www.w3.org/ns/anno.jsonld",
303
+ "id": "http://example.org/anno18",
304
+ "type": "Annotation",
305
+ "body": {
306
+ "type": "SpecificResource",
307
+ "purpose": "tagging",
308
+ "source": "http://example.org/city1"
309
+ },
310
+ "target": {
311
+ "id": "http://example.org/photo1",
312
+ "type": "Image"
313
+ }
314
+ }
315
+ ```
316
+
317
+ ### Selectors data model
318
+
319
+
320
+ Other attributes specific to a certain type of selector are possible.
321
+ - `type`: `string`
322
+ - the type of selector we are using
323
+ - a selector MUST have only 1 value
324
+ - possible values are: `FragmentSelector | CssSelector | XPathSelector | TextQuoteSelector | TextPositionSelector | DataPositionSelector | SvgSelector | RangeSelector`
325
+ - `value`: `string`
326
+ - the content of the selector
327
+ - a selector MUST have only 1 value
328
+ - `refinedBy`: `IRI | Object`
329
+ a selector within a selector to augment specificity of a selector.
330
+
331
+ ```js
332
+ // the Target is a SpecificResource with a CSS selector
333
+ {
334
+ "@context": "http://www.w3.org/ns/anno.jsonld",
335
+ "id": "http://example.org/anno21",
336
+ "type": "Annotation",
337
+ "body": "http://example.org/note1",
338
+ "target": {
339
+ "source": "http://example.org/page1.html",
340
+ "selector": {
341
+ "type": "CssSelector",
342
+ "value": "#elemid > .elemclass + p"
343
+ }
344
+ }
345
+ }
346
+ ```
347
+
348
+ ```js
349
+ // the Target is a SpecificResource that points to "annotation" preceded by "this is an " and suffixed by " that has some"
350
+ {
351
+ "@context": "http://www.w3.org/ns/anno.jsonld",
352
+ "id": "http://example.org/anno23",
353
+ "type": "Annotation",
354
+ "body": "http://example.org/comment1",
355
+ "target": {
356
+ "source": "http://example.org/page1",
357
+ "selector": {
358
+ "type": "TextQuoteSelector",
359
+ "exact": "anotation",
360
+ "prefix": "this is an ",
361
+ "suffix": " that has some"
362
+ }
363
+ }
364
+ }
365
+ ```
366
+
367
+ ```js
368
+ // refinedBy in action: 1st selector targets a fragment, second selector targets a quote
369
+ {
370
+ "@context": "http://www.w3.org/ns/anno.jsonld",
371
+ "id": "http://example.org/anno29",
372
+ "type": "Annotation",
373
+ "body": "http://example.org/comment1",
374
+ "target": {
375
+ "source": "http://example.org/page1",
376
+ "selector": {
377
+ "type": "FragmentSelector",
378
+ "value": "para5",
379
+ "refinedBy": {
380
+ "type": "TextQuoteSelector",
381
+ "exact": "Selected Text",
382
+ "prefix": "text before the ",
383
+ "suffix": " and text after it"
384
+ }
385
+ }
386
+ }
387
+ }
388
+ ```
389
+
390
+ ---
391
+
392
+ ## Annotation collections
393
+
394
+ An annotation Collection
395
+ - is an ordered list grouping annotations
396
+ - is divided in 2 sections:
397
+ - Annotation Collection manages the identify and description of the list (metadata that contextualises the collection and helps in its understanding)
398
+ - Annotation Pages list all annotations that are members of the Collection. Each Page is an ordered list of Annotations.
399
+ - contains 0+ annotations
400
+ - MAY have multiple pages that are traversed through `first/last` (at collection level) and `prev/next` (at page level)
401
+
402
+ ### Annotation Collection data model
403
+
404
+ - `@context`: `http://www.w3.org/ns/anno.jsonld`
405
+ - MUST have one or more value and MUST contain the value `http://www.w3.org/ns/anno.jsonld`
406
+ - `id`: IRI
407
+ - MUST have exactly 1 IRI that identifies the collection
408
+ - `type`: `AnnotationCollection`
409
+ - MUST have 1+ types and MUST have the type `AnnotationCollection`
410
+ - `label`: `string | string[]`
411
+ - a human readable label for the collection
412
+ - SHOULD have 1+ labels
413
+ - `total`: `int`
414
+ - the number of Annotations in the collection
415
+ - SHOULD be used
416
+ - `first`: the first Annotation Page
417
+ - a Collection with >1 annotations MUST have a `first`
418
+ - the first Annotation Page MAY be embedded within the Collection, or it MAY be an IRI
419
+ - `last`: the last Annotation Page
420
+ - a Collection with >1 annotations SHOULD have a `last`
421
+ - `last` is an IRI pointing to the last Annotation Page
422
+
423
+ ```js
424
+ {
425
+ "@context": "http://www.w3.org/ns/anno.jsonld",
426
+ "id": "http://example.org/collection1",
427
+ "type": "AnnotationCollection",
428
+ "label": "Steampunk Annotations",
429
+ "creator": "http://example.com/publisher",
430
+ "total": 42023,
431
+ "first": "http://example.org/page1",
432
+ "last": "http://example.org/page42"
433
+ }
434
+ ```
435
+
436
+ ### Annotation Page data model
437
+
438
+ - `@context`: `IRI?`
439
+ - if Page is not embedded in a collection, it MUST have 1+ values including `http://www.w3.org/ns/anno.jsonld`.
440
+ - if Page is embedded, it SHOULD NOT have an `@context`
441
+ - `id`: IRI
442
+ - an Annotation Page MUST have 1 IRI to identify it
443
+ - `type`: `AnnotationPage`
444
+ - MUST have 1+ types, including `AnnotationPage`
445
+ - `partOf`: `IRI | Object`
446
+ - the relation to the Collection
447
+ - the Collection MAY be identified by its IRI or by an Object containing properties of the Collection and at least the Collection's `id`
448
+ - `items`: `[]`
449
+ - the list of annotations on the Page.
450
+ - MUST be used
451
+ - `next`: `IRI`
452
+ - next Page related to the current Page in the Collection
453
+ - MUST be used unless the current Page is the last one
454
+ - `prev`: `IRI`
455
+ - previous page in the Collection
456
+ - SHOULD be used unless the current Page is the first one
457
+ - `startIndex`: `int`
458
+ - SHOULD be used, MUST be a single positive or null integer
459
+ - represents the index of the 1st annotation on the page relative to all the annotations in the Collection
460
+
461
+ ```js
462
+ {
463
+ "@context": "http://www.w3.org/ns/anno.jsonld",
464
+ "id": "http://example.org/page1",
465
+ "type": "AnnotationPage",
466
+ "partOf": {
467
+ "id": "http://example.org/collection1",
468
+ "label": "Steampunk Annotations",
469
+ "total": 42023
470
+ },
471
+ "next": "http://example.org/page2",
472
+ "startIndex": 0,
473
+ "items": [
474
+ {
475
+ "id": "http://example.org/anno1",
476
+ "type": "Annotation",
477
+ "body": "http://example.net/comment1",
478
+ "target": "http://example.com/book/chapter1"
479
+ },
480
+ {
481
+ "id": "http://example.org/anno2",
482
+ "type": "Annotation",
483
+ "body": "http://example.net/comment2",
484
+ "target": "http://example.com/book/chapter2"
485
+ }
486
+ ]
487
+ }
488
+ ```
489
+
490
+ ---
491
+
492
+ ## Complete Example
493
+
494
+ ```js
495
+ {
496
+ "@context": "http://www.w3.org/ns/anno.jsonld",
497
+ "id": "http://example.org/anno38",
498
+ "type": "Annotation",
499
+ "motivation": "commenting",
500
+ "creator": {
501
+ "id": "http://example.org/user1",
502
+ "type": "Person",
503
+ "name": "A. Person",
504
+ "nickname": "user1"
505
+ },
506
+ "created": "2015-10-13T13:00:00Z",
507
+ "generator": {
508
+ "id": "http://example.org/client1",
509
+ "type": "Software",
510
+ "name": "Code v2.1",
511
+ "homepage": "http://example.org/homepage1"
512
+ },
513
+ "generated": "2015-10-14T15:13:28Z",
514
+ "stylesheet": {
515
+ "id": "http://example.org/stylesheet1",
516
+ "type": "CssStylesheet"
517
+ },
518
+ "body": [
519
+ {
520
+ "type": "TextualBody",
521
+ "purpose": "tagging",
522
+ "value": "love"
523
+ },
524
+ {
525
+ "type": "Choice",
526
+ "items": [
527
+ {
528
+ "type": "TextualBody",
529
+ "purpose": "describing",
530
+ "value": "I really love this particular bit of text in this XML. No really.",
531
+ "format": "text/plain",
532
+ "language": "en",
533
+ "creator": "http://example.org/user1"
534
+ },
535
+ {
536
+ "type": "SpecificResource",
537
+ "purpose": "describing",
538
+ "source": {
539
+ "id": "http://example.org/comment1",
540
+ "type": "Audio",
541
+ "format": "audio/mpeg",
542
+ "language": "de",
543
+ "creator": {
544
+ "id": "http://example.org/user2",
545
+ "type": "Person"
546
+ }
547
+ }
548
+ }
549
+ ]
550
+ }
551
+ ],
552
+ "target": {
553
+ "type": "SpecificResource",
554
+ "styleClass": "mystyle",
555
+ "source": "http://example.com/document1",
556
+ "state": [
557
+ {
558
+ "type": "HttpRequestState",
559
+ "value": "Accept: application/xml",
560
+ "refinedBy": {
561
+ "type": "TimeState",
562
+ "sourceDate": "2015-09-25T12:00:00Z"
563
+ }
564
+ }
565
+ ],
566
+ "selector": {
567
+ "type": "FragmentSelector",
568
+ "value": "xpointer(/doc/body/section[2]/para[1])",
569
+ "refinedBy": {
570
+ "type": "TextPositionSelector",
571
+ "start": 6,
572
+ "end": 27
573
+ }
574
+ }
575
+ }
576
+ }
577
+ ```