├── proxy.php ├── style.css ├── README.md ├── zoomify.js ├── natgal.js ├── dezoomify.php ├── dezoomify.html ├── zoommanager.js ├── dezoomify.js └── natgal-orig.js /proxy.php: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin:0; 3 | padding:0; 4 | width:100%; 5 | background-color:black; 6 | } 7 | 8 | #status { 9 | position:fixed; 10 | top:20%; 11 | left:30%; 12 | 13 | width:40%; 14 | min-height:20%; 15 | 16 | background:linear-gradient(rgba(10,10,10,0.8),rgba(50,50,50,0.5)); 17 | box-shadow:0 0 8px rgba(200,200,255,0.5); 18 | border-radius:16px; 19 | color:white; 20 | } 21 | 22 | h1 { 23 | text-align:center; 24 | text-shadow:0 0 2px rgba(200,200,255); 25 | } 26 | 27 | #description { 28 | text-align:justify; 29 | } 30 | 31 | #licenseinfo { 32 | font-size:0.7em; 33 | color:#888; 34 | } 35 | 36 | #licenseinfo a { 37 | color:#444; 38 | } 39 | 40 | form { 41 | margin-top:10%; 42 | padding-left:7%; 43 | width:86%; 44 | } 45 | 46 | input#url { 47 | width:100%; 48 | max-width:100%; 49 | margin-bottom:10px; 50 | border:0; 51 | box-shadow:0 0 3px #aaa; 52 | height:1.5em; 53 | } 54 | input#url:focus { 55 | box-shadow:0 0 10px #aaf; 56 | } 57 | 58 | .dezoomifiers label { 59 | margin-right:20px; 60 | } 61 | input[type=submit] { 62 | width:30%; 63 | margin:25px 35% 0; 64 | } 65 | input[type=submit]:focus { 66 | box-shadow:0 0 10px #aaf; 67 | } 68 | 69 | #percent { 70 | color:white; 71 | font-size:1em; 72 | text-align:center; 73 | } 74 | #progressbar { 75 | width:0; 76 | background-color:red; 77 | height:7px; 78 | } 79 | #error { 80 | margin:30px; 81 | text-align:justify; 82 | background-color:white; 83 | color:black; 84 | display:none; 85 | padding:7px; 86 | } 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Dezoomify 2 | #Reassemble tiles of a zoomify-powered image 3 | 4 | This script makes a downloadable image from an image viewable via a flash or html5 zoomify interface. 5 | It also works with ZoomTool, the zoomable images tool from the National Gallery of London. 6 | 7 | Input : the URL of a site containing a zoomify viewer. 8 | Output : An image that you can download (in Firefox). 9 | 10 | #Try it 11 | If you are not interested in the source code and just want to *assemble tiles of (dezoomify) a zoomify-powered image*, go there : [unzoomify an image](http://ophir.lojkine.free.fr/dezoomify/dezoomify.html) 12 | 13 | #Programming Languages 14 | The aim of the script is to do as much as possible in _Javascript_ (with the HTML5 __ tag), and only the network-related stuffs on the server side (in this case, _PHP_). 15 | 16 | #Wikimedia 17 | This script on wikimedia : [Zoomify in the help about zoomable Images on wikimedia](https://secure.wikimedia.org/wikipedia/commons/wiki/Help:Zoomable_images) 18 | 19 | #GPL 20 | > Copyright © 2011 Lovasoa 21 | > 22 | > This file is part of Dezoomify. 23 | > 24 | > Dezoomify is free software; you can redistribute it and/or modify 25 | > it under the terms of the GNU General Public License as published by 26 | > the Free Software Foundation; either version 2 of the License, or 27 | > (at your option) any later version. 28 | > 29 | > Dezoomify is distributed in the hope that it will be useful, 30 | > but WITHOUT ANY WARRANTY; without even the implied warranty of 31 | > MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 32 | > GNU General Public License for more details. 33 | > 34 | > You should have received a copy of the GNU General Public License 35 | > along with Dezoomify; if not, write to the Free Software 36 | > Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 37 | > USA*/ 38 | -------------------------------------------------------------------------------- /zoomify.js: -------------------------------------------------------------------------------- 1 | var zoomify = (function () { //Code isolation 2 | //var PHPSCRIPT = "http://ophir.alwaysdata.net/dezoomify/dezoomify.php" //Use a remote php script if you can't host PHP 3 | var PHPSCRIPT = "dezoomify.php"; 4 | 5 | return { 6 | "open" : function (url) { 7 | var codedurl = encodeURIComponent(url); 8 | 9 | var xhr = new XMLHttpRequest(); 10 | xhr.open("GET", PHPSCRIPT + "?url=" + codedurl, true); 11 | 12 | xhr.onloadstart = function () { 13 | ZoomManager.updateProgress(0, "Sent a request in order to get informations about the image..."); 14 | }; 15 | xhr.onerror = function (e) { 16 | console.log("XHR error", e); 17 | ZoomManager.error("Unable to connect to the proxy server to get the required informations."); 18 | }; 19 | xhr.onloadend = function () { 20 | var xml = xhr.responseXML; 21 | var infos = xml.getElementsByTagName("IMAGE_PROPERTIES")[0]; 22 | if (!infos) { 23 | ZoomManager.error(); 24 | console.log(xhr.responseText); 25 | } 26 | var data = {}; 27 | data.path = xml.firstChild.getAttribute("path"); 28 | data.width = parseInt(infos.getAttribute("WIDTH")); 29 | data.height = parseInt(infos.getAttribute("HEIGHT")); 30 | data.tileSize = parseInt(infos.getAttribute("TILESIZE")); 31 | data.numTiles = parseInt(infos.getAttribute("NUMTILES")); //Total number of tiles (for all zoom levels) 32 | data.zoomFactor = 2; //Zooming factor between two consecutive zoom levels 33 | 34 | ZoomManager.readyToRender(data); 35 | }; 36 | xhr.send(null); 37 | }, 38 | "getTileURL" : function (col, row, zoom, data) { 39 | var totalTiles = data.nbrTilesX * data.nbrTilesY; 40 | var tileGroup = Math.floor((data.numTiles - totalTiles + col + row*data.nbrTilesX) / 256); 41 | return data.path + "/TileGroup" + tileGroup + "/" + zoom + "-" + col + "-" + row + ".jpg"; 42 | } 43 | }; 44 | })(); 45 | ZoomManager.dezoomersList["zoomify"] = zoomify; 46 | ZoomManager.dezoomer = nationalgallery; 47 | -------------------------------------------------------------------------------- /natgal.js: -------------------------------------------------------------------------------- 1 | var nationalgallery = (function(){ 2 | var PHPSCRIPT = "proxy.php"; 3 | 4 | function getObfuscatedTileId (id, zoom, row, col) { 5 | var repl = "vRfOdXapKz"; 6 | var num = "0000000" + (row*1e9 + id*1e4 + col*1e2 + zoom), res=''; 7 | for (var i=num.length-1; i>num.length-12; i--) res = repl[num[i]] + res; 8 | return res; 9 | } 10 | 11 | return { 12 | "open" : function (url) { 13 | var codedurl = encodeURIComponent(url); 14 | 15 | var base = url.split("/").slice(0,3).join("/"); 16 | var path = base + "/custom/ng/tile.php?id="; 17 | 18 | var xhr = new XMLHttpRequest(); 19 | xhr.open("GET", PHPSCRIPT + "?url=" + codedurl, true); 20 | xhr.responseType = "document"; 21 | 22 | xhr.onloadstart = function () { 23 | ZoomManager.updateProgress(0, "Sent a request in order to get informations about the image..."); 24 | }; 25 | xhr.onerror = function (e) { 26 | console.log("XHR error", e); 27 | ZoomManager.error(); 28 | }; 29 | xhr.onloadend = function () { 30 | var doc = xhr.response; 31 | var dataElems = doc.querySelectorAll(".data dt"); 32 | if (dataElems.length===0) ZoomManager.error("No valid zoomify information found."); 33 | var rawdata = {}; 34 | for (var i=0; i. 17 | 18 | This script extracts the ImageProperties.xml file from the webpage specified by the GET parameter "url". 19 | */ 20 | 21 | //Allow cross-domain requests on this script. This allows people to host only the JS + HTML part of the script, and use an other server to get the image properties from the web. 22 | header("Access-Control-Allow-Origin: *"); 23 | 24 | ini_set("display_errors",0);//Comment this line to debug the script 25 | 26 | function url_to_absolute ($url, $path) { 27 | //Returns the absolute path to an URL and a path 28 | 29 | //If the path is in fact an url, return it 30 | if (preg_match("#^[a-zA-Z0-9]+://#i", $path)) return $path; 31 | 32 | if ($path[0] == '/'){//If the path is relative to the base server 33 | return preg_replace ("#([a-zA-Z0-9]+://[^/]+)(/.*)?#i", "$1/".$path, $url); 34 | 35 | }else { 36 | return preg_replace ("#([a-zA-Z0-9]+://[^/]+(/[^$\.\?&]*)?)(/.*?)?$#i", "$1/".$path, $url); 37 | } 38 | } 39 | 40 | function zoomifyPath ($url){ 41 | //Extract the path to the zoomify folder from any page containing a Zoomify viewer 42 | $html = file_get_contents($url); 43 | if ($html === FALSE){//If an error occured 44 | return $url;//We assume that the provided url was the zoomifyImagePath 45 | } 46 | 47 | /*First test if the webpage contains a Zoomify flash player. 48 | We search a string of the form zomifyImagePath=sth*/ 49 | preg_match ( '/zoomifyImagePath=([^\'"&]*)[\'"&]/i', $html , $addr ); 50 | 51 | /*If no flash player was found, search a Zoomify HTML5 player. 52 | The searched url is within a javascript script. It's the second argument of the showImage function.*/ 53 | if (count($addr)===0) 54 | preg_match("#showImage\([^),]*,[^\"']*[\"']([^'\"]*)[^)]*\)#", $html, $addr); 55 | 56 | //var_dump($addr); 57 | return url_to_absolute ($url, $addr[1]); 58 | } 59 | 60 | if (isset($_GET['url'])){ 61 | $url = addslashes(rawurldecode($_GET['url'])); 62 | $path = zoomifyPath ($url); 63 | }elseif (isset($_GET["path"])){ 64 | $path=addslashes(rawurldecode($_GET["path"])); 65 | }else { 66 | echo "
67 |

68 | 69 | 70 | 71 | 72 |

"; 73 | exit(); 74 | } 75 | 76 | 77 | $xml = file_get_contents($path."/ImageProperties.xml"); 78 | 79 | header('Content-Type: text/xml');//The script returns xml and not html 80 | echo "\n"; 81 | echo " 82 | 83 | $xml 84 | 85 | "; 86 | ?> 87 | -------------------------------------------------------------------------------- /dezoomify.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | Dezoomify 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 |
22 |
23 | 24 |
25 |

Dezoomify

26 |

27 | Enter the URL of a webpage containing a zoomify viewer. 28 | The image will be downloaded at maximal resolution. 29 | On Firefox, you will then be able to right-click on the image, 30 | and choose "Save As" in order to save it as a PNG file on your computer. 31 |

32 |

33 | This script is released under the GPL. 34 | See the source code. 35 |

36 |
37 | 38 |

39 | 40 | 41 | 42 | 43 | 44 | 45 |

46 | 47 | 48 |

49 |

50 | 51 |

Error : Unable to extract informations from the given file !

52 |

Try to extract the zoomifyImagePath in the webpage containing the zoomify viewer on your own, and paste this path to the form on this page. Search 'zoomifyImagePath' in the real source code of the page (the one you can get with firebug in firefox, or the web inspector in Safari/Chrome.

53 |

Exemple of path : http://images.famsf.org/Zoom/7822312331480020/

54 |
55 |
56 | 57 | 58 | 59 | 60 | 61 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /zoommanager.js: -------------------------------------------------------------------------------- 1 | var UI = {}; 2 | UI.canvas = document.getElementById("rendering-canvas"); 3 | 4 | /** Adjusts the size of the image, so that is fits page width or page height 5 | **/ 6 | UI.changeSize = function () { 7 | var width = UI.canvas.width, height = UI.canvas.height; 8 | console.log("changeSize"); 9 | switch (this.fit) { 10 | case "width": 11 | this.fit = "height"; 12 | UI.canvas.style.height = window.innerHeight + "px"; 13 | UI.canvas.style.width = window.innerHeight / height * width + "px"; 14 | break; 15 | case "height": 16 | this.fit = "none"; 17 | UI.canvas.style.width = width + "px"; 18 | UI.canvas.style.height = height + "px"; 19 | break; 20 | default: 21 | this.fit = "width"; 22 | UI.canvas.style.width = window.innerWidth + "px"; 23 | UI.canvas.style.height = window.innerWidth / width * height + "px"; 24 | } 25 | }; 26 | /** Sets the width and height of the canvas 27 | **/ 28 | UI.setupRendering = function (data) { 29 | document.getElementById("urlform").style.display = "none"; 30 | UI.canvas.width = data.width; 31 | UI.canvas.height = data.height; 32 | UI.canvas.onclick = UI.changeSize; 33 | UI.ctx = UI.canvas.getContext("2d"); 34 | UI.changeSize(); 35 | console.log("Image size: " + data.width + "x" + data.height); 36 | }; 37 | 38 | UI.drawTile = function(tileImg, x, y) { 39 | UI.ctx.drawImage(tileImg, x, y); 40 | }; 41 | 42 | UI.error = function(errmsg) { 43 | var elem = document.getElementById("error"); 44 | if (errmsg) elem.innerHTML = errmsg; 45 | elem.style.display = "block"; 46 | }; 47 | 48 | UI.updateProgress = function (percent, text) { 49 | document.getElementById("percent").innerHTML = text + ' (' + parseInt(percent) + ") %"; 50 | document.getElementById("progressbar").style.width = percent + "%"; 51 | }; 52 | 53 | UI.loadEnd = function() { 54 | document.getElementById("status").style.display = "none"; 55 | }; 56 | 57 | var ZoomManager = {}; 58 | 59 | ZoomManager.error = function (errmsg) { 60 | console.error(errmsg); 61 | UI.error(errmsg); 62 | }; 63 | ZoomManager.updateProgress = UI.updateProgress; 64 | 65 | ZoomManager.status = { 66 | "loaded" : 0, 67 | "totalTiles" : 1 68 | }; 69 | 70 | ZoomManager.startTimer = function () { 71 | var timer = setInterval(function () { 72 | /*Update the User Interface each 500ms, and not in addTile, because it would 73 | slow down the all process to update the UI too often.*/ 74 | var loaded = ZoomManager.status.loaded, total = ZoomManager.status.totalTiles; 75 | ZoomManager.updateProgress(100 * loaded / total, "Loading the tiles..."); 76 | 77 | if (loaded == total) { 78 | clearInterval(timer); 79 | ZoomManager.loadEnd(); 80 | } 81 | }, 500); 82 | return timer; 83 | }; 84 | 85 | ZoomManager.loadEnd = UI.loadEnd; 86 | 87 | 88 | ZoomManager.readyToRender = function(data) { 89 | 90 | data.nbrTilesX = data.nbrTilesX || Math.ceil(data.width / data.tileSize); 91 | data.nbrTilesY = data.nbrTilesY|| Math.ceil(data.height / data.tileSize); 92 | data.totalTiles = data.totalTiles || data.nbrTilesX*data.nbrTilesY; 93 | 94 | ZoomManager.status.totalTiles = data.totalTiles; 95 | ZoomManager.data = data; 96 | UI.setupRendering(data); 97 | 98 | ZoomManager.updateProgress(0, "Preparing tiles load..."); 99 | ZoomManager.startTimer(); 100 | 101 | var render = ZoomManager.dezoomer.render || ZoomManager.defaultRender; 102 | setTimeout(render, 1, data); //Give time to refresh the UI, in case render would take a long time 103 | }; 104 | 105 | ZoomManager.defaultRender = function (data) { 106 | var zoom = data.maxZoomLevel || ZoomManager.findMaxZoom(data); 107 | for (var x=0; x height) ? findZoom(width) : findZoom(height)); 113 | 114 | zoom = (width > height) ? findZoom(width) : findZoom(height); 115 | 116 | var nbrTilesX = Math.ceil(width / tileSize); 117 | var nbrTilesY = Math.ceil(height / tileSize); 118 | 119 | loaded = 0; 120 | totalTiles = nbrTilesX * nbrTilesY; //Total number of tiles to load 121 | 122 | var skippedTiles = numTiles - totalTiles; 123 | 124 | for (var y = 0; y < nbrTilesY; y++) { 125 | for (var x = 0; x < nbrTilesX; x++) { 126 | 127 | var tileGroup = Math.floor(skippedTiles / 256); 128 | skippedTiles++; 129 | 130 | var url = path + "/TileGroup" + tileGroup + "/" + zoom + "-" + x + "-" + y + ".jpg"; 131 | addTile(url, x * tileSize, y * tileSize); 132 | } 133 | } 134 | 135 | timer = setInterval(function () { 136 | /*Update the User Interface each 500ms, and not in addTile, because it would 137 | slow down the all process to update the UI too often.*/ 138 | updateProgress(100 * loaded / totalTiles, "Loading the tiles..."); 139 | 140 | if (loaded == totalTiles) { 141 | loadEnd(); 142 | } 143 | }, 500); 144 | } 145 | -------------------------------------------------------------------------------- /natgal-orig.js: -------------------------------------------------------------------------------- 1 | /******************/ 2 | /*Module: ZoomTool*/ 3 | /******************/ 4 | var g_aZoomTool = []; 5 | 6 | function ZoomTool(id) { 7 | this.container = document.getElementById(id); 8 | if (!this.container) 9 | return; 10 | this.data = ZoomTool.parseData(this.container); 11 | this.fallbackImage; 12 | this.viewport; 13 | this.tiles; 14 | this.main; 15 | this.controls; 16 | this.mark; 17 | this.pressed = false; 18 | this.ticker = 0; 19 | this.ie = (navigator.userAgent.search(/MSIE/i) > -1); 20 | this.ieVersion = -1; 21 | if (this.ie) { 22 | var ua = navigator.userAgent; 23 | var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); 24 | if (re.exec(ua) != null) 25 | this.ieVersion = parseFloat(RegExp.$1); 26 | } 27 | this.safari = (navigator.userAgent.search(/Safari/i) > -1); 28 | this.grabCursor = (navigator.userAgent.search(/KHTML|Opera|MSIE/i) > -1 ? 'pointer' : '-moz-grab'); 29 | this.dragCursor = (navigator.userAgent.search(/KHTML|Opera|MSIE/i) > -1 ? 'move' : '-moz-grabbing'); 30 | this.cache = {}; 31 | this.map = null; 32 | this.area = null; 33 | this.tilePath = 34 | (this.data.siteWebRoot != undefined ? this.data.siteWebRoot : "/") + "custom/ng/tile.php?id="; 35 | this.idCache = {}; 36 | this.previousZoom = this.data.zoom; 37 | this.fadeTime = this.data.fade != undefined ? this.data.fade : 20; 38 | this.draw = true; 39 | this.links = {}; 40 | this.obfuscateCache = {}; 41 | this.dimensionCache = {}; 42 | this.attach(); 43 | } 44 | ZoomTool.parseData = function (container) { 45 | var data = {}; 46 | for (var i = 0; i < container.childNodes.length; i++) { 47 | if (container.childNodes[i].className == 'data') { 48 | var dl = container.childNodes[i]; 49 | for (var n = 0; n < dl.childNodes.length; n = n + 2) { 50 | var val = dl.childNodes[n + 1].innerHTML; 51 | if (!isNaN(val)) val = val / 1; 52 | if (val == "false") val = 0; 53 | data[dl.childNodes[n].innerHTML] = val; 54 | } 55 | break; 56 | } 57 | } 58 | /** 59 | * http://boxuk.unfuddle.com/projects/15733/tickets/by_number/6?cycle=true 60 | * 61 | * (kludgy) fix for: 62 | * 63 | * "If you zoom in then switch to full screen it centres the full screen 64 | * image on the top left point you had on the painting page, which is OK 65 | * but centre to centre would make more sense." 66 | * 67 | * @todo tidy this up - remove magic numbers 68 | */ 69 | if (data.x != 0 && data.y != 0) { 70 | data.x -= (document.documentElement.clientWidth / data.levelStep) - 240; 71 | data.y -= (document.documentElement.clientHeight / data.levelStep) - 232; 72 | } 73 | return data; 74 | }; 75 | ZoomTool.prototype = { 76 | attach: function () { 77 | if (this.ie) { 78 | document.body.ondragstart = function () { 79 | return false; 80 | }; 81 | } 82 | var divs = this.container.getElementsByTagName('DIV'); 83 | for (var i = 0; i < divs.length; i++) { 84 | var child = divs[i]; 85 | var childClass = child.className.substr(0, child.className.indexOf(' ') != -1 ? child.className.indexOf(' ') : child.className.length); 86 | switch (childClass) { 87 | case 'map': 88 | this.setMap(child); 89 | break; 90 | case 'tiles': 91 | this.tiles = child; 92 | break; 93 | case 'viewport': 94 | this.viewport = child; 95 | break; 96 | case 'main': 97 | this.main = child; 98 | break; 99 | case 'controls': 100 | this.setControls(child); 101 | break; 102 | case 'footer': 103 | this.footer = child; 104 | break; 105 | } 106 | } 107 | this.mark = { 108 | x: this.data.x, 109 | y: this.data.y 110 | }; 111 | for (var t = 0; t < this.tiles.childNodes.length; t++) { 112 | var tile = this.tiles.childNodes[t]; 113 | var tileId = tile.src.substr(tile.src.indexOf('id=') + 3); 114 | tile.tileId = tileId; 115 | tile.zoom = this.data.zoom; 116 | tile.tileLoaded = true; 117 | tile.self = this; 118 | if (this.ie) { 119 | tile.oncontextmenu = function () { 120 | return false; 121 | }; 122 | tile.onmousedown = this.onMouseDown; 123 | tile.ondblclick = this.onMouseDoubleClick; 124 | if (this.grabCursor) 125 | tile.style.cursor = this.grabCursor; 126 | tile.unselectable = 'on'; 127 | } 128 | this.cache[tileId] = tile; 129 | } 130 | this.viewport.self = this; 131 | this.viewport.onmousedown = this.onMouseDown; 132 | this.viewport.onmouseup = this.viewport.onmouseout = this.onMouseUp; 133 | if (window.addEventListener) { 134 | window.addEventListener('DOMMouseScroll', ZoomTool.onMouseWheel, false); 135 | } else { 136 | window.onmousewheel = document.onmousewheel = ZoomTool.onMouseWheel; 137 | } 138 | if (this.ie) { 139 | document.attachEvent('onkeypress', ZoomTool.onKeyPress); 140 | document.attachEvent('onkeydown', ZoomTool.onKeyDown); 141 | } else { 142 | window.onkeypress = ZoomTool.onKeyPress; 143 | window.onkeydown = ZoomTool.onKeyDown; 144 | } 145 | if (this.grabCursor) 146 | this.viewport.style.cursor = this.grabCursor; 147 | this.setArrowAmount(); 148 | g_aZoomTool[g_aZoomTool.length] = this; 149 | this.updateMap(); 150 | this.updateControls(); 151 | /** 152 | * To get around the problem of National Gallery using their own images 153 | * rather than the automatically generated ones they should be using, 154 | * grab hold of the original image. When the user zooms in and out we'll either 155 | * 1) Use this image when the zoom level is 1, or 156 | * 2) Use the automatically generated images when the zoom level is greater than 1 157 | * Doesn't apply in full screen. 158 | * 159 | * See this.getFallbackImage() and this.positionTiles() 160 | */ 161 | if (this.data.fullscreen) { 162 | this.setupFullscreen(); 163 | } else { 164 | var p_oThis = this; 165 | $(document).ready( 166 | function () { 167 | p_oThis.fallbackImage = p_oThis.getFallbackImage('.tiles > .tile'); 168 | /** 169 | * Replace the original image with the manipulated copy 170 | * (manipulating the original image itself is buggy) 171 | */ 172 | /*$('.tiles').empty().append( 173 | p_oThis.fallbackImage 174 | );*/ 175 | } 176 | ); 177 | } 178 | }, 179 | setArrowAmount: function () { 180 | this.arrowAmount = { 181 | x: this.viewport.clientWidth / 6, 182 | y: this.viewport.clientHeight / 6 183 | }; 184 | }, 185 | setControls: function (controls) { 186 | if (controls.className.indexOf('controlsRight') != -1) 187 | this.controlsRight = controls; 188 | else 189 | this.controlsBottom = controls; 190 | var links = controls.getElementsByTagName('A'); 191 | for (var n = 0; n < links.length; n++) { 192 | var link = links[n]; 193 | var self = this; 194 | this.links[link.className] = link; 195 | switch (link.className) { 196 | case 'zoomIn': 197 | link.onclick = function (e) { 198 | self.zoom(+1); 199 | var evt = e || window.event; 200 | if (evt.preventDefault) { 201 | evt.preventDefault(); 202 | } else { 203 | evt.returnValue = false; 204 | evt.cancelBubble = true; 205 | } 206 | }; 207 | break; 208 | case 'zoomOut': 209 | link.onclick = function (e) { 210 | self.zoom(-1); 211 | var evt = e || window.event; 212 | if (evt.preventDefault) { 213 | evt.preventDefault(); 214 | } else { 215 | evt.returnValue = false; 216 | evt.cancelBubble = true; 217 | } 218 | }; 219 | break; 220 | case 'moveRight': 221 | link.onclick = function (e) { 222 | self.move('right'); 223 | var evt = e || window.event; 224 | if (evt.preventDefault) { 225 | evt.preventDefault(); 226 | } else { 227 | evt.returnValue = false; 228 | evt.cancelBubble = true; 229 | } 230 | }; 231 | break; 232 | case 'moveUp': 233 | link.onclick = function (e) { 234 | self.move('up'); 235 | var evt = e || window.event; 236 | if (evt.preventDefault) { 237 | evt.preventDefault(); 238 | } else { 239 | evt.returnValue = false; 240 | evt.cancelBubble = true; 241 | } 242 | }; 243 | break; 244 | case 'moveDown': 245 | link.onclick = function (e) { 246 | self.move('down'); 247 | e.preventDefault(); 248 | }; 249 | break; 250 | case 'moveLeft': 251 | link.onclick = function (e) { 252 | self.move('left'); 253 | var evt = e || window.event; 254 | if (evt.preventDefault) { 255 | evt.preventDefault(); 256 | } else { 257 | evt.returnValue = false; 258 | evt.cancelBubble = true; 259 | } 260 | }; 261 | break; 262 | case 'reset': 263 | link.onclick = function (e) { 264 | self.reset(); 265 | var evt = e || window.event; 266 | if (evt.preventDefault) { 267 | evt.preventDefault(); 268 | } else { 269 | evt.returnValue = false; 270 | evt.cancelBubble = true; 271 | } 272 | }; 273 | break; 274 | case 'fullscreen': 275 | link.onclick = function (e) { 276 | self.fullscreen(); 277 | var evt = e || window.event; 278 | if (evt.preventDefault) { 279 | evt.preventDefault(); 280 | } else { 281 | evt.returnValue = false; 282 | evt.cancelBubble = true; 283 | } 284 | }; 285 | break; 286 | } 287 | } 288 | var divs = controls.getElementsByTagName('DIV'); 289 | var numDivs = divs.length; 290 | for (var n = 0; n < numDivs; n++) { 291 | if (divs[n].className == 'slider') { 292 | this.setSlider(divs[n]); /* break; */ 293 | } else if (divs[n].className == 'sliderBounds') { 294 | this.setSliderBounds(divs[n]); 295 | } 296 | } 297 | }, 298 | setMap: function (map) { 299 | this.map = map; 300 | this.map.self = this; 301 | this.map.onmousedown = this.onMapMouseDown; 302 | this.area = map.getElementsByTagName('DIV')[0].getElementsByTagName('DIV')[0]; 303 | this.area.self = this; 304 | }, 305 | setSliderBounds: function (sliderBounds) { 306 | var me = this; 307 | sliderBounds.onmousedown = function (e) { 308 | e = e ? e : window.event; 309 | pos = me.getCoords(e, sliderBounds.parentNode); 310 | if (!pos.y || pos.y < 0) 311 | return false; 312 | var slideMax = me.data.viewportCY - me.data.slideMargin; 313 | var slideStep = slideMax / me.data.max; 314 | var slidePos = Math.floor(me.data.max - (pos.y / slideStep) + 1); 315 | if (slidePos != me.data.zoom) { 316 | me.zoom(slidePos > me.data.zoom ? 1 : -1); 317 | } 318 | return false; 319 | }; 320 | }, 321 | setSlider: function (slider) { 322 | this.slider = slider; 323 | var me = this; 324 | slider.onmousedown = function (e) { 325 | e = e ? e : window.event; 326 | slider.dragging = true; 327 | slider.parentNode.style.cursor = 'pointer'; 328 | slider.parentNode.onmousemove = function (e) { 329 | e = e ? e : window.event; 330 | pos = me.getCoords(e, slider.parentNode); 331 | if (!pos.y || pos.y < 0) 332 | return false; 333 | var offsetHeight = slider.getElementsByTagName('IMG')[0].offsetHeight; 334 | if (pos.y > (slider.parentNode.offsetHeight - offsetHeight)) 335 | pos.y = (slider.parentNode.offsetHeight - offsetHeight); 336 | me.setSliderPadding(pos.y); 337 | var slideMax = me.data.viewportCY - me.data.slideMargin; 338 | var slideStep = slideMax / me.data.max; 339 | var slidePos = Math.floor(me.data.max - (pos.y / slideStep) + 1); 340 | if (slidePos != me.data.zoom) { 341 | me.zoom(slidePos > me.data.zoom ? 1 : -1); 342 | } 343 | return false; 344 | }; 345 | slider.parentNode.onmouseup = function (e) { 346 | me.stopSliderDragging(); 347 | }; 348 | document.onmouseup = function (e) { 349 | me.stopSliderDragging(); 350 | }; 351 | return false; 352 | }; 353 | }, 354 | stopSliderDragging: function () { 355 | this.slider.parentNode.onmousemove = function () {}; 356 | this.slider.parentNode.onmouseup = function () {}; 357 | this.slider.dragging = false; 358 | this.slider.parentNode.style.cursor = 'default'; 359 | document.onmouseup = function () {}; 360 | return false; 361 | }, 362 | setupFullscreen: function () { 363 | this.footer.style.position = 'absolute'; 364 | this.footer.style.positionTop = '-1000px'; 365 | this.footer.style.display = 'block'; 366 | this.footerHeight = this.footer.offsetHeight; 367 | this.footer.style.display = 'none'; 368 | this.data.slideMargin += 30; 369 | this.resize(); 370 | this.main.style.display = 'block'; 371 | this.controlsRight.style.display = 'block'; 372 | this.data.x += (this.data.viewportCX / this.data.levelStep); 373 | this.data.y += (this.data.viewportCY / this.data.levelStep); 374 | this.positionTiles({ 375 | x: this.data.x, 376 | y: this.data.y 377 | }, true, true, false); 378 | var self = this; 379 | window.onresize = function () { 380 | self.resize(); 381 | self.positionTiles({ 382 | x: self.data.x, 383 | y: self.data.y 384 | }, true, true, false); 385 | }; 386 | }, 387 | resize: function () { 388 | var clientHeight = (document.documentElement.clientHeight - (17 * 2) - this.footerHeight); 389 | var clientWidth = document.documentElement.lastChild.clientWidth; 390 | this.data.viewportCX = clientWidth; 391 | this.data.viewportCY = clientHeight; 392 | this.main.parentNode.style.height = clientHeight + 'px'; 393 | this.main.style.height = clientHeight + 'px'; 394 | this.controlsRight.style.height = clientHeight + 'px'; 395 | this.slider.parentNode.style.height = (clientHeight - this.data.slideMargin) + 'px'; 396 | this.main.parentNode.style.width = clientWidth + 'px'; 397 | this.main.style.width = (clientWidth - 36) + 'px'; 398 | this.footer.style.positionBottom = '17px'; 399 | this.footer.style.positionLeft = '17px'; 400 | this.footer.style.display = 'block'; 401 | this.footer.style.width = (clientWidth - 5 - 165) + 'px'; 402 | }, 403 | getCoords: function (e, relativeTo) { 404 | var offset = { 405 | left: 0, 406 | top: 0 407 | }; 408 | for (var parentNode = relativeTo; parentNode; parentNode = parentNode.offsetParent) { 409 | offset.top += parentNode.offsetTop; 410 | offset.left += parentNode.offsetLeft; 411 | } 412 | return { 413 | 'x': (e.pageX || (e.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft))) - offset.left, 414 | 'y': (e.pageY || (e.clientY + (document.documentElement.scrollTop || document.body.scrollTop))) - offset.top 415 | }; 416 | }, 417 | getDimensions: function () { 418 | var dimensions = this.dimensionCache[this.data.zoom]; 419 | if (!dimensions) { 420 | var width = (this.data.nw ? this.data.nw : this.data.width / Math.pow(this.data.levelStep, this.data.max - this.data.zoom)); 421 | var height = (this.data.nh ? this.data.nh : this.data.height / Math.pow(this.data.levelStep, this.data.max - this.data.zoom)); 422 | dimensions = this.dimensionCache[this.data.zoom] = { 423 | width: width, 424 | height: height, 425 | rows: Math.ceil(height / this.data.tileSize), 426 | cols: Math.ceil(width / this.data.tileSize) 427 | }; 428 | } 429 | return dimensions; 430 | }, 431 | reset: function () { 432 | if (window.stop) 433 | window.stop(); 434 | this.draw = false; 435 | this.tiles.innerHTML = ''; 436 | this.cache = {}; 437 | while (this.data.zoom > 1) { 438 | this.zoom(-1); 439 | } 440 | this.draw = true; 441 | this.clear(); 442 | this.positionTiles({ 443 | x: this.data.x, 444 | y: this.data.y 445 | }, true, true, false); 446 | return false; 447 | }, 448 | zoom: function (direction) { 449 | var newLevel = this.data.zoom + direction; 450 | if (newLevel < 1 || newLevel > this.data.max) { 451 | return false; 452 | } 453 | var dimensions = this.getDimensions(); 454 | var multiplier; 455 | if (direction == 1) { 456 | multiplier = this.data.levelStep; 457 | } else { 458 | multiplier = 1 / this.data.levelStep; 459 | } 460 | var coords = { 461 | 'x': Math.floor(this.viewport.offsetWidth / 2), 462 | 'y': Math.floor(this.viewport.offsetHeight / 2) 463 | }; 464 | var offset = { 465 | 'x': coords.x - this.data.x, 466 | 'y': coords.y - this.data.y 467 | }; 468 | var newOffset = { 469 | 'x': Math.floor(offset.x * multiplier), 470 | 'y': Math.floor(offset.y * multiplier) 471 | }; 472 | this.data.x = coords.x - newOffset.x; 473 | this.data.y = coords.y - newOffset.y; 474 | this.previousZoom = this.data.zoom; 475 | this.data.zoom = newLevel; 476 | this.data.nw = (this.data.width / Math.pow(this.data.levelStep, this.data.max)) * (Math.pow(this.data.levelStep, this.data.zoom)); 477 | this.data.nh = (this.data.height / Math.pow(this.data.levelStep, this.data.max)) * (Math.pow(this.data.levelStep, this.data.zoom)); 478 | this.clear(); 479 | this.positionTiles({ 480 | x: this.data.x, 481 | y: this.data.y 482 | }, true, true, true); 483 | return false; 484 | }, 485 | updateControls: function () { 486 | if (this.slider && !this.slider.dragging) { 487 | var slideMax = this.data.viewportCY - this.data.slideMargin; 488 | var slideOffset = (slideMax - this.slider.childNodes[0].offsetHeight) - ((slideMax - this.slider.childNodes[0].offsetHeight) / (this.data.max - 1) * (this.data.zoom - 1)); 489 | this.setSliderPadding(Math.round(slideOffset)); 490 | } 491 | if (this.links['zoomIn']) 492 | this.links['zoomIn'].className = 'zoomIn' + (this.data.zoom + 1 > this.data.max ? ' disabled' : ''); 493 | if (this.links['zoomOut']) 494 | this.links['zoomOut'].className = 'zoomOut' + (this.data.zoom - 1 < 1 ? ' disabled' : ''); 495 | var dimensions = this.getDimensions(); 496 | if (this.links['moveLeft']) 497 | this.links['moveLeft'].className = 'moveLeft' + (this.data.x >= 0 ? ' disabled' : ''); 498 | if (this.links['moveUp']) 499 | this.links['moveUp'].className = 'moveUp' + (this.data.y >= 0 ? ' disabled' : ''); 500 | if (this.links['moveRight']) 501 | this.links['moveRight'].className = 'moveRight' + (this.data.x <= -(dimensions.width - this.viewport.offsetWidth) ? ' disabled' : ''); 502 | if (this.links['moveDown']) 503 | this.links['moveDown'].className = 'moveDown' + (this.data.y <= -(dimensions.height - this.viewport.offsetHeight) ? ' disabled' : ''); 504 | }, 505 | checkBounds: function (coords, dimensions) { 506 | if (coords.y > 0) { 507 | if (dimensions.height < this.viewport.offsetHeight) 508 | coords.y = (this.viewport.offsetHeight - dimensions.height) / 2; 509 | else 510 | coords.y = 0; 511 | } else if (coords.y < -(dimensions.height - this.viewport.offsetHeight)) { 512 | if (dimensions.height < this.viewport.offsetHeight) 513 | coords.y = (this.viewport.offsetHeight - dimensions.height) / 2; 514 | else 515 | coords.y = -(dimensions.height - this.viewport.offsetHeight); 516 | } 517 | if (coords.x > 0) { 518 | if (dimensions.width < this.viewport.offsetWidth) 519 | coords.x = (this.viewport.offsetWidth - dimensions.width) / 2; 520 | else 521 | coords.x = 0; 522 | } else if (coords.x < -(dimensions.width - this.viewport.offsetWidth)) { 523 | if (dimensions.width < this.viewport.offsetWidth) 524 | coords.x = (this.viewport.offsetWidth - dimensions.width) / 2; 525 | else 526 | coords.x = -(dimensions.width - this.viewport.offsetWidth); 527 | } 528 | return coords; 529 | }, 530 | getFallbackImage: function (p_selector) { 531 | var originalImage = $(p_selector); 532 | $imageHeight = originalImage.height(); 533 | $imageWidth = originalImage.width(); 534 | $viewportHeight = this.viewport.clientHeight; 535 | $viewportWidth = this.viewport.clientWidth; 536 | if ($imageHeight > $imageWidth) { 537 | $scaleFactor = $viewportHeight / $imageHeight; 538 | } else { 539 | $scaleFactor = $viewportHeight / $imageHeight; 540 | if ($imageWidth * $scaleFactor > $viewportWidth) { 541 | $scaleFactor = $viewportWidth / $imageWidth; 542 | } 543 | } 544 | $imageWidth = $imageWidth * $scaleFactor; 545 | $imageHeight = $imageHeight * $scaleFactor; 546 | $offsetLeft = ($viewportWidth - $imageWidth) / 2; 547 | $offsetTop = ($viewportHeight - $imageHeight) / 2; 548 | fallbackImage = originalImage.clone(); 549 | fallbackImage.css({ 550 | 'margin': '0px', 551 | 'padding': '0px', 552 | 'top': $offsetTop + 'px', 553 | 'left': $offsetLeft + 'px', 554 | 'width': $imageWidth + 'px', 555 | 'height': $imageHeight + 'px' 556 | }); 557 | return fallbackImage; 558 | }, 559 | positionTiles: function (coords, checkBounds, addMissing, forceFade) { 560 | var dimensions = this.getDimensions(); 561 | if (checkBounds) { 562 | coords = this.checkBounds(coords, dimensions); 563 | } 564 | var tileNum = 0; 565 | var rows = dimensions.rows; 566 | var cols = dimensions.cols; 567 | var loadTiles = []; 568 | var loadTile = 0; 569 | for (var c = 0; c < cols; c++) { 570 | for (var r = 0; r < rows; r++) { 571 | var tx = Math.floor(coords.x + (c * this.data.tileSize)); 572 | var ty = Math.floor(coords.y + (r * this.data.tileSize)); 573 | var visible = ( 574 | tx + this.data.tileSize > 0 && tx < this.viewport.offsetWidth && 575 | ty + this.data.tileSize > 0 && ty < this.viewport.offsetHeight 576 | ); 577 | var preload = ( 578 | tx + this.data.tileSize > -this.viewport.offsetWidth && 579 | tx + this.viewport.offsetWidth < this.viewport.offsetWidth && 580 | ty + this.data.tileSize > -this.viewport.offsetHeight && 581 | ty + this.viewport.offsetHeight < this.viewport.offsetHeight 582 | ); 583 | var tileId = this.getTileId(this.data.contentId, this.data.zoom, r, c); 584 | var tile = this.cache[tileId]; 585 | if (!visible) { 586 | if (tile) { 587 | for (var t = 0; t < this.tiles.childNodes.length; t++) { 588 | if (this.tiles.childNodes[t].tileId == tile.tileId) 589 | this.tiles.removeChild(tile); 590 | } 591 | } 592 | continue; 593 | } 594 | if (!tile && addMissing) { 595 | if (this.data.zoom == 1 && this.data.firstLevel == 'untiled' && (c > 0 || r > 0)) { 596 | continue; 597 | } 598 | tile = this.cache[tileId] = document.createElement('img'); 599 | tile.tileId = tileId; 600 | tile.className = 'tile'; 601 | tile.self = this; 602 | tile.zoom = this.data.zoom; 603 | tile.tiles = this.tiles; 604 | tile.alt = ''; 605 | if (this.draw && this.fadeTime) { 606 | this.startTileFade(tile); 607 | } 608 | var visibleX = this.data.tileSize; 609 | if (tx < 0) { 610 | visibleX += tx; 611 | } else if (tx + this.data.tileSize > this.viewport.offsetWidth) { 612 | visibleX -= (tx + this.data.tileSize) - this.viewport.offsetWidth; 613 | } 614 | var visibleY = this.data.tileSize; 615 | if (ty < 0) { 616 | visibleY += ty; 617 | } else if (ty + this.data.tileSize > this.viewport.offsetHeight) { 618 | visibleY -= (ty + this.data.tileSize) - this.viewport.offsetHeight; 619 | } 620 | var visiblePixels = visibleX * visibleY; 621 | loadTiles[loadTile++] = [tile, tileId, visiblePixels]; 622 | if (this.ie) { 623 | tile.onmousedown = this.onMouseDown; 624 | tile.ondblclick = this.onMouseDoubleClick; 625 | tile.oncontextmenu = function () { 626 | return false; 627 | }; 628 | tile.unselectable = 'on'; 629 | } 630 | } else if (tile && forceFade && this.draw && this.fadeTime) { 631 | this.startTileFade(tile); 632 | } 633 | if (tile) { 634 | var present = false; 635 | var numTiles = this.tiles.childNodes.length; 636 | for (var t = 0; t < numTiles; t++) { 637 | if (this.tiles.childNodes[t].tileId == tile.tileId) { 638 | present = true; 639 | break; 640 | } 641 | } 642 | if (!present && this.draw) 643 | this.tiles.appendChild(tile); 644 | tile.style.left = tx + 'px'; 645 | tile.style.top = ty + 'px'; 646 | } 647 | } 648 | } 649 | if (this.fallbackImage && this.data.zoom == 1) { 650 | $('.tiles').empty().append( 651 | this.fallbackImage 652 | ) 653 | } else { 654 | loadTiles.sort(this.sortTiles); 655 | var numLoadTiles = loadTiles.length; 656 | for (var t = 0; t < numLoadTiles; t++) { 657 | loadTiles[t][0].src = this.tilePath + loadTiles[t][1]; 658 | } 659 | } 660 | this.data.x = coords.x; 661 | this.data.y = coords.y; 662 | this.updateMap(); 663 | this.updateControls(); 664 | return coords; 665 | }, 666 | sortTiles: function (a, b) { 667 | return a[2] == b[2] ? 0 : (a[2] > b[2] ? -1 : 1); 668 | }, 669 | startTileFade: function (tile) { 670 | this.setOpacity(tile, 1); 671 | if (tile.tileLoaded) { 672 | var thisTile = tile; 673 | tile.intervalId = window.setInterval(function () { 674 | thisTile.self.fade(thisTile.self, thisTile); 675 | }, tile.self.fadeTime); 676 | } else { 677 | tile.onload = function () { 678 | var thisTile = this; 679 | this.intervalId = window.setInterval(function () { 680 | thisTile.self.fade(thisTile.self, thisTile); 681 | }, this.self.fadeTime); 682 | this.onload = function () {}; 683 | this.tileLoaded = true; 684 | return true; 685 | }; 686 | } 687 | }, 688 | setOpacity: function (object, value) { 689 | if (value < 1 || value > 10) value = 10; 690 | object.style.opacity = value / 10; 691 | if (this.ie) 692 | object.style.filter = 'alpha(opacity=' + (value * 10) + ')'; 693 | }, 694 | getOpacity: function (object) { 695 | var opacity = 10; 696 | if (object.style.opacity) { 697 | opacity = (object.style.opacity * 10); 698 | } else if (object.style.filter) { 699 | var filter = object.style.filter; 700 | var key = 'opacity='; 701 | var value = filter.substring(filter.indexOf(key) + key.length); 702 | value = value.substring(0, value.indexOf(')')); 703 | if (value) { 704 | opacity = (value / 10); 705 | } 706 | } 707 | return opacity; 708 | }, 709 | fade: function (self, tile) { 710 | var opacity = self.getOpacity(tile); 711 | if (opacity < 10) { 712 | self.setOpacity(tile, opacity + 2); 713 | } else if (tile.intervalId) { 714 | window.clearInterval(tile.intervalId); 715 | } 716 | }, 717 | clear: function () { 718 | for (var i = 0; i < this.tiles.childNodes.length;) { 719 | var tile = this.tiles.childNodes[i]; 720 | if (tile.tagName == 'IMG') { 721 | if (tile.zoom != this.data.zoom) { 722 | this.tiles.removeChild(tile); 723 | continue; 724 | } 725 | } 726 | i++; 727 | } 728 | }, 729 | getTileId: function (contentId, zoom, r, c) { 730 | var key = contentId + ',' + zoom + ',' + r + ',' + c; 731 | var id = this.idCache[key]; 732 | if (!id) { 733 | id = this.idCache[key] = this.obfuscate( 734 | this.pad(r, 2) + 735 | this.pad(contentId, 5) + 736 | this.pad(c, 2) + 737 | this.pad(zoom, 2) 738 | ); 739 | } 740 | return id; 741 | }, 742 | pad: function (value, chars) { 743 | return ('00000' + value).substr(-chars); 744 | }, 745 | obfuscate: function (value) { 746 | var result = this.obfuscateCache[value]; 747 | if (!result) { 748 | var from = '0123456789'; 749 | var to = 'vRfOdXapKz'; 750 | var keylen = from.length; 751 | var valuelen = value.length; 752 | var result = ''; 753 | for (var i = 0; i < valuelen; i++) { 754 | for (var n = 0; n < keylen; n++) { 755 | if (value.charAt(i) == from.charAt(n)) { 756 | result = result + to.charAt(n); 757 | break;; 758 | } 759 | } 760 | } 761 | this.obfuscateCache[value] = result; 762 | } 763 | return result; 764 | }, 765 | onMouseDown: function (e) { 766 | e = e ? e : window.event; 767 | if (e.button > 1) return false; 768 | var self = this.self; 769 | if (!self) return false; 770 | var coords = self.getCoords(e, self.viewport); 771 | if ( 772 | coords.x < 0 || coords.x > self.viewport.offsetWidth || 773 | coords.y < 0 || coords.y > self.viewport.offsetHeight 774 | ) { 775 | e.cancelBubble = true; 776 | if (self.grabCursor) 777 | self.viewport.style.cursor = self.grabCursor; 778 | } else { 779 | self.pressed = true; 780 | if (self.ie && e.srcElement.tagName == 'IMG') { 781 | e.srcElement.onmousemove = (self.pressed ? self.onMouseMove : function () {}); 782 | e.srcElement.onmouseup = (self.pressed ? self.onMouseUp : function () {}); 783 | for (t = 0; t < self.tiles.childNodes.length; t++) { 784 | self.tiles.childNodes[t].onmousemove = (self.pressed ? self.onMouseMove : function () {}); 785 | self.tiles.childNodes[t].onmouseup = (self.pressed ? self.onMouseUp : function () {}); 786 | if (self.grabCursor) 787 | self.tiles.childNodes[t].style.cursor = self.grabCursor; 788 | self.tiles.unselectable = true; 789 | } 790 | } else { 791 | self.viewport.onmousemove = (self.pressed ? self.onMouseMove : function () {}); 792 | } 793 | self.mark = { 794 | x: coords.x - self.data.x, 795 | y: coords.y - self.data.y 796 | }; 797 | if (self.dragCursor) 798 | self.viewport.style.cursor = self.dragCursor; 799 | } 800 | return false; 801 | }, 802 | onMouseMove: function (e) { 803 | e = e ? e : window.event; 804 | var self = this.self; 805 | if (!self) return false; 806 | self.ticker++; 807 | if (self.ticker % 2 == 0) { 808 | var move = self.getCoords(e, self.viewport); 809 | if ( 810 | move.x < 0 || move.x > self.viewport.offsetWidth || 811 | move.y < 0 || move.y > self.viewport.offsetHeight 812 | ) { 813 | self.onMouseUp(e); 814 | /* 815 | http://boxuk.unfuddle.com/projects/15733/tickets/by_number/3?cycle=true 816 | If the user clicks in and then drags out of the thumbnail 817 | subsequent mouse moves over the thumbnail act as �drag� actions. 818 | (A mouse down and mouse up on the thumbnail fixes this so looks 819 | like a need to react to �mouse leave� as well as �mouse up�?) 820 | */ 821 | for (t = 0; t < self.tiles.childNodes.length; t++) { 822 | var tile = self.tiles.childNodes[t]; 823 | tile.onmousemove = function () {}; 824 | tile.onmouseup = function () {}; 825 | } 826 | self.viewport.onmousemove = function () {}; 827 | } 828 | var coords = { 829 | x: move.x - self.mark.x, 830 | y: move.y - self.mark.y 831 | }; 832 | self.positionTiles(coords, true, true, false); 833 | } 834 | return false; 835 | }, 836 | onMouseUp: function (e) { 837 | var self = this.self; 838 | if (!self) { 839 | return true; 840 | } 841 | if (self.pressed) { 842 | self.pressed = false; 843 | if (self.ie) { 844 | for (t = 0; t < self.tiles.childNodes.length; t++) { 845 | var tile = self.tiles.childNodes[t]; 846 | tile.onmousemove = function () {}; 847 | tile.onmouseup = function () {}; 848 | } 849 | } 850 | self.viewport.onmousemove = function () {}; 851 | if (self.grabCursor) 852 | self.viewport.style.cursor = self.grabCursor; 853 | } 854 | return false; 855 | }, 856 | onMouseDoubleClick: function (e) { 857 | e = e ? e : window.event; 858 | var self = this.self; 859 | if (!self) 860 | return; 861 | if (self.data.zoom == self.data.max) 862 | return; 863 | var target = self.getTarget(e); 864 | if (!(target.className == 'tile' || target.className == 'viewport')) { 865 | return; 866 | } 867 | var point = self.getCoords(e, self.viewport); 868 | var coords = { 869 | x: (self.viewport.offsetWidth / 2) - (point.x - self.data.x), 870 | y: (self.viewport.offsetHeight / 2) - (point.y - self.data.y) 871 | }; 872 | self.positionTiles(coords, false, true, false); 873 | self.zoom(1); 874 | }, 875 | getTarget: function (e) { 876 | var target = null; 877 | if (e.target) 878 | target = e.target; 879 | else if (e.srcElement) 880 | target = e.srcElement; 881 | if (target.nodeType == 3) 882 | target = target.parentNode; 883 | return target; 884 | }, 885 | move: function (direction, amount) { 886 | if (this.data.zoom == 1) 887 | return false; 888 | var coords; 889 | var prevCoords = { 890 | x: this.data.x, 891 | y: this.data.y 892 | }; 893 | if (!amount) { 894 | this.setArrowAmount(); 895 | if (direction == 'left' || direction == 'right') 896 | amount = this.arrowAmount.x; 897 | else 898 | amount = this.arrowAmount.y; 899 | } 900 | switch (direction) { 901 | case 'left': 902 | coords = { 903 | x: this.data.x + (amount), 904 | y: this.data.y 905 | }; 906 | break; 907 | case 'right': 908 | coords = { 909 | x: this.data.x - (amount), 910 | y: this.data.y 911 | }; 912 | break; 913 | case 'up': 914 | coords = { 915 | x: this.data.x, 916 | y: this.data.y + (amount) 917 | }; 918 | break; 919 | case 'down': 920 | coords = { 921 | x: this.data.x, 922 | y: this.data.y - (amount) 923 | }; 924 | break; 925 | } 926 | var newCoords = this.positionTiles(coords, true, true, false); 927 | return prevCoords.x != newCoords.x || prevCoords.y != newCoords.y; 928 | }, 929 | handleKey: function (keyCode) { 930 | if (keyCode == 38) return this.move('up'); 931 | if (keyCode == 39) return this.move('right'); 932 | if (keyCode == 40) return this.move('down'); 933 | if (keyCode == 37) return this.move('left'); 934 | if (keyCode == 107) return this.zoom(+1); 935 | if (!this.ie && (keyCode == 61)) return this.zoom(+1); 936 | if (this.ie && (keyCode == 187)) return this.zoom(+1); 937 | if (keyCode == 109 || keyCode == 189) return this.zoom(-1); 938 | /* 939 | if (keyCode == 109) return this.zoom(-1); 940 | if (this.ie && (keyCode == 95 || keyCode == 43)) { return false; } 941 | if (keyCode == 45 || keyCode == 95) 942 | { 943 | return this.zoom(-1); 944 | } 945 | if (keyCode == 43) 946 | { 947 | return this.zoom(1); 948 | } 949 | */ 950 | return false; 951 | }, 952 | updateMap: function () { 953 | if (!this.area) 954 | return; 955 | if (this.data.zoom == 1) { 956 | this.area.style.display = 'none'; 957 | return; 958 | } else { 959 | this.area.style.display = 'block'; 960 | } 961 | var dimensions = this.getDimensions(); 962 | var box = { 963 | left: Math.floor((this.map.offsetWidth / dimensions.width) * -this.data.x), 964 | top: Math.floor((this.map.offsetHeight / dimensions.height) * -this.data.y), 965 | width: Math.floor((this.map.offsetWidth / dimensions.width) * this.viewport.offsetWidth) - 1, 966 | height: Math.floor((this.map.offsetHeight / dimensions.height) * this.viewport.offsetHeight) - 1 967 | }; 968 | if (box.left < 0) box.left = 0; 969 | if (box.top < 0) box.top = 0; 970 | if (box.left + box.width > this.map.offsetWidth - 2) box.width = this.map.offsetWidth - box.left - 2; 971 | if (box.top + box.height > this.map.offsetHeight - 2) box.height = this.map.offsetHeight - box.top - 2; 972 | if (box.width < 0 || box.height < 0) { 973 | return; 974 | } 975 | this.area.style.left = box.left + 'px'; 976 | this.area.style.top = box.top + 'px'; 977 | this.area.style.width = box.width + 'px'; 978 | this.area.style.height = box.height + 'px'; 979 | }, 980 | onAreaMouseMove: function (e) { 981 | e = e ? e : window.event; 982 | var self = this.self; 983 | if (!self) return false; 984 | var move = self.getCoords(e, self.map); 985 | var pos = { 986 | x: move.x - (self.area.offsetWidth / 2), 987 | y: move.y - (self.area.offsetHeight / 2) 988 | }; 989 | var dimensions = self.getDimensions(); 990 | var coords = { 991 | x: (dimensions.width / self.map.offsetWidth) * -pos.x, 992 | y: (dimensions.height / self.map.offsetHeight) * -pos.y 993 | }; 994 | self.positionTiles(coords, true, true, false); 995 | return false; 996 | }, 997 | onMapMouseUp: function (e) { 998 | e = e ? e : window.event; 999 | var self = this.self; 1000 | if (!self) return false; 1001 | self.map.onmousemove = function () {}; 1002 | self.map.onmouseout = function () {}; 1003 | self.map.onmouseup = function () {}; 1004 | return false; 1005 | }, 1006 | onMapMouseOut: function (e) { 1007 | e = e ? e : window.event; 1008 | e.cancelBubble = true; 1009 | if (e.stopPropagation) e.stopPropagation(); 1010 | var self = this.self; 1011 | if (!self) return false; 1012 | if (ZoomTool.checkMouseLeave(this, e)) { 1013 | self.map.onmousemove = function () {}; 1014 | self.map.onmouseout = function () {}; 1015 | self.map.onmouseup = function () {}; 1016 | } 1017 | return false; 1018 | }, 1019 | onMapMouseDown: function (e) { 1020 | e = e ? e : window.event; 1021 | if (e.button > 1) return false; 1022 | var self = this.self; 1023 | if (!self || self.data.zoom == 1) 1024 | return false; 1025 | var move = self.getCoords(e, self.map); 1026 | var pos = { 1027 | x: move.x - (self.area.offsetWidth / 2), 1028 | y: move.y - (self.area.offsetHeight / 2) 1029 | }; 1030 | var dimensions = self.getDimensions(); 1031 | var coords = { 1032 | x: (dimensions.width / self.map.offsetWidth) * -pos.x, 1033 | y: (dimensions.height / self.map.offsetHeight) * -pos.y 1034 | }; 1035 | self.positionTiles(coords, true, true, false); 1036 | self.map.onmousemove = self.onAreaMouseMove; 1037 | self.map.onmouseout = self.onMapMouseOut; 1038 | self.map.onmouseup = self.onMapMouseUp; 1039 | return false; 1040 | }, 1041 | onMapMouseDoubleClick: function (e) { 1042 | e = e ? e : window.event; 1043 | var self = this.self; 1044 | if (!self) return false; 1045 | self.onMapMouseDown(e); 1046 | self.zoom(1); 1047 | return false; 1048 | }, 1049 | fullscreen: function (e) { 1050 | if (this.data.fullscreen) 1051 | return false; 1052 | var args = 'server.php?controller=content&contentType=ConMediaFile&contentId=' + this.data.contentId; 1053 | args += '&moduleId=ZoomTool'; 1054 | args += '&z=' + this.data.zoom; 1055 | args += '&x=' + this.data.x; 1056 | args += '&y=' + this.data.y; 1057 | var popup = 0; 1058 | if (window.open) { 1059 | popup = window.open( 1060 | (this.data.siteWebRoot != undefined ? this.data.siteWebRoot : "/") + args, 1061 | 'ZoomTool_' + this.data.contentId, 1062 | 'status=0,toolbar=0,location=0,menubar=0,resizable=1,scrollbars=0' 1063 | ); 1064 | popup.focus(); 1065 | if (this.ie) { 1066 | popup.moveTo(window.screenLeft + 10, window.screenTop + 10); 1067 | } else { 1068 | popup.moveTo(window.screenX + 10, window.screenY + 10); 1069 | } 1070 | popup.resizeTo(document.documentElement.clientWidth - 10, document.documentElement.clientHeight - 10); 1071 | } 1072 | if (!popup) { 1073 | alert('Popups are being blocked by your browser'); 1074 | } 1075 | return false; 1076 | }, 1077 | onMouseWheel: function (e) { 1078 | if (!( 1079 | (e.target && e.target == this.viewport) || 1080 | (e.srcElement && e.srcElement.className == 'tile') 1081 | )) 1082 | return true; 1083 | var direction = 0; 1084 | if (e.wheelDelta) { 1085 | direction = e.wheelDelta > 0 ? 1 : -1; 1086 | } else if (e.detail) { 1087 | direction = e.detail > 0 ? -1 : 1; 1088 | } 1089 | if (!direction) 1090 | return true; 1091 | if (e.preventDefault) 1092 | e.preventDefault(); 1093 | else 1094 | e.returnValue = false; 1095 | this.zoom(direction); 1096 | return false; 1097 | }, 1098 | setSliderPadding: function (p) { 1099 | if (this.totalSliderHeight == null || this.totalSliderHeight == 0) { 1100 | this.totalSliderHeight = document.getElementById('sliderAll').offsetHeight; 1101 | if (this.ie && this.ieVersion <= 6) { 1102 | this.totalSliderHeight -= 13; 1103 | } 1104 | if (this.totalSliderHeight == 0) { 1105 | return; 1106 | } 1107 | } 1108 | var sliderHeight = document.getElementById('sliderDraggable').offsetHeight; 1109 | document.getElementById('sliderPaddingTop').style.height = p + 'px'; 1110 | var sliderTopHeight = document.getElementById('sliderPaddingTop').offsetHeight; 1111 | var sliderBottomHeight = this.totalSliderHeight - sliderTopHeight - sliderHeight; 1112 | if (this.ie && this.ieVersion <= 6) {} 1113 | if (sliderBottomHeight < 0) { 1114 | sliderBottomHeight = 0; 1115 | } 1116 | document.getElementById('sliderPaddingBottom').style.height = sliderBottomHeight + "px"; 1117 | var sum = (document.getElementById('sliderDraggable').offsetHeight + document.getElementById('sliderPaddingTop').offsetHeight + document.getElementById('sliderPaddingBottom').offsetHeight); 1118 | if (sum > this.totalSliderHeight) { 1119 | var pxToMove = (sum - this.totalSliderHeight); 1120 | var curTopH = document.getElementById('sliderPaddingTop').offsetHeight; 1121 | var newH = (curTopH - pxToMove); 1122 | if (newH >= 0) { 1123 | document.getElementById('sliderPaddingTop').style.height = (curTopH - pxToMove) + "px"; 1124 | } 1125 | } 1126 | /* 1127 | window.status="TOT: " + this.totalSliderHeight 1128 | + " BOT: " + document.getElementById('sliderPaddingBottom').offsetHeight 1129 | + " TOP: " + document.getElementById('sliderPaddingTop').offsetHeight 1130 | + " SL: " + document.getElementById('sliderDraggable').offsetHeight 1131 | + " SUM: " + sum 1132 | + " p: " + p 1133 | ; 1134 | */ 1135 | } 1136 | }; 1137 | ZoomTool.onKeyPress = function (e) { 1138 | e = e ? e : window.event; 1139 | for (var i = 0; i < g_aZoomTool.length; i++) { 1140 | if (g_aZoomTool[i].handleKey(e.keyCode)) 1141 | return false; 1142 | } 1143 | return true; 1144 | }; 1145 | ZoomTool.onKeyDown = function (e) { 1146 | e = e ? e : window.event; 1147 | for (var i = 0; i < g_aZoomTool.length; i++) { 1148 | if (g_aZoomTool[i].handleKey(e.keyCode)) 1149 | return false; 1150 | } 1151 | return true; 1152 | }; 1153 | ZoomTool.onMouseWheel = function (e) { 1154 | e = e ? e : window.event; 1155 | if (g_aZoomTool[0]) 1156 | return g_aZoomTool[0].onMouseWheel(e); 1157 | return true; 1158 | }; 1159 | ZoomTool.containsDOM = function (container, containee) { 1160 | var isParent = false; 1161 | do { 1162 | if ((isParent = container == containee)) 1163 | break; 1164 | containee = containee.parentNode; 1165 | } 1166 | while (containee != null); 1167 | return isParent; 1168 | }; 1169 | ZoomTool.checkMouseLeave = function (element, evt) { 1170 | if (element.contains && evt.toElement) { 1171 | return !element.contains(evt.toElement); 1172 | } else if (evt.relatedTarget) { 1173 | return !ZoomTool.containsDOM(element, evt.relatedTarget); 1174 | } else return false; 1175 | }; 1176 | --------------------------------------------------------------------------------