triiiceratops 0.9.1 → 0.9.3
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/index-bundle.d.ts +2 -0
- package/dist/index-bundle.js +2 -0
- package/dist/plugins/image-manipulation/ImageManipulationPlugin.svelte.js +37 -72
- package/dist/plugins/image-manipulation/filters.js +6 -8
- package/dist/plugins/image-manipulation/types.js +1 -1
- package/dist/state/i18n.svelte.d.ts +3 -2
- package/dist/state/i18n.svelte.js +5 -5
- package/dist/state/manifests.svelte.js +77 -149
- package/dist/state/manifests.test.js +128 -223
- package/dist/state/viewer.svelte.d.ts +2 -2
- package/dist/state/viewer.svelte.js +374 -513
- package/dist/theme/colorUtils.js +35 -35
- package/dist/theme/colorUtils.test.js +30 -30
- package/dist/theme/themeManager.js +10 -12
- package/dist/theme/types.js +1 -1
- package/dist/triiiceratops.css +1 -0
- package/dist/types/plugin.js +18 -31
- package/dist/utils/annotationAdapter.js +80 -93
- package/dist/utils/annotationAdapter.test.js +24 -24
- package/package.json +3 -3
|
@@ -1,153 +1,60 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
-
switch (op[0]) {
|
|
20
|
-
case 0: case 1: t = op; break;
|
|
21
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
-
default:
|
|
25
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
-
if (t[2]) _.ops.pop();
|
|
30
|
-
_.trys.pop(); continue;
|
|
31
|
-
}
|
|
32
|
-
op = body.call(thisArg, _);
|
|
33
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
1
|
+
import { manifestsState } from './manifests.svelte.js';
|
|
2
|
+
export class ViewerState {
|
|
3
|
+
manifestId = $state(null);
|
|
4
|
+
canvasId = $state(null);
|
|
5
|
+
showAnnotations = $state(false);
|
|
6
|
+
showThumbnailGallery = $state(false);
|
|
7
|
+
isGalleryDockedBottom = $state(false);
|
|
8
|
+
isGalleryDockedRight = $state(false);
|
|
9
|
+
isFullScreen = $state(false);
|
|
10
|
+
showMetadataDialog = $state(false);
|
|
11
|
+
dockSide = $state('bottom');
|
|
12
|
+
visibleAnnotationIds = $state(new Set());
|
|
13
|
+
// UI Configuration
|
|
14
|
+
config = $state({});
|
|
15
|
+
// Derived configuration specific getters
|
|
16
|
+
get showRightMenu() {
|
|
17
|
+
return this.config.showRightMenu ?? true;
|
|
35
18
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
39
|
-
if (ar || !(i in from)) {
|
|
40
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
41
|
-
ar[i] = from[i];
|
|
42
|
-
}
|
|
19
|
+
get showLeftMenu() {
|
|
20
|
+
return this.config.showLeftMenu ?? true;
|
|
43
21
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
import { manifestsState } from './manifests.svelte.js';
|
|
47
|
-
var ViewerState = /** @class */ (function () {
|
|
48
|
-
function ViewerState(initialManifestId, initialCanvasId, initialPlugins) {
|
|
49
|
-
if (initialManifestId === void 0) { initialManifestId = null; }
|
|
50
|
-
if (initialCanvasId === void 0) { initialCanvasId = null; }
|
|
51
|
-
if (initialPlugins === void 0) { initialPlugins = []; }
|
|
52
|
-
this.manifestId = $state(null);
|
|
53
|
-
this.canvasId = $state(null);
|
|
54
|
-
this.showAnnotations = $state(false);
|
|
55
|
-
this.showThumbnailGallery = $state(false);
|
|
56
|
-
this.isGalleryDockedBottom = $state(false);
|
|
57
|
-
this.isGalleryDockedRight = $state(false);
|
|
58
|
-
this.isFullScreen = $state(false);
|
|
59
|
-
this.showMetadataDialog = $state(false);
|
|
60
|
-
this.dockSide = $state('bottom');
|
|
61
|
-
this.visibleAnnotationIds = $state(new Set());
|
|
62
|
-
// UI Configuration
|
|
63
|
-
this.config = $state({});
|
|
64
|
-
// Gallery State (Lifted for persistence during re-docking)
|
|
65
|
-
this.galleryPosition = $state({ x: 20, y: 100 });
|
|
66
|
-
this.gallerySize = $state({ width: 300, height: 400 });
|
|
67
|
-
this.isGalleryDragging = $state(false);
|
|
68
|
-
this.galleryDragOffset = $state({ x: 0, y: 0 });
|
|
69
|
-
this.dragOverSide = $state(null);
|
|
70
|
-
this.galleryCenterPanelRect = $state(null);
|
|
71
|
-
// ==================== EVENT DISPATCH (Web Component Only) ====================
|
|
72
|
-
/**
|
|
73
|
-
* Event target for dispatching CustomEvents.
|
|
74
|
-
* Only set by TriiiceratopsViewerElement (web component build).
|
|
75
|
-
* Remains null for Svelte component usage → no events dispatched.
|
|
76
|
-
*/
|
|
77
|
-
this.eventTarget = null;
|
|
78
|
-
this.searchQuery = $state('');
|
|
79
|
-
this.searchResults = $state([]);
|
|
80
|
-
this.isSearching = $state(false);
|
|
81
|
-
this.showSearchPanel = $state(false);
|
|
82
|
-
this.searchAnnotations = $state([]);
|
|
83
|
-
// ==================== PLUGIN STATE ====================
|
|
84
|
-
/** Registered plugins */
|
|
85
|
-
this.plugins = $state([]);
|
|
86
|
-
/** Plugin-registered menu buttons */
|
|
87
|
-
this.pluginMenuButtons = $state([]);
|
|
88
|
-
/** Plugin-registered panels */
|
|
89
|
-
this.pluginPanels = $state([]);
|
|
90
|
-
/** OpenSeadragon viewer instance (set by OSDViewer) */
|
|
91
|
-
this.osdViewer = $state(null);
|
|
92
|
-
/** Event handlers for inter-plugin communication */
|
|
93
|
-
this.pluginEventHandlers = new Map();
|
|
94
|
-
this.manifestId = initialManifestId || null;
|
|
95
|
-
this.canvasId = initialCanvasId || null;
|
|
96
|
-
// Fetch manifest immediately
|
|
97
|
-
if (this.manifestId) {
|
|
98
|
-
manifestsState.fetchManifest(this.manifestId);
|
|
99
|
-
}
|
|
100
|
-
// Register initial plugins
|
|
101
|
-
for (var _i = 0, initialPlugins_1 = initialPlugins; _i < initialPlugins_1.length; _i++) {
|
|
102
|
-
var initialPlugin = initialPlugins_1[_i];
|
|
103
|
-
this.registerPlugin(initialPlugin);
|
|
104
|
-
}
|
|
22
|
+
get showCanvasNav() {
|
|
23
|
+
return this.config.showCanvasNav ?? true;
|
|
105
24
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
enumerable: false,
|
|
121
|
-
configurable: true
|
|
122
|
-
});
|
|
123
|
-
Object.defineProperty(ViewerState.prototype, "showCanvasNav", {
|
|
124
|
-
get: function () {
|
|
125
|
-
var _a;
|
|
126
|
-
return (_a = this.config.showCanvasNav) !== null && _a !== void 0 ? _a : true;
|
|
127
|
-
},
|
|
128
|
-
enumerable: false,
|
|
129
|
-
configurable: true
|
|
130
|
-
});
|
|
25
|
+
// Gallery State (Lifted for persistence during re-docking)
|
|
26
|
+
galleryPosition = $state({ x: 20, y: 100 });
|
|
27
|
+
gallerySize = $state({ width: 300, height: 400 });
|
|
28
|
+
isGalleryDragging = $state(false);
|
|
29
|
+
galleryDragOffset = $state({ x: 0, y: 0 });
|
|
30
|
+
dragOverSide = $state(null);
|
|
31
|
+
galleryCenterPanelRect = $state(null);
|
|
32
|
+
// ==================== EVENT DISPATCH (Web Component Only) ====================
|
|
33
|
+
/**
|
|
34
|
+
* Event target for dispatching CustomEvents.
|
|
35
|
+
* Only set by TriiiceratopsViewerElement (web component build).
|
|
36
|
+
* Remains null for Svelte component usage → no events dispatched.
|
|
37
|
+
*/
|
|
38
|
+
eventTarget = null;
|
|
131
39
|
/**
|
|
132
40
|
* Set the event target for dispatching state change events.
|
|
133
41
|
* Called by TriiiceratopsViewerElement to enable event-driven API.
|
|
134
42
|
*/
|
|
135
|
-
|
|
43
|
+
setEventTarget(target) {
|
|
136
44
|
this.eventTarget = target;
|
|
137
|
-
}
|
|
45
|
+
}
|
|
138
46
|
/**
|
|
139
47
|
* Get current state as a plain object snapshot.
|
|
140
48
|
* Safe to use outside Svelte's reactive system.
|
|
141
49
|
* NOTE: We calculate currentCanvasIndex inline to avoid triggering the canvases getter
|
|
142
50
|
* which can cause infinite loops when it auto-sets canvasId.
|
|
143
51
|
*/
|
|
144
|
-
|
|
145
|
-
var _this = this;
|
|
52
|
+
getSnapshot() {
|
|
146
53
|
// Calculate canvas index without triggering reactive side effects
|
|
147
|
-
|
|
54
|
+
let canvasIndex = -1;
|
|
148
55
|
if (this.manifestId && this.canvasId) {
|
|
149
|
-
|
|
150
|
-
canvasIndex = canvases.findIndex(
|
|
56
|
+
const canvases = manifestsState.getCanvases(this.manifestId);
|
|
57
|
+
canvasIndex = canvases.findIndex((c) => c.id === this.canvasId);
|
|
151
58
|
}
|
|
152
59
|
return {
|
|
153
60
|
manifestId: this.manifestId,
|
|
@@ -160,7 +67,7 @@ var ViewerState = /** @class */ (function () {
|
|
|
160
67
|
isFullScreen: this.isFullScreen,
|
|
161
68
|
dockSide: this.dockSide,
|
|
162
69
|
};
|
|
163
|
-
}
|
|
70
|
+
}
|
|
164
71
|
/**
|
|
165
72
|
* Dispatch a state change event to the web component.
|
|
166
73
|
* No-op if eventTarget is null (Svelte component usage).
|
|
@@ -168,106 +75,92 @@ var ViewerState = /** @class */ (function () {
|
|
|
168
75
|
* Uses queueMicrotask to dispatch asynchronously AFTER the current
|
|
169
76
|
* reactive cycle completes, preventing infinite update loops.
|
|
170
77
|
*/
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (eventName === void 0) { eventName = 'statechange'; }
|
|
174
|
-
console.log("[ViewerState] Dispatching ".concat(eventName), JSON.stringify(this.getSnapshot()));
|
|
78
|
+
dispatchStateChange(eventName = 'statechange') {
|
|
79
|
+
console.log(`[ViewerState] Dispatching ${eventName}`, JSON.stringify(this.getSnapshot()));
|
|
175
80
|
if (!this.eventTarget)
|
|
176
81
|
return;
|
|
177
82
|
// Dispatch asynchronously to break reactive loops
|
|
178
|
-
queueMicrotask(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
detail: _this.getSnapshot(),
|
|
83
|
+
queueMicrotask(() => {
|
|
84
|
+
this.eventTarget?.dispatchEvent(new CustomEvent(eventName, {
|
|
85
|
+
detail: this.getSnapshot(),
|
|
182
86
|
bubbles: true,
|
|
183
87
|
composed: true,
|
|
184
88
|
}));
|
|
185
89
|
});
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
return
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
configurable: true
|
|
236
|
-
});
|
|
237
|
-
Object.defineProperty(ViewerState.prototype, "hasPrevious", {
|
|
238
|
-
get: function () {
|
|
239
|
-
return this.currentCanvasIndex > 0;
|
|
240
|
-
},
|
|
241
|
-
enumerable: false,
|
|
242
|
-
configurable: true
|
|
243
|
-
});
|
|
244
|
-
ViewerState.prototype.nextCanvas = function () {
|
|
90
|
+
}
|
|
91
|
+
constructor(initialManifestId = null, initialCanvasId = null, initialPlugins = []) {
|
|
92
|
+
this.manifestId = initialManifestId || null;
|
|
93
|
+
this.canvasId = initialCanvasId || null;
|
|
94
|
+
// Fetch manifest immediately
|
|
95
|
+
if (this.manifestId) {
|
|
96
|
+
manifestsState.fetchManifest(this.manifestId);
|
|
97
|
+
}
|
|
98
|
+
// Register initial plugins
|
|
99
|
+
for (const initialPlugin of initialPlugins) {
|
|
100
|
+
this.registerPlugin(initialPlugin);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
get manifest() {
|
|
104
|
+
if (!this.manifestId)
|
|
105
|
+
return null;
|
|
106
|
+
return manifestsState.getManifest(this.manifestId);
|
|
107
|
+
}
|
|
108
|
+
get canvases() {
|
|
109
|
+
if (!this.manifestId)
|
|
110
|
+
return [];
|
|
111
|
+
const canvases = manifestsState.getCanvases(this.manifestId);
|
|
112
|
+
// Auto-initialize canvasId to first canvas if not set
|
|
113
|
+
if (canvases.length > 0 && !this.canvasId) {
|
|
114
|
+
// Use setTimeout to avoid updating state during a derived computation
|
|
115
|
+
setTimeout(() => {
|
|
116
|
+
if (!this.canvasId && canvases.length > 0) {
|
|
117
|
+
this.canvasId = canvases[0].id;
|
|
118
|
+
}
|
|
119
|
+
}, 0);
|
|
120
|
+
}
|
|
121
|
+
return canvases;
|
|
122
|
+
}
|
|
123
|
+
get currentCanvasIndex() {
|
|
124
|
+
if (!this.canvasId) {
|
|
125
|
+
if (this.canvases.length > 0)
|
|
126
|
+
return 0;
|
|
127
|
+
return -1;
|
|
128
|
+
}
|
|
129
|
+
// Manifesto canvases have an id property
|
|
130
|
+
return this.canvases.findIndex((c) => c.id === this.canvasId);
|
|
131
|
+
}
|
|
132
|
+
get hasNext() {
|
|
133
|
+
return this.currentCanvasIndex < this.canvases.length - 1;
|
|
134
|
+
}
|
|
135
|
+
get hasPrevious() {
|
|
136
|
+
return this.currentCanvasIndex > 0;
|
|
137
|
+
}
|
|
138
|
+
nextCanvas() {
|
|
245
139
|
if (this.hasNext) {
|
|
246
|
-
|
|
247
|
-
|
|
140
|
+
const nextIndex = this.currentCanvasIndex + 1;
|
|
141
|
+
const canvas = this.canvases[nextIndex];
|
|
248
142
|
this.setCanvas(canvas.id);
|
|
249
143
|
}
|
|
250
|
-
}
|
|
251
|
-
|
|
144
|
+
}
|
|
145
|
+
previousCanvas() {
|
|
252
146
|
if (this.hasPrevious) {
|
|
253
|
-
|
|
254
|
-
|
|
147
|
+
const prevIndex = this.currentCanvasIndex - 1;
|
|
148
|
+
const canvas = this.canvases[prevIndex];
|
|
255
149
|
this.setCanvas(canvas.id);
|
|
256
150
|
}
|
|
257
|
-
}
|
|
258
|
-
|
|
151
|
+
}
|
|
152
|
+
setManifest(manifestId) {
|
|
259
153
|
this.manifestId = manifestId;
|
|
260
154
|
this.canvasId = null;
|
|
261
155
|
manifestsState.fetchManifest(manifestId);
|
|
262
156
|
this.dispatchStateChange('manifestchange');
|
|
263
|
-
}
|
|
264
|
-
|
|
157
|
+
}
|
|
158
|
+
setCanvas(canvasId) {
|
|
265
159
|
this.canvasId = canvasId;
|
|
266
160
|
this.dispatchStateChange('canvaschange');
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
var oldConfig = this.config;
|
|
161
|
+
}
|
|
162
|
+
updateConfig(newConfig) {
|
|
163
|
+
const oldConfig = this.config;
|
|
271
164
|
this.config = newConfig;
|
|
272
165
|
// Sync state from config
|
|
273
166
|
if (newConfig.gallery) {
|
|
@@ -285,8 +178,8 @@ var ViewerState = /** @class */ (function () {
|
|
|
285
178
|
// Only search if the CONFIG has changed its query requirement.
|
|
286
179
|
// This prevents stale config updates (e.g. from other property changes)
|
|
287
180
|
// from overwriting a newer internal search state.
|
|
288
|
-
|
|
289
|
-
|
|
181
|
+
const newQuery = newConfig.search.query;
|
|
182
|
+
const oldQuery = oldConfig.search?.query;
|
|
290
183
|
if (newQuery !== undefined &&
|
|
291
184
|
newQuery !== oldQuery &&
|
|
292
185
|
newQuery !== this.searchQuery) {
|
|
@@ -301,20 +194,20 @@ var ViewerState = /** @class */ (function () {
|
|
|
301
194
|
// NOTE: We intentionally do NOT dispatch events here.
|
|
302
195
|
// Config updates are external configuration, not user-initiated state changes.
|
|
303
196
|
// Dispatching here would cause infinite loops when the consumer re-renders.
|
|
304
|
-
}
|
|
305
|
-
|
|
197
|
+
}
|
|
198
|
+
toggleAnnotations() {
|
|
306
199
|
this.showAnnotations = !this.showAnnotations;
|
|
307
200
|
this.dispatchStateChange();
|
|
308
|
-
}
|
|
309
|
-
|
|
201
|
+
}
|
|
202
|
+
toggleThumbnailGallery() {
|
|
310
203
|
this.showThumbnailGallery = !this.showThumbnailGallery;
|
|
311
204
|
this.dispatchStateChange();
|
|
312
|
-
}
|
|
313
|
-
|
|
205
|
+
}
|
|
206
|
+
toggleFullScreen() {
|
|
314
207
|
if (!document.fullscreenElement) {
|
|
315
|
-
|
|
208
|
+
const el = document.getElementById('triiiceratops-viewer');
|
|
316
209
|
if (el) {
|
|
317
|
-
el.requestFullscreen().catch(
|
|
210
|
+
el.requestFullscreen().catch((e) => {
|
|
318
211
|
console.warn('Fullscreen request failed', e);
|
|
319
212
|
});
|
|
320
213
|
}
|
|
@@ -322,199 +215,111 @@ var ViewerState = /** @class */ (function () {
|
|
|
322
215
|
else {
|
|
323
216
|
document.exitFullscreen();
|
|
324
217
|
}
|
|
325
|
-
}
|
|
326
|
-
|
|
218
|
+
}
|
|
219
|
+
toggleMetadataDialog() {
|
|
327
220
|
this.showMetadataDialog = !this.showMetadataDialog;
|
|
328
|
-
}
|
|
329
|
-
|
|
221
|
+
}
|
|
222
|
+
searchQuery = $state('');
|
|
223
|
+
searchResults = $state([]);
|
|
224
|
+
isSearching = $state(false);
|
|
225
|
+
showSearchPanel = $state(false);
|
|
226
|
+
toggleSearchPanel() {
|
|
330
227
|
this.showSearchPanel = !this.showSearchPanel;
|
|
331
228
|
if (!this.showSearchPanel) {
|
|
332
229
|
// Clear ephemeral annotations when closing search
|
|
333
230
|
this.searchAnnotations = [];
|
|
334
231
|
}
|
|
335
232
|
this.dispatchStateChange();
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
233
|
+
}
|
|
234
|
+
searchAnnotations = $state([]);
|
|
235
|
+
get currentCanvasSearchAnnotations() {
|
|
236
|
+
if (!this.canvasId)
|
|
237
|
+
return [];
|
|
238
|
+
return this.searchAnnotations.filter((a) => a.canvasId === this.canvasId);
|
|
239
|
+
}
|
|
240
|
+
async search(query) {
|
|
241
|
+
this.dispatchStateChange();
|
|
242
|
+
await this._performSearch(query);
|
|
243
|
+
this.dispatchStateChange();
|
|
244
|
+
}
|
|
245
|
+
async _performSearch(query) {
|
|
246
|
+
if (!query.trim())
|
|
247
|
+
return;
|
|
248
|
+
this.isSearching = true;
|
|
249
|
+
this.searchQuery = query;
|
|
250
|
+
this.searchResults = [];
|
|
251
|
+
try {
|
|
252
|
+
const manifest = this.manifest;
|
|
253
|
+
if (!manifest)
|
|
254
|
+
throw new Error('No manifest loaded');
|
|
255
|
+
let service = manifest.getService('http://iiif.io/api/search/1/search') ||
|
|
256
|
+
manifest.getService('http://iiif.io/api/search/0/search');
|
|
257
|
+
if (!service) {
|
|
258
|
+
// Fallback: check json directly if manifesto fails me
|
|
259
|
+
if (manifest.__jsonld && manifest.__jsonld.service) {
|
|
260
|
+
const services = Array.isArray(manifest.__jsonld.service)
|
|
261
|
+
? manifest.__jsonld.service
|
|
262
|
+
: [manifest.__jsonld.service];
|
|
263
|
+
service = services.find((s) => s.profile ===
|
|
264
|
+
'http://iiif.io/api/search/1/search' ||
|
|
265
|
+
s.profile === 'http://iiif.io/api/search/0/search');
|
|
358
266
|
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
data = _h.sent();
|
|
411
|
-
resources = data.resources || [];
|
|
412
|
-
processedResults = [];
|
|
413
|
-
parseSelector = function (onVal) {
|
|
414
|
-
var val = typeof onVal === 'string'
|
|
415
|
-
? onVal
|
|
416
|
-
: onVal['@id'] || onVal.id;
|
|
417
|
-
if (!val)
|
|
418
|
-
return null;
|
|
419
|
-
var parts = val.split('#xywh=');
|
|
420
|
-
if (parts.length < 2)
|
|
421
|
-
return null;
|
|
422
|
-
var coords = parts[1].split(',').map(Number);
|
|
423
|
-
if (coords.length === 4)
|
|
424
|
-
return coords; // [x, y, w, h]
|
|
425
|
-
return null;
|
|
426
|
-
};
|
|
427
|
-
if (data.hits) {
|
|
428
|
-
for (_i = 0, _a = data.hits; _i < _a.length; _i++) {
|
|
429
|
-
hit = _a[_i];
|
|
430
|
-
annotations = hit.annotations || [];
|
|
431
|
-
hitBoundsByCanvas = new Map();
|
|
432
|
-
_loop_1 = function (annoId) {
|
|
433
|
-
var annotation = resources.find(function (r) { return r['@id'] === annoId || r.id === annoId; });
|
|
434
|
-
if (annotation && annotation.on) {
|
|
435
|
-
// annotation.on can be "canvas-id" or "canvas-id#xywh=..."
|
|
436
|
-
var onVal = typeof annotation.on === 'string'
|
|
437
|
-
? annotation.on
|
|
438
|
-
: annotation.on['@id'] || annotation.on.id;
|
|
439
|
-
var cleanOn_1 = onVal.split('#')[0];
|
|
440
|
-
var bounds = parseSelector(onVal);
|
|
441
|
-
var canvasIndex = this_1.canvases.findIndex(function (c) { return c.id === cleanOn_1; });
|
|
442
|
-
if (canvasIndex >= 0) {
|
|
443
|
-
if (!hitBoundsByCanvas.has(canvasIndex)) {
|
|
444
|
-
var canvas = this_1.canvases[canvasIndex];
|
|
445
|
-
// Try to get a label
|
|
446
|
-
var label = 'Canvas ' + (canvasIndex + 1);
|
|
447
|
-
try {
|
|
448
|
-
if (canvas.getLabel) {
|
|
449
|
-
var l = canvas.getLabel();
|
|
450
|
-
if (Array.isArray(l) &&
|
|
451
|
-
l.length > 0)
|
|
452
|
-
label = l[0].value;
|
|
453
|
-
else if (typeof l === 'string')
|
|
454
|
-
label = l;
|
|
455
|
-
}
|
|
456
|
-
else if (canvas.label) {
|
|
457
|
-
// Fallback if raw object
|
|
458
|
-
if (typeof canvas.label === 'string')
|
|
459
|
-
label = canvas.label;
|
|
460
|
-
else if (Array.isArray(canvas.label))
|
|
461
|
-
label = (_f = canvas.label[0]) === null || _f === void 0 ? void 0 : _f.value;
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
catch (e) {
|
|
465
|
-
/* ignore */
|
|
466
|
-
}
|
|
467
|
-
hitBoundsByCanvas.set(canvasIndex, {
|
|
468
|
-
label: String(label),
|
|
469
|
-
bounds: [],
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
if (bounds) {
|
|
473
|
-
hitBoundsByCanvas
|
|
474
|
-
.get(canvasIndex)
|
|
475
|
-
.bounds.push(bounds);
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
};
|
|
480
|
-
this_1 = this;
|
|
481
|
-
for (_b = 0, annotations_1 = annotations; _b < annotations_1.length; _b++) {
|
|
482
|
-
annoId = annotations_1[_b];
|
|
483
|
-
_loop_1(annoId);
|
|
484
|
-
}
|
|
485
|
-
// Create one result per canvas for this hit
|
|
486
|
-
for (_c = 0, hitBoundsByCanvas_1 = hitBoundsByCanvas; _c < hitBoundsByCanvas_1.length; _c++) {
|
|
487
|
-
_d = hitBoundsByCanvas_1[_c], canvasIndex = _d[0], data_1 = _d[1];
|
|
488
|
-
processedResults.push({
|
|
489
|
-
type: 'hit',
|
|
490
|
-
before: hit.before,
|
|
491
|
-
match: hit.match,
|
|
492
|
-
after: hit.after,
|
|
493
|
-
canvasIndex: canvasIndex,
|
|
494
|
-
canvasLabel: data_1.label,
|
|
495
|
-
// Store all bounds for this hit on this canvas
|
|
496
|
-
allBounds: data_1.bounds,
|
|
497
|
-
// Keep first bounds for backwards compatibility
|
|
498
|
-
bounds: data_1.bounds.length > 0 ? data_1.bounds[0] : null,
|
|
499
|
-
});
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
else if (resources.length > 0) {
|
|
504
|
-
_loop_2 = function (res) {
|
|
505
|
-
var onVal = typeof res.on === 'string'
|
|
506
|
-
? res.on
|
|
507
|
-
: res.on['@id'] || res.on.id;
|
|
508
|
-
var cleanOn = onVal.split('#')[0];
|
|
509
|
-
var bounds = parseSelector(onVal);
|
|
510
|
-
var canvasIndex = this_2.canvases.findIndex(function (c) { return c.id === cleanOn; });
|
|
511
|
-
if (canvasIndex >= 0) {
|
|
512
|
-
var canvas = this_2.canvases[canvasIndex];
|
|
513
|
-
var label = 'Canvas ' + (canvasIndex + 1);
|
|
267
|
+
}
|
|
268
|
+
if (!service) {
|
|
269
|
+
console.warn('No IIIF search service found in manifest');
|
|
270
|
+
this.isSearching = false;
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
// Handle service object which might be a manifesto object or raw json
|
|
274
|
+
const serviceId = service.id || service['@id'];
|
|
275
|
+
const searchUrl = `${serviceId}?q=${encodeURIComponent(query)}`;
|
|
276
|
+
const response = await fetch(searchUrl);
|
|
277
|
+
if (!response.ok)
|
|
278
|
+
throw new Error('Search request failed');
|
|
279
|
+
const data = await response.json();
|
|
280
|
+
const resources = data.resources || [];
|
|
281
|
+
const processedResults = [];
|
|
282
|
+
// Helper to parse xywh
|
|
283
|
+
const parseSelector = (onVal) => {
|
|
284
|
+
const val = typeof onVal === 'string'
|
|
285
|
+
? onVal
|
|
286
|
+
: onVal['@id'] || onVal.id;
|
|
287
|
+
if (!val)
|
|
288
|
+
return null;
|
|
289
|
+
const parts = val.split('#xywh=');
|
|
290
|
+
if (parts.length < 2)
|
|
291
|
+
return null;
|
|
292
|
+
const coords = parts[1].split(',').map(Number);
|
|
293
|
+
if (coords.length === 4)
|
|
294
|
+
return coords; // [x, y, w, h]
|
|
295
|
+
return null;
|
|
296
|
+
};
|
|
297
|
+
if (data.hits) {
|
|
298
|
+
for (const hit of data.hits) {
|
|
299
|
+
// hits have property 'annotations' which is array of ids
|
|
300
|
+
// Collapse all annotations for this hit into a single result per canvas
|
|
301
|
+
const annotations = hit.annotations || [];
|
|
302
|
+
const hitBoundsByCanvas = new Map();
|
|
303
|
+
for (const annoId of annotations) {
|
|
304
|
+
const annotation = resources.find((r) => r['@id'] === annoId || r.id === annoId);
|
|
305
|
+
if (annotation && annotation.on) {
|
|
306
|
+
// annotation.on can be "canvas-id" or "canvas-id#xywh=..."
|
|
307
|
+
const onVal = typeof annotation.on === 'string'
|
|
308
|
+
? annotation.on
|
|
309
|
+
: annotation.on['@id'] || annotation.on.id;
|
|
310
|
+
const cleanOn = onVal.split('#')[0];
|
|
311
|
+
const bounds = parseSelector(onVal);
|
|
312
|
+
const canvasIndex = this.canvases.findIndex((c) => c.id === cleanOn);
|
|
313
|
+
if (canvasIndex >= 0) {
|
|
314
|
+
if (!hitBoundsByCanvas.has(canvasIndex)) {
|
|
315
|
+
const canvas = this.canvases[canvasIndex];
|
|
316
|
+
// Try to get a label
|
|
317
|
+
let label = 'Canvas ' + (canvasIndex + 1);
|
|
514
318
|
try {
|
|
515
319
|
if (canvas.getLabel) {
|
|
516
|
-
|
|
517
|
-
if (Array.isArray(l) &&
|
|
320
|
+
const l = canvas.getLabel();
|
|
321
|
+
if (Array.isArray(l) &&
|
|
322
|
+
l.length > 0)
|
|
518
323
|
label = l[0].value;
|
|
519
324
|
else if (typeof l === 'string')
|
|
520
325
|
label = l;
|
|
@@ -524,170 +329,226 @@ var ViewerState = /** @class */ (function () {
|
|
|
524
329
|
if (typeof canvas.label === 'string')
|
|
525
330
|
label = canvas.label;
|
|
526
331
|
else if (Array.isArray(canvas.label))
|
|
527
|
-
label =
|
|
332
|
+
label = canvas.label[0]?.value;
|
|
528
333
|
}
|
|
529
334
|
}
|
|
530
335
|
catch (e) {
|
|
531
336
|
/* ignore */
|
|
532
337
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
? res.resource.chars
|
|
537
|
-
: res.chars || '',
|
|
538
|
-
canvasIndex: canvasIndex,
|
|
539
|
-
canvasLabel: String(label),
|
|
540
|
-
bounds: bounds,
|
|
338
|
+
hitBoundsByCanvas.set(canvasIndex, {
|
|
339
|
+
label: String(label),
|
|
340
|
+
bounds: [],
|
|
541
341
|
});
|
|
542
342
|
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
343
|
+
if (bounds) {
|
|
344
|
+
hitBoundsByCanvas
|
|
345
|
+
.get(canvasIndex)
|
|
346
|
+
.bounds.push(bounds);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
// Create one result per canvas for this hit
|
|
352
|
+
for (const [canvasIndex, data] of hitBoundsByCanvas) {
|
|
353
|
+
processedResults.push({
|
|
354
|
+
type: 'hit',
|
|
355
|
+
before: hit.before,
|
|
356
|
+
match: hit.match,
|
|
357
|
+
after: hit.after,
|
|
358
|
+
canvasIndex,
|
|
359
|
+
canvasLabel: data.label,
|
|
360
|
+
// Store all bounds for this hit on this canvas
|
|
361
|
+
allBounds: data.bounds,
|
|
362
|
+
// Keep first bounds for backwards compatibility
|
|
363
|
+
bounds: data.bounds.length > 0 ? data.bounds[0] : null,
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
else if (resources.length > 0) {
|
|
369
|
+
// No hits (Basic level?), just annotations
|
|
370
|
+
for (const res of resources) {
|
|
371
|
+
const onVal = typeof res.on === 'string'
|
|
372
|
+
? res.on
|
|
373
|
+
: res.on['@id'] || res.on.id;
|
|
374
|
+
const cleanOn = onVal.split('#')[0];
|
|
375
|
+
const bounds = parseSelector(onVal);
|
|
376
|
+
const canvasIndex = this.canvases.findIndex((c) => c.id === cleanOn);
|
|
377
|
+
if (canvasIndex >= 0) {
|
|
378
|
+
const canvas = this.canvases[canvasIndex];
|
|
379
|
+
let label = 'Canvas ' + (canvasIndex + 1);
|
|
380
|
+
try {
|
|
381
|
+
if (canvas.getLabel) {
|
|
382
|
+
const l = canvas.getLabel();
|
|
383
|
+
if (Array.isArray(l) && l.length > 0)
|
|
384
|
+
label = l[0].value;
|
|
385
|
+
else if (typeof l === 'string')
|
|
386
|
+
label = l;
|
|
387
|
+
}
|
|
388
|
+
else if (canvas.label) {
|
|
389
|
+
// Fallback if raw object
|
|
390
|
+
if (typeof canvas.label === 'string')
|
|
391
|
+
label = canvas.label;
|
|
392
|
+
else if (Array.isArray(canvas.label))
|
|
393
|
+
label = canvas.label[0]?.value;
|
|
549
394
|
}
|
|
550
395
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
?
|
|
558
|
-
:
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
var on = "".concat(canvas.id, "#xywh=").concat(bounds.join(','));
|
|
563
|
-
return {
|
|
564
|
-
'@id': "urn:search-hit:".concat(annotationIndex_1++),
|
|
565
|
-
'@type': 'oa:Annotation',
|
|
566
|
-
motivation: 'sc:painting',
|
|
567
|
-
on: on,
|
|
568
|
-
canvasId: canvas.id,
|
|
569
|
-
resource: {
|
|
570
|
-
'@type': 'cnt:ContentAsText',
|
|
571
|
-
chars: r.match,
|
|
572
|
-
},
|
|
573
|
-
// Flag to identify styling in Overlay?
|
|
574
|
-
// Or just standard rendering.
|
|
575
|
-
isSearchHit: true,
|
|
576
|
-
};
|
|
577
|
-
});
|
|
396
|
+
catch (e) {
|
|
397
|
+
/* ignore */
|
|
398
|
+
}
|
|
399
|
+
processedResults.push({
|
|
400
|
+
type: 'resource',
|
|
401
|
+
match: res.resource && res.resource.chars
|
|
402
|
+
? res.resource.chars
|
|
403
|
+
: res.chars || '',
|
|
404
|
+
canvasIndex,
|
|
405
|
+
canvasLabel: String(label),
|
|
406
|
+
bounds,
|
|
578
407
|
});
|
|
579
|
-
|
|
580
|
-
case 4:
|
|
581
|
-
e_1 = _h.sent();
|
|
582
|
-
console.error('Search error:', e_1);
|
|
583
|
-
return [3 /*break*/, 6];
|
|
584
|
-
case 5:
|
|
585
|
-
this.isSearching = false;
|
|
586
|
-
return [7 /*endfinally*/];
|
|
587
|
-
case 6: return [2 /*return*/];
|
|
408
|
+
}
|
|
588
409
|
}
|
|
410
|
+
}
|
|
411
|
+
this.searchResults = processedResults;
|
|
412
|
+
// Generate ephemeral search annotations
|
|
413
|
+
// Create one annotation per bound location (flatten allBounds)
|
|
414
|
+
let annotationIndex = 0;
|
|
415
|
+
this.searchAnnotations = processedResults.flatMap((r) => {
|
|
416
|
+
const canvas = this.canvases[r.canvasIndex];
|
|
417
|
+
// Use allBounds if available, otherwise fall back to single bounds
|
|
418
|
+
const boundsArray = r.allBounds && r.allBounds.length > 0
|
|
419
|
+
? r.allBounds
|
|
420
|
+
: r.bounds
|
|
421
|
+
? [r.bounds]
|
|
422
|
+
: [];
|
|
423
|
+
return boundsArray.map((bounds) => {
|
|
424
|
+
const on = `${canvas.id}#xywh=${bounds.join(',')}`;
|
|
425
|
+
return {
|
|
426
|
+
'@id': `urn:search-hit:${annotationIndex++}`,
|
|
427
|
+
'@type': 'oa:Annotation',
|
|
428
|
+
motivation: 'sc:painting',
|
|
429
|
+
on: on,
|
|
430
|
+
canvasId: canvas.id,
|
|
431
|
+
resource: {
|
|
432
|
+
'@type': 'cnt:ContentAsText',
|
|
433
|
+
chars: r.match,
|
|
434
|
+
},
|
|
435
|
+
// Flag to identify styling in Overlay?
|
|
436
|
+
// Or just standard rendering.
|
|
437
|
+
isSearchHit: true,
|
|
438
|
+
};
|
|
439
|
+
});
|
|
589
440
|
});
|
|
590
|
-
}
|
|
591
|
-
|
|
441
|
+
}
|
|
442
|
+
catch (e) {
|
|
443
|
+
console.error('Search error:', e);
|
|
444
|
+
}
|
|
445
|
+
finally {
|
|
446
|
+
this.isSearching = false;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
// ==================== PLUGIN STATE ====================
|
|
450
|
+
/** Registered plugins */
|
|
451
|
+
plugins = $state([]);
|
|
452
|
+
/** Plugin-registered menu buttons */
|
|
453
|
+
pluginMenuButtons = $state([]);
|
|
454
|
+
/** Plugin-registered panels */
|
|
455
|
+
pluginPanels = $state([]);
|
|
456
|
+
/** OpenSeadragon viewer instance (set by OSDViewer) */
|
|
457
|
+
osdViewer = $state(null);
|
|
458
|
+
/** Event handlers for inter-plugin communication */
|
|
459
|
+
pluginEventHandlers = new Map();
|
|
592
460
|
// ==================== PLUGIN METHODS ====================
|
|
593
461
|
/**
|
|
594
462
|
* Create plugin context - the stable API surface for plugins.
|
|
595
463
|
*/
|
|
596
|
-
|
|
597
|
-
|
|
464
|
+
createPluginContext() {
|
|
465
|
+
const self = this;
|
|
598
466
|
return {
|
|
599
467
|
viewerState: self,
|
|
600
|
-
getOSDViewer:
|
|
601
|
-
registerMenuButton
|
|
602
|
-
if (!self.pluginMenuButtons.find(
|
|
603
|
-
self.pluginMenuButtons =
|
|
468
|
+
getOSDViewer: () => self.osdViewer,
|
|
469
|
+
registerMenuButton(button) {
|
|
470
|
+
if (!self.pluginMenuButtons.find((b) => b.id === button.id)) {
|
|
471
|
+
self.pluginMenuButtons = [
|
|
472
|
+
...self.pluginMenuButtons,
|
|
604
473
|
button,
|
|
605
|
-
]
|
|
474
|
+
];
|
|
606
475
|
}
|
|
607
476
|
},
|
|
608
|
-
unregisterMenuButton
|
|
609
|
-
self.pluginMenuButtons = self.pluginMenuButtons.filter(
|
|
477
|
+
unregisterMenuButton(buttonId) {
|
|
478
|
+
self.pluginMenuButtons = self.pluginMenuButtons.filter((b) => b.id !== buttonId);
|
|
610
479
|
},
|
|
611
|
-
registerPanel
|
|
612
|
-
if (!self.pluginPanels.find(
|
|
613
|
-
self.pluginPanels =
|
|
480
|
+
registerPanel(panel) {
|
|
481
|
+
if (!self.pluginPanels.find((p) => p.id === panel.id)) {
|
|
482
|
+
self.pluginPanels = [...self.pluginPanels, panel];
|
|
614
483
|
}
|
|
615
484
|
},
|
|
616
|
-
unregisterPanel
|
|
617
|
-
self.pluginPanels = self.pluginPanels.filter(
|
|
485
|
+
unregisterPanel(panelId) {
|
|
486
|
+
self.pluginPanels = self.pluginPanels.filter((p) => p.id !== panelId);
|
|
618
487
|
},
|
|
619
|
-
emit
|
|
620
|
-
|
|
621
|
-
handlers
|
|
488
|
+
emit(eventName, data) {
|
|
489
|
+
const handlers = self.pluginEventHandlers.get(eventName);
|
|
490
|
+
handlers?.forEach((handler) => handler(data));
|
|
622
491
|
},
|
|
623
|
-
on
|
|
492
|
+
on(eventName, handler) {
|
|
624
493
|
if (!self.pluginEventHandlers.has(eventName)) {
|
|
625
494
|
self.pluginEventHandlers.set(eventName, new Set());
|
|
626
495
|
}
|
|
627
496
|
self.pluginEventHandlers.get(eventName).add(handler);
|
|
628
|
-
return
|
|
629
|
-
|
|
630
|
-
(_a = self.pluginEventHandlers.get(eventName)) === null || _a === void 0 ? void 0 : _a.delete(handler);
|
|
497
|
+
return () => {
|
|
498
|
+
self.pluginEventHandlers.get(eventName)?.delete(handler);
|
|
631
499
|
};
|
|
632
500
|
},
|
|
633
501
|
};
|
|
634
|
-
}
|
|
502
|
+
}
|
|
635
503
|
/**
|
|
636
504
|
* Register a plugin with this viewer instance.
|
|
637
505
|
*/
|
|
638
|
-
|
|
639
|
-
if (this.plugins.find(
|
|
640
|
-
console.warn(
|
|
506
|
+
registerPlugin(plugin) {
|
|
507
|
+
if (this.plugins.find((p) => p.id === plugin.id)) {
|
|
508
|
+
console.warn(`[Triiiceratops] Plugin "${plugin.id}" already registered`);
|
|
641
509
|
return;
|
|
642
510
|
}
|
|
643
|
-
this.plugins =
|
|
511
|
+
this.plugins = [...this.plugins, plugin];
|
|
644
512
|
plugin.onRegister(this.createPluginContext());
|
|
645
513
|
// If OSD already ready, notify immediately
|
|
646
514
|
if (this.osdViewer && plugin.onViewerReady) {
|
|
647
515
|
plugin.onViewerReady(this.osdViewer);
|
|
648
516
|
}
|
|
649
|
-
}
|
|
517
|
+
}
|
|
650
518
|
/**
|
|
651
519
|
* Unregister a plugin by ID.
|
|
652
520
|
*/
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
var plugin = this.plugins.find(function (p) { return p.id === pluginId; });
|
|
521
|
+
unregisterPlugin(pluginId) {
|
|
522
|
+
const plugin = this.plugins.find((p) => p.id === pluginId);
|
|
656
523
|
if (plugin) {
|
|
657
|
-
|
|
658
|
-
this.plugins = this.plugins.filter(
|
|
524
|
+
plugin.onDestroy?.();
|
|
525
|
+
this.plugins = this.plugins.filter((p) => p.id !== pluginId);
|
|
659
526
|
// Remove plugin's UI registrations
|
|
660
|
-
this.pluginMenuButtons = this.pluginMenuButtons.filter(
|
|
661
|
-
this.pluginPanels = this.pluginPanels.filter(
|
|
527
|
+
this.pluginMenuButtons = this.pluginMenuButtons.filter((b) => !b.id.startsWith(`${pluginId}:`));
|
|
528
|
+
this.pluginPanels = this.pluginPanels.filter((p) => !p.id.startsWith(`${pluginId}:`));
|
|
662
529
|
}
|
|
663
|
-
}
|
|
530
|
+
}
|
|
664
531
|
/**
|
|
665
532
|
* Called by OSDViewer when OpenSeadragon is ready.
|
|
666
533
|
*/
|
|
667
|
-
|
|
668
|
-
var _a;
|
|
534
|
+
notifyOSDReady(viewer) {
|
|
669
535
|
this.osdViewer = viewer;
|
|
670
|
-
for (
|
|
671
|
-
|
|
672
|
-
(_a = plugin.onViewerReady) === null || _a === void 0 ? void 0 : _a.call(plugin, viewer);
|
|
536
|
+
for (const plugin of this.plugins) {
|
|
537
|
+
plugin.onViewerReady?.(viewer);
|
|
673
538
|
}
|
|
674
|
-
}
|
|
539
|
+
}
|
|
675
540
|
/**
|
|
676
541
|
* Destroy all plugins (called on viewer unmount).
|
|
677
542
|
*/
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
var plugin = _b[_i];
|
|
682
|
-
(_a = plugin.onDestroy) === null || _a === void 0 ? void 0 : _a.call(plugin);
|
|
543
|
+
destroyAllPlugins() {
|
|
544
|
+
for (const plugin of this.plugins) {
|
|
545
|
+
plugin.onDestroy?.();
|
|
683
546
|
}
|
|
684
547
|
this.plugins = [];
|
|
685
548
|
this.pluginMenuButtons = [];
|
|
686
549
|
this.pluginPanels = [];
|
|
687
550
|
this.pluginEventHandlers.clear();
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
}());
|
|
691
|
-
export { ViewerState };
|
|
551
|
+
}
|
|
552
|
+
}
|
|
692
553
|
// Context key for providing/injecting ViewerState in components
|
|
693
|
-
export
|
|
554
|
+
export const VIEWER_STATE_KEY = 'triiiceratops:viewerState';
|