diff --git a/changelog.txt b/changelog.txt
index b1405ba9..98a94fd0 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -38,6 +38,7 @@ OPENSEADRAGON CHANGELOG
* Margins option to push the home region in from the edges of the Viewer (#505)
* Rect and Point toString() functions are now consistent: rounding values to nearest hundredth
* Overlays appear in the DOM immediately on open or addOverlay (#507)
+* imageLoaderLimit now works (#544)
1.2.0: (in progress)
@@ -58,6 +59,7 @@ OPENSEADRAGON CHANGELOG
* Added option for home button to fill viewer (#474)
* Better handling of mid-update image loaded callbacks (#409)
* Tracked pointers are now cleaned up when Viewer.setMouseNavEnabled(false) is called (#518)
+* Added explicit pointer capture for touch event model touchstart events (#552)
1.1.1:
diff --git a/src/mousetracker.js b/src/mousetracker.js
index 3485c84d..8e89e43a 100644
--- a/src/mousetracker.js
+++ b/src/mousetracker.js
@@ -170,8 +170,6 @@
* @private
* @property {Boolean} tracking
* Are we currently tracking pointer events for this element.
- * @property {Boolean} capturing
- * Are we curruently capturing mouse events (legacy mouse events only).
*/
THIS[ this.hash ] = {
click: function ( event ) { onClick( _this, event ); },
@@ -197,7 +195,9 @@
touchleave: function ( event ) { onTouchLeave( _this, event ); },
touchstart: function ( event ) { onTouchStart( _this, event ); },
touchend: function ( event ) { onTouchEnd( _this, event ); },
+ touchendcaptured: function ( event ) { onTouchEndCaptured( _this, event ); },
touchmove: function ( event ) { onTouchMove( _this, event ); },
+ touchmovecaptured: function ( event ) { onTouchMoveCaptured( _this, event ); },
touchcancel: function ( event ) { onTouchCancel( _this, event ); },
gesturestart: function ( event ) { onGestureStart( _this, event ); },
@@ -227,12 +227,6 @@
// of the element (for hover-capable devices) and/or have contact or a button press initiated in the element.
activePointersLists: [],
- // Legacy mouse capture tracking
- capturing: false,
-
- // Pointer event model capture tracking
- pointerCaptureCount: 0,
-
// Tracking for double-click gesture
lastClickPos: null,
dblClickTimeOut: null,
@@ -944,6 +938,12 @@
* @memberof OpenSeadragon.MouseTracker.GesturePointList#
*/
this.clicks = 0;
+ /**
+ * Current number of captured pointers for the device.
+ * @member {Number} captureCount
+ * @memberof OpenSeadragon.MouseTracker.GesturePointList#
+ */
+ this.captureCount = 0;
};
$.MouseTracker.GesturePointList.prototype = /** @lends OpenSeadragon.MouseTracker.GesturePointList.prototype */{
/**
@@ -1042,33 +1042,47 @@
i,
pointerListCount = delegate.activePointersLists.length;
- if ( delegate.pointerCaptureCount > 0 ) {
- $.removeEvent(
- $.MouseTracker.captureElement,
- 'mousemove',
- delegate.mousemovecaptured,
- true
- );
- $.removeEvent(
- $.MouseTracker.captureElement,
- 'mouseup',
- delegate.mouseupcaptured,
- true
- );
- $.removeEvent(
- $.MouseTracker.captureElement,
- $.MouseTracker.unprefixedPointerEvents ? 'pointermove' : 'MSPointerMove',
- delegate.pointermovecaptured,
- true
- );
- $.removeEvent(
- $.MouseTracker.captureElement,
- $.MouseTracker.unprefixedPointerEvents ? 'pointerup' : 'MSPointerUp',
- delegate.pointerupcaptured,
- true
- );
+ for ( i = 0; i < pointerListCount; i++ ) {
+ if ( delegate.activePointersLists[ i ].captureCount > 0 ) {
+ $.removeEvent(
+ $.MouseTracker.captureElement,
+ 'mousemove',
+ delegate.mousemovecaptured,
+ true
+ );
+ $.removeEvent(
+ $.MouseTracker.captureElement,
+ 'mouseup',
+ delegate.mouseupcaptured,
+ true
+ );
+ $.removeEvent(
+ $.MouseTracker.captureElement,
+ $.MouseTracker.unprefixedPointerEvents ? 'pointermove' : 'MSPointerMove',
+ delegate.pointermovecaptured,
+ true
+ );
+ $.removeEvent(
+ $.MouseTracker.captureElement,
+ $.MouseTracker.unprefixedPointerEvents ? 'pointerup' : 'MSPointerUp',
+ delegate.pointerupcaptured,
+ true
+ );
+ $.removeEvent(
+ $.MouseTracker.captureElement,
+ 'touchmove',
+ delegate.touchmovecaptured,
+ true
+ );
+ $.removeEvent(
+ $.MouseTracker.captureElement,
+ 'touchend',
+ delegate.touchendcaptured,
+ true
+ );
- delegate.pointerCaptureCount = 0;
+ delegate.activePointersLists[ i ].captureCount = 0;
+ }
}
for ( i = 0; i < pointerListCount; i++ ) {
@@ -1130,29 +1144,62 @@
}
}
+ /**
+ * @private
+ * @inner
+ */
+ function getCaptureEventParams( tracker, pointerType ) {
+ var delegate = THIS[ tracker.hash ];
+
+ if ( pointerType === 'mouse' ) {
+ return {
+ upName: 'mouseup',
+ upHandler: delegate.mouseupcaptured,
+ moveName: 'mousemove',
+ moveHandler: delegate.mousemovecaptured
+ };
+ } else if ( pointerType === 'touch' ) {
+ return {
+ upName: 'touchend',
+ upHandler: delegate.touchendcaptured,
+ moveName: 'touchmove',
+ moveHandler: delegate.touchmovecaptured
+ };
+ } else {
+ return {
+ upName: $.MouseTracker.unprefixedPointerEvents ? 'pointerup' : 'MSPointerUp',
+ upHandler: delegate.pointerupcaptured,
+ moveName: $.MouseTracker.unprefixedPointerEvents ? 'pointermove' : 'MSPointerMove',
+ moveHandler: delegate.pointermovecaptured
+ };
+ }
+ }
+
/**
* Begin capturing pointer events to the tracked element.
* @private
* @inner
*/
- function capturePointer( tracker, isLegacyMouse ) {
- var delegate = THIS[ tracker.hash ];
+ function capturePointer( tracker, pointerType ) {
+ var delegate = THIS[ tracker.hash ],
+ pointsList = tracker.getActivePointersListByType( pointerType ),
+ eventParams = getCaptureEventParams( tracker, pointerType );
- delegate.pointerCaptureCount++;
+ pointsList.captureCount++;
- if ( delegate.pointerCaptureCount === 1 ) {
+ if ( pointsList.captureCount === 1 ) {
// We emulate mouse capture by hanging listeners on the window object.
// (Note we listen on the capture phase so the captured handlers will get called first)
$.addEvent(
$.MouseTracker.captureElement,
- isLegacyMouse ? 'mouseup' : ($.MouseTracker.unprefixedPointerEvents ? 'pointerup' : 'MSPointerUp'),
- isLegacyMouse ? delegate.mouseupcaptured : delegate.pointerupcaptured,
+ eventParams.upName,
+ eventParams.upHandler,
true
);
$.addEvent(
$.MouseTracker.captureElement,
- isLegacyMouse ? 'mousemove' : ($.MouseTracker.unprefixedPointerEvents ? 'pointermove' : 'MSPointerMove'),
- isLegacyMouse ? delegate.mousemovecaptured : delegate.pointermovecaptured,
+ eventParams.moveName,
+ eventParams.moveHandler,
true
);
}
@@ -1164,24 +1211,26 @@
* @private
* @inner
*/
- function releasePointer( tracker, isLegacyMouse ) {
- var delegate = THIS[ tracker.hash ];
+ function releasePointer( tracker, pointerType ) {
+ var delegate = THIS[ tracker.hash ],
+ pointsList = tracker.getActivePointersListByType( pointerType ),
+ eventParams = getCaptureEventParams( tracker, pointerType );
- delegate.pointerCaptureCount--;
+ pointsList.captureCount--;
- if ( delegate.pointerCaptureCount === 0 ) {
+ if ( pointsList.captureCount === 0 ) {
// We emulate mouse capture by hanging listeners on the window object.
// (Note we listen on the capture phase so the captured handlers will get called first)
$.removeEvent(
$.MouseTracker.captureElement,
- isLegacyMouse ? 'mousemove' : ($.MouseTracker.unprefixedPointerEvents ? 'pointermove' : 'MSPointerMove'),
- isLegacyMouse ? delegate.mousemovecaptured : delegate.pointermovecaptured,
+ eventParams.moveName,
+ eventParams.moveHandler,
true
);
$.removeEvent(
$.MouseTracker.captureElement,
- isLegacyMouse ? 'mouseup' : ($.MouseTracker.unprefixedPointerEvents ? 'pointerup' : 'MSPointerUp'),
- isLegacyMouse ? delegate.mouseupcaptured : delegate.pointerupcaptured,
+ eventParams.upName,
+ eventParams.upHandler,
true
);
}
@@ -1526,7 +1575,7 @@
if ( updatePointersDown( tracker, event, [ gPoint ], event.button ) ) {
$.stopEvent( event );
- capturePointer( tracker, true );
+ capturePointer( tracker, 'mouse' );
}
if ( tracker.clickHandler || tracker.dblClickHandler || tracker.pressHandler || tracker.dragHandler || tracker.dragEndHandler ) {
@@ -1574,7 +1623,7 @@
};
if ( updatePointersUp( tracker, event, [ gPoint ], event.button ) ) {
- releasePointer( tracker, true );
+ releasePointer( tracker, 'mouse' );
}
}
@@ -1695,8 +1744,8 @@
}
if ( updatePointersDown( tracker, event, gPoints, 0 ) ) { // 0 means primary button press/release or touch contact
- // Touch event model start, end, and move events are always captured so we don't need to capture explicitly
$.stopEvent( event );
+ capturePointer( tracker, 'touch' );
}
$.cancelEvent( event );
@@ -1708,6 +1757,28 @@
* @inner
*/
function onTouchEnd( tracker, event ) {
+ handleTouchEnd( tracker, event );
+ }
+
+
+ /**
+ * This handler is attached to the window object (on the capture phase) to emulate pointer capture.
+ * onTouchEnd is still attached to the tracked element, so stop propagation to avoid processing twice.
+ *
+ * @private
+ * @inner
+ */
+ function onTouchEndCaptured( tracker, event ) {
+ handleTouchEnd( tracker, event );
+ $.stopEvent( event );
+ }
+
+
+ /**
+ * @private
+ * @inner
+ */
+ function handleTouchEnd( tracker, event ) {
var time,
i,
touchCount = event.changedTouches.length,
@@ -1725,9 +1796,9 @@
} );
}
- // Touch event model start, end, and move events are always captured so we don't need to release capture.
- // We'll ignore the should-release-capture return value here
- updatePointersUp( tracker, event, gPoints, 0 ); // 0 means primary button press/release or touch contact
+ if ( updatePointersUp( tracker, event, gPoints, 0 ) ) {
+ releasePointer( tracker, 'touch' );
+ }
// simulate touchleave if not natively available
if ( !$.MouseTracker.haveTouchEnter && touchCount > 0 ) {
@@ -1743,6 +1814,28 @@
* @inner
*/
function onTouchMove( tracker, event ) {
+ handleTouchMove( tracker, event );
+ }
+
+
+ /**
+ * This handler is attached to the window object (on the capture phase) to emulate pointer capture.
+ * onTouchMove is still attached to the tracked element, so stop propagation to avoid processing twice.
+ *
+ * @private
+ * @inner
+ */
+ function onTouchMoveCaptured( tracker, event ) {
+ handleTouchMove( tracker, event );
+ $.stopEvent( event );
+ }
+
+
+ /**
+ * @private
+ * @inner
+ */
+ function handleTouchMove( tracker, event ) {
var i,
touchCount = event.changedTouches.length,
gPoints = [];
@@ -1866,8 +1959,8 @@
};
if ( updatePointersDown( tracker, event, [ gPoint ], event.button ) ) {
- capturePointer( tracker, false );
$.stopEvent( event );
+ capturePointer( tracker, 'pointer' );
}
if ( tracker.clickHandler || tracker.dblClickHandler || tracker.pressHandler || tracker.dragHandler || tracker.dragEndHandler || tracker.pinchHandler ) {
@@ -1917,8 +2010,7 @@
};
if ( updatePointersUp( tracker, event, [ gPoint ], event.button ) ) {
- releasePointer( tracker, false );
- //$.stopEvent( event );
+ releasePointer( tracker, 'pointer' );
}
}
diff --git a/src/navigator.js b/src/navigator.js
index bea23ddd..8f234248 100644
--- a/src/navigator.js
+++ b/src/navigator.js
@@ -245,14 +245,10 @@ $.Navigator = function( options ){
});
viewer.world.addHandler("remove-item", function(event) {
- var count = _this.world.getItemCount();
- var item;
- for (var i = 0; i < count; i++) {
- item = _this.world.getItemAt(i);
- if (item._originalForNavigator === event.item) {
- _this.world.removeItem(item);
- break;
- }
+ var theirItem = event.item;
+ var myItem = _this._getMatchingItem(theirItem);
+ if (myItem) {
+ _this.world.removeItem(myItem);
}
});
@@ -343,16 +339,45 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
// overrides Viewer.addTiledImage
addTiledImage: function(options) {
+ var _this = this;
+
var original = options.originalTiledImage;
delete options.original;
var optionsClone = $.extend({}, options, {
success: function(event) {
- event.item._originalForNavigator = original;
+ var myItem = event.item;
+ myItem._originalForNavigator = original;
+ _this._matchBounds(myItem, original, true);
+
+ original.addHandler('bounds-change', function() {
+ _this._matchBounds(myItem, original);
+ });
}
});
return $.Viewer.prototype.addTiledImage.apply(this, [optionsClone]);
+ },
+
+ // private
+ _getMatchingItem: function(theirItem) {
+ var count = this.world.getItemCount();
+ var item;
+ for (var i = 0; i < count; i++) {
+ item = this.world.getItemAt(i);
+ if (item._originalForNavigator === theirItem) {
+ return item;
+ }
+ }
+
+ return null;
+ },
+
+ // private
+ _matchBounds: function(myItem, theirItem, immediately) {
+ var bounds = theirItem.getBounds();
+ myItem.setPosition(bounds.getTopLeft(), immediately);
+ myItem.setWidth(bounds.width, immediately);
}
});
diff --git a/src/viewer.js b/src/viewer.js
index f6650203..781af981 100644
--- a/src/viewer.js
+++ b/src/viewer.js
@@ -36,6 +36,7 @@
// dictionary from hash to private properties
var THIS = {};
+var nextHash = 1;
/**
*
@@ -89,7 +90,7 @@ $.Viewer = function( options ) {
//internal state and dom identifiers
id: options.id,
- hash: options.hash || options.id,
+ hash: options.hash || nextHash++,
//dom nodes
/**
@@ -498,7 +499,7 @@ $.Viewer = function( options ) {
}
// Open initial tilesources
- if ( this.tileSources && this.tileSources.length) {
+ if (this.tileSources) {
this.open( this.tileSources );
}
diff --git a/src/viewport.js b/src/viewport.js
index 77893189..44654f7e 100644
--- a/src/viewport.js
+++ b/src/viewport.js
@@ -230,7 +230,16 @@ $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
* @function
*/
getHomeBounds: function() {
- return this.homeBounds.clone();
+ var center = this.homeBounds.getCenter( ),
+ width = 1.0 / this.getHomeZoom( ),
+ height = width / this.getAspectRatio();
+
+ return new $.Rect(
+ center.x - ( width / 2.0 ),
+ center.y - ( height / 2.0 ),
+ width,
+ height
+ );
},
/**
diff --git a/src/world.js b/src/world.js
index a61e2f69..4b1c9826 100644
--- a/src/world.js
+++ b/src/world.js
@@ -104,7 +104,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W
* @returns {OpenSeadragon.TiledImage} The item at the specified index.
*/
getItemAt: function( index ) {
- $.console.assert(index !== 'undefined', "[World.getItemAt] index is required");
+ $.console.assert(index !== undefined, "[World.getItemAt] index is required");
return this._items[ index ];
},
@@ -133,7 +133,7 @@ $.extend( $.World.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.W
*/
setItemIndex: function( item, index ) {
$.console.assert(item, "[World.setItemIndex] item is required");
- $.console.assert(index !== 'undefined', "[World.setItemIndex] index is required");
+ $.console.assert(index !== undefined, "[World.setItemIndex] index is required");
var oldIndex = this.getIndexOfItem( item );
diff --git a/test/coverage.html b/test/coverage.html
index c55fd31f..2c3bc093 100644
--- a/test/coverage.html
+++ b/test/coverage.html
@@ -65,7 +65,7 @@
-
+
diff --git a/test/demo/collections/main.js b/test/demo/collections/main.js
index cc18a064..83563085 100644
--- a/test/demo/collections/main.js
+++ b/test/demo/collections/main.js
@@ -17,13 +17,15 @@
zoomPerScroll: 1.02,
showNavigator: testNavigator,
useCanvas: true,
+ // defaultZoomLevel: 2,
+ // homeFillsViewer: true,
// sequenceMode: true,
// showReferenceStrip: true,
// referenceStripScroll: 'vertical',
navPrevNextWrap: false,
preserveViewport: false,
- collectionMode: true,
- collectionRows: 1,
+ // collectionMode: true,
+ // collectionRows: 1,
// collectionLayout: 'vertical',
// collectionTileSize: 10,
// collectionTileMargin: 10,
@@ -33,6 +35,20 @@
prefixUrl: "../../../build/openseadragon/images/"
};
+ var highsmith = {
+ Image: {
+ xmlns: "http://schemas.microsoft.com/deepzoom/2008",
+ Url: "http://openseadragon.github.io/example-images/highsmith/highsmith_files/",
+ Format: "jpg",
+ Overlap: "2",
+ TileSize: "256",
+ Size: {
+ Height: "9221",
+ Width: "7026"
+ }
+ }
+ };
+
if (testInitialOpen) {
config.tileSources = [
{
@@ -55,6 +71,11 @@
height: 1
}
];
+
+ // config.tileSources = {
+ // tileSource: highsmith,
+ // width: 1
+ // };
}
if (testOverlays) {
diff --git a/test/demo/item-animation.html b/test/demo/item-animation.html
index a6cbf3a6..254458a6 100644
--- a/test/demo/item-animation.html
+++ b/test/demo/item-animation.html
@@ -14,12 +14,18 @@
margin: 0;
}
+ .controls {
+ position: absolute;
+ right: 10px;
+ top: 10px;
+ }
+
-
+