} gPoints
+ * @param {OpenSeadragon.MouseTracker.EventProcessInfo} eventInfo
+ * Processing info for originating DOM event.
+ * @param {OpenSeadragon.MouseTracker.GesturePoint} gPoint
* Gesture points associated with the event.
*/
- function updatePointersCancel( tracker, event, gPoints ) {
- updatePointersUp( tracker, event, gPoints, 0 );
- updatePointersExit( tracker, event, gPoints );
+ function updatePointerCancel( tracker, eventInfo, gPoint ) {
+ var pointsList = tracker.getActivePointersListByType( gPoint.type ),
+ updateGPoint;
+
+ updateGPoint = pointsList.getById( gPoint.id );
+
+ if ( updateGPoint ) {
+ stopTrackingPointer( tracker, pointsList, updateGPoint );
+ }
}
@@ -3337,38 +3763,9 @@
buttons: tracker.getActivePointersListByType( pointerType ).buttons,
isTouchEvent: pointerType === 'touch',
originalEvent: originalMoveEvent,
- preventDefaultAction: false,
userData: tracker.userData
} );
}
}
- /**
- * True if inside an iframe, otherwise false.
- * @member {Boolean} isInIframe
- * @private
- * @inner
- */
- var isInIframe = (function() {
- try {
- return window.self !== window.top;
- } catch (e) {
- return true;
- }
- })();
-
- /**
- * @function
- * @private
- * @inner
- * @returns {Boolean} True if the target has access rights to events, otherwise false.
- */
- function canAccessEvents (target) {
- try {
- return target.addEventListener && target.removeEventListener;
- } catch (e) {
- return false;
- }
- }
-
}(OpenSeadragon));
diff --git a/src/navigator.js b/src/navigator.js
index afa3a7f5..81993b74 100644
--- a/src/navigator.js
+++ b/src/navigator.js
@@ -66,15 +66,15 @@ $.Navigator = function( options ){
};
if( options.position ){
- if( 'BOTTOM_RIGHT' == options.position ){
+ if( 'BOTTOM_RIGHT' === options.position ){
options.controlOptions.anchor = $.ControlAnchor.BOTTOM_RIGHT;
- } else if( 'BOTTOM_LEFT' == options.position ){
+ } else if( 'BOTTOM_LEFT' === options.position ){
options.controlOptions.anchor = $.ControlAnchor.BOTTOM_LEFT;
- } else if( 'TOP_RIGHT' == options.position ){
+ } else if( 'TOP_RIGHT' === options.position ){
options.controlOptions.anchor = $.ControlAnchor.TOP_RIGHT;
- } else if( 'TOP_LEFT' == options.position ){
+ } else if( 'TOP_LEFT' === options.position ){
options.controlOptions.anchor = $.ControlAnchor.TOP_LEFT;
- } else if( 'ABSOLUTE' == options.position ){
+ } else if( 'ABSOLUTE' === options.position ){
options.controlOptions.anchor = $.ControlAnchor.ABSOLUTE;
options.controlOptions.top = options.top;
options.controlOptions.left = options.left;
@@ -128,7 +128,7 @@ $.Navigator = function( options ){
this.totalBorderWidths = new $.Point(this.borderWidth * 2, this.borderWidth * 2).minus(this.fudge);
- if ( options.controlOptions.anchor != $.ControlAnchor.NONE ) {
+ if ( options.controlOptions.anchor !== $.ControlAnchor.NONE ) {
(function( style, borderWidth ){
style.margin = '0px';
style.border = borderWidth + 'px solid ' + options.borderColor;
@@ -167,20 +167,24 @@ $.Navigator = function( options ){
style.zIndex = 999999999;
style.cursor = 'default';
}( this.displayRegion.style, this.borderWidth ));
+ $.setElementPointerEventsNone( this.displayRegion );
+ $.setElementTouchActionNone( this.displayRegion );
this.displayRegionContainer = $.makeNeutralElement("div");
this.displayRegionContainer.id = this.element.id + '-displayregioncontainer';
this.displayRegionContainer.className = "displayregioncontainer";
this.displayRegionContainer.style.width = "100%";
this.displayRegionContainer.style.height = "100%";
+ $.setElementPointerEventsNone( this.displayRegionContainer );
+ $.setElementTouchActionNone( this.displayRegionContainer );
viewer.addControl(
this.element,
options.controlOptions
);
- this._resizeWithViewer = options.controlOptions.anchor != $.ControlAnchor.ABSOLUTE &&
- options.controlOptions.anchor != $.ControlAnchor.NONE;
+ this._resizeWithViewer = options.controlOptions.anchor !== $.ControlAnchor.ABSOLUTE &&
+ options.controlOptions.anchor !== $.ControlAnchor.NONE;
if (options.width && options.height) {
this.setWidth(options.width);
@@ -221,12 +225,29 @@ $.Navigator = function( options ){
// Remove the base class' (Viewer's) innerTracker and replace it with our own
this.innerTracker.destroy();
this.innerTracker = new $.MouseTracker({
- element: this.element,
+ userData: 'Navigator.innerTracker',
+ element: this.element, //this.canvas,
dragHandler: $.delegate( this, onCanvasDrag ),
clickHandler: $.delegate( this, onCanvasClick ),
releaseHandler: $.delegate( this, onCanvasRelease ),
- scrollHandler: $.delegate( this, onCanvasScroll )
+ scrollHandler: $.delegate( this, onCanvasScroll ),
+ preProcessEventHandler: function (eventInfo) {
+ if (eventInfo.eventType === 'wheel') {
+ //don't scroll the page up and down if the user is scrolling
+ //in the navigator
+ eventInfo.preventDefault = true;
+ }
+ }
});
+ this.outerTracker.userData = 'Navigator.outerTracker';
+
+ // this.innerTracker is attached to this.element...we need to allow pointer
+ // events to pass through this Viewer's canvas/container elements so implicit
+ // pointer capture works on touch devices
+ //TODO an alternative is to attach the new MouseTracker to this.canvas...not
+ // sure why it isn't already (see MouseTracker constructor call above)
+ $.setElementPointerEventsNone( this.canvas );
+ $.setElementPointerEventsNone( this.container );
this.addHandler("reset-size", function() {
if (_this.viewport) {
@@ -282,7 +303,7 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
*/
setWidth: function(width) {
this.width = width;
- this.element.style.width = typeof (width) == "number" ? (width + 'px') : width;
+ this.element.style.width = typeof (width) === "number" ? (width + 'px') : width;
this._resizeWithViewer = false;
},
@@ -292,7 +313,7 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
*/
setHeight: function(height) {
this.height = height;
- this.element.style.height = typeof (height) == "number" ? (height + 'px') : height;
+ this.element.style.height = typeof (height) === "number" ? (height + 'px') : height;
this._resizeWithViewer = false;
},
@@ -383,6 +404,8 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
var myItem = event.item;
myItem._originalForNavigator = original;
_this._matchBounds(myItem, original, true);
+ _this._matchOpacity(myItem, original);
+ _this._matchCompositeOperation(myItem, original);
function matchBounds() {
_this._matchBounds(myItem, original);
@@ -406,6 +429,10 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
return $.Viewer.prototype.addTiledImage.apply(this, [optionsClone]);
},
+ destroy: function() {
+ return $.Viewer.prototype.destroy.apply(this);
+ },
+
// private
_getMatchingItem: function(theirItem) {
var count = this.world.getItemCount();
@@ -427,6 +454,7 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
myItem.setWidth(bounds.width, immediately);
myItem.setRotation(theirItem.getRotation(), immediately);
myItem.setClip(theirItem.getClip());
+ myItem.setFlip(theirItem.getFlip());
},
// private
@@ -453,7 +481,7 @@ function onCanvasClick( event ) {
quick: event.quick,
shift: event.shift,
originalEvent: event.originalEvent,
- preventDefaultAction: event.preventDefaultAction
+ preventDefaultAction: false
};
/**
* Raised when a click event occurs on the {@link OpenSeadragon.Viewer#navigator} element.
@@ -505,7 +533,7 @@ function onCanvasDrag( event ) {
direction: event.direction,
shift: event.shift,
originalEvent: event.originalEvent,
- preventDefaultAction: event.preventDefaultAction
+ preventDefaultAction: false
};
/**
* Raised when a drag event occurs on the {@link OpenSeadragon.Viewer#navigator} element.
@@ -522,7 +550,7 @@ function onCanvasDrag( event ) {
* @property {Boolean} shift - True if the shift key was pressed during this event.
* @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
- * @property {Boolean} preventDefaultAction - Set to true to prevent default click to zoom behaviour. Default: false.
+ * @property {Boolean} preventDefaultAction - Set to true to prevent default drag to pan behaviour. Default: false.
*/
this.viewer.raiseEvent('navigator-drag', canvasDragEventArgs);
@@ -568,6 +596,15 @@ function onCanvasRelease( event ) {
* @function
*/
function onCanvasScroll( event ) {
+ var eventArgs = {
+ tracker: event.eventSource,
+ position: event.position,
+ scroll: event.scroll,
+ shift: event.shift,
+ originalEvent: event.originalEvent,
+ preventDefault: event.preventDefault
+ };
+
/**
* Raised when a scroll event occurs on the {@link OpenSeadragon.Viewer#navigator} element (mouse wheel, touch pinch, etc.).
*
@@ -580,19 +617,12 @@ function onCanvasScroll( event ) {
* @property {Number} scroll - The scroll delta for the event.
* @property {Boolean} shift - True if the shift key was pressed during this event.
* @property {Object} originalEvent - The original DOM event.
+ * @property {Boolean} preventDefault - Set to true to prevent the default user-agent's handling of the wheel event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
- this.viewer.raiseEvent( 'navigator-scroll', {
- tracker: event.eventSource,
- position: event.position,
- scroll: event.scroll,
- shift: event.shift,
- originalEvent: event.originalEvent
- });
+ this.viewer.raiseEvent( 'navigator-scroll', eventArgs );
- //don't scroll the page up and down if the user is scrolling
- //in the navigator
- return false;
+ event.preventDefault = eventArgs.preventDefault;
}
/**
diff --git a/src/openseadragon.js b/src/openseadragon.js
index e3d9aed7..5b0accb0 100644
--- a/src/openseadragon.js
+++ b/src/openseadragon.js
@@ -195,8 +195,9 @@
*
* @property {String} [compositeOperation=null]
* Valid values are 'source-over', 'source-atop', 'source-in', 'source-out',
- * 'destination-over', 'destination-atop', 'destination-in',
- * 'destination-out', 'lighter', 'copy' or 'xor'
+ * 'destination-over', 'destination-atop', 'destination-in', 'destination-out',
+ * 'lighter', 'difference', 'copy', 'xor', etc.
+ * For complete list of modes, please @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation/ globalCompositeOperation}
*
* @property {Boolean} [imageSmoothingEnabled=true]
* Image smoothing for canvas rendering (only if canvas is used). Note: Ignored
@@ -320,6 +321,7 @@
*
* @property {OpenSeadragon.GestureSettings} [gestureSettingsMouse]
* Settings for gestures generated by a mouse pointer device. (See {@link OpenSeadragon.GestureSettings})
+ * @property {Boolean} [gestureSettingsMouse.dragToPan=true] - Pan on drag gesture
* @property {Boolean} [gestureSettingsMouse.scrollToZoom=true] - Zoom on scroll gesture
* @property {Boolean} [gestureSettingsMouse.clickToZoom=true] - Zoom on click gesture
* @property {Boolean} [gestureSettingsMouse.dblClickToZoom=false] - Zoom on double-click gesture. Note: If set to true
@@ -334,6 +336,7 @@
*
* @property {OpenSeadragon.GestureSettings} [gestureSettingsTouch]
* Settings for gestures generated by a touch pointer device. (See {@link OpenSeadragon.GestureSettings})
+ * @property {Boolean} [gestureSettingsMouse.dragToPan=true] - Pan on drag gesture
* @property {Boolean} [gestureSettingsTouch.scrollToZoom=false] - Zoom on scroll gesture
* @property {Boolean} [gestureSettingsTouch.clickToZoom=false] - Zoom on click gesture
* @property {Boolean} [gestureSettingsTouch.dblClickToZoom=true] - Zoom on double-click gesture. Note: If set to true
@@ -348,12 +351,13 @@
*
* @property {OpenSeadragon.GestureSettings} [gestureSettingsPen]
* Settings for gestures generated by a pen pointer device. (See {@link OpenSeadragon.GestureSettings})
+ * @property {Boolean} [gestureSettingsMouse.dragToPan=true] - Pan on drag gesture
* @property {Boolean} [gestureSettingsPen.scrollToZoom=false] - Zoom on scroll gesture
* @property {Boolean} [gestureSettingsPen.clickToZoom=true] - Zoom on click gesture
* @property {Boolean} [gestureSettingsPen.dblClickToZoom=false] - Zoom on double-click gesture. Note: If set to true
* then clickToZoom should be set to false to prevent multiple zooms.
* @property {Boolean} [gestureSettingsPen.pinchToZoom=false] - Zoom on pinch gesture
- * @property {Boolean} [gestureSettingsPan.zoomToRefPoint=true] - If zoomToRefPoint is true, the zoom is centered at the pointer position. Otherwise,
+ * @property {Boolean} [gestureSettingsPen.zoomToRefPoint=true] - If zoomToRefPoint is true, the zoom is centered at the pointer position. Otherwise,
* the zoom is centered at the canvas center.
* @property {Boolean} [gestureSettingsPen.flickEnabled=false] - Enable flick gesture
* @property {Number} [gestureSettingsPen.flickMinSpeed=120] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)
@@ -362,6 +366,7 @@
*
* @property {OpenSeadragon.GestureSettings} [gestureSettingsUnknown]
* Settings for gestures generated by unknown pointer devices. (See {@link OpenSeadragon.GestureSettings})
+ * @property {Boolean} [gestureSettingsMouse.dragToPan=true] - Pan on drag gesture
* @property {Boolean} [gestureSettingsUnknown.scrollToZoom=true] - Zoom on scroll gesture
* @property {Boolean} [gestureSettingsUnknown.clickToZoom=false] - Zoom on click gesture
* @property {Boolean} [gestureSettingsUnknown.dblClickToZoom=true] - Zoom on double-click gesture. Note: If set to true
@@ -381,7 +386,11 @@
* The "zoom distance" per mouse scroll or touch pinch. Note: Setting this to 1.0 effectively disables the mouse-wheel zoom feature (also see gestureSettings[Mouse|Touch|Pen].scrollToZoom}).
*
* @property {Number} [zoomPerSecond=1.0]
- * The number of seconds to animate a single zoom event over.
+ * Sets the zoom amount per second when zoomIn/zoomOut buttons are pressed and held.
+ * The value is a factor of the current zoom, so 1.0 (the default) disables zooming when the zoomIn/zoomOut buttons
+ * are held. Higher values will increase the rate of zoom when the zoomIn/zoomOut buttons are held. Note that values
+ * < 1.0 will reverse the operation of the zoomIn/zoomOut buttons (zoomIn button will decrease the zoom, zoomOut will
+ * increase the zoom).
*
* @property {Boolean} [showNavigator=false]
* Set to true to make the navigator minimap appear.
@@ -647,6 +656,9 @@
* @typedef {Object} GestureSettings
* @memberof OpenSeadragon
*
+ * @property {Boolean} dragToPan
+ * Set to false to disable panning on drag gestures.
+ *
* @property {Boolean} scrollToZoom
* Set to false to disable zooming on scroll gestures.
*
@@ -735,7 +747,7 @@
*
*/
-
+/* eslint-disable no-redeclare */
function OpenSeadragon( options ){
return new OpenSeadragon.Viewer( options );
}
@@ -917,13 +929,65 @@ function OpenSeadragon( options ){
return isTainted;
};
+ /**
+ * True if the browser supports the EventTarget.addEventListener() method
+ * @member {Boolean} supportsAddEventListener
+ * @memberof OpenSeadragon
+ */
+ $.supportsAddEventListener = (function () {
+ return !!(document.documentElement.addEventListener && document.addEventListener);
+ }());
+
+ /**
+ * True if the browser supports the EventTarget.removeEventListener() method
+ * @member {Boolean} supportsRemoveEventListener
+ * @memberof OpenSeadragon
+ */
+ $.supportsRemoveEventListener = (function () {
+ return !!(document.documentElement.removeEventListener && document.removeEventListener);
+ }());
+
+ /**
+ * True if the browser supports the newer EventTarget.addEventListener options argument
+ * @member {Boolean} supportsEventListenerOptions
+ * @memberof OpenSeadragon
+ */
+ $.supportsEventListenerOptions = (function () {
+ var supported = 0;
+
+ if ( $.supportsAddEventListener ) {
+ try {
+ var options = {
+ get capture() {
+ supported++;
+ return false;
+ },
+ get once() {
+ supported++;
+ return false;
+ },
+ get passive() {
+ supported++;
+ return false;
+ }
+ };
+ window.addEventListener("test", null, options);
+ window.removeEventListener("test", null, options);
+ } catch ( e ) {
+ supported = 0;
+ }
+ }
+
+ return supported >= 3;
+ }());
+
/**
* A ratio comparing the device screen's pixel density to the canvas's backing store pixel density,
* clamped to a minimum of 1. Defaults to 1 if canvas isn't supported by the browser.
* @member {Number} pixelDensityRatio
* @memberof OpenSeadragon
*/
- $.pixelDensityRatio = (function () {
+ $.getCurrentPixelDensityRatio = function() {
if ( $.supportsCanvas ) {
var context = document.createElement('canvas').getContext('2d');
var devicePixelRatio = window.devicePixelRatio || 1;
@@ -936,13 +1000,19 @@ function OpenSeadragon( options ){
} else {
return 1;
}
- }());
+ };
+
+ /**
+ * @member {Number} pixelDensityRatio
+ * @memberof OpenSeadragon
+ */
+ $.pixelDensityRatio = $.getCurrentPixelDensityRatio();
}( OpenSeadragon ));
/**
* This closure defines all static methods available to the OpenSeadragon
- * namespace. Many, if not most, are taked directly from jQuery for use
+ * namespace. Many, if not most, are taken directly from jQuery for use
* to simplify and reduce common programming patterns. More static methods
* from jQuery may eventually make their way into this though we are
* attempting to avoid an explicit dependency on jQuery only because
@@ -1081,6 +1151,7 @@ function OpenSeadragon( options ){
springStiffness: 6.5,
animationTime: 1.2,
gestureSettingsMouse: {
+ dragToPan: true,
scrollToZoom: true,
clickToZoom: true,
dblClickToZoom: false,
@@ -1092,6 +1163,7 @@ function OpenSeadragon( options ){
pinchRotate: false
},
gestureSettingsTouch: {
+ dragToPan: true,
scrollToZoom: false,
clickToZoom: false,
dblClickToZoom: true,
@@ -1103,6 +1175,7 @@ function OpenSeadragon( options ){
pinchRotate: false
},
gestureSettingsPen: {
+ dragToPan: true,
scrollToZoom: false,
clickToZoom: true,
dblClickToZoom: false,
@@ -1114,6 +1187,7 @@ function OpenSeadragon( options ){
pinchRotate: false
},
gestureSettingsUnknown: {
+ dragToPan: true,
scrollToZoom: false,
clickToZoom: false,
dblClickToZoom: true,
@@ -1315,6 +1389,8 @@ function OpenSeadragon( options ){
* @property {Number} SAFARI
* @property {Number} CHROME
* @property {Number} OPERA
+ * @property {Number} EDGE
+ * @property {Number} CHROMEEDGE
*/
BROWSERS: {
UNKNOWN: 0,
@@ -1322,9 +1398,31 @@ function OpenSeadragon( options ){
FIREFOX: 2,
SAFARI: 3,
CHROME: 4,
- OPERA: 5
+ OPERA: 5,
+ EDGE: 6,
+ CHROMEEDGE: 7
},
+ /**
+ * Keep track of which {@link Viewer}s have been created.
+ * - Key: {@link Element} to which a Viewer is attached.
+ * - Value: {@link Viewer} of the element defined by the key.
+ * @private
+ * @static
+ * @type {Object}
+ */
+ _viewers: new Map(),
+
+ /**
+ * Returns the {@link Viewer} attached to a given DOM element. If there is
+ * no viewer attached to the provided element, undefined is returned.
+ * @function
+ * @param {String|Element} element Accepts an id or element.
+ * @returns {Viewer} The viewer attached to the given element, or undefined.
+ */
+ getViewer: function(element) {
+ return $._viewers.get(this.getElement(element));
+ },
/**
* Returns a DOM Element for the given id or element.
@@ -1333,7 +1431,7 @@ function OpenSeadragon( options ){
* @returns {Element} The element with the given id, null, or the element itself.
*/
getElement: function( element ) {
- if ( typeof ( element ) == "string" ) {
+ if ( typeof ( element ) === "string" ) {
element = document.getElementById( element );
}
return element;
@@ -1352,7 +1450,7 @@ function OpenSeadragon( options ){
offsetParent;
element = $.getElement( element );
- isFixed = $.getElementStyle( element ).position == "fixed";
+ isFixed = $.getElementStyle( element ).position === "fixed";
offsetParent = getOffsetParent( element, isFixed );
while ( offsetParent ) {
@@ -1365,7 +1463,7 @@ function OpenSeadragon( options ){
}
element = offsetParent;
- isFixed = $.getElementStyle( element ).position == "fixed";
+ isFixed = $.getElementStyle( element ).position === "fixed";
offsetParent = getOffsetParent( element, isFixed );
}
@@ -1397,7 +1495,7 @@ function OpenSeadragon( options ){
boundingRect = element.getBoundingClientRect();
}
- win = ( doc == doc.window ) ?
+ win = ( doc === doc.window ) ?
doc :
( doc.nodeType === 9 ) ?
doc.defaultView || doc.parentWindow :
@@ -1517,29 +1615,6 @@ function OpenSeadragon( options ){
},
- /**
- * Gets the latest event, really only useful internally since its
- * specific to IE behavior.
- * @function
- * @param {Event} [event]
- * @returns {Event}
- * @deprecated For internal use only
- * @private
- */
- getEvent: function( event ) {
- if( event ){
- $.getEvent = function( event ) {
- return event;
- };
- } else {
- $.getEvent = function() {
- return window.event;
- };
- }
- return $.getEvent( event );
- },
-
-
/**
* Gets the position of the mouse on the screen for a given event.
* @function
@@ -1548,21 +1623,19 @@ function OpenSeadragon( options ){
*/
getMousePosition: function( event ) {
- if ( typeof ( event.pageX ) == "number" ) {
+ if ( typeof ( event.pageX ) === "number" ) {
$.getMousePosition = function( event ){
var result = new $.Point();
- event = $.getEvent( event );
result.x = event.pageX;
result.y = event.pageY;
return result;
};
- } else if ( typeof ( event.clientX ) == "number" ) {
+ } else if ( typeof ( event.clientX ) === "number" ) {
$.getMousePosition = function( event ){
var result = new $.Point();
- event = $.getEvent( event );
result.x =
event.clientX +
document.body.scrollLeft +
@@ -1593,7 +1666,7 @@ function OpenSeadragon( options ){
var docElement = document.documentElement || {},
body = document.body || {};
- if ( typeof ( window.pageXOffset ) == "number" ) {
+ if ( typeof ( window.pageXOffset ) === "number" ) {
$.getPageScroll = function(){
return new $.Point(
window.pageXOffset,
@@ -1670,7 +1743,7 @@ function OpenSeadragon( options ){
};
}
- return $.setPageScroll( scroll );
+ $.setPageScroll( scroll );
},
/**
@@ -1682,7 +1755,7 @@ function OpenSeadragon( options ){
var docElement = document.documentElement || {},
body = document.body || {};
- if ( typeof ( window.innerWidth ) == 'number' ) {
+ if ( typeof ( window.innerWidth ) === 'number' ) {
$.getWindowSize = function(){
return new $.Point(
window.innerWidth,
@@ -1798,51 +1871,16 @@ function OpenSeadragon( options ){
/**
* Ensures an image is loaded correctly to support alpha transparency.
- * Generally only IE has issues doing this correctly for formats like
- * png.
* @function
* @param {String} src
* @returns {Element}
*/
makeTransparentImage: function( src ) {
+ var img = $.makeNeutralElement( "img" );
- $.makeTransparentImage = function( src ){
- var img = $.makeNeutralElement( "img" );
+ img.src = src;
- img.src = src;
-
- return img;
- };
-
- if ( $.Browser.vendor == $.BROWSERS.IE && $.Browser.version < 7 ) {
-
- $.makeTransparentImage = function( src ){
- var img = $.makeNeutralElement( "img" ),
- element = null;
-
- element = $.makeNeutralElement("span");
- element.style.display = "inline-block";
-
- img.onload = function() {
- element.style.width = element.style.width || img.width + "px";
- element.style.height = element.style.height || img.height + "px";
-
- img.onload = null;
- img = null; // to prevent memory leaks in IE
- };
-
- img.src = src;
- element.style.filter =
- "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" +
- src +
- "', sizingMethod='scale')";
-
- return element;
- };
-
- }
-
- return $.makeTransparentImage( src );
+ return img;
},
@@ -1893,6 +1931,30 @@ function OpenSeadragon( options ){
},
+ /**
+ * Sets the specified element's pointer-events style attribute to the passed value.
+ * @function
+ * @param {Element|String} element
+ * @param {String} value
+ */
+ setElementPointerEvents: function( element, value ) {
+ element = $.getElement( element );
+ if ( typeof element.style.pointerEvents !== 'undefined' ) {
+ element.style.pointerEvents = value;
+ }
+ },
+
+
+ /**
+ * Sets the specified element's pointer-events style attribute to 'none'.
+ * @function
+ * @param {Element|String} element
+ */
+ setElementPointerEventsNone: function( element ) {
+ $.setElementPointerEvents( element, 'none' );
+ },
+
+
/**
* Add the specified CSS class to the element if not present.
* @function
@@ -1978,6 +2040,34 @@ function OpenSeadragon( options ){
element.className = newClasses.join(' ');
},
+ /**
+ * Convert passed addEventListener() options to boolean or options object,
+ * depending on browser support.
+ * @function
+ * @param {Boolean|Object} [options] Boolean useCapture, or if [supportsEventListenerOptions]{@link OpenSeadragon.supportsEventListenerOptions}, can be an object
+ * @param {Boolean} [options.capture]
+ * @param {Boolean} [options.passive]
+ * @param {Boolean} [options.once]
+ * @return {String} The protocol (http:, https:, file:, ftp: ...)
+ */
+ normalizeEventListenerOptions: function (options) {
+ var opts;
+ if ( typeof options !== 'undefined' ) {
+ if ( typeof options === 'boolean' ) {
+ // Legacy Boolean useCapture
+ opts = $.supportsEventListenerOptions ? { capture: options } : options;
+ } else {
+ // Options object
+ opts = $.supportsEventListenerOptions ? options :
+ ( ( typeof options.capture !== 'undefined' ) ? options.capture : false );
+ }
+ } else {
+ // No options specified - Legacy optional useCapture argument
+ // (for IE, first supported on version 9, so we'll pass a Boolean)
+ opts = $.supportsEventListenerOptions ? { capture: false } : false;
+ }
+ return opts;
+ },
/**
* Adds an event listener for the given element, eventName and handler.
@@ -1985,16 +2075,20 @@ function OpenSeadragon( options ){
* @param {Element|String} element
* @param {String} eventName
* @param {Function} handler
- * @param {Boolean} [useCapture]
+ * @param {Boolean|Object} [options] Boolean useCapture, or if [supportsEventListenerOptions]{@link OpenSeadragon.supportsEventListenerOptions}, can be an object
+ * @param {Boolean} [options.capture]
+ * @param {Boolean} [options.passive]
+ * @param {Boolean} [options.once]
*/
addEvent: (function () {
- if ( window.addEventListener ) {
- return function ( element, eventName, handler, useCapture ) {
+ if ( $.supportsAddEventListener ) {
+ return function ( element, eventName, handler, options ) {
+ options = $.normalizeEventListenerOptions(options);
element = $.getElement( element );
- element.addEventListener( eventName, handler, useCapture );
+ element.addEventListener( eventName, handler, options );
};
- } else if ( window.attachEvent ) {
- return function ( element, eventName, handler, useCapture ) {
+ } else if ( document.documentElement.attachEvent && document.attachEvent ) {
+ return function ( element, eventName, handler ) {
element = $.getElement( element );
element.attachEvent( 'on' + eventName, handler );
};
@@ -2011,16 +2105,18 @@ function OpenSeadragon( options ){
* @param {Element|String} element
* @param {String} eventName
* @param {Function} handler
- * @param {Boolean} [useCapture]
+ * @param {Boolean|Object} [options] Boolean useCapture, or if [supportsEventListenerOptions]{@link OpenSeadragon.supportsEventListenerOptions}, can be an object
+ * @param {Boolean} [options.capture]
*/
removeEvent: (function () {
- if ( window.removeEventListener ) {
- return function ( element, eventName, handler, useCapture ) {
+ if ( $.supportsRemoveEventListener ) {
+ return function ( element, eventName, handler, options ) {
+ options = $.normalizeEventListenerOptions(options);
element = $.getElement( element );
- element.removeEventListener( eventName, handler, useCapture );
+ element.removeEventListener( eventName, handler, options );
};
- } else if ( window.detachEvent ) {
- return function( element, eventName, handler, useCapture ) {
+ } else if ( document.documentElement.detachEvent && document.detachEvent ) {
+ return function( element, eventName, handler ) {
element = $.getElement( element );
element.detachEvent( 'on' + eventName, handler );
};
@@ -2037,49 +2133,28 @@ function OpenSeadragon( options ){
* @param {Event} [event]
*/
cancelEvent: function( event ) {
- event = $.getEvent( event );
-
- if ( event.preventDefault ) {
- $.cancelEvent = function( event ){
- // W3C for preventing default
- event.preventDefault();
- };
- } else {
- $.cancelEvent = function( event ){
- event = $.getEvent( event );
- // legacy for preventing default
- event.cancel = true;
- // IE for preventing default
- event.returnValue = false;
- };
- }
- $.cancelEvent( event );
+ event.preventDefault();
},
/**
- * Stops the propagation of the event up the DOM.
+ * Returns true if {@link OpenSeadragon.cancelEvent|cancelEvent} has been called on
+ * the event, otherwise returns false.
+ * @function
+ * @param {Event} [event]
+ */
+ eventIsCanceled: function( event ) {
+ return event.defaultPrevented;
+ },
+
+
+ /**
+ * Stops the propagation of the event through the DOM in the capturing and bubbling phases.
* @function
* @param {Event} [event]
*/
stopEvent: function( event ) {
- event = $.getEvent( event );
-
- if ( event.stopPropagation ) {
- // W3C for stopping propagation
- $.stopEvent = function( event ){
- event.stopPropagation();
- };
- } else {
- // IE for stopping propagation
- $.stopEvent = function( event ){
- event = $.getEvent( event );
- event.cancelBubble = true;
- };
-
- }
-
- $.stopEvent( event );
+ event.stopPropagation();
},
@@ -2227,7 +2302,7 @@ function OpenSeadragon( options ){
request.onreadystatechange = function() {
// 4 = DONE (https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#Properties)
- if ( request.readyState == 4 ) {
+ if ( request.readyState === 4 ) {
request.onreadystatechange = function(){};
// With protocols other than http/https, a successful request status is in
@@ -2256,7 +2331,7 @@ function OpenSeadragon( options ){
if (headers) {
for (var headerName in headers) {
- if (headers.hasOwnProperty(headerName) && headers[headerName]) {
+ if (Object.prototype.hasOwnProperty.call(headers, headerName) && headers[headerName]) {
request.setRequestHeader(headerName, headers[headerName]);
}
}
@@ -2268,62 +2343,12 @@ function OpenSeadragon( options ){
request.send(null);
} catch (e) {
- var msg = e.message;
-
- /*
- IE < 10 does not support CORS and an XHR request to a different origin will fail as soon
- as send() is called. This is particularly easy to miss during development and appear in
- production if you use a CDN or domain sharding and the security policy is likely to break
- exception handlers since any attempt to access a property of the request object will
- raise an access denied TypeError inside the catch block.
-
- To be friendlier, we'll check for this specific error and add a documentation pointer
- to point developers in the right direction. We test the exception number because IE's
- error messages are localized.
- */
- var oldIE = $.Browser.vendor == $.BROWSERS.IE && $.Browser.version < 10;
- if ( oldIE && typeof ( e.number ) != "undefined" && e.number == -2147024891 ) {
- msg += "\nSee http://msdn.microsoft.com/en-us/library/ms537505(v=vs.85).aspx#xdomain";
- }
-
- $.console.log( "%s while making AJAX request: %s", e.name, msg );
+ $.console.log( "%s while making AJAX request: %s", e.name, e.message );
request.onreadystatechange = function(){};
- if (window.XDomainRequest) { // IE9 or IE8 might as well try to use XDomainRequest
- var xdr = new XDomainRequest();
- if (xdr) {
- xdr.onload = function (e) {
- if ( $.isFunction( onSuccess ) ) {
- onSuccess({ // Faking an xhr object
- responseText: xdr.responseText,
- status: 200, // XDomainRequest doesn't support status codes, so we just fake one! :/
- statusText: 'OK'
- });
- }
- };
- xdr.onerror = function (e) {
- if ($.isFunction(onError)) {
- onError({ // Faking an xhr object
- responseText: xdr.responseText,
- status: 444, // 444 No Response
- statusText: 'An error happened. Due to an XDomainRequest deficiency we can not extract any information about this error. Upgrade your browser.'
- });
- }
- };
- try {
- xdr.open('GET', url);
- xdr.send();
- } catch (e2) {
- if ( $.isFunction( onError ) ) {
- onError( request, e );
- }
- }
- }
- } else {
- if ( $.isFunction( onError ) ) {
- onError( request, e );
- }
+ if ( $.isFunction( onError ) ) {
+ onError( request, e );
}
}
@@ -2353,7 +2378,7 @@ function OpenSeadragon( options ){
callbackParam = options.param || 'callback',
callback = options.callback;
- url = url.replace( /(\=)\?(&|$)|\?\?/i, replace );
+ url = url.replace( /(=)\?(&|$)|\?\?/i, replace );
// Add callback manually
url += (/\?/.test( url ) ? "&" : "?") + callbackParam + "=" + jsonpCallback;
@@ -2462,16 +2487,7 @@ function OpenSeadragon( options ){
* @returns {Object}
*/
parseJSON: function(string) {
- if (window.JSON && window.JSON.parse) {
- $.parseJSON = window.JSON.parse;
- } else {
- // Should only be used by IE8 in non standards mode
- $.parseJSON = function(string) {
- /*jshint evil:true*/
- //eslint-disable-next-line no-eval
- return eval('(' + string + ')');
- };
- }
+ $.parseJSON = window.JSON.parse;
return $.parseJSON(string);
},
@@ -2486,11 +2502,61 @@ function OpenSeadragon( options ){
extension = extension ? extension : "";
// eslint-disable-next-line no-use-before-define
return !!FILEFORMATS[ extension.toLowerCase() ];
+ },
+
+ /**
+ * Updates supported image formats with user-specified values.
+ * Preexisting formats that are not being updated are left unchanged.
+ * By default, the defined formats are
+ * {
+ * bmp: false,
+ * jpeg: true,
+ * jpg: true,
+ * png: true,
+ * tif: false,
+ * wdp: false
+ * }
+ *
+ * @function
+ * @example
+ * // sets webp as supported and png as unsupported
+ * setImageFormatsSupported({webp: true, png: false});
+ * @param {Object} formats An object containing format extensions as
+ * keys and booleans as values.
+ */
+ setImageFormatsSupported: function(formats) {
+ // eslint-disable-next-line no-use-before-define
+ $.extend(FILEFORMATS, formats);
}
});
+ //TODO: $.console is often used inside a try/catch block which generally
+ // prevents allowings errors to occur with detection until a debugger
+ // is attached. Although I've been guilty of the same anti-pattern
+ // I eventually was convinced that errors should naturally propagate in
+ // all but the most special cases.
+ /**
+ * A convenient alias for console when available, and a simple null
+ * function when console is unavailable.
+ * @static
+ * @private
+ */
+ var nullfunction = function( msg ){
+ //document.location.hash = msg;
+ };
+
+ $.console = window.console || {
+ log: nullfunction,
+ debug: nullfunction,
+ info: nullfunction,
+ warn: nullfunction,
+ error: nullfunction,
+ assert: nullfunction
+ };
+
+
/**
* The current browser vendor, version, and related information regarding detected features.
* @member {Object} Browser
@@ -2509,12 +2575,12 @@ function OpenSeadragon( options ){
var FILEFORMATS = {
- "bmp": false,
- "jpeg": true,
- "jpg": true,
- "png": true,
- "tif": false,
- "wdp": false
+ bmp: false,
+ jpeg: true,
+ jpg: true,
+ png: true,
+ tif: false,
+ wdp: false
},
URLPARAMS = {};
@@ -2544,7 +2610,17 @@ function OpenSeadragon( options ){
break;
case "Netscape":
if (window.addEventListener) {
- if ( ua.indexOf( "Firefox" ) >= 0 ) {
+ if ( ua.indexOf( "Edge" ) >= 0 ) {
+ $.Browser.vendor = $.BROWSERS.EDGE;
+ $.Browser.version = parseFloat(
+ ua.substring( ua.indexOf( "Edge" ) + 5 )
+ );
+ } else if ( ua.indexOf( "Edg" ) >= 0 ) {
+ $.Browser.vendor = $.BROWSERS.CHROMEEDGE;
+ $.Browser.version = parseFloat(
+ ua.substring( ua.indexOf( "Edg" ) + 4 )
+ );
+ } else if ( ua.indexOf( "Firefox" ) >= 0 ) {
$.Browser.vendor = $.BROWSERS.FIREFOX;
$.Browser.version = parseFloat(
ua.substring( ua.indexOf( "Firefox" ) + 8 )
@@ -2586,56 +2662,30 @@ function OpenSeadragon( options ){
sep = part.indexOf( '=' );
if ( sep > 0 ) {
- URLPARAMS[ part.substring( 0, sep ) ] =
- decodeURIComponent( part.substring( sep + 1 ) );
+ var key = part.substring( 0, sep ),
+ value = part.substring( sep + 1 );
+ try {
+ URLPARAMS[ key ] = decodeURIComponent( value );
+ } catch (e) {
+ $.console.error( "Ignoring malformed URL parameter: %s=%s", key, value );
+ }
}
}
//determine if this browser supports image alpha transparency
$.Browser.alpha = !(
- (
- $.Browser.vendor == $.BROWSERS.IE &&
- $.Browser.version < 9
- ) || (
- $.Browser.vendor == $.BROWSERS.CHROME &&
- $.Browser.version < 2
- )
+ $.Browser.vendor === $.BROWSERS.CHROME && $.Browser.version < 2
);
//determine if this browser supports element.style.opacity
- $.Browser.opacity = !(
- $.Browser.vendor == $.BROWSERS.IE &&
- $.Browser.version < 9
- );
+ $.Browser.opacity = true;
+ if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 11 ) {
+ $.console.error('Internet Explorer versions < 11 are not supported by OpenSeadragon');
+ }
})();
- //TODO: $.console is often used inside a try/catch block which generally
- // prevents allowings errors to occur with detection until a debugger
- // is attached. Although I've been guilty of the same anti-pattern
- // I eventually was convinced that errors should naturally propagate in
- // all but the most special cases.
- /**
- * A convenient alias for console when available, and a simple null
- * function when console is unavailable.
- * @static
- * @private
- */
- var nullfunction = function( msg ){
- //document.location.hash = msg;
- };
-
- $.console = window.console || {
- log: nullfunction,
- debug: nullfunction,
- info: nullfunction,
- warn: nullfunction,
- error: nullfunction,
- assert: nullfunction
- };
-
-
// Adding support for HTML5's requestAnimationFrame as suggested by acdha.
// Implementation taken from matt synder's post here:
// http://mattsnider.com/cross-browser-and-legacy-supported-requestframeanimation/
@@ -2731,7 +2781,7 @@ function OpenSeadragon( options ){
* @returns {Element}
*/
function getOffsetParent( element, isFixed ) {
- if ( isFixed && element != document.body ) {
+ if ( isFixed && element !== document.body ) {
return document.body;
} else {
return element.offsetParent;
diff --git a/src/osmtilesource.js b/src/osmtilesource.js
index 555e0a9f..c28945bb 100644
--- a/src/osmtilesource.js
+++ b/src/osmtilesource.js
@@ -113,7 +113,7 @@ $.extend( $.OsmTileSource.prototype, $.TileSource.prototype, /** @lends OpenSead
supports: function( data, url ){
return (
data.type &&
- "openstreetmaps" == data.type
+ "openstreetmaps" === data.type
);
},
diff --git a/src/overlay.js b/src/overlay.js
index af22fa37..9fd1532c 100644
--- a/src/overlay.js
+++ b/src/overlay.js
@@ -288,10 +288,7 @@
style[transformProp] = "";
}
}
-
- if (style.display !== 'none') {
- style.display = 'block';
- }
+ style.display = 'block';
}
},
diff --git a/src/point.js b/src/point.js
index 7e55ec53..be7ff6d3 100644
--- a/src/point.js
+++ b/src/point.js
@@ -50,13 +50,13 @@ $.Point = function( x, y ) {
* @member {Number} x
* @memberof OpenSeadragon.Point#
*/
- this.x = typeof ( x ) == "number" ? x : 0;
+ this.x = typeof ( x ) === "number" ? x : 0;
/**
* The vector component 'y'.
* @member {Number} y
* @memberof OpenSeadragon.Point#
*/
- this.y = typeof ( y ) == "number" ? y : 0;
+ this.y = typeof ( y ) === "number" ? y : 0;
};
/** @lends OpenSeadragon.Point.prototype */
diff --git a/src/referencestrip.js b/src/referencestrip.js
index fb747558..20dd9ea9 100644
--- a/src/referencestrip.js
+++ b/src/referencestrip.js
@@ -85,24 +85,19 @@ $.ReferenceStrip = function ( options ) {
scroll: $.DEFAULT_SETTINGS.referenceStripScroll,
clickTimeThreshold: $.DEFAULT_SETTINGS.clickTimeThreshold
}, options, {
- //required overrides
- element: this.element,
- //These need to be overridden to prevent recursion since
- //the navigator is a viewer and a viewer has a navigator
- showNavigator: false,
- mouseNavEnabled: false,
- showNavigationControl: false,
- showSequenceControl: false
+ element: this.element
} );
$.extend( this, options );
//Private state properties
THIS[this.id] = {
- "animating": false
+ animating: false
};
this.minPixelRatio = this.viewer.minPixelRatio;
+ this.element.tabIndex = 0;
+
style = this.element.style;
style.marginTop = '0px';
style.marginRight = '0px';
@@ -119,14 +114,21 @@ $.ReferenceStrip = function ( options ) {
$.setElementOpacity( this.element, 0.8 );
this.viewer = viewer;
- this.innerTracker = new $.MouseTracker( {
+ this.tracker = new $.MouseTracker( {
+ userData: 'ReferenceStrip.tracker',
element: this.element,
+ clickHandler: $.delegate( this, onStripClick ),
dragHandler: $.delegate( this, onStripDrag ),
scrollHandler: $.delegate( this, onStripScroll ),
enterHandler: $.delegate( this, onStripEnter ),
- exitHandler: $.delegate( this, onStripExit ),
+ leaveHandler: $.delegate( this, onStripLeave ),
keyDownHandler: $.delegate( this, onKeyDown ),
- keyHandler: $.delegate( this, onKeyPress )
+ keyHandler: $.delegate( this, onKeyPress ),
+ preProcessEventHandler: function (eventInfo) {
+ if (eventInfo.eventType === 'wheel') {
+ eventInfo.preventDefault = true;
+ }
+ }
} );
//Controls the position and orientation of the reference strip and sets the
@@ -139,7 +141,7 @@ $.ReferenceStrip = function ( options ) {
{ anchor: $.ControlAnchor.BOTTOM_LEFT }
);
} else {
- if ( "horizontal" == options.scroll ) {
+ if ( "horizontal" === options.scroll ) {
this.element.style.width = (
viewerSize.x *
options.sizeRatio *
@@ -189,34 +191,12 @@ $.ReferenceStrip = function ( options ) {
element.style.width = _this.panelWidth + 'px';
element.style.height = _this.panelHeight + 'px';
element.style.display = 'inline';
- element.style.float = 'left'; //Webkit
+ element.style['float'] = 'left'; //Webkit
element.style.cssFloat = 'left'; //Firefox
element.style.styleFloat = 'left'; //IE
element.style.padding = '2px';
$.setElementTouchActionNone( element );
-
- element.innerTracker = new $.MouseTracker( {
- element: element,
- clickTimeThreshold: this.clickTimeThreshold,
- clickDistThreshold: this.clickDistThreshold,
- pressHandler: function ( event ) {
- event.eventSource.dragging = $.now();
- },
- releaseHandler: function ( event ) {
- var tracker = event.eventSource,
- id = tracker.element.id,
- page = Number( id.split( '-' )[2] ),
- now = $.now();
-
- if ( event.insideElementPressed &&
- event.insideElementReleased &&
- tracker.dragging &&
- ( now - tracker.dragging ) < tracker.clickTimeThreshold ) {
- tracker.dragging = null;
- viewer.goToPage( page );
- }
- }
- } );
+ $.setElementPointerEventsNone( element );
this.element.appendChild( element );
@@ -225,12 +205,13 @@ $.ReferenceStrip = function ( options ) {
this.panels.push( element );
}
- loadPanels( this, this.scroll == 'vertical' ? viewerSize.y : viewerSize.x, 0 );
+ loadPanels( this, this.scroll === 'vertical' ? viewerSize.y : viewerSize.x, 0 );
this.setFocus( 0 );
};
-$.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototype, /** @lends OpenSeadragon.ReferenceStrip.prototype */{
+/** @lends OpenSeadragon.ReferenceStrip.prototype */
+$.ReferenceStrip.prototype = {
/**
* @function
@@ -251,7 +232,7 @@ $.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototyp
this.currentSelected = element;
this.currentSelected.style.background = '#999';
- if ( 'horizontal' == this.scroll ) {
+ if ( 'horizontal' === this.scroll ) {
//right left
offset = ( Number( page ) ) * ( this.panelWidth + 3 );
if ( offset > offsetLeft + viewerSize.x - this.panelWidth ) {
@@ -277,7 +258,7 @@ $.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototyp
}
this.currentPage = page;
- onStripEnter.call( this, { eventSource: this.innerTracker } );
+ onStripEnter.call( this, { eventSource: this.tracker } );
}
},
@@ -292,7 +273,6 @@ $.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototyp
return false;
},
- // Overrides Viewer.destroy
destroy: function() {
if (this.miniViewers) {
for (var key in this.miniViewers) {
@@ -300,14 +280,36 @@ $.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototyp
}
}
+ this.tracker.destroy();
+
if (this.element) {
- this.element.parentNode.removeChild(this.element);
+ this.viewer.removeControl( this.element );
}
}
-} );
+};
+/**
+ * @private
+ * @inner
+ * @function
+ */
+function onStripClick( event ) {
+ if ( event.quick ) {
+ var page;
+
+ if ( 'horizontal' === this.scroll ) {
+ page = Math.floor(event.position.x / this.panelWidth);
+ } else {
+ page = Math.floor(event.position.y / this.panelHeight);
+ }
+
+ this.viewer.goToPage( page );
+ }
+
+ this.element.focus();
+}
/**
@@ -317,14 +319,15 @@ $.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototyp
*/
function onStripDrag( event ) {
- var offsetLeft = Number( this.element.style.marginLeft.replace( 'px', '' ) ),
+ this.dragging = true;
+ if ( this.element ) {
+ var offsetLeft = Number( this.element.style.marginLeft.replace( 'px', '' ) ),
offsetTop = Number( this.element.style.marginTop.replace( 'px', '' ) ),
scrollWidth = Number( this.element.style.width.replace( 'px', '' ) ),
scrollHeight = Number( this.element.style.height.replace( 'px', '' ) ),
viewerSize = $.getElementSize( this.viewer.canvas );
- this.dragging = true;
- if ( this.element ) {
- if ( 'horizontal' == this.scroll ) {
+
+ if ( 'horizontal' === this.scroll ) {
if ( -event.delta.x > 0 ) {
//forward
if ( offsetLeft > -( scrollWidth - viewerSize.x ) ) {
@@ -354,7 +357,6 @@ function onStripDrag( event ) {
}
}
}
- return false;
}
@@ -366,13 +368,14 @@ function onStripDrag( event ) {
* @function
*/
function onStripScroll( event ) {
- var offsetLeft = Number( this.element.style.marginLeft.replace( 'px', '' ) ),
+ if ( this.element ) {
+ var offsetLeft = Number( this.element.style.marginLeft.replace( 'px', '' ) ),
offsetTop = Number( this.element.style.marginTop.replace( 'px', '' ) ),
scrollWidth = Number( this.element.style.width.replace( 'px', '' ) ),
scrollHeight = Number( this.element.style.height.replace( 'px', '' ) ),
viewerSize = $.getElementSize( this.viewer.canvas );
- if ( this.element ) {
- if ( 'horizontal' == this.scroll ) {
+
+ if ( 'horizontal' === this.scroll ) {
if ( event.scroll > 0 ) {
//forward
if ( offsetLeft > -( scrollWidth - viewerSize.x ) ) {
@@ -401,9 +404,9 @@ function onStripScroll( event ) {
}
}
}
+
+ event.preventDefault = true;
}
- //cancels event
- return false;
}
@@ -412,10 +415,9 @@ function loadPanels( strip, viewerSize, scroll ) {
activePanelsStart,
activePanelsEnd,
miniViewer,
- style,
i,
element;
- if ( 'horizontal' == strip.scroll ) {
+ if ( 'horizontal' === strip.scroll ) {
panelSize = strip.panelWidth;
} else {
panelSize = strip.panelHeight;
@@ -451,36 +453,17 @@ function loadPanels( strip, viewerSize, scroll ) {
blendTime: 0,
animationTime: 0,
loadTilesWithAjax: strip.viewer.loadTilesWithAjax,
- ajaxHeaders: strip.viewer.ajaxHeaders
+ ajaxHeaders: strip.viewer.ajaxHeaders,
+ useCanvas: strip.useCanvas
} );
-
- miniViewer.displayRegion = $.makeNeutralElement( "div" );
- miniViewer.displayRegion.id = element.id + '-displayregion';
- miniViewer.displayRegion.className = 'displayregion';
-
- style = miniViewer.displayRegion.style;
- style.position = 'relative';
- style.top = '0px';
- style.left = '0px';
- style.fontSize = '0px';
- style.overflow = 'hidden';
- style.float = 'left'; //Webkit
- style.cssFloat = 'left'; //Firefox
- style.styleFloat = 'left'; //IE
- style.zIndex = 999999999;
- style.cursor = 'default';
- style.width = ( strip.panelWidth - 4 ) + 'px';
- style.height = ( strip.panelHeight - 4 ) + 'px';
-
- // TODO: What is this for? Future keyboard navigation support?
- miniViewer.displayRegion.innerTracker = new $.MouseTracker( {
- element: miniViewer.displayRegion,
- startDisabled: true
- } );
-
- element.getElementsByTagName( 'div' )[0].appendChild(
- miniViewer.displayRegion
- );
+ // Allow pointer events to pass through miniViewer's canvas/container
+ // elements so implicit pointer capture works on touch devices
+ $.setElementPointerEventsNone( miniViewer.canvas );
+ $.setElementPointerEventsNone( miniViewer.container );
+ // We'll use event delegation from the reference strip element instead of
+ // handling events on every miniViewer
+ miniViewer.innerTracker.setTracking( false );
+ miniViewer.outerTracker.setTracking( false );
strip.miniViewers[element.id] = miniViewer;
@@ -503,7 +486,7 @@ function onStripEnter( event ) {
//element.style.border = '1px solid #555';
//element.style.background = '#000';
- if ( 'horizontal' == this.scroll ) {
+ if ( 'horizontal' === this.scroll ) {
//element.style.paddingTop = "0px";
element.style.marginBottom = "0px";
@@ -514,7 +497,6 @@ function onStripEnter( event ) {
element.style.marginLeft = "0px";
}
- return false;
}
@@ -523,10 +505,10 @@ function onStripEnter( event ) {
* @inner
* @function
*/
-function onStripExit( event ) {
+function onStripLeave( event ) {
var element = event.eventSource.element;
- if ( 'horizontal' == this.scroll ) {
+ if ( 'horizontal' === this.scroll ) {
//element.style.paddingTop = "10px";
element.style.marginBottom = "-" + ( $.getElementSize( element ).y / 2 ) + "px";
@@ -537,7 +519,6 @@ function onStripExit( event ) {
element.style.marginLeft = "-" + ( $.getElementSize( element ).x / 2 ) + "px";
}
- return false;
}
@@ -549,26 +530,31 @@ function onStripExit( event ) {
function onKeyDown( event ) {
//console.log( event.keyCode );
- if ( !event.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) {
+ if ( !event.ctrl && !event.alt && !event.meta ) {
switch ( event.keyCode ) {
case 38: //up arrow
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
- return false;
+ event.preventDefault = true;
+ break;
case 40: //down arrow
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
- return false;
+ event.preventDefault = true;
+ break;
case 37: //left arrow
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
- return false;
+ event.preventDefault = true;
+ break;
case 39: //right arrow
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
- return false;
+ event.preventDefault = true;
+ break;
default:
//console.log( 'navigator keycode %s', event.keyCode );
- return true;
+ event.preventDefault = false;
+ break;
}
} else {
- return true;
+ event.preventDefault = false;
}
}
@@ -581,35 +567,42 @@ function onKeyDown( event ) {
function onKeyPress( event ) {
//console.log( event.keyCode );
- if ( !event.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) {
+ if ( !event.ctrl && !event.alt && !event.meta ) {
switch ( event.keyCode ) {
case 61: //=|+
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
- return false;
+ event.preventDefault = true;
+ break;
case 45: //-|_
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
- return false;
+ event.preventDefault = true;
+ break;
case 48: //0|)
case 119: //w
case 87: //W
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
- return false;
+ event.preventDefault = true;
+ break;
case 115: //s
case 83: //S
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
- return false;
+ event.preventDefault = true;
+ break;
case 97: //a
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
- return false;
+ event.preventDefault = true;
+ break;
case 100: //d
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
- return false;
+ event.preventDefault = true;
+ break;
default:
//console.log( 'navigator keycode %s', event.keyCode );
- return true;
+ event.preventDefault = false;
+ break;
}
} else {
- return true;
+ event.preventDefault = false;
}
}
diff --git a/src/spring.js b/src/spring.js
index 9c314b9f..8aa17b84 100644
--- a/src/spring.js
+++ b/src/spring.js
@@ -50,11 +50,11 @@
$.Spring = function( options ) {
var args = arguments;
- if( typeof ( options ) != 'object' ){
+ if( typeof ( options ) !== 'object' ){
//allows backward compatible use of ( initialValue, config ) as
//constructor parameters
options = {
- initial: args.length && typeof ( args[ 0 ] ) == "number" ?
+ initial: args.length && typeof ( args[ 0 ] ) === "number" ?
args[ 0 ] :
undefined,
/**
@@ -96,7 +96,7 @@ $.Spring = function( options ) {
* @property {Number} time
*/
this.current = {
- value: typeof ( this.initial ) == "number" ?
+ value: typeof ( this.initial ) === "number" ?
this.initial :
(this._exponential ? 0 : 1),
time: $.now() // always work in milliseconds
@@ -237,7 +237,7 @@ $.Spring.prototype = {
this.current.value = currentValue;
}
- return oldValue != this.current.value;
+ return oldValue !== this.current.value;
},
/**
diff --git a/src/strings.js b/src/strings.js
index 7d0cbdd1..bf960cf9 100644
--- a/src/strings.js
+++ b/src/strings.js
@@ -82,7 +82,7 @@ $.extend( $, /** @lends OpenSeadragon */{
}
string = container[ props[ i ] ];
- if ( typeof ( string ) != "string" ) {
+ if ( typeof ( string ) !== "string" ) {
$.console.log( "Untranslated source string:", prop );
string = ""; // FIXME: this breaks gettext()-style convention, which would return source
}
diff --git a/src/tile.js b/src/tile.js
index 74746165..7027756b 100644
--- a/src/tile.js
+++ b/src/tile.js
@@ -176,6 +176,12 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, aja
* @memberof OpenSeadragon.Tile#
*/
this.size = null;
+ /**
+ * Whether to flip the tile when rendering.
+ * @member {Boolean} flipped
+ * @memberof OpenSeadragon.Tile#
+ */
+ this.flipped = false;
/**
* The start time of this tile's blending.
* @member {Number} blendStart
@@ -284,10 +290,10 @@ $.Tile.prototype = {
this.style = this.element.style;
this.style.position = "absolute";
}
- if ( this.element.parentNode != container ) {
+ if ( this.element.parentNode !== container ) {
container.appendChild( this.element );
}
- if ( this.imgElement.parentNode != this.element ) {
+ if ( this.imgElement.parentNode !== this.element ) {
this.element.appendChild( this.imgElement );
}
@@ -296,6 +302,10 @@ $.Tile.prototype = {
this.style.height = this.size.y + "px";
this.style.width = this.size.x + "px";
+ if (this.flipped) {
+ this.style.transform = "scaleX(-1)";
+ }
+
$.setElementOpacity( this.element, this.opacity );
},
@@ -383,13 +393,17 @@ $.Tile.prototype = {
sourceHeight = rendered.canvas.height;
}
+ context.translate(position.x + size.x / 2, 0);
+ if (this.flipped) {
+ context.scale(-1, 1);
+ }
context.drawImage(
rendered.canvas,
0,
0,
sourceWidth,
sourceHeight,
- position.x,
+ -size.x / 2,
position.y,
size.x,
size.y
diff --git a/src/tilecache.js b/src/tilecache.js
index 05d4e9cd..b0745eb1 100644
--- a/src/tilecache.js
+++ b/src/tilecache.js
@@ -196,7 +196,7 @@ $.TileCache.prototype = {
worstLevel = worstTile.level;
if ( prevTime < worstTime ||
- ( prevTime == worstTime && prevLevel > worstLevel ) ) {
+ ( prevTime === worstTime && prevLevel > worstLevel ) ) {
worstTile = prevTile;
worstTileIndex = i;
worstTileRecord = prevTileRecord;
diff --git a/src/tiledimage.js b/src/tiledimage.js
index abe8c2da..95ac4f3c 100644
--- a/src/tiledimage.js
+++ b/src/tiledimage.js
@@ -326,6 +326,10 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
*/
destroy: function() {
this.reset();
+
+ if (this.source.destroy) {
+ this.source.destroy();
+ }
},
/**
@@ -388,6 +392,26 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
return bounds.rotate(this.getRotation(current), this._getRotationPoint(current));
},
+ /**
+ * @function
+ * @param {Number} level
+ * @param {Number} x
+ * @param {Number} y
+ * @returns {OpenSeadragon.Rect} Where this tile fits (in normalized coordinates).
+ */
+ getTileBounds: function( level, x, y ) {
+ var numTiles = this.source.getNumTiles(level);
+ var xMod = ( numTiles.x + ( x % numTiles.x ) ) % numTiles.x;
+ var yMod = ( numTiles.y + ( y % numTiles.y ) ) % numTiles.y;
+ var bounds = this.source.getTileBounds(level, xMod, yMod);
+ if (this.getFlip()) {
+ bounds.x = 1 - bounds.x - bounds.width;
+ }
+ bounds.x += (x - xMod) / numTiles.x;
+ bounds.y += (this._worldHeightCurrent / this._worldWidthCurrent) * ((y - yMod) / numTiles.y);
+ return bounds;
+ },
+
/**
* @returns {OpenSeadragon.Point} This TiledImage's content size, in original pixels.
*/
@@ -395,6 +419,15 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
return new $.Point(this.source.dimensions.x, this.source.dimensions.y);
},
+ /**
+ * @returns {OpenSeadragon.Point} The TiledImage's content size, in window coordinates.
+ */
+ getSizeInWindowCoordinates: function() {
+ var topLeft = this.imageToWindowCoordinates(new $.Point(0, 0));
+ var bottomRight = this.imageToWindowCoordinates(this.getContentSize());
+ return new $.Point(bottomRight.x - topLeft.x, bottomRight.y - topLeft.y);
+ },
+
// private
_viewportToImageDelta: function( viewerX, viewerY, current ) {
var scale = (current ? this._scaleSpring.current.value : this._scaleSpring.target.value);
@@ -674,6 +707,58 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
this._setScale(height / this.normHeight, immediately);
},
+ /**
+ * Sets an array of polygons to crop the TiledImage during draw tiles.
+ * The render function will use the default non-zero winding rule.
+ * @param {OpenSeadragon.Point[][]} polygons - represented in an array of point object in image coordinates.
+ * Example format: [
+ * [{x: 197, y:172}, {x: 226, y:172}, {x: 226, y:198}, {x: 197, y:198}], // First polygon
+ * [{x: 328, y:200}, {x: 330, y:199}, {x: 332, y:201}, {x: 329, y:202}] // Second polygon
+ * [{x: 321, y:201}, {x: 356, y:205}, {x: 341, y:250}] // Third polygon
+ * ]
+ */
+ setCroppingPolygons: function( polygons ) {
+
+ var isXYObject = function(obj) {
+ return obj instanceof $.Point || (typeof obj.x === 'number' && typeof obj.y === 'number');
+ };
+
+ var objectToSimpleXYObject = function(objs) {
+ return objs.map(function(obj) {
+ try {
+ if (isXYObject(obj)) {
+ return { x: obj.x, y: obj.y };
+ } else {
+ throw new Error();
+ }
+ } catch(e) {
+ throw new Error('A Provided cropping polygon point is not supported');
+ }
+ });
+ };
+
+ try {
+ if (!$.isArray(polygons)) {
+ throw new Error('Provided cropping polygon is not an array');
+ }
+ this._croppingPolygons = polygons.map(function(polygon){
+ return objectToSimpleXYObject(polygon);
+ });
+ } catch (e) {
+ $.console.error('[TiledImage.setCroppingPolygons] Cropping polygon format not supported');
+ $.console.error(e);
+ this._croppingPolygons = null;
+ }
+ },
+
+ /**
+ * Resets the cropping polygons, thus next render will remove all cropping
+ * polygon effects.
+ */
+ resetCroppingPolygons: function() {
+ this._croppingPolygons = null;
+ },
+
/**
* Positions and scales the TiledImage to fit in the specified bounds.
* Note: this method fires OpenSeadragon.TiledImage.event:bounds-change
@@ -776,6 +861,23 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
this.raiseEvent('clip-change');
},
+ /**
+ * @returns {Boolean} Whether the TiledImage should be flipped before rendering.
+ */
+ getFlip: function() {
+ return !!this.flipped;
+ },
+
+ /**
+ * @param {Boolean} flip Whether the TiledImage should be flipped before rendering.
+ * @fires OpenSeadragon.TiledImage.event:bounds-change
+ */
+ setFlip: function(flip) {
+ this.flipped = !!flip;
+ this._needsDraw = true;
+ this._raiseBoundsChange();
+ },
+
/**
* @returns {Number} The TiledImage's current opacity.
*/
@@ -1199,24 +1301,41 @@ function updateLevel(tiledImage, haveDrawn, drawLevel, level, levelOpacity,
var viewportCenter = tiledImage.viewport.pixelFromPoint(
tiledImage.viewport.getCenter());
+
+ if (tiledImage.getFlip()) {
+ // The right-most tile can be narrower than the others. When flipped,
+ // this tile is now on the left. Because it is narrower than the normal
+ // left-most tile, the subsequent tiles may not be wide enough to completely
+ // fill the viewport. Fix this by rendering an extra column of tiles. If we
+ // are not wrapping, make sure we never render more than the number of tiles
+ // in the image.
+ bottomRightTile.x += 1;
+ if (!tiledImage.wrapHorizontal) {
+ bottomRightTile.x = Math.min(bottomRightTile.x, numberOfTiles.x - 1);
+ }
+ }
+
for (var x = topLeftTile.x; x <= bottomRightTile.x; x++) {
for (var y = topLeftTile.y; y <= bottomRightTile.y; y++) {
- // Optimisation disabled with wrapping because getTileBounds does not
- // work correctly with x and y outside of the number of tiles
- if (!tiledImage.wrapHorizontal && !tiledImage.wrapVertical) {
- var tileBounds = tiledImage.source.getTileBounds(level, x, y);
- if (drawArea.intersection(tileBounds) === null) {
- // This tile is outside of the viewport, no need to draw it
- continue;
- }
+ var flippedX;
+ if (tiledImage.getFlip()) {
+ var xMod = ( numberOfTiles.x + ( x % numberOfTiles.x ) ) % numberOfTiles.x;
+ flippedX = x + numberOfTiles.x - xMod - xMod - 1;
+ } else {
+ flippedX = x;
+ }
+
+ if (drawArea.intersection(tiledImage.getTileBounds(level, flippedX, y)) === null) {
+ // This tile is outside of the viewport, no need to draw it
+ continue;
}
best = updateTile(
tiledImage,
drawLevel,
haveDrawn,
- x, y,
+ flippedX, y,
level,
levelOpacity,
levelVisibility,
@@ -1391,10 +1510,10 @@ function getTile(
tilesMatrix[ level ][ x ] = {};
}
- if ( !tilesMatrix[ level ][ x ][ y ] ) {
+ if ( !tilesMatrix[ level ][ x ][ y ] || !tilesMatrix[ level ][ x ][ y ].flipped !== !tiledImage.flipped ) {
xMod = ( numTiles.x + ( x % numTiles.x ) ) % numTiles.x;
yMod = ( numTiles.y + ( y % numTiles.y ) ) % numTiles.y;
- bounds = tileSource.getTileBounds( level, xMod, yMod );
+ bounds = tiledImage.getTileBounds( level, x, y );
sourceBounds = tileSource.getTileBounds( level, xMod, yMod, true );
exists = tileSource.tileExists( level, xMod, yMod );
url = tileSource.getTileUrl( level, xMod, yMod );
@@ -1413,9 +1532,6 @@ function getTile(
context2D = tileSource.getContext2D ?
tileSource.getContext2D(level, xMod, yMod) : undefined;
- bounds.x += ( x - xMod ) / numTiles.x;
- bounds.y += (worldHeight / worldWidth) * (( y - yMod ) / numTiles.y);
-
tile = new $.Tile(
level,
x,
@@ -1429,14 +1545,22 @@ function getTile(
sourceBounds
);
- if (xMod === numTiles.x - 1) {
- tile.isRightMost = true;
+ if (tiledImage.getFlip()) {
+ if (xMod === 0) {
+ tile.isRightMost = true;
+ }
+ } else {
+ if (xMod === numTiles.x - 1) {
+ tile.isRightMost = true;
+ }
}
if (yMod === numTiles.y - 1) {
tile.isBottomMost = true;
}
+ tile.flipped = tiledImage.flipped;
+
tilesMatrix[ level ][ x ][ y ] = tile;
}
@@ -1573,7 +1697,7 @@ function setTileLoaded(tiledImage, tile, image, cutoff, tileRequest) {
* @property {Image} image - The image of the tile.
* @property {OpenSeadragon.TiledImage} tiledImage - The tiled image of the loaded tile.
* @property {OpenSeadragon.Tile} tile - The tile which has been loaded.
- * @property {XMLHttpRequest} tiledImage - The AJAX request that loaded this tile (if applicable).
+ * @property {XMLHttpRequest} tileRequest - The AJAX request that loaded this tile (if applicable).
* @property {function} getCompletionCallback - A function giving a callback to call
* when the asynchronous processing of the image is done. The image will be
* marked as entirely loaded when the callback has been called once for each
@@ -1714,10 +1838,10 @@ function providesCoverage( coverage, level, x, y ) {
if ( x === undefined || y === undefined ) {
rows = coverage[ level ];
for ( i in rows ) {
- if ( rows.hasOwnProperty( i ) ) {
+ if ( Object.prototype.hasOwnProperty.call( rows, i ) ) {
cols = rows[ i ];
for ( j in cols ) {
- if ( cols.hasOwnProperty( j ) && !cols[ j ] ) {
+ if ( Object.prototype.hasOwnProperty.call( cols, j ) && !cols[ j ] ) {
return false;
}
}
@@ -1818,7 +1942,7 @@ function compareTiles( previousBest, tile ) {
if ( tile.visibility > previousBest.visibility ) {
return tile;
- } else if ( tile.visibility == previousBest.visibility ) {
+ } else if ( tile.visibility === previousBest.visibility ) {
if ( tile.squaredDistance < previousBest.squaredDistance ) {
return tile;
}
@@ -1877,14 +2001,15 @@ function drawTiles( tiledImage, lastDrawn ) {
// sketch canvas we are going to use for performance reasons.
bounds = tiledImage.viewport.viewportToViewerElementRectangle(
tiledImage.getClippedBounds(true))
- .getIntegerBoundingBox()
- .times($.pixelDensityRatio);
+ .getIntegerBoundingBox();
if(tiledImage._drawer.viewer.viewport.getFlip()) {
if (tiledImage.viewport.degrees !== 0 || tiledImage.getRotation(true) % 360 !== 0){
bounds.x = tiledImage._drawer.viewer.container.clientWidth - (bounds.x + bounds.width);
}
}
+
+ bounds = bounds.times($.pixelDensityRatio);
}
tiledImage._drawer._clear(true, bounds);
}
@@ -1932,6 +2057,28 @@ function drawTiles( tiledImage, lastDrawn ) {
usedClip = true;
}
+ if (tiledImage._croppingPolygons) {
+ tiledImage._drawer.saveContext(useSketch);
+ try {
+ var polygons = tiledImage._croppingPolygons.map(function (polygon) {
+ return polygon.map(function (coord) {
+ var point = tiledImage
+ .imageToViewportCoordinates(coord.x, coord.y, true)
+ .rotate(-tiledImage.getRotation(true), tiledImage._getRotationPoint(true));
+ var clipPoint = tiledImage._drawer.viewportCoordToDrawerCoord(point);
+ if (sketchScale) {
+ clipPoint = clipPoint.times(sketchScale);
+ }
+ return clipPoint;
+ });
+ });
+ tiledImage._drawer.clipWithPolygons(polygons, useSketch);
+ } catch (e) {
+ $.console.error(e);
+ }
+ usedClip = true;
+ }
+
if ( tiledImage.placeholderFillStyle && tiledImage._hasOpaqueTile === false ) {
var placeholderRect = tiledImage._drawer.viewportToDrawerRectangle(tiledImage.getBounds(true));
if (sketchScale) {
diff --git a/src/tilesource.js b/src/tilesource.js
index d85f5ee6..aab7f134 100644
--- a/src/tilesource.js
+++ b/src/tilesource.js
@@ -167,7 +167,7 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
* @memberof OpenSeadragon.TileSource#
*/
- if( 'string' == $.type( arguments[ 0 ] ) ){
+ if( 'string' === $.type( arguments[ 0 ] ) ){
this.url = arguments[0];
}
@@ -313,8 +313,8 @@ $.TileSource.prototype = {
*/
getPixelRatio: function( level ) {
var imageSizeScaled = this.dimensions.times( this.getLevelScale( level ) ),
- rx = 1.0 / imageSizeScaled.x,
- ry = 1.0 / imageSizeScaled.y;
+ rx = 1.0 / imageSizeScaled.x * $.pixelDensityRatio,
+ ry = 1.0 / imageSizeScaled.y * $.pixelDensityRatio;
return new $.Point(rx, ry);
},
@@ -500,7 +500,7 @@ $.TileSource.prototype = {
msg = "HTTP " + xhr.status + " attempting to load TileSource";
} catch ( e ) {
var formattedExc;
- if ( typeof ( exc ) == "undefined" || !exc.toString ) {
+ if ( typeof ( exc ) === "undefined" || !exc.toString ) {
formattedExc = "Unknown error";
} else {
formattedExc = exc.toString();
@@ -640,7 +640,7 @@ function processResponse( xhr ){
throw new Error( $.getString( "Errors.Security" ) );
} else if ( xhr.status !== 200 && xhr.status !== 0 ) {
status = xhr.status;
- statusText = ( status == 404 ) ?
+ statusText = ( status === 404 ) ?
"Not Found" :
xhr.statusText;
throw new Error( $.getString( "Errors.Status", status, statusText ) );
@@ -654,7 +654,7 @@ function processResponse( xhr ){
} catch (e){
data = xhr.responseText;
}
- }else if( responseText.match(/\s*[\{\[].*/) ){
+ }else if( responseText.match(/\s*[{[].*/) ){
try{
data = $.parseJSON(responseText);
} catch(e){
@@ -690,6 +690,8 @@ $.TileSource.determineType = function( tileSource, data, url ){
}
$.console.error( "No TileSource was able to open %s %s", url, data );
+
+ return null;
};
diff --git a/src/tmstilesource.js b/src/tmstilesource.js
index 66b7755c..abb8f2db 100644
--- a/src/tmstilesource.js
+++ b/src/tmstilesource.js
@@ -103,7 +103,7 @@ $.extend( $.TmsTileSource.prototype, $.TileSource.prototype, /** @lends OpenSead
* @param {String} optional - url
*/
supports: function( data, url ){
- return ( data.type && "tiledmapservice" == data.type );
+ return ( data.type && "tiledmapservice" === data.type );
},
/**
diff --git a/src/viewer.js b/src/viewer.js
index 2200287d..a879a0bd 100644
--- a/src/viewer.js
+++ b/src/viewer.js
@@ -156,7 +156,7 @@ $.Viewer = function( options ) {
drawer: null,
/**
* Keeps track of all of the tiled images in the scene.
- * @member {OpenSeadragon.Drawer} world
+ * @member {OpenSeadragon.World} world
* @memberof OpenSeadragon.Viewer#
*/
world: null,
@@ -200,19 +200,19 @@ $.Viewer = function( options ) {
//Private state properties
THIS[ this.hash ] = {
- "fsBoundsDelta": new $.Point( 1, 1 ),
- "prevContainerSize": null,
- "animating": false,
- "forceRedraw": false,
- "mouseInside": false,
- "group": null,
+ fsBoundsDelta: new $.Point( 1, 1 ),
+ prevContainerSize: null,
+ animating: false,
+ forceRedraw: false,
+ mouseInside: false,
+ group: null,
// whether we should be continuously zooming
- "zooming": false,
+ zooming: false,
// how much we should be continuously zooming by
- "zoomFactor": null,
- "lastZoomTime": null,
- "fullPage": false,
- "onfullscreenchange": null
+ zoomFactor: null,
+ lastZoomTime: null,
+ fullPage: false,
+ onfullscreenchange: null
};
this._sequenceIndex = 0;
@@ -220,6 +220,7 @@ $.Viewer = function( options ) {
this._updateRequestId = null;
this._loadQueue = [];
this.currentOverlays = [];
+ this._updatePixelDensityRatioBind = null;
this._lastScrollTime = $.now(); // variable used to help normalize the scroll event speed of different devices
@@ -267,6 +268,7 @@ $.Viewer = function( options ) {
style.top = "0px";
style.textAlign = "left"; // needed to protect against
}( this.container.style ));
+ $.setElementTouchActionNone( this.container );
this.container.insertBefore( this.canvas, this.container.firstChild );
this.element.appendChild( this.container );
@@ -280,12 +282,14 @@ $.Viewer = function( options ) {
this.docOverflow = document.documentElement.style.overflow;
this.innerTracker = new $.MouseTracker({
+ userData: 'Viewer.innerTracker',
element: this.canvas,
startDisabled: !this.mouseNavEnabled,
clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold,
dblClickTimeThreshold: this.dblClickTimeThreshold,
dblClickDistThreshold: this.dblClickDistThreshold,
+ contextMenuHandler: $.delegate( this, onCanvasContextMenu ),
keyDownHandler: $.delegate( this, onCanvasKeyDown ),
keyHandler: $.delegate( this, onCanvasKeyPress ),
clickHandler: $.delegate( this, onCanvasClick ),
@@ -293,7 +297,7 @@ $.Viewer = function( options ) {
dragHandler: $.delegate( this, onCanvasDrag ),
dragEndHandler: $.delegate( this, onCanvasDragEnd ),
enterHandler: $.delegate( this, onCanvasEnter ),
- exitHandler: $.delegate( this, onCanvasExit ),
+ leaveHandler: $.delegate( this, onCanvasLeave ),
pressHandler: $.delegate( this, onCanvasPress ),
releaseHandler: $.delegate( this, onCanvasRelease ),
nonPrimaryPressHandler: $.delegate( this, onCanvasNonPrimaryPress ),
@@ -303,6 +307,7 @@ $.Viewer = function( options ) {
});
this.outerTracker = new $.MouseTracker({
+ userData: 'Viewer.outerTracker',
element: this.container,
startDisabled: !this.mouseNavEnabled,
clickTimeThreshold: this.clickTimeThreshold,
@@ -310,7 +315,7 @@ $.Viewer = function( options ) {
dblClickTimeThreshold: this.dblClickTimeThreshold,
dblClickDistThreshold: this.dblClickDistThreshold,
enterHandler: $.delegate( this, onContainerEnter ),
- exitHandler: $.delegate( this, onContainerExit )
+ leaveHandler: $.delegate( this, onContainerLeave )
});
if( this.toolbar ){
@@ -409,17 +414,19 @@ $.Viewer = function( options ) {
if (!this.drawer.canRotate()) {
// Disable/remove the rotate left/right buttons since they aren't supported
if (this.rotateLeft) {
- i = this.buttons.buttons.indexOf(this.rotateLeft);
- this.buttons.buttons.splice(i, 1);
- this.buttons.element.removeChild(this.rotateLeft.element);
+ i = this.buttonGroup.buttons.indexOf(this.rotateLeft);
+ this.buttonGroup.buttons.splice(i, 1);
+ this.buttonGroup.element.removeChild(this.rotateLeft.element);
}
if (this.rotateRight) {
- i = this.buttons.buttons.indexOf(this.rotateRight);
- this.buttons.buttons.splice(i, 1);
- this.buttons.element.removeChild(this.rotateRight.element);
+ i = this.buttonGroup.buttons.indexOf(this.rotateRight);
+ this.buttonGroup.buttons.splice(i, 1);
+ this.buttonGroup.element.removeChild(this.rotateRight.element);
}
}
+ this._addUpdatePixelDensityRatioEvent();
+
//Instantiate a navigator if configured
if ( this.showNavigator){
this.navigator = new $.Navigator({
@@ -472,6 +479,8 @@ $.Viewer = function( options ) {
this.drawer.setImageSmoothingEnabled(this.imageSmoothingEnabled);
}
+ // Register the viewer
+ $._viewers.set(this.element, this);
};
$.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype, /** @lends OpenSeadragon.Viewer.prototype */{
@@ -521,7 +530,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
this.close();
if (!tileSources) {
- return;
+ return this;
}
if (this.sequenceMode && $.isArray(tileSources)) {
@@ -530,7 +539,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
this.referenceStrip = null;
}
- if (typeof initialPage != 'undefined' && !isNaN(initialPage)) {
+ if (typeof initialPage !== 'undefined' && !isNaN(initialPage)) {
this.initialPage = initialPage;
}
@@ -545,7 +554,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
}
this._updateSequenceButtons( this._sequenceIndex );
- return;
+ return this;
}
if (!$.isArray(tileSources)) {
@@ -553,7 +562,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
}
if (!tileSources.length) {
- return;
+ return this;
}
this._opening = true;
@@ -742,6 +751,8 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
return;
}
+ this._removeUpdatePixelDensityRatioEvent();
+
this.close();
this.clearOverlays();
@@ -765,8 +776,27 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
this.drawer.destroy();
}
+ if ( this.navigator ) {
+ this.navigator.destroy();
+ THIS[ this.navigator.hash ] = null;
+ delete THIS[ this.navigator.hash ];
+ this.navigator = null;
+ }
+
this.removeAllHandlers();
+ if (this.buttonGroup) {
+ this.buttonGroup.destroy();
+ } else if (this.customButtons) {
+ while (this.customButtons.length) {
+ this.customButtons.pop().destroy();
+ }
+ }
+
+ if (this.paging) {
+ this.paging.destroy();
+ }
+
// Go through top element (passed to us) and remove all children
// Use removeChild to make sure it handles SVG or any non-html
// also it performs better - http://jsperf.com/innerhtml-vs-removechild/15
@@ -776,6 +806,9 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
}
}
+ this.container.onsubmit = null;
+ this.clearControls();
+
// destroy the mouse trackers
if (this.innerTracker){
this.innerTracker.destroy();
@@ -791,6 +824,9 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
this.canvas = null;
this.container = null;
+ // Unregister the viewer
+ $._viewers.delete(this.element);
+
// clear our reference to the main element - they will need to pass it in again, creating a new viewer
this.element = null;
},
@@ -913,7 +949,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
i;
//don't bother modifying the DOM if we are already in full page mode.
- if ( fullPage == this.isFullPage() ) {
+ if ( fullPage === this.isFullPage() ) {
return this;
}
@@ -967,6 +1003,9 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
bodyStyle.height = "100%";
docStyle.height = "100%";
+ this.bodyDisplay = bodyStyle.display;
+ bodyStyle.display = "block";
+
//when entering full screen on the ipad it wasn't sufficient to leave
//the body intact as only only the top half of the screen would
//respond to touch events on the canvas, while the bottom half treated
@@ -1031,6 +1070,8 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
bodyStyle.height = this.bodyHeight;
docStyle.height = this.docHeight;
+ bodyStyle.display = this.bodyDisplay;
+
body.removeChild( this.element );
nodes = this.previousBody.length;
for ( i = 0; i < nodes; i++ ) {
@@ -1081,7 +1122,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
THIS[ this.hash ].fullPage = false;
// mouse will likely be outside now
- $.delegate( this, onContainerExit )( { } );
+ $.delegate( this, onContainerLeave )( { } );
}
@@ -1207,7 +1248,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
* @return {Boolean}
*/
isVisible: function () {
- return this.container.style.visibility != "hidden";
+ return this.container.style.visibility !== "hidden";
},
@@ -1273,6 +1314,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
* @param {Boolean} [options.preload=false] Default switch for loading hidden images (true loads, false blocks)
* @param {Number} [options.degrees=0] Initial rotation of the tiled image around
* its top left corner in degrees.
+ * @param {Boolean} [options.flipped=false] Whether to horizontally flip the image.
* @param {String} [options.compositeOperation] How the image is composited onto other images.
* @param {String} [options.crossOriginPolicy] The crossOriginPolicy for this specific image,
* overriding viewer.crossOriginPolicy.
@@ -1411,7 +1453,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
if (queueItem.options.replace) {
var newIndex = _this.world.getIndexOfItem(queueItem.options.replaceItem);
- if (newIndex != -1) {
+ if (newIndex !== -1) {
queueItem.options.index = newIndex;
}
_this.world.removeItem(queueItem.options.replaceItem);
@@ -1435,6 +1477,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
opacity: queueItem.options.opacity,
preload: queueItem.options.preload,
degrees: queueItem.options.degrees,
+ flipped: queueItem.options.flipped,
compositeOperation: queueItem.options.compositeOperation,
springStiffness: _this.springStiffness,
animationTime: _this.animationTime,
@@ -1457,6 +1500,17 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
if (_this.collectionMode) {
_this.world.setAutoRefigureSizes(false);
}
+
+ if (_this.navigator) {
+ optionsClone = $.extend({}, queueItem.options, {
+ replace: false, // navigator already removed the layer, nothing to replace
+ originalTiledImage: tiledImage,
+ tileSource: queueItem.tileSource
+ });
+
+ _this.navigator.addTiledImage(optionsClone);
+ }
+
_this.world.addItem( tiledImage, {
index: queueItem.options.index
});
@@ -1470,16 +1524,6 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
_this.viewport.goHome(true);
}
- if (_this.navigator) {
- optionsClone = $.extend({}, queueItem.options, {
- replace: false, // navigator already removed the layer, nothing to replace
- originalTiledImage: tiledImage,
- tileSource: queueItem.tileSource
- });
-
- _this.navigator.addTiledImage(optionsClone);
- }
-
if (queueItem.options.success) {
queueItem.options.success({
item: tiledImage
@@ -1600,8 +1644,8 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
//////////////////////////////////////////////////////////////////////////
var onFocusHandler = $.delegate( this, onFocus ),
onBlurHandler = $.delegate( this, onBlur ),
- onNextHandler = $.delegate( this, onNext ),
- onPreviousHandler = $.delegate( this, onPrevious ),
+ onNextHandler = $.delegate( this, this.goToNextPage ),
+ onPreviousHandler = $.delegate( this, this.goToPreviousPage ),
navImages = this.navImages,
useGroup = true;
@@ -1831,13 +1875,13 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
}
if ( useGroup ) {
- this.buttons = new $.ButtonGroup({
+ this.buttonGroup = new $.ButtonGroup({
buttons: buttons,
clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold
});
- this.navControl = this.buttons.element;
+ this.navControl = this.buttonGroup.element;
this.addHandler( 'open', $.delegate( this, lightUp ) );
if( this.toolbar ){
@@ -1851,6 +1895,8 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
{anchor: this.navigationControlAnchor || $.ControlAnchor.TOP_LEFT}
);
}
+ } else {
+ this.customButtons = buttons;
}
}
@@ -2227,6 +2273,7 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
width: this.referenceStripWidth,
tileSources: this.tileSources,
prefixUrl: this.prefixUrl,
+ useCanvas: this.useCanvas,
viewer: this
});
@@ -2235,7 +2282,72 @@ $.extend( $.Viewer.prototype, $.EventSource.prototype, $.ControlDock.prototype,
} else {
$.console.warn('Attempting to display a reference strip while "sequenceMode" is off.');
}
- }
+ },
+
+ /**
+ * Adds _updatePixelDensityRatio to the window resize event.
+ * @private
+ */
+ _addUpdatePixelDensityRatioEvent: function() {
+ this._updatePixelDensityRatioBind = this._updatePixelDensityRatio.bind(this);
+ $.addEvent( window, 'resize', this._updatePixelDensityRatioBind );
+ },
+
+ /**
+ * Removes _updatePixelDensityRatio from the window resize event.
+ * @private
+ */
+ _removeUpdatePixelDensityRatioEvent: function() {
+ $.removeEvent( window, 'resize', this._updatePixelDensityRatioBind );
+ },
+
+ /**
+ * Update pixel density ratio, clears all tiles and triggers updates for
+ * all items if the ratio has changed.
+ * @private
+ */
+ _updatePixelDensityRatio: function() {
+ var previusPixelDensityRatio = $.pixelDensityRatio;
+ var currentPixelDensityRatio = $.getCurrentPixelDensityRatio();
+ if (previusPixelDensityRatio !== currentPixelDensityRatio) {
+ $.pixelDensityRatio = currentPixelDensityRatio;
+ this.world.resetItems();
+ this.forceRedraw();
+ }
+ },
+
+ /**
+ * Sets the image source to the source with index equal to
+ * currentIndex - 1. Changes current image in sequence mode.
+ * If specified, wraps around (see navPrevNextWrap in
+ * {@link OpenSeadragon.Options})
+ *
+ * @method
+ */
+
+ goToPreviousPage: function () {
+ var previous = this._sequenceIndex - 1;
+ if(this.navPrevNextWrap && previous < 0){
+ previous += this.tileSources.length;
+ }
+ this.goToPage( previous );
+ },
+
+ /**
+ * Sets the image source to the source with index equal to
+ * currentIndex + 1. Changes current image in sequence mode.
+ * If specified, wraps around (see navPrevNextWrap in
+ * {@link OpenSeadragon.Options})
+ *
+ * @method
+ */
+ goToNextPage: function () {
+ var next = this._sequenceIndex + 1;
+ if(this.navPrevNextWrap && next >= this.tileSources.length){
+ next = 0;
+ }
+ this.goToPage( next );
+ },
});
@@ -2264,12 +2376,12 @@ function getTileSourceImplementation( viewer, tileSource, imgOptions, successCal
var _this = viewer;
//allow plain xml strings or json strings to be parsed here
- if ( $.type( tileSource ) == 'string' ) {
+ if ( $.type( tileSource ) === 'string' ) {
//xml should start with "<" and end with ">"
if ( tileSource.match( /^\s*<.*>\s*$/ ) ) {
tileSource = $.parseXml( tileSource );
//json should start with "{" or "[" and end with "}" or "]"
- } else if ( tileSource.match(/^\s*[\{\[].*[\}\]]\s*$/ ) ) {
+ } else if ( tileSource.match(/^\s*[{[].*[}\]]\s*$/ ) ) {
try {
var tileSourceJ = $.parseJSON(tileSource);
tileSource = tileSourceJ;
@@ -2296,14 +2408,15 @@ function getTileSourceImplementation( viewer, tileSource, imgOptions, successCal
}
setTimeout( function() {
- if ( $.type( tileSource ) == 'string' ) {
+ if ( $.type( tileSource ) === 'string' ) {
//If its still a string it means it must be a url at this point
tileSource = new $.TileSource({
url: tileSource,
crossOriginPolicy: imgOptions.crossOriginPolicy !== undefined ?
imgOptions.crossOriginPolicy : viewer.crossOriginPolicy,
ajaxWithCredentials: viewer.ajaxWithCredentials,
- ajaxHeaders: viewer.ajaxHeaders,
+ ajaxHeaders: imgOptions.ajaxHeaders ?
+ imgOptions.ajaxHeaders : viewer.ajaxHeaders,
useCanvas: viewer.useCanvas,
success: function( event ) {
successCallback( event.tileSource );
@@ -2514,10 +2627,36 @@ function onBlur(){
}
+function onCanvasContextMenu( event ) {
+ var eventArgs = {
+ tracker: event.eventSource,
+ position: event.position,
+ originalEvent: event.originalEvent,
+ preventDefault: event.preventDefault
+ };
+
+ /**
+ * Raised when a contextmenu event occurs in the {@link OpenSeadragon.Viewer#canvas} element.
+ *
+ * @event canvas-contextmenu
+ * @memberof OpenSeadragon.Viewer
+ * @type {object}
+ * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
+ * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
+ * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
+ * @property {Object} originalEvent - The original DOM event.
+ * @property {Boolean} preventDefault - Set to true to prevent the default user-agent's handling of the contextmenu event.
+ * @property {?Object} userData - Arbitrary subscriber-defined object.
+ */
+ this.raiseEvent( 'canvas-contextmenu', eventArgs );
+
+ event.preventDefault = eventArgs.preventDefault;
+}
+
function onCanvasKeyDown( event ) {
var canvasKeyDownEventArgs = {
originalEvent: event.originalEvent,
- preventDefaultAction: event.preventDefaultAction,
+ preventDefaultAction: false,
preventVerticalPan: event.preventVerticalPan,
preventHorizontalPan: event.preventHorizontalPan
};
@@ -2549,7 +2688,8 @@ function onCanvasKeyDown( event ) {
}
this.viewport.applyConstraints();
}
- return false;
+ event.preventDefault = true;
+ break;
case 40://down arrow
if (!canvasKeyDownEventArgs.preventVerticalPan) {
if ( event.shift ) {
@@ -2559,31 +2699,35 @@ function onCanvasKeyDown( event ) {
}
this.viewport.applyConstraints();
}
- return false;
+ event.preventDefault = true;
+ break;
case 37://left arrow
if (!canvasKeyDownEventArgs.preventHorizontalPan) {
this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(-this.pixelsPerArrowPress, 0)));
this.viewport.applyConstraints();
}
- return false;
+ event.preventDefault = true;
+ break;
case 39://right arrow
if (!canvasKeyDownEventArgs.preventHorizontalPan) {
this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(this.pixelsPerArrowPress, 0)));
this.viewport.applyConstraints();
}
- return false;
+ event.preventDefault = true;
+ break;
default:
//console.log( 'navigator keycode %s', event.keyCode );
- return true;
+ event.preventDefault = false;
+ break;
}
} else {
- return true;
+ event.preventDefault = false;
}
}
function onCanvasKeyPress( event ) {
var canvasKeyPressEventArgs = {
originalEvent: event.originalEvent,
- preventDefaultAction: event.preventDefaultAction,
+ preventDefaultAction: false,
preventVerticalPan: event.preventVerticalPan,
preventHorizontalPan: event.preventHorizontalPan
};
@@ -2597,15 +2741,18 @@ function onCanvasKeyPress( event ) {
case 61://=|+
this.viewport.zoomBy(1.1);
this.viewport.applyConstraints();
- return false;
+ event.preventDefault = true;
+ break;
case 45://-|_
this.viewport.zoomBy(0.9);
this.viewport.applyConstraints();
- return false;
+ event.preventDefault = true;
+ break;
case 48://0|)
this.viewport.goHome();
this.viewport.applyConstraints();
- return false;
+ event.preventDefault = true;
+ break;
case 119://w
case 87://W
if (!canvasKeyPressEventArgs.preventVerticalPan) {
@@ -2616,7 +2763,8 @@ function onCanvasKeyPress( event ) {
}
this.viewport.applyConstraints();
}
- return false;
+ event.preventDefault = true;
+ break;
case 115://s
case 83://S
if (!canvasKeyPressEventArgs.preventVerticalPan) {
@@ -2627,19 +2775,22 @@ function onCanvasKeyPress( event ) {
}
this.viewport.applyConstraints();
}
- return false;
+ event.preventDefault = true;
+ break;
case 97://a
if (!canvasKeyPressEventArgs.preventHorizontalPan) {
this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(-40, 0)));
this.viewport.applyConstraints();
}
- return false;
+ event.preventDefault = true;
+ break;
case 100://d
if (!canvasKeyPressEventArgs.preventHorizontalPan) {
this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(40, 0)));
this.viewport.applyConstraints();
}
- return false;
+ event.preventDefault = true;
+ break;
case 114: //r - clockwise rotation
if(this.viewport.flipped){
this.viewport.setRotation($.positiveModulo(this.viewport.degrees - this.rotationIncrement, 360));
@@ -2647,7 +2798,8 @@ function onCanvasKeyPress( event ) {
this.viewport.setRotation($.positiveModulo(this.viewport.degrees + this.rotationIncrement, 360));
}
this.viewport.applyConstraints();
- return false;
+ event.preventDefault = true;
+ break;
case 82: //R - counterclockwise rotation
if(this.viewport.flipped){
this.viewport.setRotation($.positiveModulo(this.viewport.degrees + this.rotationIncrement, 360));
@@ -2655,23 +2807,32 @@ function onCanvasKeyPress( event ) {
this.viewport.setRotation($.positiveModulo(this.viewport.degrees - this.rotationIncrement, 360));
}
this.viewport.applyConstraints();
- return false;
+ event.preventDefault = true;
+ break;
case 102: //f
this.viewport.toggleFlip();
- return false;
+ event.preventDefault = true;
+ break;
+ case 106: //j - previous image source
+ this.goToPreviousPage();
+ break;
+ case 107: //k - next image source
+ this.goToNextPage();
+ break;
default:
// console.log( 'navigator keycode %s', event.keyCode );
- return true;
+ event.preventDefault = false;
+ break;
}
} else {
- return true;
+ event.preventDefault = false;
}
}
function onCanvasClick( event ) {
var gestureSettings;
- var haveKeyboardFocus = document.activeElement == this.canvas;
+ var haveKeyboardFocus = document.activeElement === this.canvas;
// If we don't have keyboard focus, request it.
if ( !haveKeyboardFocus ) {
@@ -2687,7 +2848,8 @@ function onCanvasClick( event ) {
quick: event.quick,
shift: event.shift,
originalEvent: event.originalEvent,
- preventDefaultAction: event.preventDefaultAction
+ originalTarget: event.originalTarget,
+ preventDefaultAction: false
};
/**
@@ -2702,6 +2864,7 @@ function onCanvasClick( event ) {
* @property {Boolean} quick - True only if the clickDistThreshold and clickTimeThreshold are both passed. Useful for differentiating between clicks and drags.
* @property {Boolean} shift - True if the shift key was pressed during this event.
* @property {Object} originalEvent - The original DOM event.
+ * @property {Element} originalTarget - The DOM element clicked on.
* @property {Boolean} preventDefaultAction - Set to true to prevent default click to zoom behaviour. Default: false.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
@@ -2727,7 +2890,7 @@ function onCanvasDblClick( event ) {
position: event.position,
shift: event.shift,
originalEvent: event.originalEvent,
- preventDefaultAction: event.preventDefaultAction
+ preventDefaultAction: false
};
/**
@@ -2763,13 +2926,14 @@ function onCanvasDrag( event ) {
var canvasDragEventArgs = {
tracker: event.eventSource,
+ pointerType: event.pointerType,
position: event.position,
delta: event.delta,
speed: event.speed,
direction: event.direction,
shift: event.shift,
originalEvent: event.originalEvent,
- preventDefaultAction: event.preventDefaultAction
+ preventDefaultAction: false
};
/**
@@ -2780,19 +2944,21 @@ function onCanvasDrag( event ) {
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
+ * @property {String} pointerType - "mouse", "touch", "pen", etc.
* @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
* @property {OpenSeadragon.Point} delta - The x,y components of the difference between start drag and end drag.
* @property {Number} speed - Current computed speed, in pixels per second.
* @property {Number} direction - Current computed direction, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.
* @property {Boolean} shift - True if the shift key was pressed during this event.
* @property {Object} originalEvent - The original DOM event.
- * @property {Boolean} preventDefaultAction - Set to true to prevent default drag behaviour. Default: false.
+ * @property {Boolean} preventDefaultAction - Set to true to prevent default drag to pan behaviour. Default: false.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.raiseEvent( 'canvas-drag', canvasDragEventArgs);
- if ( !canvasDragEventArgs.preventDefaultAction && this.viewport ) {
- gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
+ gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
+
+ if ( gestureSettings.dragToPan && !canvasDragEventArgs.preventDefaultAction && this.viewport ) {
if( !this.panHorizontal ){
event.delta.x = 0;
}
@@ -2815,11 +2981,11 @@ function onCanvasDrag( event ) {
this.viewport.centerSpringX.target.value -= delta.x;
this.viewport.centerSpringY.target.value -= delta.y;
- if (bounds.x != constrainedBounds.x) {
+ if (bounds.x !== constrainedBounds.x) {
event.delta.x = 0;
}
- if (bounds.y != constrainedBounds.y) {
+ if (bounds.y !== constrainedBounds.y) {
event.delta.y = 0;
}
}
@@ -2829,7 +2995,37 @@ function onCanvasDrag( event ) {
}
function onCanvasDragEnd( event ) {
- if (!event.preventDefaultAction && this.viewport) {
+ var canvasDragEndEventArgs = {
+ tracker: event.eventSource,
+ pointerType: event.pointerType,
+ position: event.position,
+ speed: event.speed,
+ direction: event.direction,
+ shift: event.shift,
+ originalEvent: event.originalEvent,
+ preventDefaultAction: false
+ };
+
+ /**
+ * Raised when a mouse or touch drag operation ends on the {@link OpenSeadragon.Viewer#canvas} element.
+ *
+ * @event canvas-drag-end
+ * @memberof OpenSeadragon.Viewer
+ * @type {object}
+ * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
+ * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
+ * @property {String} pointerType - "mouse", "touch", "pen", etc.
+ * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
+ * @property {Number} speed - Speed at the end of a drag gesture, in pixels per second.
+ * @property {Number} direction - Direction at the end of a drag gesture, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.
+ * @property {Boolean} shift - True if the shift key was pressed during this event.
+ * @property {Object} originalEvent - The original DOM event.
+ * @property {Boolean} preventDefaultAction - Set to true to prevent default drag-end flick behaviour. Default: false.
+ * @property {?Object} userData - Arbitrary subscriber-defined object.
+ */
+ this.raiseEvent('canvas-drag-end', canvasDragEndEventArgs);
+
+ if (!canvasDragEndEventArgs.preventDefaultAction && this.viewport) {
var gestureSettings = this.gestureSettingsByDeviceType(event.pointerType);
if (gestureSettings.flickEnabled &&
event.speed >= gestureSettings.flickMinSpeed) {
@@ -2851,29 +3047,6 @@ function onCanvasDragEnd( event ) {
}
this.viewport.applyConstraints();
}
- /**
- * Raised when a mouse or touch drag operation ends on the {@link OpenSeadragon.Viewer#canvas} element.
- *
- * @event canvas-drag-end
- * @memberof OpenSeadragon.Viewer
- * @type {object}
- * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
- * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
- * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
- * @property {Number} speed - Speed at the end of a drag gesture, in pixels per second.
- * @property {Number} direction - Direction at the end of a drag gesture, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.
- * @property {Boolean} shift - True if the shift key was pressed during this event.
- * @property {Object} originalEvent - The original DOM event.
- * @property {?Object} userData - Arbitrary subscriber-defined object.
- */
- this.raiseEvent('canvas-drag-end', {
- tracker: event.eventSource,
- position: event.position,
- speed: event.speed,
- direction: event.direction,
- shift: event.shift,
- originalEvent: event.originalEvent
- });
}
function onCanvasEnter( event ) {
@@ -2906,12 +3079,7 @@ function onCanvasEnter( event ) {
});
}
-function onCanvasExit( event ) {
-
- if (window.location != window.parent.location){
- $.MouseTracker.resetAllMouseTrackers();
- }
-
+function onCanvasLeave( event ) {
/**
* Raised when a pointer leaves the {@link OpenSeadragon.Viewer#canvas} element.
*
@@ -3055,33 +3223,21 @@ function onCanvasPinch( event ) {
lastCenterPt,
panByPt;
- if ( !event.preventDefaultAction && this.viewport ) {
- gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
- if ( gestureSettings.pinchToZoom ) {
- centerPt = this.viewport.pointFromPixel( event.center, true );
- lastCenterPt = this.viewport.pointFromPixel( event.lastCenter, true );
- panByPt = lastCenterPt.minus( centerPt );
- if( !this.panHorizontal ) {
- panByPt.x = 0;
- }
- if( !this.panVertical ) {
- panByPt.y = 0;
- }
- this.viewport.zoomBy( event.distance / event.lastDistance, centerPt, true );
- if ( gestureSettings.zoomToRefPoint ) {
- this.viewport.panBy(panByPt, true);
- }
- this.viewport.applyConstraints();
- }
- if ( gestureSettings.pinchRotate ) {
- // Pinch rotate
- var angle1 = Math.atan2(event.gesturePoints[0].currentPos.y - event.gesturePoints[1].currentPos.y,
- event.gesturePoints[0].currentPos.x - event.gesturePoints[1].currentPos.x);
- var angle2 = Math.atan2(event.gesturePoints[0].lastPos.y - event.gesturePoints[1].lastPos.y,
- event.gesturePoints[0].lastPos.x - event.gesturePoints[1].lastPos.x);
- this.viewport.setRotation(this.viewport.getRotation() + ((angle1 - angle2) * (180 / Math.PI)));
- }
- }
+ var canvasPinchEventArgs = {
+ tracker: event.eventSource,
+ pointerType: event.pointerType,
+ gesturePoints: event.gesturePoints,
+ lastCenter: event.lastCenter,
+ center: event.center,
+ lastDistance: event.lastDistance,
+ distance: event.distance,
+ shift: event.shift,
+ originalEvent: event.originalEvent,
+ preventDefaultPanAction: false,
+ preventDefaultZoomAction: false,
+ preventDefaultRotateAction: false
+ };
+
/**
* Raised when a pinch event occurs on the {@link OpenSeadragon.Viewer#canvas} element.
*
@@ -3090,6 +3246,7 @@ function onCanvasPinch( event ) {
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
+ * @property {String} pointerType - "mouse", "touch", "pen", etc.
* @property {Array.} gesturePoints - Gesture points associated with the gesture. Velocity data can be found here.
* @property {OpenSeadragon.Point} lastCenter - The previous center point of the two pinch contact points relative to the tracked element.
* @property {OpenSeadragon.Point} center - The center point of the two pinch contact points relative to the tracked element.
@@ -3097,24 +3254,48 @@ function onCanvasPinch( event ) {
* @property {Number} distance - The distance between the two pinch contact points in CSS pixels.
* @property {Boolean} shift - True if the shift key was pressed during this event.
* @property {Object} originalEvent - The original DOM event.
+ * @property {Boolean} preventDefaultPanAction - Set to true to prevent default pinch to pan behaviour. Default: false.
+ * @property {Boolean} preventDefaultZoomAction - Set to true to prevent default pinch to zoom behaviour. Default: false.
+ * @property {Boolean} preventDefaultRotateAction - Set to true to prevent default pinch to rotate behaviour. Default: false.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
- this.raiseEvent('canvas-pinch', {
- tracker: event.eventSource,
- gesturePoints: event.gesturePoints,
- lastCenter: event.lastCenter,
- center: event.center,
- lastDistance: event.lastDistance,
- distance: event.distance,
- shift: event.shift,
- originalEvent: event.originalEvent
- });
- //cancels event
- return false;
+ this.raiseEvent('canvas-pinch', canvasPinchEventArgs);
+
+ if ( this.viewport ) {
+ gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
+ if ( gestureSettings.pinchToZoom &&
+ (!canvasPinchEventArgs.preventDefaultPanAction || !canvasPinchEventArgs.preventDefaultZoomAction) ) {
+ centerPt = this.viewport.pointFromPixel( event.center, true );
+ if ( !canvasPinchEventArgs.preventDefaultZoomAction ) {
+ this.viewport.zoomBy( event.distance / event.lastDistance, centerPt, true );
+ }
+ if ( gestureSettings.zoomToRefPoint && !canvasPinchEventArgs.preventDefaultPanAction ) {
+ lastCenterPt = this.viewport.pointFromPixel( event.lastCenter, true );
+ panByPt = lastCenterPt.minus( centerPt );
+ if( !this.panHorizontal ) {
+ panByPt.x = 0;
+ }
+ if( !this.panVertical ) {
+ panByPt.y = 0;
+ }
+ this.viewport.panBy(panByPt, true);
+ }
+ this.viewport.applyConstraints();
+ }
+ if ( gestureSettings.pinchRotate && !canvasPinchEventArgs.preventDefaultRotateAction ) {
+ // Pinch rotate
+ var angle1 = Math.atan2(event.gesturePoints[0].currentPos.y - event.gesturePoints[1].currentPos.y,
+ event.gesturePoints[0].currentPos.x - event.gesturePoints[1].currentPos.x);
+ var angle2 = Math.atan2(event.gesturePoints[0].lastPos.y - event.gesturePoints[1].lastPos.y,
+ event.gesturePoints[0].lastPos.x - event.gesturePoints[1].lastPos.x);
+ this.viewport.setRotation(this.viewport.getRotation() + ((angle1 - angle2) * (180 / Math.PI)));
+ }
+ }
}
function onCanvasScroll( event ) {
- var gestureSettings,
+ var canvasScrollEventArgs,
+ gestureSettings,
factor,
thisScrollTime,
deltaScrollTime;
@@ -3127,21 +3308,16 @@ function onCanvasScroll( event ) {
if (deltaScrollTime > this.minScrollDeltaTime) {
this._lastScrollTime = thisScrollTime;
- if(this.viewport.flipped){
- event.position.x = this.viewport.getContainerSize().x - event.position.x;
- }
+ canvasScrollEventArgs = {
+ tracker: event.eventSource,
+ position: event.position,
+ scroll: event.scroll,
+ shift: event.shift,
+ originalEvent: event.originalEvent,
+ preventDefaultAction: false,
+ preventDefault: true
+ };
- if ( !event.preventDefaultAction && this.viewport ) {
- gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
- if ( gestureSettings.scrollToZoom ) {
- factor = Math.pow( this.zoomPerScroll, event.scroll );
- this.viewport.zoomBy(
- factor,
- gestureSettings.zoomToRefPoint ? this.viewport.pointFromPixel( event.position, true ) : null
- );
- this.viewport.applyConstraints();
- }
- }
/**
* Raised when a scroll event occurs on the {@link OpenSeadragon.Viewer#canvas} element (mouse wheel).
*
@@ -3154,25 +3330,31 @@ function onCanvasScroll( event ) {
* @property {Number} scroll - The scroll delta for the event.
* @property {Boolean} shift - True if the shift key was pressed during this event.
* @property {Object} originalEvent - The original DOM event.
+ * @property {Boolean} preventDefaultAction - Set to true to prevent default scroll to zoom behaviour. Default: false.
+ * @property {Boolean} preventDefault - Set to true to prevent the default user-agent's handling of the wheel event. Default: true.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
- this.raiseEvent( 'canvas-scroll', {
- tracker: event.eventSource,
- position: event.position,
- scroll: event.scroll,
- shift: event.shift,
- originalEvent: event.originalEvent
- });
- if (gestureSettings && gestureSettings.scrollToZoom) {
- //cancels event
- return false;
- }
- }
- else {
- gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
- if (gestureSettings && gestureSettings.scrollToZoom) {
- return false; // We are swallowing this event
+ this.raiseEvent('canvas-scroll', canvasScrollEventArgs );
+
+ if ( !canvasScrollEventArgs.preventDefaultAction && this.viewport ) {
+ if(this.viewport.flipped){
+ event.position.x = this.viewport.getContainerSize().x - event.position.x;
+ }
+
+ gestureSettings = this.gestureSettingsByDeviceType( event.pointerType );
+ if ( gestureSettings.scrollToZoom ) {
+ factor = Math.pow( this.zoomPerScroll, event.scroll );
+ this.viewport.zoomBy(
+ factor,
+ gestureSettings.zoomToRefPoint ? this.viewport.pointFromPixel( event.position, true ) : null
+ );
+ this.viewport.applyConstraints();
+ }
}
+
+ event.preventDefault = canvasScrollEventArgs.preventDefault;
+ } else {
+ event.preventDefault = true;
}
}
@@ -3187,6 +3369,7 @@ function onContainerEnter( event ) {
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
+ * @property {String} pointerType - "mouse", "touch", "pen", etc.
* @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
* @property {Number} buttons - Current buttons pressed. A combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
* @property {Number} pointers - Number of pointers (all types) active in the tracked element.
@@ -3197,6 +3380,7 @@ function onContainerEnter( event ) {
*/
this.raiseEvent( 'container-enter', {
tracker: event.eventSource,
+ pointerType: event.pointerType,
position: event.position,
buttons: event.buttons,
pointers: event.pointers,
@@ -3206,7 +3390,7 @@ function onContainerEnter( event ) {
});
}
-function onContainerExit( event ) {
+function onContainerLeave( event ) {
if ( event.pointers < 1 ) {
THIS[ this.hash ].mouseInside = false;
if ( !THIS[ this.hash ].animating ) {
@@ -3221,6 +3405,7 @@ function onContainerExit( event ) {
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
+ * @property {String} pointerType - "mouse", "touch", "pen", etc.
* @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
* @property {Number} buttons - Current buttons pressed. A combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.
* @property {Number} pointers - Number of pointers (all types) active in the tracked element.
@@ -3231,6 +3416,7 @@ function onContainerExit( event ) {
*/
this.raiseEvent( 'container-exit', {
tracker: event.eventSource,
+ pointerType: event.pointerType,
position: event.position,
buttons: event.buttons,
pointers: event.pointers,
@@ -3260,7 +3446,7 @@ function updateOnce( viewer ) {
//viewer.profiler.beginUpdate();
- if (viewer._opening) {
+ if (viewer._opening || !THIS[viewer.hash]) {
return;
}
@@ -3461,8 +3647,10 @@ function doSingleZoomOut() {
function lightUp() {
- this.buttons.emulateEnter();
- this.buttons.emulateExit();
+ if (this.buttonGroup) {
+ this.buttonGroup.emulateEnter();
+ this.buttonGroup.emulateLeave();
+ }
}
@@ -3481,8 +3669,8 @@ function onFullScreen() {
this.setFullScreen( !this.isFullPage() );
}
// correct for no mouseout event on change
- if ( this.buttons ) {
- this.buttons.emulateExit();
+ if ( this.buttonGroup ) {
+ this.buttonGroup.emulateLeave();
}
this.fullPageButton.element.focus();
if ( this.viewport ) {
@@ -3522,22 +3710,4 @@ function onFlip() {
this.viewport.toggleFlip();
}
-function onPrevious(){
- var previous = this._sequenceIndex - 1;
- if(this.navPrevNextWrap && previous < 0){
- previous += this.tileSources.length;
- }
- this.goToPage( previous );
-}
-
-
-function onNext(){
- var next = this._sequenceIndex + 1;
- if(this.navPrevNextWrap && next >= this.tileSources.length){
- next = 0;
- }
- this.goToPage( next );
-}
-
-
}( OpenSeadragon ));
diff --git a/src/viewport.js b/src/viewport.js
index 43011914..513d02fb 100644
--- a/src/viewport.js
+++ b/src/viewport.js
@@ -898,7 +898,7 @@ $.Viewport.prototype = {
* @property {Number} degrees - The number of degrees the rotation was set to.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
- this.viewer.raiseEvent('rotate', {"degrees": degrees});
+ this.viewer.raiseEvent('rotate', {degrees: degrees});
return this;
},
@@ -1567,7 +1567,7 @@ $.Viewport.prototype = {
* @property {Number} flipped - The flip state after this change.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
- this.viewer.raiseEvent('flip', {"flipped": state});
+ this.viewer.raiseEvent('flip', {flipped: state});
return this;
}
diff --git a/src/zoomifytilesource.js b/src/zoomifytilesource.js
index 6009d0c1..9ffa4988 100644
--- a/src/zoomifytilesource.js
+++ b/src/zoomifytilesource.js
@@ -18,7 +18,7 @@
* tilesUrl: "/test/data/zoomify/"
* }
*
- * The tileSize is currently hardcoded to 256 (the usual Zoomify default). The tileUrl must the path to the image _directory_.
+ * The tileSize is set to 256 (the usual Zoomify default) when it is not defined. The tileUrl must the path to the image _directory_.
*
* 2) Loading image metadata from xml file: (CURRENTLY NOT SUPPORTED)
*
@@ -44,7 +44,14 @@
* @param {String} tilesUrl
*/
$.ZoomifyTileSource = function(options) {
- options.tileSize = 256;
+ if(typeof options.tileSize === 'undefined'){
+ options.tileSize = 256;
+ }
+
+ if(typeof options.fileFormat === 'undefined'){
+ options.fileFormat = 'jpg';
+ this.fileFormat = options.fileFormat;
+ }
var currentImageSize = {
x: options.width,
@@ -107,7 +114,7 @@
* @param {String} optional - url
*/
supports: function(data, url) {
- return (data.type && "zoomifytileservice" == data.type);
+ return (data.type && "zoomifytileservice" === data.type);
},
/**
@@ -133,7 +140,7 @@
var result = 0;
var num = this._calculateAbsoluteTileNumber(level, x, y);
result = Math.floor(num / 256);
- return this.tilesUrl + 'TileGroup' + result + '/' + level + '-' + x + '-' + y + '.jpg';
+ return this.tilesUrl + 'TileGroup' + result + '/' + level + '-' + x + '-' + y + '.' + this.fileFormat;
}
});
diff --git a/test/coverage.html b/test/coverage.html
index e2abc706..df0e9781 100644
--- a/test/coverage.html
+++ b/test/coverage.html
@@ -87,6 +87,7 @@
+
diff --git a/test/demo/cropping-polygons.html b/test/demo/cropping-polygons.html
new file mode 100644
index 00000000..8f69d98d
--- /dev/null
+++ b/test/demo/cropping-polygons.html
@@ -0,0 +1,165 @@
+
+
+
+ OpenSeadragon Cropping PolygonList Demo
+
+
+
+
+
+
+ Simple demo page to show cropping with polygonList in a OpenSeadragon viewer.
+
+
+ Click on Viewer to save polygon points
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/demo/flipping.html b/test/demo/flipping.html
new file mode 100644
index 00000000..3d3eb99e
--- /dev/null
+++ b/test/demo/flipping.html
@@ -0,0 +1,113 @@
+
+
+
+ OpenSeadragon Flipping Demo
+
+
+
+
+
+
+ Simple demo page to show image flipping.
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/demo/memorycheck-with-simple-image.html b/test/demo/memorycheck-with-simple-image.html
new file mode 100644
index 00000000..292c6994
--- /dev/null
+++ b/test/demo/memorycheck-with-simple-image.html
@@ -0,0 +1,55 @@
+
+
+
+ OpenSeadragon Memory Check With Simple Image Demo
+
+
+
+
+
+ Simple demo page to monitor OpenSeadragon Memory Usage.
+
+
+
+
+
+
+
+
+
diff --git a/test/helpers/legacy.mouse.shim.js b/test/helpers/legacy.mouse.shim.js
index 3609ed85..19374147 100644
--- a/test/helpers/legacy.mouse.shim.js
+++ b/test/helpers/legacy.mouse.shim.js
@@ -6,20 +6,22 @@
$.MouseTracker.subscribeEvents = [ "click", "dblclick", "keypress", "focus", "blur", $.MouseTracker.wheelEventName ];
- if( $.MouseTracker.wheelEventName == "DOMMouseScroll" ) {
+ if( $.MouseTracker.wheelEventName === "DOMMouseScroll" ) {
// Older Firefox
$.MouseTracker.subscribeEvents.push( "MozMousePixelScroll" );
}
$.MouseTracker.havePointerEvents = false;
- if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) {
- $.MouseTracker.subscribeEvents.push( "mouseenter", "mouseleave" );
- $.MouseTracker.haveMouseEnter = true;
- } else {
- $.MouseTracker.subscribeEvents.push( "mouseover", "mouseout" );
- $.MouseTracker.haveMouseEnter = false;
+ $.MouseTracker.subscribeEvents.push( "mouseenter", "mouseleave", "mouseover", "mouseout", "mousedown", "mouseup", "mousemove" );
+ $.MouseTracker.mousePointerId = "legacy-mouse";
+ // Legacy mouse events capture support (IE/Firefox only?)
+ $.MouseTracker.havePointerCapture = (function () {
+ var divElement = document.createElement( 'div' );
+ return $.isFunction( divElement.setCapture ) && $.isFunction( divElement.releaseCapture );
+ }());
+ if ( $.MouseTracker.havePointerCapture ) {
+ $.MouseTracker.subscribeEvents.push( "losecapture" );
}
- $.MouseTracker.subscribeEvents.push( "mousedown", "mouseup", "mousemove" );
if ( 'ontouchstart' in window ) {
// iOS, Android, and other W3c Touch Event implementations
// (see http://www.w3.org/TR/touch-events/)
@@ -32,8 +34,5 @@
// Subscribe to these to prevent default gesture handling
$.MouseTracker.subscribeEvents.push( "gesturestart", "gesturechange" );
}
- $.MouseTracker.mousePointerId = "legacy-mouse";
- $.MouseTracker.maxTouchPoints = 10;
-
}(OpenSeadragon));
diff --git a/test/helpers/test.js b/test/helpers/test.js
index 8e7685d8..60931bba 100644
--- a/test/helpers/test.js
+++ b/test/helpers/test.js
@@ -37,7 +37,7 @@
};
$canvas
- .simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseenter' : 'mouseover', event )
+ .simulate( 'mouseenter', event )
.simulate( 'mousedown', event );
for ( var i = 0; i < args.dragCount; i++ ) {
event.clientX += args.dragDx;
@@ -47,7 +47,7 @@
}
$canvas
.simulate( 'mouseup', event )
- .simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseleave' : 'mouseout', event );
+ .simulate( 'mouseleave', event );
},
// ----------
diff --git a/test/modules/controls.js b/test/modules/controls.js
index 57d827c9..0e6bdf7c 100644
--- a/test/modules/controls.js
+++ b/test/modules/controls.js
@@ -53,9 +53,9 @@
assert.ok(viewer.showZoomControl, 'showZoomControl should be on');
assert.ok(!!viewer.zoomInButton, "zoomIn button should not be null");
assert.ok(!!viewer.zoomOutButton, "zoomOut button should not be null");
- assert.notEqual(viewer.buttons.buttons.indexOf(viewer.zoomInButton), -1,
+ assert.notEqual(viewer.buttonGroup.buttons.indexOf(viewer.zoomInButton), -1,
"The zoomIn button should be present");
- assert.notEqual(viewer.buttons.buttons.indexOf(viewer.zoomOutButton), -1,
+ assert.notEqual(viewer.buttonGroup.buttons.indexOf(viewer.zoomOutButton), -1,
"The zoomOut button should be present");
var oldZoom = viewer.viewport.getZoom();
@@ -108,7 +108,7 @@
viewer.removeHandler('open', openHandler);
assert.ok(viewer.showHomeControl, 'showHomeControl should be on');
assert.ok(!!viewer.homeButton, "Home button should not be null");
- assert.notEqual(viewer.buttons.buttons.indexOf(viewer.homeButton), -1,
+ assert.notEqual(viewer.buttonGroup.buttons.indexOf(viewer.homeButton), -1,
"The home button should be present");
viewer.viewport.zoomBy(1.1);
@@ -167,7 +167,7 @@
viewer.removeHandler('open', openHandler);
assert.ok(viewer.showHomeControl, 'showFullPageControl should be on');
assert.ok(!!viewer.fullPageButton, "FullPage button should not be null");
- assert.notEqual(viewer.buttons.buttons.indexOf(viewer.fullPageButton), -1,
+ assert.notEqual(viewer.buttonGroup.buttons.indexOf(viewer.fullPageButton), -1,
"The full page button should be present");
assert.ok(!viewer.isFullPage(), "OSD should not be in full page.");
@@ -223,9 +223,9 @@
assert.ok(viewer.drawer, 'Drawer exists');
assert.ok(viewer.drawer.canRotate(), 'drawer.canRotate needs to be true');
assert.ok(viewer.showRotationControl, 'showRotationControl should be true');
- assert.notEqual(viewer.buttons.buttons.indexOf(viewer.rotateLeftButton), -1,
+ assert.notEqual(viewer.buttonGroup.buttons.indexOf(viewer.rotateLeftButton), -1,
"rotateLeft should be found");
- assert.notEqual(viewer.buttons.buttons.indexOf(viewer.rotateRightButton), -1,
+ assert.notEqual(viewer.buttonGroup.buttons.indexOf(viewer.rotateRightButton), -1,
"rotateRight should be found");
// Now simulate the left/right button clicks.
diff --git a/test/modules/events.js b/test/modules/events.js
index 849e86f9..7a2b00ca 100644
--- a/test/modules/events.js
+++ b/test/modules/events.js
@@ -32,7 +32,7 @@
offset = $canvas.offset(),
tracker = viewer.innerTracker,
origEnterHandler,
- origExitHandler,
+ origLeaveHandler,
origPressHandler,
origReleaseHandler,
origNonPrimaryPressHandler,
@@ -43,7 +43,7 @@
origDragHandler,
origDragEndHandler,
enterCount,
- exitCount,
+ leaveCount,
pressCount,
releaseCount,
rightPressCount,
@@ -71,11 +71,11 @@
return true;
}
};
- origExitHandler = tracker.exitHandler;
- tracker.exitHandler = function ( event ) {
- exitCount++;
- if (origExitHandler) {
- return origExitHandler( event );
+ origLeaveHandler = tracker.leaveHandler;
+ tracker.leaveHandler = function ( event ) {
+ leaveCount++;
+ if (origLeaveHandler) {
+ return origLeaveHandler( event );
} else {
return true;
}
@@ -182,7 +182,7 @@
var unhookViewerHandlers = function () {
tracker.enterHandler = origEnterHandler;
- tracker.exitHandler = origExitHandler;
+ tracker.leaveHandler = origLeaveHandler;
tracker.pressHandler = origPressHandler;
tracker.releaseHandler = origReleaseHandler;
tracker.moveHandler = origMoveHandler;
@@ -195,21 +195,21 @@
var simulateEnter = function (x, y) {
simEvent.clientX = offset.left + x;
simEvent.clientY = offset.top + y;
- $canvas.simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseenter' : 'mouseover', simEvent );
+ $canvas.simulate( 'mouseenter', simEvent );
};
var simulateLeave = function (x, y) {
simEvent.clientX = offset.left + x;
simEvent.clientY = offset.top + y;
simEvent.relatedTarget = document.body;
- $canvas.simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseleave' : 'mouseout', simEvent );
+ $canvas.simulate( 'mouseleave', simEvent );
};
//var simulateLeaveFrame = function (x, y) {
// simEvent.clientX = offset.left + x;
// simEvent.clientY = offset.top + y;
// simEvent.relatedTarget = document.getElementsByTagName("html")[0];
- // $canvas.simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseleave' : 'mouseout', simEvent );
+ // $canvas.simulate( 'mouseleave', simEvent );
//};
var simulateDown = function (x, y) {
@@ -256,7 +256,7 @@
clientY: offset.top
};
enterCount = 0;
- exitCount = 0;
+ leaveCount = 0;
pressCount = 0;
releaseCount = 0;
rightPressCount = 0;
@@ -280,8 +280,8 @@
if ('enterCount' in expected) {
assert.equal( enterCount, expected.enterCount, expected.description + 'enterHandler event count matches expected (' + expected.enterCount + ')' );
}
- if ('exitCount' in expected) {
- assert.equal( exitCount, expected.exitCount, expected.description + 'exitHandler event count matches expected (' + expected.exitCount + ')' );
+ if ('leaveCount' in expected) {
+ assert.equal( leaveCount, expected.leaveCount, expected.description + 'leaveHandler event count matches expected (' + expected.leaveCount + ')' );
}
if ('pressCount' in expected) {
assert.equal( pressCount, expected.pressCount, expected.description + 'pressHandler event count matches expected (' + expected.pressCount + ')' );
@@ -355,7 +355,7 @@
assessGestureExpectations({
description: 'enter-move-release (release in tracked element, press in unknown element): ',
enterCount: 1,
- exitCount: 0,
+ leaveCount: 0,
pressCount: 0,
releaseCount: 1,
rightPressCount: 0,
@@ -375,16 +375,16 @@
});
simulateLeave(-1, -1); // flush tracked pointer
- // enter-move-exit (fly-over)
+ // enter-move-leave (fly-over)
resetForAssessment();
simulateEnter(0, 0);
simulateMove(1, 1, 10);
simulateMove(-1, -1, 10);
simulateLeave(-1, -1);
assessGestureExpectations({
- description: 'enter-move-exit (fly-over): ',
+ description: 'enter-move-leave (fly-over): ',
enterCount: 1,
- exitCount: 1,
+ leaveCount: 1,
pressCount: 0,
releaseCount: 0,
rightPressCount: 0,
@@ -403,34 +403,7 @@
//quickClick: false
});
- // move-exit (fly-over, no enter event)
- resetForAssessment();
- simulateMove(1, 1, 10);
- simulateMove(-1, -1, 10);
- simulateLeave(-1, -1);
- assessGestureExpectations({
- description: 'move-exit (fly-over, no enter event): ',
- enterCount: 0,
- exitCount: 1,
- pressCount: 0,
- releaseCount: 0,
- rightPressCount: 0,
- rightReleaseCount: 0,
- middlePressCount: 0,
- middleReleaseCount: 0,
- moveCount: 20,
- clickCount: 0,
- dblClickCount: 0,
- dragCount: 0,
- dragEndCount: 0,
- //insideElementPressed: false,
- //insideElementReleased: false,
- contacts: 0,
- trackedPointers: 0
- //quickClick: false
- });
-
- // enter-press-release-press-release-exit (primary/left double click)
+ // enter-press-release-press-release-leave (primary/left double click)
resetForAssessment();
simulateEnter(0, 0);
simulateDown(0, 0);
@@ -439,9 +412,9 @@
simulateUp(0, 0);
simulateLeave(-1, -1);
assessGestureExpectations({
- description: 'enter-press-release-press-release-exit (primary/left double click): ',
+ description: 'enter-press-release-press-release-leave (primary/left double click): ',
enterCount: 1,
- exitCount: 1,
+ leaveCount: 1,
pressCount: 2,
releaseCount: 2,
rightPressCount: 0,
@@ -452,7 +425,7 @@
clickCount: 2,
dblClickCount: 1,
dragCount: 0,
- dragEndCount: 0,
+ dragEndCount: 2, // v2.5.0+ drag-end event now fired even if pointer didn't move (#1459)
insideElementPressed: true,
insideElementReleased: true,
contacts: 0,
@@ -460,16 +433,16 @@
//quickClick: true
});
- // enter-press-release-exit (primary/left click)
+ // enter-press-release-leave (primary/left click)
resetForAssessment();
simulateEnter(0, 0);
simulateDown(0, 0);
simulateUp(0, 0);
simulateLeave(-1, -1);
assessGestureExpectations({
- description: 'enter-press-release-exit (primary/left click): ',
+ description: 'enter-press-release-leave (primary/left click): ',
enterCount: 1,
- exitCount: 1,
+ leaveCount: 1,
pressCount: 1,
releaseCount: 1,
rightPressCount: 0,
@@ -480,7 +453,7 @@
clickCount: 1,
dblClickCount: 0,
dragCount: 0,
- dragEndCount: 0,
+ dragEndCount: 1, // v2.5.0+ drag-end event now fired even if pointer didn't move (#1459)
insideElementPressed: true,
insideElementReleased: true,
contacts: 0,
@@ -488,16 +461,16 @@
quickClick: true
});
- // enter-nonprimarypress-nonprimaryrelease-exit (secondary/right click)
+ // enter-nonprimarypress-nonprimaryrelease-leave (secondary/right click)
resetForAssessment();
simulateEnter(0, 0);
simulateNonPrimaryDown(0, 0, 2);
simulateNonPrimaryUp(0, 0, 2);
simulateLeave(-1, -1);
assessGestureExpectations({
- description: 'enter-nonprimarypress-nonprimaryrelease-exit (secondary/right click): ',
+ description: 'enter-nonprimarypress-nonprimaryrelease-leave (secondary/right click): ',
enterCount: 1,
- exitCount: 1,
+ leaveCount: 1,
pressCount: 0,
releaseCount: 0,
rightPressCount: 1,
@@ -516,16 +489,16 @@
//quickClick: true
});
- // enter-nonprimarypress-nonprimaryrelease-exit (aux/middle click)
+ // enter-nonprimarypress-nonprimaryrelease-leave (aux/middle click)
resetForAssessment();
simulateEnter(0, 0);
simulateNonPrimaryDown(0, 0, 1);
simulateNonPrimaryUp(0, 0, 1);
simulateLeave(-1, -1);
assessGestureExpectations({
- description: 'enter-nonprimarypress-nonprimaryrelease-exit (aux/middle click): ',
+ description: 'enter-nonprimarypress-nonprimaryrelease-leave (aux/middle click): ',
enterCount: 1,
- exitCount: 1,
+ leaveCount: 1,
pressCount: 0,
releaseCount: 0,
rightPressCount: 0,
@@ -544,7 +517,7 @@
//quickClick: true
});
- // enter-nonprimarypress-move-nonprimaryrelease-move-exit (secondary/right button drag, release in tracked element)
+ // enter-nonprimarypress-move-nonprimaryrelease-move-leave (secondary/right button drag, release in tracked element)
resetForAssessment();
simulateEnter(0, 0);
simulateNonPrimaryDown(0, 0, 2);
@@ -553,9 +526,9 @@
simulateMove(-1, -1, 100);
simulateLeave(-1, -1);
assessGestureExpectations({
- description: 'enter-nonprimarypress-move-nonprimaryrelease-move-exit (secondary/right button drag, release in tracked element): ',
+ description: 'enter-nonprimarypress-move-nonprimaryrelease-move-leave (secondary/right button drag, release in tracked element): ',
enterCount: 1,
- exitCount: 1,
+ leaveCount: 1,
pressCount: 0,
releaseCount: 0,
rightPressCount: 1,
@@ -574,7 +547,7 @@
//quickClick: false
});
- // enter-press-move-release-move-exit (drag, release in tracked element)
+ // enter-press-move-release-move-leave (drag, release in tracked element)
resetForAssessment();
simulateEnter(0, 0);
simulateDown(0, 0);
@@ -583,9 +556,9 @@
simulateMove(-1, -1, 100);
simulateLeave(-1, -1);
assessGestureExpectations({
- description: 'enter-press-move-release-move-exit (drag, release in tracked element): ',
+ description: 'enter-press-move-release-move-leave (drag, release in tracked element): ',
enterCount: 1,
- exitCount: 1,
+ leaveCount: 1,
pressCount: 1,
releaseCount: 1,
rightPressCount: 0,
@@ -604,7 +577,7 @@
quickClick: false
});
- // enter-press-move-exit-move-release (drag, release outside tracked element)
+ // enter-press-move-leave-move-release (drag, release outside tracked element)
resetForAssessment();
simulateEnter(0, 0);
simulateDown(0, 0);
@@ -614,9 +587,9 @@
simulateMove(-1, -1, 5);
simulateUp(-5, -5);
assessGestureExpectations({
- description: 'enter-press-move-exit-move-release (drag, release outside tracked element): ',
+ description: 'enter-press-move-leave-move-release (drag, release outside tracked element): ',
enterCount: 1,
- exitCount: 1,
+ leaveCount: 1,
pressCount: 1,
releaseCount: 1,
rightPressCount: 0,
@@ -635,7 +608,7 @@
quickClick: false
});
- //// enter-press-move-exit-move-release-outside (drag, release outside iframe)
+ //// enter-press-move-leave-move-release-outside (drag, release outside iframe)
//resetForAssessment();
//simulateEnter(0, 0);
//simulateDown(0, 0);
@@ -644,9 +617,9 @@
//simulateLeaveFrame(-1, -1);
//// you don't actually receive the mouseup if you mouseup outside of the document
//assessGestureExpectations({
- // description: 'enter-press-move-exit-move-release-outside (drag, release outside iframe): ',
- // enterCount: 1,
- // exitCount: 1,
+ // description: 'enter-press-move-leave-move-release-outside (drag, release outside iframe): ',
+ // enterCount: 1,
+ // leaveCount: 1,
// pressCount: 1,
// releaseCount: 1,
// rightPressCount: 0,
@@ -679,8 +652,7 @@
if ('TouchEvent' in window) {
QUnit.test( 'MouseTracker: touch events', function (assert) {
var done = assert.async();
- var $canvas = $( viewer.element ).find( '.openseadragon-canvas' ).not( '.navigator .openseadragon-canvas' ),
- tracker = viewer.innerTracker,
+ var tracker = viewer.innerTracker,
touches;
var reset = function () {
@@ -757,7 +729,6 @@
var done = assert.async();
var $canvas = $(viewer.element).find('.openseadragon-canvas')
.not('.navigator .openseadragon-canvas');
- var tracker = viewer.innerTracker;
var epsilon = 0.0000001;
function simulateClickAndDrag() {
@@ -787,16 +758,14 @@
viewer.removeHandler('open', onOpen);
// Hook viewer events to set preventDefaultAction
- var origClickHandler = tracker.clickHandler;
- tracker.clickHandler = function(event) {
+ var onCanvasClick = function (event) {
event.preventDefaultAction = true;
- return origClickHandler(event);
};
- var origDragHandler = tracker.dragHandler;
- tracker.dragHandler = function(event) {
+ var onCanvasDrag = function (event) {
event.preventDefaultAction = true;
- return origDragHandler(event);
};
+ viewer.addHandler("canvas-click", onCanvasClick);
+ viewer.addHandler("canvas-drag", onCanvasDrag);
var originalZoom = viewer.viewport.getZoom();
var originalBounds = viewer.viewport.getBounds();
@@ -810,8 +779,8 @@
Util.assertRectangleEquals(assert, bounds, originalBounds, epsilon,
'Pan should be prevented');
- tracker.clickHandler = origClickHandler;
- tracker.dragHandler = origDragHandler;
+ viewer.removeHandler("canvas-click", onCanvasClick);
+ viewer.removeHandler("canvas-drag", onCanvasDrag);
simulateClickAndDrag();
@@ -837,31 +806,64 @@
// ----------
QUnit.test('Viewer: preventDefaultAction in dblClickHandler', function(assert) {
var done = assert.async();
- var tracker = viewer.innerTracker;
var epsilon = 0.0000001;
+ var $canvas = $( viewer.element ).find( '.openseadragon-canvas' ).not( '.navigator .openseadragon-canvas' ),
+ simEvent = {},
+ offset = $canvas.offset();
+
+ var simulateEnter = function (x, y) {
+ simEvent.clientX = offset.left + x;
+ simEvent.clientY = offset.top + y;
+ $canvas.simulate( 'mouseenter', simEvent );
+ };
+
+ var simulateLeave = function (x, y) {
+ simEvent.clientX = offset.left + x;
+ simEvent.clientY = offset.top + y;
+ simEvent.relatedTarget = document.body;
+ $canvas.simulate( 'mouseleave', simEvent );
+ };
+
+ var simulateDown = function (x, y) {
+ simEvent.button = 0;
+ simEvent.clientX = offset.left + x;
+ simEvent.clientY = offset.top + y;
+ $canvas.simulate( 'mousedown', simEvent );
+ };
+
+ var simulateUp = function (x, y) {
+ simEvent.button = 0;
+ simEvent.clientX = offset.left + x;
+ simEvent.clientY = offset.top + y;
+ $canvas.simulate( 'mouseup', simEvent );
+ };
function simulateDblTap() {
- var touches = [];
- TouchUtil.reset();
-
- touches.push(TouchUtil.start([0,0]));
- TouchUtil.end( touches[0] );
- touches.push(TouchUtil.start([0,0]));
- TouchUtil.end( touches[1] );
+ simulateEnter(2, 2);
+ simulateDown(2, 2);
+ simulateUp(2, 2);
+ simulateDown(2, 2);
+ simulateUp(2, 2);
+ simulateLeave(-1, -1);
}
var onOpen = function() {
viewer.removeHandler('open', onOpen);
+ var origClickSetting = viewer.gestureSettingsMouse.clickToZoom;
+ var origDblClickSetting = viewer.gestureSettingsMouse.dblClickToZoom;
+
+ viewer.gestureSettingsMouse.clickToZoom = false;
+ viewer.gestureSettingsMouse.dblClickToZoom = true;
+
var originalZoom = viewer.viewport.getZoom();
- var origDblClickHandler = tracker.dblClickHandler;
- tracker.dblClickHandler = function(event) {
+ var onCanvasDblClick = function (event) {
event.preventDefaultAction = true;
- return origDblClickHandler(event);
};
- TouchUtil.initTracker(tracker);
+ viewer.addHandler('canvas-double-click', onCanvasDblClick);
+
simulateDblTap();
var zoom = viewer.viewport.getZoom();
@@ -869,37 +871,19 @@
"Zoom on double tap should be prevented");
// Reset event handler to original
- tracker.dblClickHandler = origDblClickHandler;
+ viewer.removeHandler("canvas-double-click", onCanvasDblClick);
simulateDblTap();
- originalZoom = originalZoom * viewer.zoomPerClick;
+ originalZoom *= viewer.zoomPerClick;
zoom = viewer.viewport.getZoom();
Util.assessNumericValue(assert, originalZoom, zoom, epsilon,
"Zoom on double tap should not be prevented");
- var dblClickHandler = function(event) {
- event.preventDefaultAction = true;
- };
+ viewer.gestureSettingsMouse.clickToZoom = origClickSetting;
+ viewer.gestureSettingsMouse.dblClickToZoom = origDblClickSetting;
- viewer.addHandler('canvas-double-click', dblClickHandler);
-
- zoom = viewer.viewport.getZoom();
- Util.assessNumericValue(assert, originalZoom, zoom, epsilon,
- "Zoom on double tap should be prevented");
-
- // Remove custom event handler
- viewer.removeHandler('canvas-double-click', dblClickHandler);
-
- simulateDblTap();
- originalZoom = originalZoom * viewer.zoomPerClick;
-
- zoom = viewer.viewport.getZoom();
- Util.assessNumericValue(assert, originalZoom, zoom, epsilon,
- "Zoom on double tap should not be prevented");
-
- TouchUtil.resetTracker(tracker);
viewer.close();
done();
};
@@ -926,10 +910,9 @@
eventsHandledViewer = 0,
originalEventsPassedViewer = 0,
dragEndsExpected = 1,
- releasesExpected = 1,
- clicksExpected = 1;
+ releasesExpected = 1;
- var onOpen = function ( event ) {
+ var onOpen = function ( ) {
viewer.removeHandler( 'open', onOpen );
viewer.addHandler( 'canvas-drag', onEventSourceDrag );
@@ -953,7 +936,7 @@
dragEndHandler: onMouseTrackerDragEnd,
releaseHandler: onMouseTrackerRelease,
clickHandler: onMouseTrackerClick,
- exitHandler: onMouseTrackerExit
+ leaveHandler: onMouseTrackerLeave
} );
var event = {
@@ -1050,7 +1033,7 @@
checkOriginalEventReceived( event );
};
- var onMouseTrackerExit = function ( event ) {
+ var onMouseTrackerLeave = function ( event ) {
checkOriginalEventReceived( event );
mouseTracker.destroy();
diff --git a/test/modules/iiif.js b/test/modules/iiif.js
new file mode 100644
index 00000000..3c98e24b
--- /dev/null
+++ b/test/modules/iiif.js
@@ -0,0 +1,249 @@
+(function() {
+
+ var id = "http://example.com/identifier";
+
+ var configure = function(data) {
+ return OpenSeadragon.IIIFTileSource.prototype.configure.apply(
+ new OpenSeadragon.TileSource(), [ data, 'http://example.com/identifier' ]
+ );
+ };
+
+ var getSource = function( data ) {
+ var options = configure( data );
+ return new OpenSeadragon.IIIFTileSource( options );
+ };
+
+ var infoXml10level0 = new DOMParser().parseFromString('' +
+ '' +
+ 'http://example.com/identifier' +
+ '6000' +
+ '4000' +
+ '' +
+ '1' +
+ '2' +
+ '4' +
+ '' +
+ 'http://library.stanford.edu/iiif/image-api/compliance.html#level0' +
+ '',
+ 'text/xml'
+ ),
+ infoXml10level1 = new DOMParser().parseFromString('' +
+ '' +
+ 'http://example.com/identifier' +
+ '6000' +
+ '4000' +
+ 'http://library.stanford.edu/iiif/image-api/compliance.html#level1' +
+ '',
+ 'text/xml'
+ ),
+ infoJson10level0 = {
+ "identifier": id,
+ "width": 2000,
+ "height": 1000,
+ "profile" : "http://library.stanford.edu/iiif/image-api/compliance.html#level0"
+ },
+ infoJson10level1 = {
+ "identifier": id,
+ "width": 2000,
+ "height": 1000,
+ "profile" : "http://library.stanford.edu/iiif/image-api/compliance.html#level1"
+ },
+ infoJson11level0 = {
+ "@context": "http://library.stanford.edu/iiif/image-api/1.1/context.json",
+ "@id": id,
+ "width": 2000,
+ "height": 1000,
+ "profile": "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level0"
+ },
+ infoJson11level1 = {
+ "@context": "http://library.stanford.edu/iiif/image-api/1.1/context.json",
+ "@id": id,
+ "width": 2000,
+ "height": 1000,
+ "profile": "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level1"
+ },
+ infoJson11level1WithTiles = {
+ "@context": "http://library.stanford.edu/iiif/image-api/1.1/context.json",
+ "@id": id,
+ "width": 2000,
+ "height": 1000,
+ "tile_width": 512,
+ "tile_height": 256,
+ "profile": "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level1"
+ },
+ infoJson2level0 = {
+ "@context": "http://iiif.io/api/image/2/context.json",
+ "@id": id,
+ "width": 2000,
+ "height": 1000,
+ "sizes": [
+ { width: 2000, height: 1000 },
+ { width: 1000, height: 500 }
+ ],
+ "profile": ["http://iiif.io/api/image/2/level0.json"]
+ },
+ infoJson2level0sizeByW = {
+ "@context": "http://iiif.io/api/image/2/context.json",
+ "@id": id,
+ "width": 2000,
+ "height": 1000,
+ "profile": ["http://iiif.io/api/image/2/level0.json", {"supports": "sizeByW"} ]
+ },
+ infoJson2level1 = {
+ "@context": "http://iiif.io/api/image/2/context.json",
+ "@id": id,
+ "width": 2000,
+ "height": 1000,
+ "profile": ["http://iiif.io/api/image/2/level1.json"]
+ },
+ infoJson3level0 = {
+ "@context": "http://iiif.io/api/image/3/context.json",
+ "id": id,
+ "width": 2000,
+ "height": 1000,
+ "sizes": [
+ { width: 2000, height: 1000 },
+ { width: 1000, height: 500 }
+ ],
+ "profile": "level0"
+ },
+ infoJson3level0ContextExtension = {
+ "@context": [
+ "http://iiif.io/api/image/3/context.json",
+ {
+ "example": "http://example.com/vocab"
+ }
+ ],
+ "id": id,
+ "width": 2000,
+ "height": 1000,
+ "profile": "level0"
+ },
+ infoJson3level0sizeByW = {
+ "@context": "http://iiif.io/api/image/3/context.json",
+ "id": id,
+ "width": 2000,
+ "height": 1000,
+ "profile": "level0",
+ "extraFeatures": "sizeByW"
+ },
+ infoJson3level0sizeByWh = {
+ "@context": "http://iiif.io/api/image/3/context.json",
+ "id": id,
+ "width": 2000,
+ "height": 1000,
+ "profile": "level0",
+ "extraFeatures": "sizeByWh"
+ },
+ infoJson3level1 = {
+ "@context": "http://iiif.io/api/image/3/context.json",
+ "id": id,
+ "width": 2000,
+ "height": 1000,
+ "profile": "level1"
+ };
+
+ QUnit.test('IIIFTileSource.configure determins correct version', function(assert) {
+ var options1_0xml = configure(infoXml10level0);
+ assert.ok(options1_0xml.version);
+ assert.equal(options1_0xml.version, 1, 'Version is 1 for version 1.0 info.xml');
+
+ var options1_0 = configure(infoJson10level0);
+ assert.ok(options1_0.version);
+ assert.equal(options1_0.version, 1, 'Version is 1 for version 1.0 info.json');
+
+ var options1_1 = configure(infoJson11level0);
+ assert.ok(options1_1.version);
+ assert.equal(options1_1.version, 1, 'Version is 1 for version 1.1 info.json');
+
+ var options2 = configure(infoJson2level0);
+ assert.ok(options2.version);
+ assert.equal(options2.version, 2, 'Version is 2 for version 2 info.json');
+
+ var options3 = configure(infoJson3level0);
+ assert.ok(options3.version);
+ assert.equal(options3.version, 3, 'Version is 3 for version 3 info.json');
+
+ var options3withContextExtension = configure(infoJson3level0ContextExtension);
+ assert.ok(options3withContextExtension.version);
+ assert.equal(options3withContextExtension.version, 3, 'Version is 3 for version 3 info.json');
+ });
+
+ QUnit.test('IIIFTileSource private function canBeTiled works as expected', function(assert) {
+ var canBeTiled = function( data ) {
+ var source = getSource( data );
+ return source.__testonly__.canBeTiled( source );
+ };
+
+ assert.notOk(canBeTiled(infoXml10level0));
+ assert.ok(canBeTiled(infoXml10level1));
+ assert.notOk(canBeTiled(infoJson10level0));
+ assert.ok(canBeTiled(infoJson10level1));
+ assert.notOk(canBeTiled(infoJson11level0));
+ assert.ok(canBeTiled(infoJson11level1));
+ assert.notOk(canBeTiled(infoJson2level0));
+ assert.ok(canBeTiled(infoJson2level0sizeByW));
+ assert.ok(canBeTiled(infoJson2level1));
+ assert.notOk(canBeTiled(infoJson3level0));
+ assert.notOk(canBeTiled(infoJson3level0sizeByW));
+ assert.ok(canBeTiled(infoJson3level0sizeByWh));
+ assert.ok(canBeTiled(infoJson3level1));
+ });
+
+ QUnit.test('IIIFTileSource private function constructLevels creates correct URLs for legacy pyramid', function( assert ) {
+ var constructLevels = function( data ) {
+ var source = getSource( data );
+ return source.__testonly__.constructLevels( source );
+ };
+ var levelsVersion2 = constructLevels(infoJson2level0);
+ assert.ok(Array.isArray(levelsVersion2));
+ assert.equal(levelsVersion2.length, 2, 'Constructed levels contain 2 entries');
+ assert.equal(levelsVersion2[0].url, 'http://example.com/identifier/full/1000,/0/default.jpg');
+ assert.equal(levelsVersion2[1].url, 'http://example.com/identifier/full/2000,/0/default.jpg');
+ // FIXME see below
+ // assert.equal(levelsVersion2[1].url, 'http://example.com/identifier/full/full/0/default.jpg');
+
+ var levelsVersion3 = constructLevels(infoJson3level0);
+ assert.ok(Array.isArray(levelsVersion3));
+ assert.equal(levelsVersion3.length, 2, 'Constructed levels contain 2 entries');
+ assert.equal(levelsVersion3[0].url, 'http://example.com/identifier/full/1000,500/0/default.jpg');
+ assert.equal(levelsVersion3[1].url, 'http://example.com/identifier/full/2000,1000/0/default.jpg');
+ /*
+ * FIXME: following https://iiif.io/api/image/3.0/#47-canonical-uri-syntax and
+ * https://iiif.io/api/image/2.1/#canonical-uri-syntax, I'd expect 'max' to be required to
+ * be served by a level 0 compliant service instead of 'w,h', 'full' instead of 'w,' respectivley.
+ */
+ //assert.equal(levelsVersion3[1].url, 'http://example.com/identifier/full/max/0/default.jpg');
+ });
+
+ QUnit.test('IIIFTileSource.getTileUrl returns the correct URLs', function( assert ) {
+ var source11Level1 = getSource(infoJson11level1);
+ assert.equal(source11Level1.getTileUrl(0, 0, 0), "http://example.com/identifier/full/8,/0/native.jpg");
+ assert.equal(source11Level1.getTileUrl(7, 0, 0), "http://example.com/identifier/0,0,1024,1000/512,/0/native.jpg");
+ assert.equal(source11Level1.getTileUrl(7, 1, 0), "http://example.com/identifier/1024,0,976,1000/488,/0/native.jpg");
+ assert.equal(source11Level1.getTileUrl(8, 0, 0), "http://example.com/identifier/0,0,512,512/512,/0/native.jpg");
+
+ var source2Level1 = getSource(infoJson2level1);
+ assert.equal(source2Level1.getTileUrl(0, 0, 0), "http://example.com/identifier/full/8,/0/default.jpg");
+ assert.equal(source2Level1.getTileUrl(7, 0, 0), "http://example.com/identifier/0,0,1024,1000/512,/0/default.jpg");
+ assert.equal(source2Level1.getTileUrl(7, 1, 0), "http://example.com/identifier/1024,0,976,1000/488,/0/default.jpg");
+ assert.equal(source2Level1.getTileUrl(8, 0, 0), "http://example.com/identifier/0,0,512,512/512,/0/default.jpg");
+ assert.equal(source2Level1.getTileUrl(8, 3, 0), "http://example.com/identifier/1536,0,464,512/464,/0/default.jpg");
+ assert.equal(source2Level1.getTileUrl(8, 0, 1), "http://example.com/identifier/0,512,512,488/512,/0/default.jpg");
+ assert.equal(source2Level1.getTileUrl(8, 3, 1), "http://example.com/identifier/1536,512,464,488/464,/0/default.jpg");
+
+ var source2Level0 = getSource(infoJson2level0);
+ assert.equal(source2Level0.getTileUrl(0, 0, 0), "http://example.com/identifier/full/1000,/0/default.jpg");
+ assert.equal(source2Level0.getTileUrl(1, 0, 0), "http://example.com/identifier/full/2000,/0/default.jpg");
+
+ var source3Level1 = getSource(infoJson3level1);
+ assert.equal(source3Level1.getTileUrl(0, 0, 0), "http://example.com/identifier/full/8,4/0/default.jpg");
+ assert.equal(source3Level1.getTileUrl(7, 0, 0), "http://example.com/identifier/0,0,1024,1000/512,500/0/default.jpg");
+ assert.equal(source3Level1.getTileUrl(7, 1, 0), "http://example.com/identifier/1024,0,976,1000/488,500/0/default.jpg");
+ assert.equal(source3Level1.getTileUrl(8, 0, 0), "http://example.com/identifier/0,0,512,512/512,512/0/default.jpg");
+ assert.equal(source3Level1.getTileUrl(8, 3, 0), "http://example.com/identifier/1536,0,464,512/464,512/0/default.jpg");
+ assert.equal(source3Level1.getTileUrl(8, 0, 1), "http://example.com/identifier/0,512,512,488/512,488/0/default.jpg");
+ assert.equal(source3Level1.getTileUrl(8, 3, 1), "http://example.com/identifier/1536,512,464,488/464,488/0/default.jpg");
+ });
+
+})();
diff --git a/test/modules/navigator.js b/test/modules/navigator.js
index e7f5f327..e55c5400 100644
--- a/test/modules/navigator.js
+++ b/test/modules/navigator.js
@@ -216,19 +216,34 @@
clientY: offset.top + locationY
};
$canvas
- .simulate(OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseenter' : 'mouseover', event)
+ .simulate('mouseenter', event)
.simulate('mousedown', event)
.simulate('mouseup', event);
};
var simulateNavigatorDrag = function (viewer, distanceX, distanceY) {
- var $canvas = $(viewer.element).find('.displayregion'),
- event = {
- dx: Math.floor(distanceX),
- dy: Math.floor(distanceY)
- };
- $canvas
- .simulate('drag', event);
+ var $canvas = $(viewer.element).find('.openseadragon-canvas'),
+ offset = $canvas.offset(),
+ event = {};
+
+ event.clientX = offset.left + 1;
+ event.clientY = offset.top + 1;
+ $canvas.simulate( 'mouseenter', event );
+
+ event.button = 0;
+ $canvas.simulate( 'mousedown', event );
+
+ event.clientX += distanceX;
+ event.clientY += distanceY;
+ $canvas.simulate( 'mousemove', event );
+
+ event.button = 0;
+ $canvas.simulate( 'mouseup', event );
+
+ event.clientX = offset.left - 1;
+ event.clientY = offset.top - 1;
+ event.relatedTarget = document.body;
+ $canvas.simulate( 'mouseleave', event );
};
var dragNavigatorBackToCenter = function () {
diff --git a/test/modules/tiledimage.js b/test/modules/tiledimage.js
index 46860648..4a075328 100644
--- a/test/modules/tiledimage.js
+++ b/test/modules/tiledimage.js
@@ -41,8 +41,11 @@
viewer.addHandler('open', function(event) {
var image = viewer.world.getItemAt(0);
var contentSize = image.getContentSize();
+ var sizeInWindowCoords = image.getSizeInWindowCoordinates();
assert.equal(contentSize.x, 500, 'contentSize.x');
assert.equal(contentSize.y, 2000, 'contentSize.y');
+ assert.equal(sizeInWindowCoords.x, 125, 'sizeInWindowCoords.x');
+ assert.equal(sizeInWindowCoords.y, 500, 'sizeInWindowCoords.y');
checkBounds(assert, image, new OpenSeadragon.Rect(5, 6, 10, 40), 'initial bounds');
@@ -74,7 +77,18 @@
image.setHeight(4);
checkBounds(assert, image, new OpenSeadragon.Rect(7, 8, 1, 4), 'bounds after width');
- assert.equal(handlerCount, 1, 'correct number of handlers called');
+ viewer.addHandler('zoom', function zoomHandler(event) {
+ var sizeInWindowCoords = image.getSizeInWindowCoordinates();
+ viewer.removeHandler('zoom', zoomHandler);
+ handlerCount++;
+ assert.equal(sizeInWindowCoords.x, 4000, 'sizeInWindowCoords.x after zoom');
+ assert.equal(sizeInWindowCoords.y, 16000, 'sizeInWindowCoords.y after zoom');
+ });
+
+ viewer.viewport.zoomTo(8, null, true);
+
+ assert.equal(handlerCount, 2, 'correct number of handlers called');
+
done();
});
diff --git a/test/modules/viewerretrieval.js b/test/modules/viewerretrieval.js
new file mode 100644
index 00000000..79c1c7b2
--- /dev/null
+++ b/test/modules/viewerretrieval.js
@@ -0,0 +1,93 @@
+/* global QUnit, $, testLog */
+
+(function() {
+ var viewer1;
+ var viewer2;
+
+ QUnit.module('ViewerRetrieval', {
+ beforeEach: function () {
+ $('')
+ .appendTo("#qunit-fixture");
+
+ testLog.reset();
+
+ viewer1 = OpenSeadragon({
+ id: 'example1',
+ prefixUrl: 'build/openseadragon/images/',
+ springStiffness: 100 // Faster animation = faster tests
+ });
+
+ viewer2 = OpenSeadragon({
+ id: 'example2',
+ prefixUrl: 'build/openseadragon/images/',
+ springStiffness: 100
+ });
+ },
+
+ afterEach: function () {
+ if (viewer1 && viewer1.destroy) {
+ viewer1.destroy();
+ }
+ if (viewer2 && viewer2.destroy) {
+ viewer2.destroy();
+ }
+ viewer1 = viewer2 = null;
+ }
+ });
+
+ QUnit.test('Get Viewers by Id', function(assert) {
+ var retrievedViewer1 = OpenSeadragon.getViewer('example1');
+ assert.ok(retrievedViewer1, 'Attached viewer retrieved');
+ assert.equal(retrievedViewer1, viewer1, 'Viewers are same instance');
+
+ var retrievedViewer2 = OpenSeadragon.getViewer('example2');
+ assert.ok(retrievedViewer2, 'Attached viewer retrieved');
+ assert.equal(retrievedViewer2, viewer2, 'Viewers are same instance');
+
+ // Internal state
+ assert.equal(OpenSeadragon._viewers.size, 2, 'Correct amount of viewers');
+ });
+
+ QUnit.test('Get Viewers by Element', function(assert) {
+ var retrievedViewer1 = OpenSeadragon.getViewer(
+ document.getElementById('example1'));
+ assert.ok(retrievedViewer1, 'Attached viewer retrieved');
+ assert.equal(retrievedViewer1, viewer1, 'Viewers are same instance');
+
+ var retrievedViewer2 = OpenSeadragon.getViewer(
+ document.getElementById('example2'));
+ assert.ok(retrievedViewer2, 'Attached viewer retrieved');
+ assert.equal(retrievedViewer2, viewer2, 'Viewers are same instance');
+
+ // Internal state
+ assert.equal(OpenSeadragon._viewers.size, 2, 'Correct amount of viewers');
+ });
+
+ QUnit.test('Undefined on Get Non-Existent Viewer by Id', function(assert) {
+ var notFoundViewer = OpenSeadragon.getViewer('no-viewer');
+ assert.equal(notFoundViewer, undefined, "Not found viewer is undefined");
+ });
+
+ QUnit.test('Undefined on Get Non-Existent Viewer by Element', function(assert) {
+ var element = document.createElement('div');
+ element.id = 'no-viewer';
+ document.body.appendChild(element);
+
+ var notFoundViewer = OpenSeadragon.getViewer(element);
+ assert.equal(notFoundViewer, undefined, "Not found viewer is undefined");
+ });
+
+ QUnit.test('Cleanup Viewers Registration', function(assert) {
+ viewer1.destroy();
+ viewer2.destroy();
+ viewer1 = viewer2 = null;
+
+ var retrievedViewer1 = OpenSeadragon.getViewer('example1');
+ var retrievedViewer2 = OpenSeadragon.getViewer('example2');
+ assert.equal(retrievedViewer1, undefined, 'Viewer was destroyed');
+ assert.equal(retrievedViewer2, undefined, 'Viewer was destroyed');
+
+ // Internal state
+ assert.equal(OpenSeadragon._viewers.size, 0, 'No viewers are registered');
+ });
+})();
diff --git a/test/test.html b/test/test.html
index 786af542..f971a257 100644
--- a/test/test.html
+++ b/test/test.html
@@ -22,6 +22,7 @@
+
@@ -44,6 +45,7 @@
+