-
Notifications
You must be signed in to change notification settings - Fork 0
/
viewer.js
299 lines (275 loc) · 13.6 KB
/
viewer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
// This viewer is a prototype to explore UX around date based navigation via ranges, and hotspot linking between resources.
// It might not work wth any old manifest.
var viewer = function(){
var self = this;
function load(manifestUri){
$.ajax({
dataType: "json",
url: manifestUri,
cache: true,
success: function (manifest) {
IIIF.wrap(manifest);
// we're going to assume that there is one top level range, and then the
// child ranges. This is for the one manifest demo.
if(manifest.structures && "top" === manifest.structures[0].viewingHint){
var canvases = manifest.sequences[0].canvases;
var displayRanges = getDisplayRanges(manifest);
makeCanvasNav(canvases, displayRanges);
makeRangeNav(canvases, displayRanges);
self.canvases = canvases;
self.displayRanges = displayRanges;
$(".canvasSource").on('click', function(ev){
cvid = this.getAttribute("data-canvasid");
if(cvid){
navigateToCanvas(cvid);
}
});
$('#scaleMode').change(function() {
if(this.checked) {
$('#canvases').hide();
$('#canvasDisplayRanges').hide();
$('#ranges').show();
} else {
$('#canvases').show();
$('#canvasDisplayRanges').show();
$('#ranges').hide();
}
});
$('#ranges').hide();
navigateToCanvas(self.canvases[0].id);
} else {
console.log("need a top level range");
}
}
});
}
function makeCanvasNav(canvases, displayRanges){
// see https://github.com/digirati-co-uk/nui-galway-viewer/issues/1
var canvasWidth = 100.0/canvases.length;
var $canvases = $('#canvases');
var $canvasDisplayRanges = $('#canvasDisplayRanges');
// yeah, storing the model in the DOM...
// create two sets of divs. First set has one div for each canvas:
for(var ci=0; ci<canvases.length; ci++){
canvas = canvases[ci];
cv = document.createElement("div");
cv.className = "navCanvas canvasSource " + (ci % 2 == 0 ? "even" : "odd");
cv.style.width = canvasWidth + "%";
cv.setAttribute("data-canvasid", canvas.id);
$canvases.append(cv);
}
// We are going to make some very strong assumptions in the prototype.
// We assume that within a range, the canvases are in the right order and that they are contiguous.
// A more robust solution would do some sorting.
// This approach also offers later possibility of stacked overlapping ranges:
// ---- --------- ----
// ------------ -----
// ---- ------
// ...etc
for(var ri=0; ri<displayRanges.length; ri++){
displayRange = displayRanges[ri];
navRange = document.createElement("div");
navRange.className = "navRange canvasSource " + (ri % 2 == 0 ? "even" : "odd");
navRange.style.width = canvasWidth*displayRange.canvases.length + "%";
navRange.textContent = displayRange.label;
index = canvases.findIndexById(displayRange.canvases[0].id);
navRange.style.left = index*canvasWidth + "%";
navRange.setAttribute("data-rangeid", displayRange.id);
navRange.setAttribute("data-canvasid", displayRange.canvases[0].id);
$canvasDisplayRanges.append(navRange);
}
}
function makeRangeNav(canvases, displayRanges){
// here we need to make the timeline div proportional to the time coverage of each range
var start = null;
var end = null;
for(var ri=0; ri<displayRanges.length; ri++){
testRange = displayRanges[ri];
if(!start || testRange.start < start){
start = testRange.start;
}
if(!end || testRange.end > end){
end = testRange.end;
}
}
var $dateDisplayRanges = $('#ranges');
var timelineDuration = end.getTime() - start.getTime();
for(var ri=0; ri<displayRanges.length; ri++){
displayRange = displayRanges[ri];
navRange = document.createElement("div");
navRange.className = "navRange canvasSource " + (ri % 2 == 0 ? "even" : "odd");
offset = displayRange.start.getTime() - start.getTime();
displayRangeDuration = displayRange.end.getTime() - displayRange.start.getTime();
navRange.style.width = (displayRangeDuration * 100.0) / timelineDuration + "%";
navRange.style.left = offset * 100.0 / timelineDuration + "%";
navRange.textContent = displayRange.label;
index = canvases.findIndexById(displayRange.canvases[0].id);
navRange.setAttribute("data-rangeid", displayRange.id);
navRange.setAttribute("data-canvasid", displayRange.canvases[0].id);
$dateDisplayRanges.append(navRange);
}
}
function navigateToCanvas(canvasId){
$('.canvasSource').removeClass('selected');
canvasIndex = self.canvases.findIndexById(canvasId);
canvas = self.canvases[canvasIndex];
$("#main button").attr("data-canvasid", "");
if(canvasIndex > 0){
$('#previous').attr("data-canvasid", self.canvases[canvasIndex - 1].id);
}
if(canvasIndex < self.canvases.length - 1){
$('#next').attr("data-canvasid", self.canvases[canvasIndex + 1].id);
}
// highlight active navigation element(s)
$('.navCanvas').eq(canvasIndex).addClass('selected');
for(var ri=0; ri<self.displayRanges.length; ri++){
var displayRange = self.displayRanges[ri];
for(var ci=0; ci<displayRange.canvases.length; ci++){
if(displayRange.canvases[ci].id == canvas.id){
$(".navRange[data-rangeid='" + displayRange.id + "']").addClass('selected');
}
}
}
$('#position').text(canvas["label"] + " (" + (canvasIndex + 1) + " of " + self.canvases.length + ")");
loadCanvas(canvas);
}
function loadCanvas(canvas){
// here you need to add sensible logic for your images. I know that Galway's are level 2 (Loris),
// and I know that the annotated resource image is full size, and too big. So I'm going to ask for a smaller one.
var imageUrl = canvas.images[0].resource.service.id + "/full/!1600,1600/0/default.jpg";
$('#main').css('background-image', 'url(' + imageUrl + ')');
$('#linkDump').empty();
$('#linkDump').hide();
$('#supplementalTitle').empty();
$('#supplementalImages').empty();
$('#supplementalDesc').empty();
$('#supplementalImages').scrollTop(0);
$('#supplemental').css("width", "0%");
if(canvas.otherContent){
for(var ai=0; ai<canvas.otherContent.length; ai++){
$.ajax({
dataType: "json",
url: canvas.otherContent[ai]["@id"],
cache: true,
success: function (annoList) {
if(annoList.resources){
for(var ri=0; ri<annoList.resources.length; ri++){
// we're only interested in links to canvases in other manifests here.
anno = annoList.resources[ri];
if(anno.motivation == "oa:linking"){
parts = anno.on.split("#");
cvid = parts[0];
xywh = null;
if(parts.length > 1){
xywh = parts[1];
}
// will populate this object:
linkToManifest = {
xywh: xywh,
url: null,
canvasId: null,
label: null,
description: null
};
if(anno.resource["@type"] == "sc:Manifest"){
linkToManifest.url = anno.resource["@id"];
linkToManifest.label = anno.resource.label;
linkToManifest.description = anno.resource.description;
} else if (anno.resource["@type"] == "sc:Canvas"){
// we MUST be given a within otherwise we're stuffed
if(anno.resource.within && anno.resource.within["@type"] == "sc:Manifest"){
linkToManifest.url = anno.resource.within["@id"];
linkToManifest.label = anno.resource.within.label;
linkToManifest.description = anno.resource.within.description;
linkToManifest.canvasId = anno.resource["@id"];
}
}
if(linkToManifest.url){
renderLink(linkToManifest);
}
}
}
}
}
});
}
}
}
function renderLink(linkToManifest){
// won't actually draw this on the canvas for this demo
var html = "<p>Draw link at <a href='{manifest}' data-canvas='{canvas}'>{xywh}</a> going to <i>{label}</i></p>";
html += "<p class='desc'>{description}</p>"
html = html.replace("{manifest}", linkToManifest.url);
html = html.replace("{canvas}", linkToManifest.canvasId);
html = html.replace("{xywh}", linkToManifest.xywh);
html = html.replace("{label}", linkToManifest.label);
html = html.replace("{description}", linkToManifest.description);
$('#linkDump').append(html);
$('#linkDump').show();
$('#linkDump a').click(function(ev){
ev.preventDefault();
canvasId = $(this).attr("data-canvas");
$.ajax({
dataType: "json",
url: this.href,
cache: true,
success: function (manifest) {
loadSupplemental(manifest, canvasId);
}
});
});
}
function loadSupplemental(manifest, canvasId){
$('#supplemental').css("width", "50%");
$('#supplementalTitle').text(manifest.label);
$('#supplementalDesc').text(manifest.description || "(no description)");
$supplementalImages = $("#supplementalImages");
IIIF.wrap(manifest);
if(manifest["related"]){
repo = manifest["related"].asArray()[0]; // todo - prefer HTML format
$('#supplementalDesc').prepend("<p><a href='" + repo["@id"] + "'>" + (repo["label"] || "View in repository") + "</a></p>");
}
canvasIndex = manifest.sequences[0].canvases.findIndexById(canvasId);
if(canvasIndex < 0) canvasIndex = 0;
// TODO - only load an image when its pixels are on screen
for(var ci=0; ci<manifest.sequences[0].canvases.length; ci++){
var canvas = manifest.sequences[0].canvases[ci];
var imageUrl = canvas.images[0].resource.service.id + "/full/!1000,1000/0/default.jpg";
$supplementalImages.append("<img id='suppcv_" + ci + "' src='" + imageUrl + "' />");
}
var imageOfInterest = document.getElementById("suppcv_" + canvasIndex);
$(imageOfInterest).on('load', function() {
document.getElementById('supplementalImages').scrollTop = imageOfInterest.offsetTop;
});
}
function getDisplayRanges(manifest){
// wire up ranges into something more useful. This is making a lot of assumptions,
// needs to be generalised to work with arbitrary manifests
canvases = manifest.sequences[0].canvases;
rangesOfInterest = manifest.structures.where(function(range){
return range["dcterms:temporal"] && range["canvases"] && range["canvases"].length > 0;
});
return rangesOfInterest.map(function(iiifRange){
var dateStrings = iiifRange["dcterms:temporal"].split("/");
return {
start: new Date(dateStrings[0]),
end: new Date(dateStrings[1]),
label: iiifRange["label"],
canvases: mapCanvasIds(manifest, iiifRange["canvases"]),
id: iiifRange.id
}
});
}
function mapCanvasIds(manifest, canvasIds){
return canvasIds.map(function(canvasId){
return manifest.sequences[0].canvases.first(function(cv){
return cv.id == canvasId;
})
}).filter(function(cv){
return cv; // filter missing canvases
})
}
return {
initialise: function(manifestUri){ load(manifestUri)}
};
}();