Mercurial > hg > digilib-old
comparison client/src/main/webapp/jquery/jquery.digilib.js @ 892:ba1eb2d821a2 mvnify
rearrange sources to maven directory standard
author | robcast |
---|---|
date | Tue, 19 Apr 2011 18:44:25 +0200 |
parents | client/digitallibrary/jquery/jquery.digilib.js@613c2089bc7a |
children | 1b627b5c9fad |
comparison
equal
deleted
inserted
replaced
891:6584af320296 | 892:ba1eb2d821a2 |
---|---|
1 /* Copyright (c) 2011 Martin Raspe, Robert Casties | |
2 | |
3 This program is free software: you can redistribute it and/or modify | |
4 it under the terms of the GNU Lesser General Public License as published by | |
5 the Free Software Foundation, either version 2 of the License, or | |
6 (at your option) any later version. | |
7 | |
8 This program is distributed in the hope that it will be useful, | |
9 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 GNU Lesser General Public License for more details. | |
12 | |
13 You should have received a copy of the GNU Lesser General Public License | |
14 along with this program. If not, see <http://www.gnu.org/licenses/>. | |
15 | |
16 Authors: | |
17 Martin Raspe, Robert Casties, 11.1.2011 | |
18 */ | |
19 | |
20 /** | |
21 * digilib jQuery plugin | |
22 **/ | |
23 | |
24 /*jslint browser: true, debug: true, forin: true | |
25 */ | |
26 | |
27 // fallback for console.log calls | |
28 if (typeof console === 'undefined') { | |
29 var console = { | |
30 log : function(){}, | |
31 debug : function(){}, | |
32 error : function(){} | |
33 }; | |
34 var customConsole = false; // set to true if debugging for MS IE | |
35 } | |
36 | |
37 (function($) { | |
38 var buttons = { | |
39 reference : { | |
40 onclick : "reference", | |
41 tooltip : "get a reference URL", | |
42 icon : "reference.png" | |
43 }, | |
44 zoomin : { | |
45 onclick : ["zoomBy", 1.4], | |
46 tooltip : "zoom in", | |
47 icon : "zoom-in.png" | |
48 }, | |
49 zoomout : { | |
50 onclick : ["zoomBy", 0.7], | |
51 tooltip : "zoom out", | |
52 icon : "zoom-out.png" | |
53 }, | |
54 zoomarea : { | |
55 onclick : "zoomArea", | |
56 tooltip : "zoom area", | |
57 icon : "zoom-area.png" | |
58 }, | |
59 zoomfull : { | |
60 onclick : "zoomFull", | |
61 tooltip : "view the whole image", | |
62 icon : "zoom-full.png" | |
63 }, | |
64 pagewidth : { | |
65 onclick : ["zoomFull", "width"], | |
66 tooltip : "page width", | |
67 icon : "pagewidth.png" | |
68 }, | |
69 back : { | |
70 onclick : ["gotoPage", "-1"], | |
71 tooltip : "goto previous image", | |
72 icon : "back.png" | |
73 }, | |
74 fwd : { | |
75 onclick : ["gotoPage", "+1"], | |
76 tooltip : "goto next image", | |
77 icon : "fwd.png" | |
78 }, | |
79 page : { | |
80 onclick : "gotoPage", | |
81 tooltip : "goto image number", | |
82 icon : "page.png" | |
83 }, | |
84 help : { | |
85 onclick : "showAboutDiv", | |
86 tooltip : "about Digilib", | |
87 icon : "help.png" | |
88 }, | |
89 reset : { | |
90 onclick : "reset", | |
91 tooltip : "reset image", | |
92 icon : "reset.png" | |
93 }, | |
94 mark : { | |
95 onclick : "setMark", | |
96 tooltip : "set a mark", | |
97 icon : "mark.png" | |
98 }, | |
99 delmark : { | |
100 onclick : "removeMark", | |
101 tooltip : "delete the last mark", | |
102 icon : "delmark.png" | |
103 }, | |
104 hmir : { | |
105 onclick : ["mirror", "h"], | |
106 tooltip : "mirror horizontally", | |
107 icon : "mirror-horizontal.png" | |
108 }, | |
109 vmir : { | |
110 onclick : ["mirror", "v"], | |
111 tooltip : "mirror vertically", | |
112 icon : "mirror-vertical.png" | |
113 }, | |
114 rot : { | |
115 onclick : "rotate", | |
116 tooltip : "rotate image", | |
117 icon : "rotate.png" | |
118 }, | |
119 brgt : { | |
120 onclick : "brightness", | |
121 tooltip : "set brightness", | |
122 icon : "brightness.png" | |
123 }, | |
124 cont : { | |
125 onclick : "contrast", | |
126 tooltip : "set contrast", | |
127 icon : "contrast.png" | |
128 }, | |
129 rgb : { | |
130 onclick : "javascript:setParamWin('rgb', '...')", | |
131 tooltip : "set rgb values", | |
132 icon : "rgb.png" | |
133 }, | |
134 quality : { | |
135 onclick : "setQuality", | |
136 tooltip : "set image quality", | |
137 icon : "quality.png" | |
138 }, | |
139 size : { | |
140 onclick : "javascript:toggleSizeMenu()", | |
141 tooltip : "set page size", | |
142 icon : "size.png" | |
143 }, | |
144 calibrationx : { | |
145 onclick : "calibrate", | |
146 tooltip : "calibrate screen resolution", | |
147 icon : "calibration-x.png" | |
148 }, | |
149 scale : { | |
150 onclick : "setScaleMode", | |
151 tooltip : "change image scale", | |
152 icon : "original-size.png" | |
153 }, | |
154 toggleoptions : { | |
155 onclick : "moreButtons", | |
156 tooltip : "more options", | |
157 icon : "options.png" | |
158 }, | |
159 moreoptions : { | |
160 onclick : ["moreButtons", "+1"], | |
161 tooltip : "more options", | |
162 icon : "options.png" | |
163 }, | |
164 lessoptions : { | |
165 onclick : ["moreButtons", "-1"], | |
166 tooltip : "less options", | |
167 icon : "options.png" | |
168 }, | |
169 up : { | |
170 onclick : ["moveZoomArea", 0, -1], | |
171 tooltip : "move zoom area up", | |
172 icon : "up.png" | |
173 }, | |
174 down : { | |
175 onclick : ["moveZoomArea", 0, 1], | |
176 tooltip : "move zoom area down", | |
177 icon : "down.png" | |
178 }, | |
179 left : { | |
180 onclick : ["moveZoomArea", -1, 0], | |
181 tooltip : "move zoom area left", | |
182 icon : "left.png" | |
183 }, | |
184 right : { | |
185 onclick : ["moveZoomArea", 1, 0], | |
186 tooltip : "move zoom area right", | |
187 icon : "right.png" | |
188 }, | |
189 SEP : { | |
190 icon : "sep.png" | |
191 } | |
192 }; | |
193 | |
194 var defaults = { | |
195 // version of this script | |
196 'version' : 'jquery.digilib.js 0.9', | |
197 // logo url | |
198 'logoUrl' : '../img/digilib-logo-text1.png', | |
199 // homepage url (behind logo) | |
200 'homeUrl' : 'http://digilib.berlios.de', | |
201 // base URL to digilib viewer (for reference URLs) | |
202 'digilibBaseUrl' : null, | |
203 // base URL to Scaler servlet | |
204 'scalerBaseUrl' : null, | |
205 // list of Scaler parameters | |
206 'scalerParamNames' : ['fn','pn','dw','dh','ww','wh','wx','wy','ws','mo', | |
207 'rot','cont','brgt','rgbm','rgba','ddpi','ddpix','ddpiy'], | |
208 // Scaler parameter defaults | |
209 'pn' : 1, | |
210 'ww' : 1.0, | |
211 'wh' : 1.0, | |
212 'wx' : 0.0, | |
213 'wy' : 0.0, | |
214 'ws' : 1.0, | |
215 'mo' : '', | |
216 'rot' : 0, | |
217 'cont' : 0, | |
218 'brgt' : 0, | |
219 'rgbm' : '0/0/0', | |
220 'rgba' : '0/0/0', | |
221 'ddpi' : null, | |
222 'ddpix' : null, | |
223 'ddpiy' : null, | |
224 // list of digilib parameters | |
225 'digilibParamNames' : ['fn','pn','ww','wh','wx','wy','ws','mo','rot','cont','brgt','rgbm','rgba','ddpi','mk','clop'], | |
226 // digilib parameter defaults | |
227 'mk' : '', | |
228 'clop' : '', | |
229 // mode of operation: | |
230 // fullscreen = take parameters from page URL, keep state in page URL | |
231 // embedded = take parameters from Javascript options, keep state inside object | |
232 'interactionMode' : 'fullscreen', | |
233 // buttons | |
234 'buttons' : buttons, | |
235 // defaults for digilib buttons | |
236 'buttonSettings' : { | |
237 'fullscreen' : { | |
238 // path to button images (must end with a slash) | |
239 'imagePath' : 'img/fullscreen/', | |
240 'buttonSetWidth' : 36, | |
241 'standardSet' : ["reference","zoomin","zoomout","zoomarea","zoomfull","pagewidth","back","fwd","page","help","reset","toggleoptions"], | |
242 'specialSet' : ["mark","delmark","hmir","vmir","rot","brgt","cont","rgb","quality","size","calibrationx","scale","lessoptions"], | |
243 'arrowSet' : ["up", "down", "left", "right"], | |
244 'buttonSets' : ['standardSet', 'specialSet'] | |
245 }, | |
246 'embedded' : { | |
247 'imagePath' : 'img/embedded/16/', | |
248 'buttonSetWidth' : 18, | |
249 'standardSet' : ["reference","zoomin","zoomout","zoomarea","zoomfull","help","reset","toggleoptions"], | |
250 'specialSet' : ["mark","delmark","hmir","vmir","rot","brgt","cont","rgb","quality","scale","lessoptions"], | |
251 'arrowSet' : ["up", "down", "left", "right"], | |
252 'buttonSets' : ['standardSet', 'specialSet'] | |
253 }, | |
254 }, | |
255 // arrow bar overlays for moving the zoomed area | |
256 'showZoomArrows' : true, | |
257 // zoom arrow bar minimal width (for small images) | |
258 'zoomArrowMinWidth' : 6, | |
259 // zoom arrow bar standard width | |
260 'zoomArrowWidth' : 32, | |
261 // by what percentage should the arrows move the zoomed area? | |
262 'zoomArrowMoveFactor' : 0.5, | |
263 // number of visible button groups | |
264 'visibleButtonSets' : 1, | |
265 // is the "about" window shown? | |
266 'isAboutDivVisible' : false, | |
267 // default size of background image for drag-scroll (same as Bird's Eye View image) | |
268 'bgImgWidth' : 200, | |
269 'bgImgHeight' : 200, | |
270 // maximum width or height of background image for drag-scroll | |
271 'maxBgSize' : 10000, | |
272 // parameters used by background image | |
273 'bgImgParams' : ['fn','pn','dw','dh','mo','rot'], | |
274 // reserved space in full page display (default value accounts for vertical scrollbar) | |
275 'scalerInset' : 10 | |
276 }; | |
277 | |
278 // list of plugins | |
279 var plugins = {}; | |
280 // object to export functions to plugins | |
281 var fn; | |
282 // affine geometry plugin stub | |
283 var geom; | |
284 | |
285 var FULL_AREA; | |
286 | |
287 var actions = { | |
288 // init: digilib initialization | |
289 init : function(options) { | |
290 // import geometry classes | |
291 if (plugins.geometry == null) { | |
292 $.error("jquery.digilib.geometry plugin not found!"); | |
293 // last straw: old version | |
294 geom = dlGeometry(); | |
295 } else { | |
296 // geometry plugin puts classes in the shared fn | |
297 geom = fn.geometry; | |
298 } | |
299 FULL_AREA = geom.rectangle(0, 0, 1, 1); | |
300 | |
301 // settings for this digilib instance are merged from defaults and options | |
302 var settings = $.extend({}, defaults, options); | |
303 var isFullscreen = settings.interactionMode === 'fullscreen'; | |
304 var queryParams = {}; | |
305 if (isFullscreen) { | |
306 queryParams = parseQueryParams(); | |
307 // check scalerBaseUrl | |
308 if (settings.scalerBaseUrl == null) { | |
309 // try the host this came from | |
310 var h = window.location.host; | |
311 if (window.location.host) { | |
312 var url = window.location.href; | |
313 // assume the page lives in [webapp]/jquery/ | |
314 var pos = url.indexOf('jquery/'); | |
315 if (pos > 0) { | |
316 settings.scalerBaseUrl = url.substring(0, pos) + 'servlet/Scaler'; | |
317 } | |
318 } | |
319 } | |
320 } | |
321 return this.each(function() { | |
322 var $elem = $(this); | |
323 var data = $elem.data('digilib'); | |
324 var params, elemSettings; | |
325 // if the plugin hasn't been initialized yet | |
326 if (!data) { | |
327 // merge query parameters | |
328 if (isFullscreen) { | |
329 params = queryParams; | |
330 } else { | |
331 params = parseImgParams($elem); | |
332 if ($.cookie) { | |
333 // retrieve params from cookie | |
334 var ck = "digilib-embed:fn:" + escape(params.fn) + ":pn:" + (params.pn || '1'); | |
335 var cs = $.cookie(ck); | |
336 console.debug("get cookie=", ck, " value=", cs); | |
337 if (cs) { | |
338 var cp = parseQueryString(cs); | |
339 // ignore fn and pn from cookie TODO: should we keep pn? | |
340 delete cp.fn; | |
341 delete cp.pn; | |
342 $.extend(params, cp); | |
343 } | |
344 } | |
345 } | |
346 // setup $elem.data, needs "deep copy" because of nesting | |
347 elemSettings = $.extend(true, {}, settings, params); | |
348 data = { | |
349 // let $(this) know about $(this) :-) | |
350 $elem : $elem, | |
351 // let $elem have its own copy of settings | |
352 settings : elemSettings, | |
353 // and of the URL query parameters | |
354 queryParams : params, | |
355 // TODO: move plugins reference out of data | |
356 plugins : plugins | |
357 }; | |
358 // store in jQuery data element | |
359 $elem.data('digilib', data); | |
360 } | |
361 unpackParams(data); | |
362 // check if browser knows *background-size | |
363 for (var bs in {'':1, '-moz-':1, '-webkit-':1, '-o-':1}) { | |
364 if ($elem.css(bs+'background-size')) { | |
365 data.hasBgSize = true; | |
366 data.bgSizeName = bs+'background-size'; | |
367 break; | |
368 } | |
369 } | |
370 // check digilib base URL | |
371 if (elemSettings.digilibBaseUrl == null) { | |
372 if (isFullscreen) { | |
373 // take current host | |
374 var url = window.location.toString(); | |
375 var pos = url.indexOf('?'); | |
376 elemSettings.digilibBaseUrl = url.substring(0, pos); | |
377 } else { | |
378 var url = elemSettings.scalerBaseUrl; | |
379 if (url) { | |
380 // build it from scaler URL | |
381 var bp = url.indexOf('/servlet/Scaler'); | |
382 elemSettings.digilibBaseUrl = url.substring(0, bp) + '/digilib.jsp'; | |
383 } | |
384 } | |
385 } | |
386 // initialise plugins | |
387 for (n in plugins) { | |
388 var p = plugins[n]; | |
389 if (typeof p.init === 'function') { | |
390 p.init(data); | |
391 } | |
392 } | |
393 // get image info from server if needed | |
394 if (data.scaleMode === 'pixel' || data.scaleMode === 'size') { | |
395 $(data).bind('imageInfo', handleImageInfo); | |
396 loadImageInfo(data); // triggers "imageInfo" on completion | |
397 } | |
398 // create buttons before scaler | |
399 for (var i = 0; i < elemSettings.visibleButtonSets; ++i) { | |
400 showButtons(data, true, i); | |
401 } | |
402 // create HTML structure for scaler, taking width of buttons div into account | |
403 setupScalerDiv(data); | |
404 highlightButtons(data); | |
405 // about window creation - TODO: could be deferred? restrict to only one item? | |
406 setupAboutDiv(data); | |
407 // arrow overlays for moving zoomed detail | |
408 setupZoomArrows(data); | |
409 // send setup event | |
410 $(data).trigger('setup'); | |
411 }); | |
412 }, | |
413 | |
414 // destroy: clean up digilib | |
415 destroy : function(data) { | |
416 return this.each(function(){ | |
417 var $elem = $(this); | |
418 $(window).unbind('.digilib'); // unbind all digilibs(?) | |
419 data.digilib.remove(); | |
420 $elem.removeData('digilib'); | |
421 }); | |
422 }, | |
423 | |
424 // show or hide the 'about' window | |
425 showAboutDiv : function(data, show) { | |
426 var on = showDiv(data.settings.isAboutDivVisible, data.$aboutDiv, show); | |
427 data.settings.isAboutDivVisible = on; | |
428 highlightButtons(data, 'help', on); | |
429 }, | |
430 | |
431 // goto given page nr (+/-: relative) | |
432 gotoPage : function (data, pageNr) { | |
433 var settings = data.settings; | |
434 var oldpn = settings.pn; | |
435 if (pageNr == null) { | |
436 pageNr = window.prompt("Goto page number", oldpn); | |
437 } | |
438 var pn = setNumValue(settings, "pn", pageNr); | |
439 if (pn == null) return false; // nothing happened | |
440 if (pn < 1) { | |
441 alert("no such page (page number too low)"); | |
442 settings.pn = oldpn; | |
443 return false; | |
444 } | |
445 // TODO: how do we get pt? | |
446 if (settings.pt) { | |
447 if (pn > settings.pt) { | |
448 alert("no such page (page number too high)"); | |
449 settings.pn = oldpn; | |
450 return false; | |
451 } | |
452 } | |
453 // reset mk and others(?) | |
454 data.marks = []; | |
455 data.zoomArea = FULL_AREA; | |
456 // then reload | |
457 redisplay(data); | |
458 }, | |
459 | |
460 // zoom by a given factor | |
461 zoomBy : function (data, factor) { | |
462 zoomBy(data, factor); | |
463 }, | |
464 | |
465 // zoom to area (or interactive) | |
466 zoomArea : function (data, area) { | |
467 var settings = data.settings; | |
468 if (area == null) { | |
469 // interactively | |
470 zoomArea(data); | |
471 } else { | |
472 data.zoomArea = geom.rectangle(area); | |
473 redisplay(data); | |
474 } | |
475 }, | |
476 | |
477 // zoom out to full page | |
478 zoomFull : function (data, mode) { | |
479 data.zoomArea = FULL_AREA; | |
480 if (mode === 'width') { | |
481 data.dlOpts.fitwidth = 1; | |
482 delete data.dlOpts.fitheight; | |
483 } else if (mode === 'height') { | |
484 data.dlOpts.fitheight = 1; | |
485 delete data.dlOpts.fitwidth; | |
486 } else { | |
487 delete data.dlOpts.fitwidth; | |
488 delete data.dlOpts.fitheight; | |
489 } | |
490 redisplay(data); | |
491 }, | |
492 | |
493 // move zoomed area | |
494 moveZoomArea : function (data, dx, dy) { | |
495 var za = data.zoomArea; | |
496 var factor = data.settings.zoomArrowMoveFactor; | |
497 var deltaX = dx * factor * za.width; | |
498 var deltaY = dy * factor * za.height; | |
499 za.addPosition(geom.position(deltaX, deltaY)); | |
500 data.zoomArea = FULL_AREA.fit(za); | |
501 redisplay(data); | |
502 }, | |
503 | |
504 // set a mark by clicking (or giving a position) | |
505 setMark : function (data, mpos) { | |
506 if (mpos == null) { | |
507 // interactive | |
508 setMark(data); | |
509 } else { | |
510 // use position | |
511 data.marks.push(pos); | |
512 redisplay(data); | |
513 } | |
514 }, | |
515 | |
516 // remove the last mark | |
517 removeMark : function (data) { | |
518 data.marks.pop(); | |
519 redisplay(data); | |
520 }, | |
521 | |
522 // mirror the image | |
523 mirror : function (data, mode) { | |
524 var flags = data.scalerFlags; | |
525 if (mode === 'h') { | |
526 if (flags.hmir) { | |
527 delete flags.hmir; | |
528 } else { | |
529 flags.hmir = 1; | |
530 } | |
531 } else { | |
532 if (flags.vmir) { | |
533 delete flags.vmir; | |
534 } else { | |
535 flags.vmir = 1; | |
536 } | |
537 } | |
538 redisplay(data); | |
539 }, | |
540 | |
541 // rotate the image | |
542 rotate : function (data, angle) { | |
543 var rot = data.settings.rot; | |
544 if (angle == null) { | |
545 angle = window.prompt("Rotation angle:", rot); | |
546 } | |
547 data.settings.rot = angle; | |
548 redisplay(data); | |
549 }, | |
550 | |
551 // change brightness | |
552 brightness : function (data, factor) { | |
553 var brgt = data.settings.brgt; | |
554 if (factor == null) { | |
555 factor = window.prompt("Brightness (-255..255)", brgt); | |
556 } | |
557 data.settings.brgt = factor; | |
558 redisplay(data); | |
559 }, | |
560 | |
561 // change contrast | |
562 contrast : function (data, factor) { | |
563 var cont = data.settings.cont; | |
564 if (factor == null) { | |
565 factor = window.prompt("Contrast (-8, 8)", cont); | |
566 } | |
567 data.settings.cont = factor; | |
568 redisplay(data); | |
569 }, | |
570 | |
571 // display more (or less) button sets | |
572 moreButtons : function (data, more) { | |
573 var settings = data.settings; | |
574 if (more == null) { | |
575 // toggle more or less (only works for 2 sets) | |
576 var maxbtns = settings.buttonSettings[settings.interactionMode].buttonSets.length; | |
577 if (settings.visibleButtonSets >= maxbtns) { | |
578 more = '-1'; | |
579 } else { | |
580 more = '+1'; | |
581 } | |
582 } | |
583 if (more === '-1') { | |
584 // remove set | |
585 var setIdx = settings.visibleButtonSets - 1; | |
586 if (showButtons(data, false, setIdx, true)) { | |
587 settings.visibleButtonSets--; | |
588 } | |
589 } else { | |
590 // add set | |
591 var setIdx = settings.visibleButtonSets; | |
592 if (showButtons(data, true, setIdx, true)) { | |
593 settings.visibleButtonSets++; | |
594 } | |
595 } | |
596 // persist setting | |
597 storeOptions(data); | |
598 }, | |
599 | |
600 // reset image parameters to defaults | |
601 reset : function (data) { | |
602 var settings = data.settings; | |
603 var paramNames = settings.digilibParamNames; | |
604 var params = data.queryParams; | |
605 // delete all digilib parameters | |
606 for (var i = 0; i < paramNames.length; i++) { | |
607 var paramName = paramNames[i]; | |
608 delete settings[paramName]; | |
609 } | |
610 settings.fn = params.fn || ''; // no default defined | |
611 settings.pn = params.pn || defaults.pn; | |
612 settings.dw = params.dw; | |
613 settings.dh = params.dh; | |
614 settings.isBirdDivVisible = false; | |
615 settings.visibleButtonSets = 1; | |
616 // resets zoomArea, marks, scalerflags | |
617 data.zoomArea = FULL_AREA; | |
618 data.marks = []; | |
619 data.scalerFlags = {}; | |
620 delete data.dlOpts.fitwidth; | |
621 delete data.dlOpts.fitheight; | |
622 redisplay(data); | |
623 }, | |
624 | |
625 // presents a reference url (returns value if noprompt) | |
626 reference : function (data, noprompt) { | |
627 var settings = data.settings; | |
628 var url = getDigilibUrl(data); | |
629 if (noprompt == null) { | |
630 window.prompt("URL reference to the current view", url); | |
631 } | |
632 return url; | |
633 }, | |
634 | |
635 // set image quality | |
636 setQuality : function (data, qual) { | |
637 var oldq = getQuality(data); | |
638 if (qual == null) { | |
639 qual = window.prompt("Image quality (0..2)", oldq); | |
640 } | |
641 qual = parseInt(qual, 10); | |
642 if (qual >= 0 && qual <= 2) { | |
643 setQuality(data, qual); | |
644 redisplay(data); | |
645 } | |
646 }, | |
647 | |
648 // calibrate (only faking) | |
649 calibrate : function (data, res) { | |
650 var oldRes = data.settings.ddpi; | |
651 if (res == null) { | |
652 res = window.prompt("Display resolution (dpi)", oldRes); | |
653 } | |
654 if (res != null) { | |
655 data.settings.ddpi = res; | |
656 redisplay(data); | |
657 } | |
658 }, | |
659 | |
660 // set image scale mode | |
661 setScaleMode : function (data, mode) { | |
662 var oldM = getScaleMode(data); | |
663 if (mode == null) { | |
664 mode = window.prompt("Image scale mode (screen, pixel, size)", oldM); | |
665 } | |
666 if (mode != null) { | |
667 setScaleMode(data, mode); | |
668 data.scaleMode = mode; | |
669 redisplay(data); | |
670 } | |
671 } | |
672 | |
673 // end of actions | |
674 }; | |
675 | |
676 // returns parameters from page url | |
677 var parseQueryParams = function() { | |
678 return parseQueryString(window.location.search.slice(1)); | |
679 }; | |
680 | |
681 // returns parameters from embedded img-element | |
682 var parseImgParams = function($elem) { | |
683 var src = $elem.find('img').first().attr('src'); | |
684 if (!src) return null; | |
685 var pos = src.indexOf('?'); | |
686 var query = (pos < 0) ? '' : src.substring(pos + 1); | |
687 var scalerUrl = src.substring(0, pos); | |
688 var params = parseQueryString(query); | |
689 params.scalerBaseUrl = scalerUrl; | |
690 return params; | |
691 }; | |
692 | |
693 // parses query parameter string into parameter object | |
694 var parseQueryString = function(query) { | |
695 var params = {}; | |
696 if (query == null) return params; | |
697 var pairs = query.split("&"); | |
698 //var keys = []; | |
699 for (var i = 0; i < pairs.length; i++) { | |
700 var pair = pairs[i].split("="); | |
701 if (pair.length === 2) { | |
702 params[pair[0]] = pair[1]; | |
703 //keys.push(pair[0]); | |
704 } | |
705 } | |
706 return params; | |
707 }; | |
708 | |
709 // returns a query string from key names from a parameter hash (ignoring if the same value is in defaults) | |
710 var getParamString = function (settings, keys, defaults) { | |
711 var paramString = ''; | |
712 var nx = false; | |
713 for (i = 0; i < keys.length; ++i) { | |
714 var key = keys[i]; | |
715 if ((settings[key] != null) && ((defaults == null) || (settings[key] != defaults[key]))) { | |
716 // first param gets no '&' | |
717 if (nx) { | |
718 paramString += '&'; | |
719 } else { | |
720 nx = true; | |
721 } | |
722 // add parm=val | |
723 paramString += key + '=' + settings[key]; | |
724 } | |
725 } | |
726 return paramString; | |
727 }; | |
728 | |
729 // returns URL and query string for Scaler | |
730 var getScalerUrl = function (data) { | |
731 packParams(data); | |
732 var settings = data.settings; | |
733 if (settings.scalerBaseUrl == null) { | |
734 alert("ERROR: URL of digilib Scaler servlet missing!"); | |
735 } | |
736 var keys = settings.scalerParamNames; | |
737 var queryString = getParamString(settings, keys, defaults); | |
738 var url = settings.scalerBaseUrl + '?' + queryString; | |
739 return url; | |
740 }; | |
741 | |
742 // returns URL for bird's eye view image | |
743 var getBgImgUrl = function (data, moreParams) { | |
744 var settings = data.settings; | |
745 var bgOptions = { | |
746 dw : settings.bgImgWidth, | |
747 dh : settings.bgImgHeight | |
748 }; | |
749 var bgSettings = $.extend({}, settings, bgOptions); | |
750 // filter scaler flags | |
751 if (bgSettings.mo != null) { | |
752 var mo = ''; | |
753 if (data.scalerFlags.hmir != null) { | |
754 mo += 'hmir,'; | |
755 } | |
756 if (data.scalerFlags.vmir != null) { | |
757 mo += 'vmir'; | |
758 } | |
759 bgSettings.mo = mo; | |
760 } | |
761 var params = getParamString(bgSettings, settings.bgImgParams, defaults); | |
762 var url = settings.scalerBaseUrl + '?' + params; | |
763 return url; | |
764 }; | |
765 | |
766 // returns URL and query string for current digilib | |
767 var getDigilibUrl = function (data) { | |
768 packParams(data); | |
769 var settings = data.settings; | |
770 var queryString = getParamString(settings, settings.digilibParamNames, defaults); | |
771 return settings.digilibBaseUrl + '?' + queryString; | |
772 }; | |
773 | |
774 // loads image information from digilib server via HTTP | |
775 var loadImageInfo = function (data) { | |
776 var settings = data.settings; | |
777 var p = settings.scalerBaseUrl.indexOf('/servlet/Scaler'); | |
778 var url = settings.scalerBaseUrl.substring(0, p) + '/ImgInfo-json.jsp'; | |
779 url += '?' + getParamString(settings, ['fn', 'pn'], defaults); | |
780 // TODO: better error handling | |
781 $.getJSON(url, function (json) { | |
782 console.debug("got json data=", json); | |
783 data.imgInfo = json; | |
784 // send event | |
785 $(data).trigger('imageInfo', [json]); | |
786 }); | |
787 }; | |
788 | |
789 // processes some parameters into objects and stuff | |
790 var unpackParams = function (data) { | |
791 var settings = data.settings; | |
792 // zoom area | |
793 var zoomArea = geom.rectangle(settings.wx, settings.wy, settings.ww, settings.wh); | |
794 data.zoomArea = zoomArea; | |
795 // marks | |
796 var marks = []; | |
797 if (settings.mk) { | |
798 var mk = settings.mk; | |
799 if (mk.indexOf(";") >= 0) { | |
800 var pa = mk.split(";"); // old format with ";" | |
801 } else { | |
802 var pa = mk.split(","); // new format | |
803 } | |
804 for (var i = 0; i < pa.length ; i++) { | |
805 var pos = pa[i].split("/"); | |
806 if (pos.length > 1) { | |
807 marks.push(geom.position(pos[0], pos[1])); | |
808 } | |
809 } | |
810 } | |
811 data.marks = marks; | |
812 // mo (Scaler flags) | |
813 var flags = {}; | |
814 if (settings.mo) { | |
815 var pa = settings.mo.split(","); | |
816 for (var i = 0; i < pa.length ; i++) { | |
817 flags[pa[i]] = pa[i]; | |
818 } | |
819 } | |
820 data.scalerFlags = flags; | |
821 data.scaleMode = getScaleMode(data); | |
822 retrieveOptions(data); | |
823 }; | |
824 | |
825 // put area into parameters | |
826 var packArea = function (settings, area) { | |
827 if (!area) return; | |
828 // zoom area | |
829 settings.wx = cropFloat(area.x); | |
830 settings.wy = cropFloat(area.y); | |
831 settings.ww = cropFloat(area.width); | |
832 settings.wh = cropFloat(area.height); | |
833 }; | |
834 | |
835 // put marks into parameters | |
836 var packMarks = function (settings, marks) { | |
837 if (!marks) return; | |
838 settings.mk = ''; | |
839 for (var i = 0; i < marks.length; i++) { | |
840 if (i) { | |
841 settings.mk += ','; | |
842 } | |
843 settings.mk += | |
844 cropFloatStr(marks[i].x) + '/' + | |
845 cropFloatStr(marks[i].y); | |
846 } | |
847 }; | |
848 | |
849 // pack scaler flags into parameters | |
850 var packScalerFlags = function (settings, flags) { | |
851 if (!flags) return; | |
852 var mo = ''; | |
853 for (var f in flags) { | |
854 if (mo) { | |
855 mo += ','; | |
856 } | |
857 mo += f; | |
858 } | |
859 settings.mo = mo; | |
860 }; | |
861 | |
862 // put objects back into parameters | |
863 var packParams = function (data) { | |
864 var settings = data.settings; | |
865 packArea(settings, data.zoomArea); | |
866 packMarks(settings, data.marks); | |
867 packScalerFlags(settings, data.scalerFlags); | |
868 // store user interface options in cookie | |
869 storeOptions(data); | |
870 }; | |
871 | |
872 var storeOptions = function (data) { | |
873 // save digilib options in cookie | |
874 var settings = data.settings; | |
875 if (data.dlOpts) { | |
876 // save digilib settings in options | |
877 data.dlOpts.birdview = settings.isBirdDivVisible ? 1 : 0; | |
878 data.dlOpts.buttons = settings.visibleButtonSets; | |
879 var clop = ''; | |
880 for (var o in data.dlOpts) { | |
881 if (clop) { | |
882 clop += '&'; | |
883 } | |
884 clop += o + '=' + data.dlOpts[o]; | |
885 } | |
886 if ($.cookie) { | |
887 var ck = "digilib:fn:" + escape(settings.fn) + ":pn:" + settings.pn; | |
888 console.debug("set cookie=", ck, " value=", clop); | |
889 $.cookie(ck, clop); | |
890 } | |
891 } | |
892 if (settings.interactionMode !== 'fullscreen' && $.cookie) { | |
893 // store normal parameters in cookie for embedded mode | |
894 var qs = getParamString(settings, settings.digilibParamNames, defaults); | |
895 var ck = "digilib-embed:fn:" + escape(settings.fn) + ":pn:" + settings.pn; | |
896 console.debug("set cookie=", ck, " value=", qs); | |
897 $.cookie(ck, qs); | |
898 } | |
899 }; | |
900 | |
901 var retrieveOptions = function (data) { | |
902 // clop (digilib options) | |
903 var opts = {}; | |
904 var settings = data.settings; | |
905 if ($.cookie) { | |
906 // read from cookie | |
907 var ck = "digilib:fn:" + escape(settings.fn) + ":pn:" + settings.pn; | |
908 var cp = $.cookie(ck); | |
909 console.debug("get cookie=", ck, " value=", cp); | |
910 // in query string format | |
911 opts = parseQueryString(cp); | |
912 } | |
913 data.dlOpts = opts; | |
914 // birdview option | |
915 if (opts.birdview != null) { | |
916 settings.isBirdDivVisible = opts.birdview === '1'; | |
917 } | |
918 // visible button sets | |
919 if (opts.buttons != null) { | |
920 settings.visibleButtonSets = opts.buttons; | |
921 } | |
922 }; | |
923 | |
924 // (re)load the img from a new scaler URL | |
925 var redisplay = function (data) { | |
926 var settings = data.settings; | |
927 if (settings.interactionMode === 'fullscreen') { | |
928 // update location.href (browser URL) in fullscreen mode | |
929 var url = getDigilibUrl(data); | |
930 var history = window.history; | |
931 if (typeof history.pushState === 'function') { | |
932 console.debug("faking reload to "+url); | |
933 // change url without reloading (stateObj, title, url) | |
934 // TODO: we really need to push the state in stateObj and listen to pop-events | |
935 history.replaceState({}, '', url); | |
936 // change img src | |
937 var imgurl = getScalerUrl(data); | |
938 data.$img.attr('src', imgurl); | |
939 highlightButtons(data); | |
940 // send event | |
941 $(data).trigger('redisplay'); | |
942 } else { | |
943 // reload window | |
944 window.location = url; | |
945 } | |
946 } else { | |
947 // embedded mode -- just change img src | |
948 var url = getScalerUrl(data); | |
949 data.$img.attr('src', url); | |
950 highlightButtons(data); | |
951 // send event | |
952 $(data).trigger('redisplay'); | |
953 } | |
954 }; | |
955 | |
956 // update display (overlays etc.) | |
957 var updateDisplay = function (data) { | |
958 updateImgTrafo(data); | |
959 renderMarks(data); | |
960 setupZoomDrag(data); | |
961 renderZoomArrows(data); | |
962 // send event | |
963 $(data).trigger('update'); | |
964 }; | |
965 | |
966 // returns maximum size for scaler img in fullscreen mode | |
967 var getFullscreenImgSize = function (data) { | |
968 var mode = data.settings.interactionMode; | |
969 var $win = $(window); | |
970 var winH = $win.height(); | |
971 var winW = $win.width(); | |
972 var $body = $('body'); | |
973 // include standard body margins and check plausibility | |
974 var borderW = $body.outerWidth(true) - $body.width(); | |
975 if (borderW === 0 || borderW > 100) { | |
976 console.debug("fixing border width for getFullscreenImgSize!"); | |
977 borderW = data.settings.scalerInset; | |
978 } | |
979 var borderH = $body.outerHeight(true) - $body.height(); | |
980 if (borderH === 0 || borderH > 100) { | |
981 console.debug("fixing border height for getFullscreenImgSize!"); | |
982 borderH = 5; | |
983 } | |
984 var buttonsW = 0; | |
985 if (data.settings.visibleButtonSets) { | |
986 // get button width from settings | |
987 buttonsW = data.settings.buttonSettings[mode].buttonSetWidth; | |
988 // TODO: leave space for all button sets? | |
989 } | |
990 // account for left/right border, body margins and additional requirements | |
991 var imgW = winW - borderW - buttonsW; | |
992 var imgH = winH - borderH; | |
993 console.debug(winW, winH, 'winW:', $win.width(), 'border:', borderW, 'buttonsW:', buttonsW, 'calc:', imgW); | |
994 return geom.size(imgW, imgH); | |
995 }; | |
996 | |
997 // creates HTML structure for digilib in elem | |
998 var setupScalerDiv = function (data) { | |
999 var settings = data.settings; | |
1000 var $elem = data.$elem; | |
1001 $elem.addClass('digilib'); | |
1002 var $img; | |
1003 var scalerUrl; | |
1004 if (settings.interactionMode === 'fullscreen') { | |
1005 // fullscreen | |
1006 $elem.addClass('dl_fullscreen'); | |
1007 var imgSize = getFullscreenImgSize(data); | |
1008 // fitwidth/height omits destination height/width | |
1009 if (data.dlOpts.fitheight == null) { | |
1010 settings.dw = imgSize.width; | |
1011 } | |
1012 if (data.dlOpts.fitwidth == null) { | |
1013 settings.dh = imgSize.height; | |
1014 } | |
1015 scalerUrl = getScalerUrl(data); | |
1016 $img = $('<img/>'); | |
1017 } else { | |
1018 // embedded mode -- try to keep img tag | |
1019 $elem.addClass('dl_embedded'); | |
1020 scalerUrl = getScalerUrl(data); | |
1021 $img = $elem.find('img'); | |
1022 if ($img.length > 0) { | |
1023 var oldUrl = $img.attr('src'); | |
1024 // keep img attributes from html | |
1025 var title = $img.attr('title'); | |
1026 var alt = $img.attr('alt'); | |
1027 if (oldUrl === scalerUrl) { | |
1028 console.debug("img detach:", $img); | |
1029 $img.detach(); | |
1030 } else { | |
1031 $img = $('<img/>'); | |
1032 $img.attr("title", title); | |
1033 $img.attr("alt", alt); | |
1034 } | |
1035 } else { | |
1036 $img = $('<img/>'); | |
1037 } | |
1038 } | |
1039 // create new inner html, keeping buttons and content marked with "keep" class | |
1040 $elem.contents(":not(.keep)").remove(); | |
1041 var $scaler = $('<div class="scaler"/>'); | |
1042 // scaler should be the first child element? | |
1043 $elem.prepend($scaler); | |
1044 $scaler.append($img); | |
1045 $img.addClass('pic'); | |
1046 data.$scaler = $scaler; | |
1047 data.$img = $img; | |
1048 // setup image load handler before setting the src attribute (IE bug) | |
1049 $img.load(scalerImgLoadedHandler(data)); | |
1050 $img.error(function () {console.error("error loading scaler image");}); | |
1051 $img.attr('src', scalerUrl); | |
1052 }; | |
1053 | |
1054 // creates HTML structure for a single button | |
1055 var createButton = function (data, $div, buttonName) { | |
1056 var $elem = data.$elem; | |
1057 var settings = data.settings; | |
1058 var mode = settings.interactionMode; | |
1059 var imagePath = settings.buttonSettings[mode].imagePath; | |
1060 var buttonConfig = settings.buttons[buttonName]; | |
1061 // button properties | |
1062 var action = buttonConfig.onclick; | |
1063 var tooltip = buttonConfig.tooltip; | |
1064 var icon = imagePath + buttonConfig.icon; | |
1065 // construct the button html | |
1066 var $button = $('<div class="button"></div>'); | |
1067 var $a = $('<a/>'); | |
1068 var $img = $('<img class="button"/>'); | |
1069 $div.append($button); | |
1070 $button.append($a); | |
1071 $a.append($img); | |
1072 // add attributes and bindings | |
1073 $button.attr('title', tooltip); | |
1074 $button.addClass('button-' + buttonName); | |
1075 $img.attr('src', icon); | |
1076 // create handler for the buttons | |
1077 $button.bind('click.digilib', (function () { | |
1078 // we create a new closure to capture the value of action | |
1079 if ($.isArray(action)) { | |
1080 // the handler function calls digilib with action and parameters | |
1081 return function (evt) { | |
1082 console.debug('click action=', action, ' evt=', evt); | |
1083 $elem.digilib.apply($elem, action); | |
1084 return false; | |
1085 }; | |
1086 } else { | |
1087 // the handler function calls digilib with action | |
1088 return function (evt) { | |
1089 console.debug('click action=', action, ' evt=', evt); | |
1090 $elem.digilib(action); | |
1091 return false; | |
1092 }; | |
1093 } | |
1094 })()); | |
1095 }; | |
1096 | |
1097 // creates HTML structure for buttons in elem | |
1098 var createButtons = function (data, buttonSetIdx) { | |
1099 var $elem = data.$elem; | |
1100 var settings = data.settings; | |
1101 var mode = settings.interactionMode; | |
1102 var buttonSettings = settings.buttonSettings[mode]; | |
1103 var buttonGroup = buttonSettings.buttonSets[buttonSetIdx]; | |
1104 if (buttonGroup == null) { | |
1105 // no buttons here | |
1106 return; | |
1107 } | |
1108 // button divs are marked with class "keep" | |
1109 var $buttonsDiv = $('<div class="keep buttons"/>'); | |
1110 var buttonNames = buttonSettings[buttonGroup]; | |
1111 for (var i = 0; i < buttonNames.length; i++) { | |
1112 var buttonName = buttonNames[i]; | |
1113 createButton(data, $buttonsDiv, buttonName); | |
1114 } | |
1115 // make buttons div scroll if too large for window | |
1116 if ($buttonsDiv.height() > $(window).height() - 10) { | |
1117 $buttonsDiv.css('position', 'absolute'); | |
1118 } | |
1119 // buttons hidden at first | |
1120 $buttonsDiv.hide(); | |
1121 $elem.append($buttonsDiv); | |
1122 if (data.$buttonSets == null) { | |
1123 // first button set | |
1124 data.$buttonSets = [$buttonsDiv]; | |
1125 } else { | |
1126 $elem.append($buttonsDiv); | |
1127 data.$buttonSets[buttonSetIdx] = $buttonsDiv; | |
1128 } | |
1129 return $buttonsDiv; | |
1130 }; | |
1131 | |
1132 // creates arrow overlays for moving the zoomed area | |
1133 var setupZoomArrows = function (data) { | |
1134 var $elem = data.$elem; | |
1135 var settings = data.settings; | |
1136 var show = settings.showZoomArrows; | |
1137 console.log('zoom arrows:', show); | |
1138 if (!show) return; | |
1139 var mode = settings.interactionMode; | |
1140 var arrowNames = settings.buttonSettings[mode].arrowSet; | |
1141 if (arrowNames == null) return; | |
1142 // arrow divs are marked with class "keep" | |
1143 var $arrowsDiv = $('<div class="keep arrows"/>'); | |
1144 $elem.append($arrowsDiv); | |
1145 // create all arrow buttons | |
1146 $.each(arrowNames, function(i, arrowName){ | |
1147 createButton(data, $arrowsDiv, arrowName); | |
1148 }); | |
1149 }; | |
1150 | |
1151 // size and show arrow overlays, called after scaler img is loaded | |
1152 var renderZoomArrows = function (data) { | |
1153 var settings = data.settings; | |
1154 var $arrowsDiv = data.$elem.find('div.arrows'); | |
1155 if (isFullArea(data.zoomArea) || !settings.showZoomArrows) { | |
1156 $arrowsDiv.hide(); | |
1157 return; | |
1158 } | |
1159 $arrowsDiv.show(); | |
1160 var r = geom.rectangle(data.$scaler); | |
1161 // calculate arrow bar width | |
1162 var aw = settings.zoomArrowWidth; | |
1163 var minWidth = settings.zoomArrowMinWidth; | |
1164 // arrow bar width should not exceed 10% of scaler width/height | |
1165 var maxWidth = Math.min(r.width, r.height)/10; | |
1166 if (aw > maxWidth) { | |
1167 aw = maxWidth; | |
1168 if (aw < minWidth) { | |
1169 aw = minWidth; | |
1170 } | |
1171 } | |
1172 // vertical position of left/right image | |
1173 var arrowData = [{ | |
1174 name : 'up', | |
1175 rect : geom.rectangle(r.x, r.y, r.width, aw), | |
1176 show : canMove(data, 0, -1) | |
1177 }, { | |
1178 name : 'down', | |
1179 rect : geom.rectangle(r.x, r.y + r.height - aw, r.width, aw), | |
1180 show : canMove(data, 0, 1) | |
1181 }, { | |
1182 name : 'left', | |
1183 rect : geom.rectangle(r.x, r.y, aw, r.height), | |
1184 show : canMove(data, -1, 0) | |
1185 }, { | |
1186 name : 'right', | |
1187 rect : geom.rectangle(r.x + r.width - aw, r.y, aw, r.height), | |
1188 show : canMove(data, 1, 0) | |
1189 }]; | |
1190 // render a single zoom Arrow | |
1191 var render = function (i, item) { | |
1192 var $arrow = $arrowsDiv.find('div.button-' + item.name); | |
1193 if (item.show) { | |
1194 $arrow.show(); | |
1195 } else { | |
1196 $arrow.hide(); | |
1197 return; | |
1198 } | |
1199 var r = item.rect; | |
1200 r.adjustDiv($arrow); | |
1201 var $a = $arrow.contents('a'); | |
1202 var $img = $a.contents('img'); | |
1203 $img.width(aw).height(aw); | |
1204 // hack for missing vertical-align | |
1205 if (item.name.match(/left|right/)) { | |
1206 var top = (r.height - $a.height())/2; | |
1207 $a.css({'top' : top}); // position : 'relative' | |
1208 } | |
1209 }; | |
1210 $.each(arrowData, render); | |
1211 }; | |
1212 | |
1213 // creates HTML structure for the about view in elem | |
1214 var setupAboutDiv = function (data) { | |
1215 var $elem = data.$elem; | |
1216 var settings = data.settings; | |
1217 var $aboutDiv = $('<div class="about" style="display:none"/>'); | |
1218 var $header = $('<p>Digilib Image Viewer</p>'); | |
1219 var $link = $('<a/>'); | |
1220 var $logo = $('<img class="logo" title="digilib"/>'); | |
1221 var $content = $('<p/>'); | |
1222 $elem.append($aboutDiv); | |
1223 $aboutDiv.append($header); | |
1224 $aboutDiv.append($link); | |
1225 $aboutDiv.append($content); | |
1226 $link.append($logo); | |
1227 $logo.attr('src', settings.logoUrl); | |
1228 $link.attr('href', settings.homeUrl); | |
1229 $content.text('Version: ' + settings.version); | |
1230 data.$aboutDiv = $aboutDiv; | |
1231 // click hides | |
1232 $aboutDiv.bind('click.digilib', function () { | |
1233 actions['showAboutDiv'](data, false); | |
1234 }); | |
1235 }; | |
1236 | |
1237 // shows some window e.g. 'about' (toggle visibility if show is null) | |
1238 var showDiv = function (isVisible, $div, show) { | |
1239 if (show == null) { | |
1240 // toggle visibility | |
1241 isVisible = !isVisible; | |
1242 } else { | |
1243 // set visibility | |
1244 isVisible = show; | |
1245 } | |
1246 if (isVisible) { | |
1247 $div.fadeIn(); | |
1248 } else { | |
1249 $div.fadeOut(); | |
1250 } | |
1251 return isVisible; | |
1252 }; | |
1253 | |
1254 // display more (or less) button sets | |
1255 var showButtons = function (data, more, setIdx, animated) { | |
1256 var atime = animated ? 'fast': 0; | |
1257 // get button width from settings | |
1258 var mode = data.settings.interactionMode; | |
1259 var btnWidth = data.settings.buttonSettings[mode].buttonSetWidth; | |
1260 if (more) { | |
1261 // add set | |
1262 var $otherSets = data.$elem.find('div.buttons:visible'); | |
1263 var $set; | |
1264 if (data.$buttonSets && data.$buttonSets[setIdx]) { | |
1265 // set exists | |
1266 $set = data.$buttonSets[setIdx]; | |
1267 } else { | |
1268 $set = createButtons(data, setIdx); | |
1269 } | |
1270 if ($set == null) return false; | |
1271 // include border in calculation | |
1272 //var btnWidth = $set.outerWidth(); | |
1273 // console.debug("btnWidth", btnWidth); | |
1274 // move remaining sets left and show new set | |
1275 if ($otherSets.length > 0) { | |
1276 $otherSets.animate({right : '+='+btnWidth+'px'}, atime, | |
1277 function () {$set.show();}); | |
1278 } else { | |
1279 $set.show(); | |
1280 } | |
1281 } else { | |
1282 // remove set | |
1283 var $set = data.$buttonSets[setIdx]; | |
1284 if ($set == null) return false; | |
1285 //var btnWidth = $set.outerWidth(); | |
1286 // hide last set | |
1287 $set.hide(); | |
1288 // take remaining sets and move right | |
1289 var $otherSets = data.$elem.find('div.buttons:visible'); | |
1290 $otherSets.animate({right : '-='+btnWidth+'px'}, atime); | |
1291 } | |
1292 return true; | |
1293 }; | |
1294 | |
1295 // check for buttons to highlight | |
1296 var highlightButtons = function (data, name, on) { | |
1297 var $buttons = data.$elem.find('div.buttons:visible'); // include hidden? | |
1298 // add a class for highlighted button | |
1299 var highlight = function (name, on) { | |
1300 var $button = $buttons.find('div.button-' + name); | |
1301 if (on) { | |
1302 $button.addClass('button-on'); | |
1303 } else { | |
1304 $button.removeClass('button-on'); | |
1305 } | |
1306 }; | |
1307 if (name != null) { | |
1308 return highlight(name, on); | |
1309 } | |
1310 var flags = data.scalerFlags; | |
1311 var settings = data.settings; | |
1312 highlight('rot', settings.rot); | |
1313 highlight('brgt', settings.brgt); | |
1314 highlight('cont', settings.cont); | |
1315 highlight('bird', settings.isBirdDivVisible); | |
1316 highlight('help', settings.isAboutDivVisible); | |
1317 highlight('hmir', flags.hmir); | |
1318 highlight('vmir', flags.vmir); | |
1319 highlight('quality', flags.q1 || flags.q2); | |
1320 highlight('zoomin', ! isFullArea(data.zoomArea)); | |
1321 }; | |
1322 | |
1323 // create Transform from area and $img | |
1324 var getImgTrafo = function ($img, area, rot, hmir, vmir, mode, data) { | |
1325 var picrect = geom.rectangle($img); | |
1326 if (mode != null) { | |
1327 var imgInfo = data.imgInfo; | |
1328 if (mode === 'pixel') { | |
1329 // scaler mo=clip - image area size does not come from ww, wh | |
1330 if (imgInfo != null) { | |
1331 area.width = picrect.width / imgInfo.width; | |
1332 area.height = picrect.height / imgInfo.height; | |
1333 } else { | |
1334 console.error("No image info for pixel mode!"); | |
1335 } | |
1336 } | |
1337 if (mode === 'size') { | |
1338 // scaler mo=osize - image area size does not come from ww, wh | |
1339 if (imgInfo != null) { | |
1340 var ddpi = parseFloat(data.settings.ddpi); | |
1341 area.width = (picrect.width / ddpi) / (imgInfo.width / imgInfo.dpi_x); | |
1342 area.height = (picrect.height / ddpi) / (imgInfo.height / imgInfo.dpi_y); | |
1343 } else { | |
1344 console.error("No image info for original size mode!"); | |
1345 } | |
1346 } | |
1347 } | |
1348 var trafo = geom.transform(); | |
1349 // move zoom area offset to center | |
1350 trafo.concat(trafo.getTranslation(geom.position(-area.x, -area.y))); | |
1351 // scale zoom area size to [1,1] | |
1352 trafo.concat(trafo.getScale(geom.size(1/area.width, 1/area.height))); | |
1353 // rotate and mirror (around transformed image center i.e. [0.5,0.5]) | |
1354 if (rot || hmir || vmir) { | |
1355 // move [0.5,0.5] to center | |
1356 trafo.concat(trafo.getTranslation(geom.position(-0.5, -0.5))); | |
1357 if (hmir) { | |
1358 // mirror about center | |
1359 trafo.concat(trafo.getMirror('y')); | |
1360 } | |
1361 if (vmir) { | |
1362 // mirror about center | |
1363 trafo.concat(trafo.getMirror('x')); | |
1364 } | |
1365 if (rot) { | |
1366 // rotate around center | |
1367 trafo.concat(trafo.getRotation(parseFloat(rot))); | |
1368 } | |
1369 // move back | |
1370 trafo.concat(trafo.getTranslation(geom.position(0.5, 0.5))); | |
1371 } | |
1372 // scale to screen position and size | |
1373 trafo.concat(trafo.getScale(picrect)); | |
1374 trafo.concat(trafo.getTranslation(picrect)); | |
1375 return trafo; | |
1376 }; | |
1377 | |
1378 // update scaler image transform | |
1379 var updateImgTrafo = function (data) { | |
1380 var $img = data.$img; | |
1381 if ($img != null && $img.get(0).complete) { | |
1382 // create Transform from current zoomArea and image size | |
1383 data.imgTrafo = getImgTrafo($img, data.zoomArea, | |
1384 data.settings.rot, data.scalerFlags.hmir, data.scalerFlags.vmir, | |
1385 data.scaleMode, data); | |
1386 // console.debug("imgTrafo=", data.imgTrafo); | |
1387 } | |
1388 }; | |
1389 | |
1390 // returns handler for load event of scaler img | |
1391 var scalerImgLoadedHandler = function (data) { | |
1392 return function () { | |
1393 var $img = $(this); | |
1394 console.debug("scaler img loaded=",$img); | |
1395 var $scaler = data.$scaler; | |
1396 var imgRect = geom.rectangle($img); | |
1397 // adjust scaler div size | |
1398 imgRect.adjustDiv($scaler); | |
1399 // show image in case it was hidden (for example in zoomDrag) | |
1400 $img.css('visibility', 'visible'); | |
1401 $scaler.css({'opacity' : '1', 'background-image' : 'none'}); | |
1402 // update display (render marks, etc.) | |
1403 updateDisplay(data); | |
1404 }; | |
1405 }; | |
1406 | |
1407 // handler for imageInfo loaded event | |
1408 var handleImageInfo = function (evt, json) { | |
1409 var data = this; | |
1410 updateDisplay(data); | |
1411 }; | |
1412 | |
1413 // place marks on the image | |
1414 var renderMarks = function (data) { | |
1415 if (data.$img == null || data.imgTrafo == null) return; | |
1416 console.debug("renderMarks: img=",data.$img," imgtrafo=",data.imgTrafo); | |
1417 var $elem = data.$elem; | |
1418 var marks = data.marks; | |
1419 // clear marks | |
1420 $elem.find('div.mark').remove(); | |
1421 for (var i = 0; i < marks.length; i++) { | |
1422 var mark = marks[i]; | |
1423 if (data.zoomArea.containsPosition(mark)) { | |
1424 var mpos = data.imgTrafo.transform(mark); | |
1425 console.debug("renderMarks: pos=",mpos); | |
1426 // create mark | |
1427 var html = '<div class="mark overlay">'+(i+1)+'</div>'; | |
1428 var $mark = $(html); | |
1429 $mark.attr("id", "digilib-mark-"+(i+1)); | |
1430 $elem.append($mark); | |
1431 mpos.adjustDiv($mark); | |
1432 } | |
1433 } | |
1434 }; | |
1435 | |
1436 // zooms by the given factor | |
1437 var zoomBy = function(data, factor) { | |
1438 var area = data.zoomArea; | |
1439 var newarea = area.copy(); | |
1440 // scale | |
1441 newarea.width /= factor; | |
1442 newarea.height /= factor; | |
1443 // and recenter | |
1444 newarea.x -= 0.5 * (newarea.width - area.width); | |
1445 newarea.y -= 0.5 * (newarea.height - area.height); | |
1446 newarea = FULL_AREA.fit(newarea); | |
1447 data.zoomArea = newarea; | |
1448 redisplay(data); | |
1449 }; | |
1450 | |
1451 // add a mark where clicked | |
1452 var setMark = function (data) { | |
1453 var $scaler = data.$scaler; | |
1454 // unbind other handler | |
1455 $scaler.unbind(".dlZoomDrag"); | |
1456 // start event capturing | |
1457 $scaler.one('mousedown.dlSetMark', function (evt) { | |
1458 // event handler adding a new mark | |
1459 console.log("setmark at=", evt); | |
1460 var mpos = geom.position(evt); | |
1461 var pos = data.imgTrafo.invtransform(mpos); | |
1462 data.marks.push(pos); | |
1463 redisplay(data); | |
1464 return false; | |
1465 }); | |
1466 }; | |
1467 | |
1468 // zoom to the area around two clicked points | |
1469 var zoomArea = function(data) { | |
1470 $elem = data.$elem; | |
1471 $scaler = data.$scaler; | |
1472 var pt1, pt2; | |
1473 var $zoomDiv = $('<div class="zoomrect" style="display:none"/>'); | |
1474 $elem.append($zoomDiv); | |
1475 // $zoomDiv.css(data.settings.zoomrectStyle); | |
1476 var picRect = geom.rectangle($scaler); | |
1477 // FIX ME: is there a way to query the border width from CSS info? | |
1478 // rect.x -= 2; // account for overlay borders | |
1479 // rect.y -= 2; | |
1480 | |
1481 var zoomStart = function (evt) { | |
1482 pt1 = geom.position(evt); | |
1483 // setup and show zoom div | |
1484 pt1.adjustDiv($zoomDiv); | |
1485 $zoomDiv.width(0).height(0); | |
1486 $zoomDiv.show(); | |
1487 // register events | |
1488 $elem.bind("mousemove.dlZoomArea", zoomMove); | |
1489 $elem.bind("mouseup.dlZoomArea", zoomEnd); | |
1490 return false; | |
1491 }; | |
1492 | |
1493 // mouse move handler | |
1494 var zoomMove = function (evt) { | |
1495 pt2 = geom.position(evt); | |
1496 var rect = geom.rectangle(pt1, pt2); | |
1497 rect.clipTo(picRect); | |
1498 // update zoom div | |
1499 rect.adjustDiv($zoomDiv); | |
1500 return false; | |
1501 }; | |
1502 | |
1503 // mouseup handler: end moving | |
1504 var zoomEnd = function (evt) { | |
1505 pt2 = geom.position(evt); | |
1506 // assume a click and continue if the area is too small | |
1507 var clickRect = geom.rectangle(pt1, pt2); | |
1508 if (clickRect.getArea() <= 5) return false; | |
1509 // hide zoom div | |
1510 $zoomDiv.remove(); | |
1511 // unregister events | |
1512 $elem.unbind("mousemove.dlZoomArea", zoomMove); | |
1513 $elem.unbind("mouseup.dlZoomArea", zoomEnd); | |
1514 // clip and transform | |
1515 clickRect.clipTo(picRect); | |
1516 var area = data.imgTrafo.invtransform(clickRect); | |
1517 data.zoomArea = area; | |
1518 // zoomed is always fit | |
1519 data.settings.ws = 1; | |
1520 delete data.dlOpts.fitwidth; | |
1521 delete data.dlOpts.fitheight; | |
1522 redisplay(data); | |
1523 return false; | |
1524 }; | |
1525 | |
1526 // clear old handler (also ZoomDrag) | |
1527 $scaler.unbind('.dlZoomArea'); | |
1528 $scaler.unbind(".dlZoomDrag"); | |
1529 $elem.unbind('.dlZoomArea'); | |
1530 // bind start zoom handler | |
1531 $scaler.one('mousedown.dlZoomArea', zoomStart); | |
1532 }; | |
1533 | |
1534 // set zoom background | |
1535 var setZoomBG = function(data) { | |
1536 var $scaler = data.$scaler; | |
1537 var $img = data.$img; | |
1538 var fullRect = null; | |
1539 // hide the scaler img, show background of div instead | |
1540 $img.css('visibility', 'hidden'); | |
1541 var scalerCss = { | |
1542 'background-image' : 'url(' + $img.attr('src') + ')', | |
1543 'background-repeat' : 'no-repeat', | |
1544 'background-position' : 'left top', | |
1545 'opacity' : '0.5', | |
1546 'cursor' : 'move' | |
1547 }; | |
1548 if (data.hasBgSize) { | |
1549 // full-size background using CSS3-background-size | |
1550 fullRect = data.imgTrafo.transform(FULL_AREA); | |
1551 if (fullRect.height < data.settings.maxBgSize && fullRect.width < data.settings.maxBgSize) { | |
1552 // correct offset because background is relative | |
1553 var scalerPos = geom.position($scaler); | |
1554 fullRect.addPosition(scalerPos.neg()); | |
1555 var url = getBgImgUrl(data); | |
1556 scalerCss['background-image'] = 'url(' + url + ')'; | |
1557 scalerCss[data.bgSizeName] = fullRect.width + 'px ' + fullRect.height + 'px'; | |
1558 scalerCss['background-position'] = fullRect.x + 'px '+ fullRect.y + 'px'; | |
1559 } else { | |
1560 // too big | |
1561 fullRect = null; | |
1562 } | |
1563 } | |
1564 $scaler.css(scalerCss); | |
1565 // isBgReady = true; | |
1566 return fullRect; | |
1567 }; | |
1568 | |
1569 // setup handlers for dragging the zoomed image | |
1570 var setupZoomDrag = function(data) { | |
1571 var startPos, delta, fullRect; | |
1572 var $document = $(document); | |
1573 var $data = $(data); | |
1574 var $elem = data.$elem; | |
1575 var $scaler = data.$scaler; | |
1576 var $img = data.$img; | |
1577 | |
1578 // drag the image and load a new detail on mouse up | |
1579 var dragStart = function (evt) { | |
1580 console.debug("dragstart at=", evt); | |
1581 // don't start dragging if not zoomed | |
1582 if (isFullArea(data.zoomArea)) return false; | |
1583 $elem.find(".overlay").hide(); // hide all overlays (marks/regions) | |
1584 startPos = geom.position(evt); | |
1585 delta = null; | |
1586 // set low res background immediately on mousedown | |
1587 fullRect = setZoomBG(data); | |
1588 $document.bind("mousemove.dlZoomDrag", dragMove); | |
1589 $document.bind("mouseup.dlZoomDrag", dragEnd); | |
1590 return false; | |
1591 }; | |
1592 | |
1593 // mousemove handler: drag zoomed image | |
1594 var dragMove = function (evt) { | |
1595 var pos = geom.position(evt); | |
1596 delta = startPos.delta(pos); | |
1597 if (fullRect) { | |
1598 var bgPos = fullRect.getPosition().add(delta); | |
1599 } else { | |
1600 var bgPos = delta; | |
1601 } | |
1602 // move the background image to the new position | |
1603 $scaler.css({ | |
1604 'background-position' : bgPos.x + "px " + bgPos.y + "px" | |
1605 }); | |
1606 // send message event with current zoom position | |
1607 var za = geom.rectangle($img); | |
1608 za.addPosition(delta.neg()); | |
1609 $data.trigger('dragZoom', [za]); | |
1610 //TODO: setBirdZoom(data, za); | |
1611 return false; | |
1612 }; | |
1613 | |
1614 // mouseup handler: reload zoomed image in new position | |
1615 var dragEnd = function (evt) { | |
1616 $scaler.css('cursor', 'auto'); | |
1617 $document.unbind("mousemove.dlZoomDrag", dragMove); | |
1618 $document.unbind("mouseup.dlZoomDrag", dragEnd); | |
1619 if (delta == null || delta.distance() < 2) { | |
1620 // no movement | |
1621 $img.css('visibility', 'visible'); | |
1622 $scaler.css({'opacity' : '1', 'background-image' : 'none'}); | |
1623 // unhide marks | |
1624 data.$elem.find('div.mark').show(); | |
1625 $(data).trigger('redisplay'); | |
1626 return false; | |
1627 } | |
1628 // get old zoom area (screen coordinates) | |
1629 var za = geom.rectangle($img); | |
1630 // move | |
1631 za.addPosition(delta.neg()); | |
1632 // transform back | |
1633 var newArea = data.imgTrafo.invtransform(za); | |
1634 data.zoomArea = FULL_AREA.fit(newArea); | |
1635 redisplay(data); | |
1636 return false; | |
1637 }; | |
1638 | |
1639 // clear old handler | |
1640 $document.unbind(".dlZoomDrag"); | |
1641 $scaler.unbind(".dlZoomDrag"); | |
1642 if (! isFullArea(data.zoomArea)) { | |
1643 // set handler | |
1644 $scaler.bind("mousedown.dlZoomDrag", dragStart); | |
1645 } | |
1646 }; | |
1647 | |
1648 // get image quality as a number (0..2) | |
1649 var getQuality = function (data) { | |
1650 var flags = data.scalerFlags; | |
1651 var q = flags.q2 || flags.q1 || 'q0'; // assume q0 as default | |
1652 return parseInt(q[1], 10); | |
1653 }; | |
1654 | |
1655 // set image quality as a number (0..2) | |
1656 var setQuality = function (data, qual) { | |
1657 var flags = data.scalerFlags; | |
1658 // clear flags | |
1659 for (var i = 0; i < 3; ++i) { | |
1660 delete flags['q'+i]; | |
1661 } | |
1662 flags['q'+qual] = 'q'+qual; | |
1663 }; | |
1664 | |
1665 // get image scale mode (screen, pixel, size) | |
1666 var getScaleMode = function (data) { | |
1667 if (data.scalerFlags.clip != null) { | |
1668 return 'pixel'; | |
1669 } else if (data.scalerFlags.osize != null) { | |
1670 return 'size'; | |
1671 } | |
1672 // mo=fit is default | |
1673 return 'screen'; | |
1674 }; | |
1675 | |
1676 // set image scale mode (screen, pixel, size) | |
1677 var setScaleMode = function (data, mode) { | |
1678 delete data.scalerFlags.fit; | |
1679 delete data.scalerFlags.clip; | |
1680 delete data.scalerFlags.osize; | |
1681 if (mode === 'pixel') { | |
1682 data.scalerFlags.clip = 'clip'; | |
1683 } else if (mode === 'size') { | |
1684 data.scalerFlags.osize = 'osize'; | |
1685 } | |
1686 // mo=fit is default | |
1687 }; | |
1688 | |
1689 // sets a key to a value (relative values with +/- if relative=true) | |
1690 var setNumValue = function(settings, key, value) { | |
1691 if (value == null) return null; | |
1692 if (isNumber(value)) { | |
1693 settings[key] = value; | |
1694 return value; | |
1695 } | |
1696 var sign = value[0]; | |
1697 if (sign === '+' || sign === '-') { | |
1698 if (settings[key] == null) { | |
1699 // this isn't perfect but still... | |
1700 settings[key] = 0; | |
1701 } | |
1702 settings[key] = parseFloat(settings[key]) + parseFloat(value); | |
1703 } else { | |
1704 settings[key] = value; | |
1705 } | |
1706 return settings[key]; | |
1707 }; | |
1708 | |
1709 // auxiliary function, assuming equal border width on all sides | |
1710 var getBorderWidth = function($elem) { | |
1711 var border = $elem.outerWidth() - $elem.width(); | |
1712 return border/2; | |
1713 }; | |
1714 | |
1715 // auxiliary function, can the current zoomarea be moved further? | |
1716 var canMove = function(data, movx, movy) { | |
1717 var za = data.zoomArea; | |
1718 if (isFullArea(za)) return false; | |
1719 var x2 = za.x + za.width; | |
1720 var y2 = za.y + za.height; | |
1721 return ((movx < 0) && (za.x > 0)) | |
1722 || ((movx > 0) && (x2 < 1.0)) | |
1723 || ((movy < 0) && (za.y > 0)) | |
1724 || ((movy > 0) && (y2 < 1.0)) | |
1725 }; | |
1726 | |
1727 // auxiliary function (from old dllib.js) | |
1728 var isFullArea = function (area) { | |
1729 return (area.width === 1.0) && (area.height === 1.0); | |
1730 }; | |
1731 | |
1732 // auxiliary function (from Douglas Crockford, A.10) | |
1733 var isNumber = function (value) { | |
1734 return typeof value === 'number' && isFinite(value); | |
1735 }; | |
1736 | |
1737 // auxiliary function to crop senseless precision | |
1738 var cropFloat = function (x) { | |
1739 return parseInt(10000 * x, 10) / 10000; | |
1740 }; | |
1741 | |
1742 // idem, string version | |
1743 var cropFloatStr = function (x) { | |
1744 return cropFloat(x).toString(); | |
1745 }; | |
1746 | |
1747 // fallback for console.log calls | |
1748 if (customConsole) { | |
1749 var logFunction = function(type) { | |
1750 return function(){ | |
1751 var $debug = $('#debug'); // debug div | |
1752 if (!$debug) return; | |
1753 var args = Array.prototype.slice.call(arguments); | |
1754 var argtext = args.join(' '); | |
1755 var $logDiv = $('<div/>'); | |
1756 $logDiv.addClass(type); | |
1757 $logDiv.text(argtext); | |
1758 $debug.append($logDiv); | |
1759 }; | |
1760 }; | |
1761 console.log = logFunction('_log'); | |
1762 console.debug = logFunction('_debug'); | |
1763 console.error = logFunction('_error'); | |
1764 } | |
1765 | |
1766 // functions to export to plugins | |
1767 fn = { | |
1768 geometry : geom, | |
1769 parseQueryString : parseQueryString, | |
1770 getScalerUrl : getScalerUrl, | |
1771 getParamString : getParamString, | |
1772 getDigilibUrl : getDigilibUrl, | |
1773 unpackParams : unpackParams, | |
1774 packParams : packParams, | |
1775 packArea : packArea, | |
1776 packMarks : packMarks, | |
1777 packScalerFlags : packScalerFlags, | |
1778 storeOptions : storeOptions, | |
1779 redisplay : redisplay, | |
1780 updateDisplay : updateDisplay, | |
1781 highlightButtons : highlightButtons, | |
1782 showDiv : showDiv, | |
1783 setZoomBG : setZoomBG, | |
1784 getImgTrafo : getImgTrafo, | |
1785 getQuality : getQuality, | |
1786 setQuality : setQuality, | |
1787 getScaleMode : getScaleMode, | |
1788 setScaleMode : setScaleMode, | |
1789 canMove : canMove, | |
1790 isFullArea : isFullArea, | |
1791 isNumber : isNumber, | |
1792 getBorderWidth : getBorderWidth, | |
1793 cropFloat : cropFloat, | |
1794 cropFloatStr : cropFloatStr | |
1795 }; | |
1796 | |
1797 // hook digilib plugin into jquery | |
1798 $.fn.digilib = function (action) { | |
1799 // plugin extension mechanism, called when the plugins' code is read | |
1800 if (action === 'plugin') { | |
1801 var plugin = arguments[1]; | |
1802 // each plugin needs a name | |
1803 if (plugin.name != null) { | |
1804 plugins[plugin.name] = plugin; | |
1805 // share common objects | |
1806 plugin.defaults = defaults; | |
1807 plugin.buttons = buttons; | |
1808 plugin.actions = actions; | |
1809 plugin.fn = fn; | |
1810 plugin.plugins = plugins; | |
1811 // and install | |
1812 if (typeof plugin.install === 'function') { | |
1813 plugin.install(plugin); | |
1814 } | |
1815 } | |
1816 // plugins will be initialised when action.init is called | |
1817 } else if (actions[action]) { | |
1818 // call action on this with the remaining arguments (inserting data as first argument) | |
1819 var $elem = $(this); | |
1820 var data = $elem.data('digilib'); | |
1821 var args = Array.prototype.slice.call(arguments, 1); | |
1822 args.unshift(data); | |
1823 return actions[action].apply(this, args); | |
1824 } else if (typeof action === 'object' || !action) { | |
1825 // call init on the digilib jQuery object | |
1826 return actions.init.apply(this, arguments); | |
1827 } else { | |
1828 $.error('action ' + action + ' does not exist on jQuery.digilib'); | |
1829 } | |
1830 }; | |
1831 | |
1832 })(jQuery); |