Added simple ant build. Broke main file into composite parts in src directory. Concatenated release is still included in the project as a release artifact.

This commit is contained in:
thatcher 2011-12-05 22:50:25 -05:00
parent deaabc6a91
commit 916ada5f02
34 changed files with 8943 additions and 4250 deletions

8
README
View File

@ -4,9 +4,15 @@ This project is a fork of the OpenSeadragon project at http://openseadragon.code
We are forking it primarily to do heavy clean-up on the code base, simplify the build, and see what happens from there.
== Building from Source
Building from source is easy with 'ant'. The result is included in the
distribution as 'openseadragon.js', the version being recording in the header.
> ant
== Original license preserved below
Original license preserved below
-------------------------------------
License: New BSD License (BSD)
Copyright (c) 2010, OpenSeadragon

8
build.properties Normal file
View File

@ -0,0 +1,8 @@
#OpenSeadragon build.properties
PROJECT: openseadragon
BUILD_MAJOR: 0
BUILD_MINOR: 8
BUILD_ID: 0
BUILD: ${PROJECT}.${BUILD_MAJOR}.${BUILD_MINOR}.${BUILD_ID}
VERSION: ${BUILD_MAJOR}.${BUILD_MINOR}.${BUILD_ID}

60
build.xml Normal file
View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Version: $Id: build.xml 495 2011-07-07 20:14:37Z chth $ -->
<project name="openseadragon"
default="build">
<tstamp/>
<description>
Build file for Zoomable User Interface HTML5 project OpenSeadragon.
</description>
<!-- BASIC PROJECT PROPERTIES TO HELP TRACK VERSION -->
<property file="build.properties" />
<target name="build">
<echo>| OpenSeadragon </echo>
<filelist id="openseadragon.javascript"
dir=".">
<file name="src/openseadragon.js" />
<file name="src/eventhandlerlist.js" />
<file name="src/utils.js" />
<file name="src/mousetracker.js" />
<file name="src/controlanchor.js" />
<file name="src/overlayplacement.js" />
<file name="src/navcontrol.js" />
<file name="src/control.js" />
<file name="src/viewer.js" />
<file name="src/strings.js" />
<file name="src/point.js" />
<file name="src/profiler.js" />
<file name="src/job.js" />
<file name="src/imageloader.js" />
<file name="src/tilesource.js" />
<file name="src/dzierror.js" />
<file name="src/dzitilesource.js" />
<file name="src/button.js" />
<file name="src/buttongroup.js" />
<file name="src/config.js" />
<file name="src/rectangle.js" />
<file name="src/displayrectangle.js" />
<file name="src/spring.js" />
<file name="src/tile.js" />
<file name="src/overlay.js" />
<file name="src/drawer.js" />
<file name="src/viewport.js" />
</filelist>
<echo>| Building OpenSeadragon Javascript </echo>
<concat destFile="openseadragon.js">
<filterchain>
<replacetokens>
<token key="VERSION" value="${VERSION}"/>
</replacetokens>
</filterchain>
<filelist refid="openseadragon.javascript" />
</concat>
</target>
</project>

View File

@ -1,4 +1,5 @@
This license is preserved from the OpenSeadragon project at http://openseadragon.codeplex.com/ as per the text below.
This license is preserved from the OpenSeadragon project at
http://openseadragon.codeplex.com/ as per the text below.
-------------------------------------
@ -6,14 +7,30 @@ License: New BSD License (BSD)
Copyright (c) 2010, OpenSeadragon
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of OpenSeadragon nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
* Neither the name of OpenSeadragon nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
-------------------------------------

8659
OpenSeadragon.debug.js → openseadragon.js Executable file → Normal file

File diff suppressed because it is too large Load Diff

6
src/browser.js Normal file
View File

@ -0,0 +1,6 @@
(function( $ ){
}( OpenSeadragon ));

269
src/button.js Normal file
View File

@ -0,0 +1,269 @@
(function( $ ){
$.ButtonState = {
REST: 0,
GROUP: 1,
HOVER: 2,
DOWN: 3
};
$.Button = function( properties, events ) {
this._tooltip = properties.tooltip;
this._srcRest = properties.srcRest;
this._srcGroup = properties.srcGroup;
this._srcHover = properties.srcHover;
this._srcDown = properties.srcDown;
this._button = properties.button;
this.config = properties.config;
this.initialize( events );
};
$.Button.prototype = {
initialize: function( events ) {
this._events = new $.EventHandlerList();
if (events.onPress != undefined)
this.add_onPress(events.onPress);
if (events.onRelease != undefined)
this.add_onRelease(events.onRelease);
if (events.onClick != undefined)
this.add_onClick(events.onClick);
if (events.onEnter != undefined)
this.add_onEnter(events.onEnter);
if (events.onExit != undefined)
this.add_onExit(events.onExit);
this._button = $.Utils.makeNeutralElement("span");
this._currentState = $.ButtonState.GROUP;
this._tracker = new $.MouseTracker(this._button, this.config.clickTimeThreshold, this.config.clickDistThreshold);
this._imgRest = $.Utils.makeTransparentImage(this._srcRest);
this._imgGroup = $.Utils.makeTransparentImage(this._srcGroup);
this._imgHover = $.Utils.makeTransparentImage(this._srcHover);
this._imgDown = $.Utils.makeTransparentImage(this._srcDown);
this._fadeDelay = 0; // begin fading immediately
this._fadeLength = 2000; // fade over a period of 2 seconds
this._fadeBeginTime = null;
this._shouldFade = false;
this._button.style.display = "inline-block";
this._button.style.position = "relative";
this._button.title = this._tooltip;
this._button.appendChild(this._imgRest);
this._button.appendChild(this._imgGroup);
this._button.appendChild(this._imgHover);
this._button.appendChild(this._imgDown);
var styleRest = this._imgRest.style;
var styleGroup = this._imgGroup.style;
var styleHover = this._imgHover.style;
var styleDown = this._imgDown.style;
styleGroup.position = styleHover.position = styleDown.position = "absolute";
styleGroup.top = styleHover.top = styleDown.top = "0px";
styleGroup.left = styleHover.left = styleDown.left = "0px";
styleHover.visibility = styleDown.visibility = "hidden";
if ($.Utils.getBrowser() == $.Browser.FIREFOX &&
$.Utils.getBrowserVersion() < 3) {
styleGroup.top = styleHover.top = styleDown.top = "";
}
this._tracker.enterHandler = $.delegate(this, this._enterHandler);
this._tracker.exitHandler = $.delegate(this, this._exitHandler);
this._tracker.pressHandler = $.delegate(this, this._pressHandler);
this._tracker.releaseHandler = $.delegate(this, this._releaseHandler);
this._tracker.clickHandler = $.delegate(this, this._clickHandler);
this._tracker.setTracking(true);
this._outTo($.ButtonState.REST);
},
_scheduleFade: function() {
window.setTimeout($.delegate(this, this._updateFade), 20);
},
_updateFade: function() {
if (this._shouldFade) {
var currentTime = new Date().getTime();
var deltaTime = currentTime - this._fadeBeginTime;
var opacity = 1.0 - deltaTime / this._fadeLength;
opacity = Math.min(1.0, opacity);
opacity = Math.max(0.0, opacity);
$.Utils.setElementOpacity(this._imgGroup, opacity, true);
if (opacity > 0) {
this._scheduleFade(); // fade again
}
}
},
_beginFading: function() {
this._shouldFade = true;
this._fadeBeginTime = new Date().getTime() + this._fadeDelay;
window.setTimeout($.delegate(this, this._scheduleFade), this._fadeDelay);
},
_stopFading: function() {
this._shouldFade = false;
$.Utils.setElementOpacity(this._imgGroup, 1.0, true);
},
_inTo: function(newState) {
if (newState >= $.ButtonState.GROUP && this._currentState == $.ButtonState.REST) {
this._stopFading();
this._currentState = $.ButtonState.GROUP;
}
if (newState >= $.ButtonState.HOVER && this._currentState == $.ButtonState.GROUP) {
this._imgHover.style.visibility = "";
this._currentState = $.ButtonState.HOVER;
}
if (newState >= $.ButtonState.DOWN && this._currentState == $.ButtonState.HOVER) {
this._imgDown.style.visibility = "";
this._currentState = $.ButtonState.DOWN;
}
},
_outTo: function(newState) {
if (newState <= $.ButtonState.HOVER && this._currentState == $.ButtonState.DOWN) {
this._imgDown.style.visibility = "hidden";
this._currentState = $.ButtonState.HOVER;
}
if (newState <= $.ButtonState.GROUP && this._currentState == $.ButtonState.HOVER) {
this._imgHover.style.visibility = "hidden";
this._currentState = $.ButtonState.GROUP;
}
if (this._newState <= $.ButtonState.REST && this._currentState == $.ButtonState.GROUP) {
this._beginFading();
this._currentState = $.ButtonState.REST;
}
},
_enterHandler: function(tracker, position, buttonDownElmt, buttonDownAny) {
if (buttonDownElmt) {
this._inTo($.ButtonState.DOWN);
this._raiseEvent("onEnter", this);
} else if (!buttonDownAny) {
this._inTo($.ButtonState.HOVER);
}
},
_exitHandler: function(tracker, position, buttonDownElmt, buttonDownAny) {
this._outTo($.ButtonState.GROUP);
if (buttonDownElmt) {
this._raiseEvent("onExit", this);
}
},
_pressHandler: function(tracker, position) {
this._inTo($.ButtonState.DOWN);
this._raiseEvent("onPress", this);
},
_releaseHandler: function(tracker, position, insideElmtPress, insideElmtRelease) {
if (insideElmtPress && insideElmtRelease) {
this._outTo($.ButtonState.HOVER);
this._raiseEvent("onRelease", this);
} else if (insideElmtPress) {
this._outTo($.ButtonState.GROUP);
} else {
this._inTo($.ButtonState.HOVER);
}
},
_clickHandler: function(tracker, position, quick, shift) {
if (quick) {
this._raiseEvent("onClick", this);
}
},
get_events: function get_events() {
return this._events;
},
_raiseEvent: function(eventName, eventArgs) {
var handler = this.get_events().getHandler(eventName);
if (handler) {
if (!eventArgs) {
eventArgs = new Object(); // Sys.EventArgs.Empty;
}
handler(this, eventArgs);
}
},
get_element: function() {
return this._button;
},
get_tooltip: function() {
return this._tooltip;
},
set_tooltip: function(value) {
this._tooltip = value;
},
get_config: function() {
return this.config;
},
set_config: function(value) {
this.config = value;
},
get_srcRest: function() {
return this._srcRest;
},
set_srcRest: function(value) {
this._srcRest = value;
},
get_srcGroup: function() {
return this._srcGroup;
},
set_srcGroup: function(value) {
this._srcGroup = value;
},
get_srcHover: function() {
return this._srcHover;
},
set_srcHover: function(value) {
this._srcHover = value;
},
get_srcDown: function() {
return this._srcDown;
},
set_srcDown: function(value) {
this._srcDown = value;
},
add_onPress: function(handler) {
this.get_events().addHandler("onPress", handler);
},
remove_onPress: function(handler) {
this.get_events().removeHandler("onPress", handler);
},
add_onClick: function(handler) {
this.get_events().addHandler("onClick", handler);
},
remove_onClick: function(handler) {
this.get_events().removeHandler("onClick", handler);
},
add_onEnter: function(handler) {
this.get_events().addHandler("onEnter", handler);
},
remove_onEnter: function(handler) {
this.get_events().removeHandler("onEnter", handler);
},
add_onRelease: function(handler) {
this.get_events().addHandler("onRelease", handler);
},
remove_onRelease: function(handler) {
this.get_events().removeHandler("onRelease", handler);
},
add_onExit: function(handler) {
this.get_events().addHandler("onExit", handler);
},
remove_onExit: function(handler) {
this.get_events().removeHandler("onExit", handler);
},
notifyGroupEnter: function() {
this._inTo($.ButtonState.GROUP);
},
notifyGroupExit: function() {
this._outTo($.ButtonState.REST);
}
};
}( OpenSeadragon ));

76
src/buttongroup.js Normal file
View File

@ -0,0 +1,76 @@
(function( $ ){
$.ButtonGroup = function(properties) {
this._buttons = properties.buttons;
this._group = properties.group;
this.config = properties.config;
this.initialize();
};
$.ButtonGroup.prototype = {
initialize: function() {
this._group = $.Utils.makeNeutralElement("span");
var buttons = this._buttons.concat([]); // copy
var tracker = new $.MouseTracker(this._group, this.config.clickTimeThreshold, this.config.clickDistThreshold);
this._group.style.display = "inline-block";
for (var i = 0; i < buttons.length; i++) {
this._group.appendChild(buttons[i].get_element());
}
tracker.enterHandler = $.delegate(this, this._enterHandler);
tracker.exitHandler = $.delegate(this, this._exitHandler);
tracker.releaseHandler = $.delegate(this, this._releaseHandler);
tracker.setTracking(true);
},
get_buttons: function() {
return this._buttons;
},
set_buttons: function(value) {
this._buttons = value;
},
get_element: function() {
return this._group;
},
get_config: function() {
return this.config;
},
set_config: function(value) {
this.config = value;
},
_enterHandler: function(tracker, position, buttonDownElmt, buttonDownAny) {
for (var i = 0; i < this._buttons.length; i++) {
this._buttons[i].notifyGroupEnter();
}
},
_exitHandler: function(tracker, position, buttonDownElmt, buttonDownAny) {
if (!buttonDownElmt) {
for (var i = 0; i < this._buttons.length; i++) {
this._buttons[i].notifyGroupExit();
}
}
},
_releaseHandler: function(tracker, position, insideElmtPress, insideElmtRelease) {
if (!insideElmtRelease) {
for (var i = 0; i < this._buttons.length; i++) {
this._buttons[i].notifyGroupExit();
}
}
},
emulateEnter: function() {
this._enterHandler();
},
emulateExit: function() {
this._exitHandler();
}
};
}( OpenSeadragon ));

78
src/config.js Normal file
View File

@ -0,0 +1,78 @@
(function( $ ){
$.Config = function () {
this.debugMode = true;
this.animationTime = 1.5;
this.blendTime = 0.5;
this.alwaysBlend = false;
this.autoHideControls = true;
this.immediateRender = false;
this.wrapHorizontal = false;
this.wrapVertical = false;
this.minZoomImageRatio = 0.8;
this.maxZoomPixelRatio = 2;
this.visibilityRatio = 0.5;
this.springStiffness = 5.0;
this.imageLoaderLimit = 2;
this.clickTimeThreshold = 200;
this.clickDistThreshold = 5;
this.zoomPerClick = 2.0;
this.zoomPerScroll = 1.2;
this.zoomPerSecond = 2.0;
this.showNavigationControl = true;
this.maxImageCacheCount = 100;
this.minPixelRatio = 0.5;
this.mouseNavEnabled = true;
this.navImages = {
zoomIn: {
REST: '/Scripts/images/zoomin_rest.png',
GROUP: '/Scripts/images/zoomin_grouphover.png',
HOVER: '/Scripts/images/zoomin_hover.png',
DOWN: '/Scripts/images/zoomin_pressed.png'
},
zoomOut: {
REST: '/Scripts/images/zoomout_rest.png',
GROUP: '/Scripts/images/zoomout_grouphover.png',
HOVER: '/Scripts/images/zoomout_hover.png',
DOWN: '/Scripts/images/zoomout_pressed.png'
},
home: {
REST: '/Scripts/images/home_rest.png',
GROUP: '/Scripts/images/home_grouphover.png',
HOVER: '/Scripts/images/home_hover.png',
DOWN: '/Scripts/images/home_pressed.png'
},
fullpage: {
REST: '/Scripts/images/fullpage_rest.png',
GROUP: '/Scripts/images/fullpage_grouphover.png',
HOVER: '/Scripts/images/fullpage_hover.png',
DOWN: '/Scripts/images/fullpage_pressed.png'
}
}
};
}( OpenSeadragon ));

43
src/control.js Normal file
View File

@ -0,0 +1,43 @@
(function( $ ){
$.Control = function (elmt, anchor, container) {
this.elmt = elmt;
this.anchor = anchor;
this.container = container;
this.wrapper = $.Utils.makeNeutralElement("span");
this.wrapper.style.display = "inline-block";
this.wrapper.appendChild(this.elmt);
if (this.anchor == $.ControlAnchor.NONE) {
this.wrapper.style.width = this.wrapper.style.height = "100%"; // IE6 fix
}
if (this.anchor == $.ControlAnchor.TOP_RIGHT || this.anchor == $.ControlAnchor.BOTTOM_RIGHT) {
this.container.insertBefore(this.wrapper, this.container.firstChild);
} else {
this.container.appendChild(this.wrapper);
}
};
$.Control.prototype = {
destroy: function() {
this.wrapper.removeChild(this.elmt);
this.container.removeChild(this.wrapper);
},
isVisible: function() {
return this.wrapper.style.display != "none";
},
setVisible: function(visible) {
this.wrapper.style.display = visible ? "inline-block" : "none";
},
setOpacity: function(opacity) {
if (this.elmt[SIGNAL] && $.Utils.getBrowser() == $.Browser.IE) {
$.Utils.setElementOpacity(this.elmt, opacity, true);
} else {
$.Utils.setElementOpacity(this.wrapper, opacity, true);
}
}
};
}( OpenSeadragon ));

20
src/controlanchor.js Normal file
View File

@ -0,0 +1,20 @@
(function( $ ){
$.ControlAnchor = function() {
throw Error.invalidOperation();
};
$.ControlAnchor = {
NONE: 0,
TOP_LEFT: 1,
TOP_RIGHT: 2,
BOTTOM_RIGHT: 3,
BOTTOM_LEFT: 4
};
$.ControlAnchor = $.ControlAnchor;
}( OpenSeadragon ));

13
src/displayrectangle.js Normal file
View File

@ -0,0 +1,13 @@
(function( $ ){
$.DisplayRect = function(x, y, width, height, minLevel, maxLevel) {
$.Rect.apply(this, [x, y, width, height]);
this.minLevel = minLevel;
this.maxLevel = maxLevel;
}
$.DisplayRect.prototype = new $.Rect();
$.DisplayRect.prototype.constructor = $.DisplayRect;
}( OpenSeadragon ));

580
src/drawer.js Normal file
View File

@ -0,0 +1,580 @@
(function( $ ){
var QUOTA = 100; // the max number of images we should keep in memory
var MIN_PIXEL_RATIO = 0.5; // the most shrunk a tile should be
var browser = $.Utils.getBrowser();
var browserVer = $.Utils.getBrowserVersion();
var subpixelRenders = browser == $.Browser.FIREFOX ||
browser == $.Browser.OPERA ||
(browser == $.Browser.SAFARI && browserVer >= 4) ||
(browser == $.Browser.CHROME && browserVer >= 2);
var useCanvas =
typeof (document.createElement("canvas").getContext) == "function" &&
subpixelRenders;
$.Drawer = function(source, viewport, elmt) {
this._container = $.Utils.getElement(elmt);
this._canvas = $.Utils.makeNeutralElement(useCanvas ? "canvas" : "div");
this._context = useCanvas ? this._canvas.getContext("2d") : null;
this._viewport = viewport;
this._source = source;
this.config = this._viewport.config;
this._imageLoader = new $.ImageLoader(this.config.imageLoaderLimit);
this._profiler = new $.Profiler();
this._minLevel = source.minLevel;
this._maxLevel = source.maxLevel;
this._tileSize = source.tileSize;
this._tileOverlap = source.tileOverlap;
this._normHeight = source.dimensions.y / source.dimensions.x;
this._cacheNumTiles = {}; // 1d dictionary [level] --> Point
this._cachePixelRatios = {}; // 1d dictionary [level] --> Point
this._tilesMatrix = {}; // 3d dictionary [level][x][y] --> Tile
this._tilesLoaded = []; // unordered list of Tiles with loaded images
this._coverage = {}; // 3d dictionary [level][x][y] --> Boolean
this._overlays = []; // unordered list of Overlays added
this._lastDrawn = []; // unordered list of Tiles drawn last frame
this._lastResetTime = 0;
this._midUpdate = false;
this._updateAgain = true;
this.elmt = this._container;
this._init();
};
$.Drawer.prototype = {
_init: function() {
this._canvas.style.width = "100%";
this._canvas.style.height = "100%";
this._canvas.style.position = "absolute";
this._container.style.textAlign = "left"; // explicit left-align
this._container.appendChild(this._canvas);
},
_compareTiles: function(prevBest, tile) {
if (!prevBest) {
return tile;
}
if (tile.visibility > prevBest.visibility) {
return tile;
} else if (tile.visibility == prevBest.visibility) {
if (tile.distance < prevBest.distance) {
return tile;
}
}
return prevBest;
},
_getNumTiles: function(level) {
if (!this._cacheNumTiles[level]) {
this._cacheNumTiles[level] = this._source.getNumTiles(level);
}
return this._cacheNumTiles[level];
},
_getPixelRatio: function(level) {
if (!this._cachePixelRatios[level]) {
this._cachePixelRatios[level] = this._source.getPixelRatio(level);
}
return this._cachePixelRatios[level];
},
_getTile: function(level, x, y, time, numTilesX, numTilesY) {
if (!this._tilesMatrix[level]) {
this._tilesMatrix[level] = {};
}
if (!this._tilesMatrix[level][x]) {
this._tilesMatrix[level][x] = {};
}
if (!this._tilesMatrix[level][x][y]) {
var xMod = (numTilesX + (x % numTilesX)) % numTilesX;
var yMod = (numTilesY + (y % numTilesY)) % numTilesY;
var bounds = this._source.getTileBounds(level, xMod, yMod);
var exists = this._source.tileExists(level, xMod, yMod);
var url = this._source.getTileUrl(level, xMod, yMod);
bounds.x += 1.0 * (x - xMod) / numTilesX;
bounds.y += this._normHeight * (y - yMod) / numTilesY;
this._tilesMatrix[level][x][y] = new $.Tile(level, x, y, bounds, exists, url);
}
var tile = this._tilesMatrix[level][x][y];
tile.lastTouchTime = time;
return tile;
},
_loadTile: function(tile, time) {
tile.loading = this._imageLoader.loadImage(tile.url,
$.Utils.createCallback(null, $.delegate(this, this._onTileLoad), tile, time));
},
_onTileLoad: function(tile, time, image) {
tile.loading = false;
if (this._midUpdate) {
$.Debug.error("Tile load callback in middle of drawing routine.");
return;
} else if (!image) {
$.Debug.log("Tile " + tile + " failed to load: " + tile.url);
tile.exists = false;
return;
} else if (time < this._lastResetTime) {
$.Debug.log("Ignoring tile " + tile + " loaded before reset: " + tile.url);
return;
}
tile.loaded = true;
tile.image = image;
var insertionIndex = this._tilesLoaded.length;
if (this._tilesLoaded.length >= QUOTA) {
var cutoff = Math.ceil(Math.log(this._tileSize) / Math.log(2));
var worstTile = null;
var worstTileIndex = -1;
for (var i = this._tilesLoaded.length - 1; i >= 0; i--) {
var prevTile = this._tilesLoaded[i];
if (prevTile.level <= this._cutoff || prevTile.beingDrawn) {
continue;
} else if (!worstTile) {
worstTile = prevTile;
worstTileIndex = i;
continue;
}
var prevTime = prevTile.lastTouchTime;
var worstTime = worstTile.lastTouchTime;
var prevLevel = prevTile.level;
var worstLevel = worstTile.level;
if (prevTime < worstTime ||
(prevTime == worstTime && prevLevel > worstLevel)) {
worstTile = prevTile;
worstTileIndex = i;
}
}
if (worstTile && worstTileIndex >= 0) {
worstTile.unload();
insertionIndex = worstTileIndex;
}
}
this._tilesLoaded[insertionIndex] = tile;
this._updateAgain = true;
},
_clearTiles: function() {
this._tilesMatrix = {};
this._tilesLoaded = [];
},
/**
* Returns true if the given tile provides coverage to lower-level tiles of
* lower resolution representing the same content. If neither x nor y is
* given, returns true if the entire visible level provides coverage.
*
* Note that out-of-bounds tiles provide coverage in this sense, since
* there's no content that they would need to cover. Tiles at non-existent
* levels that are within the image bounds, however, do not.
*/
_providesCoverage: function(level, x, y) {
if (!this._coverage[level]) {
return false;
}
if (x === undefined || y === undefined) {
var rows = this._coverage[level];
for (var i in rows) {
if (rows.hasOwnProperty(i)) {
var cols = rows[i];
for (var j in cols) {
if (cols.hasOwnProperty(j) && !cols[j]) {
return false;
}
}
}
}
return true;
}
return (this._coverage[level][x] === undefined ||
this._coverage[level][x][y] === undefined ||
this._coverage[level][x][y] === true);
},
/**
* Returns true if the given tile is completely covered by higher-level
* tiles of higher resolution representing the same content. If neither x
* nor y is given, returns true if the entire visible level is covered.
*/
_isCovered: function(level, x, y) {
if (x === undefined || y === undefined) {
return this._providesCoverage(level + 1);
} else {
return (this._providesCoverage(level + 1, 2 * x, 2 * y) &&
this._providesCoverage(level + 1, 2 * x, 2 * y + 1) &&
this._providesCoverage(level + 1, 2 * x + 1, 2 * y) &&
this._providesCoverage(level + 1, 2 * x + 1, 2 * y + 1));
}
},
/**
* Sets whether the given tile provides coverage or not.
*/
_setCoverage: function(level, x, y, covers) {
if (!this._coverage[level]) {
$.Debug.error("Setting coverage for a tile before its " +
"level's coverage has been reset: " + level);
return;
}
if (!this._coverage[level][x]) {
this._coverage[level][x] = {};
}
this._coverage[level][x][y] = covers;
},
/**
* Resets coverage information for the given level. This should be called
* after every draw routine. Note that at the beginning of the next draw
* routine, coverage for every visible tile should be explicitly set.
*/
_resetCoverage: function(level) {
this._coverage[level] = {};
},
_compareTiles: function(prevBest, tile) {
if (!prevBest) {
return tile;
}
if (tile.visibility > prevBest.visibility) {
return tile;
} else if (tile.visibility == prevBest.visibility) {
if (tile.distance < prevBest.distance) {
return tile;
}
}
return prevBest;
},
_getOverlayIndex: function(elmt) {
for (var i = this._overlays.length - 1; i >= 0; i--) {
if (this._overlays[i].elmt == elmt) {
return i;
}
}
return -1;
},
_updateActual: function() {
this._updateAgain = false;
var _canvas = this._canvas;
var _context = this._context;
var _container = this._container;
var _useCanvas = useCanvas;
var _lastDrawn = this._lastDrawn;
while (_lastDrawn.length > 0) {
var tile = _lastDrawn.pop();
tile.beingDrawn = false;
}
var viewportSize = this._viewport.getContainerSize();
var viewportWidth = viewportSize.x;
var viewportHeight = viewportSize.y;
_canvas.innerHTML = "";
if (_useCanvas) {
_canvas.width = viewportWidth;
_canvas.height = viewportHeight;
_context.clearRect(0, 0, viewportWidth, viewportHeight);
}
var viewportBounds = this._viewport.getBounds(true);
var viewportTL = viewportBounds.getTopLeft();
var viewportBR = viewportBounds.getBottomRight();
if (!this.config.wrapHorizontal &&
(viewportBR.x < 0 || viewportTL.x > 1)) {
return;
} else if (!this.config.wrapVertical &&
(viewportBR.y < 0 || viewportTL.y > this._normHeight)) {
return;
}
var _abs = Math.abs;
var _ceil = Math.ceil;
var _floor = Math.floor;
var _log = Math.log;
var _max = Math.max;
var _min = Math.min;
var alwaysBlend = this.config.alwaysBlend;
var blendTimeMillis = 1000 * this.config.blendTime;
var immediateRender = this.config.immediateRender;
var wrapHorizontal = this.config.wrapHorizontal;
var wrapVertical = this.config.wrapVertical;
if (!wrapHorizontal) {
viewportTL.x = _max(viewportTL.x, 0);
viewportBR.x = _min(viewportBR.x, 1);
}
if (!wrapVertical) {
viewportTL.y = _max(viewportTL.y, 0);
viewportBR.y = _min(viewportBR.y, this._normHeight);
}
var best = null;
var haveDrawn = false;
var currentTime = new Date().getTime();
var viewportCenter = this._viewport.pixelFromPoint(this._viewport.getCenter());
var zeroRatioT = this._viewport.deltaPixelsFromPoints(this._source.getPixelRatio(0), false).x;
var optimalPixelRatio = immediateRender ? 1 : zeroRatioT;
var lowestLevel = _max(this._minLevel, _floor(_log(this.config.minZoomImageRatio) / _log(2)));
var zeroRatioC = this._viewport.deltaPixelsFromPoints(this._source.getPixelRatio(0), true).x;
var highestLevel = _min(this._maxLevel,
_floor(_log(zeroRatioC / MIN_PIXEL_RATIO) / _log(2)));
lowestLevel = _min(lowestLevel, highestLevel);
for (var level = highestLevel; level >= lowestLevel; level--) {
var drawLevel = false;
var renderPixelRatioC = this._viewport.deltaPixelsFromPoints(
this._source.getPixelRatio(level), true).x; // note the .x!
if ((!haveDrawn && renderPixelRatioC >= MIN_PIXEL_RATIO) ||
level == lowestLevel) {
drawLevel = true;
haveDrawn = true;
} else if (!haveDrawn) {
continue;
}
this._resetCoverage(level);
var levelOpacity = _min(1, (renderPixelRatioC - 0.5) / 0.5);
var renderPixelRatioT = this._viewport.deltaPixelsFromPoints(
this._source.getPixelRatio(level), false).x;
var levelVisibility = optimalPixelRatio /
_abs(optimalPixelRatio - renderPixelRatioT);
var tileTL = this._source.getTileAtPoint(level, viewportTL);
var tileBR = this._source.getTileAtPoint(level, viewportBR);
var numTiles = this._getNumTiles(level);
var numTilesX = numTiles.x;
var numTilesY = numTiles.y;
if (!wrapHorizontal) {
tileBR.x = _min(tileBR.x, numTilesX - 1);
}
if (!wrapVertical) {
tileBR.y = _min(tileBR.y, numTilesY - 1);
}
for (var x = tileTL.x; x <= tileBR.x; x++) {
for (var y = tileTL.y; y <= tileBR.y; y++) {
var tile = this._getTile(level, x, y, currentTime, numTilesX, numTilesY);
var drawTile = drawLevel;
this._setCoverage(level, x, y, false);
if (!tile.exists) {
continue;
}
if (haveDrawn && !drawTile) {
if (this._isCovered(level, x, y)) {
this._setCoverage(level, x, y, true);
} else {
drawTile = true;
}
}
if (!drawTile) {
continue;
}
var boundsTL = tile.bounds.getTopLeft();
var boundsSize = tile.bounds.getSize();
var positionC = this._viewport.pixelFromPoint(boundsTL, true);
var sizeC = this._viewport.deltaPixelsFromPoints(boundsSize, true);
if (!this._tileOverlap) {
sizeC = sizeC.plus(new $.Point(1, 1));
}
var positionT = this._viewport.pixelFromPoint(boundsTL, false);
var sizeT = this._viewport.deltaPixelsFromPoints(boundsSize, false);
var tileCenter = positionT.plus(sizeT.divide(2));
var tileDistance = viewportCenter.distanceTo(tileCenter);
tile.position = positionC;
tile.size = sizeC;
tile.distance = tileDistance;
tile.visibility = levelVisibility;
if (tile.loaded) {
if (!tile.blendStart) {
tile.blendStart = currentTime;
}
var deltaTime = currentTime - tile.blendStart;
var opacity = _min(1, deltaTime / blendTimeMillis);
if (alwaysBlend) {
opacity *= levelOpacity;
}
tile.opacity = opacity;
_lastDrawn.push(tile);
if (opacity == 1) {
this._setCoverage(level, x, y, true);
} else if (deltaTime < blendTimeMillis) {
updateAgain = true;
}
} else if (tile.Loading) {
} else {
best = this._compareTiles(best, tile);
}
}
}
if (this._providesCoverage(level)) {
break;
}
}
for (var i = _lastDrawn.length - 1; i >= 0; i--) {
var tile = _lastDrawn[i];
if (_useCanvas) {
tile.drawCanvas(_context);
} else {
tile.drawHTML(_canvas);
}
tile.beingDrawn = true;
}
var numOverlays = this._overlays.length;
for (var i = 0; i < numOverlays; i++) {
var overlay = this._overlays[i];
var bounds = overlay.bounds;
overlay.position = this._viewport.pixelFromPoint(bounds.getTopLeft(), true);
overlay.size = this._viewport.deltaPixelsFromPoints(bounds.getSize(), true);
overlay.drawHTML(_container);
}
if (best) {
this._loadTile(best, currentTime);
this._updateAgain = true; // because we haven't finished drawing, so
}
},
addOverlay: function(elmt, loc, placement) {
var elmt = $.Utils.getElement(elmt);
if (this._getOverlayIndex(elmt) >= 0) {
return; // they're trying to add a duplicate overlay
}
this._overlays.push(new $.Overlay(elmt, loc, placement));
this._updateAgain = true;
},
updateOverlay: function(elmt, loc, placement) {
var elmt = $.Utils.getElement(elmt);
var i = this._getOverlayIndex(elmt);
if (i >= 0) {
this._overlays[i].update(loc, placement);
this._updateAgain = true;
}
},
removeOverlay: function(elmt) {
var elmt = $.Utils.getElement(elmt);
var i = this._getOverlayIndex(elmt);
if (i >= 0) {
this._overlays[i].destroy();
this._overlays.splice(i, 1);
this._updateAgain = true;
}
},
clearOverlays: function() {
while (this._overlays.length > 0) {
this._overlays.pop().destroy();
this._updateAgain = true;
}
},
needsUpdate: function() {
return this._updateAgain;
},
numTilesLoaded: function() {
return this._tilesLoaded.length;
},
reset: function() {
this._clearTiles();
this._lastResetTime = new Date().getTime();
this._updateAgain = true;
},
update: function() {
this._profiler.beginUpdate();
this._midUpdate = true;
this._updateActual();
this._midUpdate = false;
this._profiler.endUpdate();
},
idle: function() {
}
};
}( OpenSeadragon ));

6
src/dzi.js Normal file
View File

@ -0,0 +1,6 @@
(function( $ ){
}( OpenSeadragon ));

11
src/dzierror.js Normal file
View File

@ -0,0 +1,11 @@
(function( $ ){
$.DziError = function(message) {
Error.apply(this, arguments);
this.message = message;
};
$.DziError.prototype = new Error();
$.DziError.constructor = $.DziError;
}( OpenSeadragon ));

235
src/dzitilesource.js Normal file
View File

@ -0,0 +1,235 @@
(function( $ ){
$.DziTileSource = function(width, height, tileSize, tileOverlap, tilesUrl, fileFormat, displayRects) {
$.TileSource.call(this, width, height, tileSize, tileOverlap, null, null);
this._levelRects = {};
this.tilesUrl = tilesUrl;
this.fileFormat = fileFormat;
this.displayRects = displayRects;
this.initialize();
};
$.DziTileSource.prototype = new $.TileSource();
$.DziTileSource.prototype.constructor = $.DziTileSource;
$.DziTileSource.prototype.initialize = function() {
if (!this.displayRects) {
return;
}
for (var i = this.displayRects.length - 1; i >= 0; i--) {
var rect = this.displayRects[i];
for (var level = rect.minLevel; level <= rect.maxLevel; level++) {
if (!this._levelRects[level]) {
this._levelRects[level] = [];
}
this._levelRects[level].push(rect);
}
}
};
$.DziTileSource.prototype.getTileUrl = function(level, x, y) {
return [this.tilesUrl, level, '/', x, '_', y, '.', this.fileFormat].join('');
};
$.DziTileSource.prototype.tileExists = function(level, x, y) {
var rects = this._levelRects[level];
if (!rects || !rects.length) {
return true;
}
for (var i = rects.length - 1; i >= 0; i--) {
var rect = rects[i];
if (level < rect.minLevel || level > rect.maxLevel) {
continue;
}
var scale = this.getLevelScale(level);
var xMin = rect.x * scale;
var yMin = rect.y * scale;
var xMax = xMin + rect.width * scale;
var yMax = yMin + rect.height * scale;
xMin = Math.floor(xMin / this.tileSize);
yMin = Math.floor(yMin / this.tileSize);
xMax = Math.ceil(xMax / this.tileSize);
yMax = Math.ceil(yMax / this.tileSize);
if (xMin <= x && x < xMax && yMin <= y && y < yMax) {
return true;
}
}
return false;
};
$._DziTileSourceHelper = function() {
};
$._DziTileSourceHelper.prototype = {
createFromXml: function(xmlUrl, xmlString, callback) {
var async = typeof (callback) == "function";
var error = null;
if (!xmlUrl) {
this.error = $.Strings.getString("Errors.Empty");
if (async) {
window.setTimeout(function() {
callback(null, error);
}, 1);
return null;
}
throw new $.DziError(error);
}
var urlParts = xmlUrl.split('/');
var filename = urlParts[urlParts.length - 1];
var lastDot = filename.lastIndexOf('.');
if (lastDot > -1) {
urlParts[urlParts.length - 1] = filename.slice(0, lastDot);
}
var tilesUrl = urlParts.join('/') + "_files/";
function finish(func, obj) {
try {
return func(obj, tilesUrl);
} catch (e) {
if (async) {
//Start Thatcher - Throwable doesnt have getError
//error = this.getError(e).message;
return null;
//End Thatcher
} else {
throw this.getError(e);
}
}
}
if (async) {
if (xmlString) {
var handler = $.delegate(this, this.processXml);
window.setTimeout(function() {
var source = finish(handler, $.Utils.parseXml(xmlString));
callback(source, error); // call after finish sets error
}, 1);
} else {
var handler = $.delegate(this, this.processResponse);
$.Utils.makeAjaxRequest(xmlUrl, function(xhr) {
var source = finish(handler, xhr);
callback(source, error); // call after finish sets error
});
}
return null;
}
if (xmlString) {
return finish($.delegate(this, this.processXml), $.Utils.parseXml(xmlString));
} else {
return finish($.delegate(this, this.processResponse), $.Utils.makeAjaxRequest(xmlUrl));
}
},
processResponse: function(xhr, tilesUrl) {
if (!xhr) {
throw new $.DziError($.Strings.getString("Errors.Security"));
} else if (xhr.status !== 200 && xhr.status !== 0) {
var status = xhr.status;
var statusText = (status == 404) ? "Not Found" : xhr.statusText;
throw new $.DziError($.Strings.getString("Errors.Status", status, statusText));
}
var doc = null;
if (xhr.responseXML && xhr.responseXML.documentElement) {
doc = xhr.responseXML;
} else if (xhr.responseText) {
doc = $.Utils.parseXml(xhr.responseText);
}
return this.processXml(doc, tilesUrl);
},
processXml: function(xmlDoc, tilesUrl) {
if (!xmlDoc || !xmlDoc.documentElement) {
throw new $.DziError($.Strings.getString("Errors.Xml"));
}
var root = xmlDoc.documentElement;
var rootName = root.tagName;
if (rootName == "Image") {
try {
return this.processDzi(root, tilesUrl);
} catch (e) {
var defMsg = $.Strings.getString("Errors.Dzi");
throw (e instanceof $.DziError) ? e : new $.DziError(defMsg);
}
} else if (rootName == "Collection") {
throw new $.DziError($.Strings.getString("Errors.Dzc"));
} else if (rootName == "Error") {
return this.processError(root);
}
throw new $.DziError($.Strings.getString("Errors.Dzi"));
},
processDzi: function(imageNode, tilesUrl) {
var fileFormat = imageNode.getAttribute("Format");
if (!$.Utils.imageFormatSupported(fileFormat)) {
throw new $.DziError($.Strings.getString("Errors.ImageFormat",
fileFormat.toUpperCase()));
}
var sizeNode = imageNode.getElementsByTagName("Size")[0];
var dispRectNodes = imageNode.getElementsByTagName("DisplayRect");
var width = parseInt(sizeNode.getAttribute("Width"), 10);
var height = parseInt(sizeNode.getAttribute("Height"), 10);
var tileSize = parseInt(imageNode.getAttribute("TileSize"));
var tileOverlap = parseInt(imageNode.getAttribute("Overlap"));
var dispRects = [];
for (var i = 0; i < dispRectNodes.length; i++) {
var dispRectNode = dispRectNodes[i];
var rectNode = dispRectNode.getElementsByTagName("Rect")[0];
dispRects.push(new $.DisplayRect(
parseInt(rectNode.getAttribute("X"), 10),
parseInt(rectNode.getAttribute("Y"), 10),
parseInt(rectNode.getAttribute("Width"), 10),
parseInt(rectNode.getAttribute("Height"), 10),
0, // ignore MinLevel attribute, bug in Deep Zoom Composer
parseInt(dispRectNode.getAttribute("MaxLevel"), 10)
));
}
return new $.DziTileSource(width, height, tileSize, tileOverlap,
tilesUrl, fileFormat, dispRects);
},
processError: function(errorNode) {
var messageNode = errorNode.getElementsByTagName("Message")[0];
var message = messageNode.firstChild.nodeValue;
throw new $.DziError(message);
},
getError: function(e) {
if (!(e instanceof DziError)) {
$.Debug.error(e.name + " while creating DZI from XML: " + e.message);
e = new $.DziError($.Strings.getString("Errors.Unknown"));
}
}
};
$.DziTileSourceHelper = new $._DziTileSourceHelper();
}( OpenSeadragon ));

41
src/eventhandlerlist.js Normal file
View File

@ -0,0 +1,41 @@
(function($){
$.EventHandlerList = function() {
this._list = {};
};
$.EventHandlerList.prototype = {
addHandler: function(id, handler) {
var events = this._list[ id ];
if( !events ){
this._list[ id ] = events = [];
}
events[events.length] = handler;
},
removeHandler: function(id, handler) {
//Start Thatcher - unneccessary indirection. Also, because events were
// - not actually being removed, we need to add the code
// - to do the removal ourselves. TODO
var evt = this._list[ id ];
if (!evt) return;
//End Thatcher
},
getHandler: function(id) {
var evt = this._list[ id ];
if (!evt || !evt.length) return null;
evt = evt.length === 1 ?
[evt[0]] :
Array.apply( null, evt );
return function(source, args) {
for (var i = 0, l = evt.length; i < l; i++) {
evt[i](source, args);
}
};
}
};
}( OpenSeadragon ));

36
src/imageloader.js Normal file
View File

@ -0,0 +1,36 @@
(function( $ ){
$.ImageLoader = function(imageLoaderLimit) {
this._downloading = 0;
this.imageLoaderLimit = imageLoaderLimit;
};
$.ImageLoader.prototype = {
_onComplete: function(callback, src, image) {
this._downloading--;
if (typeof (callback) == "function") {
try {
callback(image);
} catch (e) {
$.Debug.error(e.name + " while executing " + src +
" callback: " + e.message, e);
}
}
},
loadImage: function(src, callback) {
if (this._downloading >= this.imageLoaderLimit) {
return false;
}
var func = $.Utils.createCallback(null, $.delegate(this, this._onComplete), callback);
var job = new $.Job(src, func);
this._downloading++;
job.start();
return true;
}
};
}( OpenSeadragon ));

47
src/job.js Normal file
View File

@ -0,0 +1,47 @@
(function( $ ){
$.Job = function(src, callback) {
this._image = null;
this._timeout = null;
this._src = src;
this._callback = callback;
this.TIMEOUT = 5000;
};
$.Job.prototype = {
_finish: function(success) {
this._image.onload = null;
this._image.onabort = null;
this._image.onerror = null;
if (this._timeout) {
window.clearTimeout(this._timeout);
}
var image = this._image;
var callback = this._callback;
window.setTimeout(function() {
callback(this._src, success ? image : null);
}, 1);
},
_onloadHandler: function() {
this._finish(true);
},
_onerrorHandler: function() {
this._finish(false);
},
start: function() {
this._image = new Image();
this._image.onload = $.delegate(this, this._onloadHandler);
this._image.onabort = $.delegate(this, this._onerrorHandler);
this._image.onerror = $.delegate(this, this._onerrorHandler);
this._timeout = window.setTimeout($.delegate(this, this._onerrorHandler), this.TIMEOUT);
this._image.src = this._src;
}
};
}( OpenSeadragon ));

455
src/mousetracker.js Normal file
View File

@ -0,0 +1,455 @@
(function( $ ){
if ($.MouseTracker) {
return;
}
var isIE = $.Utils.getBrowser() == $.Browser.IE;
var buttonDownAny = false;
var ieCapturingAny = false;
var ieTrackersActive = {}; // dictionary from hash to MouseTracker
var ieTrackersCapturing = []; // list of trackers interested in capture
function getMouseAbsolute(event) {
return $.Utils.getMousePosition(event);
}
function getMouseRelative(event, elmt) {
var mouse = $.Utils.getMousePosition(event);
var offset = $.Utils.getElementPosition(elmt);
return mouse.minus(offset);
}
/**
* Returns true if elmtB is a child node of elmtA, or if they're equal.
*/
function isChild(elmtA, elmtB) {
var body = document.body;
while (elmtB && elmtA != elmtB && body != elmtB) {
try {
elmtB = elmtB.parentNode;
} catch (e) {
return false;
}
}
return elmtA == elmtB;
}
function onGlobalMouseDown() {
buttonDownAny = true;
}
function onGlobalMouseUp() {
buttonDownAny = false;
}
(function () {
if (isIE) {
$.Utils.addEvent(document, "mousedown", onGlobalMouseDown, false);
$.Utils.addEvent(document, "mouseup", onGlobalMouseUp, false);
} else {
$.Utils.addEvent(window, "mousedown", onGlobalMouseDown, true);
$.Utils.addEvent(window, "mouseup", onGlobalMouseUp, true);
}
})();
$.MouseTracker = function (elmt, clickTimeThreshold, clickDistThreshold) {
//Start Thatcher - TODO: remove local function definitions in favor of
// - a global closre for MouseTracker so the number
// - of Viewers has less memory impact. Also use
// - prototype pattern instead of Singleton pattern.
//End Thatcher
var self = this;
var ieSelf = null;
var hash = Math.random(); // a unique hash for this tracker
var elmt = $.Utils.getElement(elmt);
var tracking = false;
var capturing = false;
var buttonDownElmt = false;
var insideElmt = false;
var lastPoint = null; // position of last mouse down/move
var lastMouseDownTime = null; // time of last mouse down
var lastMouseDownPoint = null; // position of last mouse down
var clickTimeThreshold = clickTimeThreshold;
var clickDistThreshold = clickDistThreshold;
this.target = elmt;
this.enterHandler = null; // function(tracker, position, buttonDownElmt, buttonDownAny)
this.exitHandler = null; // function(tracker, position, buttonDownElmt, buttonDownAny)
this.pressHandler = null; // function(tracker, position)
this.releaseHandler = null; // function(tracker, position, insideElmtPress, insideElmtRelease)
this.scrollHandler = null; // function(tracker, position, scroll, shift)
this.clickHandler = null; // function(tracker, position, quick, shift)
this.dragHandler = null; // function(tracker, position, delta, shift)
function startTracking() {
if (!tracking) {
$.Utils.addEvent(elmt, "mouseover", onMouseOver, false);
$.Utils.addEvent(elmt, "mouseout", onMouseOut, false);
$.Utils.addEvent(elmt, "mousedown", onMouseDown, false);
$.Utils.addEvent(elmt, "mouseup", onMouseUp, false);
$.Utils.addEvent(elmt, "click", onMouseClick, false);
$.Utils.addEvent(elmt, "DOMMouseScroll", onMouseWheelSpin, false);
$.Utils.addEvent(elmt, "mousewheel", onMouseWheelSpin, false); // Firefox
tracking = true;
ieTrackersActive[hash] = ieSelf;
}
}
function stopTracking() {
if (tracking) {
$.Utils.removeEvent(elmt, "mouseover", onMouseOver, false);
$.Utils.removeEvent(elmt, "mouseout", onMouseOut, false);
$.Utils.removeEvent(elmt, "mousedown", onMouseDown, false);
$.Utils.removeEvent(elmt, "mouseup", onMouseUp, false);
$.Utils.removeEvent(elmt, "click", onMouseClick, false);
$.Utils.removeEvent(elmt, "DOMMouseScroll", onMouseWheelSpin, false);
$.Utils.removeEvent(elmt, "mousewheel", onMouseWheelSpin, false);
releaseMouse();
tracking = false;
delete ieTrackersActive[hash];
}
}
function captureMouse() {
if (!capturing) {
if (isIE) {
$.Utils.removeEvent(elmt, "mouseup", onMouseUp, false);
$.Utils.addEvent(elmt, "mouseup", onMouseUpIE, true);
$.Utils.addEvent(elmt, "mousemove", onMouseMoveIE, true);
} else {
$.Utils.addEvent(window, "mouseup", onMouseUpWindow, true);
$.Utils.addEvent(window, "mousemove", onMouseMove, true);
}
capturing = true;
}
}
function releaseMouse() {
if (capturing) {
if (isIE) {
$.Utils.removeEvent(elmt, "mousemove", onMouseMoveIE, true);
$.Utils.removeEvent(elmt, "mouseup", onMouseUpIE, true);
$.Utils.addEvent(elmt, "mouseup", onMouseUp, false);
} else {
$.Utils.removeEvent(window, "mousemove", onMouseMove, true);
$.Utils.removeEvent(window, "mouseup", onMouseUpWindow, true);
}
capturing = false;
}
}
function triggerOthers(eventName, event) {
var trackers = ieTrackersActive;
for (var otherHash in trackers) {
if (trackers.hasOwnProperty(otherHash) && hash != otherHash) {
trackers[otherHash][eventName](event);
}
}
}
function hasMouse() {
return insideElmt;
}
function onMouseOver(event) {
var event = $.Utils.getEvent(event);
if (isIE && capturing && !isChild(event.srcElement, elmt)) {
triggerOthers("onMouseOver", event);
}
var to = event.target ? event.target : event.srcElement;
var from = event.relatedTarget ? event.relatedTarget : event.fromElement;
if (!isChild(elmt, to) || isChild(elmt, from)) {
return;
}
insideElmt = true;
if (typeof (self.enterHandler) == "function") {
try {
self.enterHandler(self, getMouseRelative(event, elmt),
buttonDownElmt, buttonDownAny);
} catch (e) {
$.Debug.error(e.name +
" while executing enter handler: " + e.message, e);
}
}
}
function onMouseOut(event) {
var event = $.Utils.getEvent(event);
if (isIE && capturing && !isChild(event.srcElement, elmt)) {
triggerOthers("onMouseOut", event);
}
var from = event.target ? event.target : event.srcElement;
var to = event.relatedTarget ? event.relatedTarget : event.toElement;
if (!isChild(elmt, from) || isChild(elmt, to)) {
return;
}
insideElmt = false;
if (typeof (self.exitHandler) == "function") {
try {
self.exitHandler(self, getMouseRelative(event, elmt),
buttonDownElmt, buttonDownAny);
} catch (e) {
$.Debug.error(e.name +
" while executing exit handler: " + e.message, e);
}
}
}
function onMouseDown(event) {
var event = $.Utils.getEvent(event);
if (event.button == 2) {
return;
}
buttonDownElmt = true;
lastPoint = getMouseAbsolute(event);
lastMouseDownPoint = lastPoint;
lastMouseDownTime = new Date().getTime();
if (typeof (self.pressHandler) == "function") {
try {
self.pressHandler(self, getMouseRelative(event, elmt));
} catch (e) {
$.Debug.error(e.name +
" while executing press handler: " + e.message, e);
}
}
if (self.pressHandler || self.dragHandler) {
$.Utils.cancelEvent(event);
}
if (!isIE || !ieCapturingAny) {
captureMouse();
ieCapturingAny = true;
ieTrackersCapturing = [ieSelf]; // reset to empty & add us
} else if (isIE) {
ieTrackersCapturing.push(ieSelf); // add us to the list
}
}
function onMouseUp(event) {
var event = $.Utils.getEvent(event);
var insideElmtPress = buttonDownElmt;
var insideElmtRelease = insideElmt;
if (event.button == 2) {
return;
}
buttonDownElmt = false;
if (typeof (self.releaseHandler) == "function") {
try {
self.releaseHandler(self, getMouseRelative(event, elmt),
insideElmtPress, insideElmtRelease);
} catch (e) {
$.Debug.error(e.name +
" while executing release handler: " + e.message, e);
}
}
if (insideElmtPress && insideElmtRelease) {
handleMouseClick(event);
}
}
/**
* Only triggered once by the deepest element that initially received
* the mouse down event. We want to make sure THIS event doesn't bubble.
* Instead, we want to trigger the elements that initially received the
* mouse down event (including this one) only if the mouse is no longer
* inside them. Then, we want to release capture, and emulate a regular
* mouseup on the event that this event was meant for.
*/
function onMouseUpIE(event) {
var event = $.Utils.getEvent(event);
if (event.button == 2) {
return;
}
for (var i = 0; i < ieTrackersCapturing.length; i++) {
var tracker = ieTrackersCapturing[i];
if (!tracker.hasMouse()) {
tracker.onMouseUp(event);
}
}
releaseMouse();
ieCapturingAny = false;
event.srcElement.fireEvent("on" + event.type,
document.createEventObject(event));
$.Utils.stopEvent(event);
}
/**
* Only triggered in W3C browsers by elements within which the mouse was
* initially pressed, since they are now listening to the window for
* mouseup during the capture phase. We shouldn't handle the mouseup
* here if the mouse is still inside this element, since the regular
* mouseup handler will still fire.
*/
function onMouseUpWindow(event) {
if (!insideElmt) {
onMouseUp(event);
}
releaseMouse();
}
function onMouseClick(event) {
if (self.clickHandler) {
$.Utils.cancelEvent(event);
}
}
function onMouseWheelSpin(event) {
var nDelta = 0;
if (!event) { // For IE, access the global (window) event object
event = window.event;
}
if (event.wheelDelta) { // IE and Opera
nDelta = event.wheelDelta;
if (window.opera) { // Opera has the values reversed
nDelta = -nDelta;
}
}
else if (event.detail) { // Mozilla FireFox
nDelta = -event.detail;
}
nDelta = nDelta > 0 ? 1 : -1;
if (typeof (self.scrollHandler) == "function") {
try {
self.scrollHandler(self, getMouseRelative(event, elmt), nDelta, event.shiftKey);
} catch (e) {
$.Debug.error(e.name +
" while executing scroll handler: " + e.message, e);
}
$.Utils.cancelEvent(event);
}
}
function handleMouseClick(event) {
var event = $.Utils.getEvent(event);
if (event.button == 2) {
return;
}
var time = new Date().getTime() - lastMouseDownTime;
var point = getMouseAbsolute(event);
var distance = lastMouseDownPoint.distanceTo(point);
var quick = time <= clickTimeThreshold &&
distance <= clickDistThreshold;
if (typeof (self.clickHandler) == "function") {
try {
self.clickHandler(self, getMouseRelative(event, elmt),
quick, event.shiftKey);
} catch (e) {
$.Debug.error(e.name +
" while executing click handler: " + e.message, e);
}
}
}
function onMouseMove(event) {
var event = $.Utils.getEvent(event);
var point = getMouseAbsolute(event);
var delta = point.minus(lastPoint);
lastPoint = point;
if (typeof (self.dragHandler) == "function") {
try {
self.dragHandler(self, getMouseRelative(event, elmt),
delta, event.shiftKey);
} catch (e) {
$.Debug.error(e.name +
" while executing drag handler: " + e.message, e);
}
$.Utils.cancelEvent(event);
}
}
/**
* Only triggered once by the deepest element that initially received
* the mouse down event. Since no other element has captured the mouse,
* we want to trigger the elements that initially received the mouse
* down event (including this one).
*/
function onMouseMoveIE(event) {
for (var i = 0; i < ieTrackersCapturing.length; i++) {
ieTrackersCapturing[i].onMouseMove(event);
}
$.Utils.stopEvent(event);
}
(function () {
ieSelf = {
hasMouse: hasMouse,
onMouseOver: onMouseOver,
onMouseOut: onMouseOut,
onMouseUp: onMouseUp,
onMouseMove: onMouseMove
};
})();
this.isTracking = function () {
return tracking;
};
this.setTracking = function (track) {
if (track) {
startTracking();
} else {
stopTracking();
}
};
};
}( OpenSeadragon ));

159
src/navcontrol.js Normal file
View File

@ -0,0 +1,159 @@
(function( $ ){
$.NavControl = function(viewer) {
this._group = null;
this._zooming = false; // whether we should be continuously zooming
this._zoomFactor = null; // how much we should be continuously zooming by
this._lastZoomTime = null;
this._viewer = viewer;
this.config = this._viewer.config;
this.elmt = null;
this.initialize();
};
$.NavControl.prototype = {
initialize: function() {
var beginZoomingInHandler = $.delegate(this, this._beginZoomingIn);
var endZoomingHandler = $.delegate(this, this._endZooming);
var doSingleZoomInHandler = $.delegate(this, this._doSingleZoomIn);
var beginZoomingOutHandler = $.delegate(this, this._beginZoomingOut);
var doSingleZoomOutHandler = $.delegate(this, this._doSingleZoomOut);
var onHomeHandler = $.delegate(this, this._onHome);
var onFullPageHandler = $.delegate(this, this._onFullPage);
var navImages = this._viewer.config.navImages;
var zoomIn = new $.Button({
config: this._viewer.config,
tooltip: $.Strings.getString("Tooltips.ZoomIn"),
srcRest: this._resolveUrl(navImages.zoomIn.REST),
srcGroup: this._resolveUrl(navImages.zoomIn.GROUP),
srcHover: this._resolveUrl(navImages.zoomIn.HOVER),
srcDown: this._resolveUrl(navImages.zoomIn.DOWN)
},{
onPress: beginZoomingInHandler,
onRelease: endZoomingHandler,
onClick: doSingleZoomInHandler,
onEnter: beginZoomingInHandler,
onExit: endZoomingHandler
});
var zoomOut = new $.Button({
config: this._viewer.config,
tooltip: $.Strings.getString("Tooltips.ZoomOut"),
srcRest: this._resolveUrl(navImages.zoomOut.REST),
srcGroup: this._resolveUrl(navImages.zoomOut.GROUP),
srcHover: this._resolveUrl(navImages.zoomOut.HOVER),
srcDown: this._resolveUrl(navImages.zoomOut.DOWN)
}, {
onPress: beginZoomingOutHandler,
onRelease: endZoomingHandler,
onClick: doSingleZoomOutHandler,
onEnter: beginZoomingOutHandler,
onExit: endZoomingHandler
});
var goHome = new $.Button({
config: this._viewer.config,
tooltip: $.Strings.getString("Tooltips.Home"),
srcRest: this._resolveUrl(navImages.home.REST),
srcGroup: this._resolveUrl(navImages.home.GROUP),
srcHover: this._resolveUrl(navImages.home.HOVER),
srcDown: this._resolveUrl(navImages.home.DOWN)
},{
onRelease: onHomeHandler
});
var fullPage = new $.Button({
config: this._viewer.config,
tooltip: $.Strings.getString("Tooltips.FullPage"),
srcRest: this._resolveUrl(navImages.fullpage.REST),
srcGroup: this._resolveUrl(navImages.fullpage.GROUP),
srcHover: this._resolveUrl(navImages.fullpage.HOVER),
srcDown: this._resolveUrl(navImages.fullpage.DOWN)
},{
onRelease: onFullPageHandler
});
this._group = new $.ButtonGroup({
config: this._viewer.config,
buttons: [zoomIn, zoomOut, goHome, fullPage]
});
this.elmt = this._group.get_element();
this.elmt[SIGNAL] = true; // hack to get our controls to fade
this._viewer.add_open($.delegate(this, this._lightUp));
},
get_events: function() {
return this._events;
},
set_events: function(value) {
this._events = value;
},
_resolveUrl: function(url) {
return $.format("{1}", this._viewer.get_prefixUrl(), url);
},
_beginZoomingIn: function() {
this._lastZoomTime = new Date().getTime();
this._zoomFactor = this.config.zoomPerSecond;
this._zooming = true;
this._scheduleZoom();
},
_beginZoomingOut: function() {
this._lastZoomTime = new Date().getTime();
this._zoomFactor = 1.0 / this.config.zoomPerSecond;
this._zooming = true;
this._scheduleZoom();
},
_endZooming: function() {
this._zooming = false;
},
_scheduleZoom: function() {
window.setTimeout($.delegate(this, this._doZoom), 10);
},
_doZoom: function() {
if (this._zooming && this._viewer.viewport) {
var currentTime = new Date().getTime();
var deltaTime = currentTime - this._lastZoomTime;
var adjustedFactor = Math.pow(this._zoomFactor, deltaTime / 1000);
this._viewer.viewport.zoomBy(adjustedFactor);
this._viewer.viewport.applyConstraints();
this._lastZoomTime = currentTime;
this._scheduleZoom();
}
},
_doSingleZoomIn: function() {
if (this._viewer.viewport) {
this._zooming = false;
this._viewer.viewport.zoomBy(this.config.zoomPerClick / 1.0);
this._viewer.viewport.applyConstraints();
}
},
_doSingleZoomOut: function() {
if (this._viewer.viewport) {
this._zooming = false;
this._viewer.viewport.zoomBy(1.0 / this.config.zoomPerClick);
this._viewer.viewport.applyConstraints();
}
},
_lightUp: function() {
this._group.emulateEnter();
this._group.emulateExit();
},
_onHome: function() {
if (this._viewer.viewport) {
this._viewer.viewport.goHome();
}
},
_onFullPage: function() {
this._viewer.setFullPage(!this._viewer.isFullPage());
this._group.emulateExit(); // correct for no mouseout event on change
if (this._viewer.viewport) {
this._viewer.viewport.applyConstraints();
}
}
};
}( OpenSeadragon ));

139
src/openseadragon.js Normal file
View File

@ -0,0 +1,139 @@
/**
* (c) 2011 Christopher Thatcher
* (c) 2010 OpenSeadragon
* (c) 2010 CodePlex Foundation
*
* OpenSeadragon @VERSION@
* ----------------------------------------------------------------------------
*
* License: New BSD License (BSD)
* Copyright (c) 2010, OpenSeadragon
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of OpenSeadragon nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* ----------------------------------------------------------------------------
*
**/
OpenSeadragon = window.OpenSeadragon || function(){};
//TODO: remove this hack
if (!window.SIGNAL){
window.SIGNAL = "----seadragon----";
}
(function( $ ){
$.delegate = function(object, method) {
return function() {
if (arguments === undefined)
arguments = [];
return method.apply(object, arguments);
};
};
$.format = function(){
var args = arguments,
useLocale = false,
result = '',
format = args[0],
open,
close,
brace,
colonIndex,
argNumber,
argFormat,
arg,
i;
for ( i = 0; ; ) {
open = format.indexOf('{', i);
close = format.indexOf('}', i);
if ((open < 0) && (close < 0)) {
result += format.slice(i);
break;
}
if ((close > 0) && ((close < open) || (open < 0))) {
if (format.charAt(close + 1) !== '}') {
throw Error('SeadragonError: Formatted String Brace Mismatch. \n' + format );
}
result += format.slice(i, close + 1);
i = close + 2;
continue;
}
result += format.slice(i, open);
i = open + 1;
if (format.charAt(i) === '{') {
result += '{';
i++;
continue;
}
if (close < 0) {
throw Error('SeadragonError: Formatted String Brace Mismatch. \n' + format );
}
brace = format.substring(i, close);
colonIndex = brace.indexOf(':');
argNumber = parseInt((colonIndex < 0) ? brace : brace.substring(0, colonIndex), 10) + 1;
if( isNaN( argNumber ) ){
throw Error('SeadragonError: Invalid Format String\n' + format );
}
argFormat = (colonIndex < 0) ? '' : brace.substring(colonIndex + 1);
arg = args[argNumber];
if (typeof (arg) === "undefined" || arg === null) {
arg = '';
}
if (arg.toFormattedString) {
result += arg.toFormattedString(argFormat);
}
else if (useLocale && arg.localeFormat) {
result += arg.localeFormat(argFormat);
}
else if ( arg.format ) {
result += arg.format(argFormat);
}
else
result += arg.toString();
i = close + 1;
}
return result;
};
}( OpenSeadragon ));

107
src/overlay.js Normal file
View File

@ -0,0 +1,107 @@
(function( $ ){
$.Overlay = function(elmt, loc, placement) {
this.elmt = elmt;
this.scales = (loc instanceof $.Rect);
this.bounds = new $.Rect(loc.x, loc.y, loc.width, loc.height);
this.placement = loc instanceof $.Point ? placement : $.OverlayPlacement.TOP_LEFT; // rects are always top-left
this.position = new $.Point(loc.x, loc.y);
this.size = new $.Point(loc.width, loc.height);
this.style = elmt.style;
};
$.Overlay.prototype = {
adjust: function(position, size) {
switch (this.placement) {
case $.OverlayPlacement.TOP_LEFT:
break;
case $.OverlayPlacement.TOP:
position.x -= size.x / 2;
break;
case $.OverlayPlacement.TOP_RIGHT:
position.x -= size.x;
break;
case $.OverlayPlacement.RIGHT:
position.x -= size.x;
position.y -= size.y / 2;
break;
case $.OverlayPlacement.BOTTOM_RIGHT:
position.x -= size.x;
position.y -= size.y;
break;
case $.OverlayPlacement.BOTTOM:
position.x -= size.x / 2;
position.y -= size.y;
break;
case $.OverlayPlacement.BOTTOM_LEFT:
position.y -= size.y;
break;
case $.OverlayPlacement.LEFT:
position.y -= size.y / 2;
break;
case $.OverlayPlacement.CENTER:
default:
position.x -= size.x / 2;
position.y -= size.y / 2;
break;
}
},
destroy: function() {
var elmt = this.elmt;
var style = this.style;
if (elmt.parentNode) {
elmt.parentNode.removeChild(elmt);
}
style.top = "";
style.left = "";
style.position = "";
if (this.scales) {
style.width = "";
style.height = "";
}
},
drawHTML: function(container) {
var elmt = this.elmt;
var style = this.style;
var scales = this.scales;
if (elmt.parentNode != container) {
container.appendChild(elmt);
}
if (!scales) {
this.size = $.Utils.getElementSize(elmt);
}
var position = this.position;
var size = this.size;
this.adjust(position, size);
position = position.apply(Math.floor);
size = size.apply(Math.ceil);
style.left = position.x + "px";
style.top = position.y + "px";
style.position = "absolute";
if (scales) {
style.width = size.x + "px";
style.height = size.y + "px";
}
},
update: function(loc, placement) {
this.scales = (loc instanceof $.Rect);
this.bounds = new $.Rect(loc.x, loc.y, loc.width, loc.height);
this.placement = loc instanceof $.Point ?
placement : $.OverlayPlacement.TOP_LEFT; // rects are always top-left
}
};
}( OpenSeadragon ));

20
src/overlayplacement.js Normal file
View File

@ -0,0 +1,20 @@
(function( $ ){
$.OverlayPlacement = function() {
throw Error.invalidOperation();
};
$.OverlayPlacement = {
CENTER: 0,
TOP_LEFT: 1,
TOP: 2,
TOP_RIGHT: 3,
RIGHT: 4,
BOTTOM_RIGHT: 5,
BOTTOM: 6,
BOTTOM_LEFT: 7,
LEFT: 8
};
}( OpenSeadragon ));

50
src/point.js Normal file
View File

@ -0,0 +1,50 @@
(function( $ ){
$.Point=$.Point = function(x, y) {
this.x = typeof (x) == "number" ? x : 0;
this.y = typeof (y) == "number" ? y : 0;
};
$.Point.prototype = {
plus: function(point) {
return new $.Point(this.x + point.x, this.y + point.y);
},
minus: function(point) {
return new $.Point(this.x - point.x, this.y - point.y);
},
times: function(factor) {
return new $.Point(this.x * factor, this.y * factor);
},
divide: function(factor) {
return new $.Point(this.x / factor, this.y / factor);
},
negate: function() {
return new $.Point(-this.x, -this.y);
},
distanceTo: function(point) {
return Math.sqrt(Math.pow(this.x - point.x, 2) +
Math.pow(this.y - point.y, 2));
},
apply: function(func) {
return new $.Point(func(this.x), func(this.y));
},
equals: function(point) {
return (point instanceof $.Point) &&
(this.x === point.x) && (this.y === point.y);
},
toString: function() {
return "(" + this.x + "," + this.y + ")";
}
};
}( OpenSeadragon ));

120
src/profiler.js Normal file
View File

@ -0,0 +1,120 @@
(function( $ ){
$.Profiler = function() {
this._midUpdate = false;
this._numUpdates = 0;
this._lastBeginTime = null;
this._lastEndTime = null;
this._minUpdateTime = Infinity;
this._avgUpdateTime = 0;
this._maxUpdateTime = 0;
this._minIdleTime = Infinity;
this._avgIdleTime = 0;
this._maxIdleTime = 0;
};
$.Profiler.prototype = {
getAvgUpdateTime: function() {
return this._avgUpdateTime;
},
getMinUpdateTime: function() {
return this._minUpdateTime;
},
getMaxUpdateTime: function() {
return this._maxUpdateTime;
},
getAvgIdleTime: function() {
return this._avgIdleTime;
},
getMinIdleTime: function() {
return this._minIdleTime;
},
getMaxIdleTime: function() {
return this._maxIdleTime;
},
isMidUpdate: function() {
return this._midUpdate;
},
getNumUpdates: function() {
return this._numUpdates;
},
beginUpdate: function() {
if (this._midUpdate) {
this.endUpdate();
}
this._midUpdate = true;
this._lastBeginTime = new Date().getTime();
if (this._numUpdates < 1) {
return; // this is the first update
}
var time = this._lastBeginTime - this._lastEndTime;
this._avgIdleTime = (this._avgIdleTime * (this._numUpdates - 1) + time) / this._numUpdates;
if (time < this._minIdleTime) {
this._minIdleTime = time;
}
if (time > this._maxIdleTime) {
this._maxIdleTime = time;
}
},
endUpdate: function() {
if (!this._midUpdate) {
return;
}
this._lastEndTime = new Date().getTime();
this._midUpdate = false;
var time = this._lastEndTime - this._lastBeginTime;
this._numUpdates++;
this._avgUpdateTime = (this._avgUpdateTime * (this._numUpdates - 1) + time) / this._numUpdates;
if (time < this._minUpdateTime) {
this._minUpdateTime = time;
}
if (time > this._maxUpdateTime) {
this._maxUpdateTime = time;
}
},
clearProfile: function() {
this._midUpdate = false;
this._numUpdates = 0;
this._lastBeginTime = null;
this._lastEndTime = null;
this._minUpdateTime = Infinity;
this._avgUpdateTime = 0;
this._maxUpdateTime = 0;
this._minIdleTime = Infinity;
this._avgIdleTime = 0;
this._maxIdleTime = 0;
}
};
}( OpenSeadragon ));

45
src/rectangle.js Normal file
View File

@ -0,0 +1,45 @@
(function( $ ){
$.Rect = function(x, y, width, height) {
this.x = typeof (x) == "number" ? x : 0;
this.y = typeof (y) == "number" ? y : 0;
this.width = typeof (width) == "number" ? width : 0;
this.height = typeof (height) == "number" ? height : 0;
};
$.Rect.prototype = {
getAspectRatio: function() {
return this.width / this.height;
},
getTopLeft: function() {
return new $.Point(this.x, this.y);
},
getBottomRight: function() {
return new $.Point(this.x + this.width, this.y + this.height);
},
getCenter: function() {
return new $.Point(this.x + this.width / 2.0,
this.y + this.height / 2.0);
},
getSize: function() {
return new $.Point(this.width, this.height);
},
equals: function(other) {
return (other instanceof $.Rect) &&
(this.x === other.x) && (this.y === other.y) &&
(this.width === other.width) && (this.height === other.height);
},
toString: function() {
return "[" + this.x + "," + this.y + "," + this.width + "x" +
this.height + "]";
}
};
}( OpenSeadragon ));

55
src/spring.js Normal file
View File

@ -0,0 +1,55 @@
(function( $ ){
$.Spring = function(initialValue, config) {
this._currentValue = typeof (initialValue) == "number" ? initialValue : 0;
this._startValue = this._currentValue;
this._targetValue = this._currentValue;
this.config = config;
this._currentTime = new Date().getTime(); // always work in milliseconds
this._startTime = this._currentTime;
this._targetTime = this._currentTime;
};
$.Spring.prototype = {
_transform: function(x) {
var s = this.config.springStiffness;
return (1.0 - Math.exp(-x * s)) / (1.0 - Math.exp(-s));
},
getCurrent: function() {
return this._currentValue;
},
getTarget: function() {
return this._targetValue;
},
resetTo: function(target) {
this._targetValue = target;
this._targetTime = this._currentTime;
this._startValue = this._targetValue;
this._startTime = this._targetTime;
},
springTo: function(target) {
this._startValue = this._currentValue;
this._startTime = this._currentTime;
this._targetValue = target;
this._targetTime = this._startTime + 1000 * this.config.animationTime;
},
shiftBy: function(delta) {
this._startValue += delta;
this._targetValue += delta;
},
update: function() {
this._currentTime = new Date().getTime();
this._currentValue = (this._currentTime >= this._targetTime) ? this._targetValue :
this._startValue + (this._targetValue - this._startValue) *
this._transform((this._currentTime - this._startTime) / (this._targetTime - this._startTime));
}
}
}( OpenSeadragon ));

66
src/strings.js Normal file
View File

@ -0,0 +1,66 @@
(function( $ ){
$.Strings = {
Errors: {
Failure: "Sorry, but Seadragon Ajax can't run on your browser!\n" +
"Please try using IE 7 or Firefox 3.\n",
Dzc: "Sorry, we don't support Deep Zoom Collections!",
Dzi: "Hmm, this doesn't appear to be a valid Deep Zoom Image.",
Xml: "Hmm, this doesn't appear to be a valid Deep Zoom Image.",
Empty: "You asked us to open nothing, so we did just that.",
ImageFormat: "Sorry, we don't support {0}-based Deep Zoom Images.",
Security: "It looks like a security restriction stopped us from " +
"loading this Deep Zoom Image.",
Status: "This space unintentionally left blank ({0} {1}).",
Unknown: "Whoops, something inexplicably went wrong. Sorry!"
},
Messages: {
Loading: "Loading..."
},
Tooltips: {
FullPage: "Toggle full page",
Home: "Go home",
ZoomIn: "Zoom in",
ZoomOut: "Zoom out"
},
getString: function(prop) {
var props = prop.split('.');
var string = $.Strings;
for (var i = 0; i < props.length; i++) {
string = string[props[i]] || {}; // in case not a subproperty
}
if (typeof (string) != "string") {
string = "";
}
var args = arguments;
return string.replace(/\{\d+\}/g, function(capture) {
var i = parseInt(capture.match(/\d+/)) + 1;
return i < args.length ? args[i] : "";
});
},
setString: function(prop, value) {
var props = prop.split('.');
var container = $.Strings;
for (var i = 0; i < props.length - 1; i++) {
if (!container[props[i]]) {
container[props[i]] = {};
}
container = container[props[i]];
}
container[props[i]] = value;
}
};
$.Strings = $.Strings;
}( OpenSeadragon ));

94
src/tile.js Normal file
View File

@ -0,0 +1,94 @@
(function( $ ){
$.Tile = function(level, x, y, bounds, exists, url) {
this.level = level;
this.x = x;
this.y = y;
this.bounds = bounds; // where this tile fits, in normalized coordinates
this.exists = exists; // part of sparse image? tile hasn't failed to load?
this.loaded = false; // is this tile loaded?
this.loading = false; // or is this tile loading?
this.elmt = null; // the HTML element for this tile
this.image = null; // the Image object for this tile
this.url = url; // the URL of this tile's image
this.style = null; // alias of this.elmt.style
this.position = null; // this tile's position on screen, in pixels
this.size = null; // this tile's size on screen, in pixels
this.blendStart = null; // the start time of this tile's blending
this.opacity = null; // the current opacity this tile should be
this.distance = null; // the distance of this tile to the viewport center
this.visibility = null; // the visibility score of this tile
this.beingDrawn = false; // whether this tile is currently being drawn
this.lastTouchTime = 0; // the time that tile was last touched
};
$.Tile.prototype = {
toString: function() {
return this.level + "/" + this.x + "_" + this.y;
},
drawHTML: function(container) {
if (!this.loaded) {
$.Debug.error("Attempting to draw tile " + this.toString() +
" when it's not yet loaded.");
return;
}
if (!this.elmt) {
this.elmt = $.Utils.makeNeutralElement("img");
this.elmt.src = this.url;
this.style = this.elmt.style;
this.style.position = "absolute";
this.style.msInterpolationMode = "nearest-neighbor";
}
var elmt = this.elmt;
var style = this.style;
var position = this.position.apply(Math.floor);
var size = this.size.apply(Math.ceil);
if (elmt.parentNode != container) {
container.appendChild(elmt);
}
style.left = position.x + "px";
style.top = position.y + "px";
style.width = size.x + "px";
style.height = size.y + "px";
$.Utils.setElementOpacity(elmt, this.opacity);
},
drawCanvas: function(context) {
if (!this.loaded) {
$.Debug.error("Attempting to draw tile " + this.toString() +
" when it's not yet loaded.");
return;
}
var position = this.position;
var size = this.size;
context.globalAlpha = this.opacity;
context.drawImage(this.image, position.x, position.y, size.x, size.y);
},
unload: function() {
if (this.elmt && this.elmt.parentNode) {
this.elmt.parentNode.removeChild(this.elmt);
}
this.elmt = null;
this.image = null;
this.loaded = false;
this.loading = false;
}
};
}( OpenSeadragon ));

71
src/tilesource.js Normal file
View File

@ -0,0 +1,71 @@
(function( $ ){
$.TileSource = function(width, height, tileSize, tileOverlap, minLevel, maxLevel) {
this.aspectRatio = width / height;
this.dimensions = new $.Point(width, height);
this.minLevel = minLevel ? minLevel : 0;
this.maxLevel = maxLevel ? maxLevel :
Math.ceil(Math.log(Math.max(width, height)) / Math.log(2));
this.tileSize = tileSize ? tileSize : 0;
this.tileOverlap = tileOverlap ? tileOverlap : 0;
};
$.TileSource.prototype = {
getLevelScale: function(level) {
return 1 / (1 << (this.maxLevel - level));
},
getNumTiles: function(level) {
var scale = this.getLevelScale(level);
var x = Math.ceil(scale * this.dimensions.x / this.tileSize);
var y = Math.ceil(scale * this.dimensions.y / this.tileSize);
return new $.Point(x, y);
},
getPixelRatio: function(level) {
var imageSizeScaled = this.dimensions.times(this.getLevelScale(level));
var rx = 1.0 / imageSizeScaled.x;
var ry = 1.0 / imageSizeScaled.y;
return new $.Point(rx, ry);
},
getTileAtPoint: function(level, point) {
var pixel = point.times(this.dimensions.x).times(this.getLevelScale(level));
var tx = Math.floor(pixel.x / this.tileSize);
var ty = Math.floor(pixel.y / this.tileSize);
return new $.Point(tx, ty);
},
getTileBounds: function(level, x, y) {
var dimensionsScaled = this.dimensions.times(this.getLevelScale(level));
var px = (x === 0) ? 0 : this.tileSize * x - this.tileOverlap;
var py = (y === 0) ? 0 : this.tileSize * y - this.tileOverlap;
var sx = this.tileSize + (x === 0 ? 1 : 2) * this.tileOverlap;
var sy = this.tileSize + (y === 0 ? 1 : 2) * this.tileOverlap;
sx = Math.min(sx, dimensionsScaled.x - px);
sy = Math.min(sy, dimensionsScaled.y - py);
var scale = 1.0 / dimensionsScaled.x;
return new $.Rect(px * scale, py * scale, sx * scale, sy * scale);
},
getTileUrl: function(level, x, y) {
throw new Error("Method not implemented.");
},
tileExists: function(level, x, y) {
var numTiles = this.getNumTiles(level);
return level >= this.minLevel && level <= this.maxLevel &&
x >= 0 && y >= 0 && x < numTiles.x && y < numTiles.y;
}
};
}( OpenSeadragon ));

502
src/utils.js Normal file
View File

@ -0,0 +1,502 @@
OpenSeadragon.Utils = OpenSeadragon.Utils || function(){};
(function( $ ){
$.Utils = function() {
var Browser = {
UNKNOWN: 0,
IE: 1,
FIREFOX: 2,
SAFARI: 3,
CHROME: 4,
OPERA: 5
};
$.Browser = Browser;
var self = this;
var arrActiveX = ["Msxml2.XMLHTTP", "Msxml3.XMLHTTP", "Microsoft.XMLHTTP"];
var fileFormats = {
"bmp": false,
"jpeg": true,
"jpg": true,
"png": true,
"tif": false,
"wdp": false
};
var browser = Browser.UNKNOWN;
var browserVersion = 0;
var badAlphaBrowser = false; // updated in constructor
var urlParams = {};
(function() {
var app = navigator.appName;
var ver = navigator.appVersion;
var ua = navigator.userAgent;
if (app == "Microsoft Internet Explorer" &&
!!window.attachEvent && !!window.ActiveXObject) {
var ieOffset = ua.indexOf("MSIE");
browser = Browser.IE;
browserVersion = parseFloat(
ua.substring(ieOffset + 5, ua.indexOf(";", ieOffset)));
} else if (app == "Netscape" && !!window.addEventListener) {
var ffOffset = ua.indexOf("Firefox");
var saOffset = ua.indexOf("Safari");
var chOffset = ua.indexOf("Chrome");
if (ffOffset >= 0) {
browser = Browser.FIREFOX;
browserVersion = parseFloat(ua.substring(ffOffset + 8));
} else if (saOffset >= 0) {
var slash = ua.substring(0, saOffset).lastIndexOf("/");
browser = (chOffset >= 0) ? Browser.CHROME : Browser.SAFARI;
browserVersion = parseFloat(ua.substring(slash + 1, saOffset));
}
} else if (app == "Opera" && !!window.opera && !!window.attachEvent) {
browser = Browser.OPERA;
browserVersion = parseFloat(ver);
}
var query = window.location.search.substring(1); // ignore '?'
var parts = query.split('&');
for (var i = 0; i < parts.length; i++) {
var part = parts[i];
var sep = part.indexOf('=');
if (sep > 0) {
urlParams[part.substring(0, sep)] =
decodeURIComponent(part.substring(sep + 1));
}
}
badAlphaBrowser = (browser == Browser.IE ||
(browser == Browser.CHROME && browserVersion < 2));
})();
function getOffsetParent(elmt, isFixed) {
if (isFixed && elmt != document.body) {
return document.body;
} else {
return elmt.offsetParent;
}
}
this.getBrowser = function() {
return browser;
};
this.getBrowserVersion = function() {
return browserVersion;
};
this.getElement = function(elmt) {
if (typeof (elmt) == "string") {
elmt = document.getElementById(elmt);
}
return elmt;
};
this.getElementPosition = function(elmt) {
var elmt = self.getElement(elmt);
var result = new $.Point();
var isFixed = self.getElementStyle(elmt).position == "fixed";
var offsetParent = getOffsetParent(elmt, isFixed);
while (offsetParent) {
result.x += elmt.offsetLeft;
result.y += elmt.offsetTop;
if (isFixed) {
result = result.plus(self.getPageScroll());
}
elmt = offsetParent;
isFixed = self.getElementStyle(elmt).position == "fixed";
offsetParent = getOffsetParent(elmt, isFixed);
}
return result;
};
this.getElementSize = function(elmt) {
var elmt = self.getElement(elmt);
return new $.Point(elmt.clientWidth, elmt.clientHeight);
};
this.getElementStyle = function(elmt) {
var elmt = self.getElement(elmt);
if (elmt.currentStyle) {
return elmt.currentStyle;
} else if (window.getComputedStyle) {
return window.getComputedStyle(elmt, "");
} else {
$.Debug.fail("Unknown element style, no known technique.");
}
};
this.getEvent = function(event) {
return event ? event : window.event;
};
this.getMousePosition = function(event) {
var event = self.getEvent(event);
var result = new $.Point();
if (typeof (event.pageX) == "number") {
result.x = event.pageX;
result.y = event.pageY;
} else if (typeof (event.clientX) == "number") {
result.x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
result.y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
} else {
$.Debug.fail("Unknown event mouse position, no known technique.");
}
return result;
};
this.getPageScroll = function() {
var result = new $.Point();
var docElmt = document.documentElement || {};
var body = document.body || {};
if (typeof (window.pageXOffset) == "number") {
result.x = window.pageXOffset;
result.y = window.pageYOffset;
} else if (body.scrollLeft || body.scrollTop) {
result.x = body.scrollLeft;
result.y = body.scrollTop;
} else if (docElmt.scrollLeft || docElmt.scrollTop) {
result.x = docElmt.scrollLeft;
result.y = docElmt.scrollTop;
}
return result;
};
this.getWindowSize = function() {
var result = new $.Point();
var docElmt = document.documentElement || {};
var body = document.body || {};
if (typeof (window.innerWidth) == 'number') {
result.x = window.innerWidth;
result.y = window.innerHeight;
} else if (docElmt.clientWidth || docElmt.clientHeight) {
result.x = docElmt.clientWidth;
result.y = docElmt.clientHeight;
} else if (body.clientWidth || body.clientHeight) {
result.x = body.clientWidth;
result.y = body.clientHeight;
} else {
$.Debug.fail("Unknown window size, no known technique.");
}
return result;
};
this.imageFormatSupported = function(ext) {
var ext = ext ? ext : "";
return !!fileFormats[ext.toLowerCase()];
};
this.makeCenteredNode = function(elmt) {
var elmt = $.Utils.getElement(elmt);
var div = self.makeNeutralElement("div");
var html = [];
html.push('<div style="display:table; height:100%; width:100%;');
html.push('border:none; margin:0px; padding:0px;'); // neutralizing
html.push('#position:relative; overflow:hidden; text-align:left;">');
html.push('<div style="#position:absolute; #top:50%; width:100%; ');
html.push('border:none; margin:0px; padding:0px;'); // neutralizing
html.push('display:table-cell; vertical-align:middle;">');
html.push('<div style="#position:relative; #top:-50%; width:100%; ');
html.push('border:none; margin:0px; padding:0px;'); // neutralizing
html.push('text-align:center;"></div></div></div>');
div.innerHTML = html.join('');
div = div.firstChild;
var innerDiv = div;
var innerDivs = div.getElementsByTagName("div");
while (innerDivs.length > 0) {
innerDiv = innerDivs[0];
innerDivs = innerDiv.getElementsByTagName("div");
}
innerDiv.appendChild(elmt);
return div;
};
this.makeNeutralElement = function(tagName) {
var elmt = document.createElement(tagName);
var style = elmt.style;
style.background = "transparent none";
style.border = "none";
style.margin = "0px";
style.padding = "0px";
style.position = "static";
return elmt;
};
this.makeTransparentImage = function(src) {
var img = self.makeNeutralElement("img");
var elmt = null;
if (browser == Browser.IE && browserVersion < 7) {
elmt = self.makeNeutralElement("span");
elmt.style.display = "inline-block";
img.onload = function() {
elmt.style.width = elmt.style.width || img.width + "px";
elmt.style.height = elmt.style.height || img.height + "px";
img.onload = null;
img = null; // to prevent memory leaks in IE
};
img.src = src;
elmt.style.filter =
"progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" +
src + "', sizingMethod='scale')";
} else {
elmt = img;
elmt.src = src;
}
return elmt;
};
this.setElementOpacity = function(elmt, opacity, usesAlpha) {
var elmt = self.getElement(elmt);
if (usesAlpha && badAlphaBrowser) {
opacity = Math.round(opacity);
}
if (opacity < 1) {
elmt.style.opacity = opacity;
} else {
elmt.style.opacity = "";
}
if (opacity == 1) {
var prevFilter = elmt.style.filter || "";
elmt.style.filter = prevFilter.replace(/alpha\(.*?\)/g, "");
return;
}
var ieOpacity = Math.round(100 * opacity);
var ieFilter = " alpha(opacity=" + ieOpacity + ") ";
try {
if (elmt.filters && elmt.filters.alpha) {
elmt.filters.alpha.opacity = ieOpacity;
} else {
elmt.style.filter += ieFilter;
}
} catch (e) {
elmt.style.filter += ieFilter;
}
};
this.addEvent = function(elmt, eventName, handler, useCapture) {
var elmt = self.getElement(elmt);
if (elmt.addEventListener) {
elmt.addEventListener(eventName, handler, useCapture);
} else if (elmt.attachEvent) {
elmt.attachEvent("on" + eventName, handler);
if (useCapture && elmt.setCapture) {
elmt.setCapture();
}
} else {
$.Debug.fail("Unable to attach event handler, no known technique.");
}
};
this.removeEvent = function(elmt, eventName, handler, useCapture) {
var elmt = self.getElement(elmt);
if (elmt.removeEventListener) {
elmt.removeEventListener(eventName, handler, useCapture);
} else if (elmt.detachEvent) {
elmt.detachEvent("on" + eventName, handler);
if (useCapture && elmt.releaseCapture) {
elmt.releaseCapture();
}
} else {
$.Debug.fail("Unable to detach event handler, no known technique.");
}
};
this.cancelEvent = function(event) {
var event = self.getEvent(event);
if (event.preventDefault) {
event.preventDefault(); // W3C for preventing default
}
event.cancel = true; // legacy for preventing default
event.returnValue = false; // IE for preventing default
};
this.stopEvent = function(event) {
var event = self.getEvent(event);
if (event.stopPropagation) {
event.stopPropagation(); // W3C for stopping propagation
}
event.cancelBubble = true; // IE for stopping propagation
};
this.createCallback = function(object, method) {
var initialArgs = [];
for (var i = 2; i < arguments.length; i++) {
initialArgs.push(arguments[i]);
}
return function() {
var args = initialArgs.concat([]);
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
return method.apply(object, args);
};
};
this.getUrlParameter = function(key) {
var value = urlParams[key];
return value ? value : null;
};
this.makeAjaxRequest = function(url, callback) {
var async = typeof (callback) == "function";
var req = null;
if (async) {
var actual = callback;
var callback = function() {
window.setTimeout($.Utils.createCallback(null, actual, req), 1);
};
}
if (window.ActiveXObject) {
for (var i = 0; i < arrActiveX.length; i++) {
try {
req = new ActiveXObject(arrActiveX[i]);
break;
} catch (e) {
continue;
}
}
} else if (window.XMLHttpRequest) {
req = new XMLHttpRequest();
}
if (!req) {
$.Debug.fail("Browser doesn't support XMLHttpRequest.");
}
if (async) {
req.onreadystatechange = function() {
if (req.readyState == 4) {
req.onreadystatechange = new function() { };
callback();
}
};
}
try {
req.open("GET", url, async);
req.send(null);
} catch (e) {
$.Debug.log(e.name + " while making AJAX request: " + e.message);
req.onreadystatechange = null;
req = null;
if (async) {
callback();
}
}
return async ? null : req;
};
this.parseXml = function(string) {
var xmlDoc = null;
if (window.ActiveXObject) {
try {
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = false;
xmlDoc.loadXML(string);
} catch (e) {
$.Debug.log(e.name + " while parsing XML (ActiveX): " + e.message);
}
} else if (window.DOMParser) {
try {
var parser = new DOMParser();
xmlDoc = parser.parseFromString(string, "text/xml");
} catch (e) {
$.Debug.log(e.name + " while parsing XML (DOMParser): " + e.message);
}
} else {
$.Debug.fail("Browser doesn't support XML DOM.");
}
return xmlDoc;
};
};
//Start Thatcher - Remove Singleton pattern in favor of object literals
// TODO
$.Utils = new $.Utils();
//End Thatcher
}( OpenSeadragon ));

805
src/viewer.js Normal file
View File

@ -0,0 +1,805 @@
(function( $ ){
$.Viewer = function(element, xmlPath, prefixUrl, controls, overlays, overlayControls) {
this.config = new $.Config();
this._prefixUrl = prefixUrl ? prefixUrl : "";
this._element = document.getElementById(element);
this._controls = controls ? controls : [];
this._customControls = [];
this._overlays = overlays ? overlays : [];
this._overlayControls = overlayControls ? overlayControls : [];
this._container = null;
this._canvas = null;
this._controlsTL = null;
this._controlsTR = null;
this._controlsBR = null;
this._controlsBL = null;
this._bodyWidth = null;
this._bodyHeight = null;
this._bodyOverflow = null;
this._docOverflow = null;
this._fsBoundsDelta = null;
this._prevContainerSize = null;
this._lastOpenStartTime = 0;
this._lastOpenEndTime = 0;
this._animating = false;
this._forceRedraw = false;
this._mouseInside = false;
this._xmlPath = xmlPath ? xmlPath : undefined;
this.source = null;
this.drawer = null;
this.viewport = null;
this.profiler = null;
this.initialize();
};
$.Viewer.prototype = {
initialize: function () {
this._events = new $.EventHandlerList();
this._container = $.Utils.makeNeutralElement("div");
this._canvas = $.Utils.makeNeutralElement("div");
this._controlsTL = $.Utils.makeNeutralElement("div");
this._controlsTR = $.Utils.makeNeutralElement("div");
this._controlsBR = $.Utils.makeNeutralElement("div");
this._controlsBL = $.Utils.makeNeutralElement("div");
var innerTracker = new $.MouseTracker(this._canvas, this.config.clickTimeThreshold, this.config.clickDistThreshold);
var outerTracker = new $.MouseTracker(this._container, this.config.clickTimeThreshold, this.config.clickDistThreshold);
this._bodyWidth = document.body.style.width;
this._bodyHeight = document.body.style.height;
this._bodyOverflow = document.body.style.overflow;
this._docOverflow = document.documentElement.style.overflow;
this._fsBoundsDelta = new $.Point(1, 1);
var canvasStyle = this._canvas.style;
var containerStyle = this._container.style;
var controlsTLStyle = this._controlsTL.style;
var controlsTRStyle = this._controlsTR.style;
var controlsBRStyle = this._controlsBR.style;
var controlsBLStyle = this._controlsBL.style;
containerStyle.width = "100%";
containerStyle.height = "100%";
containerStyle.position = "relative";
containerStyle.left = "0px";
containerStyle.top = "0px";
containerStyle.textAlign = "left"; // needed to protect against
canvasStyle.width = "100%";
canvasStyle.height = "100%";
canvasStyle.overflow = "hidden";
canvasStyle.position = "absolute";
canvasStyle.top = "0px";
canvasStyle.left = "0px";
controlsTLStyle.position = controlsTRStyle.position =
controlsBRStyle.position = controlsBLStyle.position =
"absolute";
controlsTLStyle.top = controlsTRStyle.top = "0px";
controlsTLStyle.left = controlsBLStyle.left = "0px";
controlsTRStyle.right = controlsBRStyle.right = "0px";
controlsBLStyle.bottom = controlsBRStyle.bottom = "0px";
innerTracker.clickHandler = $.delegate(this, this._onCanvasClick);
innerTracker.dragHandler = $.delegate(this, this._onCanvasDrag);
innerTracker.releaseHandler = $.delegate(this, this._onCanvasRelease);
innerTracker.scrollHandler = $.delegate(this, this._onCanvasScroll);
innerTracker.setTracking(true); // default state
if (this.get_showNavigationControl()) {
navControl = (new $.NavControl(this)).elmt;
navControl.style.marginRight = "4px";
navControl.style.marginBottom = "4px";
this.addControl(navControl, $.ControlAnchor.BOTTOM_RIGHT);
}
for (var i = 0; i < this._customControls.length; i++) {
this.addControl(this._customControls[i].id, this._customControls[i].anchor);
}
outerTracker.enterHandler = $.delegate(this, this._onContainerEnter);
outerTracker.exitHandler = $.delegate(this, this._onContainerExit);
outerTracker.releaseHandler = $.delegate(this, this._onContainerRelease);
outerTracker.setTracking(true); // always tracking
window.setTimeout($.delegate(this, this._beginControlsAutoHide), 1); // initial fade out
this._container.appendChild(this._canvas);
this._container.appendChild(this._controlsTL);
this._container.appendChild(this._controlsTR);
this._container.appendChild(this._controlsBR);
this._container.appendChild(this._controlsBL);
this.get_element().appendChild(this._container);
if (this._xmlPath)
this.openDzi(this._xmlPath);
},
get_events: function get_events() {
return this._events;
},
_raiseEvent: function (eventName, eventArgs) {
var handler = this.get_events().getHandler(eventName);
if (handler) {
if (!eventArgs) {
eventArgs = new Object(); // Sys.EventArgs.Empty;
}
handler(this, eventArgs);
}
},
_beginControlsAutoHide: function () {
if (!this.config.autoHideControls) {
return;
}
this._controlsShouldFade = true;
this._controlsFadeBeginTime = new Date().getTime() + this._controlsFadeDelay;
window.setTimeout($.delegate(this, this._scheduleControlsFade), this._controlsFadeDelay);
},
_scheduleControlsFade: function () {
window.setTimeout($.delegate(this, this._updateControlsFade), 20);
},
_updateControlsFade: function () {
if (this._controlsShouldFade) {
var currentTime = new Date().getTime();
var deltaTime = currentTime - this._controlsFadeBeginTime;
var opacity = 1.0 - deltaTime / this._controlsFadeLength;
opacity = Math.min(1.0, opacity);
opacity = Math.max(0.0, opacity);
for (var i = this._controls.length - 1; i >= 0; i--) {
this._controls[i].setOpacity(opacity);
}
if (opacity > 0) {
this._scheduleControlsFade(); // fade again
}
}
},
_onCanvasClick: function (tracker, position, quick, shift) {
if (this.viewport && quick) { // ignore clicks where mouse moved
var zoomPerClick = this.config.zoomPerClick;
var factor = shift ? 1.0 / zoomPerClick : zoomPerClick;
this.viewport.zoomBy(factor, this.viewport.pointFromPixel(position, true));
this.viewport.applyConstraints();
}
},
_onCanvasDrag: function (tracker, position, delta, shift) {
if (this.viewport) {
this.viewport.panBy(this.viewport.deltaPointsFromPixels(delta.negate()));
}
},
_onCanvasRelease: function (tracker, position, insideElmtPress, insideElmtRelease) {
if (insideElmtPress && this.viewport) {
this.viewport.applyConstraints();
}
},
_onCanvasScroll: function (tracker, position, scroll, shift) {
if (this.viewport) {
var factor = Math.pow(this.config.zoomPerScroll,scroll);
this.viewport.zoomBy(factor, this.viewport.pointFromPixel(position, true));
this.viewport.applyConstraints();
}
},
_onContainerExit: function (tracker, position, buttonDownElmt, buttonDownAny) {
if (!buttonDownElmt) {
this._mouseInside = false;
if (!this._animating) {
this._beginControlsAutoHide();
}
}
},
_onContainerRelease: function (tracker, position, insideElmtPress, insideElmtRelease) {
if (!insideElmtRelease) {
this._mouseInside = false;
if (!this._animating) {
this._beginControlsAutoHide();
}
}
},
_getControlIndex: function (elmt) {
for (var i = this._controls.length - 1; i >= 0; i--) {
if (this._controls[i].elmt == elmt) {
return i;
}
}
return -1;
},
_abortControlsAutoHide: function () {
this._controlsShouldFade = false;
for (var i = this._controls.length - 1; i >= 0; i--) {
this._controls[i].setOpacity(1.0);
}
},
_onContainerEnter: function (tracker, position, buttonDownElmt, buttonDownAny) {
this._mouseInside = true;
this._abortControlsAutoHide();
},
_updateOnce: function () {
if (!this.source) {
return;
}
this.profiler.beginUpdate();
var containerSize = $.Utils.getElementSize(this._container);
if (!containerSize.equals(this._prevContainerSize)) {
this.viewport.resize(containerSize, true); // maintain image position
this._prevContainerSize = containerSize;
this._raiseEvent("resize", this);
}
var animated = this.viewport.update();
if (!this._animating && animated) {
this._raiseEvent("animationstart", self);
this._abortControlsAutoHide();
}
if (animated) {
this.drawer.update();
this._raiseEvent("animation", self);
} else if (this._forceRedraw || this.drawer.needsUpdate()) {
this.drawer.update();
this._forceRedraw = false;
} else {
this.drawer.idle();
}
if (this._animating && !animated) {
this._raiseEvent("animationfinish", this);
if (!this._mouseInside) {
this._beginControlsAutoHide();
}
}
this._animating = animated;
this.profiler.endUpdate();
},
_onClose: function () {
this.source = null;
this.viewport = null;
this.drawer = null;
this.profiler = null;
this._canvas.innerHTML = "";
},
_beforeOpen: function () {
if (this.source) {
this._onClose();
}
this._lastOpenStartTime = new Date().getTime(); // to ignore earlier opens
window.setTimeout($.delegate(this, function () {
if (this._lastOpenStartTime > this._lastOpenEndTime) {
this._setMessage($.Strings.getString("Messages.Loading"));
}
}), 2000);
return this._lastOpenStartTime;
},
_setMessage: function (message) {
var textNode = document.createTextNode(message);
this._canvas.innerHTML = "";
this._canvas.appendChild($.Utils.makeCenteredNode(textNode));
var textStyle = textNode.parentNode.style;
textStyle.color = "white";
textStyle.fontFamily = "verdana";
textStyle.fontSize = "13px";
textStyle.fontSizeAdjust = "none";
textStyle.fontStyle = "normal";
textStyle.fontStretch = "normal";
textStyle.fontVariant = "normal";
textStyle.fontWeight = "normal";
textStyle.lineHeight = "1em";
textStyle.textAlign = "center";
textStyle.textDecoration = "none";
},
_onOpen: function (time, _source, error) {
this._lastOpenEndTime = new Date().getTime();
if (time < this._lastOpenStartTime) {
$.Debug.log("Ignoring out-of-date open.");
this._raiseEvent("ignore");
return;
} else if (!_source) {
this._setMessage(error);
this._raiseEvent("error");
return;
}
this._canvas.innerHTML = "";
this._prevContainerSize = $.Utils.getElementSize(this._container);
this.source = _source;
this.viewport = new $.Viewport(this._prevContainerSize, this.source.dimensions, this.config);
this.drawer = new $.Drawer(this.source, this.viewport, this._canvas);
this.profiler = new $.Profiler();
this._animating = false;
this._forceRedraw = true;
this._scheduleUpdate(this._updateMulti);
for (var i = 0; i < this._overlayControls.length; i++) {
var overlay = this._overlayControls[i];
if (overlay.point != null) {
this.drawer.addOverlay(overlay.id, new $.Point(overlay.point.X, overlay.point.Y), $.OverlayPlacement.TOP_LEFT);
}
else {
this.drawer.addOverlay(overlay.id, new $.Rect(overlay.rect.Point.X, overlay.rect.Point.Y, overlay.rect.Width, overlay.rect.Height), overlay.placement);
}
}
this._raiseEvent("open");
},
_scheduleUpdate: function (updateFunc, prevUpdateTime) {
if (this._animating) {
return window.setTimeout($.delegate(this, updateFunc), 1);
}
var currentTime = new Date().getTime();
var prevUpdateTime = prevUpdateTime ? prevUpdateTime : currentTime;
var targetTime = prevUpdateTime + 1000 / 60; // 60 fps ideal
var deltaTime = Math.max(1, targetTime - currentTime);
return window.setTimeout($.delegate(this, updateFunc), deltaTime);
},
_updateMulti: function () {
if (!this.source) {
return;
}
var beginTime = new Date().getTime();
this._updateOnce();
this._scheduleUpdate(arguments.callee, beginTime);
},
_updateOnce: function () {
if (!this.source) {
return;
}
this.profiler.beginUpdate();
var containerSize = $.Utils.getElementSize(this._container);
if (!containerSize.equals(this._prevContainerSize)) {
this.viewport.resize(containerSize, true); // maintain image position
this._prevContainerSize = containerSize;
this._raiseEvent("resize");
}
var animated = this.viewport.update();
if (!this._animating && animated) {
this._raiseEvent("animationstart");
this._abortControlsAutoHide();
}
if (animated) {
this.drawer.update();
this._raiseEvent("animation");
} else if (this._forceRedraw || this.drawer.needsUpdate()) {
this.drawer.update();
this._forceRedraw = false;
} else {
this.drawer.idle();
}
if (this._animating && !animated) {
this._raiseEvent("animationfinish");
if (!this._mouseInside) {
this._beginControlsAutoHide();
}
}
this._animating = animated;
this.profiler.endUpdate();
},
getNavControl: function () {
return this._navControl;
},
get_element: function () {
return this._element;
},
get_xmlPath: function () {
return this._xmlPath;
},
set_xmlPath: function (value) {
this._xmlPath = value;
},
get_debugMode: function () {
return this.config.debugMode;
},
set_debugMode: function (value) {
this.config.debugMode = value;
},
get_animationTime: function () {
return this.config.animationTime;
},
set_animationTime: function (value) {
this.config.animationTime = value;
},
get_blendTime: function () {
return this.config.blendTime;
},
set_blendTime: function (value) {
this.config.blendTime = value;
},
get_alwaysBlend: function () {
return this.config.alwaysBlend;
},
set_alwaysBlend: function (value) {
this.config.alwaysBlend = value;
},
get_autoHideControls: function () {
return this.config.autoHideControls;
},
set_autoHideControls: function (value) {
this.config.autoHideControls = value;
},
get_immediateRender: function () {
return this.config.immediateRender;
},
set_immediateRender: function (value) {
this.config.immediateRender = value;
},
get_wrapHorizontal: function () {
return this.config.wrapHorizontal;
},
set_wrapHorizontal: function (value) {
this.config.wrapHorizontal = value;
},
get_wrapVertical: function () {
return this.config.wrapVertical;
},
set_wrapVertical: function (value) {
this.config.wrapVertical = value;
},
get_minZoomImageRatio: function () {
return this.config.minZoomImageRatio;
},
set_minZoomImageRatio: function (value) {
this.config.minZoomImageRatio = value;
},
get_maxZoomPixelRatio: function () {
return this.config.maxZoomPixelRatio;
},
set_maxZoomPixelRatio: function (value) {
this.config.maxZoomPixelRatio = value;
},
get_visibilityRatio: function () {
return this.config.visibilityRatio;
},
set_visibilityRatio: function (value) {
this.config.visibilityRatio = value;
},
get_springStiffness: function () {
return this.config.springStiffness;
},
set_springStiffness: function (value) {
this.config.springStiffness = value;
},
get_imageLoaderLimit: function () {
return this.config.imageLoaderLimit;
},
set_imageLoaderLimit: function (value) {
this.config.imageLoaderLimit = value;
},
get_clickTimeThreshold: function () {
return this.config.clickTimeThreshold;
},
set_clickTimeThreshold: function (value) {
this.config.clickTimeThreshold = value;
},
get_clickDistThreshold: function () {
return this.config.clickDistThreshold;
},
set_clickDistThreshold: function (value) {
this.config.clickDistThreshold = value;
},
get_zoomPerClick: function () {
return this.config.zoomPerClick;
},
set_zoomPerClick: function (value) {
this.config.zoomPerClick = value;
},
get_zoomPerSecond: function () {
return this.config.zoomPerSecond;
},
set_zoomPerSecond: function (value) {
this.config.zoomPerSecond = value;
},
get_zoomPerScroll: function () {
return this.config.zoomPerScroll;
},
set_zoomPerScroll: function (value) {
this.config.zoomPerScroll = value;
},
get_maxImageCacheCount: function () {
return this.config.maxImageCacheCount;
},
set_maxImageCacheCount: function (value) {
this.config.maxImageCacheCount = value;
},
get_showNavigationControl: function () {
return this.config.showNavigationControl;
},
set_showNavigationControl: function (value) {
this.config.showNavigationControl = value;
},
get_minPixelRatio: function () {
return this.config.minPixelRatio;
},
set_minPixelRatio: function (value) {
this.config.minPixelRatio = value;
},
get_mouseNavEnabled: function () {
return this.config.mouseNavEnabled;
},
set_mouseNavEnabled: function (value) {
this.config.mouseNavEnabled = value;
},
get_controls: function () {
return this._customControls;
},
set_controls: function (value) {
this._customControls = value;
},
get_overlays: function () {
return this._overlayControls;
},
set_overlays: function (value) {
this._overlayControls = value;
},
get_prefixUrl: function () {
return this._prefixUrl;
},
set_prefixUrl: function (value) {
this._prefixUrl = value;
},
add_open: function (handler) {
this.get_events().addHandler("open", handler);
},
remove_open: function (handler) {
this.get_events().removeHandler("open", handler);
},
add_error: function (handler) {
this.get_events().addHandler("error", handler);
},
remove_error: function (handler) {
this.get_events().removeHandler("error", handler);
},
add_ignore: function (handler) {
this.get_events().addHandler("ignore", handler);
},
remove_ignore: function (handler) {
this.get_events().removeHandler("ignore", handler);
},
add_resize: function (handler) {
this.get_events().addHandler("resize", handler);
},
remove_resize: function (handler) {
this.get_events().removeHandler("resize", handler);
},
add_animationstart: function (handler) {
this.get_events().addHandler("animationstart", handler);
},
remove_animationstart: function (handler) {
this.get_events().removeHandler("animationstart", handler);
},
add_animation: function (handler) {
this.get_events().addHandler("animation", handler);
},
remove_animation: function (handler) {
this.get_events().removeHandler("animation", handler);
},
add_animationfinish: function (handler) {
this.get_events().addHandler("animationfinish", handler);
},
remove_animationfinish: function (handler) {
this.get_events().removeHandler("animationfinish", handler);
},
addControl: function (elmt, anchor) {
var elmt = $.Utils.getElement(elmt);
if (this._getControlIndex(elmt) >= 0) {
return; // they're trying to add a duplicate control
}
var div = null;
switch (anchor) {
case $.ControlAnchor.TOP_RIGHT:
div = this._controlsTR;
elmt.style.position = "relative";
break;
case $.ControlAnchor.BOTTOM_RIGHT:
div = this._controlsBR;
elmt.style.position = "relative";
break;
case $.ControlAnchor.BOTTOM_LEFT:
div = this._controlsBL;
elmt.style.position = "relative";
break;
case $.ControlAnchor.TOP_LEFT:
div = this._controlsTL;
elmt.style.position = "relative";
break;
case $.ControlAnchor.NONE:
default:
div = this._container;
elmt.style.position = "absolute";
break;
}
this._controls.push(new $.Control(elmt, anchor, div));
elmt.style.display = "inline-block";
},
isOpen: function () {
return !!this.source;
},
openDzi: function (xmlUrl, xmlString) {
var currentTime = this._beforeOpen();
$.DziTileSourceHelper.createFromXml(xmlUrl, xmlString,
$.Utils.createCallback(null, $.delegate(this, this._onOpen), currentTime));
},
openTileSource: function (tileSource) {
var currentTime = beforeOpen();
window.setTimeout($.delegate(this, function () {
onOpen(currentTime, tileSource);
}), 1);
},
close: function () {
if (!this.source) {
return;
}
this._onClose();
},
removeControl: function (elmt) {
var elmt = $.Utils.getElement(elmt);
var i = this._getControlIndex(elmt);
if (i >= 0) {
this._controls[i].destroy();
this._controls.splice(i, 1);
}
},
clearControls: function () {
while (this._controls.length > 0) {
this._controls.pop().destroy();
}
},
isDashboardEnabled: function () {
for (var i = this._controls.length - 1; i >= 0; i--) {
if (this._controls[i].isVisible()) {
return true;
}
}
return false;
},
isFullPage: function () {
return this._container.parentNode == document.body;
},
isMouseNavEnabled: function () {
return this._innerTracker.isTracking();
},
isVisible: function () {
return this._container.style.visibility != "hidden";
},
setDashboardEnabled: function (enabled) {
for (var i = this._controls.length - 1; i >= 0; i--) {
this._controls[i].setVisible(enabled);
}
},
setFullPage: function (fullPage) {
if (fullPage == this.isFullPage()) {
return;
}
var body = document.body;
var bodyStyle = body.style;
var docStyle = document.documentElement.style;
var containerStyle = this._container.style;
var canvasStyle = this._canvas.style;
if (fullPage) {
bodyOverflow = bodyStyle.overflow;
docOverflow = docStyle.overflow;
bodyStyle.overflow = "hidden";
docStyle.overflow = "hidden";
bodyWidth = bodyStyle.width;
bodyHeight = bodyStyle.height;
bodyStyle.width = "100%";
bodyStyle.height = "100%";
canvasStyle.backgroundColor = "black";
canvasStyle.color = "white";
containerStyle.position = "fixed";
containerStyle.zIndex = "99999999";
body.appendChild(this._container);
this._prevContainerSize = $.Utils.getWindowSize();
this._onContainerEnter(); // mouse will be inside container now
} else {
bodyStyle.overflow = bodyOverflow;
docStyle.overflow = docOverflow;
bodyStyle.width = bodyWidth;
bodyStyle.height = bodyHeight;
canvasStyle.backgroundColor = "";
canvasStyle.color = "";
containerStyle.position = "relative";
containerStyle.zIndex = "";
this.get_element().appendChild(this._container);
this._prevContainerSize = $.Utils.getElementSize(this.get_element());
this._onContainerExit(); // mouse will likely be outside now
}
if (this.viewport) {
var oldBounds = this.viewport.getBounds();
this.viewport.resize(this._prevContainerSize);
var newBounds = this.viewport.getBounds();
if (fullPage) {
this._fsBoundsDelta = new $.Point(newBounds.width / oldBounds.width,
newBounds.height / oldBounds.height);
} else {
this.viewport.update();
this.viewport.zoomBy(Math.max(this._fsBoundsDelta.x, this._fsBoundsDelta.y),
null, true);
}
this._forceRedraw = true;
this._raiseEvent("resize", this);
this._updateOnce();
}
},
setMouseNavEnabled: function (enabled) {
this._innerTracker.setTracking(enabled);
},
setVisible: function (visible) {
this._container.style.visibility = visible ? "" : "hidden";
}
};
}( OpenSeadragon ));

280
src/viewport.js Normal file
View File

@ -0,0 +1,280 @@
(function( $ ){
$.Viewport = function(containerSize, contentSize, config) {
this.zoomPoint = null;
this.config = config;
this._containerSize = containerSize;
this._contentSize = contentSize;
this._contentAspect = contentSize.x / contentSize.y;
this._contentHeight = contentSize.y / contentSize.x;
this._centerSpringX = new $.Spring(0, this.config);
this._centerSpringY = new $.Spring(0, this.config);
this._zoomSpring = new $.Spring(1, this.config);
this._homeBounds = new $.Rect(0, 0, 1, this._contentHeight);
this.goHome(true);
this.update();
};
$.Viewport.prototype = {
_getHomeZoom: function() {
var aspectFactor = this._contentAspect / this.getAspectRatio();
return (aspectFactor >= 1) ? 1 : aspectFactor;
},
_getMinZoom: function() {
var homeZoom = this._getHomeZoom();
var zoom = this.config.minZoomImageRatio * homeZoom;
return Math.min(zoom, homeZoom);
},
_getMaxZoom: function() {
var zoom = this._contentSize.x * this.config.maxZoomPixelRatio / this._containerSize.x;
return Math.max(zoom, this._getHomeZoom());
},
getAspectRatio: function() {
return this._containerSize.x / this._containerSize.y;
},
getContainerSize: function() {
return new $.Point(this._containerSize.x, this._containerSize.y);
},
getBounds: function(current) {
var center = this.getCenter(current);
var width = 1.0 / this.getZoom(current);
var height = width / this.getAspectRatio();
return new $.Rect(center.x - width / 2.0, center.y - height / 2.0,
width, height);
},
getCenter: function(current) {
var centerCurrent = new $.Point(this._centerSpringX.getCurrent(),
this._centerSpringY.getCurrent());
var centerTarget = new $.Point(this._centerSpringX.getTarget(),
this._centerSpringY.getTarget());
if (current) {
return centerCurrent;
} else if (!this.zoomPoint) {
return centerTarget;
}
var oldZoomPixel = this.pixelFromPoint(this.zoomPoint, true);
var zoom = this.getZoom();
var width = 1.0 / zoom;
var height = width / this.getAspectRatio();
var bounds = new $.Rect(centerCurrent.x - width / 2.0,
centerCurrent.y - height / 2.0, width, height);
var newZoomPixel = this.zoomPoint.minus(bounds.getTopLeft()).times(this._containerSize.x / bounds.width);
var deltaZoomPixels = newZoomPixel.minus(oldZoomPixel);
var deltaZoomPoints = deltaZoomPixels.divide(this._containerSize.x * zoom);
return centerTarget.plus(deltaZoomPoints);
},
getZoom: function(current) {
if (current) {
return this._zoomSpring.getCurrent();
} else {
return this._zoomSpring.getTarget();
}
},
applyConstraints: function(immediately) {
var actualZoom = this.getZoom();
var constrainedZoom = Math.max(Math.min(actualZoom, this._getMaxZoom()), this._getMinZoom());
if (actualZoom != constrainedZoom) {
this.zoomTo(constrainedZoom, this.zoomPoint, immediately);
}
var bounds = this.getBounds();
var visibilityRatio = this.config.visibilityRatio;
var horThres = visibilityRatio * bounds.width;
var verThres = visibilityRatio * bounds.height;
var left = bounds.x + bounds.width;
var right = 1 - bounds.x;
var top = bounds.y + bounds.height;
var bottom = this._contentHeight - bounds.y;
var dx = 0;
if (this.config.wrapHorizontal) {
} else if (left < horThres) {
dx = horThres - left;
} else if (right < horThres) {
dx = right - horThres;
}
var dy = 0;
if (this.config.wrapVertical) {
} else if (top < verThres) {
dy = verThres - top;
} else if (bottom < verThres) {
dy = bottom - verThres;
}
if (dx || dy) {
bounds.x += dx;
bounds.y += dy;
this.fitBounds(bounds, immediately);
}
},
ensureVisible: function(immediately) {
this.applyConstraints(immediately);
},
fitBounds: function(bounds, immediately) {
var aspect = this.getAspectRatio();
var center = bounds.getCenter();
var newBounds = new $.Rect(bounds.x, bounds.y, bounds.width, bounds.height);
if (newBounds.getAspectRatio() >= aspect) {
newBounds.height = bounds.width / aspect;
newBounds.y = center.y - newBounds.height / 2;
} else {
newBounds.width = bounds.height * aspect;
newBounds.x = center.x - newBounds.width / 2;
}
this.panTo(this.getCenter(true), true);
this.zoomTo(this.getZoom(true), null, true);
var oldBounds = this.getBounds();
var oldZoom = this.getZoom();
var newZoom = 1.0 / newBounds.width;
if (newZoom == oldZoom || newBounds.width == oldBounds.width) {
this.panTo(center, immediately);
return;
}
var refPoint = oldBounds.getTopLeft().times(this._containerSize.x / oldBounds.width).minus(
newBounds.getTopLeft().times(this._containerSize.x / newBounds.width)).divide(
this._containerSize.x / oldBounds.width - this._containerSize.x / newBounds.width);
this.zoomTo(newZoom, refPoint, immediately);
},
goHome: function(immediately) {
var center = this.getCenter();
if (this.config.wrapHorizontal) {
center.x = (1 + (center.x % 1)) % 1;
this._centerSpringX.resetTo(center.x);
this._centerSpringX.update();
}
if (this.config.wrapVertical) {
center.y = (this._contentHeight + (center.y % this._contentHeight)) % this._contentHeight;
this._centerSpringY.resetTo(center.y);
this._centerSpringY.update();
}
this.fitBounds(this._homeBounds, immediately);
},
panBy: function(delta, immediately) {
var center = new $.Point(this._centerSpringX.getTarget(),
this._centerSpringY.getTarget());
this.panTo(center.plus(delta), immediately);
},
panTo: function(center, immediately) {
if (immediately) {
this._centerSpringX.resetTo(center.x);
this._centerSpringY.resetTo(center.y);
} else {
this._centerSpringX.springTo(center.x);
this._centerSpringY.springTo(center.y);
}
},
zoomBy: function(factor, refPoint, immediately) {
this.zoomTo(this._zoomSpring.getTarget() * factor, refPoint, immediately);
},
zoomTo: function(zoom, refPoint, immediately) {
if (immediately) {
this._zoomSpring.resetTo(zoom);
} else {
this._zoomSpring.springTo(zoom);
}
this.zoomPoint = refPoint instanceof $.Point ? refPoint : null;
},
resize: function(newContainerSize, maintain) {
var oldBounds = this.getBounds();
var newBounds = oldBounds;
var widthDeltaFactor = newContainerSize.x / this._containerSize.x;
this._containerSize = new $.Point(newContainerSize.x, newContainerSize.y);
if (maintain) {
newBounds.width = oldBounds.width * widthDeltaFactor;
newBounds.height = newBounds.width / this.getAspectRatio();
}
this.fitBounds(newBounds, true);
},
update: function() {
var oldCenterX = this._centerSpringX.getCurrent();
var oldCenterY = this._centerSpringY.getCurrent();
var oldZoom = this._zoomSpring.getCurrent();
if (this.zoomPoint) {
var oldZoomPixel = this.pixelFromPoint(this.zoomPoint, true);
}
this._zoomSpring.update();
if (this.zoomPoint && this._zoomSpring.getCurrent() != oldZoom) {
var newZoomPixel = this.pixelFromPoint(this.zoomPoint, true);
var deltaZoomPixels = newZoomPixel.minus(oldZoomPixel);
var deltaZoomPoints = this.deltaPointsFromPixels(deltaZoomPixels, true);
this._centerSpringX.shiftBy(deltaZoomPoints.x);
this._centerSpringY.shiftBy(deltaZoomPoints.y);
} else {
this.zoomPoint = null;
}
this._centerSpringX.update();
this._centerSpringY.update();
return this._centerSpringX.getCurrent() != oldCenterX ||
this._centerSpringY.getCurrent() != oldCenterY ||
this._zoomSpring.getCurrent() != oldZoom;
},
deltaPixelsFromPoints: function(deltaPoints, current) {
return deltaPoints.times(this._containerSize.x * this.getZoom(current));
},
deltaPointsFromPixels: function(deltaPixels, current) {
return deltaPixels.divide(this._containerSize.x * this.getZoom(current));
},
pixelFromPoint: function(point, current) {
var bounds = this.getBounds(current);
return point.minus(bounds.getTopLeft()).times(this._containerSize.x / bounds.width);
},
pointFromPixel: function(pixel, current) {
var bounds = this.getBounds(current);
return pixel.divide(this._containerSize.x / bounds.width).plus(bounds.getTopLeft());
}
};
}( OpenSeadragon ));