');
// the text-align:left guards against incorrect centering in IE
html.push('
');
div.innerHTML = html.join('');
div = div.firstChild;
// now add the element as a child to the inner-most div
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;
// TODO reset neutral element's style in a better way
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";
// to size span correctly, load image and get natural size,
// but don't override any user-set CSS values
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)
// images with alpha channels won't fade well, so round
opacity = Math.round(opacity);
// for CSS opacity browsers, remove opacity value if it's unnecessary
if(opacity < 1)
elmt.style.opacity = opacity;
else
elmt.style.opacity = "";
// for CSS filter browsers (IE), remove alpha filter if it's unnecessary
if(opacity == 1) {
var prevFilter = elmt.style.filter || "";
elmt.style.filter = prevFilter.replace(/alpha\(.*?\)/g, "");
// important: note the lazy star! this protects against
// multiple filters; we don't want to delete the other ones.
return;
}
var ieOpacity = Math.round(100 * opacity);
var ieFilter = " alpha(opacity=" + ieOpacity + ") ";
// check if this element has filters associated with it (IE only),
// but prevent bug where IE throws error "Member not found" sometimes.
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);
// technique from:
// http://blog.paranoidferret.com/index.php/2007/08/10/javascript-working-with-events/
if(elmt.addEventListener) {
elmt.addEventListener(eventName, handler, useCapture);
} else if(elmt.attachEvent) {
elmt.attachEvent("on" + eventName, handler);
if(useCapture && elmt.setCapture)
elmt.setCapture();
} else {
Seadragon.Debug.fail("Unable to attach event handler, no known technique.");
}
};
this.removeEvent = function(elmt, eventName, handler, useCapture) {
var elmt = self.getElement(elmt);
// technique from:
// http://blog.paranoidferret.com/index.php/2007/08/10/javascript-working-with-events/
if(elmt.removeEventListener) {
elmt.removeEventListener(eventName, handler, useCapture);
} else if(elmt.detachEvent) {
elmt.detachEvent("on" + eventName, handler);
if(useCapture && elmt.releaseCapture)
elmt.releaseCapture();
} else {
Seadragon.Debug.fail("Unable to detach event handler, no known technique.");
}
};
this.cancelEvent = function(event) {
var event = self.getEvent(event);
// technique from:
// http://blog.paranoidferret.com/index.php/2007/08/10/javascript-working-with-events/
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);
// technique from:
// http://blog.paranoidferret.com/index.php/2007/08/10/javascript-working-with-events/
if(event.stopPropagation) {
event.stopPropagation(); // W3C for stopping propagation
}
event.cancelBubble = true; // IE for stopping propagation
};
this.createCallback = function(object, method) {
// create callback args
var initialArgs = [];
for(var i = 2; i < arguments.length; i++)
initialArgs.push(arguments[i]);
// create closure to apply method
return function() {
// concatenate new args, but make a copy of initialArgs first
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(Seadragon.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)
Seadragon.Debug.fail("Browser doesn't support XMLHttpRequest.");
if(async)
req.onreadystatechange = function() {
if(req.readyState == 4) {
// prevent memory leaks by breaking circular reference now
req.onreadystatechange = new Function();
callback();
}
};
try {
req.open("GET", url, async);
req.send(null);
} catch(e) {
Seadragon.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) {
Seadragon.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) {
Seadragon.Debug.log(e.name + " while parsing XML (DOMParser): " + e.message);
}
else
Seadragon.Debug.fail("Browser doesn't support XML DOM.");
return xmlDoc;
};
};
// Seadragon.Utils is a static class, so make it singleton instance
Seadragon.Utils = new Seadragon.Utils();
Sys.Extended.UI.Seadragon.ButtonState = function() {
throw Error.invalidOperation();
};
Sys.Extended.UI.Seadragon.ButtonState.prototype = {
REST: 0,
GROUP: 1,
HOVER: 2,
DOWN: 3
};
Sys.Extended.UI.Seadragon.ButtonState.registerEnum("Sys.Extended.UI.Seadragon.ButtonState", false);
Sys.Extended.UI.Seadragon.Button = function() {
Sys.Extended.UI.Seadragon.Button.initializeBase(this);
this._tooltip = null;
this._srcRest = null;
this._srcGroup = null;
this._srcHover = null;
this._srcDown = null;
this._button = null;
this.config = null;
};
Sys.Extended.UI.Seadragon.Button.prototype = {
initialize: function() {
Sys.Extended.UI.Seadragon.Button.callBaseMethod(this, 'initialize');
this._button = Seadragon.Utils.makeNeutralElement("span");
this._currentState = Sys.Extended.UI.Seadragon.ButtonState.GROUP;
this._tracker = new Seadragon.MouseTracker(this._button, this.config.clickTimeThreshold, this.config.clickDistThreshold);
this._imgRest = Seadragon.Utils.makeTransparentImage(this._srcRest);
this._imgGroup = Seadragon.Utils.makeTransparentImage(this._srcGroup);
this._imgHover = Seadragon.Utils.makeTransparentImage(this._srcHover);
this._imgDown = Seadragon.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;
// DON'T position imgRest absolutely -- let it be inline so it fills
// up the div, sizing the div appropriately
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";
// rest and group are always visible
// FF2 is very buggy with inline-block. it squashes the button div,
// making the group-pressed states' images lower than rest. but
// apparently, clearing the "top" style fixes this. (note that this
// breaks the buttons in every other browser, so we're not clearing
// the "top" style by default...)
if(Seadragon.Utils.getBrowser() == Seadragon.Browser.FIREFOX &&
Seadragon.Utils.getBrowserVersion() < 3)
styleGroup.top = styleHover.top = styleDown.top = "";
this._tracker.enterHandler = Function.createDelegate(this, this._enterHandler);
this._tracker.exitHandler = Function.createDelegate(this, this._exitHandler);
this._tracker.pressHandler = Function.createDelegate(this, this._pressHandler);
this._tracker.releaseHandler = Function.createDelegate(this, this._releaseHandler);
this._tracker.clickHandler = Function.createDelegate(this, this._clickHandler);
this._tracker.setTracking(true);
this._outTo(Sys.Extended.UI.Seadragon.ButtonState.REST);
},
dispose: function() {
},
_scheduleFade: function() {
window.setTimeout(Function.createDelegate(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);
Seadragon.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(Function.createDelegate(this, this._scheduleFade), this._fadeDelay);
},
_stopFading: function() {
this._shouldFade = false;
Seadragon.Utils.setElementOpacity(this._imgGroup, 1.0, true);
},
_inTo: function(newState) {
if(newState >= Sys.Extended.UI.Seadragon.ButtonState.GROUP && this._currentState == Sys.Extended.UI.Seadragon.ButtonState.REST) {
this._stopFading();
this._currentState = Sys.Extended.UI.Seadragon.ButtonState.GROUP;
}
if(newState >= Sys.Extended.UI.Seadragon.ButtonState.HOVER && this._currentState == Sys.Extended.UI.Seadragon.ButtonState.GROUP) {
// important: don't explicitly say "visibility: visible".
// see note in Viewer.setVisible() for explanation.
this._imgHover.style.visibility = "";
this._currentState = Sys.Extended.UI.Seadragon.ButtonState.HOVER;
}
if(newState >= Sys.Extended.UI.Seadragon.ButtonState.DOWN && this._currentState == Sys.Extended.UI.Seadragon.ButtonState.HOVER) {
// important: don't explicitly say "visibility: visible".
// see note in Viewer.setVisible() for explanation.
this._imgDown.style.visibility = "";
this._currentState = Sys.Extended.UI.Seadragon.ButtonState.DOWN;
}
},
_outTo: function(newState) {
if(newState <= Sys.Extended.UI.Seadragon.ButtonState.HOVER && this._currentState == Sys.Extended.UI.Seadragon.ButtonState.DOWN) {
this._imgDown.style.visibility = "hidden";
this._currentState = Sys.Extended.UI.Seadragon.ButtonState.HOVER;
}
if(newState <= Sys.Extended.UI.Seadragon.ButtonState.GROUP && this._currentState == Sys.Extended.UI.Seadragon.ButtonState.HOVER) {
this._imgHover.style.visibility = "hidden";
this._currentState = Sys.Extended.UI.Seadragon.ButtonState.GROUP;
}
if(this._newState <= Sys.Extended.UI.Seadragon.ButtonState.REST && this._currentState == Sys.Extended.UI.Seadragon.ButtonState.GROUP) {
this._beginFading();
this._currentState = Sys.Extended.UI.Seadragon.ButtonState.REST;
}
},
_enterHandler: function(tracker, position, buttonDownElmt, buttonDownAny) {
if(buttonDownElmt) {
this._inTo(Sys.Extended.UI.Seadragon.ButtonState.DOWN);
this._raiseEvent("onEnter", this);
} else if(!buttonDownAny) {
this._inTo(Sys.Extended.UI.Seadragon.ButtonState.HOVER);
}
},
_exitHandler: function(tracker, position, buttonDownElmt, buttonDownAny) {
this._outTo(Sys.Extended.UI.Seadragon.ButtonState.GROUP);
if(buttonDownElmt)
this._raiseEvent("onExit", this);
},
_pressHandler: function(tracker, position) {
this._inTo(Sys.Extended.UI.Seadragon.ButtonState.DOWN);
this._raiseEvent("onPress", this);
},
_releaseHandler: function(tracker, position, insideElmtPress, insideElmtRelease) {
if(insideElmtPress && insideElmtRelease) {
this._outTo(Sys.Extended.UI.Seadragon.ButtonState.HOVER);
this._raiseEvent("onRelease", this);
} else if(insideElmtPress) {
this._outTo(Sys.Extended.UI.Seadragon.ButtonState.GROUP);
} else {
// pressed elsewhere, but released on it. if we ignored the
// enter event because a button was down, activate hover now
this._inTo(Sys.Extended.UI.Seadragon.ButtonState.HOVER);
}
},
_clickHandler: function(tracker, position, quick, shift) {
if(quick)
this._raiseEvent("onClick", this);
},
_raiseEvent: function(eventName, eventArgs) {
// Get handler for event.
var handler = this.get_events().getHandler(eventName);
if(handler) {
if(!eventArgs)
eventArgs = Sys.EventArgs.Empty;
// Fire event.
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(Sys.Extended.UI.Seadragon.ButtonState.GROUP);
},
notifyGroupExit: function() {
this._outTo(Sys.Extended.UI.Seadragon.ButtonState.REST);
}
};
Sys.Extended.UI.Seadragon.Button.registerClass('Sys.Extended.UI.Seadragon.Button', Sys.Component);
Sys.Extended.UI.Seadragon.ButtonGroup = function() {
Sys.Extended.UI.Seadragon.ButtonGroup.initializeBase(this);
this._buttons = null;
this._group = null;
this.config = null;
};
Sys.Extended.UI.Seadragon.ButtonGroup.prototype = {
initialize: function() {
Sys.Extended.UI.Seadragon.ButtonGroup.callBaseMethod(this, 'initialize');
this._group = Seadragon.Utils.makeNeutralElement("span");
var buttons = this._buttons.concat([]); // copy
var tracker = new Seadragon.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 = Function.createDelegate(this, this._enterHandler);
tracker.exitHandler = Function.createDelegate(this, this._exitHandler);
tracker.releaseHandler = Function.createDelegate(this, this._releaseHandler);
tracker.setTracking(true);
},
dispose: function() {
},
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) {
// somewhat office ribbon style -- we do this regardless of whether
// the mouse is down from elsewhere. it's a nice soft glow effect.
for(var i = 0; i < this._buttons.length; i++)
this._buttons[i].notifyGroupEnter();
},
_exitHandler: function(tracker, position, buttonDownElmt, buttonDownAny) {
if(!buttonDownElmt)
// only go to rest if the mouse isn't down from a button
for(var i = 0; i < this._buttons.length; i++)
this._buttons[i].notifyGroupExit();
},
_releaseHandler: function(tracker, position, insideElmtPress, insideElmtRelease) {
if(!insideElmtRelease)
// this means was the mouse was inside the div during press, so
// since it's no longer inside the div during release, it left
// the div. but onDivExit() ignored it since the mouse was down
// from the div, so we'll go out to rest state now.
for(var i = 0; i < this._buttons.length; i++)
this._buttons[i].notifyGroupExit();
},
emulateEnter: function() {
this._enterHandler();
},
emulateExit: function() {
this._exitHandler();
}
};
Sys.Extended.UI.Seadragon.ButtonGroup.registerClass('Sys.Extended.UI.Seadragon.ButtonGroup', Sys.Component);
Sys.Extended.UI.Seadragon.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.minZoomDimension = 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.zoomPerSecond = 2.0;
this.showNavigationControl = true;
this.maxImageCacheCount = 100;
this.minPixelRatio = 0.5;
this.mouseNavEnabled = true;
this.navImages = {
zoomIn: {
REST: Sys.Extended.UI.Images["Seadragon.ZoomIn-Rest.png"],
GROUP: Sys.Extended.UI.Images["Seadragon.ZoomIn-Grouphover.png"],
HOVER: Sys.Extended.UI.Images["Seadragon.ZoomIn-Hover.png"],
DOWN: Sys.Extended.UI.Images["Seadragon.ZoomIn-Pressed.png"]
},
zoomOut: {
REST: Sys.Extended.UI.Images["Seadragon.ZoomOut-Rest.png"],
GROUP: Sys.Extended.UI.Images["Seadragon.ZoomOut-Grouphover.png"],
HOVER: Sys.Extended.UI.Images["Seadragon.ZoomOut-Hover.png"],
DOWN: Sys.Extended.UI.Images["Seadragon.ZoomOut-Pressed.png"]
},
home: {
REST: Sys.Extended.UI.Images["Seadragon.Home-Rest.png"],
GROUP: Sys.Extended.UI.Images["Seadragon.Home-Grouphover.png"],
HOVER: Sys.Extended.UI.Images["Seadragon.Home-Hover.png"],
DOWN: Sys.Extended.UI.Images["Seadragon.Home-Pressed.png"]
},
fullpage: {
REST: Sys.Extended.UI.Images["Seadragon.Fullscreen-Rest.png"],
GROUP: Sys.Extended.UI.Images["Seadragon.Fullscreen-Grouphover.png"],
HOVER: Sys.Extended.UI.Images["Seadragon.Fullscreen-Hover.png"],
DOWN: Sys.Extended.UI.Images["Seadragon.Fullscreen-Pressed.png"]
}
};
};
Sys.Extended.UI.Seadragon.Config.registerClass('Sys.Extended.UI.Seadragon.Config', null, Sys.IDisposable);
if(!window.SIGNAL)
window.SIGNAL = "----seadragon----";
Sys.Extended.UI.Seadragon.ControlAnchor = function() {
throw Error.invalidOperation();
};
Sys.Extended.UI.Seadragon.ControlAnchor.prototype = {
NONE: 0,
TOP_LEFT: 1,
TOP_RIGHT: 2,
BOTTOM_RIGHT: 3,
BOTTOM_LEFT: 4
};
Sys.Extended.UI.Seadragon.ControlAnchor.registerEnum("Sys.Extended.UI.Seadragon.ControlAnchor", false);
Seadragon.ControlAnchor = Sys.Extended.UI.Seadragon.ControlAnchor;
Sys.Extended.UI.Seadragon.OverlayPlacement = function() {
throw Error.invalidOperation();
};
Sys.Extended.UI.Seadragon.OverlayPlacement.prototype = {
CENTER: 0,
TOP_LEFT: 1,
TOP: 2,
TOP_RIGHT: 3,
RIGHT: 4,
BOTTOM_RIGHT: 5,
BOTTOM: 6,
BOTTOM_LEFT: 7,
LEFT: 8
};
Sys.Extended.UI.Seadragon.OverlayPlacement.registerEnum("Sys.Extended.UI.Seadragon.OverlayPlacement", false);
Seadragon.OverlayPlacement = Sys.Extended.UI.Seadragon.OverlayPlacement;
Sys.Extended.UI.Seadragon.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();
};
Sys.Extended.UI.Seadragon.NavControl.prototype = {
initialize: function() {
var beginZoomingInHandler = Function.createDelegate(this, this._beginZoomingIn);
var endZoomingHandler = Function.createDelegate(this, this._endZooming);
var doSingleZoomInHandler = Function.createDelegate(this, this._doSingleZoomIn);
var beginZoomingOutHandler = Function.createDelegate(this, this._beginZoomingOut);
var doSingleZoomOutHandler = Function.createDelegate(this, this._doSingleZoomOut);
var onHomeHandler = Function.createDelegate(this, this._onHome);
var onFullPageHandler = Function.createDelegate(this, this._onFullPage);
var navImages = this._viewer.config.navImages;
var zoomIn = $create(Sys.Extended.UI.Seadragon.Button,
{ config: this._viewer.config, tooltip: Seadragon.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 }, null, null);
var zoomOut = $create(Sys.Extended.UI.Seadragon.Button,
{ config: this._viewer.config, tooltip: Seadragon.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 }, null, null);
var goHome = $create(Sys.Extended.UI.Seadragon.Button,
{ config: this._viewer.config, tooltip: Seadragon.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 }, null, null);
var fullPage = $create(Sys.Extended.UI.Seadragon.Button,
{ config: this._viewer.config, tooltip: Seadragon.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 }, null, null);
this._group = $create(Sys.Extended.UI.Seadragon.ButtonGroup,
{ config: this._viewer.config, buttons: [zoomIn, zoomOut, goHome, fullPage] }, null, null, null);
this.elmt = this._group.get_element();
this.elmt[SIGNAL] = true; // hack to get our controls to fade
this._viewer.add_open(Function.createDelegate(this, this._lightUp));
},
dispose: function() {
},
_resolveUrl: function(url) {
return String.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(Function.createDelegate(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();
}
};
Sys.Extended.UI.Seadragon.NavControl.registerClass('Sys.Extended.UI.Seadragon.NavControl', null, Sys.IDisposable);
Sys.Extended.UI.Seadragon.Control = function(elmt, anchor, container) {
// Properties
this.elmt = elmt;
this.anchor = anchor;
this.container = container;
this.wrapper = Seadragon.Utils.makeNeutralElement("span");
this.initialize();
};
Sys.Extended.UI.Seadragon.Control.prototype = {
initialize: function() {
this.wrapper = Seadragon.Utils.makeNeutralElement("span");
// Constructor
this.wrapper.style.display = "inline-block";
this.wrapper.appendChild(this.elmt);
if(this.anchor == Seadragon.ControlAnchor.NONE)
this.wrapper.style.width = this.wrapper.style.height = "100%"; // IE6 fix
this.addToAnchor();
},
addToAnchor: function() {
if(this.anchor == Seadragon.ControlAnchor.TOP_RIGHT || this.anchor == Seadragon.ControlAnchor.BOTTOM_RIGHT)
this.container.insertBefore(this.elmt, this.container.firstChild);
else
this.container.appendChild(this.elmt);
},
destroy: function() {
this.wrapper.removeChild(this.elmt);
this.container.removeChild(this.wrapper);
},
isVisible: function() {
// see note in setVisible() below about using "display: none"
return this.wrapper.style.display != "none";
},
setVisible: function(visible) {
// using "display: none" instead of "visibility: hidden" so that mouse
// events are no longer blocked by this invisible control.
this.wrapper.style.display = visible ? "inline-block" : "none";
},
setOpacity: function(opacity) {
// like with setVisible() above, we really should be working with the
// wrapper element and not the passed in element directly, so that we
// don't conflict with the developer's own opacity settings. but this
// doesn't work in IE always, so for our controls, use a hack for now...
if(this.elmt[SIGNAL] && Seadragon.Utils.getBrowser() == Seadragon.Browser.IE)
Seadragon.Utils.setElementOpacity(this.elmt, opacity, true);
else
Seadragon.Utils.setElementOpacity(this.wrapper, opacity, true);
}
};
Sys.Extended.UI.Seadragon.Control.registerClass('Sys.Extended.UI.Seadragon.Control', null, Sys.IDisposable);
Sys.Extended.UI.Seadragon.Viewer = function(element) {
Sys.Extended.UI.Seadragon.Viewer.initializeBase(this, [element]);
//Fields
this.config = new Sys.Extended.UI.Seadragon.Config();
this._prefixUrl = null;
this._controls = [];
this._customControls = null;
this._overlays = [];
this._overlayControls = null;
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 = null;
//Public fields
this.source = null;
this.drawer = null;
this.viewport = null;
this.profiler = null;
};
Sys.Extended.UI.Seadragon.Viewer.prototype = {
initialize: function() {
Sys.Extended.UI.Seadragon.Viewer.callBaseMethod(this, 'initialize');
this._container = Seadragon.Utils.makeNeutralElement("div");
this._canvas = Seadragon.Utils.makeNeutralElement("div");
this._controlsTL = Seadragon.Utils.makeNeutralElement("div");
this._controlsTR = Seadragon.Utils.makeNeutralElement("div");
this._controlsBR = Seadragon.Utils.makeNeutralElement("div");
this._controlsBL = Seadragon.Utils.makeNeutralElement("div");
var innerTracker = new Seadragon.MouseTracker(this._canvas, this.config.clickTimeThreshold, this.config.clickDistThreshold);
var outerTracker = new Seadragon.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 Sys.Extended.UI.Seadragon.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
// incorrect centering
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";
// mouse tracker handler for canvas (pan and zoom)
innerTracker.clickHandler = Function.createDelegate(this, this._onCanvasClick);
innerTracker.dragHandler = Function.createDelegate(this, this._onCanvasDrag);
innerTracker.releaseHandler = Function.createDelegate(this, this._onCanvasRelease);
innerTracker.setTracking(true); // default state
// create default navigation control
if(this.get_showNavigationControl()) {
navControl = (new Sys.Extended.UI.Seadragon.NavControl(this)).elmt;
navControl.style.marginRight = "4px";
navControl.style.marginBottom = "4px";
this.addControl(navControl, Sys.Extended.UI.Seadragon.ControlAnchor.BOTTOM_RIGHT);
}
for(var i = 0; i < this._customControls.length; i++)
this.addControl(this._customControls[i].id, this._customControls[i].anchor);
// mouse tracker handler for container (controls fading)
outerTracker.enterHandler = Function.createDelegate(this, this._onContainerEnter);
outerTracker.exitHandler = Function.createDelegate(this, this._onContainerExit);
outerTracker.releaseHandler = Function.createDelegate(this, this._onContainerRelease);
outerTracker.setTracking(true); // always tracking
window.setTimeout(Function.createDelegate(this, this._beginControlsAutoHide), 1); // initial fade out
//append to DOM only at end
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);
},
_raiseEvent: function(eventName, eventArgs) {
// Get handler for event.
var handler = this.get_events().getHandler(eventName);
if(handler) {
if(!eventArgs)
eventArgs = Sys.EventArgs.Empty;
// Fire event.
handler(this, eventArgs);
}
},
_beginControlsAutoHide: function() {
if(!this.config.autoHideControls)
return;
this._controlsShouldFade = true;
this._controlsFadeBeginTime = new Date().getTime() + this._controlsFadeDelay;
window.setTimeout(Function.createDelegate(this, this._scheduleControlsFade), this._controlsFadeDelay);
},
_scheduleControlsFade: function() {
window.setTimeout(Function.createDelegate(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)
// negate since dragging is opposite of panning.
// analogy: in adobe pdf, dragging vs using scrollbars.
this.viewport.panBy(this.viewport.deltaPointsFromPixels(delta.negate()));
},
_onCanvasRelease: function(tracker, position, insideElmtPress, insideElmtRelease) {
if(insideElmtPress && this.viewport)
this.viewport.applyConstraints();
},
_onContainerExit: function(tracker, position, buttonDownElmt, buttonDownAny) {
// fade controls out over time, only if the mouse isn't down from
// within the container (e.g. panning, or using a control)
if(!buttonDownElmt) {
this._mouseInside = false;
if(!this._animating)
this._beginControlsAutoHide();
}
},
_onContainerRelease: function(tracker, position, insideElmtPress, insideElmtRelease) {
// the mouse may have exited the container and we ignored it if the
// mouse was down from within the container. now when the mouse is
// released, we should fade the controls out now.
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 = Seadragon.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) {
// we weren't animating, and now we did ==> animation start
this._raiseEvent("animationstart", self);
this._abortControlsAutoHide();
}
if(animated) {
// viewport moved
this.drawer.update();
this._raiseEvent("animation", self);
} else if(this._forceRedraw || this.drawer.needsUpdate()) {
// need to load or blend images, etc.
this.drawer.update();
this._forceRedraw = false;
} else {
// no changes, so preload images, etc.
this.drawer.idle();
}
if(this._animating && !animated) {
// we were animating, and now we're not anymore ==> animation finish
this._raiseEvent("animationfinish", this);
// if the mouse has left the container, begin fading controls
if(!this._mouseInside)
this._beginControlsAutoHide();
}
this._animating = animated;
this.profiler.endUpdate();
},
_onClose: function() {
// TODO need destroy() methods to prevent leaks? check for null if so.
// nullify fields and properties
this.source = null;
this.viewport = null;
this.drawer = null;
this.profiler = null;
// clear all tiles and any message
this._canvas.innerHTML = "";
},
_beforeOpen: function() {
if(this.source)
this._onClose();
this._lastOpenStartTime = new Date().getTime(); // to ignore earlier opens
// show loading message after a delay if it still hasn't loaded
window.setTimeout(Function.createDelegate(this, function() {
if(this._lastOpenStartTime > this._lastOpenEndTime)
this._setMessage(Seadragon.Strings.getString("Messages.Loading"));
}), 2000);
return this._lastOpenStartTime;
},
_setMessage: function(message) {
var textNode = document.createTextNode(message);
this._canvas.innerHTML = "";
this._canvas.appendChild(Seadragon.Utils.makeCenteredNode(textNode));
var textStyle = textNode.parentNode.style;
// explicit styles for error message
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) {
Seadragon.Debug.log("Ignoring out-of-date open.");
this._raiseEvent("ignore");
return;
} else if(!_source) {
this._setMessage(error);
this._raiseEvent("error");
return;
}
// clear any previous message
this._canvas.innerHTML = "";
this._prevContainerSize = Seadragon.Utils.getElementSize(this._container);
// assign fields
this.source = _source;
this.viewport = new Sys.Extended.UI.Seadragon.Viewport(this._prevContainerSize, this.source.dimensions, this.config);
this.drawer = new Sys.Extended.UI.Seadragon.Drawer(this.source, this.viewport, this._canvas);
this.profiler = new Sys.Extended.UI.Seadragon.Profiler();
// begin updating
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 Sys.Extended.UI.Seadragon.Point(overlay.point.X, overlay.point.Y), Sys.Extended.UI.Seadragon.OverlayPlacement.TOP_LEFT);
else
this.drawer.addOverlay(overlay.id, new Sys.Extended.UI.Seadragon.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 we're animating, update as fast as possible to stay smooth
if(this._animating)
return window.setTimeout(Function.createDelegate(this, updateFunc), 1);
// if no previous update, consider this an update
var currentTime = new Date().getTime();
var prevUpdateTime = prevUpdateTime ? prevUpdateTime : currentTime;
var targetTime = prevUpdateTime + 1000 / 60; // 60 fps ideal
// calculate delta time to be a positive number
var deltaTime = Math.max(1, targetTime - currentTime);
return window.setTimeout(Function.createDelegate(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 = Seadragon.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) {
// we weren't animating, and now we did ==> animation start
this._raiseEvent("animationstart");
this._abortControlsAutoHide();
}
if(animated) {
// viewport moved
this.drawer.update();
this._raiseEvent("animation");
} else if(this._forceRedraw || this.drawer.needsUpdate()) {
// need to load or blend images, etc.
this.drawer.update();
this._forceRedraw = false;
} else {
// no changes, so preload images, etc.
this.drawer.idle();
}
if(this._animating && !animated) {
// we were animating, and now we're not anymore ==> animation finish
this._raiseEvent("animationfinish");
// if the mouse has left the container, begin fading controls
if(!this._mouseInside)
this._beginControlsAutoHide();
}
this._animating = animated;
this.profiler.endUpdate();
},
getNavControl: function() {
return this._navControl;
},
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_minZoomDimension: function() {
return this.config.minZoomDimension;
},
set_minZoomDimension: function(value) {
this.config.minZoomDimension = 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_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_animationend: function(handler) {
this.get_events().addHandler("animationend", handler);
},
remove_animationend: function(handler) {
this.get_events().removeHandler("animationend", handler);
},
addControl: function(elmt, anchor) {
var elmt = Seadragon.Utils.getElement(elmt);
if(this._getControlIndex(elmt) >= 0)
return; // they're trying to add a duplicate control
var div = null;
switch(anchor) {
case Sys.Extended.UI.Seadragon.ControlAnchor.TOP_RIGHT:
div = this._controlsTR;
elmt.style.position = "relative";
break;
case Sys.Extended.UI.Seadragon.ControlAnchor.BOTTOM_RIGHT:
div = this._controlsBR;
elmt.style.position = "relative";
break;
case Sys.Extended.UI.Seadragon.ControlAnchor.BOTTOM_LEFT:
div = this._controlsBL;
elmt.style.position = "relative";
break;
case Sys.Extended.UI.Seadragon.ControlAnchor.TOP_LEFT:
div = this._controlsTL;
elmt.style.position = "relative";
break;
case Sys.Extended.UI.Seadragon.ControlAnchor.NONE:
default:
div = this._container;
elmt.style.position = "absolute";
break;
}
this._controls.push(new Sys.Extended.UI.Seadragon.Control(elmt, anchor, div));
},
isOpen: function() {
return !!this.source;
},
openDzi: function(xmlUrl, xmlString) {
var currentTime = this._beforeOpen();
Sys.Extended.UI.Seadragon.DziTileSourceHelper.createFromXml(xmlUrl, xmlString,
Seadragon.Utils.createCallback(null, Function.createDelegate(this, this._onOpen), currentTime));
},
openTileSource: function(tileSource) {
var currentTime = this._beforeOpen();
window.setTimeout(Function.createDelegate(this, function() {
this._onOpen(currentTime, tileSource);
}), 1);
},
close: function() {
if(!this.source)
return;
this._onClose();
},
removeControl: function(elmt) {
var elmt = Seadragon.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;
// copy locally to improve perf
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) {
// change overflow, but preserve what current values are
bodyOverflow = bodyStyle.overflow;
docOverflow = docStyle.overflow;
bodyStyle.overflow = "hidden";
docStyle.overflow = "hidden";
// IE6 needs the body width/height to be 100% also
bodyWidth = bodyStyle.width;
bodyHeight = bodyStyle.height;
bodyStyle.width = "100%";
bodyStyle.height = "100%";
// always show black background, etc., for fullpage
canvasStyle.backgroundColor = "black";
canvasStyle.color = "white";
// make container attached to the window, immune to scrolling,
// and above any other things with a z-index set.
containerStyle.position = "fixed";
containerStyle.zIndex = "99999999";
body.appendChild(this._container);
this._prevContainerSize = Seadragon.Utils.getWindowSize();
this._onContainerEnter(); // mouse will be inside container now
} else {
// restore previous values for overflow
bodyStyle.overflow = bodyOverflow;
docStyle.overflow = docOverflow;
// IE6 needed to overwrite the body width/height also
bodyStyle.width = bodyWidth;
bodyStyle.height = bodyHeight;
// return to inheriting style
canvasStyle.backgroundColor = "";
canvasStyle.color = "";
// make container be inline on page again, and auto z-index
containerStyle.position = "relative";
containerStyle.zIndex = "";
this.get_element().appendChild(this._container);
this._prevContainerSize = Seadragon.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) {
// going to fullpage, remember how much bounds changed by.
this._fsBoundsDelta = new Sys.Extended.UI.Seadragon.Point(newBounds.width / oldBounds.width,
newBounds.height / oldBounds.height);
} else {
// leaving fullpage, negate how much the fullpage zoomed by.
// note that we have to negate the bigger of the two changes.
// we have to zoom about the center of the new bounds, but
// that's NOT the zoom point. so we have to manually update
// first so that that center becomes the viewport center.
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) {
// important: don't explicitly say "visibility: visible", because
// the W3C spec actually says children of hidden elements that have
// "visibility: visible" should still be rendered. that's usually
// not what we (or developers) want. browsers are inconsistent in
// this regard, but IE seems to follow this spec.
this._container.style.visibility = visible ? "" : "hidden";
}
};
Sys.Extended.UI.Seadragon.Viewer.registerClass('Sys.Extended.UI.Seadragon.Viewer', Sys.UI.Control);
(function() {
// DUPLICATION CHECK -- necessary here because of private static state
if(Seadragon.MouseTracker)
return;
// Constants
var isIE = Seadragon.Utils.getBrowser() == Seadragon.Browser.IE;
// Static fields
var buttonDownAny = false;
var ieCapturingAny = false;
var ieTrackersActive = {}; // dictionary from hash to MouseTracker
var ieTrackersCapturing = []; // list of trackers interested in capture
// Static helpers
function getMouseAbsolute(event) {
return Seadragon.Utils.getMousePosition(event);
}
function getMouseRelative(event, elmt) {
var mouse = Seadragon.Utils.getMousePosition(event);
var offset = Seadragon.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) {
// Firefox sometimes fires events for XUL elements, which throws
// a "permission denied" error. so this is not a child.
return false;
}
return elmtA == elmtB;
}
function onGlobalMouseDown() {
buttonDownAny = true;
}
function onGlobalMouseUp() {
buttonDownAny = false;
}
// Static constructor
(function() {
// the W3C event model lets us listen to the capture phase of events, so
// to know if the mouse is globally up or down, we'll listen to the
// capture phase of the window's events. we can't do this in IE, so
// we'll give it a best effort by listening to the regular bubble phase,
// and on the document since window isn't legal in IE for mouse events.
if(isIE) {
Seadragon.Utils.addEvent(document, "mousedown", onGlobalMouseDown, false);
Seadragon.Utils.addEvent(document, "mouseup", onGlobalMouseUp, false);
} else {
Seadragon.Utils.addEvent(window, "mousedown", onGlobalMouseDown, true);
Seadragon.Utils.addEvent(window, "mouseup", onGlobalMouseUp, true);
}
})();
// Class
Seadragon.MouseTracker = function(elmt, clickTimeThreshold, clickDistThreshold) {
// Fields
var self = this;
var ieSelf = null;
var hash = Math.random(); // a unique hash for this tracker
var elmt = Seadragon.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;
// Properties
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.clickHandler = null; // function(tracker, position, quick, shift)
this.dragHandler = null; // function(tracker, position, delta, shift)
// Helpers
function startTracking() {
if(!tracking) {
Seadragon.Utils.addEvent(elmt, "mouseover", onMouseOver, false);
Seadragon.Utils.addEvent(elmt, "mouseout", onMouseOut, false);
Seadragon.Utils.addEvent(elmt, "mousedown", onMouseDown, false);
Seadragon.Utils.addEvent(elmt, "mouseup", onMouseUp, false);
Seadragon.Utils.addEvent(elmt, "click", onMouseClick, false);
tracking = true;
ieTrackersActive[hash] = ieSelf;
}
}
function stopTracking() {
if(tracking) {
Seadragon.Utils.removeEvent(elmt, "mouseover", onMouseOver, false);
Seadragon.Utils.removeEvent(elmt, "mouseout", onMouseOut, false);
Seadragon.Utils.removeEvent(elmt, "mousedown", onMouseDown, false);
Seadragon.Utils.removeEvent(elmt, "mouseup", onMouseUp, false);
Seadragon.Utils.removeEvent(elmt, "click", onMouseClick, false);
releaseMouse();
tracking = false;
delete ieTrackersActive[hash];
}
}
function captureMouse() {
if(!capturing) {
// IE lets the element capture the mouse directly, but other
// browsers use the capture phase on the highest element.
if(isIE) {
// we need to capture the mouse, but we also don't want to
// handle mouseup like normally (special case for bubbling)
Seadragon.Utils.removeEvent(elmt, "mouseup", onMouseUp, false);
Seadragon.Utils.addEvent(elmt, "mouseup", onMouseUpIE, true);
Seadragon.Utils.addEvent(elmt, "mousemove", onMouseMoveIE, true);
} else {
Seadragon.Utils.addEvent(window, "mouseup", onMouseUpWindow, true);
Seadragon.Utils.addEvent(window, "mousemove", onMouseMove, true);
}
capturing = true;
}
}
function releaseMouse() {
if(capturing) {
// similar reasoning as captureMouse()
if(isIE) {
// we need to release the mouse, and also go back to handling
// mouseup like normal (no longer a hack for capture phase)
Seadragon.Utils.removeEvent(elmt, "mousemove", onMouseMoveIE, true);
Seadragon.Utils.removeEvent(elmt, "mouseup", onMouseUpIE, true);
Seadragon.Utils.addEvent(elmt, "mouseup", onMouseUp, false);
} else {
Seadragon.Utils.removeEvent(window, "mousemove", onMouseMove, true);
Seadragon.Utils.removeEvent(window, "mouseup", onMouseUpWindow, true);
}
capturing = false;
}
}
// IE-specific helpers
function triggerOthers(eventName, event) {
// update: protecting against properties added to the Object class's
// prototype, which can and does happen (e.g. through js libraries)
var trackers = ieTrackersActive;
for(var otherHash in trackers)
if(trackers.hasOwnProperty(otherHash) && hash != otherHash)
trackers[otherHash][eventName](event);
}
function hasMouse() {
return insideElmt;
}
// Listeners
function onMouseOver(event) {
var event = Seadragon.Utils.getEvent(event);
// IE capturing model doesn't raise or bubble the events on any
// other element if we're capturing currently. so pass this event to
// other elements being tracked so they can adjust if the element
// was from them or from a child. however, IE seems to always fire
// events originating from parents to those parents, so don't double
// fire the event if the event originated from a parent.
if(isIE && capturing && !isChild(event.srcElement, elmt))
triggerOthers("onMouseOver", event);
// similar to onMouseOut() tricky bubbling case...
var to = event.target ? event.target : event.srcElement;
var from = event.relatedTarget ? event.relatedTarget : event.fromElement;
if(!isChild(elmt, to) || isChild(elmt, from))
// the mouseover needs to end on this or a child node, and it
// needs to start from this or an outer node.
return;
insideElmt = true;
if(typeof (self.enterHandler) == "function")
try {
self.enterHandler(self, getMouseRelative(event, elmt),
buttonDownElmt, buttonDownAny);
} catch(e) {
// handler threw an error, ignore
Seadragon.Debug.error(e.name +
" while executing enter handler: " + e.message, e);
}
}
function onMouseOut(event) {
var event = Seadragon.Utils.getEvent(event);
// similar to onMouseOver() case for IE capture model
if(isIE && capturing && !isChild(event.srcElement, elmt))
triggerOthers("onMouseOut", event);
// we have to watch out for a tricky case: a mouseout occurs on a
// child element, but the mouse is still inside the parent element.
// the mouseout event will bubble up to us. this happens in all
// browsers, so we need to correct for this. technique from:
// http://www.quirksmode.org/js/events_mouse.html
var from = event.target ? event.target : event.srcElement;
var to = event.relatedTarget ? event.relatedTarget : event.toElement;
if(!isChild(elmt, from) || isChild(elmt, to))
// the mouseout needs to start from this or a child node, and it
// needs to end on this or an outer node.
return;
insideElmt = false;
if(typeof (self.exitHandler) == "function")
try {
self.exitHandler(self, getMouseRelative(event, elmt),
buttonDownElmt, buttonDownAny);
} catch(e) {
// handler threw an error, ignore
Seadragon.Debug.error(e.name +
" while executing exit handler: " + e.message, e);
}
}
function onMouseDown(event) {
var event = Seadragon.Utils.getEvent(event);
// don't consider right-clicks (fortunately this is cross-browser)
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) {
// handler threw an error, ignore
Seadragon.Debug.error(e.name +
" while executing press handler: " + e.message, e);
}
if(self.pressHandler || self.dragHandler)
// if a press or drag handler is registered, don't drag-drop images, etc.
Seadragon.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 = Seadragon.Utils.getEvent(event);
var insideElmtPress = buttonDownElmt;
var insideElmtRelease = insideElmt;
// don't consider right-clicks (fortunately this is cross-browser)
if(event.button == 2)
return;
buttonDownElmt = false;
if(typeof (self.releaseHandler) == "function")
try {
self.releaseHandler(self, getMouseRelative(event, elmt),
insideElmtPress, insideElmtRelease);
} catch(e) {
// handler threw an error, ignore
Seadragon.Debug.error(e.name +
" while executing release handler: " + e.message, e);
}
// some browsers sometimes don't fire click events when we're also
// listening for mouseup events. i'm not sure why, it could be
// something i'm doing. in the meantime, this is a temporary fix.
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 = Seadragon.Utils.getEvent(event);
// don't consider right-clicks (fortunately this is cross-browser)
if(event.button == 2)
return;
// first trigger those that were capturing
for(var i = 0; i < ieTrackersCapturing.length; i++) {
var tracker = ieTrackersCapturing[i];
if(!tracker.hasMouse())
tracker.onMouseUp(event);
}
// then release capture and emulate a regular event
releaseMouse();
ieCapturingAny = false;
event.srcElement.fireEvent("on" + event.type, document.createEventObject(event));
// make sure to stop this event -- shouldn't bubble up
Seadragon.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) {
// see onMouseUp() bug -- handleClick() is already called by
// onMouseUp() as a temporary fix, so don't duplicate the call here.
if(self.clickHandler)
// since a click handler was registered, don't follow href's, etc.
Seadragon.Utils.cancelEvent(event);
}
function handleMouseClick(event) {
var event = Seadragon.Utils.getEvent(event);
// don't consider right-clicks (fortunately this is cross-browser)
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) {
// handler threw an error, ignore
Seadragon.Debug.error(e.name +
" while executing click handler: " + e.message, e);
}
}
function onMouseMove(event) {
var event = Seadragon.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) {
// handler threw an error, ignore
Seadragon.Debug.error(e.name + " while executing drag handler: " + e.message, e);
}
// since a drag handler was registered, don't allow highlighting, etc.
Seadragon.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) {
// manually trigger those that are capturing
for(var i = 0; i < ieTrackersCapturing.length; i++)
ieTrackersCapturing[i].onMouseMove(event);
// make sure to stop this event -- shouldn't bubble up. note that at
// the time of this writing, there is no harm in letting it bubble,
// but a minor change to our implementation would necessitate this.
Seadragon.Utils.stopEvent(event);
}
// Constructor
(function() {
ieSelf = {
hasMouse: hasMouse,
onMouseOver: onMouseOver,
onMouseOut: onMouseOut,
onMouseUp: onMouseUp,
onMouseMove: onMouseMove
};
})();
// Methods
this.isTracking = function() {
return tracking;
};
this.setTracking = function(track) {
if(track)
startTracking();
else
stopTracking();
};
};
})();
Seadragon.Point = Sys.Extended.UI.Seadragon.Point = function(x, y) {
this.x = typeof (x) == "number" ? x : 0;
this.y = typeof (y) == "number" ? y : 0;
};
Sys.Extended.UI.Seadragon.Point.prototype = {
// Methods
plus: function(point) {
return new Sys.Extended.UI.Seadragon.Point(this.x + point.x, this.y + point.y);
},
minus: function(point) {
return new Sys.Extended.UI.Seadragon.Point(this.x - point.x, this.y - point.y);
},
times: function(factor) {
return new Sys.Extended.UI.Seadragon.Point(this.x * factor, this.y * factor);
},
divide: function(factor) {
return new Sys.Extended.UI.Seadragon.Point(this.x / factor, this.y / factor);
},
negate: function() {
return new Sys.Extended.UI.Seadragon.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 Sys.Extended.UI.Seadragon.Point(func(this.x), func(this.y));
},
equals: function(point) {
return (point instanceof Sys.Extended.UI.Seadragon.Point) && (this.x === point.x) && (this.y === point.y);
},
toString: function() {
return "(" + this.x + "," + this.y + ")";
}
};
Sys.Extended.UI.Seadragon.Point.registerClass('Sys.Extended.UI.Seadragon.Point', null, Sys.IDisposable);
Sys.Extended.UI.Seadragon.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 = Sys.Extended.UI.Seadragon.Strings;
// get property, which may contain dots, meaning subproperty
for(var i = 0; i < props.length; i++)
string = string[props[i]] || {}; // in case not a subproperty
// in case the string didn't exist
if(typeof (string) != "string")
string = "";
// regular expression and lambda technique from:
// http://frogsbrain.wordpress.com/2007/04/28/javascript-stringformat-method/#comment-236
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 = Seadragon.Strings;
// get property's container, up to but not after last dot
for(var i = 0; i < props.length - 1; i++)
if(!container[props[i]])
container[props[i]] = {};
container = container[props[i]];
container[props[i]] = value;
}
};
Seadragon.Strings = Sys.Extended.UI.Seadragon.Strings;
// Constants
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
// Method of drawing
var browser = Seadragon.Utils.getBrowser();
var browserVer = Seadragon.Utils.getBrowserVersion();
// only Firefox and Opera implement