IMHO SEADragon should include some controls to go to next/previous image and not restrict the navigation between images to clicking the thumnails. So I decided to build these new custom buttons.
Documentation
Before you continue reading these post you should be familiar with SEADragon, DeepZoom and jCarousel. If you are not, please read the following posts:
[1] DeepZoom & SEADragon part one (build basic html page with the viewer)
[2] DeepZoom & SEADragon part two (use jCarousel to display thumbs)
[3] DeepZoom & SEADragon part three (remove SEADragon logo).
Adding custom controls to the viewer: some “easy” example
Before changing my code I decided to ask google if someone had added custom buttons to the viewer, and google, as usual, answered my question. In ajaxdaddy.com I found this -great- example of how to do it. It’s quite well explained there. Please read it carefully.
Adding custom controls to the viewer: integration with jCarousel
The example shown above is great. But I had a slightly diferent problem: I had to integrate the next/previous button behaviour with my thumbs carousel, so it moved forwards and backwards when necessary.
We well part from the previous example code to explain all the modifications.
- Change
doOpenfunction: this function had two parameters, the first one was theitemto show. I changed this parameter so it was the index to read, and not the item itself.
I also defined a new global variable (I know, it is not the best choice, but it works) calledselectedIndexto know what is the index corresponding to the shown image at any time. I did all this in order to make easier the next steps
This was my code *before* the modifications:
function doOpen(item, anchor) { if(selectedItem) selectedItem.button.src = thumbsPath + selectedItem.thumb + '_rest.png'; if(item){ viewer.openDzi(item.dzi, item.xml); titleDiv.innerHTML = item.title; descDiv.innerHTML = item.desc; item.button.src = thumbsPath + item.thumb + '_selected.png'; selectedItem = item; if (anchor) { window.location.hash = "#" + item.thumb; } } }
*After* the mods, my code is:
var selectedIndex = null; function doOpen(index, anchor){ item = data[index]; if(selectedItem) selectedItem.button.src = thumbsPath + selectedItem.thumb + '_rest.png'; if(item){ viewer.openDzi(item.dzi, item.xml); titleDiv.innerHTML = item.title; descDiv.innerHTML = item.desc; item.button.src = thumbsPath + item.thumb + '_selected.png'; selectedItem = item; selectedIndex = index; if (anchor) { window.location.hash = "#" + item.thumb; } } }
Of course the way of calling these functions also changes. Again, a before&after:
*before*:doOpen(dataMap[hash] || data[0], false);
*after*:doOpen(hash || 0, false);
- Download two images for the buttons. For instance, you can take this two:
Next
PreviousPlace them in
./imgfolder and name themload_next_image.pngandload_prev_image.png. - Now, we have to add the new buttons and their behaviour, which is done by the following code (i show only the part for the “next” button, since the “prev” is pretty similar):
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
var nextControl = makeControlNEXT(); nextControl.style.marginLeft="4px"; nextControl.style.marginBottom="4px"; viewer.addControl(nextControl, Seadragon.ControlAnchor.TOP_RIGHT); /* * Creates the new "next" button which does: * (1) Show -if available- the next image in the SeaDragon viewer * (2) Moves forwards -if necessary- the JCarousel so the */ function makeControlNEXT() { var controlNEXT = document.createElement("a"); var controlImg = document.createElement('img'); controlImg.src="./img/load_next_image.png"; controlImg.className="thumb"; controlImg.title="next"; controlNEXT.href = "#"; // so browser shows it as link controlNEXT.id = "controlNEXT2"; // this is QUITE important for the next part!! (jCarousel) controlNEXT.className = "controlNEXT"; controlNEXT.appendChild(controlImg); Seadragon.Utils.addEvent(controlNEXT, "click", doOpenNEXT); return controlNEXT; } function doOpenNEXT(event) { Seadragon.Utils.cancelEvent(event); // so link isn't processed if (!viewer.isOpen()) { return; } if ( (selectedIndex+1) < count){ // if there is a next image to show...show it! doOpen(selectedIndex+1,true); } }
Note the
controlNEXT.id = "controlNEXT2";in line 18. We will use this in next section to “connect” this to the function that must be executed in jCarousel. - At this point you should have both buttons working with SEADragon. This is, when you click next button the viewer will show the next image. But there still remains something: notice the jCarousel in the bottom, it does not move forwards nor backwards when the displayed image changes! This is not a problem if you only have three images, but I’ll assume you’ll have more than three. So lets get to work.
- Modify the way jCarousel is built. Again, a before&after is shown:
*before:
<script type="text/javascript"> jQuery(document).ready(function() { jQuery('#mycarousel').jcarousel(); }); </script>
*after:
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
<script type="text/javascript"> function mycarousel_initCallback(carousel) { jQuery('.jcarousel-control a').bind('click', function() { carousel.scroll(jQuery.jcarousel.intval(jQuery(this).text())); return false; }); jQuery('#controlNEXT2').bind('click', function() { $.balance = selectedIndex% 3; $.bloquear = selectedIndex; if ($.balance == 0 && $.bloquear!=0){ // must the carousel be moved? carousel.next(); } return false; }); jQuery('#controlPREV2').bind('click', function() { $.balance = selectedIndex% 3; $.bloquear = selectedIndex; if ($.balance == 2 && $.bloquear!=0){ carousel.prev(); } return false; }); }; jQuery(document).ready(function() { if ($.balance === undefined) $.balance = -1; jQuery('#mycarousel').jcarousel( { scroll: 3, initCallback: mycarousel_initCallback } ); }); </script>
Notice the
controNEXT2in line 17? It is the same id that we had in section 3, line 18.
So, the behaviour when clicking “next” or “previous” buttons in the top of the viewer is:
- First, call doOpenNEXT or doOpenPREV which makes SEADragon viewer to show the next / previous image.
- Second, call the function defined in section 5, line 18 which makes jCarousel to move forwards or backwards if necesary.
- We use selectedIndex variable to decide if the jCarousel must or mustn’t move.
You can see a FULL EXAMPLE HERE.
Solving the browser issues: IE8 vs Mozilla
If you run the example above in a Mozilla/Netscape browser it will work fine. But if you try to do it in a Internet Explorer browser, the jCarousel will not move when it has to.
This is related with the order of execution of functions:
- Mozilla: it executes first doOpenNEXT / doOpenPREV and then the function next of jCarousel.
- IE8: the order of execution is first the function of jCarousel and then the others.
So selectedIndex is not updated when it has to be in IE8 and it fails to move the jCarousel when it has to.
Please read my browser detection with javascript post before doing anything. Then modify the jCarousel functions as in:
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 | /* browser detection! */ var browser=navigator.appName; var b_version=navigator.appVersion; var version=parseFloat(b_version); jQuery('#controlNEXT2').bind('click', function() { if (browser=="Netscape"){ // mozilla, netscape, ... $.balance = selectedIndex% 3; $.bloquear = selectedIndex; } else if (browser=="Microsoft Internet Explorer"){ // Internet Explorer 4 or Opera with IE user agent $.balance = (selectedIndex+1)% 3; $.bloquear = (selectedIndex+1); } if ($.balance == 0 && $.bloquear!=0){ carousel.next(); } return false; }); jQuery('#controlPREV2').bind('click', function() { if (browser=="Netscape"){ $.balance = selectedIndex% 3; $.bloquear = selectedIndex; } else if (browser=="Microsoft Internet Explorer"){ $.balance = (selectedIndex-1)% 3; $.bloquear = (selectedIndex-1); } if ($.balance == 2 && $.bloquear!=0){ carousel.prev(); } return false; }); |
Now it works fine!
Visit the FULL FINAL EXAMPLE!





