Merge branch 'master' into ig-seams

This commit is contained in:
Ian Gilman 2021-11-09 11:31:37 -08:00
commit 2a84ce365e
49 changed files with 7331 additions and 6129 deletions

View File

@ -278,8 +278,9 @@
] ]
}, },
"globals": { "globals": {
"OpenSeadragon": true, "OpenSeadragon": "writable",
"define": false, "define": "readonly",
"module": false "module": "readonly",
"Map": "readonly"
} }
} }

View File

@ -1,9 +1,34 @@
{ {
"root": true,
"extends": [
"eslint:recommended"
],
"env": { "env": {
"es6": false,
"browser": true "browser": true
}, },
"extends": "eslint:recommended", "parserOptions": {
"ecmaVersion": 5,
"sourceType": "script",
"ecmaFeatures": {
"globalReturn": false,
"impliedStrict": false,
"jsx": false
}
},
"globals": {
"OpenSeadragon": "writable",
"define": "readonly",
"module": "readonly",
"Map": "readonly"
},
"rules": { "rules": {
"no-unused-vars": [
"error",
{
"args": "none"
}
],
"indent": [ "indent": [
"off", "off",
4 4
@ -16,24 +41,18 @@
"error", "error",
"always" "always"
], ],
"no-unused-vars": [
"error",
{
"args": "none"
}
],
"block-scoped-var": [ "block-scoped-var": [
"error" "error"
], ],
"consistent-return": [ "consistent-return": [
"off" "error"
], ],
"curly": [ "curly": [
"error", "error",
"all" "all"
], ],
"eqeqeq": [ "eqeqeq": [
"off" "error"
], ],
"no-eval": [ "no-eval": [
"error" "error"
@ -83,7 +102,7 @@
"error" "error"
], ],
"no-useless-escape": [ "no-useless-escape": [
"off" "error"
], ],
"no-useless-return": [ "no-useless-return": [
"error" "error"
@ -103,7 +122,9 @@
"no-use-before-define": [ "no-use-before-define": [
"error", "error",
{ {
"functions": false "functions": false,
"classes": true,
"variables": true
} }
], ],
"array-bracket-spacing": [ "array-bracket-spacing": [
@ -237,7 +258,7 @@
"after" "after"
], ],
"quote-props": [ "quote-props": [
"off", "error",
"as-needed" "as-needed"
], ],
"semi-spacing": [ "semi-spacing": [
@ -273,10 +294,5 @@
"no-loop-func": [ "no-loop-func": [
"error" "error"
] ]
},
"globals": {
"OpenSeadragon": true,
"define": false,
"module": false
} }
} }

12
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: # iangilman
patreon: # iangilman
open_collective: openseadragon
ko_fi: # iangilman
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

2
.gitignore vendored
View File

@ -7,4 +7,4 @@ instrumented/
.idea .idea
/nbproject/private/ /nbproject/private/
.directory .directory
test/demo/temp local-test

21
.vscode/tasks.json vendored
View File

@ -1,25 +1,26 @@
{ {
// See https://go.microsoft.com/fwlink/?LinkId=733558 // See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format // for the documentation about the tasks.json format
"version": "0.1.0", "version": "2.0.0",
"command": "grunt", "command": "grunt",
"isShellCommand": true,
"tasks": [ "tasks": [
{ {
"taskName": "build", "label": "build",
"args": [], "type": "grunt",
"isBuildCommand": true, "task": "build",
"isWatching": false,
"problemMatcher": [ "problemMatcher": [
"$lessCompile", "$lessCompile",
"$tsc", "$tsc",
"$jshint" "$jshint"
] ],
"group": "build"
}, },
{ {
"taskName": "test", "label": "test",
"args": [], "type": "grunt",
"isTestCommand": true "task": "test",
"problemMatcher": [],
"group": "test"
} }
] ]
} }

25
.vscode/tasks.json.old vendored Normal file
View File

@ -0,0 +1,25 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "0.1.0",
"command": "grunt",
"isShellCommand": true,
"tasks": [
{
"taskName": "build",
"args": [],
"isBuildCommand": true,
"isWatching": false,
"problemMatcher": [
"$lessCompile",
"$tsc",
"$jshint"
]
},
{
"taskName": "test",
"args": [],
"isTestCommand": true
}
]
}

View File

@ -1,6 +1,8 @@
/* eslint-disable no-redeclare */
/* global module */ /* global module */
module.exports = function(grunt) { module.exports = function(grunt) {
/* eslint-disable no-undef */
var dateFormat = require('dateformat'); var dateFormat = require('dateformat');
// ---------- // ----------
@ -123,6 +125,7 @@ module.exports = function(grunt) {
banner: banner, banner: banner,
compress: { compress: {
sequences: false, sequences: false,
/* eslint-disable camelcase */
join_vars: false join_vars: false
}, },
sourceMap: true, sourceMap: true,
@ -199,7 +202,7 @@ module.exports = function(grunt) {
target: sources target: sources
}, },
"git-describe": { "git-describe": {
"options": { options: {
failOnError: false failOnError: false
}, },
build: {} build: {}

View File

@ -1,13 +1,78 @@
OPENSEADRAGON CHANGELOG OPENSEADRAGON CHANGELOG
======================= =======================
2.4.2: (In Progress) 3.0.0: (In progress)
* BREAKING CHANGE: Dropped support for older browsers (IE < 11) (#1872 #1949 #1951 @msalsbery, #1950 @rmontroy)
* BREAKING CHANGE: Removed deprecated OpenSeadragon.getEvent function (#1949 @msalsbery)
* DEPRECATION: MouseTracker exitHandler deprecated for name change to leaveHandler for consistency with DOM event names (#1872 @msalsbery)
* Now when "simple image" tile sources are removed from the viewer, they free the memory used by the pyramid they create (#1789 @TakumaKira)
* Improvements to docs (#1814 @kenanchristian, #1872 @msalsbery, #1996 @tdiprima)
* Better cleanup on destruction, to avoid memory leaks (#1832 @JoFrMueller)
* Better handle destruction when navigator in custom location (#1884 @woodchuck)
* Miscellaneous code cleanup (#1840 @msalsbery)
* You can now specify tileSize for the Zoomify Tile Source (#1868 @abrlam)
* Better use of IIIF "max" and "full" URL parameters (#1871 @MImranAsghar)
* You can now specify the file format of the tiles in the Zoomify tile source (#1889 @abrlam)
* Improved browser sniffing - detect EDGE and CHROMEEDGE browsers (#1872 @msalsbery)
* Improved DOM event model feature detection (#1872 @msalsbery)
* Added support for options parameter on addEvent()/removeEvent (to support passive option) (#1872 @msalsbery)
* Added OpenSeadragon.eventIsCanceled() function for defaultPrevented detection on DOM events (#1872 @msalsbery)
* MouseTracker: better PointerEvent model detection - removed use of deprecated window.navigator.pointerEnabled (#1872 @msalsbery)
* MouseTracker: added overHandler/outHandler options for handling corresponding pointerover/pointerout events (#1872 @msalsbery)
* MouseTracker: changed enterHandler/leaveHandler to use DOM pointerenter/pointerleave events instead of simulating using pointerover/pointerout (#1872 @msalsbery)
* All internal uses of MouseTracker use pointerenter/pointerleave events instead of pointerover/pointerout events for more consistent pointer tracking (#1872 @msalsbery)
* Fixed bug in Button class where two MouseTracker event handlers used an invalid "this" causing issues in some browsers (#1872 @msalsbery)
* Added pointerType property to Viewer container-enter, container-exit, canvas-drag, canvas-drag-end, canvas-pinch events (#1872 @msalsbery)
* MouseTracker: Fire dragEndHandler event even if release point same as initial contact point (#1872 @msalsbery)
* MouseTracker: Pointer capture implemented with capture APIs where available. Only fallback to emulated capture on extremely old browsers (#1872 @msalsbery)
* MouseTracker: Added preProcessEventHandler option to allow MouseTracker instances to control bubbling and default behavior of events on their associated element (#1872 @msalsbery)
* MouseTracker: Improved handling of canceled events (#1872 @msalsbery)
* MouseTracker: Improved releasing of tracked pointers on destroy()/stopTracking() (#1872 @msalsbery)
* Updated Viewer, Button, Drawer, Navigator, ReferenceStrip DOM for proper DOM event handling (#1872 @msalsbery)
* Added OpenSeadragon.setElementPointerEventsNone() for setting pointer-events:'none' on DOM elements (#1872 @msalsbery)
* MouseTracker: added contextMenuHandler option for handling contextmenu events (#1872 @msalsbery)
* Viewer: added a canvas-contextmenu event (#1872 @msalsbery)
* Fixed simulated drag events in navigator tests (#1949 @msalsbery)
* Added preventDefault option to MouseTracker.contextMenuHandler and Viewer 'canvas-contextmenu' event args (#1951 @msalsbery)
* MouseTracker: Added preProcessEventHandler for keydown, keyup, keypress, focus, blur Events (#1951 @msalsbery)
* Fixed preventDefaultAction functionality in viewer events (#1953 @msalsbery)
* Added setImageFormatsSupported function (#1954 @pandaxtc)
* Added dragToPan to the GestureSettings class, implemented in Viewer (#1956 @msalsbery)
* Added preventDefault option to MouseTracker handlers: scrollHandler, keyDownHandler, keyUpHandler, keyHandler (#1957 @msalsbery)
* Fixed test "Events: Viewer: preventDefaultAction in dblClickHandler". Fixes #1372 (#1960 @msalsbery)
* ReferenceStrip: Fixed issue where its element was being removed from its parent element twice on destroy, causing an exception (#1958 @msalsbery)
* ReferenceStrip: Made its element focusable for keyboard navigation (#1958 @msalsbery)
* You can now flip individual images (not just the whole viewport) (#1903 @ali1234)
* Accessibility: we now take the browser's zoom into account when choosing what detail level to draw (#1937 @ronnymikalsen)
* Fixed a bug causing overlays to disappear in Sequence Mode (#1865 @gunmiosb)
* Fixed a bug where the ajaxHeaders provided per-image were not being used for image requests (#1968 @maxshuty)
* MouseTracker: Added workaround for WebKit Pointer Event Implicit Capture Bug (#1972 @msalsbery)
* Removed test for move-leave (fly-over, no enter event)...not a valid, handleable event state, no longer supported (#1972 @msalsbery)
* Added OpenSeadragon.setElementPointerEvents() for setting pointer-events to other values besides 'none' on DOM elements (#1972 @msalsbery)
* Now ensuring the page body is display:block when in fullscreen (#1995 @thewilkybarkid)
* Added a static method in OpenSeadragon to get an existing viewer (#2000 @HerCerM)
* Now ensuring that the new item is already in the navigator when the "add-item" event fires (#2005 @RammasEchor)
* Added keys to change image in sequence mode (j: previous, k: next) (#2007 @RammasEchor)
* Fixed a bug where the navigator wouldn't pick up opacity/composite changes made while it is loading (#2018 @crydell)
* Explicitly set passive:false for wheel event handlers to suppress console warnings. Fixes #1669 (#2043 @msalsbery)
* Viewer's canvas-click events now include an originalTarget property so you can know which element received the click (#2037 @iangilman)
* Added method for getting the size of an image in window coordinates (#2049 @superbland)
2.4.2:
* Add support for IIIF Image API 3.0 beta (#1764)
* You can now crop an image with arbitrary polygons (#1772)
* Improved support for using the Reference Strip in an OpenSeadragon Viewer inside a Web Component (#1676) * Improved support for using the Reference Strip in an OpenSeadragon Viewer inside a Web Component (#1676)
* Added setWidth and setHeight methods to Navigator (#1686) * Added setWidth and setHeight methods to Navigator (#1686)
* Fixed: Navigator was still resizing after you explicitly set its width and height with navigatorWidth and navigatorHeight (#1686) * Improvements to docs (#1696, #1698, #1716, #1719)
* Improvements to docs (#1696, #1698)
* Now passing Viewer AJAX configs down to ReferenceStrip thumbnails (#1701) * Now passing Viewer AJAX configs down to ReferenceStrip thumbnails (#1701)
* The ReferenceStrip now honors the useCanvas option from the Viewer (#1742)
* Fixed: Navigator was still resizing after you explicitly set its width and height with navigatorWidth and navigatorHeight (#1686)
* Fixed issues with touches on iOS 13 and iPad (#1754, #1756)
* No longer throwing an exception on pages that have malformed URL parameters (#1758)
* Fixed an issue with flipping the viewport on high pixel density screens (#1779)
* Removed use of deprecated imageSmoothingEnabled prefixes (#1740)
2.4.1: 2.4.1:
@ -40,7 +105,7 @@ OPENSEADRAGON CHANGELOG
* You can now prevent canvas-click events on the navigator (#1416) * You can now prevent canvas-click events on the navigator (#1416)
* The navigator can now be restricted to just horizontal or just vertical panning (#1416) * The navigator can now be restricted to just horizontal or just vertical panning (#1416)
* Fixed DziTileSource so it doesn't load levels above maxLevel or below minLevel, if set (#1492) * Fixed DziTileSource so it doesn't load levels above maxLevel or below minLevel, if set (#1492)
2.3.1: 2.3.1:
* Debug mode now uses different colors for different tiled images (customizable via debugGridColor) (#1271) * Debug mode now uses different colors for different tiled images (customizable via debugGridColor) (#1271)

7210
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "openseadragon", "name": "openseadragon",
"version": "2.4.1", "version": "2.4.2",
"description": "Provides a smooth, zoomable user interface for HTML/Javascript.", "description": "Provides a smooth, zoomable user interface for HTML/Javascript.",
"keywords": [ "keywords": [
"image", "image",
@ -14,7 +14,8 @@
"osm", "osm",
"tms" "tms"
], ],
"homepage": "http://openseadragon.github.io/", "homepage": "https://openseadragon.github.io/",
"funding": "https://opencollective.com/openseadragon",
"bugs": { "bugs": {
"url": "https://github.com/openseadragon/openseadragon/issues" "url": "https://github.com/openseadragon/openseadragon/issues"
}, },
@ -28,15 +29,15 @@
"url": "https://github.com/openseadragon/openseadragon.git" "url": "https://github.com/openseadragon/openseadragon.git"
}, },
"devDependencies": { "devDependencies": {
"grunt": "^1.0.4", "grunt": "^1.1.0",
"grunt-contrib-clean": "^1.1.0", "grunt-contrib-clean": "^2.0.0",
"grunt-contrib-compress": "^1.5.0", "grunt-contrib-compress": "^1.6.0",
"grunt-contrib-concat": "^1.0.1", "grunt-contrib-concat": "^1.0.1",
"grunt-contrib-connect": "^1.0.2", "grunt-contrib-connect": "^2.1.0",
"grunt-contrib-qunit": "^3.1.0", "grunt-contrib-qunit": "^3.1.0",
"grunt-contrib-uglify": "^3.4.0", "grunt-contrib-uglify": "^4.0.1",
"grunt-contrib-watch": "^1.1.0", "grunt-contrib-watch": "^1.1.0",
"grunt-eslint": "^20.2.0", "grunt-eslint": "^23.0.0",
"grunt-git-describe": "^2.4.4", "grunt-git-describe": "^2.4.4",
"grunt-istanbul": "^0.8.0", "grunt-istanbul": "^0.8.0",
"grunt-text-replace": "^0.4.0", "grunt-text-replace": "^0.4.0",

View File

@ -77,6 +77,7 @@ $.ButtonState = {
* @param {OpenSeadragon.EventHandler} [options.onExit=null] Event handler callback for {@link OpenSeadragon.Button.event:exit}. * @param {OpenSeadragon.EventHandler} [options.onExit=null] Event handler callback for {@link OpenSeadragon.Button.event:exit}.
* @param {OpenSeadragon.EventHandler} [options.onFocus=null] Event handler callback for {@link OpenSeadragon.Button.event:focus}. * @param {OpenSeadragon.EventHandler} [options.onFocus=null] Event handler callback for {@link OpenSeadragon.Button.event:focus}.
* @param {OpenSeadragon.EventHandler} [options.onBlur=null] Event handler callback for {@link OpenSeadragon.Button.event:blur}. * @param {OpenSeadragon.EventHandler} [options.onBlur=null] Event handler callback for {@link OpenSeadragon.Button.event:blur}.
* @param {Object} [options.userData=null] Arbitrary object to be passed unchanged to any attached handler methods.
*/ */
$.Button = function( options ) { $.Button = function( options ) {
@ -111,7 +112,8 @@ $.Button = function( options ) {
onEnter: null, onEnter: null,
onExit: null, onExit: null,
onFocus: null, onFocus: null,
onBlur: null onBlur: null,
userData: null
}, options ); }, options );
@ -136,6 +138,13 @@ $.Button = function( options ) {
this.imgDown.alt = this.imgDown.alt =
this.tooltip; this.tooltip;
// Allow pointer events to pass through the img elements so implicit
// pointer capture works on touch devices
$.setElementPointerEventsNone( this.imgRest );
$.setElementPointerEventsNone( this.imgGroup );
$.setElementPointerEventsNone( this.imgHover );
$.setElementPointerEventsNone( this.imgDown );
this.element.style.position = "relative"; this.element.style.position = "relative";
$.setElementTouchActionNone( this.element ); $.setElementTouchActionNone( this.element );
@ -158,7 +167,7 @@ $.Button = function( options ) {
this.imgDown.style.visibility = this.imgDown.style.visibility =
"hidden"; "hidden";
if ($.Browser.vendor == $.BROWSERS.FIREFOX && $.Browser.version < 3) { if ($.Browser.vendor === $.BROWSERS.FIREFOX && $.Browser.version < 3) {
this.imgGroup.style.top = this.imgGroup.style.top =
this.imgHover.style.top = this.imgHover.style.top =
this.imgDown.style.top = this.imgDown.style.top =
@ -203,6 +212,7 @@ $.Button = function( options ) {
*/ */
this.tracker = new $.MouseTracker({ this.tracker = new $.MouseTracker({
userData: 'Button.tracker',
element: this.element, element: this.element,
clickTimeThreshold: this.clickTimeThreshold, clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold, clickDistThreshold: this.clickDistThreshold,
@ -227,7 +237,7 @@ $.Button = function( options ) {
}, },
focusHandler: function ( event ) { focusHandler: function ( event ) {
this.enterHandler( event ); _this.tracker.enterHandler( event );
/** /**
* Raised when the Button element receives focus. * Raised when the Button element receives focus.
* *
@ -241,7 +251,7 @@ $.Button = function( options ) {
_this.raiseEvent( "focus", { originalEvent: event.originalEvent } ); _this.raiseEvent( "focus", { originalEvent: event.originalEvent } );
}, },
exitHandler: function( event ) { leaveHandler: function( event ) {
outTo( _this, $.ButtonState.GROUP ); outTo( _this, $.ButtonState.GROUP );
if ( event.insideElementPressed ) { if ( event.insideElementPressed ) {
/** /**
@ -259,7 +269,7 @@ $.Button = function( options ) {
}, },
blurHandler: function ( event ) { blurHandler: function ( event ) {
this.exitHandler( event ); _this.tracker.leaveHandler( event );
/** /**
* Raised when the Button element loses focus. * Raised when the Button element loses focus.
* *
@ -350,9 +360,11 @@ $.Button = function( options ) {
* @property {?Object} userData - Arbitrary subscriber-defined object. * @property {?Object} userData - Arbitrary subscriber-defined object.
*/ */
_this.raiseEvent( "release", { originalEvent: event.originalEvent } ); _this.raiseEvent( "release", { originalEvent: event.originalEvent } );
return false;
event.preventDefault = true;
} else{
event.preventDefault = false;
} }
return true;
} }
}); });
@ -363,8 +375,8 @@ $.Button = function( options ) {
$.extend( $.Button.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.Button.prototype */{ $.extend( $.Button.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.Button.prototype */{
/** /**
* TODO: Determine what this function is intended to do and if it's actually * Used by a button container element (e.g. a ButtonGroup) to transition the button state
* useful as an API point. * to ButtonState.GROUP.
* @function * @function
*/ */
notifyGroupEnter: function() { notifyGroupEnter: function() {
@ -372,8 +384,8 @@ $.extend( $.Button.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.
}, },
/** /**
* TODO: Determine what this function is intended to do and if it's actually * Used by a button container element (e.g. a ButtonGroup) to transition the button state
* useful as an API point. * to ButtonState.REST.
* @function * @function
*/ */
notifyGroupExit: function() { notifyGroupExit: function() {
@ -396,6 +408,28 @@ $.extend( $.Button.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.
this.element.disabled = false; this.element.disabled = false;
$.setElementOpacity( this.element, 1.0, true ); $.setElementOpacity( this.element, 1.0, true );
this.notifyGroupEnter(); this.notifyGroupEnter();
},
destroy: function() {
if (this.imgRest) {
this.element.removeChild(this.imgRest);
this.imgRest = null;
}
if (this.imgGroup) {
this.element.removeChild(this.imgGroup);
this.imgGroup = null;
}
if (this.imgHover) {
this.element.removeChild(this.imgHover);
this.imgHover = null;
}
if (this.imgDown) {
this.element.removeChild(this.imgDown);
this.imgDown = null;
}
this.removeAllHandlers();
this.tracker.destroy();
this.element = null;
} }
}); });
@ -451,13 +485,13 @@ function inTo( button, newState ) {
} }
if ( newState >= $.ButtonState.GROUP && if ( newState >= $.ButtonState.GROUP &&
button.currentState == $.ButtonState.REST ) { button.currentState === $.ButtonState.REST ) {
stopFading( button ); stopFading( button );
button.currentState = $.ButtonState.GROUP; button.currentState = $.ButtonState.GROUP;
} }
if ( newState >= $.ButtonState.HOVER && if ( newState >= $.ButtonState.HOVER &&
button.currentState == $.ButtonState.GROUP ) { button.currentState === $.ButtonState.GROUP ) {
if( button.imgHover ){ if( button.imgHover ){
button.imgHover.style.visibility = ""; button.imgHover.style.visibility = "";
} }
@ -465,7 +499,7 @@ function inTo( button, newState ) {
} }
if ( newState >= $.ButtonState.DOWN && if ( newState >= $.ButtonState.DOWN &&
button.currentState == $.ButtonState.HOVER ) { button.currentState === $.ButtonState.HOVER ) {
if( button.imgDown ){ if( button.imgDown ){
button.imgDown.style.visibility = ""; button.imgDown.style.visibility = "";
} }
@ -481,7 +515,7 @@ function outTo( button, newState ) {
} }
if ( newState <= $.ButtonState.HOVER && if ( newState <= $.ButtonState.HOVER &&
button.currentState == $.ButtonState.DOWN ) { button.currentState === $.ButtonState.DOWN ) {
if( button.imgDown ){ if( button.imgDown ){
button.imgDown.style.visibility = "hidden"; button.imgDown.style.visibility = "hidden";
} }
@ -489,7 +523,7 @@ function outTo( button, newState ) {
} }
if ( newState <= $.ButtonState.GROUP && if ( newState <= $.ButtonState.GROUP &&
button.currentState == $.ButtonState.HOVER ) { button.currentState === $.ButtonState.HOVER ) {
if( button.imgHover ){ if( button.imgHover ){
button.imgHover.style.visibility = "hidden"; button.imgHover.style.visibility = "hidden";
} }
@ -497,7 +531,7 @@ function outTo( button, newState ) {
} }
if ( newState <= $.ButtonState.REST && if ( newState <= $.ButtonState.REST &&
button.currentState == $.ButtonState.GROUP ) { button.currentState === $.ButtonState.GROUP ) {
beginFading( button ); beginFading( button );
button.currentState = $.ButtonState.REST; button.currentState = $.ButtonState.REST;
} }

View File

@ -88,6 +88,7 @@ $.ButtonGroup = function( options ) {
* @memberof OpenSeadragon.ButtonGroup# * @memberof OpenSeadragon.ButtonGroup#
*/ */
this.tracker = new $.MouseTracker({ this.tracker = new $.MouseTracker({
userData: 'ButtonGroup.tracker',
element: this.element, element: this.element,
clickTimeThreshold: this.clickTimeThreshold, clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold, clickDistThreshold: this.clickDistThreshold,
@ -97,7 +98,7 @@ $.ButtonGroup = function( options ) {
_this.buttons[ i ].notifyGroupEnter(); _this.buttons[ i ].notifyGroupEnter();
} }
}, },
exitHandler: function ( event ) { leaveHandler: function ( event ) {
var i; var i;
if ( !event.insideElementPressed ) { if ( !event.insideElementPressed ) {
for ( i = 0; i < _this.buttons.length; i++ ) { for ( i = 0; i < _this.buttons.length; i++ ) {
@ -127,8 +128,18 @@ $.ButtonGroup.prototype = {
* @function * @function
* @private * @private
*/ */
emulateExit: function() { emulateLeave: function() {
this.tracker.exitHandler( { eventSource: this.tracker } ); this.tracker.leaveHandler( { eventSource: this.tracker } );
},
destroy: function() {
while (this.buttons.length) {
var button = this.buttons.pop();
this.element.removeChild(button.element);
button.destroy();
}
this.tracker.destroy();
this.element = null;
} }
}; };

View File

@ -112,13 +112,13 @@ $.Control = function ( element, options, container ) {
* @member {Element} wrapper * @member {Element} wrapper
* @memberof OpenSeadragon.Control# * @memberof OpenSeadragon.Control#
*/ */
if ( this.anchor == $.ControlAnchor.ABSOLUTE ) { if ( this.anchor === $.ControlAnchor.ABSOLUTE ) {
this.wrapper = $.makeNeutralElement( "div" ); this.wrapper = $.makeNeutralElement( "div" );
this.wrapper.style.position = "absolute"; this.wrapper.style.position = "absolute";
this.wrapper.style.top = typeof (options.top) == "number" ? (options.top + 'px') : options.top; this.wrapper.style.top = typeof (options.top) === "number" ? (options.top + 'px') : options.top;
this.wrapper.style.left = typeof (options.left) == "number" ? (options.left + 'px') : options.left; this.wrapper.style.left = typeof (options.left) === "number" ? (options.left + 'px') : options.left;
this.wrapper.style.height = typeof (options.height) == "number" ? (options.height + 'px') : options.height; this.wrapper.style.height = typeof (options.height) === "number" ? (options.height + 'px') : options.height;
this.wrapper.style.width = typeof (options.width) == "number" ? (options.width + 'px') : options.width; this.wrapper.style.width = typeof (options.width) === "number" ? (options.width + 'px') : options.width;
this.wrapper.style.margin = "0px"; this.wrapper.style.margin = "0px";
this.wrapper.style.padding = "0px"; this.wrapper.style.padding = "0px";
@ -130,7 +130,7 @@ $.Control = function ( element, options, container ) {
} else { } else {
this.wrapper = $.makeNeutralElement( "div" ); this.wrapper = $.makeNeutralElement( "div" );
this.wrapper.style.display = "inline-block"; this.wrapper.style.display = "inline-block";
if ( this.anchor == $.ControlAnchor.NONE ) { if ( this.anchor === $.ControlAnchor.NONE ) {
// IE6 fix // IE6 fix
this.wrapper.style.width = this.wrapper.style.height = "100%"; this.wrapper.style.width = this.wrapper.style.height = "100%";
} }
@ -138,8 +138,8 @@ $.Control = function ( element, options, container ) {
this.wrapper.appendChild( this.element ); this.wrapper.appendChild( this.element );
if (options.attachToViewer ) { if (options.attachToViewer ) {
if ( this.anchor == $.ControlAnchor.TOP_RIGHT || if ( this.anchor === $.ControlAnchor.TOP_RIGHT ||
this.anchor == $.ControlAnchor.BOTTOM_RIGHT ) { this.anchor === $.ControlAnchor.BOTTOM_RIGHT ) {
this.container.insertBefore( this.container.insertBefore(
this.wrapper, this.wrapper,
this.container.firstChild this.container.firstChild
@ -161,7 +161,9 @@ $.Control.prototype = {
*/ */
destroy: function() { destroy: function() {
this.wrapper.removeChild( this.element ); this.wrapper.removeChild( this.element );
this.container.removeChild( this.wrapper ); if (this.anchor !== $.ControlAnchor.NONE) {
this.container.removeChild(this.wrapper);
}
}, },
/** /**
@ -170,7 +172,7 @@ $.Control.prototype = {
* @return {Boolean} true if currently visible, false otherwise. * @return {Boolean} true if currently visible, false otherwise.
*/ */
isVisible: function() { isVisible: function() {
return this.wrapper.style.display != "none"; return this.wrapper.style.display !== "none";
}, },
/** /**
@ -180,7 +182,7 @@ $.Control.prototype = {
*/ */
setVisible: function( visible ) { setVisible: function( visible ) {
this.wrapper.style.display = visible ? this.wrapper.style.display = visible ?
( this.anchor == $.ControlAnchor.ABSOLUTE ? 'block' : 'inline-block' ) : ( this.anchor === $.ControlAnchor.ABSOLUTE ? 'block' : 'inline-block' ) :
"none"; "none";
}, },
@ -190,7 +192,7 @@ $.Control.prototype = {
* @param {Number} opactiy - a value between 1 and 0 inclusively. * @param {Number} opactiy - a value between 1 and 0 inclusively.
*/ */
setOpacity: function( opacity ) { setOpacity: function( opacity ) {
if ( this.element[ $.SIGNAL ] && $.Browser.vendor == $.BROWSERS.IE ) { if ( this.element[ $.SIGNAL ] && $.Browser.vendor === $.BROWSERS.IE ) {
$.setElementOpacity( this.element, opacity, true ); $.setElementOpacity( this.element, opacity, true );
} else { } else {
$.setElementOpacity( this.wrapper, opacity, true ); $.setElementOpacity( this.wrapper, opacity, true );

View File

@ -218,7 +218,7 @@
i; i;
for ( i = controls.length - 1; i >= 0; i-- ) { for ( i = controls.length - 1; i >= 0; i-- ) {
if ( controls[ i ].element == element ) { if ( controls[ i ].element === element ) {
return i; return i;
} }
} }

View File

@ -126,6 +126,10 @@ $.Drawer = function( options ) {
this.canvas.style.height = "100%"; this.canvas.style.height = "100%";
this.canvas.style.position = "absolute"; this.canvas.style.position = "absolute";
$.setElementOpacity( this.canvas, this.opacity, true ); $.setElementOpacity( this.canvas, this.opacity, true );
// Allow pointer events to pass through the canvas element so implicit
// pointer capture works on touch devices
$.setElementPointerEventsNone( this.canvas );
$.setElementTouchActionNone( this.canvas );
// explicit left-align // explicit left-align
this.container.style.textAlign = "left"; this.container.style.textAlign = "left";
@ -166,6 +170,41 @@ $.Drawer.prototype = {
return this; return this;
}, },
/**
* This function converts the given point from to the drawer coordinate by
* multiplying it with the pixel density.
* This function does not take rotation into account, thus assuming provided
* point is at 0 degree.
* @param {OpenSeadragon.Point} point - the pixel point to convert
*/
viewportCoordToDrawerCoord: function(point) {
var vpPoint = this.viewport.pixelFromPointNoRotate(point, true);
return new $.Point(
vpPoint.x * $.pixelDensityRatio,
vpPoint.y * $.pixelDensityRatio
);
},
/**
* This function will create multiple polygon paths on the drawing context by provided polygons,
* then clip the context to the paths.
* @param {OpenSeadragon.Point[][]} polygons - an array of polygons. A polygon is an array of OpenSeadragon.Point
* @param {Boolean} useSketch - Whether to use the sketch canvas or not.
*/
clipWithPolygons: function (polygons, useSketch) {
if (!this.useCanvas) {
return;
}
var context = this._getContext(useSketch);
context.beginPath();
polygons.forEach(function (polygon) {
polygon.forEach(function (coord, i) {
context[i === 0 ? 'moveTo' : 'lineTo'](coord.x, coord.y);
});
});
context.clip();
},
/** /**
* Set the opacity of the drawer. * Set the opacity of the drawer.
* @param {Number} opacity * @param {Number} opacity
@ -249,8 +288,8 @@ $.Drawer.prototype = {
this.canvas.innerHTML = ""; this.canvas.innerHTML = "";
if ( this.useCanvas ) { if ( this.useCanvas ) {
var viewportSize = this._calculateCanvasSize(); var viewportSize = this._calculateCanvasSize();
if( this.canvas.width != viewportSize.x || if( this.canvas.width !== viewportSize.x ||
this.canvas.height != viewportSize.y ) { this.canvas.height !== viewportSize.y ) {
this.canvas.width = viewportSize.x; this.canvas.width = viewportSize.x;
this.canvas.height = viewportSize.y; this.canvas.height = viewportSize.y;
this._updateImageSmoothingEnabled(this.context); this._updateImageSmoothingEnabled(this.context);
@ -633,8 +672,6 @@ $.Drawer.prototype = {
// private // private
_updateImageSmoothingEnabled: function(context){ _updateImageSmoothingEnabled: function(context){
context.mozImageSmoothingEnabled = this._imageSmoothingEnabled;
context.webkitImageSmoothingEnabled = this._imageSmoothingEnabled;
context.msImageSmoothingEnabled = this._imageSmoothingEnabled; context.msImageSmoothingEnabled = this._imageSmoothingEnabled;
context.imageSmoothingEnabled = this._imageSmoothingEnabled; context.imageSmoothingEnabled = this._imageSmoothingEnabled;
}, },

View File

@ -108,7 +108,7 @@ $.extend( $.DziTileSource.prototype, $.TileSource.prototype, /** @lends OpenSead
if ( data.Image ) { if ( data.Image ) {
ns = data.Image.xmlns; ns = data.Image.xmlns;
} else if ( data.documentElement) { } else if ( data.documentElement) {
if ("Image" == data.documentElement.localName || "Image" == data.documentElement.tagName) { if ("Image" === data.documentElement.localName || "Image" === data.documentElement.tagName) {
ns = data.documentElement.namespaceURI; ns = data.documentElement.namespaceURI;
} }
} }
@ -142,9 +142,9 @@ $.extend( $.DziTileSource.prototype, $.TileSource.prototype, /** @lends OpenSead
if (url && !options.tilesUrl) { if (url && !options.tilesUrl) {
options.tilesUrl = url.replace( options.tilesUrl = url.replace(
/([^\/]+?)(\.(dzi|xml|js)?(\?[^\/]*)?)?\/?$/, '$1_files/'); /([^/]+?)(\.(dzi|xml|js)?(\?[^/]*)?)?\/?$/, '$1_files/');
if (url.search(/\.(dzi|xml|js)\?/) != -1) { if (url.search(/\.(dzi|xml|js)\?/) !== -1) {
options.queryParams = url.match(/\?.*/); options.queryParams = url.match(/\?.*/);
}else{ }else{
options.queryParams = ''; options.queryParams = '';
@ -240,7 +240,7 @@ function configureFromXML( tileSource, xmlDoc ){
sizeNode, sizeNode,
i; i;
if ( rootName == "Image" ) { if ( rootName === "Image" ) {
try { try {
sizeNode = root.getElementsByTagName("Size" )[ 0 ]; sizeNode = root.getElementsByTagName("Size" )[ 0 ];
@ -304,9 +304,9 @@ function configureFromXML( tileSource, xmlDoc ){
e : e :
new Error( $.getString("Errors.Dzi") ); new Error( $.getString("Errors.Dzi") );
} }
} else if ( rootName == "Collection" ) { } else if ( rootName === "Collection" ) {
throw new Error( $.getString( "Errors.Dzc" ) ); throw new Error( $.getString( "Errors.Dzc" ) );
} else if ( rootName == "Error" ) { } else if ( rootName === "Error" ) {
var messageNode = root.getElementsByTagName("Message")[0]; var messageNode = root.getElementsByTagName("Message")[0];
var message = messageNode.firstChild.nodeValue; var message = messageNode.firstChild.nodeValue;
throw new Error(message); throw new Error(message);

View File

@ -59,6 +59,8 @@ $.IIIFTileSource = function( options ){
this.tileFormat = this.tileFormat || 'jpg'; this.tileFormat = this.tileFormat || 'jpg';
this.version = options.version;
// N.B. 2.0 renamed scale_factors to scaleFactors // N.B. 2.0 renamed scale_factors to scaleFactors
if ( this.tile_width && this.tile_height ) { if ( this.tile_width && this.tile_height ) {
options.tileWidth = this.tile_width; options.tileWidth = this.tile_width;
@ -69,7 +71,7 @@ $.IIIFTileSource = function( options ){
options.tileSize = this.tile_height; options.tileSize = this.tile_height;
} else if ( this.tiles ) { } else if ( this.tiles ) {
// Version 2.0 forwards // Version 2.0 forwards
if ( this.tiles.length == 1 ) { if ( this.tiles.length === 1 ) {
options.tileWidth = this.tiles[0].width; options.tileWidth = this.tiles[0].width;
// Use height if provided, otherwise assume square tiles and use width. // Use height if provided, otherwise assume square tiles and use width.
options.tileHeight = this.tiles[0].height || this.tiles[0].width; options.tileHeight = this.tiles[0].height || this.tiles[0].width;
@ -88,7 +90,7 @@ $.IIIFTileSource = function( options ){
} }
} }
} }
} else if ( canBeTiled(options.profile) ) { } else if ( canBeTiled(options) ) {
// use the largest of tileOptions that is smaller than the short dimension // use the largest of tileOptions that is smaller than the short dimension
var shortDim = Math.min( this.height, this.width ), var shortDim = Math.min( this.height, this.width ),
tileOptions = [256, 512, 1024], tileOptions = [256, 512, 1024],
@ -150,12 +152,12 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
supports: function( data, url ) { supports: function( data, url ) {
// Version 2.0 and forwards // Version 2.0 and forwards
if (data.protocol && data.protocol == 'http://iiif.io/api/image') { if (data.protocol && data.protocol === 'http://iiif.io/api/image') {
return true; return true;
// Version 1.1 // Version 1.1
} else if ( data['@context'] && ( } else if ( data['@context'] && (
data['@context'] == "http://library.stanford.edu/iiif/image-api/1.1/context.json" || data['@context'] === "http://library.stanford.edu/iiif/image-api/1.1/context.json" ||
data['@context'] == "http://iiif.io/api/image/1/context.json") ) { data['@context'] === "http://iiif.io/api/image/1/context.json") ) {
// N.B. the iiif.io context is wrong, but where the representation lives so likely to be used // N.B. the iiif.io context is wrong, but where the representation lives so likely to be used
return true; return true;
@ -166,8 +168,8 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
} else if ( data.identifier && data.width && data.height ) { } else if ( data.identifier && data.width && data.height ) {
return true; return true;
} else if ( data.documentElement && } else if ( data.documentElement &&
"info" == data.documentElement.tagName && "info" === data.documentElement.tagName &&
"http://library.stanford.edu/iiif/image-api/ns/" == "http://library.stanford.edu/iiif/image-api/ns/" ===
data.documentElement.namespaceURI) { data.documentElement.namespaceURI) {
return true; return true;
@ -201,11 +203,42 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
var options = configureFromXml10( data ); var options = configureFromXml10( data );
options['@context'] = "http://iiif.io/api/image/1.0/context.json"; options['@context'] = "http://iiif.io/api/image/1.0/context.json";
options['@id'] = url.replace('/info.xml', ''); options['@id'] = url.replace('/info.xml', '');
options.version = 1;
return options; return options;
} else { } else {
if ( !data['@context'] ) { if ( !data['@context'] ) {
data['@context'] = 'http://iiif.io/api/image/1.0/context.json'; data['@context'] = 'http://iiif.io/api/image/1.0/context.json';
data['@id'] = url.replace('/info.json', ''); data['@id'] = url.replace('/info.json', '');
data.version = 1;
} else {
var context = data['@context'];
if (Array.isArray(context)) {
for (var i = 0; i < context.length; i++) {
if (typeof context[i] === 'string' &&
( /^http:\/\/iiif\.io\/api\/image\/[1-3]\/context\.json$/.test(context[i]) ||
context[i] === 'http://library.stanford.edu/iiif/image-api/1.1/context.json' ) ) {
context = context[i];
break;
}
}
}
switch (context) {
case 'http://iiif.io/api/image/1/context.json':
case 'http://library.stanford.edu/iiif/image-api/1.1/context.json':
data.version = 1;
break;
case 'http://iiif.io/api/image/2/context.json':
data.version = 2;
break;
case 'http://iiif.io/api/image/3/context.json':
data.version = 3;
break;
default:
$.console.error('Data has a @context property which contains no known IIIF context URI.');
}
}
if ( !data['@id'] && data['id'] ) {
data['@id'] = data['id'];
} }
if(data.preferredFormats) { if(data.preferredFormats) {
for (var f = 0; f < data.preferredFormats.length; f++ ) { for (var f = 0; f < data.preferredFormats.length; f++ ) {
@ -350,27 +383,28 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
iiifTileH, iiifTileH,
iiifSize, iiifSize,
iiifSizeW, iiifSizeW,
iiifSizeH,
iiifQuality, iiifQuality,
uri, uri;
isv1;
tileWidth = this.getTileWidth(level); tileWidth = this.getTileWidth(level);
tileHeight = this.getTileHeight(level); tileHeight = this.getTileHeight(level);
iiifTileSizeWidth = Math.ceil( tileWidth / scale ); iiifTileSizeWidth = Math.ceil( tileWidth / scale );
iiifTileSizeHeight = Math.ceil( tileHeight / scale ); iiifTileSizeHeight = Math.ceil( tileHeight / scale );
isv1 = ( this['@context'].indexOf('/1.0/context.json') > -1 || if (this.version === 1) {
this['@context'].indexOf('/1.1/context.json') > -1 ||
this['@context'].indexOf('/1/context.json') > -1 );
if (isv1) {
iiifQuality = "native." + this.tileFormat; iiifQuality = "native." + this.tileFormat;
} else { } else {
iiifQuality = "default." + this.tileFormat; iiifQuality = "default." + this.tileFormat;
} }
if ( levelWidth < tileWidth && levelHeight < tileHeight ){ if ( levelWidth < tileWidth && levelHeight < tileHeight ){
if ( isv1 || levelWidth !== this.width ) { if ( this.version === 2 && levelWidth === this.width ) {
iiifSize = levelWidth + ","; iiifSize = "full";
} else { } else if ( this.version === 3 && levelWidth === this.width && levelHeight === this.height ) {
iiifSize = "max"; iiifSize = "max";
} else if ( this.version === 3 ) {
iiifSize = levelWidth + "," + levelHeight;
} else {
iiifSize = levelWidth + ",";
} }
iiifRegion = 'full'; iiifRegion = 'full';
} else { } else {
@ -384,8 +418,13 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
iiifRegion = [ iiifTileX, iiifTileY, iiifTileW, iiifTileH ].join( ',' ); iiifRegion = [ iiifTileX, iiifTileY, iiifTileW, iiifTileH ].join( ',' );
} }
iiifSizeW = Math.ceil( iiifTileW * scale ); iiifSizeW = Math.ceil( iiifTileW * scale );
if ( (!isv1) && iiifSizeW === this.width ) { iiifSizeH = Math.ceil( iiifTileH * scale );
if ( this.version === 2 && iiifSizeW === this.width ) {
iiifSize = "full";
} else if ( this.version === 3 && iiifSizeW === this.width && iiifSizeH === this.height ) {
iiifSize = "max"; iiifSize = "max";
} else if (this.version === 3) {
iiifSize = iiifSizeW + "," + iiifSizeH;
} else { } else {
iiifSize = iiifSizeW + ","; iiifSize = iiifSizeW + ",";
} }
@ -393,6 +432,11 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
uri = [ this['@id'], iiifRegion, iiifSize, IIIF_ROTATION, iiifQuality ].join( '/' ); uri = [ this['@id'], iiifRegion, iiifSize, IIIF_ROTATION, iiifQuality ].join( '/' );
return uri; return uri;
},
__testonly__: {
canBeTiled: canBeTiled,
constructLevels: constructLevels
} }
}); });
@ -403,18 +447,24 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
* @param {array} profile - IIIF profile array * @param {array} profile - IIIF profile array
* @throws {Error} * @throws {Error}
*/ */
function canBeTiled ( profile ) { function canBeTiled ( options ) {
var level0Profiles = [ var level0Profiles = [
"http://library.stanford.edu/iiif/image-api/compliance.html#level0", "http://library.stanford.edu/iiif/image-api/compliance.html#level0",
"http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level0", "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level0",
"http://iiif.io/api/image/2/level0.json" "http://iiif.io/api/image/2/level0.json",
"level0",
"https://iiif.io/api/image/3/level0.json"
]; ];
var isLevel0 = (level0Profiles.indexOf(profile[0]) !== -1); var profileLevel = Array.isArray(options.profile) ? options.profile[0] : options.profile;
var hasSizeByW = false; var isLevel0 = (level0Profiles.indexOf(profileLevel) !== -1);
if ( profile.length > 1 && profile[1].supports ) { var hasCanoncicalSizeFeature = false;
hasSizeByW = profile[1].supports.indexOf( "sizeByW" ) !== -1; if ( options.version === 2 && options.profile.length > 1 && options.profile[1].supports ) {
hasCanoncicalSizeFeature = options.profile[1].supports.indexOf( "sizeByW" ) !== -1;
} }
return !isLevel0 || hasSizeByW; if ( options.version === 3 && options.extraFeatures ) {
hasCanoncicalSizeFeature = options.extraFeatures.indexOf( "sizeByWh" ) !== -1;
}
return !isLevel0 || hasCanoncicalSizeFeature;
} }
/** /**
@ -427,7 +477,9 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
var levels = []; var levels = [];
for(var i = 0; i < options.sizes.length; i++) { for(var i = 0; i < options.sizes.length; i++) {
levels.push({ levels.push({
url: options['@id'] + '/full/' + options.sizes[i].width + ',/0/default.' + options.tileFormat, url: options['@id'] + '/full/' + options.sizes[i].width + ',' +
(options.version === 3 ? options.sizes[i].height : '') +
'/0/default.' + options.tileFormat,
width: options.sizes[i].width, width: options.sizes[i].width,
height: options.sizes[i].height height: options.sizes[i].height
}); });
@ -448,7 +500,7 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
rootName = root.tagName, rootName = root.tagName,
configuration = null; configuration = null;
if ( rootName == "info" ) { if ( rootName === "info" ) {
try { try {
configuration = {}; configuration = {};
parseXML10( root, configuration ); parseXML10( root, configuration );
@ -466,7 +518,7 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
function parseXML10( node, configuration, property ) { function parseXML10( node, configuration, property ) {
var i, var i,
value; value;
if ( node.nodeType == 3 && property ) {//text node if ( node.nodeType === 3 && property ) {//text node
value = node.nodeValue.trim(); value = node.nodeValue.trim();
if( value.match(/^\d*$/)){ if( value.match(/^\d*$/)){
value = Number( value ); value = Number( value );
@ -479,7 +531,7 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSea
} }
configuration[ property ].push( value ); configuration[ property ].push( value );
} }
} else if( node.nodeType == 1 ){ } else if( node.nodeType === 1 ){
for( i = 0; i < node.childNodes.length; i++ ){ for( i = 0; i < node.childNodes.length; i++ ){
parseXML10( node.childNodes[ i ], configuration, node.nodeName ); parseXML10( node.childNodes[ i ], configuration, node.nodeName );
} }

View File

@ -114,9 +114,8 @@
} }
$.addEvent(image, 'load', function () { $.addEvent(image, 'load', function () {
/* IE8 fix since it has no naturalWidth and naturalHeight */ _this.width = image.naturalWidth;
_this.width = Object.prototype.hasOwnProperty.call(image, 'naturalWidth') ? image.naturalWidth : image.width; _this.height = image.naturalHeight;
_this.height = Object.prototype.hasOwnProperty.call(image, 'naturalHeight') ? image.naturalHeight : image.height;
_this.aspectRatio = _this.width / _this.height; _this.aspectRatio = _this.width / _this.height;
_this.dimensions = new $.Point(_this.width, _this.height); _this.dimensions = new $.Point(_this.width, _this.height);
_this._tileWidth = _this.width; _this._tileWidth = _this.width;
@ -195,6 +194,13 @@
} }
return context; return context;
}, },
/**
* Destroys ImageTileSource
* @function
*/
destroy: function () {
this._freeupCanvasMemory();
},
// private // private
// //
@ -203,9 +209,8 @@
_buildLevels: function () { _buildLevels: function () {
var levels = [{ var levels = [{
url: this._image.src, url: this._image.src,
/* IE8 fix since it has no naturalWidth and naturalHeight */ width: this._image.naturalWidth,
width: Object.prototype.hasOwnProperty.call(this._image, 'naturalWidth') ? this._image.naturalWidth : this._image.width, height: this._image.naturalHeight
height: Object.prototype.hasOwnProperty.call(this._image, 'naturalHeight') ? this._image.naturalHeight : this._image.height
}]; }];
if (!this.buildPyramid || !$.supportsCanvas || !this.useCanvas) { if (!this.buildPyramid || !$.supportsCanvas || !this.useCanvas) {
@ -214,9 +219,8 @@
return levels; return levels;
} }
/* IE8 fix since it has no naturalWidth and naturalHeight */ var currentWidth = this._image.naturalWidth;
var currentWidth = Object.prototype.hasOwnProperty.call(this._image, 'naturalWidth') ? this._image.naturalWidth : this._image.width; var currentHeight = this._image.naturalHeight;
var currentHeight = Object.prototype.hasOwnProperty.call(this._image, 'naturalHeight') ? this._image.naturalHeight : this._image.height;
var bigCanvas = document.createElement("canvas"); var bigCanvas = document.createElement("canvas");
@ -258,7 +262,19 @@
bigContext = smallContext; bigContext = smallContext;
} }
return levels; return levels;
} },
/**
* Free up canvas memory
* (iOS 12 or higher on 2GB RAM device has only 224MB canvas memory,
* and Safari keeps canvas until its height and width will be set to 0).
* @function
*/
_freeupCanvasMemory: function () {
for (var i = 0; i < this.levels.length; i++) {
this.levels[i].context2D.canvas.height = 0;
this.levels[i].context2D.canvas.width = 0;
}
},
}); });
}(OpenSeadragon)); }(OpenSeadragon));

View File

@ -109,10 +109,10 @@ $.extend( $.LegacyTileSource.prototype, $.TileSource.prototype, /** @lends OpenS
supports: function( data, url ){ supports: function( data, url ){
return ( return (
data.type && data.type &&
"legacy-image-pyramid" == data.type "legacy-image-pyramid" === data.type
) || ( ) || (
data.documentElement && data.documentElement &&
"legacy-image-pyramid" == data.documentElement.getAttribute('type') "legacy-image-pyramid" === data.documentElement.getAttribute('type')
); );
}, },
@ -241,7 +241,7 @@ function configureFromXML( tileSource, xmlDoc ){
level, level,
i; i;
if ( rootName == "image" ) { if ( rootName === "image" ) {
try { try {
conf = { conf = {
@ -267,9 +267,9 @@ function configureFromXML( tileSource, xmlDoc ){
e : e :
new Error( 'Unknown error parsing Legacy Image Pyramid XML.' ); new Error( 'Unknown error parsing Legacy Image Pyramid XML.' );
} }
} else if ( rootName == "collection" ) { } else if ( rootName === "collection" ) {
throw new Error( 'Legacy Image Pyramid Collections not yet supported.' ); throw new Error( 'Legacy Image Pyramid Collections not yet supported.' );
} else if ( rootName == "error" ) { } else if ( rootName === "error" ) {
throw new Error( 'Error: ' + xmlDoc ); throw new Error( 'Error: ' + xmlDoc );
} }

File diff suppressed because it is too large Load Diff

View File

@ -66,15 +66,15 @@ $.Navigator = function( options ){
}; };
if( options.position ){ if( options.position ){
if( 'BOTTOM_RIGHT' == options.position ){ if( 'BOTTOM_RIGHT' === options.position ){
options.controlOptions.anchor = $.ControlAnchor.BOTTOM_RIGHT; options.controlOptions.anchor = $.ControlAnchor.BOTTOM_RIGHT;
} else if( 'BOTTOM_LEFT' == options.position ){ } else if( 'BOTTOM_LEFT' === options.position ){
options.controlOptions.anchor = $.ControlAnchor.BOTTOM_LEFT; options.controlOptions.anchor = $.ControlAnchor.BOTTOM_LEFT;
} else if( 'TOP_RIGHT' == options.position ){ } else if( 'TOP_RIGHT' === options.position ){
options.controlOptions.anchor = $.ControlAnchor.TOP_RIGHT; options.controlOptions.anchor = $.ControlAnchor.TOP_RIGHT;
} else if( 'TOP_LEFT' == options.position ){ } else if( 'TOP_LEFT' === options.position ){
options.controlOptions.anchor = $.ControlAnchor.TOP_LEFT; options.controlOptions.anchor = $.ControlAnchor.TOP_LEFT;
} else if( 'ABSOLUTE' == options.position ){ } else if( 'ABSOLUTE' === options.position ){
options.controlOptions.anchor = $.ControlAnchor.ABSOLUTE; options.controlOptions.anchor = $.ControlAnchor.ABSOLUTE;
options.controlOptions.top = options.top; options.controlOptions.top = options.top;
options.controlOptions.left = options.left; 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); 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 ){ (function( style, borderWidth ){
style.margin = '0px'; style.margin = '0px';
style.border = borderWidth + 'px solid ' + options.borderColor; style.border = borderWidth + 'px solid ' + options.borderColor;
@ -167,20 +167,24 @@ $.Navigator = function( options ){
style.zIndex = 999999999; style.zIndex = 999999999;
style.cursor = 'default'; style.cursor = 'default';
}( this.displayRegion.style, this.borderWidth )); }( this.displayRegion.style, this.borderWidth ));
$.setElementPointerEventsNone( this.displayRegion );
$.setElementTouchActionNone( this.displayRegion );
this.displayRegionContainer = $.makeNeutralElement("div"); this.displayRegionContainer = $.makeNeutralElement("div");
this.displayRegionContainer.id = this.element.id + '-displayregioncontainer'; this.displayRegionContainer.id = this.element.id + '-displayregioncontainer';
this.displayRegionContainer.className = "displayregioncontainer"; this.displayRegionContainer.className = "displayregioncontainer";
this.displayRegionContainer.style.width = "100%"; this.displayRegionContainer.style.width = "100%";
this.displayRegionContainer.style.height = "100%"; this.displayRegionContainer.style.height = "100%";
$.setElementPointerEventsNone( this.displayRegionContainer );
$.setElementTouchActionNone( this.displayRegionContainer );
viewer.addControl( viewer.addControl(
this.element, this.element,
options.controlOptions options.controlOptions
); );
this._resizeWithViewer = options.controlOptions.anchor != $.ControlAnchor.ABSOLUTE && this._resizeWithViewer = options.controlOptions.anchor !== $.ControlAnchor.ABSOLUTE &&
options.controlOptions.anchor != $.ControlAnchor.NONE; options.controlOptions.anchor !== $.ControlAnchor.NONE;
if (options.width && options.height) { if (options.width && options.height) {
this.setWidth(options.width); this.setWidth(options.width);
@ -221,12 +225,29 @@ $.Navigator = function( options ){
// Remove the base class' (Viewer's) innerTracker and replace it with our own // Remove the base class' (Viewer's) innerTracker and replace it with our own
this.innerTracker.destroy(); this.innerTracker.destroy();
this.innerTracker = new $.MouseTracker({ this.innerTracker = new $.MouseTracker({
element: this.element, userData: 'Navigator.innerTracker',
element: this.element, //this.canvas,
dragHandler: $.delegate( this, onCanvasDrag ), dragHandler: $.delegate( this, onCanvasDrag ),
clickHandler: $.delegate( this, onCanvasClick ), clickHandler: $.delegate( this, onCanvasClick ),
releaseHandler: $.delegate( this, onCanvasRelease ), 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() { this.addHandler("reset-size", function() {
if (_this.viewport) { if (_this.viewport) {
@ -282,7 +303,7 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
*/ */
setWidth: function(width) { setWidth: function(width) {
this.width = 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; this._resizeWithViewer = false;
}, },
@ -292,7 +313,7 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
*/ */
setHeight: function(height) { setHeight: function(height) {
this.height = 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; this._resizeWithViewer = false;
}, },
@ -383,6 +404,8 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
var myItem = event.item; var myItem = event.item;
myItem._originalForNavigator = original; myItem._originalForNavigator = original;
_this._matchBounds(myItem, original, true); _this._matchBounds(myItem, original, true);
_this._matchOpacity(myItem, original);
_this._matchCompositeOperation(myItem, original);
function matchBounds() { function matchBounds() {
_this._matchBounds(myItem, original); _this._matchBounds(myItem, original);
@ -406,6 +429,10 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
return $.Viewer.prototype.addTiledImage.apply(this, [optionsClone]); return $.Viewer.prototype.addTiledImage.apply(this, [optionsClone]);
}, },
destroy: function() {
return $.Viewer.prototype.destroy.apply(this);
},
// private // private
_getMatchingItem: function(theirItem) { _getMatchingItem: function(theirItem) {
var count = this.world.getItemCount(); var count = this.world.getItemCount();
@ -427,6 +454,7 @@ $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /*
myItem.setWidth(bounds.width, immediately); myItem.setWidth(bounds.width, immediately);
myItem.setRotation(theirItem.getRotation(), immediately); myItem.setRotation(theirItem.getRotation(), immediately);
myItem.setClip(theirItem.getClip()); myItem.setClip(theirItem.getClip());
myItem.setFlip(theirItem.getFlip());
}, },
// private // private
@ -453,7 +481,7 @@ function onCanvasClick( event ) {
quick: event.quick, quick: event.quick,
shift: event.shift, shift: event.shift,
originalEvent: event.originalEvent, originalEvent: event.originalEvent,
preventDefaultAction: event.preventDefaultAction preventDefaultAction: false
}; };
/** /**
* Raised when a click event occurs on the {@link OpenSeadragon.Viewer#navigator} element. * Raised when a click event occurs on the {@link OpenSeadragon.Viewer#navigator} element.
@ -505,7 +533,7 @@ function onCanvasDrag( event ) {
direction: event.direction, direction: event.direction,
shift: event.shift, shift: event.shift,
originalEvent: event.originalEvent, originalEvent: event.originalEvent,
preventDefaultAction: event.preventDefaultAction preventDefaultAction: false
}; };
/** /**
* Raised when a drag event occurs on the {@link OpenSeadragon.Viewer#navigator} element. * 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 {Boolean} shift - True if the shift key was pressed during this event.
* @property {Object} originalEvent - The original DOM event. * @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object. * @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); this.viewer.raiseEvent('navigator-drag', canvasDragEventArgs);
@ -568,6 +596,15 @@ function onCanvasRelease( event ) {
* @function * @function
*/ */
function onCanvasScroll( event ) { 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.). * 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 {Number} scroll - The scroll delta for the event.
* @property {Boolean} shift - True if the shift key was pressed during this event. * @property {Boolean} shift - True if the shift key was pressed during this event.
* @property {Object} originalEvent - The original DOM 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. * @property {?Object} userData - Arbitrary subscriber-defined object.
*/ */
this.viewer.raiseEvent( 'navigator-scroll', { this.viewer.raiseEvent( 'navigator-scroll', eventArgs );
tracker: event.eventSource,
position: event.position,
scroll: event.scroll,
shift: event.shift,
originalEvent: event.originalEvent
});
//don't scroll the page up and down if the user is scrolling event.preventDefault = eventArgs.preventDefault;
//in the navigator
return false;
} }
/** /**

View File

@ -195,8 +195,9 @@
* *
* @property {String} [compositeOperation=null] * @property {String} [compositeOperation=null]
* Valid values are 'source-over', 'source-atop', 'source-in', 'source-out', * Valid values are 'source-over', 'source-atop', 'source-in', 'source-out',
* 'destination-over', 'destination-atop', 'destination-in', * 'destination-over', 'destination-atop', 'destination-in', 'destination-out',
* 'destination-out', 'lighter', 'copy' or 'xor' * '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] * @property {Boolean} [imageSmoothingEnabled=true]
* Image smoothing for canvas rendering (only if canvas is used). Note: Ignored * Image smoothing for canvas rendering (only if canvas is used). Note: Ignored
@ -320,6 +321,7 @@
* *
* @property {OpenSeadragon.GestureSettings} [gestureSettingsMouse] * @property {OpenSeadragon.GestureSettings} [gestureSettingsMouse]
* Settings for gestures generated by a mouse pointer device. (See {@link OpenSeadragon.GestureSettings}) * 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.scrollToZoom=true] - Zoom on scroll gesture
* @property {Boolean} [gestureSettingsMouse.clickToZoom=true] - Zoom on click 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 * @property {Boolean} [gestureSettingsMouse.dblClickToZoom=false] - Zoom on double-click gesture. Note: If set to true
@ -334,6 +336,7 @@
* *
* @property {OpenSeadragon.GestureSettings} [gestureSettingsTouch] * @property {OpenSeadragon.GestureSettings} [gestureSettingsTouch]
* Settings for gestures generated by a touch pointer device. (See {@link OpenSeadragon.GestureSettings}) * 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.scrollToZoom=false] - Zoom on scroll gesture
* @property {Boolean} [gestureSettingsTouch.clickToZoom=false] - Zoom on click 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 * @property {Boolean} [gestureSettingsTouch.dblClickToZoom=true] - Zoom on double-click gesture. Note: If set to true
@ -348,12 +351,13 @@
* *
* @property {OpenSeadragon.GestureSettings} [gestureSettingsPen] * @property {OpenSeadragon.GestureSettings} [gestureSettingsPen]
* Settings for gestures generated by a pen pointer device. (See {@link OpenSeadragon.GestureSettings}) * 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.scrollToZoom=false] - Zoom on scroll gesture
* @property {Boolean} [gestureSettingsPen.clickToZoom=true] - Zoom on click 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 * @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. * then clickToZoom should be set to false to prevent multiple zooms.
* @property {Boolean} [gestureSettingsPen.pinchToZoom=false] - Zoom on pinch gesture * @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. * the zoom is centered at the canvas center.
* @property {Boolean} [gestureSettingsPen.flickEnabled=false] - Enable flick gesture * @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) * @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] * @property {OpenSeadragon.GestureSettings} [gestureSettingsUnknown]
* Settings for gestures generated by unknown pointer devices. (See {@link OpenSeadragon.GestureSettings}) * 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.scrollToZoom=true] - Zoom on scroll gesture
* @property {Boolean} [gestureSettingsUnknown.clickToZoom=false] - Zoom on click 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 * @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. <em><strong>Note:</strong> Setting this to 1.0 effectively disables the mouse-wheel zoom feature (also see gestureSettings[Mouse|Touch|Pen].scrollToZoom}).</em> * The "zoom distance" per mouse scroll or touch pinch. <em><strong>Note:</strong> Setting this to 1.0 effectively disables the mouse-wheel zoom feature (also see gestureSettings[Mouse|Touch|Pen].scrollToZoom}).</em>
* *
* @property {Number} [zoomPerSecond=1.0] * @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] * @property {Boolean} [showNavigator=false]
* Set to true to make the navigator minimap appear. * Set to true to make the navigator minimap appear.
@ -647,6 +656,9 @@
* @typedef {Object} GestureSettings * @typedef {Object} GestureSettings
* @memberof OpenSeadragon * @memberof OpenSeadragon
* *
* @property {Boolean} dragToPan
* Set to false to disable panning on drag gestures.
*
* @property {Boolean} scrollToZoom * @property {Boolean} scrollToZoom
* Set to false to disable zooming on scroll gestures. * Set to false to disable zooming on scroll gestures.
* *
@ -735,7 +747,7 @@
* *
*/ */
/* eslint-disable no-redeclare */
function OpenSeadragon( options ){ function OpenSeadragon( options ){
return new OpenSeadragon.Viewer( options ); return new OpenSeadragon.Viewer( options );
} }
@ -917,13 +929,65 @@ function OpenSeadragon( options ){
return isTainted; 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, * 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. * clamped to a minimum of 1. Defaults to 1 if canvas isn't supported by the browser.
* @member {Number} pixelDensityRatio * @member {Number} pixelDensityRatio
* @memberof OpenSeadragon * @memberof OpenSeadragon
*/ */
$.pixelDensityRatio = (function () { $.getCurrentPixelDensityRatio = function() {
if ( $.supportsCanvas ) { if ( $.supportsCanvas ) {
var context = document.createElement('canvas').getContext('2d'); var context = document.createElement('canvas').getContext('2d');
var devicePixelRatio = window.devicePixelRatio || 1; var devicePixelRatio = window.devicePixelRatio || 1;
@ -936,13 +1000,19 @@ function OpenSeadragon( options ){
} else { } else {
return 1; return 1;
} }
}()); };
/**
* @member {Number} pixelDensityRatio
* @memberof OpenSeadragon
*/
$.pixelDensityRatio = $.getCurrentPixelDensityRatio();
}( OpenSeadragon )); }( OpenSeadragon ));
/** /**
* This closure defines all static methods available to the 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 * to simplify and reduce common programming patterns. More static methods
* from jQuery may eventually make their way into this though we are * from jQuery may eventually make their way into this though we are
* attempting to avoid an explicit dependency on jQuery only because * attempting to avoid an explicit dependency on jQuery only because
@ -1081,6 +1151,7 @@ function OpenSeadragon( options ){
springStiffness: 6.5, springStiffness: 6.5,
animationTime: 1.2, animationTime: 1.2,
gestureSettingsMouse: { gestureSettingsMouse: {
dragToPan: true,
scrollToZoom: true, scrollToZoom: true,
clickToZoom: true, clickToZoom: true,
dblClickToZoom: false, dblClickToZoom: false,
@ -1092,6 +1163,7 @@ function OpenSeadragon( options ){
pinchRotate: false pinchRotate: false
}, },
gestureSettingsTouch: { gestureSettingsTouch: {
dragToPan: true,
scrollToZoom: false, scrollToZoom: false,
clickToZoom: false, clickToZoom: false,
dblClickToZoom: true, dblClickToZoom: true,
@ -1103,6 +1175,7 @@ function OpenSeadragon( options ){
pinchRotate: false pinchRotate: false
}, },
gestureSettingsPen: { gestureSettingsPen: {
dragToPan: true,
scrollToZoom: false, scrollToZoom: false,
clickToZoom: true, clickToZoom: true,
dblClickToZoom: false, dblClickToZoom: false,
@ -1114,6 +1187,7 @@ function OpenSeadragon( options ){
pinchRotate: false pinchRotate: false
}, },
gestureSettingsUnknown: { gestureSettingsUnknown: {
dragToPan: true,
scrollToZoom: false, scrollToZoom: false,
clickToZoom: false, clickToZoom: false,
dblClickToZoom: true, dblClickToZoom: true,
@ -1315,6 +1389,8 @@ function OpenSeadragon( options ){
* @property {Number} SAFARI * @property {Number} SAFARI
* @property {Number} CHROME * @property {Number} CHROME
* @property {Number} OPERA * @property {Number} OPERA
* @property {Number} EDGE
* @property {Number} CHROMEEDGE
*/ */
BROWSERS: { BROWSERS: {
UNKNOWN: 0, UNKNOWN: 0,
@ -1322,9 +1398,31 @@ function OpenSeadragon( options ){
FIREFOX: 2, FIREFOX: 2,
SAFARI: 3, SAFARI: 3,
CHROME: 4, 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. * 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. * @returns {Element} The element with the given id, null, or the element itself.
*/ */
getElement: function( element ) { getElement: function( element ) {
if ( typeof ( element ) == "string" ) { if ( typeof ( element ) === "string" ) {
element = document.getElementById( element ); element = document.getElementById( element );
} }
return element; return element;
@ -1352,7 +1450,7 @@ function OpenSeadragon( options ){
offsetParent; offsetParent;
element = $.getElement( element ); element = $.getElement( element );
isFixed = $.getElementStyle( element ).position == "fixed"; isFixed = $.getElementStyle( element ).position === "fixed";
offsetParent = getOffsetParent( element, isFixed ); offsetParent = getOffsetParent( element, isFixed );
while ( offsetParent ) { while ( offsetParent ) {
@ -1365,7 +1463,7 @@ function OpenSeadragon( options ){
} }
element = offsetParent; element = offsetParent;
isFixed = $.getElementStyle( element ).position == "fixed"; isFixed = $.getElementStyle( element ).position === "fixed";
offsetParent = getOffsetParent( element, isFixed ); offsetParent = getOffsetParent( element, isFixed );
} }
@ -1397,7 +1495,7 @@ function OpenSeadragon( options ){
boundingRect = element.getBoundingClientRect(); boundingRect = element.getBoundingClientRect();
} }
win = ( doc == doc.window ) ? win = ( doc === doc.window ) ?
doc : doc :
( doc.nodeType === 9 ) ? ( doc.nodeType === 9 ) ?
doc.defaultView || doc.parentWindow : 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. * Gets the position of the mouse on the screen for a given event.
* @function * @function
@ -1548,21 +1623,19 @@ function OpenSeadragon( options ){
*/ */
getMousePosition: function( event ) { getMousePosition: function( event ) {
if ( typeof ( event.pageX ) == "number" ) { if ( typeof ( event.pageX ) === "number" ) {
$.getMousePosition = function( event ){ $.getMousePosition = function( event ){
var result = new $.Point(); var result = new $.Point();
event = $.getEvent( event );
result.x = event.pageX; result.x = event.pageX;
result.y = event.pageY; result.y = event.pageY;
return result; return result;
}; };
} else if ( typeof ( event.clientX ) == "number" ) { } else if ( typeof ( event.clientX ) === "number" ) {
$.getMousePosition = function( event ){ $.getMousePosition = function( event ){
var result = new $.Point(); var result = new $.Point();
event = $.getEvent( event );
result.x = result.x =
event.clientX + event.clientX +
document.body.scrollLeft + document.body.scrollLeft +
@ -1593,7 +1666,7 @@ function OpenSeadragon( options ){
var docElement = document.documentElement || {}, var docElement = document.documentElement || {},
body = document.body || {}; body = document.body || {};
if ( typeof ( window.pageXOffset ) == "number" ) { if ( typeof ( window.pageXOffset ) === "number" ) {
$.getPageScroll = function(){ $.getPageScroll = function(){
return new $.Point( return new $.Point(
window.pageXOffset, 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 || {}, var docElement = document.documentElement || {},
body = document.body || {}; body = document.body || {};
if ( typeof ( window.innerWidth ) == 'number' ) { if ( typeof ( window.innerWidth ) === 'number' ) {
$.getWindowSize = function(){ $.getWindowSize = function(){
return new $.Point( return new $.Point(
window.innerWidth, window.innerWidth,
@ -1798,51 +1871,16 @@ function OpenSeadragon( options ){
/** /**
* Ensures an image is loaded correctly to support alpha transparency. * Ensures an image is loaded correctly to support alpha transparency.
* Generally only IE has issues doing this correctly for formats like
* png.
* @function * @function
* @param {String} src * @param {String} src
* @returns {Element} * @returns {Element}
*/ */
makeTransparentImage: function( src ) { makeTransparentImage: function( src ) {
var img = $.makeNeutralElement( "img" );
$.makeTransparentImage = function( src ){ img.src = src;
var img = $.makeNeutralElement( "img" );
img.src = src; return img;
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 );
}, },
@ -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. * Add the specified CSS class to the element if not present.
* @function * @function
@ -1978,6 +2040,34 @@ function OpenSeadragon( options ){
element.className = newClasses.join(' '); 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. * Adds an event listener for the given element, eventName and handler.
@ -1985,16 +2075,20 @@ function OpenSeadragon( options ){
* @param {Element|String} element * @param {Element|String} element
* @param {String} eventName * @param {String} eventName
* @param {Function} handler * @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 () { addEvent: (function () {
if ( window.addEventListener ) { if ( $.supportsAddEventListener ) {
return function ( element, eventName, handler, useCapture ) { return function ( element, eventName, handler, options ) {
options = $.normalizeEventListenerOptions(options);
element = $.getElement( element ); element = $.getElement( element );
element.addEventListener( eventName, handler, useCapture ); element.addEventListener( eventName, handler, options );
}; };
} else if ( window.attachEvent ) { } else if ( document.documentElement.attachEvent && document.attachEvent ) {
return function ( element, eventName, handler, useCapture ) { return function ( element, eventName, handler ) {
element = $.getElement( element ); element = $.getElement( element );
element.attachEvent( 'on' + eventName, handler ); element.attachEvent( 'on' + eventName, handler );
}; };
@ -2011,16 +2105,18 @@ function OpenSeadragon( options ){
* @param {Element|String} element * @param {Element|String} element
* @param {String} eventName * @param {String} eventName
* @param {Function} handler * @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 () { removeEvent: (function () {
if ( window.removeEventListener ) { if ( $.supportsRemoveEventListener ) {
return function ( element, eventName, handler, useCapture ) { return function ( element, eventName, handler, options ) {
options = $.normalizeEventListenerOptions(options);
element = $.getElement( element ); element = $.getElement( element );
element.removeEventListener( eventName, handler, useCapture ); element.removeEventListener( eventName, handler, options );
}; };
} else if ( window.detachEvent ) { } else if ( document.documentElement.detachEvent && document.detachEvent ) {
return function( element, eventName, handler, useCapture ) { return function( element, eventName, handler ) {
element = $.getElement( element ); element = $.getElement( element );
element.detachEvent( 'on' + eventName, handler ); element.detachEvent( 'on' + eventName, handler );
}; };
@ -2037,49 +2133,28 @@ function OpenSeadragon( options ){
* @param {Event} [event] * @param {Event} [event]
*/ */
cancelEvent: function( event ) { cancelEvent: function( event ) {
event = $.getEvent( event ); event.preventDefault();
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 );
}, },
/** /**
* 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 * @function
* @param {Event} [event] * @param {Event} [event]
*/ */
stopEvent: function( event ) { stopEvent: function( event ) {
event = $.getEvent( event ); event.stopPropagation();
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 );
}, },
@ -2227,7 +2302,7 @@ function OpenSeadragon( options ){
request.onreadystatechange = function() { request.onreadystatechange = function() {
// 4 = DONE (https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#Properties) // 4 = DONE (https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#Properties)
if ( request.readyState == 4 ) { if ( request.readyState === 4 ) {
request.onreadystatechange = function(){}; request.onreadystatechange = function(){};
// With protocols other than http/https, a successful request status is in // With protocols other than http/https, a successful request status is in
@ -2256,7 +2331,7 @@ function OpenSeadragon( options ){
if (headers) { if (headers) {
for (var headerName in 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]); request.setRequestHeader(headerName, headers[headerName]);
} }
} }
@ -2268,62 +2343,12 @@ function OpenSeadragon( options ){
request.send(null); request.send(null);
} catch (e) { } catch (e) {
var msg = e.message; $.console.log( "%s while making AJAX request: %s", e.name, 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 );
request.onreadystatechange = function(){}; request.onreadystatechange = function(){};
if (window.XDomainRequest) { // IE9 or IE8 might as well try to use XDomainRequest if ( $.isFunction( onError ) ) {
var xdr = new XDomainRequest(); onError( request, e );
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 );
}
} }
} }
@ -2353,7 +2378,7 @@ function OpenSeadragon( options ){
callbackParam = options.param || 'callback', callbackParam = options.param || 'callback',
callback = options.callback; callback = options.callback;
url = url.replace( /(\=)\?(&|$)|\?\?/i, replace ); url = url.replace( /(=)\?(&|$)|\?\?/i, replace );
// Add callback manually // Add callback manually
url += (/\?/.test( url ) ? "&" : "?") + callbackParam + "=" + jsonpCallback; url += (/\?/.test( url ) ? "&" : "?") + callbackParam + "=" + jsonpCallback;
@ -2462,16 +2487,7 @@ function OpenSeadragon( options ){
* @returns {Object} * @returns {Object}
*/ */
parseJSON: function(string) { parseJSON: function(string) {
if (window.JSON && window.JSON.parse) { $.parseJSON = 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 + ')');
};
}
return $.parseJSON(string); return $.parseJSON(string);
}, },
@ -2486,11 +2502,61 @@ function OpenSeadragon( options ){
extension = extension ? extension : ""; extension = extension ? extension : "";
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
return !!FILEFORMATS[ extension.toLowerCase() ]; 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
* <pre><code>{
* bmp: false,
* jpeg: true,
* jpg: true,
* png: true,
* tif: false,
* wdp: false
* }
* </code></pre>
* @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. * The current browser vendor, version, and related information regarding detected features.
* @member {Object} Browser * @member {Object} Browser
@ -2509,12 +2575,12 @@ function OpenSeadragon( options ){
var FILEFORMATS = { var FILEFORMATS = {
"bmp": false, bmp: false,
"jpeg": true, jpeg: true,
"jpg": true, jpg: true,
"png": true, png: true,
"tif": false, tif: false,
"wdp": false wdp: false
}, },
URLPARAMS = {}; URLPARAMS = {};
@ -2544,7 +2610,17 @@ function OpenSeadragon( options ){
break; break;
case "Netscape": case "Netscape":
if (window.addEventListener) { 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.vendor = $.BROWSERS.FIREFOX;
$.Browser.version = parseFloat( $.Browser.version = parseFloat(
ua.substring( ua.indexOf( "Firefox" ) + 8 ) ua.substring( ua.indexOf( "Firefox" ) + 8 )
@ -2586,56 +2662,30 @@ function OpenSeadragon( options ){
sep = part.indexOf( '=' ); sep = part.indexOf( '=' );
if ( sep > 0 ) { if ( sep > 0 ) {
URLPARAMS[ part.substring( 0, sep ) ] = var key = part.substring( 0, sep ),
decodeURIComponent( part.substring( sep + 1 ) ); 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 //determine if this browser supports image alpha transparency
$.Browser.alpha = !( $.Browser.alpha = !(
( $.Browser.vendor === $.BROWSERS.CHROME && $.Browser.version < 2
$.Browser.vendor == $.BROWSERS.IE &&
$.Browser.version < 9
) || (
$.Browser.vendor == $.BROWSERS.CHROME &&
$.Browser.version < 2
)
); );
//determine if this browser supports element.style.opacity //determine if this browser supports element.style.opacity
$.Browser.opacity = !( $.Browser.opacity = true;
$.Browser.vendor == $.BROWSERS.IE &&
$.Browser.version < 9
);
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. // Adding support for HTML5's requestAnimationFrame as suggested by acdha.
// Implementation taken from matt synder's post here: // Implementation taken from matt synder's post here:
// http://mattsnider.com/cross-browser-and-legacy-supported-requestframeanimation/ // http://mattsnider.com/cross-browser-and-legacy-supported-requestframeanimation/
@ -2731,7 +2781,7 @@ function OpenSeadragon( options ){
* @returns {Element} * @returns {Element}
*/ */
function getOffsetParent( element, isFixed ) { function getOffsetParent( element, isFixed ) {
if ( isFixed && element != document.body ) { if ( isFixed && element !== document.body ) {
return document.body; return document.body;
} else { } else {
return element.offsetParent; return element.offsetParent;

View File

@ -113,7 +113,7 @@ $.extend( $.OsmTileSource.prototype, $.TileSource.prototype, /** @lends OpenSead
supports: function( data, url ){ supports: function( data, url ){
return ( return (
data.type && data.type &&
"openstreetmaps" == data.type "openstreetmaps" === data.type
); );
}, },

View File

@ -288,10 +288,7 @@
style[transformProp] = ""; style[transformProp] = "";
} }
} }
style.display = 'block';
if (style.display !== 'none') {
style.display = 'block';
}
} }
}, },

View File

@ -50,13 +50,13 @@ $.Point = function( x, y ) {
* @member {Number} x * @member {Number} x
* @memberof OpenSeadragon.Point# * @memberof OpenSeadragon.Point#
*/ */
this.x = typeof ( x ) == "number" ? x : 0; this.x = typeof ( x ) === "number" ? x : 0;
/** /**
* The vector component 'y'. * The vector component 'y'.
* @member {Number} y * @member {Number} y
* @memberof OpenSeadragon.Point# * @memberof OpenSeadragon.Point#
*/ */
this.y = typeof ( y ) == "number" ? y : 0; this.y = typeof ( y ) === "number" ? y : 0;
}; };
/** @lends OpenSeadragon.Point.prototype */ /** @lends OpenSeadragon.Point.prototype */

View File

@ -85,24 +85,19 @@ $.ReferenceStrip = function ( options ) {
scroll: $.DEFAULT_SETTINGS.referenceStripScroll, scroll: $.DEFAULT_SETTINGS.referenceStripScroll,
clickTimeThreshold: $.DEFAULT_SETTINGS.clickTimeThreshold clickTimeThreshold: $.DEFAULT_SETTINGS.clickTimeThreshold
}, options, { }, options, {
//required overrides element: this.element
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
} ); } );
$.extend( this, options ); $.extend( this, options );
//Private state properties //Private state properties
THIS[this.id] = { THIS[this.id] = {
"animating": false animating: false
}; };
this.minPixelRatio = this.viewer.minPixelRatio; this.minPixelRatio = this.viewer.minPixelRatio;
this.element.tabIndex = 0;
style = this.element.style; style = this.element.style;
style.marginTop = '0px'; style.marginTop = '0px';
style.marginRight = '0px'; style.marginRight = '0px';
@ -119,14 +114,21 @@ $.ReferenceStrip = function ( options ) {
$.setElementOpacity( this.element, 0.8 ); $.setElementOpacity( this.element, 0.8 );
this.viewer = viewer; this.viewer = viewer;
this.innerTracker = new $.MouseTracker( { this.tracker = new $.MouseTracker( {
userData: 'ReferenceStrip.tracker',
element: this.element, element: this.element,
clickHandler: $.delegate( this, onStripClick ),
dragHandler: $.delegate( this, onStripDrag ), dragHandler: $.delegate( this, onStripDrag ),
scrollHandler: $.delegate( this, onStripScroll ), scrollHandler: $.delegate( this, onStripScroll ),
enterHandler: $.delegate( this, onStripEnter ), enterHandler: $.delegate( this, onStripEnter ),
exitHandler: $.delegate( this, onStripExit ), leaveHandler: $.delegate( this, onStripLeave ),
keyDownHandler: $.delegate( this, onKeyDown ), 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 //Controls the position and orientation of the reference strip and sets the
@ -139,7 +141,7 @@ $.ReferenceStrip = function ( options ) {
{ anchor: $.ControlAnchor.BOTTOM_LEFT } { anchor: $.ControlAnchor.BOTTOM_LEFT }
); );
} else { } else {
if ( "horizontal" == options.scroll ) { if ( "horizontal" === options.scroll ) {
this.element.style.width = ( this.element.style.width = (
viewerSize.x * viewerSize.x *
options.sizeRatio * options.sizeRatio *
@ -189,34 +191,12 @@ $.ReferenceStrip = function ( options ) {
element.style.width = _this.panelWidth + 'px'; element.style.width = _this.panelWidth + 'px';
element.style.height = _this.panelHeight + 'px'; element.style.height = _this.panelHeight + 'px';
element.style.display = 'inline'; element.style.display = 'inline';
element.style.float = 'left'; //Webkit element.style['float'] = 'left'; //Webkit
element.style.cssFloat = 'left'; //Firefox element.style.cssFloat = 'left'; //Firefox
element.style.styleFloat = 'left'; //IE element.style.styleFloat = 'left'; //IE
element.style.padding = '2px'; element.style.padding = '2px';
$.setElementTouchActionNone( element ); $.setElementTouchActionNone( element );
$.setElementPointerEventsNone( 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 );
}
}
} );
this.element.appendChild( element ); this.element.appendChild( element );
@ -225,12 +205,13 @@ $.ReferenceStrip = function ( options ) {
this.panels.push( element ); 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 ); this.setFocus( 0 );
}; };
$.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototype, /** @lends OpenSeadragon.ReferenceStrip.prototype */{ /** @lends OpenSeadragon.ReferenceStrip.prototype */
$.ReferenceStrip.prototype = {
/** /**
* @function * @function
@ -251,7 +232,7 @@ $.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototyp
this.currentSelected = element; this.currentSelected = element;
this.currentSelected.style.background = '#999'; this.currentSelected.style.background = '#999';
if ( 'horizontal' == this.scroll ) { if ( 'horizontal' === this.scroll ) {
//right left //right left
offset = ( Number( page ) ) * ( this.panelWidth + 3 ); offset = ( Number( page ) ) * ( this.panelWidth + 3 );
if ( offset > offsetLeft + viewerSize.x - this.panelWidth ) { if ( offset > offsetLeft + viewerSize.x - this.panelWidth ) {
@ -277,7 +258,7 @@ $.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototyp
} }
this.currentPage = page; 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; return false;
}, },
// Overrides Viewer.destroy
destroy: function() { destroy: function() {
if (this.miniViewers) { if (this.miniViewers) {
for (var key in 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) { 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 ) { 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', '' ) ), offsetTop = Number( this.element.style.marginTop.replace( 'px', '' ) ),
scrollWidth = Number( this.element.style.width.replace( 'px', '' ) ), scrollWidth = Number( this.element.style.width.replace( 'px', '' ) ),
scrollHeight = Number( this.element.style.height.replace( 'px', '' ) ), scrollHeight = Number( this.element.style.height.replace( 'px', '' ) ),
viewerSize = $.getElementSize( this.viewer.canvas ); viewerSize = $.getElementSize( this.viewer.canvas );
this.dragging = true;
if ( this.element ) { if ( 'horizontal' === this.scroll ) {
if ( 'horizontal' == this.scroll ) {
if ( -event.delta.x > 0 ) { if ( -event.delta.x > 0 ) {
//forward //forward
if ( offsetLeft > -( scrollWidth - viewerSize.x ) ) { if ( offsetLeft > -( scrollWidth - viewerSize.x ) ) {
@ -354,7 +357,6 @@ function onStripDrag( event ) {
} }
} }
} }
return false;
} }
@ -366,13 +368,14 @@ function onStripDrag( event ) {
* @function * @function
*/ */
function onStripScroll( event ) { 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', '' ) ), offsetTop = Number( this.element.style.marginTop.replace( 'px', '' ) ),
scrollWidth = Number( this.element.style.width.replace( 'px', '' ) ), scrollWidth = Number( this.element.style.width.replace( 'px', '' ) ),
scrollHeight = Number( this.element.style.height.replace( 'px', '' ) ), scrollHeight = Number( this.element.style.height.replace( 'px', '' ) ),
viewerSize = $.getElementSize( this.viewer.canvas ); viewerSize = $.getElementSize( this.viewer.canvas );
if ( this.element ) {
if ( 'horizontal' == this.scroll ) { if ( 'horizontal' === this.scroll ) {
if ( event.scroll > 0 ) { if ( event.scroll > 0 ) {
//forward //forward
if ( offsetLeft > -( scrollWidth - viewerSize.x ) ) { 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, activePanelsStart,
activePanelsEnd, activePanelsEnd,
miniViewer, miniViewer,
style,
i, i,
element; element;
if ( 'horizontal' == strip.scroll ) { if ( 'horizontal' === strip.scroll ) {
panelSize = strip.panelWidth; panelSize = strip.panelWidth;
} else { } else {
panelSize = strip.panelHeight; panelSize = strip.panelHeight;
@ -451,36 +453,17 @@ function loadPanels( strip, viewerSize, scroll ) {
blendTime: 0, blendTime: 0,
animationTime: 0, animationTime: 0,
loadTilesWithAjax: strip.viewer.loadTilesWithAjax, loadTilesWithAjax: strip.viewer.loadTilesWithAjax,
ajaxHeaders: strip.viewer.ajaxHeaders ajaxHeaders: strip.viewer.ajaxHeaders,
useCanvas: strip.useCanvas
} ); } );
// Allow pointer events to pass through miniViewer's canvas/container
miniViewer.displayRegion = $.makeNeutralElement( "div" ); // elements so implicit pointer capture works on touch devices
miniViewer.displayRegion.id = element.id + '-displayregion'; $.setElementPointerEventsNone( miniViewer.canvas );
miniViewer.displayRegion.className = 'displayregion'; $.setElementPointerEventsNone( miniViewer.container );
// We'll use event delegation from the reference strip element instead of
style = miniViewer.displayRegion.style; // handling events on every miniViewer
style.position = 'relative'; miniViewer.innerTracker.setTracking( false );
style.top = '0px'; miniViewer.outerTracker.setTracking( false );
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
);
strip.miniViewers[element.id] = miniViewer; strip.miniViewers[element.id] = miniViewer;
@ -503,7 +486,7 @@ function onStripEnter( event ) {
//element.style.border = '1px solid #555'; //element.style.border = '1px solid #555';
//element.style.background = '#000'; //element.style.background = '#000';
if ( 'horizontal' == this.scroll ) { if ( 'horizontal' === this.scroll ) {
//element.style.paddingTop = "0px"; //element.style.paddingTop = "0px";
element.style.marginBottom = "0px"; element.style.marginBottom = "0px";
@ -514,7 +497,6 @@ function onStripEnter( event ) {
element.style.marginLeft = "0px"; element.style.marginLeft = "0px";
} }
return false;
} }
@ -523,10 +505,10 @@ function onStripEnter( event ) {
* @inner * @inner
* @function * @function
*/ */
function onStripExit( event ) { function onStripLeave( event ) {
var element = event.eventSource.element; var element = event.eventSource.element;
if ( 'horizontal' == this.scroll ) { if ( 'horizontal' === this.scroll ) {
//element.style.paddingTop = "10px"; //element.style.paddingTop = "10px";
element.style.marginBottom = "-" + ( $.getElementSize( element ).y / 2 ) + "px"; element.style.marginBottom = "-" + ( $.getElementSize( element ).y / 2 ) + "px";
@ -537,7 +519,6 @@ function onStripExit( event ) {
element.style.marginLeft = "-" + ( $.getElementSize( element ).x / 2 ) + "px"; element.style.marginLeft = "-" + ( $.getElementSize( element ).x / 2 ) + "px";
} }
return false;
} }
@ -549,26 +530,31 @@ function onStripExit( event ) {
function onKeyDown( event ) { function onKeyDown( event ) {
//console.log( event.keyCode ); //console.log( event.keyCode );
if ( !event.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) { if ( !event.ctrl && !event.alt && !event.meta ) {
switch ( event.keyCode ) { switch ( event.keyCode ) {
case 38: //up arrow case 38: //up arrow
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } ); onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
return false; event.preventDefault = true;
break;
case 40: //down arrow case 40: //down arrow
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } ); onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
return false; event.preventDefault = true;
break;
case 37: //left arrow case 37: //left arrow
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } ); onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
return false; event.preventDefault = true;
break;
case 39: //right arrow case 39: //right arrow
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } ); onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
return false; event.preventDefault = true;
break;
default: default:
//console.log( 'navigator keycode %s', event.keyCode ); //console.log( 'navigator keycode %s', event.keyCode );
return true; event.preventDefault = false;
break;
} }
} else { } else {
return true; event.preventDefault = false;
} }
} }
@ -581,35 +567,42 @@ function onKeyDown( event ) {
function onKeyPress( event ) { function onKeyPress( event ) {
//console.log( event.keyCode ); //console.log( event.keyCode );
if ( !event.preventDefaultAction && !event.ctrl && !event.alt && !event.meta ) { if ( !event.ctrl && !event.alt && !event.meta ) {
switch ( event.keyCode ) { switch ( event.keyCode ) {
case 61: //=|+ case 61: //=|+
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } ); onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
return false; event.preventDefault = true;
break;
case 45: //-|_ case 45: //-|_
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } ); onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
return false; event.preventDefault = true;
break;
case 48: //0|) case 48: //0|)
case 119: //w case 119: //w
case 87: //W case 87: //W
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } ); onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
return false; event.preventDefault = true;
break;
case 115: //s case 115: //s
case 83: //S case 83: //S
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } ); onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
return false; event.preventDefault = true;
break;
case 97: //a case 97: //a
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } ); onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
return false; event.preventDefault = true;
break;
case 100: //d case 100: //d
onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } ); onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
return false; event.preventDefault = true;
break;
default: default:
//console.log( 'navigator keycode %s', event.keyCode ); //console.log( 'navigator keycode %s', event.keyCode );
return true; event.preventDefault = false;
break;
} }
} else { } else {
return true; event.preventDefault = false;
} }
} }

View File

@ -50,11 +50,11 @@
$.Spring = function( options ) { $.Spring = function( options ) {
var args = arguments; var args = arguments;
if( typeof ( options ) != 'object' ){ if( typeof ( options ) !== 'object' ){
//allows backward compatible use of ( initialValue, config ) as //allows backward compatible use of ( initialValue, config ) as
//constructor parameters //constructor parameters
options = { options = {
initial: args.length && typeof ( args[ 0 ] ) == "number" ? initial: args.length && typeof ( args[ 0 ] ) === "number" ?
args[ 0 ] : args[ 0 ] :
undefined, undefined,
/** /**
@ -96,7 +96,7 @@ $.Spring = function( options ) {
* @property {Number} time * @property {Number} time
*/ */
this.current = { this.current = {
value: typeof ( this.initial ) == "number" ? value: typeof ( this.initial ) === "number" ?
this.initial : this.initial :
(this._exponential ? 0 : 1), (this._exponential ? 0 : 1),
time: $.now() // always work in milliseconds time: $.now() // always work in milliseconds
@ -237,7 +237,7 @@ $.Spring.prototype = {
this.current.value = currentValue; this.current.value = currentValue;
} }
return oldValue != this.current.value; return oldValue !== this.current.value;
}, },
/** /**

View File

@ -82,7 +82,7 @@ $.extend( $, /** @lends OpenSeadragon */{
} }
string = container[ props[ i ] ]; string = container[ props[ i ] ];
if ( typeof ( string ) != "string" ) { if ( typeof ( string ) !== "string" ) {
$.console.log( "Untranslated source string:", prop ); $.console.log( "Untranslated source string:", prop );
string = ""; // FIXME: this breaks gettext()-style convention, which would return source string = ""; // FIXME: this breaks gettext()-style convention, which would return source
} }

View File

@ -176,6 +176,12 @@ $.Tile = function(level, x, y, bounds, exists, url, context2D, loadWithAjax, aja
* @memberof OpenSeadragon.Tile# * @memberof OpenSeadragon.Tile#
*/ */
this.size = null; 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. * The start time of this tile's blending.
* @member {Number} blendStart * @member {Number} blendStart
@ -284,10 +290,10 @@ $.Tile.prototype = {
this.style = this.element.style; this.style = this.element.style;
this.style.position = "absolute"; this.style.position = "absolute";
} }
if ( this.element.parentNode != container ) { if ( this.element.parentNode !== container ) {
container.appendChild( this.element ); container.appendChild( this.element );
} }
if ( this.imgElement.parentNode != this.element ) { if ( this.imgElement.parentNode !== this.element ) {
this.element.appendChild( this.imgElement ); this.element.appendChild( this.imgElement );
} }
@ -296,6 +302,10 @@ $.Tile.prototype = {
this.style.height = this.size.y + "px"; this.style.height = this.size.y + "px";
this.style.width = this.size.x + "px"; this.style.width = this.size.x + "px";
if (this.flipped) {
this.style.transform = "scaleX(-1)";
}
$.setElementOpacity( this.element, this.opacity ); $.setElementOpacity( this.element, this.opacity );
}, },
@ -383,13 +393,17 @@ $.Tile.prototype = {
sourceHeight = rendered.canvas.height; sourceHeight = rendered.canvas.height;
} }
context.translate(position.x + size.x / 2, 0);
if (this.flipped) {
context.scale(-1, 1);
}
context.drawImage( context.drawImage(
rendered.canvas, rendered.canvas,
0, 0,
0, 0,
sourceWidth, sourceWidth,
sourceHeight, sourceHeight,
position.x, -size.x / 2,
position.y, position.y,
size.x, size.x,
size.y size.y

View File

@ -196,7 +196,7 @@ $.TileCache.prototype = {
worstLevel = worstTile.level; worstLevel = worstTile.level;
if ( prevTime < worstTime || if ( prevTime < worstTime ||
( prevTime == worstTime && prevLevel > worstLevel ) ) { ( prevTime === worstTime && prevLevel > worstLevel ) ) {
worstTile = prevTile; worstTile = prevTile;
worstTileIndex = i; worstTileIndex = i;
worstTileRecord = prevTileRecord; worstTileRecord = prevTileRecord;

View File

@ -326,6 +326,10 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
*/ */
destroy: function() { destroy: function() {
this.reset(); 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)); 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. * @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); 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 // private
_viewportToImageDelta: function( viewerX, viewerY, current ) { _viewportToImageDelta: function( viewerX, viewerY, current ) {
var scale = (current ? this._scaleSpring.current.value : this._scaleSpring.target.value); 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); 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. * Positions and scales the TiledImage to fit in the specified bounds.
* Note: this method fires OpenSeadragon.TiledImage.event:bounds-change * Note: this method fires OpenSeadragon.TiledImage.event:bounds-change
@ -776,6 +861,23 @@ $.extend($.TiledImage.prototype, $.EventSource.prototype, /** @lends OpenSeadrag
this.raiseEvent('clip-change'); 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. * @returns {Number} The TiledImage's current opacity.
*/ */
@ -1199,24 +1301,41 @@ function updateLevel(tiledImage, haveDrawn, drawLevel, level, levelOpacity,
var viewportCenter = tiledImage.viewport.pixelFromPoint( var viewportCenter = tiledImage.viewport.pixelFromPoint(
tiledImage.viewport.getCenter()); 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 x = topLeftTile.x; x <= bottomRightTile.x; x++) {
for (var y = topLeftTile.y; y <= bottomRightTile.y; y++) { for (var y = topLeftTile.y; y <= bottomRightTile.y; y++) {
// Optimisation disabled with wrapping because getTileBounds does not var flippedX;
// work correctly with x and y outside of the number of tiles if (tiledImage.getFlip()) {
if (!tiledImage.wrapHorizontal && !tiledImage.wrapVertical) { var xMod = ( numberOfTiles.x + ( x % numberOfTiles.x ) ) % numberOfTiles.x;
var tileBounds = tiledImage.source.getTileBounds(level, x, y); flippedX = x + numberOfTiles.x - xMod - xMod - 1;
if (drawArea.intersection(tileBounds) === null) { } else {
// This tile is outside of the viewport, no need to draw it flippedX = x;
continue; }
}
if (drawArea.intersection(tiledImage.getTileBounds(level, flippedX, y)) === null) {
// This tile is outside of the viewport, no need to draw it
continue;
} }
best = updateTile( best = updateTile(
tiledImage, tiledImage,
drawLevel, drawLevel,
haveDrawn, haveDrawn,
x, y, flippedX, y,
level, level,
levelOpacity, levelOpacity,
levelVisibility, levelVisibility,
@ -1391,10 +1510,10 @@ function getTile(
tilesMatrix[ level ][ x ] = {}; 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; xMod = ( numTiles.x + ( x % numTiles.x ) ) % numTiles.x;
yMod = ( numTiles.y + ( y % numTiles.y ) ) % numTiles.y; 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 ); sourceBounds = tileSource.getTileBounds( level, xMod, yMod, true );
exists = tileSource.tileExists( level, xMod, yMod ); exists = tileSource.tileExists( level, xMod, yMod );
url = tileSource.getTileUrl( level, xMod, yMod ); url = tileSource.getTileUrl( level, xMod, yMod );
@ -1413,9 +1532,6 @@ function getTile(
context2D = tileSource.getContext2D ? context2D = tileSource.getContext2D ?
tileSource.getContext2D(level, xMod, yMod) : undefined; tileSource.getContext2D(level, xMod, yMod) : undefined;
bounds.x += ( x - xMod ) / numTiles.x;
bounds.y += (worldHeight / worldWidth) * (( y - yMod ) / numTiles.y);
tile = new $.Tile( tile = new $.Tile(
level, level,
x, x,
@ -1429,14 +1545,22 @@ function getTile(
sourceBounds sourceBounds
); );
if (xMod === numTiles.x - 1) { if (tiledImage.getFlip()) {
tile.isRightMost = true; if (xMod === 0) {
tile.isRightMost = true;
}
} else {
if (xMod === numTiles.x - 1) {
tile.isRightMost = true;
}
} }
if (yMod === numTiles.y - 1) { if (yMod === numTiles.y - 1) {
tile.isBottomMost = true; tile.isBottomMost = true;
} }
tile.flipped = tiledImage.flipped;
tilesMatrix[ level ][ x ][ y ] = tile; 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 {Image} image - The image of the tile.
* @property {OpenSeadragon.TiledImage} tiledImage - The tiled image of the loaded tile. * @property {OpenSeadragon.TiledImage} tiledImage - The tiled image of the loaded tile.
* @property {OpenSeadragon.Tile} tile - The tile which has been loaded. * @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 * @property {function} getCompletionCallback - A function giving a callback to call
* when the asynchronous processing of the image is done. The image will be * 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 * 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 ) { if ( x === undefined || y === undefined ) {
rows = coverage[ level ]; rows = coverage[ level ];
for ( i in rows ) { for ( i in rows ) {
if ( rows.hasOwnProperty( i ) ) { if ( Object.prototype.hasOwnProperty.call( rows, i ) ) {
cols = rows[ i ]; cols = rows[ i ];
for ( j in cols ) { for ( j in cols ) {
if ( cols.hasOwnProperty( j ) && !cols[ j ] ) { if ( Object.prototype.hasOwnProperty.call( cols, j ) && !cols[ j ] ) {
return false; return false;
} }
} }
@ -1818,7 +1942,7 @@ function compareTiles( previousBest, tile ) {
if ( tile.visibility > previousBest.visibility ) { if ( tile.visibility > previousBest.visibility ) {
return tile; return tile;
} else if ( tile.visibility == previousBest.visibility ) { } else if ( tile.visibility === previousBest.visibility ) {
if ( tile.squaredDistance < previousBest.squaredDistance ) { if ( tile.squaredDistance < previousBest.squaredDistance ) {
return tile; return tile;
} }
@ -1877,14 +2001,15 @@ function drawTiles( tiledImage, lastDrawn ) {
// sketch canvas we are going to use for performance reasons. // sketch canvas we are going to use for performance reasons.
bounds = tiledImage.viewport.viewportToViewerElementRectangle( bounds = tiledImage.viewport.viewportToViewerElementRectangle(
tiledImage.getClippedBounds(true)) tiledImage.getClippedBounds(true))
.getIntegerBoundingBox() .getIntegerBoundingBox();
.times($.pixelDensityRatio);
if(tiledImage._drawer.viewer.viewport.getFlip()) { if(tiledImage._drawer.viewer.viewport.getFlip()) {
if (tiledImage.viewport.degrees !== 0 || tiledImage.getRotation(true) % 360 !== 0){ if (tiledImage.viewport.degrees !== 0 || tiledImage.getRotation(true) % 360 !== 0){
bounds.x = tiledImage._drawer.viewer.container.clientWidth - (bounds.x + bounds.width); bounds.x = tiledImage._drawer.viewer.container.clientWidth - (bounds.x + bounds.width);
} }
} }
bounds = bounds.times($.pixelDensityRatio);
} }
tiledImage._drawer._clear(true, bounds); tiledImage._drawer._clear(true, bounds);
} }
@ -1932,6 +2057,28 @@ function drawTiles( tiledImage, lastDrawn ) {
usedClip = true; 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 ) { if ( tiledImage.placeholderFillStyle && tiledImage._hasOpaqueTile === false ) {
var placeholderRect = tiledImage._drawer.viewportToDrawerRectangle(tiledImage.getBounds(true)); var placeholderRect = tiledImage._drawer.viewportToDrawerRectangle(tiledImage.getBounds(true));
if (sketchScale) { if (sketchScale) {

View File

@ -167,7 +167,7 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
* @memberof OpenSeadragon.TileSource# * @memberof OpenSeadragon.TileSource#
*/ */
if( 'string' == $.type( arguments[ 0 ] ) ){ if( 'string' === $.type( arguments[ 0 ] ) ){
this.url = arguments[0]; this.url = arguments[0];
} }
@ -313,8 +313,8 @@ $.TileSource.prototype = {
*/ */
getPixelRatio: function( level ) { getPixelRatio: function( level ) {
var imageSizeScaled = this.dimensions.times( this.getLevelScale( level ) ), var imageSizeScaled = this.dimensions.times( this.getLevelScale( level ) ),
rx = 1.0 / imageSizeScaled.x, rx = 1.0 / imageSizeScaled.x * $.pixelDensityRatio,
ry = 1.0 / imageSizeScaled.y; ry = 1.0 / imageSizeScaled.y * $.pixelDensityRatio;
return new $.Point(rx, ry); return new $.Point(rx, ry);
}, },
@ -500,7 +500,7 @@ $.TileSource.prototype = {
msg = "HTTP " + xhr.status + " attempting to load TileSource"; msg = "HTTP " + xhr.status + " attempting to load TileSource";
} catch ( e ) { } catch ( e ) {
var formattedExc; var formattedExc;
if ( typeof ( exc ) == "undefined" || !exc.toString ) { if ( typeof ( exc ) === "undefined" || !exc.toString ) {
formattedExc = "Unknown error"; formattedExc = "Unknown error";
} else { } else {
formattedExc = exc.toString(); formattedExc = exc.toString();
@ -640,7 +640,7 @@ function processResponse( xhr ){
throw new Error( $.getString( "Errors.Security" ) ); throw new Error( $.getString( "Errors.Security" ) );
} else if ( xhr.status !== 200 && xhr.status !== 0 ) { } else if ( xhr.status !== 200 && xhr.status !== 0 ) {
status = xhr.status; status = xhr.status;
statusText = ( status == 404 ) ? statusText = ( status === 404 ) ?
"Not Found" : "Not Found" :
xhr.statusText; xhr.statusText;
throw new Error( $.getString( "Errors.Status", status, statusText ) ); throw new Error( $.getString( "Errors.Status", status, statusText ) );
@ -654,7 +654,7 @@ function processResponse( xhr ){
} catch (e){ } catch (e){
data = xhr.responseText; data = xhr.responseText;
} }
}else if( responseText.match(/\s*[\{\[].*/) ){ }else if( responseText.match(/\s*[{[].*/) ){
try{ try{
data = $.parseJSON(responseText); data = $.parseJSON(responseText);
} catch(e){ } catch(e){
@ -690,6 +690,8 @@ $.TileSource.determineType = function( tileSource, data, url ){
} }
$.console.error( "No TileSource was able to open %s %s", url, data ); $.console.error( "No TileSource was able to open %s %s", url, data );
return null;
}; };

View File

@ -103,7 +103,7 @@ $.extend( $.TmsTileSource.prototype, $.TileSource.prototype, /** @lends OpenSead
* @param {String} optional - url * @param {String} optional - url
*/ */
supports: function( data, url ){ supports: function( data, url ){
return ( data.type && "tiledmapservice" == data.type ); return ( data.type && "tiledmapservice" === data.type );
}, },
/** /**

File diff suppressed because it is too large Load Diff

View File

@ -898,7 +898,7 @@ $.Viewport.prototype = {
* @property {Number} degrees - The number of degrees the rotation was set to. * @property {Number} degrees - The number of degrees the rotation was set to.
* @property {?Object} userData - Arbitrary subscriber-defined object. * @property {?Object} userData - Arbitrary subscriber-defined object.
*/ */
this.viewer.raiseEvent('rotate', {"degrees": degrees}); this.viewer.raiseEvent('rotate', {degrees: degrees});
return this; return this;
}, },
@ -1567,7 +1567,7 @@ $.Viewport.prototype = {
* @property {Number} flipped - The flip state after this change. * @property {Number} flipped - The flip state after this change.
* @property {?Object} userData - Arbitrary subscriber-defined object. * @property {?Object} userData - Arbitrary subscriber-defined object.
*/ */
this.viewer.raiseEvent('flip', {"flipped": state}); this.viewer.raiseEvent('flip', {flipped: state});
return this; return this;
} }

View File

@ -18,7 +18,7 @@
* tilesUrl: "/test/data/zoomify/" * 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) * 2) Loading image metadata from xml file: (CURRENTLY NOT SUPPORTED)
* *
@ -44,7 +44,14 @@
* @param {String} tilesUrl * @param {String} tilesUrl
*/ */
$.ZoomifyTileSource = function(options) { $.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 = { var currentImageSize = {
x: options.width, x: options.width,
@ -107,7 +114,7 @@
* @param {String} optional - url * @param {String} optional - url
*/ */
supports: function(data, 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 result = 0;
var num = this._calculateAbsoluteTileNumber(level, x, y); var num = this._calculateAbsoluteTileNumber(level, x, y);
result = Math.floor(num / 256); result = Math.floor(num / 256);
return this.tilesUrl + 'TileGroup' + result + '/' + level + '-' + x + '-' + y + '.jpg'; return this.tilesUrl + 'TileGroup' + result + '/' + level + '-' + x + '-' + y + '.' + this.fileFormat;
} }
}); });

View File

@ -87,6 +87,7 @@
<script src="/test/modules/rectangle.js"></script> <script src="/test/modules/rectangle.js"></script>
<script src="/test/modules/ajax-tiles.js"></script> <script src="/test/modules/ajax-tiles.js"></script>
<script src="/test/modules/imageloader.js"></script> <script src="/test/modules/imageloader.js"></script>
<script src="/test/modules/iiif.js"></script>
<!-- The navigator tests are the slowest (for now; hopefully they can be sped up) <!-- The navigator tests are the slowest (for now; hopefully they can be sped up)
so we put them last. --> so we put them last. -->
<script src="/test/modules/navigator.js"></script> <script src="/test/modules/navigator.js"></script>

View File

@ -0,0 +1,165 @@
<!DOCTYPE html>
<html>
<head>
<title>OpenSeadragon Cropping PolygonList Demo</title>
<script type="text/javascript" src='../../build/openseadragon/openseadragon.js'></script>
<script type="text/javascript" src='../lib/jquery-1.9.1.min.js'></script>
<style type="text/css">
.openseadragon1 {
width: 800px;
height: 600px;
background: lightgreen;
}
textarea {
width: 215px;
height: 200px;
}
.box-with-title {
padding-top: 1em;
display: inline-block;
text-align: center;
}
.buttons {
width: 215px;
}
*:focus {
outline: none;
}
</style>
</head>
<body>
<h3>
Simple demo page to show cropping with polygonList in a OpenSeadragon viewer.
</h3>
<div id="contentDiv" class="openseadragon1"></div>
<span>Click on Viewer to save polygon points</span>
<div>
<button id='resetBtn'>Reset</button>
<button id='exampleBtn'>Load Example</button>
</div>
<div class='box-with-title'>
<div class="buttons">
<button id="addPointBtn">Add Points as Polygon</button>
<button onclick="emptyElement('polygonPointEl')">Clear</button>
</div>
<textarea id="polygonPointEl"></textarea>
</div>
<div class='box-with-title'>
<div class="buttons">
<button id="cropBtn">Crop With Polygon</button>
<button onclick="emptyElement('previewEl')">Clear</button>
</div>
<textarea id='previewEl'></textarea>
</div>
<!-- Setup Viewer -->
<script type="text/javascript">
var viewer = OpenSeadragon({
// debugMode: true,
id: "contentDiv",
prefixUrl: "../../build/openseadragon/images/",
tileSources: "../data/testpattern.dzi",
showNavigator: false,
gestureSettingsMouse: {
clickToZoom: false
}
});
</script>
<script>
// Global Variables
var previewEl = document.getElementById('previewEl');
var polygonPointEl = document.getElementById('polygonPointEl');
var examples = [
[{x: 480, y: 300},{x: 300, y: 420},{x: 600, y: 420}], // Triangle
[{x: 300, y: 550},{x: 300, y: 750},{x: 600, y: 750},{x: 600, y: 550}] // Rectangle
];
// Load default examples
function loadExample(){
previewEl.value = JSON.stringify(examples);
}
loadExample();
// Set a given element's value to empty string
function emptyElement(elementId) {
document.getElementById(elementId).value = '';
}
// JSON parse a given object, then insert object assuming parsed value is array.
function insertObjectToElement(object, element) {
var parsed = []; // Default to empty array
try {
parsed = JSON.parse(element.value);
} catch(error) { }
parsed.push(object);
element.value = JSON.stringify(parsed)
}
// Add click handler to clicked point tracker
viewer.addHandler('canvas-click', function(event) {
var viewportPoint = viewer.viewport.pointFromPixel(event.position);
var p = viewer.viewport.viewportToImageCoordinates(viewportPoint);
p.x = Math.round((p.x + Number.EPSILON) * 100) / 100
p.y = Math.round((p.y + Number.EPSILON) * 100) / 100
insertObjectToElement({x:p.x, y:p.y}, polygonPointEl);
});
// Evaluate give element in JavaScript variable, default to empty array.
function readElementValueDefaultToEmptyArray(elementId) {
try {
var val = document.getElementById(elementId).value;
if (val === '') { return []; }
return eval(val); // If using JSON.parse, user must put quotes [{"x": 123, "y":12}]
} catch (e) {
return [];
}
}
// Insert value from given element into preview element
function insertValueFromElementToPreviewElement(element) {
try {
if (element.value === '') { return; }
var polygon = eval(element.value);
var polygonList = readElementValueDefaultToEmptyArray('previewEl');
polygonList.push(polygon);
element.value = '';
previewEl.value = JSON.stringify(polygonList);
} catch(error) {
console.log(error);
}
}
// Add clicked points to polygon tracker variable as point objects
document.getElementById('addPointBtn').onclick = function(){
insertValueFromElementToPreviewElement(polygonPointEl);
};
// Crop image with value in the preview element
document.getElementById('cropBtn').onclick = function(){
var polygonList = eval(previewEl.value);
var tiledImage = viewer.world.getItemAt(0);
tiledImage.setCroppingPolygons(polygonList);
viewer.forceRedraw();
emptyElement('previewEl');
};
document.getElementById('resetBtn').onclick = function(){
emptyElement('polygonPointEl');
emptyElement('previewEl');
var tiledImage = viewer.world.getItemAt(0);
tiledImage.resetCroppingPolygons();
viewer.forceRedraw();
};
document.getElementById('exampleBtn').onclick = loadExample;
</script>
</body>
</html>

113
test/demo/flipping.html Normal file
View File

@ -0,0 +1,113 @@
<!DOCTYPE html>
<html>
<head>
<title>OpenSeadragon Flipping Demo</title>
<script type="text/javascript" src='../../build/openseadragon/openseadragon.js'></script>
<script type="text/javascript" src='../lib/jquery-1.9.1.min.js'></script>
<style type="text/css">
.openseadragon1 {
width: 800px;
height: 600px;
float: left;
}
.options {
margin: 0.5em;
}
.button {
margin: 0.3em;
}
</style>
</head>
<body>
<div>
Simple demo page to show image flipping.
</div>
<div id="contentDiv" class="openseadragon1">
</div>
<div class="options">
First
<div class="button">
<input type="checkbox" id="ffirst" onchange="flip(0, this.checked)">
<label for="ffirst">Flip</label>
</div>
<div class="button">
<input type="checkbox" id="rfirst" onchange="rotate(0, this.checked * 45)">
<label for="rfirst">Rotate</label>
</div>
</div>
<div class="options">
Second
<div class="button">
<input type="checkbox" id="fsecond" onchange="flip(1, this.checked)" checked>
<label for="fsecond">Flip</label>
</div>
<div class="button">
<input type="checkbox" id="rsecond" onchange="rotate(1, this.checked * 45)">
<label for="rsecond">Rotate</label>
</div>
</div>
<div class="options">
Viewport
<div class="button">
<input type="checkbox" id="fview" onchange="flipViewport(this.checked)">
<label for="fview">Flip Viewport</label>
</div>
<div class="button">
<input type="checkbox" id="debug" onchange="debug(this.checked)">
<label for="debug">Debug Mode</label>
</div>
</div>
<script type="text/javascript">
var viewer = OpenSeadragon({
// debugMode: true,
id: "contentDiv",
prefixUrl: "../../build/openseadragon/images/",
showNavigator:true,
tileSources: [
{
tileSource: "../data/testpattern.dzi",
x: 0,
y: 0,
flipped: document.getElementById("ffirst").checked,
degrees: document.getElementById("rfirst").checked * 45,
}, {
tileSource: "../data/testpattern.dzi",
x: 1,
y: 0,
flipped: document.getElementById("fsecond").checked,
degrees: document.getElementById("rsecond").checked * 45,
}
]
});
viewer.viewport.setFlip(document.getElementById("fview").checked);
function debug(v) {
viewer.setDebugMode(v);
}
function flip(n, v) {
viewer.world.getItemAt(n).setFlip(v);
}
function rotate(n, v) {
viewer.world.getItemAt(n).setRotation(v);
}
function flipViewport(v) {
viewer.viewport.setFlip(v);
}
</script>
</body>
</html>

View File

@ -0,0 +1,55 @@
<!DOCTYPE html>
<html>
<head>
<title>OpenSeadragon Memory Check With Simple Image Demo</title>
<script type="text/javascript" src='../../build/openseadragon/openseadragon.js'></script>
<style type="text/css">
.openseadragon1 {
width: 800px;
height: 600px;
}
</style>
</head>
<body>
<div>
Simple demo page to monitor OpenSeadragon Memory Usage.
</div>
<!-- To get "Total canvas memory use exceeds the maximum limit" warning and then "null is not an object (evaluating 'smallContext.drawImage')" error,
disable _freeupCanvasMemory method in ImageTileSource,
then click Create button below 12 times on "iPad Air (3rd generation) -- 13.3" Simulator on Mac with Web Inspector by Safari. -->
<button onclick="createViewer()">Create</button>
<button onclick="destroyViewer()">Destroy</button>
<div id="contentDiv" class="openseadragon1"></div>
<script type="text/javascript">
var _viewer;
function createViewer() {
if ( _viewer ) {
destroyViewer();
}
_viewer = OpenSeadragon({
element: document.getElementById("contentDiv"),
showNavigationControl: false,
prefixUrl: "../../build/openseadragon/images/",
tileSources: {
type: "image",
url: "../data/CCyan.png"
}
});
}
function destroyViewer() {
if ( _viewer ) {
_viewer.destroy();
}
_viewer = null;
}
</script>
</body>
</html>

View File

@ -6,20 +6,22 @@
$.MouseTracker.subscribeEvents = [ "click", "dblclick", "keypress", "focus", "blur", $.MouseTracker.wheelEventName ]; $.MouseTracker.subscribeEvents = [ "click", "dblclick", "keypress", "focus", "blur", $.MouseTracker.wheelEventName ];
if( $.MouseTracker.wheelEventName == "DOMMouseScroll" ) { if( $.MouseTracker.wheelEventName === "DOMMouseScroll" ) {
// Older Firefox // Older Firefox
$.MouseTracker.subscribeEvents.push( "MozMousePixelScroll" ); $.MouseTracker.subscribeEvents.push( "MozMousePixelScroll" );
} }
$.MouseTracker.havePointerEvents = false; $.MouseTracker.havePointerEvents = false;
if ( $.Browser.vendor === $.BROWSERS.IE && $.Browser.version < 9 ) { $.MouseTracker.subscribeEvents.push( "mouseenter", "mouseleave", "mouseover", "mouseout", "mousedown", "mouseup", "mousemove" );
$.MouseTracker.subscribeEvents.push( "mouseenter", "mouseleave" ); $.MouseTracker.mousePointerId = "legacy-mouse";
$.MouseTracker.haveMouseEnter = true; // Legacy mouse events capture support (IE/Firefox only?)
} else { $.MouseTracker.havePointerCapture = (function () {
$.MouseTracker.subscribeEvents.push( "mouseover", "mouseout" ); var divElement = document.createElement( 'div' );
$.MouseTracker.haveMouseEnter = false; return $.isFunction( divElement.setCapture ) && $.isFunction( divElement.releaseCapture );
}());
if ( $.MouseTracker.havePointerCapture ) {
$.MouseTracker.subscribeEvents.push( "losecapture" );
} }
$.MouseTracker.subscribeEvents.push( "mousedown", "mouseup", "mousemove" );
if ( 'ontouchstart' in window ) { if ( 'ontouchstart' in window ) {
// iOS, Android, and other W3c Touch Event implementations // iOS, Android, and other W3c Touch Event implementations
// (see http://www.w3.org/TR/touch-events/) // (see http://www.w3.org/TR/touch-events/)
@ -32,8 +34,5 @@
// Subscribe to these to prevent default gesture handling // Subscribe to these to prevent default gesture handling
$.MouseTracker.subscribeEvents.push( "gesturestart", "gesturechange" ); $.MouseTracker.subscribeEvents.push( "gesturestart", "gesturechange" );
} }
$.MouseTracker.mousePointerId = "legacy-mouse";
$.MouseTracker.maxTouchPoints = 10;
}(OpenSeadragon)); }(OpenSeadragon));

View File

@ -37,7 +37,7 @@
}; };
$canvas $canvas
.simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseenter' : 'mouseover', event ) .simulate( 'mouseenter', event )
.simulate( 'mousedown', event ); .simulate( 'mousedown', event );
for ( var i = 0; i < args.dragCount; i++ ) { for ( var i = 0; i < args.dragCount; i++ ) {
event.clientX += args.dragDx; event.clientX += args.dragDx;
@ -47,7 +47,7 @@
} }
$canvas $canvas
.simulate( 'mouseup', event ) .simulate( 'mouseup', event )
.simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseleave' : 'mouseout', event ); .simulate( 'mouseleave', event );
}, },
// ---------- // ----------

View File

@ -53,9 +53,9 @@
assert.ok(viewer.showZoomControl, 'showZoomControl should be on'); assert.ok(viewer.showZoomControl, 'showZoomControl should be on');
assert.ok(!!viewer.zoomInButton, "zoomIn button should not be null"); assert.ok(!!viewer.zoomInButton, "zoomIn button should not be null");
assert.ok(!!viewer.zoomOutButton, "zoomOut 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"); "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"); "The zoomOut button should be present");
var oldZoom = viewer.viewport.getZoom(); var oldZoom = viewer.viewport.getZoom();
@ -108,7 +108,7 @@
viewer.removeHandler('open', openHandler); viewer.removeHandler('open', openHandler);
assert.ok(viewer.showHomeControl, 'showHomeControl should be on'); assert.ok(viewer.showHomeControl, 'showHomeControl should be on');
assert.ok(!!viewer.homeButton, "Home button should not be null"); 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"); "The home button should be present");
viewer.viewport.zoomBy(1.1); viewer.viewport.zoomBy(1.1);
@ -167,7 +167,7 @@
viewer.removeHandler('open', openHandler); viewer.removeHandler('open', openHandler);
assert.ok(viewer.showHomeControl, 'showFullPageControl should be on'); assert.ok(viewer.showHomeControl, 'showFullPageControl should be on');
assert.ok(!!viewer.fullPageButton, "FullPage button should not be null"); 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"); "The full page button should be present");
assert.ok(!viewer.isFullPage(), "OSD should not be in full page."); 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, 'Drawer exists');
assert.ok(viewer.drawer.canRotate(), 'drawer.canRotate needs to be true'); assert.ok(viewer.drawer.canRotate(), 'drawer.canRotate needs to be true');
assert.ok(viewer.showRotationControl, 'showRotationControl should 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"); "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"); "rotateRight should be found");
// Now simulate the left/right button clicks. // Now simulate the left/right button clicks.

View File

@ -32,7 +32,7 @@
offset = $canvas.offset(), offset = $canvas.offset(),
tracker = viewer.innerTracker, tracker = viewer.innerTracker,
origEnterHandler, origEnterHandler,
origExitHandler, origLeaveHandler,
origPressHandler, origPressHandler,
origReleaseHandler, origReleaseHandler,
origNonPrimaryPressHandler, origNonPrimaryPressHandler,
@ -43,7 +43,7 @@
origDragHandler, origDragHandler,
origDragEndHandler, origDragEndHandler,
enterCount, enterCount,
exitCount, leaveCount,
pressCount, pressCount,
releaseCount, releaseCount,
rightPressCount, rightPressCount,
@ -71,11 +71,11 @@
return true; return true;
} }
}; };
origExitHandler = tracker.exitHandler; origLeaveHandler = tracker.leaveHandler;
tracker.exitHandler = function ( event ) { tracker.leaveHandler = function ( event ) {
exitCount++; leaveCount++;
if (origExitHandler) { if (origLeaveHandler) {
return origExitHandler( event ); return origLeaveHandler( event );
} else { } else {
return true; return true;
} }
@ -182,7 +182,7 @@
var unhookViewerHandlers = function () { var unhookViewerHandlers = function () {
tracker.enterHandler = origEnterHandler; tracker.enterHandler = origEnterHandler;
tracker.exitHandler = origExitHandler; tracker.leaveHandler = origLeaveHandler;
tracker.pressHandler = origPressHandler; tracker.pressHandler = origPressHandler;
tracker.releaseHandler = origReleaseHandler; tracker.releaseHandler = origReleaseHandler;
tracker.moveHandler = origMoveHandler; tracker.moveHandler = origMoveHandler;
@ -195,21 +195,21 @@
var simulateEnter = function (x, y) { var simulateEnter = function (x, y) {
simEvent.clientX = offset.left + x; simEvent.clientX = offset.left + x;
simEvent.clientY = offset.top + y; simEvent.clientY = offset.top + y;
$canvas.simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseenter' : 'mouseover', simEvent ); $canvas.simulate( 'mouseenter', simEvent );
}; };
var simulateLeave = function (x, y) { var simulateLeave = function (x, y) {
simEvent.clientX = offset.left + x; simEvent.clientX = offset.left + x;
simEvent.clientY = offset.top + y; simEvent.clientY = offset.top + y;
simEvent.relatedTarget = document.body; simEvent.relatedTarget = document.body;
$canvas.simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseleave' : 'mouseout', simEvent ); $canvas.simulate( 'mouseleave', simEvent );
}; };
//var simulateLeaveFrame = function (x, y) { //var simulateLeaveFrame = function (x, y) {
// simEvent.clientX = offset.left + x; // simEvent.clientX = offset.left + x;
// simEvent.clientY = offset.top + y; // simEvent.clientY = offset.top + y;
// simEvent.relatedTarget = document.getElementsByTagName("html")[0]; // simEvent.relatedTarget = document.getElementsByTagName("html")[0];
// $canvas.simulate( OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseleave' : 'mouseout', simEvent ); // $canvas.simulate( 'mouseleave', simEvent );
//}; //};
var simulateDown = function (x, y) { var simulateDown = function (x, y) {
@ -256,7 +256,7 @@
clientY: offset.top clientY: offset.top
}; };
enterCount = 0; enterCount = 0;
exitCount = 0; leaveCount = 0;
pressCount = 0; pressCount = 0;
releaseCount = 0; releaseCount = 0;
rightPressCount = 0; rightPressCount = 0;
@ -280,8 +280,8 @@
if ('enterCount' in expected) { if ('enterCount' in expected) {
assert.equal( enterCount, expected.enterCount, expected.description + 'enterHandler event count matches expected (' + expected.enterCount + ')' ); assert.equal( enterCount, expected.enterCount, expected.description + 'enterHandler event count matches expected (' + expected.enterCount + ')' );
} }
if ('exitCount' in expected) { if ('leaveCount' in expected) {
assert.equal( exitCount, expected.exitCount, expected.description + 'exitHandler event count matches expected (' + expected.exitCount + ')' ); assert.equal( leaveCount, expected.leaveCount, expected.description + 'leaveHandler event count matches expected (' + expected.leaveCount + ')' );
} }
if ('pressCount' in expected) { if ('pressCount' in expected) {
assert.equal( pressCount, expected.pressCount, expected.description + 'pressHandler event count matches expected (' + expected.pressCount + ')' ); assert.equal( pressCount, expected.pressCount, expected.description + 'pressHandler event count matches expected (' + expected.pressCount + ')' );
@ -355,7 +355,7 @@
assessGestureExpectations({ assessGestureExpectations({
description: 'enter-move-release (release in tracked element, press in unknown element): ', description: 'enter-move-release (release in tracked element, press in unknown element): ',
enterCount: 1, enterCount: 1,
exitCount: 0, leaveCount: 0,
pressCount: 0, pressCount: 0,
releaseCount: 1, releaseCount: 1,
rightPressCount: 0, rightPressCount: 0,
@ -375,16 +375,16 @@
}); });
simulateLeave(-1, -1); // flush tracked pointer simulateLeave(-1, -1); // flush tracked pointer
// enter-move-exit (fly-over) // enter-move-leave (fly-over)
resetForAssessment(); resetForAssessment();
simulateEnter(0, 0); simulateEnter(0, 0);
simulateMove(1, 1, 10); simulateMove(1, 1, 10);
simulateMove(-1, -1, 10); simulateMove(-1, -1, 10);
simulateLeave(-1, -1); simulateLeave(-1, -1);
assessGestureExpectations({ assessGestureExpectations({
description: 'enter-move-exit (fly-over): ', description: 'enter-move-leave (fly-over): ',
enterCount: 1, enterCount: 1,
exitCount: 1, leaveCount: 1,
pressCount: 0, pressCount: 0,
releaseCount: 0, releaseCount: 0,
rightPressCount: 0, rightPressCount: 0,
@ -403,34 +403,7 @@
//quickClick: false //quickClick: false
}); });
// move-exit (fly-over, no enter event) // enter-press-release-press-release-leave (primary/left double click)
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)
resetForAssessment(); resetForAssessment();
simulateEnter(0, 0); simulateEnter(0, 0);
simulateDown(0, 0); simulateDown(0, 0);
@ -439,9 +412,9 @@
simulateUp(0, 0); simulateUp(0, 0);
simulateLeave(-1, -1); simulateLeave(-1, -1);
assessGestureExpectations({ 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, enterCount: 1,
exitCount: 1, leaveCount: 1,
pressCount: 2, pressCount: 2,
releaseCount: 2, releaseCount: 2,
rightPressCount: 0, rightPressCount: 0,
@ -452,7 +425,7 @@
clickCount: 2, clickCount: 2,
dblClickCount: 1, dblClickCount: 1,
dragCount: 0, dragCount: 0,
dragEndCount: 0, dragEndCount: 2, // v2.5.0+ drag-end event now fired even if pointer didn't move (#1459)
insideElementPressed: true, insideElementPressed: true,
insideElementReleased: true, insideElementReleased: true,
contacts: 0, contacts: 0,
@ -460,16 +433,16 @@
//quickClick: true //quickClick: true
}); });
// enter-press-release-exit (primary/left click) // enter-press-release-leave (primary/left click)
resetForAssessment(); resetForAssessment();
simulateEnter(0, 0); simulateEnter(0, 0);
simulateDown(0, 0); simulateDown(0, 0);
simulateUp(0, 0); simulateUp(0, 0);
simulateLeave(-1, -1); simulateLeave(-1, -1);
assessGestureExpectations({ assessGestureExpectations({
description: 'enter-press-release-exit (primary/left click): ', description: 'enter-press-release-leave (primary/left click): ',
enterCount: 1, enterCount: 1,
exitCount: 1, leaveCount: 1,
pressCount: 1, pressCount: 1,
releaseCount: 1, releaseCount: 1,
rightPressCount: 0, rightPressCount: 0,
@ -480,7 +453,7 @@
clickCount: 1, clickCount: 1,
dblClickCount: 0, dblClickCount: 0,
dragCount: 0, dragCount: 0,
dragEndCount: 0, dragEndCount: 1, // v2.5.0+ drag-end event now fired even if pointer didn't move (#1459)
insideElementPressed: true, insideElementPressed: true,
insideElementReleased: true, insideElementReleased: true,
contacts: 0, contacts: 0,
@ -488,16 +461,16 @@
quickClick: true quickClick: true
}); });
// enter-nonprimarypress-nonprimaryrelease-exit (secondary/right click) // enter-nonprimarypress-nonprimaryrelease-leave (secondary/right click)
resetForAssessment(); resetForAssessment();
simulateEnter(0, 0); simulateEnter(0, 0);
simulateNonPrimaryDown(0, 0, 2); simulateNonPrimaryDown(0, 0, 2);
simulateNonPrimaryUp(0, 0, 2); simulateNonPrimaryUp(0, 0, 2);
simulateLeave(-1, -1); simulateLeave(-1, -1);
assessGestureExpectations({ assessGestureExpectations({
description: 'enter-nonprimarypress-nonprimaryrelease-exit (secondary/right click): ', description: 'enter-nonprimarypress-nonprimaryrelease-leave (secondary/right click): ',
enterCount: 1, enterCount: 1,
exitCount: 1, leaveCount: 1,
pressCount: 0, pressCount: 0,
releaseCount: 0, releaseCount: 0,
rightPressCount: 1, rightPressCount: 1,
@ -516,16 +489,16 @@
//quickClick: true //quickClick: true
}); });
// enter-nonprimarypress-nonprimaryrelease-exit (aux/middle click) // enter-nonprimarypress-nonprimaryrelease-leave (aux/middle click)
resetForAssessment(); resetForAssessment();
simulateEnter(0, 0); simulateEnter(0, 0);
simulateNonPrimaryDown(0, 0, 1); simulateNonPrimaryDown(0, 0, 1);
simulateNonPrimaryUp(0, 0, 1); simulateNonPrimaryUp(0, 0, 1);
simulateLeave(-1, -1); simulateLeave(-1, -1);
assessGestureExpectations({ assessGestureExpectations({
description: 'enter-nonprimarypress-nonprimaryrelease-exit (aux/middle click): ', description: 'enter-nonprimarypress-nonprimaryrelease-leave (aux/middle click): ',
enterCount: 1, enterCount: 1,
exitCount: 1, leaveCount: 1,
pressCount: 0, pressCount: 0,
releaseCount: 0, releaseCount: 0,
rightPressCount: 0, rightPressCount: 0,
@ -544,7 +517,7 @@
//quickClick: true //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(); resetForAssessment();
simulateEnter(0, 0); simulateEnter(0, 0);
simulateNonPrimaryDown(0, 0, 2); simulateNonPrimaryDown(0, 0, 2);
@ -553,9 +526,9 @@
simulateMove(-1, -1, 100); simulateMove(-1, -1, 100);
simulateLeave(-1, -1); simulateLeave(-1, -1);
assessGestureExpectations({ 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, enterCount: 1,
exitCount: 1, leaveCount: 1,
pressCount: 0, pressCount: 0,
releaseCount: 0, releaseCount: 0,
rightPressCount: 1, rightPressCount: 1,
@ -574,7 +547,7 @@
//quickClick: false //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(); resetForAssessment();
simulateEnter(0, 0); simulateEnter(0, 0);
simulateDown(0, 0); simulateDown(0, 0);
@ -583,9 +556,9 @@
simulateMove(-1, -1, 100); simulateMove(-1, -1, 100);
simulateLeave(-1, -1); simulateLeave(-1, -1);
assessGestureExpectations({ 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, enterCount: 1,
exitCount: 1, leaveCount: 1,
pressCount: 1, pressCount: 1,
releaseCount: 1, releaseCount: 1,
rightPressCount: 0, rightPressCount: 0,
@ -604,7 +577,7 @@
quickClick: false 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(); resetForAssessment();
simulateEnter(0, 0); simulateEnter(0, 0);
simulateDown(0, 0); simulateDown(0, 0);
@ -614,9 +587,9 @@
simulateMove(-1, -1, 5); simulateMove(-1, -1, 5);
simulateUp(-5, -5); simulateUp(-5, -5);
assessGestureExpectations({ 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, enterCount: 1,
exitCount: 1, leaveCount: 1,
pressCount: 1, pressCount: 1,
releaseCount: 1, releaseCount: 1,
rightPressCount: 0, rightPressCount: 0,
@ -635,7 +608,7 @@
quickClick: false 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(); //resetForAssessment();
//simulateEnter(0, 0); //simulateEnter(0, 0);
//simulateDown(0, 0); //simulateDown(0, 0);
@ -644,9 +617,9 @@
//simulateLeaveFrame(-1, -1); //simulateLeaveFrame(-1, -1);
//// you don't actually receive the mouseup if you mouseup outside of the document //// you don't actually receive the mouseup if you mouseup outside of the document
//assessGestureExpectations({ //assessGestureExpectations({
// description: 'enter-press-move-exit-move-release-outside (drag, release outside iframe): ', // description: 'enter-press-move-leave-move-release-outside (drag, release outside iframe): ',
// enterCount: 1, // enterCount: 1,
// exitCount: 1, // leaveCount: 1,
// pressCount: 1, // pressCount: 1,
// releaseCount: 1, // releaseCount: 1,
// rightPressCount: 0, // rightPressCount: 0,
@ -679,8 +652,7 @@
if ('TouchEvent' in window) { if ('TouchEvent' in window) {
QUnit.test( 'MouseTracker: touch events', function (assert) { QUnit.test( 'MouseTracker: touch events', function (assert) {
var done = assert.async(); var done = assert.async();
var $canvas = $( viewer.element ).find( '.openseadragon-canvas' ).not( '.navigator .openseadragon-canvas' ), var tracker = viewer.innerTracker,
tracker = viewer.innerTracker,
touches; touches;
var reset = function () { var reset = function () {
@ -757,7 +729,6 @@
var done = assert.async(); var done = assert.async();
var $canvas = $(viewer.element).find('.openseadragon-canvas') var $canvas = $(viewer.element).find('.openseadragon-canvas')
.not('.navigator .openseadragon-canvas'); .not('.navigator .openseadragon-canvas');
var tracker = viewer.innerTracker;
var epsilon = 0.0000001; var epsilon = 0.0000001;
function simulateClickAndDrag() { function simulateClickAndDrag() {
@ -787,16 +758,14 @@
viewer.removeHandler('open', onOpen); viewer.removeHandler('open', onOpen);
// Hook viewer events to set preventDefaultAction // Hook viewer events to set preventDefaultAction
var origClickHandler = tracker.clickHandler; var onCanvasClick = function (event) {
tracker.clickHandler = function(event) {
event.preventDefaultAction = true; event.preventDefaultAction = true;
return origClickHandler(event);
}; };
var origDragHandler = tracker.dragHandler; var onCanvasDrag = function (event) {
tracker.dragHandler = function(event) {
event.preventDefaultAction = true; event.preventDefaultAction = true;
return origDragHandler(event);
}; };
viewer.addHandler("canvas-click", onCanvasClick);
viewer.addHandler("canvas-drag", onCanvasDrag);
var originalZoom = viewer.viewport.getZoom(); var originalZoom = viewer.viewport.getZoom();
var originalBounds = viewer.viewport.getBounds(); var originalBounds = viewer.viewport.getBounds();
@ -810,8 +779,8 @@
Util.assertRectangleEquals(assert, bounds, originalBounds, epsilon, Util.assertRectangleEquals(assert, bounds, originalBounds, epsilon,
'Pan should be prevented'); 'Pan should be prevented');
tracker.clickHandler = origClickHandler; viewer.removeHandler("canvas-click", onCanvasClick);
tracker.dragHandler = origDragHandler; viewer.removeHandler("canvas-drag", onCanvasDrag);
simulateClickAndDrag(); simulateClickAndDrag();
@ -837,31 +806,64 @@
// ---------- // ----------
QUnit.test('Viewer: preventDefaultAction in dblClickHandler', function(assert) { QUnit.test('Viewer: preventDefaultAction in dblClickHandler', function(assert) {
var done = assert.async(); var done = assert.async();
var tracker = viewer.innerTracker;
var epsilon = 0.0000001; 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() { function simulateDblTap() {
var touches = []; simulateEnter(2, 2);
TouchUtil.reset(); simulateDown(2, 2);
simulateUp(2, 2);
touches.push(TouchUtil.start([0,0])); simulateDown(2, 2);
TouchUtil.end( touches[0] ); simulateUp(2, 2);
touches.push(TouchUtil.start([0,0])); simulateLeave(-1, -1);
TouchUtil.end( touches[1] );
} }
var onOpen = function() { var onOpen = function() {
viewer.removeHandler('open', onOpen); 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 originalZoom = viewer.viewport.getZoom();
var origDblClickHandler = tracker.dblClickHandler; var onCanvasDblClick = function (event) {
tracker.dblClickHandler = function(event) {
event.preventDefaultAction = true; event.preventDefaultAction = true;
return origDblClickHandler(event);
}; };
TouchUtil.initTracker(tracker); viewer.addHandler('canvas-double-click', onCanvasDblClick);
simulateDblTap(); simulateDblTap();
var zoom = viewer.viewport.getZoom(); var zoom = viewer.viewport.getZoom();
@ -869,37 +871,19 @@
"Zoom on double tap should be prevented"); "Zoom on double tap should be prevented");
// Reset event handler to original // Reset event handler to original
tracker.dblClickHandler = origDblClickHandler; viewer.removeHandler("canvas-double-click", onCanvasDblClick);
simulateDblTap(); simulateDblTap();
originalZoom = originalZoom * viewer.zoomPerClick; originalZoom *= viewer.zoomPerClick;
zoom = viewer.viewport.getZoom(); zoom = viewer.viewport.getZoom();
Util.assessNumericValue(assert, originalZoom, zoom, epsilon, Util.assessNumericValue(assert, originalZoom, zoom, epsilon,
"Zoom on double tap should not be prevented"); "Zoom on double tap should not be prevented");
var dblClickHandler = function(event) { viewer.gestureSettingsMouse.clickToZoom = origClickSetting;
event.preventDefaultAction = true; 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(); viewer.close();
done(); done();
}; };
@ -926,10 +910,9 @@
eventsHandledViewer = 0, eventsHandledViewer = 0,
originalEventsPassedViewer = 0, originalEventsPassedViewer = 0,
dragEndsExpected = 1, dragEndsExpected = 1,
releasesExpected = 1, releasesExpected = 1;
clicksExpected = 1;
var onOpen = function ( event ) { var onOpen = function ( ) {
viewer.removeHandler( 'open', onOpen ); viewer.removeHandler( 'open', onOpen );
viewer.addHandler( 'canvas-drag', onEventSourceDrag ); viewer.addHandler( 'canvas-drag', onEventSourceDrag );
@ -953,7 +936,7 @@
dragEndHandler: onMouseTrackerDragEnd, dragEndHandler: onMouseTrackerDragEnd,
releaseHandler: onMouseTrackerRelease, releaseHandler: onMouseTrackerRelease,
clickHandler: onMouseTrackerClick, clickHandler: onMouseTrackerClick,
exitHandler: onMouseTrackerExit leaveHandler: onMouseTrackerLeave
} ); } );
var event = { var event = {
@ -1050,7 +1033,7 @@
checkOriginalEventReceived( event ); checkOriginalEventReceived( event );
}; };
var onMouseTrackerExit = function ( event ) { var onMouseTrackerLeave = function ( event ) {
checkOriginalEventReceived( event ); checkOriginalEventReceived( event );
mouseTracker.destroy(); mouseTracker.destroy();

249
test/modules/iiif.js Normal file
View File

@ -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('<?xml version="1.0" encoding="UTF-8"?>' +
'<info xmlns="http://library.stanford.edu/iiif/image-api/ns/">' +
'<identifier>http://example.com/identifier</identifier>' +
'<width>6000</width>' +
'<height>4000</height>' +
'<scale_factors>' +
'<scale_factor>1</scale_factor>' +
'<scale_factor>2</scale_factor>' +
'<scale_factor>4</scale_factor>' +
'</scale_factors>' +
'<profile>http://library.stanford.edu/iiif/image-api/compliance.html#level0</profile>' +
'</info>',
'text/xml'
),
infoXml10level1 = new DOMParser().parseFromString('<?xml version="1.0" encoding="UTF-8"?>' +
'<info xmlns="http://library.stanford.edu/iiif/image-api/ns/">' +
'<identifier>http://example.com/identifier</identifier>' +
'<width>6000</width>' +
'<height>4000</height>' +
'<profile>http://library.stanford.edu/iiif/image-api/compliance.html#level1</profile>' +
'</info>',
'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");
});
})();

View File

@ -216,19 +216,34 @@
clientY: offset.top + locationY clientY: offset.top + locationY
}; };
$canvas $canvas
.simulate(OpenSeadragon.MouseTracker.haveMouseEnter ? 'mouseenter' : 'mouseover', event) .simulate('mouseenter', event)
.simulate('mousedown', event) .simulate('mousedown', event)
.simulate('mouseup', event); .simulate('mouseup', event);
}; };
var simulateNavigatorDrag = function (viewer, distanceX, distanceY) { var simulateNavigatorDrag = function (viewer, distanceX, distanceY) {
var $canvas = $(viewer.element).find('.displayregion'), var $canvas = $(viewer.element).find('.openseadragon-canvas'),
event = { offset = $canvas.offset(),
dx: Math.floor(distanceX), event = {};
dy: Math.floor(distanceY)
}; event.clientX = offset.left + 1;
$canvas event.clientY = offset.top + 1;
.simulate('drag', event); $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 () { var dragNavigatorBackToCenter = function () {

View File

@ -41,8 +41,11 @@
viewer.addHandler('open', function(event) { viewer.addHandler('open', function(event) {
var image = viewer.world.getItemAt(0); var image = viewer.world.getItemAt(0);
var contentSize = image.getContentSize(); var contentSize = image.getContentSize();
var sizeInWindowCoords = image.getSizeInWindowCoordinates();
assert.equal(contentSize.x, 500, 'contentSize.x'); assert.equal(contentSize.x, 500, 'contentSize.x');
assert.equal(contentSize.y, 2000, 'contentSize.y'); 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'); checkBounds(assert, image, new OpenSeadragon.Rect(5, 6, 10, 40), 'initial bounds');
@ -74,7 +77,18 @@
image.setHeight(4); image.setHeight(4);
checkBounds(assert, image, new OpenSeadragon.Rect(7, 8, 1, 4), 'bounds after width'); 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(); done();
}); });

View File

@ -0,0 +1,93 @@
/* global QUnit, $, testLog */
(function() {
var viewer1;
var viewer2;
QUnit.module('ViewerRetrieval', {
beforeEach: function () {
$('<div id="example1"></div><div id="example2"></div>')
.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');
});
})();

View File

@ -22,6 +22,7 @@
<!-- Polyfill must be inserted first because it is testing functions <!-- Polyfill must be inserted first because it is testing functions
reassignments which could be done by other test. --> reassignments which could be done by other test. -->
<script src="/test/modules/polyfills.js"></script> <script src="/test/modules/polyfills.js"></script>
<script src="/test/modules/viewerretrieval.js"></script>
<script src="/test/modules/basic.js"></script> <script src="/test/modules/basic.js"></script>
<script src="/test/modules/strings.js"></script> <script src="/test/modules/strings.js"></script>
<script src="/test/modules/formats.js"></script> <script src="/test/modules/formats.js"></script>
@ -44,6 +45,7 @@
<script src="/test/modules/rectangle.js"></script> <script src="/test/modules/rectangle.js"></script>
<script src="/test/modules/ajax-tiles.js"></script> <script src="/test/modules/ajax-tiles.js"></script>
<script src="/test/modules/imageloader.js"></script> <script src="/test/modules/imageloader.js"></script>
<script src="/test/modules/iiif.js"></script>
<!--The navigator tests are the slowest (for now; hopefully they can be sped up) <!--The navigator tests are the slowest (for now; hopefully they can be sped up)
so we put them last. --> so we put them last. -->
<script src="/test/modules/navigator.js"></script> <script src="/test/modules/navigator.js"></script>