@annotorious/core 3.0.0-rc.1 → 3.0.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/annotorious-core.es.js +767 -0
- package/dist/annotorious-core.es.js.map +1 -0
- package/{src/index.ts → dist/index.d.ts} +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/lifecycle/Lifecycle.d.ts +10 -0
- package/dist/lifecycle/Lifecycle.d.ts.map +1 -0
- package/dist/lifecycle/LifecycleEvents.d.ts +12 -0
- package/dist/lifecycle/LifecycleEvents.d.ts.map +1 -0
- package/dist/lifecycle/index.d.ts +3 -0
- package/dist/lifecycle/index.d.ts.map +1 -0
- package/dist/model/Annotation.d.ts +32 -0
- package/dist/model/Annotation.d.ts.map +1 -0
- package/dist/model/Annotator.d.ts +63 -0
- package/dist/model/Annotator.d.ts.map +1 -0
- package/{src/model/DrawingStyle.ts → dist/model/DrawingStyle.d.ts} +7 -14
- package/dist/model/DrawingStyle.d.ts.map +1 -0
- package/dist/model/Filter.d.ts +3 -0
- package/dist/model/Filter.d.ts.map +1 -0
- package/dist/model/FormatAdapter.d.ts +15 -0
- package/dist/model/FormatAdapter.d.ts.map +1 -0
- package/dist/model/User.d.ts +11 -0
- package/dist/model/User.d.ts.map +1 -0
- package/dist/model/W3CAnnotation.d.ts +38 -0
- package/dist/model/W3CAnnotation.d.ts.map +1 -0
- package/{src/model/index.ts → dist/model/index.d.ts} +3 -1
- package/dist/model/index.d.ts.map +1 -0
- package/dist/presence/Appearance.d.ts +6 -0
- package/dist/presence/Appearance.d.ts.map +1 -0
- package/dist/presence/AppearanceProvider.d.ts +16 -0
- package/dist/presence/AppearanceProvider.d.ts.map +1 -0
- package/dist/presence/ColorPalette.d.ts +3 -0
- package/dist/presence/ColorPalette.d.ts.map +1 -0
- package/dist/presence/PresenceEvents.d.ts +6 -0
- package/dist/presence/PresenceEvents.d.ts.map +1 -0
- package/dist/presence/PresenceProvider.d.ts +5 -0
- package/dist/presence/PresenceProvider.d.ts.map +1 -0
- package/dist/presence/PresenceState.d.ts +18 -0
- package/dist/presence/PresenceState.d.ts.map +1 -0
- package/dist/presence/PresentUser.d.ts +7 -0
- package/dist/presence/PresentUser.d.ts.map +1 -0
- package/{src/presence/index.ts → dist/presence/index.d.ts} +2 -1
- package/dist/presence/index.d.ts.map +1 -0
- package/dist/state/Hover.d.ts +34 -0
- package/dist/state/Hover.d.ts.map +1 -0
- package/dist/state/Selection.d.ts +55 -0
- package/dist/state/Selection.d.ts.map +1 -0
- package/dist/state/Store.d.ts +30 -0
- package/dist/state/Store.d.ts.map +1 -0
- package/dist/state/StoreObserver.d.ts +57 -0
- package/dist/state/StoreObserver.d.ts.map +1 -0
- package/dist/state/SvelteStore.d.ts +48 -0
- package/dist/state/SvelteStore.d.ts.map +1 -0
- package/dist/state/UndoStack.d.ts +44 -0
- package/dist/state/UndoStack.d.ts.map +1 -0
- package/dist/state/Viewport.d.ts +6 -0
- package/dist/state/Viewport.d.ts.map +1 -0
- package/{src/state/index.ts → dist/state/index.d.ts} +3 -1
- package/dist/state/index.d.ts.map +1 -0
- package/dist/utils/annotationUtils.d.ts +11 -0
- package/dist/utils/annotationUtils.d.ts.map +1 -0
- package/dist/utils/diffAnnotations.d.ts +4 -0
- package/dist/utils/diffAnnotations.d.ts.map +1 -0
- package/{src/utils/index.ts → dist/utils/index.d.ts} +1 -1
- package/dist/utils/index.d.ts.map +1 -0
- package/package.json +6 -5
- package/src/lifecycle/Lifecycle.ts +0 -197
- package/src/lifecycle/LifecycleEvents.ts +0 -21
- package/src/lifecycle/index.ts +0 -2
- package/src/model/Annotation.ts +0 -57
- package/src/model/Annotator.ts +0 -151
- package/src/model/FormatAdapter.ts +0 -36
- package/src/model/User.ts +0 -19
- package/src/model/W3CAnnotation.ts +0 -118
- package/src/presence/Appearance.ts +0 -9
- package/src/presence/AppearanceProvider.ts +0 -53
- package/src/presence/ColorPalette.ts +0 -14
- package/src/presence/PresenceEvents.ts +0 -9
- package/src/presence/PresenceProvider.ts +0 -7
- package/src/presence/PresenceState.ts +0 -145
- package/src/presence/PresentUser.ts +0 -10
- package/src/state/Hover.ts +0 -34
- package/src/state/Selection.ts +0 -113
- package/src/state/Store.ts +0 -327
- package/src/state/StoreObserver.ts +0 -149
- package/src/state/SvelteStore.ts +0 -54
- package/src/state/Viewport.ts +0 -14
- package/src/utils/annotationUtils.ts +0 -33
- package/src/utils/diffAnnotations.ts +0 -33
- package/src/vite-env.d.ts +0 -1
package/src/state/Store.ts
DELETED
|
@@ -1,327 +0,0 @@
|
|
|
1
|
-
import type { Annotation, AnnotationBody, AnnotationTarget } from '../model';
|
|
2
|
-
import { diffAnnotations } from '../utils';
|
|
3
|
-
import { Origin, shouldNotify, type Update, type ChangeSet } from './StoreObserver';
|
|
4
|
-
import type { StoreObserver, StoreChangeEvent, StoreObserveOptions } from './StoreObserver';
|
|
5
|
-
|
|
6
|
-
// Shorthand
|
|
7
|
-
type AnnotationBodyIdentifier = { id: string, annotation: string };
|
|
8
|
-
|
|
9
|
-
export type Store<T extends Annotation> = ReturnType<typeof createStore<T>>;
|
|
10
|
-
|
|
11
|
-
const isAnnotation = <T extends Annotation>(arg: any): arg is T => arg.id !== undefined;
|
|
12
|
-
|
|
13
|
-
export const createStore = <T extends Annotation>() => {
|
|
14
|
-
|
|
15
|
-
const annotationIndex = new Map<string, T>();
|
|
16
|
-
|
|
17
|
-
const bodyIndex = new Map<string, string>();
|
|
18
|
-
|
|
19
|
-
const observers: StoreObserver<T>[] = [];
|
|
20
|
-
|
|
21
|
-
const observe = (onChange: { (event: StoreChangeEvent<T>): void }, options: StoreObserveOptions = {}) =>
|
|
22
|
-
observers.push({ onChange, options });
|
|
23
|
-
|
|
24
|
-
const unobserve = (onChange: { (event: StoreChangeEvent<T>): void }) => {
|
|
25
|
-
const idx = observers.findIndex(observer => observer.onChange == onChange);
|
|
26
|
-
if (idx > -1)
|
|
27
|
-
observers.splice(idx, 1);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const emit = (origin: Origin, changes: ChangeSet<T>) => {
|
|
31
|
-
const event: StoreChangeEvent<T> = {
|
|
32
|
-
origin,
|
|
33
|
-
changes: {
|
|
34
|
-
created: changes.created || [],
|
|
35
|
-
updated: changes.updated || [],
|
|
36
|
-
deleted: changes.deleted || []
|
|
37
|
-
},
|
|
38
|
-
state: [...annotationIndex.values()]
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
observers.forEach(observer => {
|
|
42
|
-
if (shouldNotify(observer, event))
|
|
43
|
-
observer.onChange(event);
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const addAnnotation = (annotation: T, origin = Origin.LOCAL) => {
|
|
48
|
-
const existing = annotationIndex.get(annotation.id);
|
|
49
|
-
|
|
50
|
-
if (existing) {
|
|
51
|
-
throw Error(`Cannot add annotation ${annotation.id} - exists already`);
|
|
52
|
-
} else {
|
|
53
|
-
annotationIndex.set(annotation.id, annotation);
|
|
54
|
-
|
|
55
|
-
annotation.bodies.forEach(b => bodyIndex.set(b.id, annotation.id));
|
|
56
|
-
emit(origin, { created: [annotation] });
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const updateAnnotation = (arg1: string | T, arg2: T | Origin = Origin.LOCAL, arg3 = Origin.LOCAL) => {
|
|
61
|
-
const origin: Origin = isAnnotation(arg2) ? arg3 : arg2;
|
|
62
|
-
|
|
63
|
-
const updated: T = typeof arg1 === 'string' ? arg2 as T : arg1;
|
|
64
|
-
|
|
65
|
-
const oldId: string = typeof arg1 === 'string' ? arg1 : arg1.id;
|
|
66
|
-
const oldValue = annotationIndex.get(oldId);
|
|
67
|
-
|
|
68
|
-
if (oldValue) {
|
|
69
|
-
const update: Update<T> = diffAnnotations(oldValue, updated);
|
|
70
|
-
|
|
71
|
-
if (oldId === updated.id) {
|
|
72
|
-
annotationIndex.set(oldId, updated);
|
|
73
|
-
} else {
|
|
74
|
-
annotationIndex.delete(oldId);
|
|
75
|
-
annotationIndex.set(updated.id, updated);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
oldValue.bodies.forEach(b => bodyIndex.delete(b.id));
|
|
79
|
-
updated.bodies.forEach(b => bodyIndex.set(b.id, updated.id));
|
|
80
|
-
|
|
81
|
-
emit(origin, { updated: [update] })
|
|
82
|
-
} else {
|
|
83
|
-
throw Error(`Cannot update annotation ${oldId} - does not exist`);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const addBody = (body: AnnotationBody, origin = Origin.LOCAL) => {
|
|
88
|
-
const oldValue = annotationIndex.get(body.annotation);
|
|
89
|
-
if (oldValue) {
|
|
90
|
-
const newValue = {
|
|
91
|
-
...oldValue,
|
|
92
|
-
bodies: [ ...oldValue.bodies, body ]
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
annotationIndex.set(oldValue.id, newValue);
|
|
96
|
-
|
|
97
|
-
bodyIndex.set(body.id, newValue.id);
|
|
98
|
-
|
|
99
|
-
const update: Update<T> = {
|
|
100
|
-
oldValue, newValue, bodiesCreated: [ body ]
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
emit(origin, { updated: [update] });
|
|
104
|
-
} else {
|
|
105
|
-
console.warn(`Attempt to add body to missing annotation: ${body.annotation}`);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const all = () => [...annotationIndex.values()];
|
|
110
|
-
|
|
111
|
-
const clear = (origin = Origin.LOCAL) => {
|
|
112
|
-
const all = [...annotationIndex.values()];
|
|
113
|
-
|
|
114
|
-
annotationIndex.clear();
|
|
115
|
-
bodyIndex.clear();
|
|
116
|
-
|
|
117
|
-
emit(origin, { deleted: all });
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const bulkAddAnnotation = (annotations: T[], replace = true, origin = Origin.LOCAL) => {
|
|
121
|
-
if (replace) {
|
|
122
|
-
// Delete existing first
|
|
123
|
-
const deleted = [...annotationIndex.values()];
|
|
124
|
-
annotationIndex.clear();
|
|
125
|
-
bodyIndex.clear();
|
|
126
|
-
|
|
127
|
-
annotations.forEach(annotation => {
|
|
128
|
-
annotationIndex.set(annotation.id, annotation);
|
|
129
|
-
annotation.bodies.forEach(b => bodyIndex.set(b.id, annotation.id));
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
emit(origin, { created: annotations, deleted });
|
|
133
|
-
} else {
|
|
134
|
-
// Don't allow overwriting of existing annotations
|
|
135
|
-
const existing = annotations.reduce((all, next) => {
|
|
136
|
-
const existing = annotationIndex.get(next.id);
|
|
137
|
-
return existing ? [...all, existing ] : all;
|
|
138
|
-
}, []);
|
|
139
|
-
|
|
140
|
-
if (existing.length > 0)
|
|
141
|
-
throw Error(`Bulk insert would overwrite the following annotations: ${existing.map(a => a.id).join(', ')}`);
|
|
142
|
-
|
|
143
|
-
annotations.forEach(annotation => {
|
|
144
|
-
annotationIndex.set(annotation.id, annotation);
|
|
145
|
-
annotation.bodies.forEach(b => bodyIndex.set(b.id, annotation.id));
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
emit(origin, { created: annotations });
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const deleteOneAnnotation = (annotationOrId: T | string) => {
|
|
153
|
-
const id = typeof annotationOrId === 'string' ? annotationOrId : annotationOrId.id;
|
|
154
|
-
|
|
155
|
-
const existing = annotationIndex.get(id);
|
|
156
|
-
if (existing) {
|
|
157
|
-
annotationIndex.delete(id);
|
|
158
|
-
existing.bodies.forEach(b => bodyIndex.delete(b.id));
|
|
159
|
-
return existing;
|
|
160
|
-
} else {
|
|
161
|
-
console.warn(`Attempt to delete missing annotation: ${id}`);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const deleteAnnotation = (annotationOrId: T | string, origin = Origin.LOCAL) => {
|
|
166
|
-
const deleted = deleteOneAnnotation(annotationOrId);
|
|
167
|
-
if (deleted)
|
|
168
|
-
emit(origin, { deleted: [ deleted ]});
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const bulkDeleteAnnotation = (annotationsOrIds: (T | string)[], origin = Origin.LOCAL) => {
|
|
172
|
-
const deleted = annotationsOrIds.reduce((deleted, arg) => {
|
|
173
|
-
const existing = deleteOneAnnotation(arg);
|
|
174
|
-
return existing ? [...deleted, existing] : deleted;
|
|
175
|
-
}, [] as T[]);
|
|
176
|
-
|
|
177
|
-
if (deleted.length > 0)
|
|
178
|
-
emit(origin, { deleted });
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const deleteBody = (body: AnnotationBodyIdentifier, origin = Origin.LOCAL) => {
|
|
182
|
-
const oldAnnotation = annotationIndex.get(body.annotation);
|
|
183
|
-
|
|
184
|
-
if (oldAnnotation) {
|
|
185
|
-
const oldBody = oldAnnotation.bodies.find(b => b.id === body.id);
|
|
186
|
-
|
|
187
|
-
if (oldBody) {
|
|
188
|
-
bodyIndex.delete(oldBody.id);
|
|
189
|
-
|
|
190
|
-
const newAnnotation = {
|
|
191
|
-
...oldAnnotation,
|
|
192
|
-
bodies: oldAnnotation.bodies.filter(b => b.id !== body.id)
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
annotationIndex.set(oldAnnotation.id, newAnnotation);
|
|
196
|
-
|
|
197
|
-
const update: Update<T> = {
|
|
198
|
-
oldValue: oldAnnotation, newValue: newAnnotation, bodiesDeleted: [oldBody]
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
emit(origin, { updated: [update] });
|
|
202
|
-
} else {
|
|
203
|
-
console.warn(`Attempt to delete missing body ${body.id} from annotation ${body.annotation}`);
|
|
204
|
-
}
|
|
205
|
-
} else {
|
|
206
|
-
console.warn(`Attempt to delete body from missing annotation ${body.annotation}`);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const getAnnotation = (id: string): T | undefined => {
|
|
211
|
-
const a = annotationIndex.get(id);
|
|
212
|
-
return a ? {...a} : undefined;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const getBody = (id: string): AnnotationBody | undefined => {
|
|
216
|
-
const annotationId = bodyIndex.get(id);
|
|
217
|
-
if (annotationId) {
|
|
218
|
-
const annotation = getAnnotation(annotationId);
|
|
219
|
-
const body = annotation.bodies.find(b => b.id === id);
|
|
220
|
-
if (body) {
|
|
221
|
-
return body;
|
|
222
|
-
} else {
|
|
223
|
-
console.error(`Store integrity error: body ${id} in index, but not in annotation`);
|
|
224
|
-
}
|
|
225
|
-
} else {
|
|
226
|
-
console.warn(`Attempt to retrieve missing body: ${id}`);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const updateOneBody = (oldBodyId: AnnotationBodyIdentifier, newBody: AnnotationBody) => {
|
|
231
|
-
if (oldBodyId.annotation !== newBody.annotation)
|
|
232
|
-
throw 'Annotation integrity violation: annotation ID must be the same when updating bodies';
|
|
233
|
-
|
|
234
|
-
const oldAnnotation = annotationIndex.get(oldBodyId.annotation);
|
|
235
|
-
if (oldAnnotation) {
|
|
236
|
-
const oldBody = oldAnnotation.bodies.find(b => b.id === oldBodyId.id);
|
|
237
|
-
|
|
238
|
-
const newAnnotation = {
|
|
239
|
-
...oldAnnotation,
|
|
240
|
-
bodies: oldAnnotation.bodies.map(b => b.id === oldBody.id ? newBody : b)
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
annotationIndex.set(oldAnnotation.id, newAnnotation);
|
|
244
|
-
|
|
245
|
-
if (oldBody.id !== newBody.id) {
|
|
246
|
-
bodyIndex.delete(oldBody.id);
|
|
247
|
-
bodyIndex.set(newBody.id, newAnnotation.id);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
return {
|
|
251
|
-
oldValue: oldAnnotation,
|
|
252
|
-
newValue: newAnnotation,
|
|
253
|
-
bodiesUpdated: [{ oldBody, newBody }]
|
|
254
|
-
}
|
|
255
|
-
} else {
|
|
256
|
-
console.warn(`Attempt to add body to missing annotation ${oldBodyId.annotation}`);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
const updateBody = (oldBodyId: AnnotationBodyIdentifier, newBody: AnnotationBody, origin = Origin.LOCAL) => {
|
|
261
|
-
const update = updateOneBody(oldBodyId, newBody);
|
|
262
|
-
emit(origin, { updated: [ update ]} );
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
const bulkUpdateBodies = (bodies: AnnotationBody[], origin = Origin.LOCAL) => {
|
|
266
|
-
const updated = bodies.map(b => updateOneBody({ id: b.id, annotation: b.annotation }, b));
|
|
267
|
-
emit(origin, { updated });
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
const updateOneTarget = (target: AnnotationTarget): Update<T> => {
|
|
271
|
-
const oldValue = annotationIndex.get(target.annotation);
|
|
272
|
-
|
|
273
|
-
if (oldValue) {
|
|
274
|
-
const newValue = {
|
|
275
|
-
...oldValue,
|
|
276
|
-
target: {
|
|
277
|
-
...oldValue.target,
|
|
278
|
-
...target
|
|
279
|
-
}
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
annotationIndex.set(oldValue.id, newValue);
|
|
283
|
-
|
|
284
|
-
return {
|
|
285
|
-
oldValue, newValue, targetUpdated: {
|
|
286
|
-
oldTarget: oldValue.target,
|
|
287
|
-
newTarget: target
|
|
288
|
-
}
|
|
289
|
-
};
|
|
290
|
-
} else {
|
|
291
|
-
console.warn(`Attempt to update target on missing annotation: ${target.annotation}`);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
const updateTarget = (target: AnnotationTarget, origin = Origin.LOCAL) => {
|
|
296
|
-
const update = updateOneTarget(target);
|
|
297
|
-
if (update)
|
|
298
|
-
emit(origin, { updated: [ update ]} );
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
const bulkUpdateTargets = (targets: AnnotationTarget[], origin = Origin.LOCAL) => {
|
|
302
|
-
const updated = targets.map(updateOneTarget).filter(val => val);
|
|
303
|
-
if (updated.length > 0)
|
|
304
|
-
emit(origin, { updated });
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return {
|
|
308
|
-
addAnnotation,
|
|
309
|
-
addBody,
|
|
310
|
-
all,
|
|
311
|
-
bulkAddAnnotation,
|
|
312
|
-
bulkDeleteAnnotation,
|
|
313
|
-
bulkUpdateBodies,
|
|
314
|
-
bulkUpdateTargets,
|
|
315
|
-
clear,
|
|
316
|
-
deleteAnnotation,
|
|
317
|
-
deleteBody,
|
|
318
|
-
getAnnotation,
|
|
319
|
-
getBody,
|
|
320
|
-
observe,
|
|
321
|
-
unobserve,
|
|
322
|
-
updateAnnotation,
|
|
323
|
-
updateBody,
|
|
324
|
-
updateTarget
|
|
325
|
-
};
|
|
326
|
-
|
|
327
|
-
}
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import type { Annotation, AnnotationBody, AnnotationTarget } from '../model';
|
|
2
|
-
|
|
3
|
-
/** Interface for listening to changes in the annotation store **/
|
|
4
|
-
export interface StoreObserver<T extends Annotation> {
|
|
5
|
-
|
|
6
|
-
onChange: { (event: StoreChangeEvent<T>): void };
|
|
7
|
-
|
|
8
|
-
options: StoreObserveOptions;
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/** A change event fired when the store state changes **/
|
|
13
|
-
export interface StoreChangeEvent<T extends Annotation> {
|
|
14
|
-
|
|
15
|
-
origin: Origin;
|
|
16
|
-
|
|
17
|
-
changes: ChangeSet<T>;
|
|
18
|
-
|
|
19
|
-
state: T[];
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface ChangeSet<T extends Annotation> {
|
|
24
|
-
|
|
25
|
-
created?: T[];
|
|
26
|
-
|
|
27
|
-
deleted?: T[];
|
|
28
|
-
|
|
29
|
-
updated?: Update<T>[];
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface Update<T extends Annotation> {
|
|
34
|
-
|
|
35
|
-
oldValue: T;
|
|
36
|
-
|
|
37
|
-
newValue: T;
|
|
38
|
-
|
|
39
|
-
bodiesCreated?: AnnotationBody[];
|
|
40
|
-
|
|
41
|
-
bodiesDeleted?: AnnotationBody[];
|
|
42
|
-
|
|
43
|
-
bodiesUpdated?: Array<{ oldBody: AnnotationBody, newBody: AnnotationBody }>;
|
|
44
|
-
|
|
45
|
-
targetUpdated?: { oldTarget: AnnotationTarget, newTarget: AnnotationTarget};
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** Options to control which events the observer wants to get notified about **/
|
|
50
|
-
export interface StoreObserveOptions {
|
|
51
|
-
|
|
52
|
-
// Observe changes on targets, bodies or both?
|
|
53
|
-
ignore?: Ignore;
|
|
54
|
-
|
|
55
|
-
// Observe changes on one more specific annotations
|
|
56
|
-
annotations?: string | string[];
|
|
57
|
-
|
|
58
|
-
// Observer changes only for a specific origin
|
|
59
|
-
origin?: Origin
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** Allows the observer to ignore certain event types **/
|
|
64
|
-
export enum Ignore {
|
|
65
|
-
|
|
66
|
-
// Don't notify this observer for changes that involve bodies only
|
|
67
|
-
BODY_ONLY = 'BODY_ONLY',
|
|
68
|
-
|
|
69
|
-
// Don't notify for changes on targets only
|
|
70
|
-
TARGET_ONLY = 'TARGET_ONLY'
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/** Allows the observer to listen only for events that originated locally or from a remote source **/
|
|
75
|
-
export enum Origin {
|
|
76
|
-
|
|
77
|
-
LOCAL = 'LOCAL',
|
|
78
|
-
|
|
79
|
-
REMOTE = 'REMOTE'
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** Tests if this observer should be notified about this event **/
|
|
84
|
-
export const shouldNotify = <T extends Annotation>(observer: StoreObserver<T>, event: StoreChangeEvent<T>) => {
|
|
85
|
-
const { changes, origin } = event;
|
|
86
|
-
|
|
87
|
-
const isRelevantOrigin =
|
|
88
|
-
!observer.options.origin || observer.options.origin === origin;
|
|
89
|
-
|
|
90
|
-
if (!isRelevantOrigin)
|
|
91
|
-
return false;
|
|
92
|
-
|
|
93
|
-
if (observer.options.ignore) {
|
|
94
|
-
const { ignore } = observer.options;
|
|
95
|
-
|
|
96
|
-
// Shorthand
|
|
97
|
-
const has = (arg: any[]) => arg?.length > 0;
|
|
98
|
-
|
|
99
|
-
const hasAnnotationChanges =
|
|
100
|
-
has(changes.created) || has(changes.deleted);
|
|
101
|
-
|
|
102
|
-
if (!hasAnnotationChanges) {
|
|
103
|
-
const hasBodyChanges =
|
|
104
|
-
changes.updated?.some(u => has(u.bodiesCreated) || has(u.bodiesDeleted) || has(u.bodiesUpdated));
|
|
105
|
-
|
|
106
|
-
const hasTargetChanges =
|
|
107
|
-
changes.updated?.some(u => u.targetUpdated);
|
|
108
|
-
|
|
109
|
-
if (ignore === Ignore.BODY_ONLY && hasBodyChanges && !hasTargetChanges)
|
|
110
|
-
return false;
|
|
111
|
-
|
|
112
|
-
if (ignore === Ignore.TARGET_ONLY && hasTargetChanges && !hasBodyChanges)
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (observer.options.annotations) {
|
|
118
|
-
// This observer has a filter set on specific annotations - check affected
|
|
119
|
-
const affectedAnnotations = new Set([
|
|
120
|
-
...changes.created.map(a => a.id),
|
|
121
|
-
...changes.deleted.map(a => a.id),
|
|
122
|
-
...changes.updated.map(({ oldValue }) => oldValue.id)
|
|
123
|
-
]);
|
|
124
|
-
|
|
125
|
-
const observed = Array.isArray(observer.options.annotations) ?
|
|
126
|
-
observer.options.annotations : [ observer.options.annotations ];
|
|
127
|
-
|
|
128
|
-
return Boolean(observed.find(id => affectedAnnotations.has(id)));
|
|
129
|
-
} else {
|
|
130
|
-
return true;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export const mergeChanges = <T extends Annotation>(event: StoreChangeEvent<T>, toMerge: StoreChangeEvent<T>) => {
|
|
136
|
-
if (event.origin !== toMerge.origin)
|
|
137
|
-
throw 'Cannot merge events from different origins';
|
|
138
|
-
|
|
139
|
-
return {
|
|
140
|
-
origin: toMerge.origin,
|
|
141
|
-
changes: {
|
|
142
|
-
// TODO filter created that were deleted in the same go
|
|
143
|
-
created: [...(event.changes.created || []), ...(toMerge.changes.created || []) ],
|
|
144
|
-
deleted: [...(event.changes.deleted || []), ...(toMerge.changes.deleted || []) ]
|
|
145
|
-
// TODO merge updates
|
|
146
|
-
},
|
|
147
|
-
state: toMerge.state
|
|
148
|
-
}
|
|
149
|
-
}
|
package/src/state/SvelteStore.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import type { Annotation, Annotator, AnnotatorState } from '../model';
|
|
2
|
-
import type { Store } from './Store';
|
|
3
|
-
import type { StoreChangeEvent } from './StoreObserver';
|
|
4
|
-
|
|
5
|
-
type Subscriber<T extends Annotation> = (annotation: T[]) => void;
|
|
6
|
-
|
|
7
|
-
export interface SvelteStore<T extends Annotation> extends Store<T> {
|
|
8
|
-
|
|
9
|
-
subscribe(onChange: Subscriber<T>): void;
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface SvelteAnnotatorState<T extends Annotation> extends AnnotatorState<T> {
|
|
14
|
-
|
|
15
|
-
store: SvelteStore<T>
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface SvelteAnnotator<T extends Annotation> extends Annotator<T> {
|
|
20
|
-
|
|
21
|
-
state: SvelteAnnotatorState<T>
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* A simple wrapper around the event-based store implementation
|
|
27
|
-
* that adds a Svelte shim, for use with the reactive '$' notation.
|
|
28
|
-
* Other frameworks might not actually need this. But it's pretty
|
|
29
|
-
* convenient for everyone using Svelte, as well as for the
|
|
30
|
-
* basic (Svelte-based) Annotorious standard implementation.
|
|
31
|
-
*/
|
|
32
|
-
export const toSvelteStore = <T extends Annotation>(store: Store<T>): SvelteStore<T> => {
|
|
33
|
-
|
|
34
|
-
const subscribe = (onChange: Subscriber<T>) => {
|
|
35
|
-
|
|
36
|
-
// Register a store observer on behalf of the subscriber
|
|
37
|
-
const shim = (event: StoreChangeEvent<T>) => onChange(event.state);
|
|
38
|
-
store.observe(shim);
|
|
39
|
-
|
|
40
|
-
// Immediately call the subscriber function with the
|
|
41
|
-
// current store value, according to the Svelte contract.
|
|
42
|
-
// https://stackoverflow.com/questions/68220955/how-does-svelte-unsubscribe-actually-work
|
|
43
|
-
onChange(store.all());
|
|
44
|
-
|
|
45
|
-
// Return the unsubscribe function
|
|
46
|
-
return () => store.unobserve(shim);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return {
|
|
50
|
-
...store,
|
|
51
|
-
subscribe
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
}
|
package/src/state/Viewport.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { writable } from 'svelte/store';
|
|
2
|
-
|
|
3
|
-
export type ViewportState = ReturnType<typeof createViewportState>;
|
|
4
|
-
|
|
5
|
-
export const createViewportState = () => {
|
|
6
|
-
|
|
7
|
-
const { subscribe, set } = writable<string[]>([]);
|
|
8
|
-
|
|
9
|
-
return {
|
|
10
|
-
subscribe,
|
|
11
|
-
set
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
-
import type { Annotation, AnnotationBody, User } from '../model';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Returns all users listed as creators or updaters in any parts of this
|
|
6
|
-
* annotation.
|
|
7
|
-
*/
|
|
8
|
-
export const getContributors = (annotation: Annotation): User[] => {
|
|
9
|
-
const { creator, updatedBy } = annotation.target;
|
|
10
|
-
|
|
11
|
-
const bodyCollaborators = annotation.bodies.reduce((users, body) => (
|
|
12
|
-
[...users, body.creator, body.updatedBy]
|
|
13
|
-
), [] as User[]);
|
|
14
|
-
|
|
15
|
-
return [
|
|
16
|
-
creator,
|
|
17
|
-
updatedBy,
|
|
18
|
-
...bodyCollaborators
|
|
19
|
-
].filter(u => u); // Remove undefined
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export const createBody = (
|
|
23
|
-
annotation: Annotation,
|
|
24
|
-
payload: { [key: string]: any },
|
|
25
|
-
created?: Date,
|
|
26
|
-
creator?: User
|
|
27
|
-
): AnnotationBody => ({
|
|
28
|
-
id: uuidv4(),
|
|
29
|
-
annotation: annotation.id,
|
|
30
|
-
created: created || new Date(),
|
|
31
|
-
creator,
|
|
32
|
-
...payload
|
|
33
|
-
});
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { dequal } from 'dequal/lite';
|
|
2
|
-
import type { Update } from '../state';
|
|
3
|
-
import type { Annotation } from '../model';
|
|
4
|
-
|
|
5
|
-
const getAddedBodies = (oldValue: Annotation, newValue: Annotation) => {
|
|
6
|
-
const oldBodyIds = new Set(oldValue.bodies.map(b => b.id));
|
|
7
|
-
return newValue.bodies.filter(b => !oldBodyIds.has(b.id));
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const getRemovedBodies = (oldValue: Annotation, newValue: Annotation) => {
|
|
11
|
-
const newBodyIds = new Set(newValue.bodies.map(b => b.id));
|
|
12
|
-
return oldValue.bodies.filter(b => !newBodyIds.has(b.id));
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const getChangedBodies = (oldValue: Annotation, newValue: Annotation) =>
|
|
16
|
-
newValue.bodies
|
|
17
|
-
.map(newBody => {
|
|
18
|
-
const oldBody = oldValue.bodies.find(b => b.id === newBody.id);
|
|
19
|
-
return { newBody, oldBody: oldBody && !dequal(oldBody, newBody) ? oldBody : undefined }
|
|
20
|
-
})
|
|
21
|
-
.filter(({ oldBody }) => oldBody);
|
|
22
|
-
|
|
23
|
-
const hasTargetChanged = (oldValue: Annotation, newValue: Annotation) =>
|
|
24
|
-
!dequal(oldValue.target, newValue.target);
|
|
25
|
-
|
|
26
|
-
export const diffAnnotations = <T extends Annotation = Annotation>(oldValue: T, newValue: T): Update<T> => ({
|
|
27
|
-
oldValue,
|
|
28
|
-
newValue,
|
|
29
|
-
bodiesCreated: getAddedBodies(oldValue, newValue),
|
|
30
|
-
bodiesDeleted: getRemovedBodies(oldValue, newValue),
|
|
31
|
-
bodiesUpdated: getChangedBodies(oldValue, newValue),
|
|
32
|
-
targetUpdated: hasTargetChanged(oldValue, newValue) ? { oldTarget: oldValue.target, newTarget: newValue.target } : undefined
|
|
33
|
-
});
|
package/src/vite-env.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/// <reference types="vite/client" />
|