Merge remote branch 'upstream/master'

This commit is contained in:
Heath Nielson 2014-04-10 14:03:21 -06:00
commit aa4b5f6e65
212 changed files with 11529 additions and 3476 deletions

2
.gitignore vendored
View File

@ -1,5 +1,3 @@
*.sublime-workspace *.sublime-workspace
node_modules node_modules
build/ build/
openseadragon.zip
openseadragon.tar

14
.jshintrc Normal file
View File

@ -0,0 +1,14 @@
{
"browser": true,
"curly": true,
"eqeqeq": false,
"loopfunc": false,
"noarg": true,
"trailing": true,
"undef": true,
"unused": false,
"globals": {
"OpenSeadragon": true
}
}

View File

@ -10,15 +10,19 @@ module.exports = function(grunt) {
grunt.loadNpmTasks("grunt-contrib-watch"); grunt.loadNpmTasks("grunt-contrib-watch");
grunt.loadNpmTasks("grunt-contrib-clean"); grunt.loadNpmTasks("grunt-contrib-clean");
grunt.loadNpmTasks("grunt-git-describe"); grunt.loadNpmTasks("grunt-git-describe");
grunt.loadNpmTasks('grunt-text-replace');
// ---------- // ----------
var distribution = "build/openseadragon/openseadragon.js", var packageJson = grunt.file.readJSON("package.json"),
distribution = "build/openseadragon/openseadragon.js",
minified = "build/openseadragon/openseadragon.min.js", minified = "build/openseadragon/openseadragon.min.js",
packageDirName = "openseadragon-bin-" + packageJson.version,
packageDir = "build/" + packageDirName + "/",
releaseRoot = "../site-build/built-openseadragon/", releaseRoot = "../site-build/built-openseadragon/",
sources = [ sources = [
"src/openseadragon.js", "src/openseadragon.js",
"src/fullscreen.js", "src/fullscreen.js",
"src/eventhandler.js", "src/eventsource.js",
"src/mousetracker.js", "src/mousetracker.js",
"src/control.js", "src/control.js",
"src/controldock.js", "src/controldock.js",
@ -30,6 +34,7 @@ module.exports = function(grunt) {
"src/tilesource.js", "src/tilesource.js",
"src/dzitilesource.js", "src/dzitilesource.js",
"src/iiiftilesource.js", "src/iiiftilesource.js",
"src/iiif1_1tilesource.js",
"src/osmtilesource.js", "src/osmtilesource.js",
"src/tmstilesource.js", "src/tmstilesource.js",
"src/legacytilesource.js", "src/legacytilesource.js",
@ -49,9 +54,16 @@ module.exports = function(grunt) {
// ---------- // ----------
// Project configuration. // Project configuration.
grunt.initConfig({ grunt.initConfig({
pkg: grunt.file.readJSON("package.json"), pkg: packageJson,
osdVersion: {
versionStr: packageJson.version,
major: parseInt(packageJson.version.split('.')[0], 10),
minor: parseInt(packageJson.version.split('.')[1], 10),
revision: parseInt(packageJson.version.split('.')[2], 10)
},
clean: { clean: {
build: ["build"], build: ["build"],
package: [packageDir],
release: { release: {
src: [releaseRoot], src: [releaseRoot],
options: { options: {
@ -64,7 +76,8 @@ module.exports = function(grunt) {
banner: "//! <%= pkg.name %> <%= pkg.version %>\n" banner: "//! <%= pkg.name %> <%= pkg.version %>\n"
+ "//! Built on <%= grunt.template.today('yyyy-mm-dd') %>\n" + "//! Built on <%= grunt.template.today('yyyy-mm-dd') %>\n"
+ "//! Git commit: <%= gitInfo %>\n" + "//! Git commit: <%= gitInfo %>\n"
+ "//! http://openseadragon.github.io\n\n", + "//! http://openseadragon.github.io\n"
+ "//! License: http://openseadragon.github.io/license/\n\n",
process: true process: true
}, },
dist: { dist: {
@ -72,9 +85,27 @@ module.exports = function(grunt) {
dest: distribution dest: distribution
} }
}, },
replace: {
cleanPaths: {
src: ['build/openseadragon/*.map'],
overwrite: true,
replacements: [
{
from: /build\/openseadragon\//g,
to: ''
}
]
}
},
uglify: { uglify: {
options: { options: {
preserveComments: "some" preserveComments: "some",
sourceMap: function (filename) {
return filename.replace(/\.js$/, '.js.map');
},
sourceMappingURL: function (filename) {
return filename.replace(/\.js$/, '.js.map').replace('build/openseadragon/', '');
},
}, },
openseadragon: { openseadragon: {
src: [ distribution ], src: [ distribution ],
@ -84,24 +115,27 @@ module.exports = function(grunt) {
compress: { compress: {
zip: { zip: {
options: { options: {
archive: "build/openseadragon.zip" archive: "build/releases/" + packageDirName + ".zip",
level: 9
}, },
files: [ files: [
{ expand: true, cwd: "build/", src: ["openseadragon/**"] } { expand: true, cwd: "build/", src: [ packageDirName + "/**" ] }
] ]
}, },
tar: { tar: {
options: { options: {
archive: "build/openseadragon.tar" archive: "build/releases/" + packageDirName + ".tar.gz",
level: 9
}, },
files: [ files: [
{ expand: true, cwd: "build/", src: [ "openseadragon/**" ] } { expand: true, cwd: "build/", src: [ packageDirName + "/**" ] }
] ]
} }
}, },
qunit: { qunit: {
all: { all: {
options: { options: {
timeout: 10000,
urls: [ "http://localhost:8000/test/test.html" ] urls: [ "http://localhost:8000/test/test.html" ]
} }
} }
@ -120,12 +154,7 @@ module.exports = function(grunt) {
}, },
jshint: { jshint: {
options: { options: {
browser: true, jshintrc: '.jshintrc'
eqeqeq: false,
loopfunc: false,
globals: {
OpenSeadragon: true
}
}, },
beforeconcat: sources, beforeconcat: sources,
afterconcat: [ distribution ] afterconcat: [ distribution ]
@ -144,10 +173,22 @@ module.exports = function(grunt) {
// Copies the image files into the appropriate location in the build folder. // Copies the image files into the appropriate location in the build folder.
grunt.registerTask("copy:build", function() { grunt.registerTask("copy:build", function() {
grunt.file.recurse("images", function(abspath, rootdir, subdir, filename) { grunt.file.recurse("images", function(abspath, rootdir, subdir, filename) {
grunt.file.copy(abspath, "build/openseadragon/images/" + (subdir || "") + filename); grunt.file.copy(abspath, "build/openseadragon/images/" + (subdir || "") + filename);
}); });
});
grunt.file.copy("changelog.txt", "build/changelog.txt"); // ----------
// Copy:package task.
// Creates a directory tree to be compressed into a package.
grunt.registerTask("copy:package", function() {
grunt.file.recurse("build/openseadragon", function(abspath, rootdir, subdir, filename) {
var dest = packageDir
+ (subdir ? subdir + "/" : '/')
+ filename;
grunt.file.copy(abspath, dest);
});
grunt.file.copy("changelog.txt", packageDir + "changelog.txt");
grunt.file.copy("LICENSE.txt", packageDir + "LICENSE.txt");
}); });
// ---------- // ----------
@ -155,6 +196,10 @@ module.exports = function(grunt) {
// Copies the contents of the build folder into the release folder. // Copies the contents of the build folder into the release folder.
grunt.registerTask("copy:release", function() { grunt.registerTask("copy:release", function() {
grunt.file.recurse("build", function(abspath, rootdir, subdir, filename) { grunt.file.recurse("build", function(abspath, rootdir, subdir, filename) {
if (subdir === 'releases') {
return;
}
var dest = releaseRoot var dest = releaseRoot
+ (subdir ? subdir + "/" : '/') + (subdir ? subdir + "/" : '/')
+ filename; + filename;
@ -167,7 +212,8 @@ module.exports = function(grunt) {
// Build task. // Build task.
// Cleans out the build folder and builds the code and images into it, checking lint. // Cleans out the build folder and builds the code and images into it, checking lint.
grunt.registerTask("build", [ grunt.registerTask("build", [
"clean:build", "jshint:beforeconcat", "git-describe", "concat", "jshint:afterconcat", "uglify", "copy:build" "clean:build", "jshint:beforeconcat", "git-describe", "concat", "jshint:afterconcat",
"uglify", "replace:cleanPaths", "copy:build"
]); ]);
// ---------- // ----------
@ -177,8 +223,8 @@ module.exports = function(grunt) {
// ---------- // ----------
// Package task. // Package task.
// Builds and creates the .zip and .tar files. // Builds and creates the .zip and .tar.gz files.
grunt.registerTask("package", ["build", "compress"]); grunt.registerTask("package", ["build", "copy:package", "compress", "clean:package"]);
// ---------- // ----------
// Publish task. // Publish task.

28
LICENSE.txt Normal file
View File

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

View File

@ -1,14 +1,20 @@
# OpenSeadragon # OpenSeadragon
This project is a fork of the OpenSeadragon project at http://openseadragon.codeplex.com/ An open-source, web-based viewer for zoomable images, implemented in pure JavaScript.
## On the Web See it in action and get started using it at http://openseadragon.github.io/.
http://openseadragon.github.io/ ## Stable Builds
## First Time Setup See the [GitHub releases page](https://github.com/openseadragon/openseadragon/releases).
All command-line operations are scripted using [Grunt](http://gruntjs.com/) which is based on [Node.js](http://nodejs.org/). To get set up: ## Development
If you want to use OpenSeadragon in your own projects, you can find the latest stable build, API documentation, and example code at http://openseadragon.github.io/. If you want to modify OpenSeadragon and/or contribute to its development, read on.
### First Time Setup
All command-line operations for building and testing OpenSeadragon are scripted using [Grunt](http://gruntjs.com/) which is based on [Node.js](http://nodejs.org/). To get set up:
1. Install Node, if you haven't already (available at the link above) 1. Install Node, if you haven't already (available at the link above)
1. Install the Grunt command line runner (if you haven't already); on the command line, run `npm install -g grunt-cli` 1. Install the Grunt command line runner (if you haven't already); on the command line, run `npm install -g grunt-cli`
@ -18,7 +24,7 @@ All command-line operations are scripted using [Grunt](http://gruntjs.com/) whic
You're set... continue reading for build and test instructions. You're set... continue reading for build and test instructions.
## Building from Source ### Building from Source
To build, just run (on the command line, in the openseadragon folder): To build, just run (on the command line, in the openseadragon folder):
@ -40,9 +46,9 @@ You can also publish the built version to the site-build repository. This assume
grunt publish grunt publish
... which will delete the existing openseadragon folder, along with the .zip and .tar files, out of the site-build folder and replace them with newly built ones from the source in this repository; you'll then need to commit the changes to site-build. ... which will delete the existing openseadragon folder, along with the .zip and .tar.gz files, out of the site-build folder and replace them with newly built ones from the source in this repository; you'll then need to commit the changes to site-build.
## Testing ### Testing
Our tests are based on [QUnit](http://qunitjs.com/) and [PhantomJS](http://phantomjs.org/); they're both installed when you run `npm install`. At the moment we don't have much in the way of tests, but we're working to fix that. To run on the command line: Our tests are based on [QUnit](http://qunitjs.com/) and [PhantomJS](http://phantomjs.org/); they're both installed when you run `npm install`. At the moment we don't have much in the way of tests, but we're working to fix that. To run on the command line:
@ -52,58 +58,27 @@ If you wish to work interactively with the tests or test your changes:
grunt connect watch grunt connect watch
and open `http://localhost:8000/` in your browser and open `http://localhost:8000/test/test.html` in your browser.
## Contributing Another good page, if you want to interactively test out your changes, is `http://localhost:8000/test/demo/basic.html`.
### Contributing
OpenSeadragon is truly a community project; we welcome your involvement! OpenSeadragon is truly a community project; we welcome your involvement!
When contributing, please attempt to match the code style already in the codebase. Note that we use four spaces per indentation stop. For more thoughts on code style, see https://github.com/rwldrn/idiomatic.js/. When contributing, please attempt to match the code style already in the codebase. Note that we use four spaces per indentation stop. For more thoughts on code style, see https://github.com/rwldrn/idiomatic.js/.
## Licenses When fixing bugs and adding features, when appropriate please also:
OpenSeadragon was initially released with a New BSD License ( preserved below ), while work done by Chris Thatcher is additionally licensed under the MIT License. * Update related doc comments (we use [JSDoc 3](http://usejsdoc.org/))
* Add/update related unit tests
### Original license preserved below If you're new to the project, check out our [good first bug](https://github.com/openseadragon/openseadragon/issues?labels=good+first+bug&page=1&state=open) issues for some places to dip your toe in the water.
------------------------------------- If you're new to open source in general, check out [GitHub's open source intro guide](https://guides.github.com/overviews/os-contributing/).
License: New BSD License (BSD)
Copyright (c) 2010, OpenSeadragon
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: ## License
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. OpenSeadragon is released under the New BSD license. For details, see the file LICENSE.txt.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of OpenSeadragon nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
### MIT License
--------------------------------------
(c) Christopher Thatcher 2011, 2012. All rights reserved.
Licensed with the MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
[![Build Status](https://secure.travis-ci.org/openseadragon/openseadragon.png?branch=master)](http://travis-ci.org/openseadragon/openseadragon) [![Build Status](https://secure.travis-ci.org/openseadragon/openseadragon.png?branch=master)](http://travis-ci.org/openseadragon/openseadragon)

View File

@ -1,12 +1,171 @@
OPENSEADRAGON CHANGELOG OPENSEADRAGON CHANGELOG
======================= =======================
0.9.125: In Progress 1.1.0: (in progress)
* Fully deprecated OpenSeadragon.createFromDZI, safely deprecated Viewer.openTileSource and * BREAKING CHANGE: the openseadragon-canvas element now has two child divs. This means: (#298)
* The drawer element is no longer accessible via viewer.canvas.firstChild but via viewer.drawersContainer.firstChild or viewer.drawer.canvas.
* The overlays elements are no longer accessible via viewer.canvas.childNodes but via viewer.overlaysContainer.childNodes or viewer.currentOverlays[i].element.
* BREAKING CHANGE: Pseudo full screen mode on IE<11 using activex has been dropped. OpenSeadragon will run in full page if full screen mode is requested.
* DEPRECATION: overlay functions have been moved from Drawer to Viewer (#331)
* DEPRECATION: OpenSeadragon.cancelFullScreen has been renamed OpenSeadragon.exitFullScreen (#358)
* Added layers support. Multiple images can now been displayed on top of each other with transparency via the Viewer.addLayer method (#298)
* Improved overlay functions (#331)
* Fixed: Nav button highlight states aren't quite aligned on Firefox (#303)
* Added ControlAnchor options for default controls (#304)
* Enabled basic cross-domain tile loading without tainting canvas (works in Chrome and Firefox) (#308)
* Added crossOriginPolicy drawer configuration to enable or disable CORS image requests (#364)
* Disabled CORS by default (#377)
* Added a ControlAnchor.ABSOLUTE enumeration. Enables absolute positioning of control elements in the viewer (#310)
* Added a 'navigator-scroll' event to Navigator. Fired when mousewheel/pinch events occur in the navigator (#310)
* Added a navigatorMaintainSizeRatio option. If set to true, the navigator minimap resizes when the viewer element is resized (#310)
* Added 'ABSOLUTE' as a navigatorPosition option, along with corresponding navigatorTop, navigatorLeft options. Allows the navigator minimap to be placed anywhere in the viewer (#310)
* Enhanced the navigatorTop, navigatorLeft, navigatorHeight, and navigatorWidth options to allow a number for pixel units or a string for other element units (%, em, etc.) (#310)
* Additional enhancements for IIIF support (#315)
* Fixed: Setting degrees in Viewer constructor has no effect (#336)
* Added pre-draw event for tiles to allow applications to alter the image (#348)
* Added optional Rotate Left/Right buttons to standard controls (#341)
* Added optimization for large numbers of overlays: `checkResize = false` option for OpenSeadragon.Overlay (#365)
* Updated full screen API, adding support for Opera and IE11 and allowing keyboard input in Chrome (#358)
* Various fixes to bring OpenSeadragon into W3C compliance (#375)
* Added separate flags for turning off each of the nav buttons (#376)
1.0.0:
NOTE: This version has a number of breaking changes to the API, mostly in event handling. See below.
* BREAKING CHANGE: All EventSource and MouseTracker event handler method signatures changed to 'handlerMethod(event)' where event == { eventSource, userData, ... } (#251) (Also fixes #23, #224, #239)
* The new eventSource property in the event object replaces the old eventSource parameter that was passed to handler methods.
* Where the event object duplicated the eventSource value, those properties have been removed. This affects the following events:
* All Button events - 'button' property removed
* All Viewer (Viewer, Drawer, Viewport) events - 'viewer' property removed
* BREAKING CHANGE: Renamed EventHandler to EventSource (#225)
* BREAKING CHANGE: Event names changed for consistency: changed to lower case, compound names hyphenated, and "on" prefixes removed (#226):
* Viewer "animationstart" changed to "animation-start"
* Viewer "animationfinish" changed to "animation-finish"
* Button "onPress" changed to "press"
* Button "onRelease" changed to "release"
* Button "onClick" changed to "click"
* Button "onEnter" changed to "enter"
* Button "onExit" changed to "exit"
* Button "onFocus" changed to "focus"
* Button "onBlur" changed to "blur"
* BREAKING CHANGE: Numerous improvements to fullPage/fullScreen (#256):
* Retains zoom/pan position better when switching into and out of fullPage.
* Retains scroll position when switching back out.
* More resilient to styling variations on the page.
* setFullPage no longer automatically engages fullScreen; there's now a separate setFullScreen.
* 'fullpage' event is now 'full-page'.
* The `fullpage` property of the 'full-page' event is now `fullPage`.
* There is now a 'full-screen' event with a `fullScreen` property (true if it has gone to full screen).
* There are now 'pre-full-page' and 'pre-full-screen' events that include a `preventDefaultAction` property you can set in your handler to cancel. They also have `fullPage` and `fullScreen` properties respectively, to indicate if they are going into or out of the mode.
* BREAKING CHANGE: Removed the 'onPageChange' callback from the viewer options. Viewer.goToPage() now raises the 'page' event only (#285)
* Major documentation improvements (#281)
* MouseTracker now passes the original event objects to its handler methods (#23)
* MouseTracker now supports an optional 'moveHandler' method for tracking mousemove events (#215)
* Added stopHandler to MouseTracker. (#262)
* Fixed: Element-relative mouse coordinates now correct if the element and/or page is scrolled (using new OpenSeadragon.getElementOffset() method) (#131)
* Fixed: Pinch zoom event issue, regressive issue from previous event system changes (#244)
* Added IIIF Image API 1.1 Tile Source (#230)
* IIIF 1.0 now uses pixel based syntax (#249)
* Fixed: Touch event issue where no canvas-click events were being raised (#240)
* Check that zoom reference point is valid before using it in zoomTo and zoomBy (#247)
* Added a number of easier coordinate conversion methods to viewport (#243)
* Added the ability to create a viewer and start at a specified page (#252)
* Fixed image resolve issue with collection mode (#255)
* DOM events are now passed through as 'event.originalEvent' in viewer and button events where appropriate. (#257) Affects the following events:
* Viewer: 'canvas-release', 'canvas-click', 'canvas-drag', 'canvas-scroll', 'container-enter', 'container-exit', 'container-release'
* Button: 'enter', 'exit', 'press', 'release', 'focus', 'blur', 'click'
* Fixed: IE 10 not reading DZI file correctly in certain circumstances (#218)
* Added support for the 'wheel' DOM mousewheel event (#261)
* Fix for non-canvas tile rendering at large size (#264)
* Drawer now uses an HTML5 canvas element whenever it's available. Can be overridden with the Viewer.useCanvas option (#191)
* Added a boolean preventDefaultAction property (default false) to the event object passed to MouseTracker handler methods. (#270) Implemented in the following MouseTracker subscribers:
* Viewer.keyboardCommandArea.innerTracker.focusHandler: preventDefaultAction == true prevents scrolling viewer into view
* Viewer.keyboardCommandArea.innerTracker.keyHandler: preventDefaultAction == true prevents viewer keyboard navigation
* Viewer.innerTracker.clickHandler: preventDefaultAction == true prevents viewer zoom on click
* Viewer.innerTracker.dragHandler: preventDefaultAction == true prevents viewer panning with mouse/touch
* Viewer.innerTracker.scrollHandler: preventDefaultAction == true prevents viewer zooming on mousewheel/pinch
* Fixed: IE8 error with custom buttons - "Object doesn't support this action" (#279)
* Support IIIF servers that don't report tile dimensions (#286)
* Added an autoResize option. Default is true. When set to false, the viewer takes no action when its container element is resized. (#291)
* Added a static 'version' property to OpenSeadragon. Useful for plugins that require specific OpenSeadragon versions. (#292)
0.9.131:
* Fixed: canvas-click event shouldn't fire as you drag (#198)
* Fixed: LegacyTileSource doesn't fail gracefully when no supported file formats are found (#202)
* Added an optional userData argument to EventHandler.addHandler() which is passed unchanged to the handler method (#203)
* Fixed AJAX error reporting on IE8 (#208)
* Added viewportToImageRectangle method, and updated imageToViewportRectangle, imageToViewportCoordinates, and viewportToImageCoordinates to be more flexible with params (#212)
* Fixed: Viewer is not responsive (css) after returning from full screen (#222)
0.9.130:
* Added partial support for rotation (just 90 degree increments for now). (#185)
* Hiding and restoring broke the viewer; fixed (#177)
* You can now provide an onDraw function for overlays to do custom overlay manipulation (#160)
* Added a destroy function on the viewer to clean up and remove elements (#179)
* Fixed: navigatorPosition option corrected. (#163)
* OpenSeadragon.now() returned undefined the first time; fixed
* onTouchEnd did not call the correct mouse up handler; fixed (#159)
* Touch events no longer capture mouse (was causing issues on devices that support both) (#168)
* Clicking on a button control no longer refreshes page (#184)
* Drawer now works when the page is rtl (#187)
* Fixed a situation that could throw errors in touch handling (#188)
0.9.129:
* Fixed: navigator image not updating when base zoom image is changed (#147)
* Fixed tile rendering issue at lower zoom levels with the IIIF TileSource (#55)
* On IE, ajax errors would cause an exception to be thrown; fixed (#144)
* Faster and more consistent millisecond getter (#138)
* Fixed an error when using navPrevNextWrap on single images (#135)
* Various fixes to our timer handling (#133)
* Now generating source map for openseadragon.min.js (#51)
* Fix for calculating overlay width / height (#142)
* JSHint tidying (#136)
* Improved Ajax method (#149)
* Overhauled AJAX error reporting (#151)
0.9.128:
* The navigator is now off by default (#102)
* Reverted minPixelRatio to 0.5 for better quality (#116)
* Sometimes tiles wouldn't resolve if you used the blendTime option; fixed. (#95)
* You can now choose to have previous and next buttons wrap using the config.navPrevNextWrap. (#114)
* You can now specify an ID for a div to hold the navigator (#46)
* You can now click in the navigator to go to a new location (#46)
* Keyboard handling is now done in the viewer rather than navigator (#46)
* Additional navigator fixes (#46)
* Drawer events now fire properly (#94)
* Fixed an error in EventHandler.removeHandler() (#48)
* Better requestAnimationFrame detection on older Firefox (#103)
* More efficient navigator loading (#115)
* Simplified element opacity setting implementation (#123)
0.9.127:
* Fixed a problem with getString when the string property is a sub-property. (#64)
* Fixed: Tooltips for Navigation Controls not displaying (#63)
* Cleaned up some diagnostic code that was broken.
* Added fullpage class to viewer element when in fullpage mode (#61)
* Reverted to original New BSD license; cleaned up license declarations (#89)
0.9.126:
* DZI JSONp was broken; fixed.
0.9.125:
* Fully deprecated OpenSeadragon.createFromDZI, safely deprecated Viewer.openTileSource and
Viewer.openDZI to use Viewer.open internally. (#53 & #54). Viewer.openDZI to use Viewer.open internally. (#53 & #54).
* Full page bug fix for when viewer is child of document body (#43). * Full page bug fix for when viewer is child of document body (#43).
* Overlays for DZI bug fix (#45). * Overlays for DZI bug fix (#45).
* DziTileSource: avoid changing relative paths (#56).
* Fix typo in preserveViewport handling (#77).
* Fix updateMulti timer leak after multiple Viewer.open() calls (#76).
* Minor documentation fixes.
0.9.124: 0.9.124:

0
images/fullpage_grouphover.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

0
images/fullpage_hover.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

0
images/fullpage_pressed.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

0
images/fullpage_rest.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

0
images/home_grouphover.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

0
images/home_hover.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

0
images/home_pressed.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

0
images/home_rest.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
images/rotateleft_hover.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
images/rotateleft_rest.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
images/rotateright_rest.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

0
images/zoomin_grouphover.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

0
images/zoomin_hover.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

0
images/zoomin_pressed.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

0
images/zoomin_rest.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

0
images/zoomout_grouphover.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

0
images/zoomout_hover.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

0
images/zoomout_pressed.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

0
images/zoomout_rest.png Executable file → Normal file
View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -1,21 +0,0 @@
(c) Christopher Thatcher 2011, 2012. All rights reserved.
Licensed with the MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

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

View File

@ -3,12 +3,19 @@
[ [
{ {
"path": ".", "path": ".",
"file_exclude_patterns": ["*.sublime-project", "*.sublime-workspace"] "file_exclude_patterns": [
"*.sublime-project",
"*.sublime-workspace"
],
"folder_exclude_patterns": [
"node_modules"
]
} }
], ],
"settings": "settings":
{ {
"tab_size": 4, "tab_size": 4,
"translate_tabs_to_spaces": true "translate_tabs_to_spaces": true,
"trim_trailing_white_space_on_save": true
} }
} }

View File

@ -1,18 +1,19 @@
{ {
"name": "OpenSeadragon", "name": "OpenSeadragon",
"version": "0.9.124", "version": "1.0.0",
"description": "Provides a smooth, zoomable user interface for HTML/Javascript.", "description": "Provides a smooth, zoomable user interface for HTML/Javascript.",
"devDependencies": { "devDependencies": {
"grunt": "~0.4.0", "grunt": "~0.4.1",
"grunt-contrib-compress": "~0.4.0", "grunt-contrib-compress": "~0.5.2",
"grunt-contrib-concat": "~0.1.2", "grunt-contrib-concat": "~0.3.0",
"grunt-contrib-jshint": "~0.1.1", "grunt-contrib-jshint": "~0.7.2",
"grunt-contrib-uglify": "~0.1.1", "grunt-contrib-uglify": "~0.2.7",
"grunt-contrib-qunit": "~0.2.0", "grunt-contrib-qunit": "~0.3.0",
"grunt-contrib-connect": "~0.1.2", "grunt-contrib-connect": "~0.5.0",
"grunt-contrib-watch": "~0.2.0", "grunt-contrib-watch": "~0.5.3",
"grunt-contrib-clean": "~0.4.0", "grunt-contrib-clean": "~0.5.0",
"grunt-git-describe": "~2.0.0" "grunt-git-describe": "~2.0.0",
"grunt-text-replace": "~0.3.9"
}, },
"scripts": { "scripts": {
"test": "grunt test" "test": "grunt test"

View File

@ -1,8 +1,49 @@
/*
* OpenSeadragon - Button
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function( $ ){ (function( $ ){
/** /**
* An enumeration of button states including, REST, GROUP, HOVER, and DOWN * An enumeration of button states
* @member ButtonState
* @memberof OpenSeadragon
* @static * @static
* @type {Object}
* @property {Number} REST
* @property {Number} GROUP
* @property {Number} HOVER
* @property {Number} DOWN
*/ */
$.ButtonState = { $.ButtonState = {
REST: 0, REST: 0,
@ -12,47 +53,39 @@ $.ButtonState = {
}; };
/** /**
* Manages events, hover states for individual buttons, tool-tips, as well * @class Button
* as fading the bottons out when the user has not interacted with them * @classdesc Manages events, hover states for individual buttons, tool-tips, as well
* as fading the buttons out when the user has not interacted with them
* for a specified period. * for a specified period.
* @class *
* @extends OpenSeadragon.EventHandler * @memberof OpenSeadragon
* @extends OpenSeadragon.EventSource
* @param {Object} options * @param {Object} options
* @param {String} options.tooltip Provides context help for the button we the * @param {Element} [options.element=null] Element to use as the button. If not specified, an HTML &lt;button&gt; element is created.
* @param {String} [options.tooltip=null] Provides context help for the button when the
* user hovers over it. * user hovers over it.
* @param {String} options.srcRest URL of image to use in 'rest' state * @param {String} [options.srcRest=null] URL of image to use in 'rest' state.
* @param {String} options.srcGroup URL of image to use in 'up' state * @param {String} [options.srcGroup=null] URL of image to use in 'up' state.
* @param {String} options.srcHover URL of image to use in 'hover' state * @param {String} [options.srcHover=null] URL of image to use in 'hover' state.
* @param {String} options.srcDown URL of image to use in 'domn' state * @param {String} [options.srcDown=null] URL of image to use in 'down' state.
* @param {Element} [options.element] Element to use as a container for the * @param {Number} [options.fadeDelay=0] How long to wait before fading.
* button. * @param {Number} [options.fadeLength=2000] How long should it take to fade the button.
* @property {String} tooltip Provides context help for the button we the * @param {OpenSeadragon.EventHandler} [options.onPress=null] Event handler callback for {@link OpenSeadragon.Button.event:press}.
* user hovers over it. * @param {OpenSeadragon.EventHandler} [options.onRelease=null] Event handler callback for {@link OpenSeadragon.Button.event:release}.
* @property {String} srcRest URL of image to use in 'rest' state * @param {OpenSeadragon.EventHandler} [options.onClick=null] Event handler callback for {@link OpenSeadragon.Button.event:click}.
* @property {String} srcGroup URL of image to use in 'up' state * @param {OpenSeadragon.EventHandler} [options.onEnter=null] Event handler callback for {@link OpenSeadragon.Button.event:enter}.
* @property {String} srcHover URL of image to use in 'hover' state * @param {OpenSeadragon.EventHandler} [options.onExit=null] Event handler callback for {@link OpenSeadragon.Button.event:exit}.
* @property {String} srcDown URL of image to use in 'domn' state * @param {OpenSeadragon.EventHandler} [options.onFocus=null] Event handler callback for {@link OpenSeadragon.Button.event:focus}.
* @property {Object} config Configurable settings for this button. DEPRECATED. * @param {OpenSeadragon.EventHandler} [options.onBlur=null] Event handler callback for {@link OpenSeadragon.Button.event:blur}.
* @property {Element} [element] Element to use as a container for the
* button.
* @property {Number} fadeDelay How long to wait before fading
* @property {Number} fadeLength How long should it take to fade the button.
* @property {Number} fadeBeginTime When the button last began to fade.
* @property {Boolean} shouldFade Whether this button should fade after user
* stops interacting with the viewport.
this.fadeDelay = 0; // begin fading immediately
this.fadeLength = 2000; // fade over a period of 2 seconds
this.fadeBeginTime = null;
this.shouldFade = false;
*/ */
$.Button = function( options ) { $.Button = function( options ) {
var _this = this; var _this = this;
$.EventHandler.call( this ); $.EventSource.call( this );
$.extend( true, this, { $.extend( true, this, {
tooltip: null, tooltip: null,
srcRest: null, srcRest: null,
srcGroup: null, srcGroup: null,
@ -60,9 +93,17 @@ $.Button = function( options ) {
srcDown: null, srcDown: null,
clickTimeThreshold: $.DEFAULT_SETTINGS.clickTimeThreshold, clickTimeThreshold: $.DEFAULT_SETTINGS.clickTimeThreshold,
clickDistThreshold: $.DEFAULT_SETTINGS.clickDistThreshold, clickDistThreshold: $.DEFAULT_SETTINGS.clickDistThreshold,
// begin fading immediately /**
fadeDelay: 0, * How long to wait before fading.
// fade over a period of 2 seconds * @member {Number} fadeDelay
* @memberof OpenSeadragon.Button#
*/
fadeDelay: 0,
/**
* How long should it take to fade the button.
* @member {Number} fadeLength
* @memberof OpenSeadragon.Button#
*/
fadeLength: 2000, fadeLength: 2000,
onPress: null, onPress: null,
onRelease: null, onRelease: null,
@ -74,126 +115,240 @@ $.Button = function( options ) {
}, options ); }, options );
this.element = options.element || $.makeNeutralElement( "button" ); /**
this.element.href = this.element.href || '#'; * The button element.
* @member {Element} element
* @memberof OpenSeadragon.Button#
*/
this.element = options.element || $.makeNeutralElement( "div" );
//if the user has specified the element to bind the control to explicitly //if the user has specified the element to bind the control to explicitly
//then do not add the default control images //then do not add the default control images
if( !options.element ){ if ( !options.element ) {
this.imgRest = $.makeTransparentImage( this.srcRest ); this.imgRest = $.makeTransparentImage( this.srcRest );
this.imgGroup = $.makeTransparentImage( this.srcGroup ); this.imgGroup = $.makeTransparentImage( this.srcGroup );
this.imgHover = $.makeTransparentImage( this.srcHover ); this.imgHover = $.makeTransparentImage( this.srcHover );
this.imgDown = $.makeTransparentImage( this.srcDown ); this.imgDown = $.makeTransparentImage( this.srcDown );
this.imgRest.alt =
this.imgGroup.alt =
this.imgHover.alt =
this.imgDown.alt =
this.tooltip;
this.element.style.position = "relative";
this.imgGroup.style.position =
this.imgHover.style.position =
this.imgDown.style.position =
"absolute";
this.imgGroup.style.top =
this.imgHover.style.top =
this.imgDown.style.top =
"0px";
this.imgGroup.style.left =
this.imgHover.style.left =
this.imgDown.style.left =
"0px";
this.imgHover.style.visibility =
this.imgDown.style.visibility =
"hidden";
if ( $.Browser.vendor == $.BROWSERS.FIREFOX && $.Browser.version < 3 ){
this.imgGroup.style.top =
this.imgHover.style.top =
this.imgDown.style.top =
"";
}
this.element.appendChild( this.imgRest ); this.element.appendChild( this.imgRest );
this.element.appendChild( this.imgGroup ); this.element.appendChild( this.imgGroup );
this.element.appendChild( this.imgHover ); this.element.appendChild( this.imgHover );
this.element.appendChild( this.imgDown ); this.element.appendChild( this.imgDown );
this.imgGroup.style.position =
this.imgHover.style.position =
this.imgDown.style.position =
"absolute";
this.imgGroup.style.top =
this.imgHover.style.top =
this.imgDown.style.top =
"0px";
this.imgGroup.style.left =
this.imgHover.style.left =
this.imgDown.style.left =
"0px";
this.imgHover.style.visibility =
this.imgDown.style.visibility =
"hidden";
if ( $.Browser.vendor == $.BROWSERS.FIREFOX && $.Browser.version < 3 ){
this.imgGroup.style.top =
this.imgHover.style.top =
this.imgDown.style.top =
"";
}
} }
this.addHandler( "onPress", this.onPress ); this.addHandler( "press", this.onPress );
this.addHandler( "onRelease", this.onRelease ); this.addHandler( "release", this.onRelease );
this.addHandler( "onClick", this.onClick ); this.addHandler( "click", this.onClick );
this.addHandler( "onEnter", this.onEnter ); this.addHandler( "enter", this.onEnter );
this.addHandler( "onExit", this.onExit ); this.addHandler( "exit", this.onExit );
this.addHandler( "onFocus", this.onFocus ); this.addHandler( "focus", this.onFocus );
this.addHandler( "onBlur", this.onBlur ); this.addHandler( "blur", this.onBlur );
/**
* The button's current state.
* @member {OpenSeadragon.ButtonState} currentState
* @memberof OpenSeadragon.Button#
*/
this.currentState = $.ButtonState.GROUP; this.currentState = $.ButtonState.GROUP;
// When the button last began to fade.
this.fadeBeginTime = null; this.fadeBeginTime = null;
// Whether this button should fade after user stops interacting with the viewport.
this.shouldFade = false; this.shouldFade = false;
this.element.style.display = "inline-block"; this.element.style.display = "inline-block";
this.element.style.position = "relative"; this.element.style.position = "relative";
this.element.title = this.tooltip; this.element.title = this.tooltip;
/**
* Tracks mouse/touch/key events on the button.
* @member {OpenSeadragon.MouseTracker} tracker
* @memberof OpenSeadragon.Button#
*/
this.tracker = new $.MouseTracker({ this.tracker = new $.MouseTracker({
element: this.element, element: this.element,
clickTimeThreshold: this.clickTimeThreshold, clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold, clickDistThreshold: this.clickDistThreshold,
enterHandler: function( tracker, position, buttonDownElement, buttonDownAny ) { enterHandler: function( event ) {
if ( buttonDownElement ) { if ( event.insideElementPressed ) {
inTo( _this, $.ButtonState.DOWN ); inTo( _this, $.ButtonState.DOWN );
_this.raiseEvent( "onEnter", _this ); /**
} else if ( !buttonDownAny ) { * Raised when the cursor enters the Button element.
*
* @event enter
* @memberof OpenSeadragon.Button
* @type {object}
* @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.
* @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
_this.raiseEvent( "enter", { originalEvent: event.originalEvent } );
} else if ( !event.buttonDownAny ) {
inTo( _this, $.ButtonState.HOVER ); inTo( _this, $.ButtonState.HOVER );
} }
}, },
focusHandler: function( tracker, position, buttonDownElement, buttonDownAny ) { focusHandler: function ( event ) {
this.enterHandler( tracker, position, buttonDownElement, buttonDownAny ); this.enterHandler( event );
_this.raiseEvent( "onFocus", _this ); /**
* Raised when the Button element receives focus.
*
* @event focus
* @memberof OpenSeadragon.Button
* @type {object}
* @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.
* @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
_this.raiseEvent( "focus", { originalEvent: event.originalEvent } );
}, },
exitHandler: function( tracker, position, buttonDownElement, buttonDownAny ) { exitHandler: function( event ) {
outTo( _this, $.ButtonState.GROUP ); outTo( _this, $.ButtonState.GROUP );
if ( buttonDownElement ) { if ( event.insideElementPressed ) {
_this.raiseEvent( "onExit", _this ); /**
* Raised when the cursor leaves the Button element.
*
* @event exit
* @memberof OpenSeadragon.Button
* @type {object}
* @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.
* @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
_this.raiseEvent( "exit", { originalEvent: event.originalEvent } );
} }
}, },
blurHandler: function( tracker, position, buttonDownElement, buttonDownAny ) { blurHandler: function ( event ) {
this.exitHandler( tracker, position, buttonDownElement, buttonDownAny ); this.exitHandler( event );
_this.raiseEvent( "onBlur", _this ); /**
* Raised when the Button element loses focus.
*
* @event blur
* @memberof OpenSeadragon.Button
* @type {object}
* @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.
* @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
_this.raiseEvent( "blur", { originalEvent: event.originalEvent } );
}, },
pressHandler: function( tracker, position ) { pressHandler: function ( event ) {
inTo( _this, $.ButtonState.DOWN ); inTo( _this, $.ButtonState.DOWN );
_this.raiseEvent( "onPress", _this ); /**
* Raised when a mouse button is pressed or touch occurs in the Button element.
*
* @event press
* @memberof OpenSeadragon.Button
* @type {object}
* @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.
* @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
_this.raiseEvent( "press", { originalEvent: event.originalEvent } );
}, },
releaseHandler: function( tracker, position, insideElementPress, insideElementRelease ) { releaseHandler: function( event ) {
if ( insideElementPress && insideElementRelease ) { if ( event.insideElementPressed && event.insideElementReleased ) {
outTo( _this, $.ButtonState.HOVER ); outTo( _this, $.ButtonState.HOVER );
_this.raiseEvent( "onRelease", _this ); /**
} else if ( insideElementPress ) { * Raised when the mouse button is released or touch ends in the Button element.
*
* @event release
* @memberof OpenSeadragon.Button
* @type {object}
* @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.
* @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
_this.raiseEvent( "release", { originalEvent: event.originalEvent } );
} else if ( event.insideElementPressed ) {
outTo( _this, $.ButtonState.GROUP ); outTo( _this, $.ButtonState.GROUP );
} else { } else {
inTo( _this, $.ButtonState.HOVER ); inTo( _this, $.ButtonState.HOVER );
} }
}, },
clickHandler: function( tracker, position, quick, shift ) { clickHandler: function( event ) {
if ( quick ) { if ( event.quick ) {
_this.raiseEvent("onClick", _this); /**
* Raised when a mouse button is pressed and released or touch is initiated and ended in the Button element within the time and distance threshold.
*
* @event click
* @memberof OpenSeadragon.Button
* @type {object}
* @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.
* @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
_this.raiseEvent("click", { originalEvent: event.originalEvent });
} }
}, },
keyHandler: function( tracker, key ){ keyHandler: function( event ){
//console.log( "%s : handling key %s!", _this.tooltip, key); //console.log( "%s : handling key %s!", _this.tooltip, event.keyCode);
if( 13 === key ){ if( 13 === event.keyCode ){
_this.raiseEvent( "onClick", _this ); /***
_this.raiseEvent( "onRelease", _this ); * Raised when a mouse button is pressed and released or touch is initiated and ended in the Button element within the time and distance threshold.
*
* @event click
* @memberof OpenSeadragon.Button
* @type {object}
* @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.
* @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
_this.raiseEvent( "click", { originalEvent: event.originalEvent } );
/***
* Raised when the mouse button is released or touch ends in the Button element.
*
* @event release
* @memberof OpenSeadragon.Button
* @type {object}
* @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.
* @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
_this.raiseEvent( "release", { originalEvent: event.originalEvent } );
return false; return false;
} }
return true; return true;
@ -204,13 +359,12 @@ $.Button = function( options ) {
outTo( this, $.ButtonState.REST ); outTo( this, $.ButtonState.REST );
}; };
$.extend( $.Button.prototype, $.EventHandler.prototype, { $.extend( $.Button.prototype, $.EventSource.prototype, /** @lends OpenSeadragon.Button.prototype */{
/** /**
* TODO: Determine what this function is intended to do and if it's actually * TODO: Determine what this function is intended to do and if it's actually
* useful as an API point. * useful as an API point.
* @function * @function
* @name OpenSeadragon.Button.prototype.notifyGroupEnter
*/ */
notifyGroupEnter: function() { notifyGroupEnter: function() {
inTo( this, $.ButtonState.GROUP ); inTo( this, $.ButtonState.GROUP );
@ -220,18 +374,23 @@ $.extend( $.Button.prototype, $.EventHandler.prototype, {
* TODO: Determine what this function is intended to do and if it's actually * TODO: Determine what this function is intended to do and if it's actually
* useful as an API point. * useful as an API point.
* @function * @function
* @name OpenSeadragon.Button.prototype.notifyGroupExit
*/ */
notifyGroupExit: function() { notifyGroupExit: function() {
outTo( this, $.ButtonState.REST ); outTo( this, $.ButtonState.REST );
}, },
/**
* @function
*/
disable: function(){ disable: function(){
this.notifyGroupExit(); this.notifyGroupExit();
this.element.disabled = true; this.element.disabled = true;
$.setElementOpacity( this.element, 0.2, true ); $.setElementOpacity( this.element, 0.2, true );
}, },
/**
* @function
*/
enable: function(){ enable: function(){
this.element.disabled = false; this.element.disabled = false;
$.setElementOpacity( this.element, 1.0, true ); $.setElementOpacity( this.element, 1.0, true );
@ -253,7 +412,7 @@ function updateFade( button ) {
opacity; opacity;
if ( button.shouldFade ) { if ( button.shouldFade ) {
currentTime = +new Date(); currentTime = $.now();
deltaTime = currentTime - button.fadeBeginTime; deltaTime = currentTime - button.fadeBeginTime;
opacity = 1.0 - deltaTime / button.fadeLength; opacity = 1.0 - deltaTime / button.fadeLength;
opacity = Math.min( 1.0, opacity ); opacity = Math.min( 1.0, opacity );
@ -271,8 +430,8 @@ function updateFade( button ) {
function beginFading( button ) { function beginFading( button ) {
button.shouldFade = true; button.shouldFade = true;
button.fadeBeginTime = +new Date() + button.fadeDelay; button.fadeBeginTime = $.now() + button.fadeDelay;
window.setTimeout( function(){ window.setTimeout( function(){
scheduleFade( button ); scheduleFade( button );
}, button.fadeDelay ); }, button.fadeDelay );
} }
@ -290,13 +449,13 @@ function inTo( button, newState ) {
return; return;
} }
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 = "";
@ -304,7 +463,7 @@ function inTo( button, newState ) {
button.currentState = $.ButtonState.HOVER; button.currentState = $.ButtonState.HOVER;
} }
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 = "";
@ -320,7 +479,7 @@ function outTo( button, newState ) {
return; return;
} }
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";
@ -328,7 +487,7 @@ function outTo( button, newState ) {
button.currentState = $.ButtonState.HOVER; button.currentState = $.ButtonState.HOVER;
} }
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";
@ -336,7 +495,7 @@ function outTo( button, newState ) {
button.currentState = $.ButtonState.GROUP; button.currentState = $.ButtonState.GROUP;
} }
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

@ -1,41 +1,74 @@
/*
* OpenSeadragon - ButtonGroup
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function( $ ){ (function( $ ){
/** /**
* Manages events on groups of buttons. * @class ButtonGroup
* @class * @classdesc Manages events on groups of buttons.
* @param {Object} options - a dictionary of settings applied against the entire *
* group of buttons * @memberof OpenSeadragon
* @param {Array} options.buttons Array of buttons * @param {Object} options - A dictionary of settings applied against the entire group of buttons.
* @param {Element} [options.group] Element to use as the container, * @param {Array} options.buttons Array of buttons
* @param {Object} options.config Object with Viewer settings ( TODO: is * @param {Element} [options.element] Element to use as the container
* this actually used anywhere? )
* @param {Function} [options.enter] Function callback for when the mouse
* enters group
* @param {Function} [options.exit] Function callback for when mouse leaves
* the group
* @param {Function} [options.release] Function callback for when mouse is
* released
* @property {Array} buttons - An array containing the buttons themselves.
* @property {Element} element - The shared container for the buttons.
* @property {Object} config - Configurable settings for the group of buttons.
* @property {OpenSeadragon.MouseTracker} tracker - Tracks mouse events accross
* the group of buttons.
**/ **/
$.ButtonGroup = function( options ) { $.ButtonGroup = function( options ) {
$.extend( true, this, { $.extend( true, this, {
/**
* An array containing the buttons themselves.
* @member {Array} buttons
* @memberof OpenSeadragon.ButtonGroup#
*/
buttons: [], buttons: [],
clickTimeThreshold: $.DEFAULT_SETTINGS.clickTimeThreshold, clickTimeThreshold: $.DEFAULT_SETTINGS.clickTimeThreshold,
clickDistThreshold: $.DEFAULT_SETTINGS.clickDistThreshold, clickDistThreshold: $.DEFAULT_SETTINGS.clickDistThreshold,
labelText: "" labelText: ""
}, options ); }, options );
// copy the botton elements // copy the button elements TODO: Why?
var buttons = this.buttons.concat([]), var buttons = this.buttons.concat([]),
_this = this, _this = this,
i; i;
this.element = options.element || $.makeNeutralElement( "fieldgroup" ); /**
* The shared container for the buttons.
* @member {Element} element
* @memberof OpenSeadragon.ButtonGroup#
*/
this.element = options.element || $.makeNeutralElement( "div" );
// TODO What if there IS an options.group specified?
if( !options.group ){ if( !options.group ){
this.label = $.makeNeutralElement( "label" ); this.label = $.makeNeutralElement( "label" );
//TODO: support labels for ButtonGroups //TODO: support labels for ButtonGroups
@ -47,33 +80,32 @@ $.ButtonGroup = function( options ) {
} }
} }
/**
* Tracks mouse/touch/key events accross the group of buttons.
* @member {OpenSeadragon.MouseTracker} tracker
* @memberof OpenSeadragon.ButtonGroup#
*/
this.tracker = new $.MouseTracker({ this.tracker = new $.MouseTracker({
element: this.element, element: this.element,
clickTimeThreshold: this.clickTimeThreshold, clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold, clickDistThreshold: this.clickDistThreshold,
enterHandler: function() { enterHandler: function ( event ) {
var i; var i;
for ( i = 0; i < _this.buttons.length; i++ ) { for ( i = 0; i < _this.buttons.length; i++ ) {
_this.buttons[ i ].notifyGroupEnter(); _this.buttons[ i ].notifyGroupEnter();
} }
}, },
exitHandler: function() { exitHandler: function ( event ) {
var i, var i;
buttonDownElement = arguments.length > 2 ? if ( !event.insideElementPressed ) {
arguments[ 2 ] :
null;
if ( !buttonDownElement ) {
for ( i = 0; i < _this.buttons.length; i++ ) { for ( i = 0; i < _this.buttons.length; i++ ) {
_this.buttons[ i ].notifyGroupExit(); _this.buttons[ i ].notifyGroupExit();
} }
} }
}, },
releaseHandler: function() { releaseHandler: function ( event ) {
var i, var i;
insideElementRelease = arguments.length > 3 ? if ( !event.insideElementReleased ) {
arguments[ 3 ] :
null;
if ( !insideElementRelease ) {
for ( i = 0; i < _this.buttons.length; i++ ) { for ( i = 0; i < _this.buttons.length; i++ ) {
_this.buttons[ i ].notifyGroupExit(); _this.buttons[ i ].notifyGroupExit();
} }
@ -82,26 +114,26 @@ $.ButtonGroup = function( options ) {
}).setTracking( true ); }).setTracking( true );
}; };
$.ButtonGroup.prototype = { $.ButtonGroup.prototype = /** @lends OpenSeadragon.ButtonGroup.prototype */{
/** /**
* TODO: Figure out why this is used on the public API and if a more useful * TODO: Figure out why this is used on the public API and if a more useful
* api can be created. * api can be created.
* @function * @function
* @name OpenSeadragon.ButtonGroup.prototype.emulateEnter * @private
*/ */
emulateEnter: function() { emulateEnter: function() {
this.tracker.enterHandler(); this.tracker.enterHandler( { eventSource: this.tracker } );
}, },
/** /**
* TODO: Figure out why this is used on the public API and if a more useful * TODO: Figure out why this is used on the public API and if a more useful
* api can be created. * api can be created.
* @function * @function
* @name OpenSeadragon.ButtonGroup.prototype.emulateExit * @private
*/ */
emulateExit: function() { emulateExit: function() {
this.tracker.exitHandler(); this.tracker.exitHandler( { eventSource: this.tracker } );
} }
}; };

View File

@ -1,62 +1,158 @@
/*
* OpenSeadragon - Control
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function( $ ){ (function( $ ){
/** /**
* An enumeration of supported locations where controls can be anchored, * An enumeration of supported locations where controls can be anchored.
* including NONE, TOP_LEFT, TOP_RIGHT, BOTTOM_RIGHT, and BOTTOM_LEFT. * The anchoring is always relative to the container.
* The anchoring is always relative to the container * @member ControlAnchor
* @memberof OpenSeadragon
* @static * @static
* @type {Object}
* @property {Number} NONE
* @property {Number} TOP_LEFT
* @property {Number} TOP_RIGHT
* @property {Number} BOTTOM_LEFT
* @property {Number} BOTTOM_RIGHT
* @property {Number} ABSOLUTE
*/ */
$.ControlAnchor = { $.ControlAnchor = {
NONE: 0, NONE: 0,
TOP_LEFT: 1, TOP_LEFT: 1,
TOP_RIGHT: 2, TOP_RIGHT: 2,
BOTTOM_RIGHT: 3, BOTTOM_RIGHT: 3,
BOTTOM_LEFT: 4 BOTTOM_LEFT: 4,
ABSOLUTE: 5
}; };
/** /**
* A Control represents any interface element which is meant to allow the user * @class Control
* to interact with the zoomable interface. Any control can be anchored to any * @classdesc A Control represents any interface element which is meant to allow the user
* to interact with the zoomable interface. Any control can be anchored to any
* element. * element.
* @class *
* @param {Element} element - the contol element to be anchored in the container. * @memberof OpenSeadragon
* @param {OpenSeadragon.ControlAnchor} anchor - the location to anchor at. * @param {Element} element - the control element to be anchored in the container.
* @param {Element} container - the element to control will be anchored too. * @param {Object } options - All required and optional settings for configuring a control element.
* * @param {OpenSeadragon.ControlAnchor} [options.anchor=OpenSeadragon.ControlAnchor.NONE] - the position of the control
* @property {Element} element - the element providing the user interface with
* some type of control. Eg a zoom-in button
* @property {OpenSeadragon.ControlAnchor} anchor - the position of the control
* relative to the container. * relative to the container.
* @property {Element} container - the element within with the control is * @param {Boolean} [options.attachToViewer=true] - Whether the control should be added directly to the viewer, or
* positioned. * directly to the container
* @property {Element} wrapper - a nuetral element surrounding the control * @param {Boolean} [options.autoFade=true] - Whether the control should have the autofade behavior
* element. * @param {Element} container - the element to control will be anchored too.
*/ */
$.Control = function ( element, anchor, container ) { $.Control = function ( element, options, container ) {
var parent = element.parentNode;
if (typeof options === 'number')
{
$.console.error("Passing an anchor directly into the OpenSeadragon.Control constructor is deprecated; " +
"please use an options object instead. " +
"Support for this deprecated variant is scheduled for removal in December 2013");
options = {anchor: options};
}
options.attachToViewer = (typeof options.attachToViewer === 'undefined') ? true : options.attachToViewer;
/**
* True if the control should have autofade behavior.
* @member {Boolean} autoFade
* @memberof OpenSeadragon.Control#
*/
this.autoFade = (typeof options.autoFade === 'undefined') ? true : options.autoFade;
/**
* The element providing the user interface with some type of control (e.g. a zoom-in button).
* @member {Element} element
* @memberof OpenSeadragon.Control#
*/
this.element = element; this.element = element;
this.anchor = anchor; /**
* The position of the Control relative to its container.
* @member {OpenSeadragon.ControlAnchor} anchor
* @memberof OpenSeadragon.Control#
*/
this.anchor = options.anchor;
/**
* The Control's containing element.
* @member {Element} container
* @memberof OpenSeadragon.Control#
*/
this.container = container; this.container = container;
this.wrapper = $.makeNeutralElement( "span" ); /**
this.wrapper.style.display = "inline-block"; * A neutral element surrounding the control element.
* @member {Element} wrapper
* @memberof OpenSeadragon.Control#
*/
if ( this.anchor == $.ControlAnchor.ABSOLUTE ) {
this.wrapper = $.makeNeutralElement( "div" );
this.wrapper.style.position = "absolute";
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.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.margin = "0px";
this.wrapper.style.padding = "0px";
this.element.style.position = "relative";
this.element.style.top = "0px";
this.element.style.left = "0px";
this.element.style.height = "100%";
this.element.style.width = "100%";
} else {
this.wrapper = $.makeNeutralElement( "div" );
this.wrapper.style.display = "inline-block";
if ( this.anchor == $.ControlAnchor.NONE ) {
// IE6 fix
this.wrapper.style.width = this.wrapper.style.height = "100%";
}
}
this.wrapper.appendChild( this.element ); this.wrapper.appendChild( this.element );
if ( this.anchor == $.ControlAnchor.NONE ) { if (options.attachToViewer ) {
// IE6 fix if ( this.anchor == $.ControlAnchor.TOP_RIGHT ||
this.wrapper.style.width = this.wrapper.style.height = "100%"; this.anchor == $.ControlAnchor.BOTTOM_RIGHT ) {
} this.container.insertBefore(
this.wrapper,
if ( this.anchor == $.ControlAnchor.TOP_RIGHT || this.container.firstChild
this.anchor == $.ControlAnchor.BOTTOM_RIGHT ) { );
this.container.insertBefore( } else {
this.wrapper, this.container.appendChild( this.wrapper );
this.container.firstChild }
);
} else { } else {
this.container.appendChild( this.wrapper ); parent.appendChild( this.wrapper );
} }
}; };
$.Control.prototype = { $.Control.prototype = /** @lends OpenSeadragon.Control.prototype */{
/** /**
* Removes the control from the container. * Removes the control from the container.
@ -82,8 +178,8 @@ $.Control.prototype = {
* @param {Boolean} visible - true to make visible, false to hide. * @param {Boolean} visible - true to make visible, false to hide.
*/ */
setVisible: function( visible ) { setVisible: function( visible ) {
this.wrapper.style.display = visible ? this.wrapper.style.display = visible ?
"inline-block" : ( this.anchor == $.ControlAnchor.ABSOLUTE ? 'block' : 'inline-block' ) :
"none"; "none";
}, },

View File

@ -1,26 +1,65 @@
(function( $ ){ /*
* OpenSeadragon - ControlDock
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//id hash for private properties; (function( $ ){
var THIS = {};
/** /**
* @class * @class ControlDock
* @classdesc Provides a container element (a &lt;form&gt; element) with support for the layout of control elements.
*
* @memberof OpenSeadragon
*/ */
$.ControlDock = function( options ){ $.ControlDock = function( options ){
var layouts = [ 'topleft', 'topright', 'bottomright', 'bottomleft'], var layouts = [ 'topleft', 'topright', 'bottomright', 'bottomleft'],
layout, layout,
i; i;
$.extend( true, this, { $.extend( true, this, {
id: 'controldock-'+(+new Date())+'-'+Math.floor(Math.random()*1000000), id: 'controldock-'+$.now()+'-'+Math.floor(Math.random()*1000000),
container: $.makeNeutralElement('form'), container: $.makeNeutralElement( 'div' ),
controls: [] controls: []
}, options ); }, options );
// Disable the form's submit; otherwise button clicks and return keys
// can trigger it.
this.container.onsubmit = function() {
return false;
};
if( this.element ){ if( this.element ){
this.element = $.getElement( this.element ); this.element = $.getElement( this.element );
this.element.appendChild( this.container ); this.element.appendChild( this.container );
this.element.style.position = 'relative'; this.element.style.position = 'relative';
this.container.style.width = '100%'; this.container.style.width = '100%';
this.container.style.height = '100%'; this.container.style.height = '100%';
} }
@ -49,12 +88,12 @@
this.container.appendChild( this.controls.bottomleft ); this.container.appendChild( this.controls.bottomleft );
}; };
$.ControlDock.prototype = { $.ControlDock.prototype = /** @lends OpenSeadragon.ControlDock.prototype */{
/** /**
* @function * @function
*/ */
addControl: function ( element, anchor ) { addControl: function ( element, controlOptions ) {
element = $.getElement( element ); element = $.getElement( element );
var div = null; var div = null;
@ -62,7 +101,7 @@
return; // they're trying to add a duplicate control return; // they're trying to add a duplicate control
} }
switch ( anchor ) { switch ( controlOptions.anchor ) {
case $.ControlAnchor.TOP_RIGHT: case $.ControlAnchor.TOP_RIGHT:
div = this.controls.topright; div = this.controls.topright;
element.style.position = "relative"; element.style.position = "relative";
@ -87,6 +126,11 @@
element.style.paddingLeft = "0px"; element.style.paddingLeft = "0px";
element.style.paddingTop = "0px"; element.style.paddingTop = "0px";
break; break;
case $.ControlAnchor.ABSOLUTE:
div = this.container;
element.style.margin = "0px";
element.style.padding = "0px";
break;
default: default:
case $.ControlAnchor.NONE: case $.ControlAnchor.NONE:
div = this.container; div = this.container;
@ -96,7 +140,7 @@
} }
this.controls.push( this.controls.push(
new $.Control( element, anchor, div ) new $.Control( element, controlOptions, div )
); );
element.style.display = "inline-block"; element.style.display = "inline-block";
}, },
@ -109,7 +153,7 @@
removeControl: function ( element ) { removeControl: function ( element ) {
element = $.getElement( element ); element = $.getElement( element );
var i = getControlIndex( this, element ); var i = getControlIndex( this, element );
if ( i >= 0 ) { if ( i >= 0 ) {
this.controls[ i ].destroy(); this.controls[ i ].destroy();
this.controls.splice( i, 1 ); this.controls.splice( i, 1 );
@ -126,7 +170,7 @@
while ( this.controls.length > 0 ) { while ( this.controls.length > 0 ) {
this.controls.pop().destroy(); this.controls.pop().destroy();
} }
return this; return this;
}, },
@ -137,7 +181,7 @@
*/ */
areControlsEnabled: function () { areControlsEnabled: function () {
var i; var i;
for ( i = this.controls.length - 1; i >= 0; i-- ) { for ( i = this.controls.length - 1; i >= 0; i-- ) {
if ( this.controls[ i ].isVisible() ) { if ( this.controls[ i ].isVisible() ) {
return true; return true;
@ -181,4 +225,4 @@
return -1; return -1;
} }
}( OpenSeadragon )); }( OpenSeadragon ));

View File

@ -1,10 +1,46 @@
/*
* OpenSeadragon - DisplayRect
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function( $ ){ (function( $ ){
/** /**
* A display rectanlge is very similar to the OpenSeadragon.Rect but adds two * @class DisplayRect
* @classdesc A display rectangle is very similar to {@link OpenSeadragon.Rect} but adds two
* fields, 'minLevel' and 'maxLevel' which denote the supported zoom levels * fields, 'minLevel' and 'maxLevel' which denote the supported zoom levels
* for this rectangle. * for this rectangle.
* @class *
* @memberof OpenSeadragon
* @extends OpenSeadragon.Rect * @extends OpenSeadragon.Rect
* @param {Number} x The vector component 'x'. * @param {Number} x The vector component 'x'.
* @param {Number} y The vector component 'y'. * @param {Number} y The vector component 'y'.
@ -12,13 +48,21 @@
* @param {Number} height The vector component 'width'. * @param {Number} height The vector component 'width'.
* @param {Number} minLevel The lowest zoom level supported. * @param {Number} minLevel The lowest zoom level supported.
* @param {Number} maxLevel The highest zoom level supported. * @param {Number} maxLevel The highest zoom level supported.
* @property {Number} minLevel The lowest zoom level supported.
* @property {Number} maxLevel The highest zoom level supported.
*/ */
$.DisplayRect = function( x, y, width, height, minLevel, maxLevel ) { $.DisplayRect = function( x, y, width, height, minLevel, maxLevel ) {
$.Rect.apply( this, [ x, y, width, height ] ); $.Rect.apply( this, [ x, y, width, height ] );
/**
* The lowest zoom level supported.
* @member {Number} minLevel
* @memberof OpenSeadragon.DisplayRect#
*/
this.minLevel = minLevel; this.minLevel = minLevel;
/**
* The highest zoom level supported.
* @member {Number} maxLevel
* @memberof OpenSeadragon.DisplayRect#
*/
this.maxLevel = maxLevel; this.maxLevel = maxLevel;
}; };

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,42 @@
/*
* OpenSeadragon - DziTileSource
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function( $ ){ (function( $ ){
/** /**
* @class * @class DziTileSource
* @memberof OpenSeadragon
* @extends OpenSeadragon.TileSource * @extends OpenSeadragon.TileSource
* @param {Number|Object} width - the pixel width of the image or the idiomatic * @param {Number|Object} width - the pixel width of the image or the idiomatic
* options object which is used instead of positional arguments. * options object which is used instead of positional arguments.
@ -15,13 +49,13 @@
* @property {String} tilesUrl * @property {String} tilesUrl
* @property {String} fileFormat * @property {String} fileFormat
* @property {OpenSeadragon.DisplayRect[]} displayRects * @property {OpenSeadragon.DisplayRect[]} displayRects
*/ */
$.DziTileSource = function( width, height, tileSize, tileOverlap, tilesUrl, fileFormat, displayRects, minLevel, maxLevel ) { $.DziTileSource = function( width, height, tileSize, tileOverlap, tilesUrl, fileFormat, displayRects, minLevel, maxLevel ) {
var i, var i,
rect, rect,
level, level,
options; options;
if( $.isPlainObject( width ) ){ if( $.isPlainObject( width ) ){
options = width; options = width;
}else{ }else{
@ -33,7 +67,7 @@ $.DziTileSource = function( width, height, tileSize, tileOverlap, tilesUrl, file
tilesUrl: arguments[ 4 ], tilesUrl: arguments[ 4 ],
fileFormat: arguments[ 5 ], fileFormat: arguments[ 5 ],
displayRects: arguments[ 6 ], displayRects: arguments[ 6 ],
minLevel: arguments[ 7 ], minLevel: arguments[ 7 ],
maxLevel: arguments[ 8 ] maxLevel: arguments[ 8 ]
}; };
} }
@ -42,7 +76,7 @@ $.DziTileSource = function( width, height, tileSize, tileOverlap, tilesUrl, file
this.tilesUrl = options.tilesUrl; this.tilesUrl = options.tilesUrl;
this.fileFormat = options.fileFormat; this.fileFormat = options.fileFormat;
this.displayRects = options.displayRects; this.displayRects = options.displayRects;
if ( this.displayRects ) { if ( this.displayRects ) {
for ( i = this.displayRects.length - 1; i >= 0; i-- ) { for ( i = this.displayRects.length - 1; i >= 0; i-- ) {
rect = this.displayRects[ i ]; rect = this.displayRects[ i ];
@ -54,19 +88,18 @@ $.DziTileSource = function( width, height, tileSize, tileOverlap, tilesUrl, file
} }
} }
} }
$.TileSource.apply( this, [ options ] ); $.TileSource.apply( this, [ options ] );
}; };
$.extend( $.DziTileSource.prototype, $.TileSource.prototype, { $.extend( $.DziTileSource.prototype, $.TileSource.prototype, /** @lends OpenSeadragon.DziTileSource.prototype */{
/** /**
* Determine if the data and/or url imply the image service is supported by * Determine if the data and/or url imply the image service is supported by
* this tile source. * this tile source.
* @function * @function
* @name OpenSeadragon.DziTileSource.prototype.supports
* @param {Object|Array} data * @param {Object|Array} data
* @param {String} optional - url * @param {String} optional - url
*/ */
@ -83,21 +116,16 @@ $.extend( $.DziTileSource.prototype, $.TileSource.prototype, {
}, },
/** /**
* *
* @function * @function
* @name OpenSeadragon.DziTileSource.prototype.configure
* @param {Object|XMLDocument} data - the raw configuration * @param {Object|XMLDocument} data - the raw configuration
* @param {String} url - the url the data was retreived from if any. * @param {String} url - the url the data was retreived from if any.
* @return {Object} options - A dictionary of keyword arguments sufficient * @return {Object} options - A dictionary of keyword arguments sufficient
* to configure this tile sources constructor. * to configure this tile sources constructor.
*/ */
configure: function( data, url ){ configure: function( data, url ){
var dziPath, var options;
dziName,
tilesUrl,
options,
host;
if( !$.isPlainObject(data) ){ if( !$.isPlainObject(data) ){
@ -109,7 +137,7 @@ $.extend( $.DziTileSource.prototype, $.TileSource.prototype, {
} }
if (url && !options.tilesUrl) { if (url && !options.tilesUrl) {
options.tilesUrl = url.replace(/([^\/]+)\.dzi$/, '$1_files/'); options.tilesUrl = url.replace(/([^\/]+)\.(dzi|xml|js)$/, '$1_files/');
} }
return options; return options;
@ -118,7 +146,6 @@ $.extend( $.DziTileSource.prototype, $.TileSource.prototype, {
/** /**
* @function * @function
* @name OpenSeadragon.DziTileSource.prototype.getTileUrl
* @param {Number} level * @param {Number} level
* @param {Number} x * @param {Number} x
* @param {Number} y * @param {Number} y
@ -130,7 +157,6 @@ $.extend( $.DziTileSource.prototype, $.TileSource.prototype, {
/** /**
* @function * @function
* @name OpenSeadragon.DziTileSource.prototype.tileExists
* @param {Number} level * @param {Number} level
* @param {Number} x * @param {Number} x
* @param {Number} y * @param {Number} y
@ -183,7 +209,7 @@ $.extend( $.DziTileSource.prototype, $.TileSource.prototype, {
* @function * @function
*/ */
function configureFromXML( tileSource, xmlDoc ){ function configureFromXML( tileSource, xmlDoc ){
if ( !xmlDoc || !xmlDoc.documentElement ) { if ( !xmlDoc || !xmlDoc.documentElement ) {
throw new Error( $.getString( "Errors.Xml" ) ); throw new Error( $.getString( "Errors.Xml" ) );
} }
@ -199,7 +225,7 @@ function configureFromXML( tileSource, xmlDoc ){
i; i;
if ( rootName == "Image" ) { if ( rootName == "Image" ) {
try { try {
sizeNode = root.getElementsByTagName( "Size" )[ 0 ]; sizeNode = root.getElementsByTagName( "Size" )[ 0 ];
configuration = { configuration = {
@ -208,7 +234,7 @@ function configureFromXML( tileSource, xmlDoc ){
Url: root.getAttribute( "Url" ), Url: root.getAttribute( "Url" ),
Format: root.getAttribute( "Format" ), Format: root.getAttribute( "Format" ),
DisplayRect: null, DisplayRect: null,
Overlap: parseInt( root.getAttribute( "Overlap" ), 10 ), Overlap: parseInt( root.getAttribute( "Overlap" ), 10 ),
TileSize: parseInt( root.getAttribute( "TileSize" ), 10 ), TileSize: parseInt( root.getAttribute( "TileSize" ), 10 ),
Size: { Size: {
Height: parseInt( sizeNode.getAttribute( "Height" ), 10 ), Height: parseInt( sizeNode.getAttribute( "Height" ), 10 ),
@ -222,7 +248,7 @@ function configureFromXML( tileSource, xmlDoc ){
$.getString( "Errors.ImageFormat", configuration.Image.Format.toUpperCase() ) $.getString( "Errors.ImageFormat", configuration.Image.Format.toUpperCase() )
); );
} }
dispRectNodes = root.getElementsByTagName( "DisplayRect" ); dispRectNodes = root.getElementsByTagName( "DisplayRect" );
for ( i = 0; i < dispRectNodes.length; i++ ) { for ( i = 0; i < dispRectNodes.length; i++ ) {
dispRectNode = dispRectNodes[ i ]; dispRectNode = dispRectNodes[ i ];
@ -247,14 +273,14 @@ function configureFromXML( tileSource, xmlDoc ){
return configureFromObject( tileSource, configuration ); return configureFromObject( tileSource, configuration );
} catch ( e ) { } catch ( e ) {
throw (e instanceof Error) ? throw (e instanceof Error) ?
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" ) {
return processDZIError( root ); return $._processDZIError( root );
} }
throw new Error( $.getString( "Errors.Dzi" ) ); throw new Error( $.getString( "Errors.Dzi" ) );
@ -281,7 +307,7 @@ function configureFromObject( tileSource, configuration ){
//TODO: need to figure out out to better handle image format compatibility //TODO: need to figure out out to better handle image format compatibility
// which actually includes additional file formats like xml and pdf // which actually includes additional file formats like xml and pdf
// and plain text for various tilesource implementations to avoid low // and plain text for various tilesource implementations to avoid low
// level errors. // level errors.
// //
// For now, just don't perform the check. // For now, just don't perform the check.

View File

@ -1,114 +0,0 @@
(function($){
/**
* For use by classes which want to support custom, non-browser events.
* TODO: This is an aweful name! This thing represents an "event source",
* not an "event handler". PLEASE change the to EventSource. Also please
* change 'addHandler', 'removeHandler' and 'raiseEvent' to 'bind',
* 'unbind', and 'trigger' respectively. Finally add a method 'one' which
* automatically unbinds a listener after the first triggered event that
* matches.
* @class
*/
$.EventHandler = function() {
this.events = {};
};
$.EventHandler.prototype = {
/**
* Add an event handler for a given event.
* @function
* @param {String} eventName - Name of event to register.
* @param {Function} handler - Function to call when event is triggered.
*/
addHandler: function( eventName, handler ) {
var events = this.events[ eventName ];
if( !events ){
this.events[ eventName ] = events = [];
}
if( handler && $.isFunction( handler ) ){
events[ events.length ] = handler;
}
},
/**
* Remove a specific event handler for a given event.
* @function
* @param {String} eventName - Name of event for which the handler is to be removed.
* @param {Function} handler - Function to be removed.
*/
removeHandler: function( eventName, handler ) {
var events = this.events[ eventName ],
handlers = [],
i;
if ( !events ){
return;
}
if( $.isArray( events ) ){
for( i = 0; i < events.length; i++ ){
if( events[ i ] !== handler ){
handlers.push( handler );
}
}
this.events[ eventName ] = handlers;
}
},
/**
* Remove all event handler for a given event type.
* @function
* @param {String} eventName - Name of event for which all handlers are to be removed.
*/
removeAllHandlers: function( eventName ){
this.events[ eventName ] = [];
},
/**
* Retrive the list of all handlers registered for a given event.
* @function
* @param {String} eventName - Name of event to get handlers for.
*/
getHandler: function( eventName ) {
var events = this.events[ eventName ];
if ( !events || !events.length ){
return null;
}
events = events.length === 1 ?
[ events[ 0 ] ] :
Array.apply( null, events );
return function( source, args ) {
var i,
length = events.length;
for ( i = 0; i < length; i++ ) {
if( events[ i ] ){
events[ i ]( source, args );
}
}
};
},
/**
* Trigger an event, optionally passing additional information.
* @function
* @param {String} eventName - Name of event to register.
* @param {Function} handler - Function to call when event is triggered.
*/
raiseEvent: function( eventName, eventArgs ) {
//uncomment if you want to get a log og all events
//$.console.log( eventName );
var handler = this.getHandler( eventName );
if ( handler ) {
if ( !eventArgs ) {
eventArgs = {};
}
handler( this, eventArgs );
}
}
};
}( OpenSeadragon ));

164
src/eventsource.js Normal file
View File

@ -0,0 +1,164 @@
/*
* OpenSeadragon - EventSource
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function($){
/**
* Event handler method signature used by all OpenSeadragon events.
*
* @callback EventHandler
* @memberof OpenSeadragon
* @param {Object} event - See individual events for event-specific properties.
*/
/**
* @class EventSource
* @classdesc For use by classes which want to support custom, non-browser events.
*
* @memberof OpenSeadragon
*/
$.EventSource = function() {
this.events = {};
};
$.EventSource.prototype = /** @lends OpenSeadragon.EventSource.prototype */{
// TODO: Add a method 'one' which automatically unbinds a listener after the first triggered event that matches.
/**
* Add an event handler for a given event.
* @function
* @param {String} eventName - Name of event to register.
* @param {OpenSeadragon.EventHandler} handler - Function to call when event is triggered.
* @param {Object} [userData=null] - Arbitrary object to be passed unchanged to the handler.
*/
addHandler: function ( eventName, handler, userData ) {
var events = this.events[ eventName ];
if ( !events ) {
this.events[ eventName ] = events = [];
}
if ( handler && $.isFunction( handler ) ) {
events[ events.length ] = { handler: handler, userData: userData || null };
}
},
/**
* Remove a specific event handler for a given event.
* @function
* @param {String} eventName - Name of event for which the handler is to be removed.
* @param {OpenSeadragon.EventHandler} handler - Function to be removed.
*/
removeHandler: function ( eventName, handler ) {
var events = this.events[ eventName ],
handlers = [],
i;
if ( !events ) {
return;
}
if ( $.isArray( events ) ) {
for ( i = 0; i < events.length; i++ ) {
if ( events[i].handler !== handler ) {
handlers.push( events[ i ] );
}
}
this.events[ eventName ] = handlers;
}
},
/**
* Remove all event handlers for a given event type. If no type is given all
* event handlers for every event type are removed.
* @function
* @param {String} eventName - Name of event for which all handlers are to be removed.
*/
removeAllHandlers: function( eventName ) {
if ( eventName ){
this.events[ eventName ] = [];
} else{
for ( var eventType in this.events ) {
this.events[ eventType ] = [];
}
}
},
/**
* Get a function which iterates the list of all handlers registered for a given event, calling the handler for each.
* @function
* @param {String} eventName - Name of event to get handlers for.
*/
getHandler: function ( eventName ) {
var events = this.events[ eventName ];
if ( !events || !events.length ) {
return null;
}
events = events.length === 1 ?
[ events[ 0 ] ] :
Array.apply( null, events );
return function ( source, args ) {
var i,
length = events.length;
for ( i = 0; i < length; i++ ) {
if ( events[ i ] ) {
args.eventSource = source;
args.userData = events[ i ].userData;
events[ i ].handler( args );
}
}
};
},
/**
* Trigger an event, optionally passing additional information.
* @function
* @param {String} eventName - Name of event to register.
* @param {Object} eventArgs - Event-specific data.
*/
raiseEvent: function( eventName, eventArgs ) {
//uncomment if you want to get a log of all events
//$.console.log( eventName );
var handler = this.getHandler( eventName );
if ( handler ) {
if ( !eventArgs ) {
eventArgs = {};
}
handler( this, eventArgs );
}
}
};
}( OpenSeadragon ));

View File

@ -1,78 +1,145 @@
/** /*
* Determines the appropriate level of native full screen support we can get * OpenSeadragon - full-screen support functions
* from the browser. *
* Thanks to John Dyer for the implementation and research * Copyright (C) 2009 CodePlex Foundation
* http://johndyer.name/native-fullscreen-javascript-api-plus-jquery-plugin/ * Copyright (C) 2010-2013 OpenSeadragon contributors
* Also includes older IE support based on *
* http://stackoverflow.com/questions/1125084/how-to-make-in-javascript-full-screen-windows-stretching-all-over-the-screen/7525760 * Redistribution and use in source and binary forms, with or without
* @name $.supportsFullScreen * modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
(function( $ ) { (function( $ ) {
/**
* Determine native full screen support we can get from the browser.
* @member fullScreenApi
* @memberof OpenSeadragon
* @type {object}
* @property {Boolean} supportsFullScreen Return true if full screen API is supported.
* @property {Function} isFullScreen Return true if currently in full screen mode.
* @property {Function} getFullScreenElement Return the element currently in full screen mode.
* @property {Function} requestFullScreen Make a request to go in full screen mode.
* @property {Function} exitFullScreen Make a request to exit full screen mode.
* @property {Function} cancelFullScreen Deprecated, use exitFullScreen instead.
* @property {String} fullScreenEventName Event fired when the full screen mode change.
* @property {String} fullScreenErrorEventName Event fired when a request to go
* in full screen mode failed.
*/
var fullScreenApi = { var fullScreenApi = {
supportsFullScreen: false, supportsFullScreen: false,
isFullScreen: function() { return false; }, isFullScreen: function() { return false; },
requestFullScreen: function() {}, getFullScreenElement: function() { return null; },
cancelFullScreen: function() {}, requestFullScreen: function() {},
fullScreenEventName: '', exitFullScreen: function() {},
prefix: '' cancelFullScreen: function() {},
}, fullScreenEventName: '',
browserPrefixes = 'webkit moz o ms khtml'.split(' '); fullScreenErrorEventName: ''
};
// check for native support // check for native support
if (typeof document.cancelFullScreen != 'undefined') { if ( document.exitFullscreen ) {
// W3C standard
fullScreenApi.supportsFullScreen = true; fullScreenApi.supportsFullScreen = true;
} else { fullScreenApi.getFullScreenElement = function() {
// check for fullscreen support by vendor prefix return document.fullscreenElement;
for (var i = 0, il = browserPrefixes.length; i < il; i++ ) {
fullScreenApi.prefix = browserPrefixes[i];
if (typeof document[fullScreenApi.prefix + 'CancelFullScreen' ] != 'undefined' ) {
fullScreenApi.supportsFullScreen = true;
break;
}
}
}
// update methods to do something useful
if (fullScreenApi.supportsFullScreen) {
fullScreenApi.fullScreenEventName = fullScreenApi.prefix + 'fullscreenchange';
fullScreenApi.isFullScreen = function() {
switch (this.prefix) {
case '':
return document.fullScreen;
case 'webkit':
return document.webkitIsFullScreen;
default:
return document[this.prefix + 'FullScreen'];
}
}; };
fullScreenApi.requestFullScreen = function( element ) { fullScreenApi.requestFullScreen = function( element ) {
return (this.prefix === '') ? return element.requestFullscreen();
element.requestFullScreen() :
element[this.prefix + 'RequestFullScreen']();
}; };
fullScreenApi.cancelFullScreen = function( element ) { fullScreenApi.exitFullScreen = function() {
return (this.prefix === '') ? document.exitFullscreen();
document.cancelFullScreen() :
document[this.prefix + 'CancelFullScreen']();
}; };
} else if ( typeof window.ActiveXObject !== "undefined" ){ fullScreenApi.fullScreenEventName = "fullscreenchange";
// Older IE. fullScreenApi.fullScreenErrorEventName = "fullscreenerror";
fullScreenApi.requestFullScreen = function(){ } else if ( document.msExitFullscreen ) {
var wscript = new ActiveXObject("WScript.Shell"); // IE 11
if ( wscript !== null ) { fullScreenApi.supportsFullScreen = true;
wscript.SendKeys("{F11}"); fullScreenApi.getFullScreenElement = function() {
} return document.msFullscreenElement;
return false;
}; };
fullScreenApi.cancelFullScreen = fullScreenApi.requestFullScreen; fullScreenApi.requestFullScreen = function( element ) {
return element.msRequestFullscreen();
};
fullScreenApi.exitFullScreen = function() {
document.msExitFullscreen();
};
fullScreenApi.fullScreenEventName = "MSFullscreenChange";
fullScreenApi.fullScreenErrorEventName = "MSFullscreenError";
} else if ( document.webkitExitFullscreen ) {
// Recent webkit
fullScreenApi.supportsFullScreen = true;
fullScreenApi.getFullScreenElement = function() {
return document.webkitFullscreenElement;
};
fullScreenApi.requestFullScreen = function( element ) {
return element.webkitRequestFullscreen();
};
fullScreenApi.exitFullScreen = function() {
document.webkitExitFullscreen();
};
fullScreenApi.fullScreenEventName = "webkitfullscreenchange";
fullScreenApi.fullScreenErrorEventName = "webkitfullscreenerror";
} else if ( document.webkitCancelFullScreen ) {
// Old webkit
fullScreenApi.supportsFullScreen = true;
fullScreenApi.getFullScreenElement = function() {
return document.webkitCurrentFullScreenElement;
};
fullScreenApi.requestFullScreen = function( element ) {
return element.webkitRequestFullScreen();
};
fullScreenApi.exitFullScreen = function() {
document.webkitCancelFullScreen();
};
fullScreenApi.fullScreenEventName = "webkitfullscreenchange";
fullScreenApi.fullScreenErrorEventName = "webkitfullscreenerror";
} else if ( document.mozCancelFullScreen ) {
// Firefox
fullScreenApi.supportsFullScreen = true;
fullScreenApi.getFullScreenElement = function() {
return document.mozFullScreenElement;
};
fullScreenApi.requestFullScreen = function( element ) {
return element.mozRequestFullScreen();
};
fullScreenApi.exitFullScreen = function() {
document.mozCancelFullScreen();
};
fullScreenApi.fullScreenEventName = "mozfullscreenchange";
fullScreenApi.fullScreenErrorEventName = "mozfullscreenerror";
} }
fullScreenApi.isFullScreen = function() {
return fullScreenApi.getFullScreenElement() !== null;
};
fullScreenApi.cancelFullScreen = function() {
$.console.error("cancelFullScreen is deprecated. Use exitFullScreen instead.");
fullScreenApi.exitFullScreen();
};
// export api // export api
$.extend( $, fullScreenApi ); $.extend( $, fullScreenApi );
})( OpenSeadragon ); })( OpenSeadragon );

192
src/iiif1_1tilesource.js Normal file
View File

@ -0,0 +1,192 @@
/*
* OpenSeadragon - IIIF1_1TileSource
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function( $ ){
/**
* @class IIIF1_1TileSource
* @classdesc A client implementation of the International Image Interoperability
* Format: Image API 1.1
*
* @memberof OpenSeadragon
* @extends OpenSeadragon.TileSource
* @see http://library.stanford.edu/iiif/image-api/
*/
$.IIIF1_1TileSource = function( options ){
$.extend( true, this, options );
if ( !( this.height && this.width && this['@id'] ) ){
throw new Error( 'IIIF required parameters not provided.' );
}
if ( ( this.profile &&
this.profile == "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level0" ) ){
// what if not reporting a profile?
throw new Error( 'IIIF Image API 1.1 compliance level 1 or greater is required.' );
}
if ( this.tile_width ) {
options.tileSize = this.tile_width;
} else if ( this.tile_height ) {
options.tileSize = this.tile_height;
} else {
// use the largest of tileOptions that is smaller than the short
// dimension
var shortDim = Math.min( this.height, this.width ),
tileOptions = [256,512,1024],
smallerTiles = [];
for ( var c = 0; c < tileOptions.length; c++ ) {
if ( tileOptions[c] <= shortDim ) {
smallerTiles.push( tileOptions[c] );
}
}
if ( smallerTiles.length > 0 ) {
options.tileSize = Math.max.apply( null, smallerTiles );
} else {
// If we're smaller than 256, just use the short side.
options.tileSize = shortDim;
}
this.tile_width = options.tileSize; // So that 'full' gets used for
this.tile_height = options.tileSize; // the region below
}
if ( !options.maxLevel ) {
var mf = -1;
var scfs = this.scale_factors || this.scale_factor;
if ( scfs instanceof Array ) {
for ( var i = 0; i < scfs.length; i++ ) {
var cf = Number( scfs[i] );
if ( !isNaN( cf ) && cf > mf ) { mf = cf; }
}
}
if ( mf < 0 ) { options.maxLevel = Number( Math.ceil( Math.log( Math.max( this.width, this.height ), 2 ) ) ); }
else { options.maxLevel = mf; }
}
$.TileSource.apply( this, [ options ] );
};
$.extend( $.IIIF1_1TileSource.prototype, $.TileSource.prototype, /** @lends OpenSeadragon.IIIF1_1TileSource.prototype */{
/**
* Determine if the data and/or url imply the image service is supported by
* this tile source.
* @function
* @param {Object|Array} data
* @param {String} optional - url
*/
supports: function( data, url ) {
return ( data['@context'] &&
data['@context'] == "http://library.stanford.edu/iiif/image-api/1.1/context.json" );
},
/**
*
* @function
* @param {Object} data - the raw configuration
* @example <caption>IIIF 1.1 Info Looks like this (XML syntax is no more)</caption>
* {
* "@context" : "http://library.stanford.edu/iiif/image-api/1.1/context.json",
* "@id" : "http://iiif.example.com/prefix/1E34750D-38DB-4825-A38A-B60A345E591C",
* "width" : 6000,
* "height" : 4000,
* "scale_factors" : [ 1, 2, 4 ],
* "tile_width" : 1024,
* "tile_height" : 1024,
* "formats" : [ "jpg", "png" ],
* "qualities" : [ "native", "grey" ],
* "profile" : "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level0"
* }
*/
configure: function( data ){
return data;
},
/**
* Responsible for retreiving the url which will return an image for the
* region specified by the given x, y, and level components.
* @function
* @param {Number} level - z index
* @param {Number} x
* @param {Number} y
* @throws {Error}
*/
getTileUrl: function( level, x, y ){
//# constants
var IIIF_ROTATION = '0',
IIIF_QUALITY = 'native.jpg',
//## get the scale (level as a decimal)
scale = Math.pow( 0.5, this.maxLevel - level ),
//# image dimensions at this level
levelWidth = Math.ceil( this.width * scale ),
levelHeight = Math.ceil( this.height * scale ),
//## iiif region
iiifTileSizeWidth = Math.ceil( this.tileSize / scale ),
iiifTileSizeHeight = Math.ceil( this.tileSize / scale ),
iiifRegion,
iiifTileX,
iiifTileY,
iiifTileW,
iiifTileH,
iiifSize,
uri;
if ( levelWidth < this.tile_width && levelHeight < this.tile_height ){
iiifSize = levelWidth + ",";
iiifRegion = 'full';
} else {
iiifTileX = x * iiifTileSizeWidth;
iiifTileY = y * iiifTileSizeHeight;
iiifTileW = Math.min( iiifTileSizeWidth, this.width - iiifTileX );
iiifTileH = Math.min( iiifTileSizeHeight, this.height - iiifTileY );
iiifSize = Math.ceil( iiifTileW * scale ) + ",";
iiifRegion = [ iiifTileX, iiifTileY, iiifTileW, iiifTileH ].join( ',' );
}
uri = [ this['@id'], iiifRegion, iiifSize, IIIF_ROTATION, IIIF_QUALITY ].join( '/' );
return uri;
}
});
}( OpenSeadragon ));

View File

@ -1,14 +1,52 @@
(function( $ ){ /*
* OpenSeadragon - IIIFTileSource
/**
* A client implementation of the International Image Interoperability
* Format: Image API Draft 0.2 - Please read more about the specification
* at
* *
* The getTileUrl implementation is based on the gist from: * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* The getTileUrl implementation is based on Jon Stroop's Python version,
* which is released under the New BSD license:
* https://gist.github.com/jpstroop/4624253 * https://gist.github.com/jpstroop/4624253
*/
(function( $ ){
/**
* @class IIIFTileSource
* @classdesc A client implementation of the International Image Interoperability
* Format: Image API Draft 0.2
* *
* @class * @memberof OpenSeadragon
* @extends OpenSeadragon.TileSource * @extends OpenSeadragon.TileSource
* @see http://library.stanford.edu/iiif/image-api/ * @see http://library.stanford.edu/iiif/image-api/
*/ */
@ -27,32 +65,40 @@ $.IIIFTileSource = function( options ){
// to preserve backward compatibility. // to preserve backward compatibility.
options.tileSize = this.tile_width; options.tileSize = this.tile_width;
options.maxLevel = options.maxLevel ? options.maxLevel : Number( if (! options.maxLevel ) {
Math.ceil( Math.log( Math.max( this.width, this.height ), 2 ) ) var mf = -1;
); var scfs = this.scale_factors || this.scale_factor;
if ( scfs instanceof Array ) {
for ( var i = 0; i < scfs.length; i++ ) {
var cf = Number( scfs[i] );
if ( !isNaN( cf ) && cf > mf ) { mf = cf; }
}
}
if ( mf < 0 ) { options.maxLevel = Number(Math.ceil(Math.log(Math.max(this.width, this.height), 2))); }
else { options.maxLevel = mf; }
}
$.TileSource.apply( this, [ options ] ); $.TileSource.apply( this, [ options ] );
}; };
$.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, { $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, /** @lends OpenSeadragon.IIIFTileSource.prototype */{
/** /**
* Determine if the data and/or url imply the image service is supported by * Determine if the data and/or url imply the image service is supported by
* this tile source. * this tile source.
* @function * @method
* @name OpenSeadragon.IIIFTileSource.prototype.supports
* @param {Object|Array} data * @param {Object|Array} data
* @param {String} optional - url * @param {String} optional - url
*/ */
supports: function( data, url ){ supports: function( data, url ){
return ( return (
data.ns && data.ns &&
"http://library.stanford.edu/iiif/image-api/ns/" == data.ns "http://library.stanford.edu/iiif/image-api/ns/" == data.ns
) || ( ) || (
data.profile && ( data.profile && (
"http://library.stanford.edu/iiif/image-api/compliance.html#level1" == data.profile || "http://library.stanford.edu/iiif/image-api/compliance.html#level1" == data.profile ||
"http://library.stanford.edu/iiif/image-api/compliance.html#level2" == data.profile || "http://library.stanford.edu/iiif/image-api/compliance.html#level2" == data.profile ||
"http://library.stanford.edu/iiif/image-api/compliance.html#level3" == data.profile || "http://library.stanford.edu/iiif/image-api/compliance.html#level3" == data.profile ||
"http://library.stanford.edu/iiif/image-api/compliance.html" == data.profile "http://library.stanford.edu/iiif/image-api/compliance.html" == data.profile
) )
) || ( ) || (
data.documentElement && data.documentElement &&
@ -62,18 +108,16 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, {
); );
}, },
/** /**
* *
* @function * @method
* @name OpenSeadragon.IIIFTileSource.prototype.configure
* @param {Object|XMLDocument} data - the raw configuration * @param {Object|XMLDocument} data - the raw configuration
* @param {String} url - the url the data was retreived from if any. * @param {String} url - the url the data was retreived from if any.
* @return {Object} options - A dictionary of keyword arguments sufficient * @return {Object} options - A dictionary of keyword arguments sufficient
* to configure this tile source via it's constructor. * to configure this tile source via its constructor.
*/ */
configure: function( data, url ){ configure: function( data, url ){
var service, var service,
identifier,
options, options,
host; host;
@ -104,31 +148,30 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, {
}, },
/** /**
* Responsible for retreiving the url which will return an image for the * Responsible for retreiving the url which will return an image for the
* region speified by the given x, y, and level components. * region speified by the given x, y, and level components.
* @function * @method
* @name OpenSeadragon.IIIFTileSource.prototype.getTileUrl
* @param {Number} level - z index * @param {Number} level - z index
* @param {Number} x * @param {Number} x
* @param {Number} y * @param {Number} y
* @throws {Error} * @throws {Error}
*/ */
getTileUrl: function( level, x, y ){ getTileUrl: function( level, x, y ){
//# constants //# constants
var IIIF_ROTATION = '0', var IIIF_ROTATION = '0',
IIIF_QUALITY = 'native.jpg', IIIF_QUALITY = 'native.jpg',
//## get the scale (level as a decimal) //## get the scale (level as a decimal)
scale = Math.pow( 0.5, this.maxLevel - level ), scale = Math.pow( 0.5, this.maxLevel - level ),
//## get iiif size //## get iiif size
iiif_size = 'pct:' + ( scale * 100 ), // iiif_size = 'pct:' + ( scale * 100 ),
//# image dimensions at this level //# image dimensions at this level
level_width = Math.ceil( this.width * scale ), level_width = Math.ceil( this.width * scale ),
level_height = Math.ceil( this.height * scale ), level_height = Math.ceil( this.height * scale ),
//## iiif region //## iiif region
iiif_tile_size_width = Math.ceil( this.tileSize / scale ), iiif_tile_size_width = Math.ceil( this.tileSize / scale ),
iiif_tile_size_height = Math.ceil( this.tileSize / scale ), iiif_tile_size_height = Math.ceil( this.tileSize / scale ),
@ -136,26 +179,29 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, {
iiif_tile_x, iiif_tile_x,
iiif_tile_y, iiif_tile_y,
iiif_tile_w, iiif_tile_w,
iiif_tile_h; iiif_tile_h,
iiif_size;
if ( level_width < this.tile_width || level_height < this.tile_height ){
if ( level_width < this.tile_width && level_height < this.tile_height ){
iiif_size = level_width + ","; // + level_height; only one dim. for IIIF level 1 compliance
iiif_region = 'full'; iiif_region = 'full';
} else { } else {
iiif_tile_x = x * iiif_tile_size_width; iiif_tile_x = x * iiif_tile_size_width;
iiif_tile_y = y * iiif_tile_size_height; iiif_tile_y = y * iiif_tile_size_height;
iiif_tile_w = Math.min( iiif_tile_size_width, this.width - iiif_tile_x ); iiif_tile_w = Math.min( iiif_tile_size_width, this.width - iiif_tile_x );
iiif_tile_h = Math.min( iiif_tile_size_height, this.height - iiif_tile_y ); iiif_tile_h = Math.min( iiif_tile_size_height, this.height - iiif_tile_y );
iiif_size = Math.ceil(iiif_tile_w * scale) + ",";
iiif_region = [ iiif_tile_x, iiif_tile_y, iiif_tile_w, iiif_tile_h ].join(','); iiif_region = [ iiif_tile_x, iiif_tile_y, iiif_tile_w, iiif_tile_h ].join(',');
} }
return [ return [
this.tilesUrl, this.tilesUrl,
this.identifier, this.identifier,
iiif_region, iiif_region,
iiif_size, iiif_size,
IIIF_ROTATION, IIIF_ROTATION,
IIIF_QUALITY IIIF_QUALITY
].join('/'); ].join('/');
} }
@ -166,28 +212,28 @@ $.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, {
* @private * @private
* @inner * @inner
* @function * @function
* * @example
<?xml version="1.0" encoding="UTF-8"?> * <?xml version="1.0" encoding="UTF-8"?>
<info xmlns="http://library.stanford.edu/iiif/image-api/ns/"> * <info xmlns="http://library.stanford.edu/iiif/image-api/ns/">
<identifier>1E34750D-38DB-4825-A38A-B60A345E591C</identifier> * <identifier>1E34750D-38DB-4825-A38A-B60A345E591C</identifier>
<width>6000</width> * <width>6000</width>
<height>4000</height> * <height>4000</height>
<scale_factors> * <scale_factors>
<scale_factor>1</scale_factor> * <scale_factor>1</scale_factor>
<scale_factor>2</scale_factor> * <scale_factor>2</scale_factor>
<scale_factor>4</scale_factor> * <scale_factor>4</scale_factor>
</scale_factors> * </scale_factors>
<tile_width>1024</tile_width> * <tile_width>1024</tile_width>
<tile_height>1024</tile_height> * <tile_height>1024</tile_height>
<formats> * <formats>
<format>jpg</format> * <format>jpg</format>
<format>png</format> * <format>png</format>
</formats> * </formats>
<qualities> * <qualities>
<quality>native</quality> * <quality>native</quality>
<quality>grey</quality> * <quality>grey</quality>
</qualities> * </qualities>
</info> * </info>
*/ */
function configureFromXml( tileSource, xmlDoc ){ function configureFromXml( tileSource, xmlDoc ){
@ -198,14 +244,10 @@ function configureFromXml( tileSource, xmlDoc ){
var root = xmlDoc.documentElement, var root = xmlDoc.documentElement,
rootName = root.tagName, rootName = root.tagName,
configuration = null, configuration = null;
scale_factors,
formats,
qualities,
i;
if ( rootName == "info" ) { if ( rootName == "info" ) {
try { try {
configuration = { configuration = {
@ -217,8 +259,8 @@ function configureFromXml( tileSource, xmlDoc ){
return configureFromObject( tileSource, configuration ); return configureFromObject( tileSource, configuration );
} catch ( e ) { } catch ( e ) {
throw (e instanceof Error) ? throw (e instanceof Error) ?
e : e :
new Error( $.getString("Errors.IIIF") ); new Error( $.getString("Errors.IIIF") );
} }
} }
@ -261,18 +303,18 @@ function parseXML( node, configuration, property ){
* @private * @private
* @inner * @inner
* @function * @function
* * @example
{ * {
"profile" : "http://library.stanford.edu/iiif/image-api/compliance.html#level1", * "profile" : "http://library.stanford.edu/iiif/image-api/compliance.html#level1",
"identifier" : "1E34750D-38DB-4825-A38A-B60A345E591C", * "identifier" : "1E34750D-38DB-4825-A38A-B60A345E591C",
"width" : 6000, * "width" : 6000,
"height" : 4000, * "height" : 4000,
"scale_factors" : [ 1, 2, 4 ], * "scale_factors" : [ 1, 2, 4 ],
"tile_width" : 1024, * "tile_width" : 1024,
"tile_height" : 1024, * "tile_height" : 1024,
"formats" : [ "jpg", "png" ], * "formats" : [ "jpg", "png" ],
"quality" : [ "native", "grey" ] * "quality" : [ "native", "grey" ]
} * }
*/ */
function configureFromObject( tileSource, configuration ){ function configureFromObject( tileSource, configuration ){
//the image_host property is not part of the iiif standard but is included here to //the image_host property is not part of the iiif standard but is included here to
@ -284,4 +326,4 @@ function configureFromObject( tileSource, configuration ){
return configuration; return configuration;
} }
}( OpenSeadragon )); }( OpenSeadragon ));

View File

@ -1,13 +1,49 @@
/*
* OpenSeadragon - LegacyTileSource
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function( $ ){ (function( $ ){
/** /**
* The LegacyTileSource allows simple, traditional image pyramids to be loaded * @class LegacyTileSource
* @classdesc The LegacyTileSource allows simple, traditional image pyramids to be loaded
* into an OpenSeadragon Viewer. Basically, this translates to the historically * into an OpenSeadragon Viewer. Basically, this translates to the historically
* common practice of starting with a 'master' image, maybe a tiff for example, * common practice of starting with a 'master' image, maybe a tiff for example,
* and generating a set of 'service' images like one or more thumbnails, a medium * and generating a set of 'service' images like one or more thumbnails, a medium
* resolution image and a high resolution image in standard web formats like * resolution image and a high resolution image in standard web formats like
* png or jpg. * png or jpg.
* @class *
* @memberof OpenSeadragon
* @extends OpenSeadragon.TileSource * @extends OpenSeadragon.TileSource
* @param {Array} levels An array of file descriptions, each is an object with * @param {Array} levels An array of file descriptions, each is an object with
* a 'url', a 'width', and a 'height'. Overriding classes can expect more * a 'url', a 'width', and a 'height'. Overriding classes can expect more
@ -21,7 +57,7 @@
* @property {Number} minLevel * @property {Number} minLevel
* @property {Number} maxLevel * @property {Number} maxLevel
* @property {Array} levels * @property {Array} levels
*/ */
$.LegacyTileSource = function( levels ) { $.LegacyTileSource = function( levels ) {
var options, var options,
@ -37,35 +73,42 @@ $.LegacyTileSource = function( levels ) {
//clean up the levels to make sure we support all formats //clean up the levels to make sure we support all formats
options.levels = filterFiles( options.levels ); options.levels = filterFiles( options.levels );
width = options.levels[ options.levels.length - 1 ].width;
height = options.levels[ options.levels.length - 1 ].height;
$.extend( true, options, { if ( options.levels.length > 0 ) {
width: width, width = options.levels[ options.levels.length - 1 ].width;
height: height, height = options.levels[ options.levels.length - 1 ].height;
tileSize: Math.max( height, width ), }
else {
width = 0;
height = 0;
$.console.error( "No supported image formats found" );
}
$.extend( true, options, {
width: width,
height: height,
tileSize: Math.max( height, width ),
tileOverlap: 0, tileOverlap: 0,
minLevel: 0, minLevel: 0,
maxLevel: options.levels.length - 1 maxLevel: options.levels.length > 0 ? options.levels.length - 1 : 0
}); } );
$.TileSource.apply( this, [ options ] ); $.TileSource.apply( this, [ options ] );
this.levels = options.levels; this.levels = options.levels;
}; };
$.extend( $.LegacyTileSource.prototype, $.TileSource.prototype, { $.extend( $.LegacyTileSource.prototype, $.TileSource.prototype, /** @lends OpenSeadragon.LegacyTileSource.prototype */{
/** /**
* Determine if the data and/or url imply the image service is supported by * Determine if the data and/or url imply the image service is supported by
* this tile source. * this tile source.
* @function * @function
* @name OpenSeadragon.LegacyTileSource.prototype.supports
* @param {Object|Array} data * @param {Object|Array} data
* @param {String} optional - url * @param {String} optional - url
*/ */
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 &&
@ -75,12 +118,11 @@ $.extend( $.LegacyTileSource.prototype, $.TileSource.prototype, {
/** /**
* *
* @function * @function
* @name OpenSeadragon.LegacyTileSource.prototype.configure
* @param {Object|XMLDocument} configuration - the raw configuration * @param {Object|XMLDocument} configuration - the raw configuration
* @param {String} dataUrl - the url the data was retreived from if any. * @param {String} dataUrl - the url the data was retreived from if any.
* @return {Object} options - A dictionary of keyword arguments sufficient * @return {Object} options - A dictionary of keyword arguments sufficient
* to configure this tile sources constructor. * to configure this tile sources constructor.
*/ */
configure: function( configuration, dataUrl ){ configure: function( configuration, dataUrl ){
@ -99,25 +141,23 @@ $.extend( $.LegacyTileSource.prototype, $.TileSource.prototype, {
return options; return options;
}, },
/** /**
* @function * @function
* @name OpenSeadragon.LegacyTileSource.prototype.getLevelScale
* @param {Number} level * @param {Number} level
*/ */
getLevelScale: function( level ) { getLevelScale: function ( level ) {
var levelScale = NaN; var levelScale = NaN;
if ( level >= this.minLevel && level <= this.maxLevel ){ if ( this.levels.length > 0 && level >= this.minLevel && level <= this.maxLevel ) {
levelScale = levelScale =
this.levels[ level ].width / this.levels[ level ].width /
this.levels[ this.maxLevel ].width; this.levels[ this.maxLevel ].width;
} }
return levelScale; return levelScale;
}, },
/** /**
* @function * @function
* @name OpenSeadragon.LegacyTileSource.prototype.getNumTiles
* @param {Number} level * @param {Number} level
*/ */
getNumTiles: function( level ) { getNumTiles: function( level ) {
@ -131,7 +171,6 @@ $.extend( $.LegacyTileSource.prototype, $.TileSource.prototype, {
/** /**
* @function * @function
* @name OpenSeadragon.LegacyTileSource.prototype.getTileAtPoint
* @param {Number} level * @param {Number} level
* @param {OpenSeadragon.Point} point * @param {OpenSeadragon.Point} point
*/ */
@ -142,24 +181,23 @@ $.extend( $.LegacyTileSource.prototype, $.TileSource.prototype, {
/** /**
* This method is not implemented by this class other than to throw an Error * This method is not implemented by this class other than to throw an Error
* announcing you have to implement it. Because of the variety of tile * announcing you have to implement it. Because of the variety of tile
* server technologies, and various specifications for building image * server technologies, and various specifications for building image
* pyramids, this method is here to allow easy integration. * pyramids, this method is here to allow easy integration.
* @function * @function
* @name OpenSeadragon.LegacyTileSource.prototype.getTileUrl
* @param {Number} level * @param {Number} level
* @param {Number} x * @param {Number} x
* @param {Number} y * @param {Number} y
* @throws {Error} * @throws {Error}
*/ */
getTileUrl: function( level, x, y ) { getTileUrl: function ( level, x, y ) {
var url = null; var url = null;
if( level >= this.minLevel && level <= this.maxLevel ){ if ( this.levels.length > 0 && level >= this.minLevel && level <= this.maxLevel ) {
url = this.levels[ level ].url; url = this.levels[ level ].url;
} }
return url; return url;
} }
}); } );
/** /**
* This method removes any files from the Array which dont conform to our * This method removes any files from the Array which dont conform to our
@ -174,12 +212,12 @@ function filterFiles( files ){
i; i;
for( i = 0; i < files.length; i++ ){ for( i = 0; i < files.length; i++ ){
file = files[ i ]; file = files[ i ];
if( file.height && if( file.height &&
file.width && file.width &&
file.url && ( file.url && (
file.url.toLowerCase().match(/^.*\.(png|jpg|jpeg|gif)$/) || ( file.url.toLowerCase().match(/^.*\.(png|jpg|jpeg|gif)$/) || (
file.mimetype && file.mimetype &&
file.mimetype.toLowerCase().match(/^.*\/(png|jpg|jpeg|gif)$/) file.mimetype.toLowerCase().match(/^.*\/(png|jpg|jpeg|gif)$/)
) )
) ){ ) ){
//This is sufficient to serve as a level //This is sufficient to serve as a level
@ -189,6 +227,9 @@ function filterFiles( files ){
height: Number( file.height ) height: Number( file.height )
}); });
} }
else {
$.console.error( 'Unsupported image format: %s', file.url ? file.url : '<no URL>' );
}
} }
return filtered.sort(function(a,b){ return filtered.sort(function(a,b){
@ -203,7 +244,7 @@ function filterFiles( files ){
* @function * @function
*/ */
function configureFromXML( tileSource, xmlDoc ){ function configureFromXML( tileSource, xmlDoc ){
if ( !xmlDoc || !xmlDoc.documentElement ) { if ( !xmlDoc || !xmlDoc.documentElement ) {
throw new Error( $.getString( "Errors.Xml" ) ); throw new Error( $.getString( "Errors.Xml" ) );
} }
@ -216,13 +257,13 @@ function configureFromXML( tileSource, xmlDoc ){
i; i;
if ( rootName == "image" ) { if ( rootName == "image" ) {
try { try {
conf = { conf = {
type: root.getAttribute( "type" ), type: root.getAttribute( "type" ),
levels: [] levels: []
}; };
levels = root.getElementsByTagName( "level" ); levels = root.getElementsByTagName( "level" );
for ( i = 0; i < levels.length; i++ ) { for ( i = 0; i < levels.length; i++ ) {
level = levels[ i ]; level = levels[ i ];
@ -237,8 +278,8 @@ function configureFromXML( tileSource, xmlDoc ){
return configureFromObject( tileSource, conf ); return configureFromObject( tileSource, conf );
} catch ( e ) { } catch ( e ) {
throw (e instanceof Error) ? throw (e instanceof Error) ?
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" ) {
@ -256,7 +297,7 @@ function configureFromXML( tileSource, xmlDoc ){
* @function * @function
*/ */
function configureFromObject( tileSource, configuration ){ function configureFromObject( tileSource, configuration ){
return configuration.levels; return configuration.levels;
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,32 +1,98 @@
/*
* OpenSeadragon - Navigator
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function( $ ){ (function( $ ){
/** /**
* The Navigator provides a small view of the current image as fixed * @class Navigator
* @classdesc The Navigator provides a small view of the current image as fixed
* while representing the viewport as a moving box serving as a frame * while representing the viewport as a moving box serving as a frame
* of reference in the larger viewport as to which portion of the image * of reference in the larger viewport as to which portion of the image
* is currently being examined. The navigator's viewport can be interacted * is currently being examined. The navigator's viewport can be interacted
* with using the keyboard or the mouse. * with using the keyboard or the mouse.
* @class *
* @name OpenSeadragon.Navigator * @memberof OpenSeadragon
* @extends OpenSeadragon.Viewer * @extends OpenSeadragon.Viewer
* @extends OpenSeadragon.EventHandler * @extends OpenSeadragon.EventSource
* @param {Object} options * @param {Object} options
* @param {String} options.viewerId
*/ */
$.Navigator = function( options ){ $.Navigator = function( options ){
var _this = this, var viewer = options.viewer,
viewer = options.viewer, viewerSize,
viewerSize = $.getElementSize( viewer.element ); navigatorSize,
unneededElement;
//We may need to create a new element and id if they did not //We may need to create a new element and id if they did not
//provide the id for the existing element //provide the id for the existing element
if( !options.id ){ if( !options.id ){
options.id = 'navigator-' + (+new Date()); options.id = 'navigator-' + $.now();
this.element = $.makeNeutralElement( "div" ); this.element = $.makeNeutralElement( "div" );
this.element.id = options.id; options.controlOptions = {
this.element.className = 'navigator'; anchor: $.ControlAnchor.TOP_RIGHT,
attachToViewer: true,
autoFade: true
};
if( options.position ){
if( 'BOTTOM_RIGHT' == options.position ){
options.controlOptions.anchor = $.ControlAnchor.BOTTOM_RIGHT;
} else if( 'BOTTOM_LEFT' == options.position ){
options.controlOptions.anchor = $.ControlAnchor.BOTTOM_LEFT;
} else if( 'TOP_RIGHT' == options.position ){
options.controlOptions.anchor = $.ControlAnchor.TOP_RIGHT;
} else if( 'TOP_LEFT' == options.position ){
options.controlOptions.anchor = $.ControlAnchor.TOP_LEFT;
} else if( 'ABSOLUTE' == options.position ){
options.controlOptions.anchor = $.ControlAnchor.ABSOLUTE;
options.controlOptions.top = options.top;
options.controlOptions.left = options.left;
options.controlOptions.height = options.height;
options.controlOptions.width = options.width;
}
}
} else {
this.element = document.getElementById( options.id );
options.controlOptions = {
anchor: $.ControlAnchor.NONE,
attachToViewer: false,
autoFade: false
};
} }
this.element.id = options.id;
this.element.className += ' navigator';
options = $.extend( true, { options = $.extend( true, {
sizeRatio: $.DEFAULT_SETTINGS.navigatorSizeRatio sizeRatio: $.DEFAULT_SETTINGS.navigatorSizeRatio
@ -40,139 +106,71 @@ $.Navigator = function( options ){
showSequenceControl: false, showSequenceControl: false,
immediateRender: true, immediateRender: true,
blendTime: 0, blendTime: 0,
animationTime: 0 animationTime: 0,
autoResize: options.autoResize
}); });
options.minPixelRatio = this.minPixelRatio = viewer.minPixelRatio; options.minPixelRatio = this.minPixelRatio = viewer.minPixelRatio;
(function( style ){ this.borderWidth = 2;
style.marginTop = '0px'; //At some browser magnification levels the display regions lines up correctly, but at some there appears to
style.marginRight = '0px'; //be a one pixel gap.
style.marginBottom = '0px'; this.fudge = new $.Point(1, 1);
style.marginLeft = '0px'; this.totalBorderWidths = new $.Point(this.borderWidth*2, this.borderWidth*2).minus(this.fudge);
style.border = '2px solid #555';
style.background = '#000';
style.opacity = 0.8;
style.overflow = 'hidden';
}( this.element.style ));
this.displayRegion = $.makeNeutralElement( "textarea" );
if ( options.controlOptions.anchor != $.ControlAnchor.NONE ) {
(function( style, borderWidth ){
style.margin = '0px';
style.border = borderWidth + 'px solid #555';
style.padding = '0px';
style.background = '#000';
style.opacity = 0.8;
style.overflow = 'hidden';
}( this.element.style, this.borderWidth));
}
this.displayRegion = $.makeNeutralElement( "div" );
this.displayRegion.id = this.element.id + '-displayregion'; this.displayRegion.id = this.element.id + '-displayregion';
this.displayRegion.className = 'displayregion'; this.displayRegion.className = 'displayregion';
(function( style ){ (function( style, borderWidth ){
style.position = 'relative'; style.position = 'relative';
style.top = '0px'; style.top = '0px';
style.left = '0px'; style.left = '0px';
style.fontSize = '0px'; style.fontSize = '0px';
style.overflow = 'hidden'; style.overflow = 'hidden';
style.border = '2px solid #900'; style.border = borderWidth + 'px solid #900';
style.margin = '0px';
style.padding = '0px';
//TODO: IE doesnt like this property being set //TODO: IE doesnt like this property being set
//try{ style.outline = '2px auto #909'; }catch(e){/*ignore*/} //try{ style.outline = '2px auto #909'; }catch(e){/*ignore*/}
style.background = 'transparent'; style.background = 'transparent';
// We use square bracket notation on the statement below, because float is a keyword. // We use square bracket notation on the statement below, because float is a keyword.
// This is important for the Google Closure compiler, if nothing else. // This is important for the Google Closure compiler, if nothing else.
/*jshint sub:true */ /*jshint sub:true */
style['float'] = 'left'; //Webkit style['float'] = 'left'; //Webkit
style.cssFloat = 'left'; //Firefox style.cssFloat = 'left'; //Firefox
style.styleFloat = 'left'; //IE style.styleFloat = 'left'; //IE
style.zIndex = 999999999; style.zIndex = 999999999;
style.cursor = 'default'; style.cursor = 'default';
}( this.displayRegion.style )); }( this.displayRegion.style, this.borderWidth ));
this.element.innerTracker = new $.MouseTracker({ this.element.innerTracker = new $.MouseTracker({
element: this.element, element: this.element,
scrollHandler: function(){ dragHandler: $.delegate( this, onCanvasDrag ),
//dont scroll the page up and down if the user is scrolling clickHandler: $.delegate( this, onCanvasClick ),
//in the navigator releaseHandler: $.delegate( this, onCanvasRelease ),
return false; scrollHandler: $.delegate( this, onCanvasScroll )
}
}).setTracking( true ); }).setTracking( true );
this.displayRegion.innerTracker = new $.MouseTracker({
element: this.displayRegion,
clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold,
clickHandler: $.delegate( this, onCanvasClick ),
dragHandler: $.delegate( this, onCanvasDrag ),
releaseHandler: $.delegate( this, onCanvasRelease ),
scrollHandler: $.delegate( this, onCanvasScroll ),
focusHandler: function(){
var point = $.getElementPosition( _this.viewer.element );
window.scrollTo( 0, point.y );
_this.viewer.setControlsEnabled( true );
(function( style ){
style.border = '2px solid #437AB2';
//style.outline = '2px auto #437AB2';
}( this.element.style ));
},
blurHandler: function(){
_this.viewer.setControlsEnabled( false );
(function( style ){
style.border = '2px solid #900';
//style.outline = '2px auto #900';
}( this.element.style ));
},
keyHandler: function(tracker, keyCode, shiftKey){
//console.log( keyCode );
switch( keyCode ){
case 61://=|+
_this.viewer.viewport.zoomBy(1.1);
_this.viewer.viewport.applyConstraints();
return false;
case 45://-|_
_this.viewer.viewport.zoomBy(0.9);
_this.viewer.viewport.applyConstraints();
return false;
case 48://0|)
_this.viewer.viewport.goHome();
_this.viewer.viewport.applyConstraints();
return false;
case 119://w
case 87://W
case 38://up arrow
if (shiftKey)
_this.viewer.viewport.zoomBy(1.1);
else
_this.viewer.viewport.panBy(new $.Point(0, -0.05));
_this.viewer.viewport.applyConstraints();
return false;
case 115://s
case 83://S
case 40://down arrow
if (shiftKey)
_this.viewer.viewport.zoomBy(0.9);
else
_this.viewer.viewport.panBy(new $.Point(0, 0.05));
_this.viewer.viewport.applyConstraints();
return false;
case 97://a
case 37://left arrow
_this.viewer.viewport.panBy(new $.Point(-0.05, 0));
_this.viewer.viewport.applyConstraints();
return false;
case 100://d
case 39://right arrow
_this.viewer.viewport.panBy(new $.Point(0.05, 0));
_this.viewer.viewport.applyConstraints();
return false;
default:
//console.log( 'navigator keycode %s', keyCode );
return true;
}
}
}).setTracking( true ); // default state
/*this.displayRegion.outerTracker = new $.MouseTracker({ /*this.displayRegion.outerTracker = new $.MouseTracker({
element: this.container, element: this.container,
clickTimeThreshold: this.clickTimeThreshold, clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold, clickDistThreshold: this.clickDistThreshold,
enterHandler: $.delegate( this, onContainerEnter ), enterHandler: $.delegate( this, onContainerEnter ),
exitHandler: $.delegate( this, onContainerExit ), exitHandler: $.delegate( this, onContainerExit ),
@ -180,66 +178,131 @@ $.Navigator = function( options ){
}).setTracking( this.mouseNavEnabled ? true : false ); // always tracking*/ }).setTracking( this.mouseNavEnabled ? true : false ); // always tracking*/
viewer.addControl( viewer.addControl(
this.element, this.element,
$.ControlAnchor.TOP_RIGHT options.controlOptions
); );
if( options.width && options.height ){ if ( options.controlOptions.anchor != $.ControlAnchor.ABSOLUTE && options.controlOptions.anchor != $.ControlAnchor.NONE ) {
this.element.style.width = options.width + 'px'; if ( options.width && options.height ) {
this.element.style.height = options.height + 'px'; this.element.style.height = typeof ( options.height ) == "number" ? ( options.height + 'px' ) : options.height;
} else { this.element.style.width = typeof ( options.width ) == "number" ? ( options.width + 'px' ) : options.width;
this.element.style.width = ( viewerSize.x * options.sizeRatio ) + 'px'; } else {
this.element.style.height = ( viewerSize.y * options.sizeRatio ) + 'px'; viewerSize = $.getElementSize( viewer.element );
this.element.style.height = ( viewerSize.y * options.sizeRatio ) + 'px';
this.element.style.width = ( viewerSize.x * options.sizeRatio ) + 'px';
this.oldViewerSize = viewerSize;
}
navigatorSize = $.getElementSize( this.element );
this.elementArea = navigatorSize.x * navigatorSize.y;
} }
$.Viewer.apply( this, [ options ] ); this.oldContainerSize = new $.Point( 0, 0 );
this.element.getElementsByTagName('form')[0].appendChild( this.displayRegion ); $.Viewer.apply( this, [ options ] );
this.element.getElementsByTagName( 'div' )[0].appendChild( this.displayRegion );
unneededElement = this.element.getElementsByTagName('textarea')[0];
if (unneededElement) {
unneededElement.parentNode.removeChild(unneededElement);
}
}; };
$.extend( $.Navigator.prototype, $.EventHandler.prototype, $.Viewer.prototype, { $.extend( $.Navigator.prototype, $.EventSource.prototype, $.Viewer.prototype, /** @lends OpenSeadragon.Navigator.prototype */{
/** /**
* Used to notify the navigator when its size has changed.
* Especially useful when {@link OpenSeadragon.Options}.navigatorAutoResize is set to false and the navigator is resizable.
* @function * @function
* @name OpenSeadragon.Navigator.prototype.update
*/ */
update: function( viewport ){ updateSize: function () {
if ( this.viewport ) {
var containerSize = new $.Point(
(this.container.clientWidth === 0 ? 1 : this.container.clientWidth),
(this.container.clientHeight === 0 ? 1 : this.container.clientHeight)
);
if ( !containerSize.equals( this.oldContainerSize ) ) {
var oldBounds = this.viewport.getBounds();
var oldCenter = this.viewport.getCenter();
this.viewport.resize( containerSize, true );
var imageHeight = 1 / this.source.aspectRatio;
var newWidth = oldBounds.width <= 1 ? oldBounds.width : 1;
var newHeight = oldBounds.height <= imageHeight ?
oldBounds.height : imageHeight;
var newBounds = new $.Rect(
oldCenter.x - ( newWidth / 2.0 ),
oldCenter.y - ( newHeight / 2.0 ),
newWidth,
newHeight
);
this.viewport.fitBounds( newBounds, true );
this.oldContainerSize = containerSize;
this.drawer.update();
}
}
},
var bounds, /**
* Used to update the navigator minimap's viewport rectangle when a change in the viewer's viewport occurs.
* @function
* @param {OpenSeadragon.Viewport} The viewport this navigator is tracking.
*/
update: function( viewport ) {
var viewerSize,
newWidth,
newHeight,
bounds,
topleft, topleft,
bottomright; bottomright;
if( viewport && this.viewport ){ viewerSize = $.getElementSize( this.viewer.element );
bounds = viewport.getBounds( true ); if ( !viewerSize.equals( this.oldViewerSize ) ) {
topleft = this.viewport.pixelFromPoint( bounds.getTopLeft() ); this.oldViewerSize = viewerSize;
bottomright = this.viewport.pixelFromPoint( bounds.getBottomRight() ); if ( this.maintainSizeRatio ) {
newWidth = viewerSize.x * this.sizeRatio;
newHeight = viewerSize.y * this.sizeRatio;
}
else {
newWidth = Math.sqrt(this.elementArea * (viewerSize.x / viewerSize.y));
newHeight = this.elementArea / newWidth;
}
this.element.style.width = newWidth + 'px';
this.element.style.height = newHeight + 'px';
this.updateSize();
}
//update style for navigator-box if( viewport && this.viewport ) {
(function(style){ bounds = viewport.getBounds( true );
topleft = this.viewport.pixelFromPoint( bounds.getTopLeft(), false );
bottomright = this.viewport.pixelFromPoint( bounds.getBottomRight(), false ).minus( this.totalBorderWidths );
//update style for navigator-box
(function(style) {
style.top = topleft.y + 'px'; style.top = topleft.y + 'px';
style.left = topleft.x + 'px'; style.left = topleft.x + 'px';
var width = Math.abs( topleft.x - bottomright.x ) - 3; // TODO: What does this magic number mean? var width = Math.abs( topleft.x - bottomright.x );
var height = Math.abs( topleft.y - bottomright.y ) - 3; var height = Math.abs( topleft.y - bottomright.y );
// make sure width and height are non-negative so IE doesn't throw // make sure width and height are non-negative so IE doesn't throw
style.width = Math.max( width, 0 ) + 'px'; style.width = Math.max( width, 0 ) + 'px';
style.height = Math.max( height, 0 ) + 'px'; style.height = Math.max( height, 0 ) + 'px';
}( this.displayRegion.style )); }( this.displayRegion.style ));
} }
}, },
open: function( source ){ open: function( source ) {
this.updateSize();
var containerSize = this.viewer.viewport.containerSize.times( this.sizeRatio ); var containerSize = this.viewer.viewport.containerSize.times( this.sizeRatio );
if( source.tileSize > containerSize.x || if( source.tileSize > containerSize.x ||
source.tileSize > containerSize.y ){ source.tileSize > containerSize.y ){
this.minPixelRatio = Math.min( this.minPixelRatio = Math.min(
containerSize.x, containerSize.x,
containerSize.y containerSize.y
) / source.tileSize; ) / source.tileSize;
} else { } else {
this.minPixelRatio = this.viewer.minPixelRatio; this.minPixelRatio = this.viewer.minPixelRatio;
@ -249,34 +312,44 @@ $.extend( $.Navigator.prototype, $.EventHandler.prototype, $.Viewer.prototype, {
}); });
/** /**
* @private * @private
* @inner * @inner
* @function * @function
*/ */
function onCanvasClick( tracker, position, quick, shift ) { function onCanvasClick( event ) {
this.displayRegion.focus(); var newBounds,
viewerPosition,
dimensions;
if (! this.drag) {
if ( this.viewer.viewport ) {
this.viewer.viewport.panTo( this.viewport.pointFromPixel( event.position ) );
this.viewer.viewport.applyConstraints();
}
}
else {
this.drag = false;
}
} }
/** /**
* @private * @private
* @inner * @inner
* @function * @function
*/ */
function onCanvasDrag( tracker, position, delta, shift ) { function onCanvasDrag( event ) {
if ( this.viewer.viewport ) { if ( this.viewer.viewport ) {
this.drag = true;
if( !this.panHorizontal ){ if( !this.panHorizontal ){
delta.x = 0; event.delta.x = 0;
} }
if( !this.panVertical ){ if( !this.panVertical ){
delta.y = 0; event.delta.y = 0;
} }
this.viewer.viewport.panBy( this.viewer.viewport.panBy(
this.viewport.deltaPointsFromPixels( this.viewport.deltaPointsFromPixels(
delta event.delta
) )
); );
} }
} }
@ -287,8 +360,8 @@ function onCanvasDrag( tracker, position, delta, shift ) {
* @inner * @inner
* @function * @function
*/ */
function onCanvasRelease( tracker, position, insideElementPress, insideElementRelease ) { function onCanvasRelease( event ) {
if ( insideElementPress && this.viewer.viewport ) { if ( event.insideElementPressed && this.viewer.viewport ) {
this.viewer.viewport.applyConstraints(); this.viewer.viewport.applyConstraints();
} }
} }
@ -299,18 +372,31 @@ function onCanvasRelease( tracker, position, insideElementPress, insideElementRe
* @inner * @inner
* @function * @function
*/ */
function onCanvasScroll( tracker, position, scroll, shift ) { function onCanvasScroll( event ) {
var factor; /**
if ( this.viewer.viewport ) { * Raised when a scroll event occurs on the {@link OpenSeadragon.Viewer#navigator} element (mouse wheel, touch pinch, etc.).
factor = Math.pow( this.zoomPerScroll, scroll ); *
this.viewer.viewport.zoomBy( * @event navigator-scroll
factor, * @memberof OpenSeadragon.Viewer
//this.viewport.pointFromPixel( position, true ) * @type {object}
this.viewport.getCenter() * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
); * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.
this.viewer.viewport.applyConstraints(); * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.
} * @property {Number} scroll - The scroll delta for the event.
//cancels event * @property {Boolean} shift - True if the shift key was pressed during this event.
* @property {Object} originalEvent - The original DOM event.
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.viewer.raiseEvent( 'navigator-scroll', {
tracker: event.eventSource,
position: event.position,
scroll: event.scroll,
shift: event.shift,
originalEvent: event.originalEvent
});
//dont scroll the page up and down if the user is scrolling
//in the navigator
return false; return false;
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +1,63 @@
/*
* OpenSeadragon - OsmTileSource
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Derived from the OSM tile source in Rainer Simon's seajax-utils project
* <http://github.com/rsimon/seajax-utils>. Rainer Simon has contributed
* the included code to the OpenSeadragon project under the New BSD license;
* see <https://github.com/openseadragon/openseadragon/issues/58>.
*/
(function( $ ){ (function( $ ){
/** /**
* A tilesource implementation for OpenStreetMap. Adopted from Rainer Simon * @class OsmTileSource
* project http://github.com/rsimon/seajax-utils. * @classdesc A tilesource implementation for OpenStreetMap.<br><br>
* *
* Note 1. Zoomlevels. Deep Zoom and OSM define zoom levels differently. In Deep * Note 1. Zoomlevels. Deep Zoom and OSM define zoom levels differently. In Deep
* Zoom, level 0 equals an image of 1x1 pixels. In OSM, level 0 equals an image of * Zoom, level 0 equals an image of 1x1 pixels. In OSM, level 0 equals an image of
* 256x256 levels (see http://gasi.ch/blog/inside-deep-zoom-2). I.e. there is a * 256x256 levels (see http://gasi.ch/blog/inside-deep-zoom-2). I.e. there is a
* difference of log2(256)=8 levels. * difference of log2(256)=8 levels.<br><br>
* *
* Note 2. Image dimension. According to the OSM Wiki * Note 2. Image dimension. According to the OSM Wiki
* (http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Zoom_levels) * (http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Zoom_levels)
* the highest Mapnik zoom level has 256.144x256.144 tiles, with a 256x256 * the highest Mapnik zoom level has 256.144x256.144 tiles, with a 256x256
* pixel size. I.e. the Deep Zoom image dimension is 65.572.864x65.572.864 * pixel size. I.e. the Deep Zoom image dimension is 65.572.864x65.572.864
* pixels. * pixels.
* *
* @class * @memberof OpenSeadragon
* @extends OpenSeadragon.TileSource * @extends OpenSeadragon.TileSource
* @param {Number|Object} width - the pixel width of the image or the idiomatic * @param {Number|Object} width - the pixel width of the image or the idiomatic
* options object which is used instead of positional arguments. * options object which is used instead of positional arguments.
@ -23,7 +65,7 @@
* @param {Number} tileSize * @param {Number} tileSize
* @param {Number} tileOverlap * @param {Number} tileOverlap
* @param {String} tilesUrl * @param {String} tilesUrl
*/ */
$.OsmTileSource = function( width, height, tileSize, tileOverlap, tilesUrl ) { $.OsmTileSource = function( width, height, tileSize, tileOverlap, tilesUrl ) {
var options; var options;
@ -53,36 +95,34 @@ $.OsmTileSource = function( width, height, tileSize, tileOverlap, tilesUrl ) {
options.tilesUrl = "http://tile.openstreetmap.org/"; options.tilesUrl = "http://tile.openstreetmap.org/";
} }
options.minLevel = 8; options.minLevel = 8;
$.TileSource.apply( this, [ options ] ); $.TileSource.apply( this, [ options ] );
}; };
$.extend( $.OsmTileSource.prototype, $.TileSource.prototype, { $.extend( $.OsmTileSource.prototype, $.TileSource.prototype, /** @lends OpenSeadragon.OsmTileSource.prototype */{
/** /**
* Determine if the data and/or url imply the image service is supported by * Determine if the data and/or url imply the image service is supported by
* this tile source. * this tile source.
* @function * @function
* @name OpenSeadragon.OsmTileSource.prototype.supports
* @param {Object|Array} data * @param {Object|Array} data
* @param {String} optional - url * @param {String} optional - url
*/ */
supports: function( data, url ){ supports: function( data, url ){
return ( return (
data.type && data.type &&
"openstreetmaps" == data.type "openstreetmaps" == data.type
); );
}, },
/** /**
* *
* @function * @function
* @name OpenSeadragon.OsmTileSource.prototype.configure
* @param {Object} data - the raw configuration * @param {Object} data - the raw configuration
* @param {String} url - the url the data was retreived from if any. * @param {String} url - the url the data was retreived from if any.
* @return {Object} options - A dictionary of keyword arguments sufficient * @return {Object} options - A dictionary of keyword arguments sufficient
* to configure this tile sources constructor. * to configure this tile sources constructor.
*/ */
configure: function( data, url ){ configure: function( data, url ){
@ -92,7 +132,6 @@ $.extend( $.OsmTileSource.prototype, $.TileSource.prototype, {
/** /**
* @function * @function
* @name OpenSeadragon.OsmTileSource.prototype.getTileUrl
* @param {Number} level * @param {Number} level
* @param {Number} x * @param {Number} x
* @param {Number} y * @param {Number} y

View File

@ -1,10 +1,55 @@
/*
* OpenSeadragon - Overlay
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function( $ ){ (function( $ ){
/** /**
* An enumeration of positions that an overlay may be assigned relative * An enumeration of positions that an overlay may be assigned relative to
* to the viewport including CENTER, TOP_LEFT (default), TOP, TOP_RIGHT, * the viewport.
* RIGHT, BOTTOM_RIGHT, BOTTOM, BOTTOM_LEFT, and LEFT. * @member OverlayPlacement
* @memberof OpenSeadragon
* @static * @static
* @type {Object}
* @property {Number} CENTER
* @property {Number} TOP_LEFT
* @property {Number} TOP
* @property {Number} TOP_RIGHT
* @property {Number} RIGHT
* @property {Number} BOTTOM_RIGHT
* @property {Number} BOTTOM
* @property {Number} BOTTOM_LEFT
* @property {Number} LEFT
*/ */
$.OverlayPlacement = { $.OverlayPlacement = {
CENTER: 0, CENTER: 0,
@ -19,34 +64,76 @@
}; };
/** /**
* An Overlay provides a * @class Overlay
* @class * @classdesc Provides a way to float an HTML element on top of the viewer element.
*
* @memberof OpenSeadragon
* @param {Object} options
* @param {Element} options.element
* @param {OpenSeadragon.Point|OpenSeadragon.Rect} options.location - The
* location of the overlay on the image. If a {@link OpenSeadragon.Point}
* is specified, the overlay will keep a constant size independently of the
* zoom. If a {@link OpenSeadragon.Rect} is specified, the overlay size will
* be adjusted when the zoom changes.
* @param {OpenSeadragon.OverlayPlacement} [options.placement=OpenSeadragon.OverlayPlacement.TOP_LEFT]
* Relative position to the viewport.
* Only used if location is a {@link OpenSeadragon.Point}.
* @param {OpenSeadragon.Overlay.OnDrawCallback} [options.onDraw]
* @param {Boolean} [options.checkResize=true] Set to false to avoid to
* check the size of the overlay everytime it is drawn when using a
* {@link OpenSeadragon.Point} as options.location. It will improve
* performances but will cause a misalignment if the overlay size changes.
*/ */
$.Overlay = function( element, location, placement ) { $.Overlay = function( element, location, placement ) {
this.element = element;
this.scales = location instanceof $.Rect; /**
* onDraw callback signature used by {@link OpenSeadragon.Overlay}.
*
* @callback OnDrawCallback
* @memberof OpenSeadragon.Overlay
* @param {OpenSeadragon.Point} position
* @param {OpenSeadragon.Point} size
* @param {Element} element
*/
var options;
if ( $.isPlainObject( element ) ) {
options = element;
} else {
options = {
element: element,
location: location,
placement: placement
};
}
this.element = options.element;
this.scales = options.location instanceof $.Rect;
this.bounds = new $.Rect( this.bounds = new $.Rect(
location.x, options.location.x,
location.y, options.location.y,
location.width, options.location.width,
location.height options.location.height
); );
this.position = new $.Point( this.position = new $.Point(
location.x, options.location.x,
location.y options.location.y
); );
this.size = new $.Point( this.size = new $.Point(
location.width, options.location.width,
location.height options.location.height
); );
this.style = element.style; this.style = options.element.style;
// rects are always top-left // rects are always top-left
this.placement = location instanceof $.Point ? this.placement = options.location instanceof $.Point ?
placement : options.placement :
$.OverlayPlacement.TOP_LEFT; $.OverlayPlacement.TOP_LEFT;
this.onDraw = options.onDraw;
this.checkResize = options.checkResize === undefined ?
true : options.checkResize;
}; };
$.Overlay.prototype = { $.Overlay.prototype = /** @lends OpenSeadragon.Overlay.prototype */{
/** /**
* @function * @function
@ -100,9 +187,9 @@
element.parentNode.removeChild( element ); element.parentNode.removeChild( element );
//this should allow us to preserve overlays when required between //this should allow us to preserve overlays when required between
//pages //pages
if( element.prevElementParent ){ if ( element.prevElementParent ) {
style.display = 'none'; style.display = 'none';
//element.prevElementParent.insertBefore( //element.prevElementParent.insertBefore(
// element, // element,
// element.prevNextSibling // element.prevNextSibling
//); //);
@ -110,6 +197,9 @@
} }
} }
// clear the onDraw callback
this.onDraw = null;
style.top = ""; style.top = "";
style.left = ""; style.left = "";
style.position = ""; style.position = "";
@ -124,40 +214,80 @@
* @function * @function
* @param {Element} container * @param {Element} container
*/ */
drawHTML: function( container ) { drawHTML: function( container, viewport ) {
var element = this.element, var element = this.element,
style = this.style, style = this.style,
scales = this.scales, scales = this.scales,
position, degrees = viewport.degrees,
size; position = viewport.pixelFromPoint(
this.bounds.getTopLeft(),
true
),
size,
overlayCenter;
if ( element.parentNode != container ) { if ( element.parentNode != container ) {
//save the source parent for later if we need it //save the source parent for later if we need it
element.prevElementParent = element.parentNode; element.prevElementParent = element.parentNode;
element.prevNextSibling = element.nextSibling; element.prevNextSibling = element.nextSibling;
container.appendChild( element ); container.appendChild( element );
}
if ( !scales ) {
this.size = $.getElementSize( element ); this.size = $.getElementSize( element );
} }
position = this.position; if ( scales ) {
size = this.size; size = viewport.deltaPixelsFromPoints(
this.bounds.getSize(),
true
);
} else if ( this.checkResize ) {
size = $.getElementSize( element );
} else {
size = this.size;
}
this.position = position;
this.size = size;
this.adjust( position, size ); this.adjust( position, size );
position = position.apply( Math.floor ); position = position.apply( Math.floor );
size = size.apply( Math.ceil ); size = size.apply( Math.ceil );
style.left = position.x + "px"; // rotate the position of the overlay
style.top = position.y + "px"; // TODO only rotate overlays if in canvas mode
style.position = "absolute"; // TODO replace the size rotation with CSS3 transforms
style.display = 'block'; // TODO add an option to overlays to not rotate with the image
// Currently only rotates position and size
if( degrees !== 0 && this.scales ) {
overlayCenter = new $.Point( size.x / 2, size.y / 2 );
if ( scales ) { var drawerCenter = new $.Point(
style.width = size.x + "px"; viewport.viewer.drawer.canvas.width / 2,
style.height = size.y + "px"; viewport.viewer.drawer.canvas.height / 2
);
position = position.plus( overlayCenter ).rotate(
degrees,
drawerCenter
).minus( overlayCenter );
size = size.rotate( degrees, new $.Point( 0, 0 ) );
size = new $.Point( Math.abs( size.x ), Math.abs( size.y ) );
}
// call the onDraw callback if it exists to allow one to overwrite
// the drawing/positioning/sizing of the overlay
if ( this.onDraw ) {
this.onDraw( position, size, element );
} else {
style.left = position.x + "px";
style.top = position.y + "px";
style.position = "absolute";
style.display = 'block';
if ( scales ) {
style.width = size.x + "px";
style.height = size.y + "px";
}
} }
}, },
@ -168,16 +298,16 @@
*/ */
update: function( location, placement ) { update: function( location, placement ) {
this.scales = location instanceof $.Rect; this.scales = location instanceof $.Rect;
this.bounds = new $.Rect( this.bounds = new $.Rect(
location.x, location.x,
location.y, location.y,
location.width, location.width,
location.height location.height
); );
// rects are always top-left // rects are always top-left
this.placement = location instanceof $.Point ? this.placement = location instanceof $.Point ?
placement : placement :
$.OverlayPlacement.TOP_LEFT; $.OverlayPlacement.TOP_LEFT;
} }
}; };

View File

@ -1,21 +1,65 @@
/*
* OpenSeadragon - Point
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function( $ ){ (function( $ ){
/** /**
* A Point is really used as a 2-dimensional vector, equally useful for * @class Point
* @classdesc A Point is really used as a 2-dimensional vector, equally useful for
* representing a point on a plane, or the height and width of a plane * representing a point on a plane, or the height and width of a plane
* not requiring any other frame of reference. * not requiring any other frame of reference.
* @class *
* @memberof OpenSeadragon
* @param {Number} [x] The vector component 'x'. Defaults to the origin at 0. * @param {Number} [x] The vector component 'x'. Defaults to the origin at 0.
* @param {Number} [y] The vector component 'y'. Defaults to the origin at 0. * @param {Number} [y] The vector component 'y'. Defaults to the origin at 0.
* @property {Number} [x] The vector component 'x'.
* @property {Number} [y] The vector component 'y'.
*/ */
$.Point = function( x, y ) { $.Point = function( x, y ) {
/**
* The vector component 'x'.
* @member {Number} x
* @memberof OpenSeadragon.Point#
*/
this.x = typeof ( x ) == "number" ? x : 0; this.x = typeof ( x ) == "number" ? x : 0;
/**
* The vector component 'y'.
* @member {Number} y
* @memberof OpenSeadragon.Point#
*/
this.y = typeof ( y ) == "number" ? y : 0; this.y = typeof ( y ) == "number" ? y : 0;
}; };
$.Point.prototype = { $.Point.prototype = /** @lends OpenSeadragon.Point.prototype */{
/** /**
* Add another Point to this point and return a new Point. * Add another Point to this point and return a new Point.
@ -26,58 +70,57 @@ $.Point.prototype = {
*/ */
plus: function( point ) { plus: function( point ) {
return new $.Point( return new $.Point(
this.x + point.x, this.x + point.x,
this.y + point.y this.y + point.y
); );
}, },
/** /**
* Add another Point to this point and return a new Point. * Substract another Point to this point and return a new Point.
* @function * @function
* @param {OpenSeadragon.Point} point The point to add vector components. * @param {OpenSeadragon.Point} point The point to substract vector components.
* @returns {OpenSeadragon.Point} A new point representing the sum of the * @returns {OpenSeadragon.Point} A new point representing the substraction of the
* vector components * vector components
*/ */
minus: function( point ) { minus: function( point ) {
return new $.Point( return new $.Point(
this.x - point.x, this.x - point.x,
this.y - point.y this.y - point.y
); );
}, },
/** /**
* Add another Point to this point and return a new Point. * Multiply this point by a factor and return a new Point.
* @function * @function
* @param {OpenSeadragon.Point} point The point to add vector components. * @param {Number} factor The factor to multiply vector components.
* @returns {OpenSeadragon.Point} A new point representing the sum of the * @returns {OpenSeadragon.Point} A new point representing the multiplication
* vector components * of the vector components by the factor
*/ */
times: function( factor ) { times: function( factor ) {
return new $.Point( return new $.Point(
this.x * factor, this.x * factor,
this.y * factor this.y * factor
); );
}, },
/** /**
* Add another Point to this point and return a new Point. * Divide this point by a factor and return a new Point.
* @function * @function
* @param {OpenSeadragon.Point} point The point to add vector components. * @param {Number} factor The factor to divide vector components.
* @returns {OpenSeadragon.Point} A new point representing the sum of the * @returns {OpenSeadragon.Point} A new point representing the division of the
* vector components * vector components by the factor
*/ */
divide: function( factor ) { divide: function( factor ) {
return new $.Point( return new $.Point(
this.x / factor, this.x / factor,
this.y / factor this.y / factor
); );
}, },
/** /**
* Add another Point to this point and return a new Point. * Compute the opposite of this point and return a new Point.
* @function * @function
* @param {OpenSeadragon.Point} point The point to add vector components. * @returns {OpenSeadragon.Point} A new point representing the opposite of the
* @returns {OpenSeadragon.Point} A new point representing the sum of the
* vector components * vector components
*/ */
negate: function() { negate: function() {
@ -85,11 +128,10 @@ $.Point.prototype = {
}, },
/** /**
* Add another Point to this point and return a new Point. * Compute the distance between this point and another point.
* @function * @function
* @param {OpenSeadragon.Point} point The point to add vector components. * @param {OpenSeadragon.Point} point The point to compute the distance with.
* @returns {OpenSeadragon.Point} A new point representing the sum of the * @returns {Number} The distance between the 2 points
* vector components
*/ */
distanceTo: function( point ) { distanceTo: function( point ) {
return Math.sqrt( return Math.sqrt(
@ -99,39 +141,52 @@ $.Point.prototype = {
}, },
/** /**
* Add another Point to this point and return a new Point. * Apply a function to each coordinate of this point and return a new point.
* @function * @function
* @param {OpenSeadragon.Point} point The point to add vector components. * @param {function} func The function to apply to each coordinate.
* @returns {OpenSeadragon.Point} A new point representing the sum of the * @returns {OpenSeadragon.Point} A new point with the coordinates computed
* vector components * by the specified function
*/ */
apply: function( func ) { apply: function( func ) {
return new $.Point( func( this.x ), func( this.y ) ); return new $.Point( func( this.x ), func( this.y ) );
}, },
/** /**
* Add another Point to this point and return a new Point. * Check if this point is equal to another one.
* @function * @function
* @param {OpenSeadragon.Point} point The point to add vector components. * @param {OpenSeadragon.Point} point The point to compare this point with.
* @returns {OpenSeadragon.Point} A new point representing the sum of the * @returns {Boolean} true if they are equal, false otherwise.
* vector components
*/ */
equals: function( point ) { equals: function( point ) {
return ( return (
point instanceof $.Point point instanceof $.Point
) && ( ) && (
this.x === point.x this.x === point.x
) && ( ) && (
this.y === point.y this.y === point.y
); );
}, },
/** /**
* Add another Point to this point and return a new Point. * Rotates the point around the specified pivot
* From http://stackoverflow.com/questions/4465931/rotate-rectangle-around-a-point
* @function * @function
* @param {OpenSeadragon.Point} point The point to add vector components. * @param {Number} degress to rotate around the pivot.
* @returns {OpenSeadragon.Point} A new point representing the sum of the * @param {OpenSeadragon.Point} pivot Point about which to rotate.
* vector components * @returns {OpenSeadragon.Point}. A new point representing the point rotated around the specified pivot
*/
rotate: function ( degrees, pivot ) {
var angle = degrees * Math.PI / 180.0,
x = Math.cos( angle ) * ( this.x - pivot.x ) - Math.sin( angle ) * ( this.y - pivot.y ) + pivot.x,
y = Math.sin( angle ) * ( this.x - pivot.x ) + Math.cos( angle ) * ( this.y - pivot.y ) + pivot.y;
return new $.Point( x, y );
},
/**
* Convert this point to a string in the format (x,y) where x and y are
* rounded to the nearest integer.
* @function
* @returns {String} A string representation of this point.
*/ */
toString: function() { toString: function() {
return "(" + Math.round(this.x) + "," + Math.round(this.y) + ")"; return "(" + Math.round(this.x) + "," + Math.round(this.y) + ")";

View File

@ -1,9 +1,45 @@
/*
* OpenSeadragon - Profiler
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function( $ ){ (function( $ ){
/** /**
* A utility class useful for developers to establish baseline performance * @class Profiler
* @classdesc A utility class useful for developers to establish baseline performance
* metrics of rendering routines. * metrics of rendering routines.
* @class *
* @memberof OpenSeadragon
* @property {Boolean} midUpdate * @property {Boolean} midUpdate
* @property {Number} numUpdates * @property {Number} numUpdates
* @property {Number} lastBeginTime * @property {Number} lastBeginTime
@ -32,7 +68,7 @@ $.Profiler = function() {
this.maxIdleTime = 0; this.maxIdleTime = 0;
}; };
$.Profiler.prototype = { $.Profiler.prototype = /** @lends OpenSeadragon.Profiler.prototype */{
/** /**
* @function * @function
@ -43,7 +79,7 @@ $.Profiler.prototype = {
} }
this.midUpdate = true; this.midUpdate = true;
this.lastBeginTime = new Date().getTime(); this.lastBeginTime = $.now();
if (this.numUpdates < 1) { if (this.numUpdates < 1) {
return; // this is the first update return; // this is the first update
@ -69,7 +105,7 @@ $.Profiler.prototype = {
return; return;
} }
this.lastEndTime = new Date().getTime(); this.lastEndTime = $.now();
this.midUpdate = false; this.midUpdate = false;
var time = this.lastEndTime - this.lastBeginTime; var time = this.lastEndTime - this.lastBeginTime;

View File

@ -1,29 +1,80 @@
/*
* OpenSeadragon - Rect
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function( $ ){ (function( $ ){
/** /**
* A Rectangle really represents a 2x2 matrix where each row represents a * @class Rect
* 2 dimensional vector component, the first is (x,y) and the second is * @classdesc A Rectangle really represents a 2x2 matrix where each row represents a
* (width, height). The latter component implies the equation of a simple * 2 dimensional vector component, the first is (x,y) and the second is
* (width, height). The latter component implies the equation of a simple
* plane. * plane.
* *
* @class * @memberof OpenSeadragon
* @param {Number} x The vector component 'x'. * @param {Number} x The vector component 'x'.
* @param {Number} y The vector component 'y'. * @param {Number} y The vector component 'y'.
* @param {Number} width The vector component 'height'. * @param {Number} width The vector component 'height'.
* @param {Number} height The vector component 'width'. * @param {Number} height The vector component 'width'.
* @property {Number} x The vector component 'x'.
* @property {Number} y The vector component 'y'.
* @property {Number} width The vector component 'width'.
* @property {Number} height The vector component 'height'.
*/ */
$.Rect = function( x, y, width, height ) { $.Rect = function( x, y, width, height ) {
/**
* The vector component 'x'.
* @member {Number} x
* @memberof OpenSeadragon.Rect#
*/
this.x = typeof ( x ) == "number" ? x : 0; this.x = typeof ( x ) == "number" ? x : 0;
/**
* The vector component 'y'.
* @member {Number} y
* @memberof OpenSeadragon.Rect#
*/
this.y = typeof ( y ) == "number" ? y : 0; this.y = typeof ( y ) == "number" ? y : 0;
/**
* The vector component 'width'.
* @member {Number} width
* @memberof OpenSeadragon.Rect#
*/
this.width = typeof ( width ) == "number" ? width : 0; this.width = typeof ( width ) == "number" ? width : 0;
/**
* The vector component 'height'.
* @member {Number} height
* @memberof OpenSeadragon.Rect#
*/
this.height = typeof ( height ) == "number" ? height : 0; this.height = typeof ( height ) == "number" ? height : 0;
}; };
$.Rect.prototype = { $.Rect.prototype = /** @lends OpenSeadragon.Rect.prototype */{
/** /**
* The aspect ratio is simply the ratio of width to height. * The aspect ratio is simply the ratio of width to height.
@ -35,14 +86,17 @@ $.Rect.prototype = {
}, },
/** /**
* Provides the coordinates of the upper-left corner of the rectanglea s a * Provides the coordinates of the upper-left corner of the rectangle as a
* point. * point.
* @function * @function
* @returns {OpenSeadragon.Point} The coordinate of the upper-left corner of * @returns {OpenSeadragon.Point} The coordinate of the upper-left corner of
* the rectangle. * the rectangle.
*/ */
getTopLeft: function() { getTopLeft: function() {
return new $.Point( this.x, this.y ); return new $.Point(
this.x,
this.y
);
}, },
/** /**
@ -54,7 +108,35 @@ $.Rect.prototype = {
*/ */
getBottomRight: function() { getBottomRight: function() {
return new $.Point( return new $.Point(
this.x + this.width, this.x + this.width,
this.y + this.height
);
},
/**
* Provides the coordinates of the top-right corner of the rectangle as a
* point.
* @function
* @returns {OpenSeadragon.Point} The coordinate of the top-right corner of
* the rectangle.
*/
getTopRight: function() {
return new $.Point(
this.x + this.width,
this.y
);
},
/**
* Provides the coordinates of the bottom-left corner of the rectangle as a
* point.
* @function
* @returns {OpenSeadragon.Point} The coordinate of the bottom-left corner of
* the rectangle.
*/
getBottomLeft: function() {
return new $.Point(
this.x,
this.y + this.height this.y + this.height
); );
}, },
@ -62,7 +144,7 @@ $.Rect.prototype = {
/** /**
* Computes the center of the rectangle. * Computes the center of the rectangle.
* @function * @function
* @returns {OpenSeadragon.Point} The center of the rectangle as represnted * @returns {OpenSeadragon.Point} The center of the rectangle as represented
* as represented by a 2-dimensional vector (x,y) * as represented by a 2-dimensional vector (x,y)
*/ */
getCenter: function() { getCenter: function() {
@ -75,7 +157,7 @@ $.Rect.prototype = {
/** /**
* Returns the width and height component as a vector OpenSeadragon.Point * Returns the width and height component as a vector OpenSeadragon.Point
* @function * @function
* @returns {OpenSeadragon.Point} The 2 dimensional vector represnting the * @returns {OpenSeadragon.Point} The 2 dimensional vector representing the
* the width and height of the rectangle. * the width and height of the rectangle.
*/ */
getSize: function() { getSize: function() {
@ -83,31 +165,86 @@ $.Rect.prototype = {
}, },
/** /**
* Determines if two Rectanlges have equivalent components. * Determines if two Rectangles have equivalent components.
* @function * @function
* @param {OpenSeadragon.Rect} rectangle The Rectangle to compare to. * @param {OpenSeadragon.Rect} rectangle The Rectangle to compare to.
* @return {Boolean} 'true' if all components are equal, otherwise 'false'. * @return {Boolean} 'true' if all components are equal, otherwise 'false'.
*/ */
equals: function( other ) { equals: function( other ) {
return ( other instanceof $.Rect ) && return ( other instanceof $.Rect ) &&
( this.x === other.x ) && ( this.x === other.x ) &&
( this.y === other.y ) && ( this.y === other.y ) &&
( this.width === other.width ) && ( this.width === other.width ) &&
( this.height === other.height ); ( this.height === other.height );
}, },
/** /**
* Provides a string representation of the retangle which is useful for * Rotates a rectangle around a point. Currently only 90, 180, and 270
* degrees are supported.
* @function
* @param {Number} degrees The angle in degrees to rotate.
* @param {OpenSeadragon.Point} pivot The point about which to rotate.
* Defaults to the center of the rectangle.
* @return {OpenSeadragon.Rect}
*/
rotate: function( degrees, pivot ) {
// TODO support arbitrary rotation
var width = this.width,
height = this.height,
newTopLeft;
degrees = ( degrees + 360 ) % 360;
if( degrees % 90 !== 0 ) {
throw new Error('Currently only 0, 90, 180, and 270 degrees are supported.');
}
if( degrees === 0 ){
return new $.Rect(
this.x,
this.y,
this.width,
this.height
);
}
pivot = pivot || this.getCenter();
switch ( degrees ) {
case 90:
newTopLeft = this.getBottomLeft();
width = this.height;
height = this.width;
break;
case 180:
newTopLeft = this.getBottomRight();
break;
case 270:
newTopLeft = this.getTopRight();
width = this.height;
height = this.width;
break;
default:
newTopLeft = this.getTopLeft();
break;
}
newTopLeft = newTopLeft.rotate(degrees, pivot);
return new $.Rect(newTopLeft.x, newTopLeft.y, width, height);
},
/**
* Provides a string representation of the rectangle which is useful for
* debugging. * debugging.
* @function * @function
* @returns {String} A string representation of the rectangle. * @returns {String} A string representation of the rectangle.
*/ */
toString: function() { toString: function() {
return "[" + return "[" +
Math.round(this.x*100) + "," + Math.round(this.x*100) + "," +
Math.round(this.y*100) + "," + Math.round(this.y*100) + "," +
Math.round(this.width*100) + "x" + Math.round(this.width*100) + "x" +
Math.round(this.height*100) + Math.round(this.height*100) +
"]"; "]";
} }
}; };

View File

@ -1,15 +1,48 @@
/*
* OpenSeadragon - ReferenceStrip
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function ( $ ) {
(function( $ ){
// dictionary from id to private properties // dictionary from id to private properties
var THIS = {}; var THIS = {};
/** /**
* The CollectionDrawer is a reimplementation if the Drawer API that * The CollectionDrawer is a reimplementation if the Drawer API that
* focuses on allowing a viewport to be redefined as a collection * focuses on allowing a viewport to be redefined as a collection
* of smaller viewports, defined by a clear number of rows and / or * of smaller viewports, defined by a clear number of rows and / or
* columns of which each item in the matrix of viewports has its own * columns of which each item in the matrix of viewports has its own
* source. * source.
* *
* This idea is a reexpression of the idea of dzi collections * This idea is a reexpression of the idea of dzi collections
* which allows a clearer algorithm to reuse the tile sources already * which allows a clearer algorithm to reuse the tile sources already
@ -18,26 +51,29 @@ var THIS = {};
* for the purpose of image sequnces. * for the purpose of image sequnces.
* *
* TODO: The difficult part of this feature is figuring out how to express * TODO: The difficult part of this feature is figuring out how to express
* this functionality as a combination of the functionality already * this functionality as a combination of the functionality already
* provided by Drawer, Viewport, TileSource, and Navigator. It may * provided by Drawer, Viewport, TileSource, and Navigator. It may
* require better abstraction at those points in order to effeciently * require better abstraction at those points in order to effeciently
* reuse those paradigms. * reuse those paradigms.
*/ */
$.ReferenceStrip = function( options ){ /**
* @class ReferenceStrip
* @memberof OpenSeadragon
* @param {Object} options
*/
$.ReferenceStrip = function ( options ) {
var _this = this, var _this = this,
viewer = options.viewer, viewer = options.viewer,
viewerSize = $.getElementSize( viewer.element ), viewerSize = $.getElementSize( viewer.element ),
miniViewer,
minPixelRatio,
element, element,
style, style,
i; i;
//We may need to create a new element and id if they did not //We may need to create a new element and id if they did not
//provide the id for the existing element //provide the id for the existing element
if( !options.id ){ if ( !options.id ) {
options.id = 'referencestrip-' + (+new Date()); options.id = 'referencestrip-' + $.now();
this.element = $.makeNeutralElement( "div" ); this.element = $.makeNeutralElement( "div" );
this.element.id = options.id; this.element.id = options.id;
this.element.className = 'referencestrip'; this.element.className = 'referencestrip';
@ -57,17 +93,17 @@ $.ReferenceStrip = function( options ){
mouseNavEnabled: false, mouseNavEnabled: false,
showNavigationControl: false, showNavigationControl: false,
showSequenceControl: 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;
style = this.element.style; style = this.element.style;
style.marginTop = '0px'; style.marginTop = '0px';
style.marginRight = '0px'; style.marginRight = '0px';
style.marginBottom = '0px'; style.marginBottom = '0px';
@ -81,56 +117,56 @@ $.ReferenceStrip = function( options ){
$.setElementOpacity( this.element, 0.8 ); $.setElementOpacity( this.element, 0.8 );
this.viewer = viewer; this.viewer = viewer;
this.innerTracker = new $.MouseTracker({ this.innerTracker = new $.MouseTracker( {
element: this.element, element: this.element,
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 ), exitHandler: $.delegate( this, onStripExit ),
keyHandler: $.delegate( this, onKeyPress ) keyHandler: $.delegate( this, onKeyPress )
}).setTracking( true ); } ).setTracking( 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
//appropriate width and height //appropriate width and height
if( options.width && options.height ){ if ( options.width && options.height ) {
this.element.style.width = options.width + 'px'; this.element.style.width = options.width + 'px';
this.element.style.height = options.height + 'px'; this.element.style.height = options.height + 'px';
viewer.addControl( viewer.addControl(
this.element, this.element,
$.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 *
viewer.tileSources.length viewer.tileSources.length
) + ( 12 * viewer.tileSources.length ) + 'px'; ) + ( 12 * viewer.tileSources.length ) + 'px';
this.element.style.height = ( this.element.style.height = (
viewerSize.y * viewerSize.y *
options.sizeRatio options.sizeRatio
) + 'px'; ) + 'px';
viewer.addControl( viewer.addControl(
this.element, this.element,
$.ControlAnchor.BOTTOM_LEFT { anchor: $.ControlAnchor.BOTTOM_LEFT }
); );
}else { } else {
this.element.style.height = ( this.element.style.height = (
viewerSize.y * viewerSize.y *
options.sizeRatio * options.sizeRatio *
viewer.tileSources.length viewer.tileSources.length
) + ( 12 * viewer.tileSources.length ) + 'px'; ) + ( 12 * viewer.tileSources.length ) + 'px';
this.element.style.width = ( this.element.style.width = (
viewerSize.x * viewerSize.x *
options.sizeRatio options.sizeRatio
) + 'px'; ) + 'px';
viewer.addControl( viewer.addControl(
this.element, this.element,
$.ControlAnchor.TOP_LEFT { anchor: $.ControlAnchor.TOP_LEFT }
); );
} }
@ -141,9 +177,9 @@ $.ReferenceStrip = function( options ){
this.panels = []; this.panels = [];
/*jshint loopfunc:true*/ /*jshint loopfunc:true*/
for( i = 0; i < viewer.tileSources.length; i++ ){ for ( i = 0; i < viewer.tileSources.length; i++ ) {
element = $.makeNeutralElement('div'); element = $.makeNeutralElement( 'div' );
element.id = this.element.id + "-" + i; element.id = this.element.id + "-" + i;
element.style.width = _this.panelWidth + 'px'; element.style.width = _this.panelWidth + 'px';
@ -154,27 +190,28 @@ $.ReferenceStrip = function( options ){
element.style.styleFloat = 'left'; //IE element.style.styleFloat = 'left'; //IE
element.style.padding = '2px'; element.style.padding = '2px';
element.innerTracker = new $.MouseTracker({ element.innerTracker = new $.MouseTracker( {
element: element, element: element,
clickTimeThreshold: this.clickTimeThreshold, clickTimeThreshold: this.clickTimeThreshold,
clickDistThreshold: this.clickDistThreshold, clickDistThreshold: this.clickDistThreshold,
pressHandler: function( tracker ){ pressHandler: function ( event ) {
tracker.dragging = +new Date(); event.eventSource.dragging = $.now();
}, },
releaseHandler: function( tracker, position, insideElementPress, insideElementRelease ){ releaseHandler: function ( event ) {
var id = tracker.element.id, var tracker = event.eventSource,
page = Number( id.split( '-' )[ 2 ] ), id = tracker.element.id,
now = +new Date(); page = Number( id.split( '-' )[2] ),
now = $.now();
if ( insideElementPress &&
insideElementRelease && if ( event.insideElementPressed &&
event.insideElementReleased &&
tracker.dragging && tracker.dragging &&
( now - tracker.dragging ) < tracker.clickTimeThreshold ){ ( now - tracker.dragging ) < tracker.clickTimeThreshold ) {
tracker.dragging = null; tracker.dragging = null;
viewer.goToPage( page ); viewer.goToPage( page );
} }
} }
}).setTracking( true ); } ).setTracking( true );
this.element.appendChild( element ); this.element.appendChild( element );
@ -183,49 +220,52 @@ $.ReferenceStrip = function( options ){
this.panels.push( element ); this.panels.push( element );
} }
loadPanels( this, this.scroll == 'vertical' ? viewerSize.y : viewerSize.y, 0); loadPanels( this, this.scroll == 'vertical' ? viewerSize.y : viewerSize.y, 0 );
this.setFocus( 0 ); this.setFocus( 0 );
}; };
$.extend( $.ReferenceStrip.prototype, $.EventHandler.prototype, $.Viewer.prototype, { $.extend( $.ReferenceStrip.prototype, $.EventSource.prototype, $.Viewer.prototype, /** @lends OpenSeadragon.ReferenceStrip.prototype */{
setFocus: function( page ){ /**
var element = $.getElement( this.element.id + '-' + page ), * @function
viewerSize = $.getElementSize( this.viewer.canvas ), */
scrollWidth = Number(this.element.style.width.replace('px','')), setFocus: function ( page ) {
scrollHeight = Number(this.element.style.height.replace('px','')), var element = $.getElement( this.element.id + '-' + page ),
offsetLeft = -Number(this.element.style.marginLeft.replace('px','')), viewerSize = $.getElementSize( this.viewer.canvas ),
offsetTop = -Number(this.element.style.marginTop.replace('px','')), scrollWidth = Number( this.element.style.width.replace( 'px', '' ) ),
scrollHeight = Number( this.element.style.height.replace( 'px', '' ) ),
offsetLeft = -Number( this.element.style.marginLeft.replace( 'px', '' ) ),
offsetTop = -Number( this.element.style.marginTop.replace( 'px', '' ) ),
offset; offset;
if ( this.currentSelected !== element ){ if ( this.currentSelected !== element ) {
if( this.currentSelected ){ if ( this.currentSelected ) {
this.currentSelected.style.background = '#000'; this.currentSelected.style.background = '#000';
} }
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 ) {
offset = Math.min(offset, (scrollWidth - viewerSize.x)); offset = Math.min( offset, ( scrollWidth - viewerSize.x ) );
this.element.style.marginLeft = -offset + 'px'; this.element.style.marginLeft = -offset + 'px';
loadPanels( this, viewerSize.x, -offset ); loadPanels( this, viewerSize.x, -offset );
}else if( offset < offsetLeft ){ } else if ( offset < offsetLeft ) {
offset = Math.max(0, offset - viewerSize.x / 2); offset = Math.max( 0, offset - viewerSize.x / 2 );
this.element.style.marginLeft = -offset + 'px'; this.element.style.marginLeft = -offset + 'px';
loadPanels( this, viewerSize.x, -offset ); loadPanels( this, viewerSize.x, -offset );
} }
}else{ } else {
offset = (Number(page) ) * ( this.panelHeight + 3 ); offset = ( Number( page ) ) * ( this.panelHeight + 3 );
if( offset > offsetTop + viewerSize.y - this.panelHeight){ if ( offset > offsetTop + viewerSize.y - this.panelHeight ) {
offset = Math.min(offset, (scrollHeight - viewerSize.y)); offset = Math.min( offset, ( scrollHeight - viewerSize.y ) );
this.element.style.marginTop = -offset + 'px'; this.element.style.marginTop = -offset + 'px';
loadPanels( this, viewerSize.y, -offset ); loadPanels( this, viewerSize.y, -offset );
}else if( offset < offsetTop ){ } else if ( offset < offsetTop ) {
offset = Math.max(0, offset - viewerSize.y / 2); offset = Math.max( 0, offset - viewerSize.y / 2 );
this.element.style.marginTop = -offset + 'px'; this.element.style.marginTop = -offset + 'px';
loadPanels( this, viewerSize.y, -offset ); loadPanels( this, viewerSize.y, -offset );
} }
@ -233,24 +273,22 @@ $.extend( $.ReferenceStrip.prototype, $.EventHandler.prototype, $.Viewer.prototy
this.currentPage = page; this.currentPage = page;
$.getElement( element.id + '-displayregion' ).focus(); $.getElement( element.id + '-displayregion' ).focus();
onStripEnter.call( this, this.innerTracker ); onStripEnter.call( this, { eventSource: this.innerTracker } );
} }
}, },
/** /**
* @function * @function
* @name OpenSeadragon.ReferenceStrip.prototype.update
*/ */
update: function( viewport ){ update: function () {
if ( THIS[this.id].animating ) {
if( THIS[ this.id ].animating ){ $.console.log( 'image reference strip update' );
$.console.log('image reference strip update');
return true; return true;
} }
return false; return false;
} }
}); } );
@ -260,41 +298,41 @@ $.extend( $.ReferenceStrip.prototype, $.EventHandler.prototype, $.Viewer.prototy
* @inner * @inner
* @function * @function
*/ */
function onStripDrag( tracker, position, delta, shift ) { function onStripDrag( event ) {
var offsetLeft = Number(this.element.style.marginLeft.replace('px','')), 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; this.dragging = true;
if ( this.element ) { if ( this.element ) {
if( 'horizontal' == this.scroll ){ if ( 'horizontal' == this.scroll ) {
if ( -delta.x > 0 ) { if ( -event.delta.x > 0 ) {
//forward //forward
if( offsetLeft > -(scrollWidth - viewerSize.x)){ if ( offsetLeft > -( scrollWidth - viewerSize.x ) ) {
this.element.style.marginLeft = ( offsetLeft + (delta.x * 2) ) + 'px'; this.element.style.marginLeft = ( offsetLeft + ( event.delta.x * 2 ) ) + 'px';
loadPanels( this, viewerSize.x, offsetLeft + (delta.x * 2) ); loadPanels( this, viewerSize.x, offsetLeft + ( event.delta.x * 2 ) );
} }
} else if ( -delta.x < 0 ) { } else if ( -event.delta.x < 0 ) {
//reverse //reverse
if( offsetLeft < 0 ){ if ( offsetLeft < 0 ) {
this.element.style.marginLeft = ( offsetLeft + (delta.x * 2) ) + 'px'; this.element.style.marginLeft = ( offsetLeft + ( event.delta.x * 2 ) ) + 'px';
loadPanels( this, viewerSize.x, offsetLeft + (delta.x * 2) ); loadPanels( this, viewerSize.x, offsetLeft + ( event.delta.x * 2 ) );
} }
} }
}else{ } else {
if ( -delta.y > 0 ) { if ( -event.delta.y > 0 ) {
//forward //forward
if( offsetTop > -(scrollHeight - viewerSize.y)){ if ( offsetTop > -( scrollHeight - viewerSize.y ) ) {
this.element.style.marginTop = ( offsetTop + (delta.y * 2) ) + 'px'; this.element.style.marginTop = ( offsetTop + ( event.delta.y * 2 ) ) + 'px';
loadPanels( this, viewerSize.y, offsetTop + (delta.y * 2) ); loadPanels( this, viewerSize.y, offsetTop + ( event.delta.y * 2 ) );
} }
} else if ( -delta.y < 0 ) { } else if ( -event.delta.y < 0 ) {
//reverse //reverse
if( offsetTop < 0 ){ if ( offsetTop < 0 ) {
this.element.style.marginTop = ( offsetTop + (delta.y * 2) ) + 'px'; this.element.style.marginTop = ( offsetTop + ( event.delta.y * 2 ) ) + 'px';
loadPanels( this, viewerSize.y, offsetTop + (delta.y * 2) ); loadPanels( this, viewerSize.y, offsetTop + ( event.delta.y * 2 ) );
} }
} }
} }
@ -310,39 +348,39 @@ function onStripDrag( tracker, position, delta, shift ) {
* @inner * @inner
* @function * @function
*/ */
function onStripScroll( tracker, position, scroll, shift ) { function onStripScroll( event ) {
var offsetLeft = Number(this.element.style.marginLeft.replace('px','')), 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 ( this.element ) {
if( 'horizontal' == this.scroll ){ if ( 'horizontal' == this.scroll ) {
if ( scroll > 0 ) { if ( event.scroll > 0 ) {
//forward //forward
if( offsetLeft > -(scrollWidth - viewerSize.x)){ if ( offsetLeft > -( scrollWidth - viewerSize.x ) ) {
this.element.style.marginLeft = ( offsetLeft - (scroll * 60) ) + 'px'; this.element.style.marginLeft = ( offsetLeft - ( event.scroll * 60 ) ) + 'px';
loadPanels( this, viewerSize.x, offsetLeft - (scroll * 60) ); loadPanels( this, viewerSize.x, offsetLeft - ( event.scroll * 60 ) );
} }
} else if ( scroll < 0 ) { } else if ( event.scroll < 0 ) {
//reverse //reverse
if( offsetLeft < 0 ){ if ( offsetLeft < 0 ) {
this.element.style.marginLeft = ( offsetLeft - (scroll * 60) ) + 'px'; this.element.style.marginLeft = ( offsetLeft - ( event.scroll * 60 ) ) + 'px';
loadPanels( this, viewerSize.x, offsetLeft - (scroll * 60) ); loadPanels( this, viewerSize.x, offsetLeft - ( event.scroll * 60 ) );
} }
} }
}else{ } else {
if ( scroll < 0 ) { if ( event.scroll < 0 ) {
//scroll up //scroll up
if( offsetTop > viewerSize.y - scrollHeight ){ if ( offsetTop > viewerSize.y - scrollHeight ) {
this.element.style.marginTop = ( offsetTop + (scroll * 60) ) + 'px'; this.element.style.marginTop = ( offsetTop + ( event.scroll * 60 ) ) + 'px';
loadPanels( this, viewerSize.y, offsetTop + (scroll * 60) ); loadPanels( this, viewerSize.y, offsetTop + ( event.scroll * 60 ) );
} }
} else if ( scroll > 0 ) { } else if ( event.scroll > 0 ) {
//scroll dowm //scroll dowm
if( offsetTop < 0 ){ if ( offsetTop < 0 ) {
this.element.style.marginTop = ( offsetTop + (scroll * 60) ) + 'px'; this.element.style.marginTop = ( offsetTop + ( event.scroll * 60 ) ) + 'px';
loadPanels( this, viewerSize.y, offsetTop + (scroll * 60) ); loadPanels( this, viewerSize.y, offsetTop + ( event.scroll * 60 ) );
} }
} }
} }
@ -352,29 +390,30 @@ function onStripScroll( tracker, position, scroll, shift ) {
} }
function loadPanels(strip, viewerSize, scroll){ function loadPanels( strip, viewerSize, scroll ) {
var panelSize, var panelSize,
activePanelsStart, activePanelsStart,
activePanelsEnd, activePanelsEnd,
miniViewer, miniViewer,
style, style,
i; i,
if( 'horizontal' == strip.scroll ){ element;
if ( 'horizontal' == strip.scroll ) {
panelSize = strip.panelWidth; panelSize = strip.panelWidth;
}else{ } else {
panelSize = strip.panelHeight; panelSize = strip.panelHeight;
} }
activePanelsStart = Math.ceil( viewerSize / panelSize ) + 5; activePanelsStart = Math.ceil( viewerSize / panelSize ) + 5;
activePanelsEnd = Math.ceil( (Math.abs(scroll) + viewerSize ) / panelSize ) + 1; activePanelsEnd = Math.ceil( ( Math.abs( scroll ) + viewerSize ) / panelSize ) + 1;
activePanelsStart = activePanelsEnd - activePanelsStart; activePanelsStart = activePanelsEnd - activePanelsStart;
activePanelsStart = activePanelsStart < 0 ? 0 : activePanelsStart; activePanelsStart = activePanelsStart < 0 ? 0 : activePanelsStart;
for( i = activePanelsStart; i < activePanelsEnd && i < strip.panels.length; i++ ){ for ( i = activePanelsStart; i < activePanelsEnd && i < strip.panels.length; i++ ) {
element = strip.panels[ i ]; element = strip.panels[i];
if ( !element.activePanel ){ if ( !element.activePanel ) {
miniViewer = new $.Viewer( { miniViewer = new $.Viewer( {
id: element.id, id: element.id,
tileSources: [ strip.viewer.tileSources[ i ] ], tileSources: [strip.viewer.tileSources[i]],
element: element, element: element,
navigatorSizeRatio: strip.sizeRatio, navigatorSizeRatio: strip.sizeRatio,
showNavigator: false, showNavigator: false,
@ -384,13 +423,13 @@ function loadPanels(strip, viewerSize, scroll){
immediateRender: true, immediateRender: true,
blendTime: 0, blendTime: 0,
animationTime: 0 animationTime: 0
} ); } );
miniViewer.displayRegion = $.makeNeutralElement( "textarea" ); miniViewer.displayRegion = $.makeNeutralElement( "textarea" );
miniViewer.displayRegion.id = element.id + '-displayregion'; miniViewer.displayRegion.id = element.id + '-displayregion';
miniViewer.displayRegion.className = 'displayregion'; miniViewer.displayRegion.className = 'displayregion';
style = miniViewer.displayRegion.style; style = miniViewer.displayRegion.style;
style.position = 'relative'; style.position = 'relative';
style.top = '0px'; style.top = '0px';
style.left = '0px'; style.left = '0px';
@ -404,12 +443,12 @@ function loadPanels(strip, viewerSize, scroll){
style.width = ( strip.panelWidth - 4 ) + 'px'; style.width = ( strip.panelWidth - 4 ) + 'px';
style.height = ( strip.panelHeight - 4 ) + 'px'; style.height = ( strip.panelHeight - 4 ) + 'px';
miniViewer.displayRegion.innerTracker = new $.MouseTracker({ miniViewer.displayRegion.innerTracker = new $.MouseTracker( {
element: miniViewer.displayRegion element: miniViewer.displayRegion
}); } );
element.getElementsByTagName('form')[ 0 ].appendChild( element.getElementsByTagName( 'div' )[0].appendChild(
miniViewer.displayRegion miniViewer.displayRegion
); );
element.activePanel = true; element.activePanel = true;
@ -423,22 +462,23 @@ function loadPanels(strip, viewerSize, scroll){
* @inner * @inner
* @function * @function
*/ */
function onStripEnter( tracker ) { function onStripEnter( event ) {
var element = event.eventSource.element;
//$.setElementOpacity(element, 0.8);
//$.setElementOpacity(tracker.element, 0.8); //element.style.border = '1px solid #555';
//element.style.background = '#000';
//tracker.element.style.border = '1px solid #555'; if ( 'horizontal' == this.scroll ) {
//tracker.element.style.background = '#000';
if( 'horizontal' == this.scroll ){ //element.style.paddingTop = "0px";
element.style.marginBottom = "0px";
//tracker.element.style.paddingTop = "0px";
tracker.element.style.marginBottom = "0px";
} else { } else {
//tracker.element.style.paddingRight = "0px"; //element.style.paddingRight = "0px";
tracker.element.style.marginLeft = "0px"; element.style.marginLeft = "0px";
} }
return false; return false;
@ -450,25 +490,19 @@ function onStripEnter( tracker ) {
* @inner * @inner
* @function * @function
*/ */
function onStripExit( tracker ) { function onStripExit( event ) {
var element = event.eventSource.element;
var viewerSize = $.getElementSize( this.viewer.element );
//$.setElementOpacity(tracker.element, 0.4);
//tracker.element.style.border = 'none';
//tracker.element.style.background = '#fff';
if ( 'horizontal' == this.scroll ) {
//element.style.paddingTop = "10px";
element.style.marginBottom = "-" + ( $.getElementSize( element ).y / 2 ) + "px";
if( 'horizontal' == this.scroll ){
//tracker.element.style.paddingTop = "10px";
tracker.element.style.marginBottom = "-" + ( $.getElementSize( tracker.element ).y / 2 ) + "px";
} else { } else {
//tracker.element.style.paddingRight = "10px"; //element.style.paddingRight = "10px";
tracker.element.style.marginLeft = "-" + ( $.getElementSize( tracker.element ).x / 2 )+ "px"; element.style.marginLeft = "-" + ( $.getElementSize( element ).x / 2 ) + "px";
} }
return false; return false;
} }
@ -480,41 +514,41 @@ function onStripExit( tracker ) {
* @inner * @inner
* @function * @function
*/ */
function onKeyPress( tracker, keyCode, shiftKey ){ function onKeyPress( event ) {
//console.log( keyCode ); //console.log( event.keyCode );
switch( keyCode ){ switch ( event.keyCode ) {
case 61://=|+ case 61: //=|+
onStripScroll.call(this, this.tracker, null, 1, null); onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
return false; return false;
case 45://-|_ case 45: //-|_
onStripScroll.call(this, this.tracker, null, -1, null); onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
return false; return false;
case 48://0|) case 48: //0|)
case 119://w case 119: //w
case 87://W case 87: //W
case 38://up arrow case 38: //up arrow
onStripScroll.call(this, this.tracker, null, 1, null); onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
return false; return false;
case 115://s case 115: //s
case 83://S case 83: //S
case 40://down arrow case 40: //down arrow
onStripScroll.call(this, this.tracker, null, -1, null); onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
return false; return false;
case 97://a case 97: //a
case 37://left arrow case 37: //left arrow
onStripScroll.call(this, this.tracker, null, -1, null); onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: -1, shift: null } );
return false; return false;
case 100://d case 100: //d
case 39://right arrow case 39: //right arrow
onStripScroll.call(this, this.tracker, null, 1, null); onStripScroll.call( this, { eventSource: this.tracker, position: null, scroll: 1, shift: null } );
return false; return false;
default: default:
//console.log( 'navigator keycode %s', keyCode ); //console.log( 'navigator keycode %s', event.keyCode );
return true; return true;
} }
} }
}( OpenSeadragon )); } ( OpenSeadragon ) );

View File

@ -1,62 +1,116 @@
/*
* OpenSeadragon - Spring
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function( $ ){ (function( $ ){
/** /**
* @class * @class Spring
* @memberof OpenSeadragon
* @param {Object} options - Spring configuration settings. * @param {Object} options - Spring configuration settings.
* @param {Number} options.initial - Initial value of spring, default to 0 so * @param {Number} options.initial - Initial value of spring, default to 0 so
* spring is not in motion initally by default. * spring is not in motion initally by default.
* @param {Number} options.springStiffness - Spring stiffness. * @param {Number} options.springStiffness - Spring stiffness.
* @param {Number} options.animationTime - Animation duration per spring. * @param {Number} options.animationTime - Animation duration per spring.
*
* @property {Number} initial - Initial value of spring, default to 0 so
* spring is not in motion initally by default.
* @property {Number} springStiffness - Spring stiffness.
* @property {Number} animationTime - Animation duration per spring.
* @property {Object} current
* @property {Number} start
* @property {Number} target
*/ */
$.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 ] :
0, 0,
springStiffness: args.length > 1 ? /**
args[ 1 ].springStiffness : * Spring stiffness.
* @member {Number} springStiffness
* @memberof OpenSeadragon.Spring#
*/
springStiffness: args.length > 1 ?
args[ 1 ].springStiffness :
5.0, 5.0,
animationTime: args.length > 1 ? /**
args[ 1 ].animationTime : * Animation duration per spring.
* @member {Number} animationTime
* @memberof OpenSeadragon.Spring#
*/
animationTime: args.length > 1 ?
args[ 1 ].animationTime :
1.5 1.5
}; };
} }
$.extend( true, this, options); $.extend( true, this, options);
/**
* @member {Object} current
* @memberof OpenSeadragon.Spring#
* @property {Number} value
* @property {Number} time
*/
this.current = { this.current = {
value: typeof ( this.initial ) == "number" ? value: typeof ( this.initial ) == "number" ?
this.initial : this.initial :
0, 0,
time: new Date().getTime() // always work in milliseconds time: $.now() // always work in milliseconds
}; };
/**
* @member {Object} start
* @memberof OpenSeadragon.Spring#
* @property {Number} value
* @property {Number} time
*/
this.start = { this.start = {
value: this.current.value, value: this.current.value,
time: this.current.time time: this.current.time
}; };
/**
* @member {Object} target
* @memberof OpenSeadragon.Spring#
* @property {Number} value
* @property {Number} time
*/
this.target = { this.target = {
value: this.current.value, value: this.current.value,
time: this.current.time time: this.current.time
}; };
}; };
$.Spring.prototype = { $.Spring.prototype = /** @lends OpenSeadragon.Spring.prototype */{
/** /**
* @function * @function
@ -93,14 +147,14 @@ $.Spring.prototype = {
* @function * @function
*/ */
update: function() { update: function() {
this.current.time = new Date().getTime(); this.current.time = $.now();
this.current.value = (this.current.time >= this.target.time) ? this.current.value = (this.current.time >= this.target.time) ?
this.target.value : this.target.value :
this.start.value + this.start.value +
( this.target.value - this.start.value ) * ( this.target.value - this.start.value ) *
transform( transform(
this.springStiffness, this.springStiffness,
( this.current.time - this.start.time ) / ( this.current.time - this.start.time ) /
( this.target.time - this.start.time ) ( this.target.time - this.start.time )
); );
} }
@ -110,7 +164,7 @@ $.Spring.prototype = {
* @private * @private
*/ */
function transform( stiffness, x ) { function transform( stiffness, x ) {
return ( 1.0 - Math.exp( stiffness * -x ) ) / return ( 1.0 - Math.exp( stiffness * -x ) ) /
( 1.0 - Math.exp( -stiffness ) ); ( 1.0 - Math.exp( -stiffness ) );
} }

View File

@ -1,26 +1,52 @@
/*
* OpenSeadragon - getString/setString
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function( $ ){ (function( $ ){
//TODO: I guess this is where the i18n needs to be reimplemented. I'll look //TODO: I guess this is where the i18n needs to be reimplemented. I'll look
// into existing patterns for i18n in javascript but i think that mimicking // into existing patterns for i18n in javascript but i think that mimicking
// pythons gettext might be a reasonable approach. // pythons gettext might be a reasonable approach.
var I18N = { var I18N = {
Errors: { Errors: {
Failure: "Sorry, but Seadragon Ajax can't run on your browser!\n" +
"Please try using IE 7 or Firefox 3.\n",
Dzc: "Sorry, we don't support Deep Zoom Collections!", Dzc: "Sorry, we don't support Deep Zoom Collections!",
Dzi: "Hmm, this doesn't appear to be a valid Deep Zoom Image.", Dzi: "Hmm, this doesn't appear to be a valid Deep Zoom Image.",
Xml: "Hmm, this doesn't appear to be a valid Deep Zoom Image.", Xml: "Hmm, this doesn't appear to be a valid Deep Zoom Image.",
Empty: "You asked us to open nothing, so we did just that.",
ImageFormat: "Sorry, we don't support {0}-based Deep Zoom Images.", ImageFormat: "Sorry, we don't support {0}-based Deep Zoom Images.",
Security: "It looks like a security restriction stopped us from " + Security: "It looks like a security restriction stopped us from " +
"loading this Deep Zoom Image.", "loading this Deep Zoom Image.",
Status: "This space unintentionally left blank ({0} {1}).", Status: "This space unintentionally left blank ({0} {1}).",
Unknown: "Whoops, something inexplicably went wrong. Sorry!" OpenFailed: "Unable to open {0}: {1}"
},
Messages: {
Loading: "Loading..."
}, },
Tooltips: { Tooltips: {
@ -29,19 +55,20 @@ var I18N = {
ZoomIn: "Zoom in", ZoomIn: "Zoom in",
ZoomOut: "Zoom out", ZoomOut: "Zoom out",
NextPage: "Next page", NextPage: "Next page",
PreviousPage: "Previous page" PreviousPage: "Previous page",
RotateLeft: "Rotate left",
RotateRight: "Rotate right"
} }
}; };
$.extend( $, { $.extend( $, /** @lends OpenSeadragon */{
/** /**
* @function * @function
* @name OpenSeadragon.getString
* @param {String} property * @param {String} property
*/ */
getString: function( prop ) { getString: function( prop ) {
var props = prop.split('.'), var props = prop.split('.'),
string = null, string = null,
args = arguments, args = arguments,
@ -55,27 +82,27 @@ $.extend( $, {
string = container[ props[ i ] ]; string = container[ props[ i ] ];
if ( typeof( string ) != "string" ) { if ( typeof( string ) != "string" ) {
string = ""; $.console.debug( "Untranslated source string:", prop );
string = ""; // FIXME: this breaks gettext()-style convention, which would return source
} }
return string.replace(/\{\d+\}/g, function(capture) { return string.replace(/\{\d+\}/g, function(capture) {
var i = parseInt( capture.match( /\d+/ ), 10 ) + 1; var i = parseInt( capture.match( /\d+/ ), 10 ) + 1;
return i < args.length ? return i < args.length ?
args[ i ] : args[ i ] :
""; "";
}); });
}, },
/** /**
* @function * @function
* @name OpenSeadragon.setString
* @param {String} property * @param {String} property
* @param {*} value * @param {*} value
*/ */
setString: function( prop, value ) { setString: function( prop, value ) {
var props = prop.split('.'), var props = prop.split('.'),
container = $.Strings, container = I18N,
i; i;
for ( i = 0; i < props.length - 1; i++ ) { for ( i = 0; i < props.length - 1; i++ ) {

View File

@ -1,67 +1,181 @@
/*
* OpenSeadragon - Tile
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function( $ ){ (function( $ ){
var TILE_CACHE = {}; var TILE_CACHE = {};
/** /**
* @class * @class Tile
* @memberof OpenSeadragon
* @param {Number} level The zoom level this tile belongs to. * @param {Number} level The zoom level this tile belongs to.
* @param {Number} x The vector component 'x'. * @param {Number} x The vector component 'x'.
* @param {Number} y The vector component 'y'. * @param {Number} y The vector component 'y'.
* @param {OpenSeadragon.Point} bounds Where this tile fits, in normalized * @param {OpenSeadragon.Point} bounds Where this tile fits, in normalized
* coordinates. * coordinates.
* @param {Boolean} exists Is this tile a part of a sparse image? ( Also has * @param {Boolean} exists Is this tile a part of a sparse image? ( Also has
* this tile failed to load? ) * this tile failed to load? )
* @param {String} url The URL of this tile's image. * @param {String} url The URL of this tile's image.
*
* @property {Number} level The zoom level this tile belongs to.
* @property {Number} x The vector component 'x'.
* @property {Number} y The vector component 'y'.
* @property {OpenSeadragon.Point} bounds Where this tile fits, in normalized
* coordinates
* @property {Boolean} exists Is this tile a part of a sparse image? ( Also has
* this tile failed to load?
* @property {String} url The URL of this tile's image.
* @property {Boolean} loaded Is this tile loaded?
* @property {Boolean} loading Is this tile loading
* @property {Element} element The HTML element for this tile
* @property {Image} image The Image object for this tile
* @property {String} style The alias of this.element.style.
* @property {String} position This tile's position on screen, in pixels.
* @property {String} size This tile's size on screen, in pixels
* @property {String} blendStart The start time of this tile's blending
* @property {String} opacity The current opacity this tile should be.
* @property {String} distance The distance of this tile to the viewport center
* @property {String} visibility The visibility score of this tile.
* @property {Boolean} beingDrawn Whether this tile is currently being drawn
* @property {Number} lastTouchTime Timestamp the tile was last touched.
*/ */
$.Tile = function(level, x, y, bounds, exists, url) { $.Tile = function(level, x, y, bounds, exists, url) {
/**
* The zoom level this tile belongs to.
* @member {Number} level
* @memberof OpenSeadragon.Tile#
*/
this.level = level; this.level = level;
/**
* The vector component 'x'.
* @member {Number} x
* @memberof OpenSeadragon.Tile#
*/
this.x = x; this.x = x;
/**
* The vector component 'y'.
* @member {Number} y
* @memberof OpenSeadragon.Tile#
*/
this.y = y; this.y = y;
/**
* Where this tile fits, in normalized coordinates
* @member {OpenSeadragon.Point} bounds
* @memberof OpenSeadragon.Tile#
*/
this.bounds = bounds; this.bounds = bounds;
/**
* Is this tile a part of a sparse image? Also has this tile failed to load?
* @member {Boolean} exists
* @memberof OpenSeadragon.Tile#
*/
this.exists = exists; this.exists = exists;
/**
* The URL of this tile's image.
* @member {String} url
* @memberof OpenSeadragon.Tile#
*/
this.url = url; this.url = url;
/**
* Is this tile loaded?
* @member {Boolean} loaded
* @memberof OpenSeadragon.Tile#
*/
this.loaded = false; this.loaded = false;
/**
* Is this tile loading?
* @member {Boolean} loading
* @memberof OpenSeadragon.Tile#
*/
this.loading = false; this.loading = false;
/**
* The HTML div element for this tile
* @member {Element} element
* @memberof OpenSeadragon.Tile#
*/
this.element = null; this.element = null;
/**
* The HTML img element for this tile.
* @member {Element} imgElement
* @memberof OpenSeadragon.Tile#
*/
this.imgElement = null;
/**
* The Image object for this tile.
* @member {Object} image
* @memberof OpenSeadragon.Tile#
*/
this.image = null; this.image = null;
/**
* The alias of this.element.style.
* @member {String} style
* @memberof OpenSeadragon.Tile#
*/
this.style = null; this.style = null;
/**
* This tile's position on screen, in pixels.
* @member {OpenSeadragon.Point} position
* @memberof OpenSeadragon.Tile#
*/
this.position = null; this.position = null;
/**
* This tile's size on screen, in pixels.
* @member {OpenSeadragon.Point} size
* @memberof OpenSeadragon.Tile#
*/
this.size = null; this.size = null;
/**
* The start time of this tile's blending.
* @member {Number} blendStart
* @memberof OpenSeadragon.Tile#
*/
this.blendStart = null; this.blendStart = null;
/**
* The current opacity this tile should be.
* @member {Number} opacity
* @memberof OpenSeadragon.Tile#
*/
this.opacity = null; this.opacity = null;
/**
* The distance of this tile to the viewport center.
* @member {Number} distance
* @memberof OpenSeadragon.Tile#
*/
this.distance = null; this.distance = null;
/**
* The visibility score of this tile.
* @member {Number} visibility
* @memberof OpenSeadragon.Tile#
*/
this.visibility = null; this.visibility = null;
/**
* Whether this tile is currently being drawn.
* @member {Boolean} beingDrawn
* @memberof OpenSeadragon.Tile#
*/
this.beingDrawn = false; this.beingDrawn = false;
/**
* Timestamp the tile was last touched.
* @member {Number} lastTouchTime
* @memberof OpenSeadragon.Tile#
*/
this.lastTouchTime = 0; this.lastTouchTime = 0;
}; };
$.Tile.prototype = { $.Tile.prototype = /** @lends OpenSeadragon.Tile.prototype */{
/** /**
* Provides a string representation of this tiles level and (x,y) * Provides a string representation of this tiles level and (x,y)
* components. * components.
* @function * @function
* @returns {String} * @returns {String}
@ -76,9 +190,6 @@ $.Tile.prototype = {
* @param {Element} container * @param {Element} container
*/ */
drawHTML: function( container ) { drawHTML: function( container ) {
var containerSize = $.getElementSize( container );
if ( !this.loaded || !this.image ) { if ( !this.loaded || !this.image ) {
$.console.warn( $.console.warn(
"Attempting to draw tile %s when it's not yet loaded.", "Attempting to draw tile %s when it's not yet loaded.",
@ -87,33 +198,16 @@ $.Tile.prototype = {
return; return;
} }
/* EXISTING IMPLEMENTATION
if ( !this.element ) {
this.element = $.makeNeutralElement("img");
this.element.src = this.url;
this.style = this.element.style;
this.style.position = "absolute";
this.style.msInterpolationMode = "nearest-neighbor";
}
if ( this.element.parentNode != container ) {
container.appendChild( this.element );
}
this.style.top = position.y + "px";
this.style.left = position.x + "px";
this.style.height = size.y + "px";
this.style.width = size.x + "px";
*/
//EXPERIMENTAL - trying to figure out how to scale the container //EXPERIMENTAL - trying to figure out how to scale the container
// content during animation of the container size. // content during animation of the container size.
if ( !this.element ) { if ( !this.element ) {
this.element = $.makeNeutralElement("img"); this.element = $.makeNeutralElement( "div" );
this.element.src = this.url; this.imgElement = $.makeNeutralElement( "img" );
this.element.style.msInterpolationMode = "nearest-neighbor"; this.imgElement.src = this.url;
this.imgElement.style.msInterpolationMode = "nearest-neighbor";
this.imgElement.style.width = "100%";
this.imgElement.style.height = "100%";
this.style = this.element.style; this.style = this.element.style;
this.style.position = "absolute"; this.style.position = "absolute";
@ -121,23 +215,26 @@ $.Tile.prototype = {
if ( this.element.parentNode != container ) { if ( this.element.parentNode != container ) {
container.appendChild( this.element ); container.appendChild( this.element );
} }
if ( this.imgElement.parentNode != this.element ) {
this.element.appendChild( this.imgElement );
}
this.style.top = this.position.y + "px";
this.style.left = this.position.x + "px";
this.style.height = this.size.y + "px";
this.style.width = this.size.x + "px";
this.style.top = 100 * ( this.position.y / containerSize.y ) + "%";
this.style.left = 100 * ( this.position.x / containerSize.x ) + "%";
this.style.height = 100 * ( this.size.y / containerSize.y ) + "%";
this.style.width = 100 * ( this.size.x / containerSize.x ) + "%";
$.setElementOpacity( this.element, this.opacity ); $.setElementOpacity( this.element, this.opacity );
}, },
/** /**
* Renders the tile in a canvas-based context. * Renders the tile in a canvas-based context.
* @function * @function
* @param {Canvas} context * @param {Canvas} context
* @param {Function} method for firing the drawing event. drawingHandler({context, tile, rendered})
* where <code>rendered</code> is the context with the pre-drawn image.
*/ */
drawCanvas: function( context ) { drawCanvas: function( context, drawingHandler ) {
var position = this.position, var position = this.position,
size = this.size, size = this.size,
@ -162,11 +259,11 @@ $.Tile.prototype = {
if( context.globalAlpha == 1 && this.url.match('.png') ){ if( context.globalAlpha == 1 && this.url.match('.png') ){
//clearing only the inside of the rectangle occupied //clearing only the inside of the rectangle occupied
//by the png prevents edge flikering //by the png prevents edge flikering
context.clearRect( context.clearRect(
position.x+1, position.x+1,
position.y+1, position.y+1,
size.x-2, size.x-2,
size.y-2 size.y-2
); );
} }
@ -184,18 +281,21 @@ $.Tile.prototype = {
} }
rendered = TILE_CACHE[ this.url ]; rendered = TILE_CACHE[ this.url ];
// This gives the application a chance to make image manipulation changes as we are rendering the image
drawingHandler({context: context, tile: this, rendered: rendered});
//rendered.save(); //rendered.save();
context.drawImage( context.drawImage(
rendered.canvas, rendered.canvas,
0, 0,
0, 0,
rendered.canvas.width, rendered.canvas.width,
rendered.canvas.height, rendered.canvas.height,
position.x, position.x,
position.y, position.y,
size.x, size.x,
size.y size.y
); );
//rendered.restore(); //rendered.restore();
@ -203,21 +303,25 @@ $.Tile.prototype = {
}, },
/** /**
* Removes tile from it's contianer. * Removes tile from its container.
* @function * @function
*/ */
unload: function() { unload: function() {
if ( this.imgElement && this.imgElement.parentNode ) {
this.imgElement.parentNode.removeChild( this.imgElement );
}
if ( this.element && this.element.parentNode ) { if ( this.element && this.element.parentNode ) {
this.element.parentNode.removeChild( this.element ); this.element.parentNode.removeChild( this.element );
} }
if ( TILE_CACHE[ this.url ]){ if ( TILE_CACHE[ this.url ]){
delete TILE_CACHE[ this.url ]; delete TILE_CACHE[ this.url ];
} }
this.element = null; this.element = null;
this.image = null; this.imgElement = null;
this.loaded = false; this.image = null;
this.loading = false; this.loaded = false;
this.loading = false;
} }
}; };

View File

@ -1,25 +1,61 @@
/*
* OpenSeadragon - TileSource
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function( $ ){ (function( $ ){
/** /**
* The TileSource contains the most basic implementation required to create a * @class TileSource
* smooth transition between layer in an image pyramid. It has only a single key * @classdesc The TileSource contains the most basic implementation required to create a
* interface that must be implemented to complete it key functionality: * smooth transition between layer in an image pyramid. It has only a single key
* 'getTileUrl'. It also has several optional interfaces that can be * interface that must be implemented to complete it key functionality:
* 'getTileUrl'. It also has several optional interfaces that can be
* implemented if a new TileSource wishes to support configuration via a simple * implemented if a new TileSource wishes to support configuration via a simple
* object or array ('configure') and if the tile source supports or requires * object or array ('configure') and if the tile source supports or requires
* configuration via retreival of a document on the network ala AJAX or JSONP, * configuration via retreival of a document on the network ala AJAX or JSONP,
* ('getImageInfo'). * ('getImageInfo').
* <br/> * <br/>
* By default the image pyramid is split into N layers where the images longest * By default the image pyramid is split into N layers where the images longest
* side in M (in pixels), where N is the smallest integer which satisfies * side in M (in pixels), where N is the smallest integer which satisfies
* <strong>2^(N+1) >= M</strong>. * <strong>2^(N+1) >= M</strong>.
* @class *
* @extends OpenSeadragon.EventHandler * @memberof OpenSeadragon
* @param {Number|Object|Array|String} width * @extends OpenSeadragon.EventSource
* If more than a single argument is supplied, the traditional use of * @param {Number|Object|Array|String} width
* positional parameters is supplied and width is expected to be the width * If more than a single argument is supplied, the traditional use of
* source image at it's max resolution in pixels. If a single argument is supplied and * positional parameters is supplied and width is expected to be the width
* source image at its max resolution in pixels. If a single argument is supplied and
* it is an Object or Array, the construction is assumed to occur through * it is an Object or Array, the construction is assumed to occur through
* the extending classes implementation of 'configure'. Finally if only a * the extending classes implementation of 'configure'. Finally if only a
* single argument is supplied and it is a String, the extending class is * single argument is supplied and it is a String, the extending class is
@ -28,7 +64,7 @@
* Width of the source image at max resolution in pixels. * Width of the source image at max resolution in pixels.
* @param {Number} tileSize * @param {Number} tileSize
* The size of the tiles to assumed to make up each pyramid layer in pixels. * The size of the tiles to assumed to make up each pyramid layer in pixels.
* Tile size determines the point at which the image pyramid must be * Tile size determines the point at which the image pyramid must be
* divided into a matrix of smaller images. * divided into a matrix of smaller images.
* @param {Number} tileOverlap * @param {Number} tileOverlap
* The number of pixels each tile is expected to overlap touching tiles. * The number of pixels each tile is expected to overlap touching tiles.
@ -36,23 +72,9 @@
* The minimum level to attempt to load. * The minimum level to attempt to load.
* @param {Number} maxLevel * @param {Number} maxLevel
* The maximum level to attempt to load. * The maximum level to attempt to load.
* @property {Number} aspectRatio */
* Ratio of width to height
* @property {OpenSeadragon.Point} dimensions
* Vector storing x and y dimensions ( width and height respectively ).
* @property {Number} tileSize
* The size of the image tiles used to compose the image.
* @property {Number} tileOverlap
* The overlap in pixels each tile shares with it's adjacent neighbors.
* @property {Number} minLevel
* The minimum pyramid level this tile source supports or should attempt to load.
* @property {Number} maxLevel
* The maximum pyramid level this tile source supports or should attempt to load.
*/
$.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLevel ) { $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLevel ) {
var _this = this, var callback = null,
callback = null,
readyHandler = null,
args = arguments, args = arguments,
options, options,
i; i;
@ -71,8 +93,8 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
} }
//Tile sources supply some events, namely 'ready' when they must be configured //Tile sources supply some events, namely 'ready' when they must be configured
//by asyncronously fetching their configuration data. //by asynchronously fetching their configuration data.
$.EventHandler.call( this ); $.EventSource.call( this );
//we allow options to override anything we dont treat as //we allow options to override anything we dont treat as
//required via idiomatic options or which is functionally //required via idiomatic options or which is functionally
@ -82,17 +104,53 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
//Any functions that are passed as arguments are bound to the ready callback //Any functions that are passed as arguments are bound to the ready callback
/*jshint loopfunc:true*/ /*jshint loopfunc:true*/
for( i = 0; i < arguments.length; i++ ){ for ( i = 0; i < arguments.length; i++ ) {
if( $.isFunction( arguments[i] ) ){ if ( $.isFunction( arguments[ i ] ) ) {
callback = arguments[ i ]; callback = arguments[ i ];
this.addHandler( 'ready', function( placeHolderSource, readySource ){ this.addHandler( 'ready', function ( event ) {
callback( readySource ); callback( event );
}); } );
//only one callback per constructor //only one callback per constructor
break; break;
} }
} }
/**
* Ratio of width to height
* @member {Number} aspectRatio
* @memberof OpenSeadragon.TileSource#
*/
/**
* Vector storing x and y dimensions ( width and height respectively ).
* @member {OpenSeadragon.Point} dimensions
* @memberof OpenSeadragon.TileSource#
*/
/**
* The size of the image tiles used to compose the image.
* @member {Number} tileSize
* @memberof OpenSeadragon.TileSource#
*/
/**
* The overlap in pixels each tile shares with its adjacent neighbors.
* @member {Number} tileOverlap
* @memberof OpenSeadragon.TileSource#
*/
/**
* The minimum pyramid level this tile source supports or should attempt to load.
* @member {Number} minLevel
* @memberof OpenSeadragon.TileSource#
*/
/**
* The maximum pyramid level this tile source supports or should attempt to load.
* @member {Number} maxLevel
* @memberof OpenSeadragon.TileSource#
*/
/**
*
* @member {Boolean} ready
* @memberof OpenSeadragon.TileSource#
*/
if( 'string' == $.type( arguments[ 0 ] ) ){ if( 'string' == $.type( arguments[ 0 ] ) ){
//in case the getImageInfo method is overriden and/or implies an //in case the getImageInfo method is overriden and/or implies an
//async mechanism set some safe defaults first //async mechanism set some safe defaults first
@ -103,26 +161,26 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
this.minLevel = 0; this.minLevel = 0;
this.maxLevel = 0; this.maxLevel = 0;
this.ready = false; this.ready = false;
//configuration via url implies the extending class //configuration via url implies the extending class
//implements and 'configure' //implements and 'configure'
this.getImageInfo( arguments[ 0 ] ); this.getImageInfo( arguments[ 0 ] );
} else { } else {
//explicit configuration via positional args in constructor //explicit configuration via positional args in constructor
//or the more idiomatic 'options' object //or the more idiomatic 'options' object
this.ready = true; this.ready = true;
this.aspectRatio = ( options.width && options.height ) ? this.aspectRatio = ( options.width && options.height ) ?
( options.width / options.height ) : 1; ( options.width / options.height ) : 1;
this.dimensions = new $.Point( options.width, options.height ); this.dimensions = new $.Point( options.width, options.height );
this.tileSize = options.tileSize ? options.tileSize : 0; this.tileSize = options.tileSize ? options.tileSize : 0;
this.tileOverlap = options.tileOverlap ? options.tileOverlap : 0; this.tileOverlap = options.tileOverlap ? options.tileOverlap : 0;
this.minLevel = options.minLevel ? options.minLevel : 0; this.minLevel = options.minLevel ? options.minLevel : 0;
this.maxLevel = ( undefined !== options.maxLevel && null !== options.maxLevel ) ? this.maxLevel = ( undefined !== options.maxLevel && null !== options.maxLevel ) ?
options.maxLevel : ( options.maxLevel : (
( options.width && options.height ) ? Math.ceil( ( options.width && options.height ) ? Math.ceil(
Math.log( Math.max( options.width, options.height ) ) / Math.log( Math.max( options.width, options.height ) ) /
Math.log( 2 ) Math.log( 2 )
) : 0 ) : 0
); );
if( callback && $.isFunction( callback ) ){ if( callback && $.isFunction( callback ) ){
@ -134,8 +192,8 @@ $.TileSource = function( width, height, tileSize, tileOverlap, minLevel, maxLeve
}; };
$.TileSource.prototype = { $.TileSource.prototype = /** @lends OpenSeadragon.TileSource.prototype */{
/** /**
* @function * @function
* @param {Number} level * @param {Number} level
@ -230,7 +288,7 @@ $.TileSource.prototype = {
return new $.Rect( px * scale, py * scale, sx * scale, sy * scale ); return new $.Rect( px * scale, py * scale, sx * scale, sy * scale );
}, },
/** /**
* Responsible for retrieving, and caching the * Responsible for retrieving, and caching the
@ -240,16 +298,14 @@ $.TileSource.prototype = {
* @throws {Error} * @throws {Error}
*/ */
getImageInfo: function( url ) { getImageInfo: function( url ) {
var _this = this, var _this = this,
error,
callbackName, callbackName,
callback, callback,
readySource, readySource,
options, options,
urlParts, urlParts,
filename, filename,
lastDot, lastDot;
tilesUrl;
if( url ) { if( url ) {
@ -260,13 +316,42 @@ $.TileSource.prototype = {
urlParts[ urlParts.length - 1 ] = filename.slice( 0, lastDot ); urlParts[ urlParts.length - 1 ] = filename.slice( 0, lastDot );
} }
} }
callback = function( data ){ callback = function( data ){
if( typeof(data) === "string" ) {
data = $.parseXml( data );
}
var $TileSource = $.TileSource.determineType( _this, data, url ); var $TileSource = $.TileSource.determineType( _this, data, url );
if ( !$TileSource ) {
/**
* Raised when an error occurs loading a TileSource.
*
* @event open-failed
* @memberof OpenSeadragon.TileSource
* @type {object}
* @property {OpenSeadragon.TileSource} eventSource - A reference to the TileSource which raised the event.
* @property {String} message
* @property {String} source
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
_this.raiseEvent( 'open-failed', { message: "Unable to load TileSource", source: url } );
return;
}
options = $TileSource.prototype.configure.apply( _this, [ data, url ]); options = $TileSource.prototype.configure.apply( _this, [ data, url ]);
readySource = new $TileSource( options ); readySource = new $TileSource( options );
_this.ready = true; _this.ready = true;
_this.raiseEvent( 'ready', readySource ); /**
* Raised when a TileSource is opened and initialized.
*
* @event ready
* @memberof OpenSeadragon.TileSource
* @type {object}
* @property {OpenSeadragon.TileSource} eventSource - A reference to the TileSource which raised the event.
* @property {Object} tileSource
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
_this.raiseEvent( 'ready', { tileSource: readySource } );
}; };
if( url.match(/\.js$/) ){ if( url.match(/\.js$/) ){
@ -281,10 +366,46 @@ $.TileSource.prototype = {
callback: callback callback: callback
}); });
} else { } else {
// request info via xhr asyncronously. // request info via xhr asynchronously.
$.makeAjaxRequest( url, function( xhr ) { $.makeAjaxRequest( url, function( xhr ) {
var data = processResponse( xhr ); var data = processResponse( xhr );
callback( data ); callback( data );
}, function ( xhr, exc ) {
var msg;
/*
IE < 10 will block XHR requests to different origins. Any property access on the request
object will raise an exception which we'll attempt to handle by formatting the original
exception rather than the second one raised when we try to access xhr.status
*/
try {
msg = "HTTP " + xhr.status + " attempting to load TileSource";
} catch ( e ) {
var formattedExc;
if ( typeof( exc ) == "undefined" || !exc.toString ) {
formattedExc = "Unknown error";
} else {
formattedExc = exc.toString();
}
msg = formattedExc + " attempting to load TileSource";
}
/***
* Raised when an error occurs loading a TileSource.
*
* @event open-failed
* @memberof OpenSeadragon.TileSource
* @type {object}
* @property {OpenSeadragon.TileSource} eventSource - A reference to the TileSource which raised the event.
* @property {String} message
* @property {String} source
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
_this.raiseEvent( 'open-failed', {
message: msg,
source: url
});
}); });
} }
@ -300,7 +421,7 @@ $.TileSource.prototype = {
* and sufficient mechanisim for clear determination. * and sufficient mechanisim for clear determination.
* @function * @function
* @param {String|Object|Array|Document} data * @param {String|Object|Array|Document} data
* @param {String} url - the url the data was loaded * @param {String} url - the url the data was loaded
* from if any. * from if any.
* @return {Boolean} * @return {Boolean}
*/ */
@ -312,14 +433,14 @@ $.TileSource.prototype = {
* Responsible for parsing and configuring the * Responsible for parsing and configuring the
* image metadata pertinent to this TileSources implementation. * image metadata pertinent to this TileSources implementation.
* This method is not implemented by this class other than to throw an Error * This method is not implemented by this class other than to throw an Error
* announcing you have to implement it. Because of the variety of tile * announcing you have to implement it. Because of the variety of tile
* server technologies, and various specifications for building image * server technologies, and various specifications for building image
* pyramids, this method is here to allow easy integration. * pyramids, this method is here to allow easy integration.
* @function * @function
* @param {String|Object|Array|Document} data * @param {String|Object|Array|Document} data
* @param {String} url - the url the data was loaded * @param {String} url - the url the data was loaded
* from if any. * from if any.
* @return {Object} options - A dictionary of keyword arguments sufficient * @return {Object} options - A dictionary of keyword arguments sufficient
* to configure this tile sources constructor. * to configure this tile sources constructor.
* @throws {Error} * @throws {Error}
*/ */
@ -328,10 +449,10 @@ $.TileSource.prototype = {
}, },
/** /**
* Responsible for retriving the url which will return an image for the * Responsible for retriving the url which will return an image for the
* region speified by the given x, y, and level components. * region speified by the given x, y, and level components.
* This method is not implemented by this class other than to throw an Error * This method is not implemented by this class other than to throw an Error
* announcing you have to implement it. Because of the variety of tile * announcing you have to implement it. Because of the variety of tile
* server technologies, and various specifications for building image * server technologies, and various specifications for building image
* pyramids, this method is here to allow easy integration. * pyramids, this method is here to allow easy integration.
* @function * @function
@ -352,23 +473,23 @@ $.TileSource.prototype = {
*/ */
tileExists: function( level, x, y ) { tileExists: function( level, x, y ) {
var numTiles = this.getNumTiles( level ); var numTiles = this.getNumTiles( level );
return level >= this.minLevel && return level >= this.minLevel &&
level <= this.maxLevel && level <= this.maxLevel &&
x >= 0 && x >= 0 &&
y >= 0 && y >= 0 &&
x < numTiles.x && x < numTiles.x &&
y < numTiles.y; y < numTiles.y;
} }
}; };
$.extend( true, $.TileSource.prototype, $.EventHandler.prototype ); $.extend( true, $.TileSource.prototype, $.EventSource.prototype );
/** /**
* Decides whether to try to process the response as xml, json, or hand back * Decides whether to try to process the response as xml, json, or hand back
* the text * the text
* @eprivate * @private
* @inner * @inner
* @function * @function
* @param {XMLHttpRequest} xhr - the completed network request * @param {XMLHttpRequest} xhr - the completed network request
@ -383,16 +504,16 @@ 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 ) );
} }
if( responseText.match(/\s*<.*/) ){ if( responseText.match(/\s*<.*/) ){
try{ try{
data = ( xhr.responseXML && xhr.responseXML.documentElement ) ? data = ( xhr.responseXML && xhr.responseXML.documentElement ) ?
xhr.responseXML : xhr.responseXML :
$.parseXml( responseText ); $.parseXml( responseText );
} catch (e){ } catch (e){
data = xhr.responseText; data = xhr.responseText;
@ -410,7 +531,7 @@ function processResponse( xhr ){
/** /**
* Determines the TileSource Implementation by introspection of OpenSeadragon * Determines the TileSource Implementation by introspection of OpenSeadragon
* namespace, calling each TileSource implementation of 'isType' * namespace, calling each TileSource implementation of 'isType'
* @eprivate * @private
* @inner * @inner
* @function * @function
* @param {Object|Array|Document} data - the tile source configuration object * @param {Object|Array|Document} data - the tile source configuration object
@ -428,6 +549,8 @@ $.TileSource.determineType = function( tileSource, data, url ){
return OpenSeadragon[ property ]; return OpenSeadragon[ property ];
} }
} }
$.console.error( "No TileSource was able to open %s %s", url, data );
}; };

View File

@ -1,12 +1,47 @@
/*
* OpenSeadragon - TileSourceCollection
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function( $ ){ (function( $ ){
/** /**
* @class * @class TileSourceCollection
* @memberof OpenSeadragon
* @extends OpenSeadragon.TileSource * @extends OpenSeadragon.TileSource
*/ */
$.TileSourceCollection = function( tileSize, tileSources, rows, layout ) { $.TileSourceCollection = function( tileSize, tileSources, rows, layout ) {
var options;
if( $.isPlainObject( tileSize ) ){ if( $.isPlainObject( tileSize ) ){
options = tileSize; options = tileSize;
}else{ }else{
@ -51,18 +86,17 @@ $.TileSourceCollection = function( tileSize, tileSources, rows, layout ) {
options.minLevel = minLevel; options.minLevel = minLevel;
//for( var name in options ){ //for( var name in options ){
// $.console.log( 'Collection %s %s', name, options[ name ] ); // $.console.log( 'Collection %s %s', name, options[ name ] );
//} //}
$.TileSource.apply( this, [ options ] ); $.TileSource.apply( this, [ options ] );
}; };
$.extend( $.TileSourceCollection.prototype, $.TileSource.prototype, { $.extend( $.TileSourceCollection.prototype, $.TileSource.prototype, /** @lends OpenSeadragon.TileSourceCollection.prototype */{
/** /**
* @function * @function
* @name OpenSeadragon.TileSourceCollection.prototype.getTileBounds
* @param {Number} level * @param {Number} level
* @param {Number} x * @param {Number} x
* @param {Number} y * @param {Number} y
@ -82,9 +116,8 @@ $.extend( $.TileSourceCollection.prototype, $.TileSource.prototype, {
}, },
/** /**
* *
* @function * @function
* @name OpenSeadragon.TileSourceCollection.prototype.configure
*/ */
configure: function( data, url ){ configure: function( data, url ){
return; return;
@ -93,7 +126,6 @@ $.extend( $.TileSourceCollection.prototype, $.TileSource.prototype, {
/** /**
* @function * @function
* @name OpenSeadragon.TileSourceCollection.prototype.getTileUrl
* @param {Number} level * @param {Number} level
* @param {Number} x * @param {Number} x
* @param {Number} y * @param {Number} y
@ -104,7 +136,7 @@ $.extend( $.TileSourceCollection.prototype, $.TileSource.prototype, {
} }
}); });

View File

@ -1,12 +1,54 @@
(function( $ ){ /*
* OpenSeadragon - TmsTileSource
/**
* A tilesource implementation for Tiled Map Services (TMS). Adopted from Rainer Simon
* project http://github.com/rsimon/seajax-utils. TMS tile
* scheme ( [ as supported by OpenLayers ] is described here
* ( http://openlayers.org/dev/examples/tms.html ) )
* *
* @class * Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Derived from the TMS tile source in Rainer Simon's seajax-utils project
* <http://github.com/rsimon/seajax-utils>. Rainer Simon has contributed
* the included code to the OpenSeadragon project under the New BSD license;
* see <https://github.com/openseadragon/openseadragon/issues/58>.
*/
(function( $ ){
/**
* @class TmsTileSource
* @classdesc A tilesource implementation for Tiled Map Services (TMS).
* TMS tile scheme ( [ as supported by OpenLayers ] is described here
* ( http://openlayers.org/dev/examples/tms.html ).
*
* @memberof OpenSeadragon
* @extends OpenSeadragon.TileSource * @extends OpenSeadragon.TileSource
* @param {Number|Object} width - the pixel width of the image or the idiomatic * @param {Number|Object} width - the pixel width of the image or the idiomatic
* options object which is used instead of positional arguments. * options object which is used instead of positional arguments.
@ -14,7 +56,7 @@
* @param {Number} tileSize * @param {Number} tileSize
* @param {Number} tileOverlap * @param {Number} tileOverlap
* @param {String} tilesUrl * @param {String} tilesUrl
*/ */
$.TmsTileSource = function( width, height, tileSize, tileOverlap, tilesUrl ) { $.TmsTileSource = function( width, height, tileSize, tileOverlap, tilesUrl ) {
var options; var options;
@ -45,19 +87,18 @@ $.TmsTileSource = function( width, height, tileSize, tileOverlap, tilesUrl ) {
options.tileSize = 256; options.tileSize = 256;
options.width = bufferedWidth; options.width = bufferedWidth;
options.height = bufferedHeight; options.height = bufferedHeight;
$.TileSource.apply( this, [ options ] ); $.TileSource.apply( this, [ options ] );
}; };
$.extend( $.TmsTileSource.prototype, $.TileSource.prototype, { $.extend( $.TmsTileSource.prototype, $.TileSource.prototype, /** @lends OpenSeadragon.TmsTileSource.prototype */{
/** /**
* Determine if the data and/or url imply the image service is supported by * Determine if the data and/or url imply the image service is supported by
* this tile source. * this tile source.
* @function * @function
* @name OpenSeadragon.TmsTileSource.prototype.supports
* @param {Object|Array} data * @param {Object|Array} data
* @param {String} optional - url * @param {String} optional - url
*/ */
@ -66,12 +107,11 @@ $.extend( $.TmsTileSource.prototype, $.TileSource.prototype, {
}, },
/** /**
* *
* @function * @function
* @name OpenSeadragon.TmsTileSource.prototype.configure
* @param {Object} data - the raw configuration * @param {Object} data - the raw configuration
* @param {String} url - the url the data was retreived from if any. * @param {String} url - the url the data was retreived from if any.
* @return {Object} options - A dictionary of keyword arguments sufficient * @return {Object} options - A dictionary of keyword arguments sufficient
* to configure this tile sources constructor. * to configure this tile sources constructor.
*/ */
configure: function( data, url ){ configure: function( data, url ){
@ -81,7 +121,6 @@ $.extend( $.TmsTileSource.prototype, $.TileSource.prototype, {
/** /**
* @function * @function
* @name OpenSeadragon.TmsTileSource.prototype.getTileUrl
* @param {Number} level * @param {Number} level
* @param {Number} x * @param {Number} x
* @param {Number} y * @param {Number} y
@ -95,4 +134,4 @@ $.extend( $.TmsTileSource.prototype, $.TileSource.prototype, {
}); });
}( OpenSeadragon )); }( OpenSeadragon ));

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,50 @@
/*
* OpenSeadragon - Viewport
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
(function( $ ){ (function( $ ){
/** /**
* @class * @class Viewport
* @classdesc Handles coordinate-related functionality (zoom, pan, rotation, etc.) for an {@link OpenSeadragon.Viewer}.
* A new instance is created for each TileSource opened (see {@link OpenSeadragon.Viewer#viewport}).
*
* @memberof OpenSeadragon
*/ */
$.Viewport = function( options ) { $.Viewport = function( options ) {
//backward compatibility for positional args while prefering more //backward compatibility for positional args while prefering more
//idiomatic javascript options object as the only argument //idiomatic javascript options object as the only argument
var args = arguments; var args = arguments;
if( args.length && args[ 0 ] instanceof $.Point ){ if( args.length && args[ 0 ] instanceof $.Point ){
@ -19,14 +57,14 @@ $.Viewport = function( options ) {
//options.config and the general config argument are deprecated //options.config and the general config argument are deprecated
//in favor of the more direct specification of optional settings //in favor of the more direct specification of optional settings
//being pass directly on the options object //being passed directly on the options object
if ( options.config ){ if ( options.config ){
$.extend( true, options, options.config ); $.extend( true, options, options.config );
delete options.config; delete options.config;
} }
$.extend( true, this, { $.extend( true, this, {
//required settings //required settings
containerSize: null, containerSize: null,
contentSize: null, contentSize: null,
@ -45,22 +83,23 @@ $.Viewport = function( options ) {
wrapVertical: $.DEFAULT_SETTINGS.wrapVertical, wrapVertical: $.DEFAULT_SETTINGS.wrapVertical,
defaultZoomLevel: $.DEFAULT_SETTINGS.defaultZoomLevel, defaultZoomLevel: $.DEFAULT_SETTINGS.defaultZoomLevel,
minZoomLevel: $.DEFAULT_SETTINGS.minZoomLevel, minZoomLevel: $.DEFAULT_SETTINGS.minZoomLevel,
maxZoomLevel: $.DEFAULT_SETTINGS.maxZoomLevel maxZoomLevel: $.DEFAULT_SETTINGS.maxZoomLevel,
degrees: $.DEFAULT_SETTINGS.degrees
}, options ); }, options );
this.centerSpringX = new $.Spring({ this.centerSpringX = new $.Spring({
initial: 0, initial: 0,
springStiffness: this.springStiffness, springStiffness: this.springStiffness,
animationTime: this.animationTime animationTime: this.animationTime
}); });
this.centerSpringY = new $.Spring({ this.centerSpringY = new $.Spring({
initial: 0, initial: 0,
springStiffness: this.springStiffness, springStiffness: this.springStiffness,
animationTime: this.animationTime animationTime: this.animationTime
}); });
this.zoomSpring = new $.Spring({ this.zoomSpring = new $.Spring({
initial: 1, initial: 1,
springStiffness: this.springStiffness, springStiffness: this.springStiffness,
animationTime: this.animationTime animationTime: this.animationTime
}); });
@ -70,11 +109,12 @@ $.Viewport = function( options ) {
this.update(); this.update();
}; };
$.Viewport.prototype = { $.Viewport.prototype = /** @lends OpenSeadragon.Viewport.prototype */{
/** /**
* @function * @function
* @return {OpenSeadragon.Viewport} Chainable. * @return {OpenSeadragon.Viewport} Chainable.
* @fires OpenSeadragon.Viewer.event:reset-size
*/ */
resetContentSize: function( contentSize ){ resetContentSize: function( contentSize ){
this.contentSize = contentSize; this.contentSize = contentSize;
@ -86,12 +126,21 @@ $.Viewport.prototype = {
this.homeBounds = new $.Rect( 0, 0, 1, this.contentAspectY ); this.homeBounds = new $.Rect( 0, 0, 1, this.contentAspectY );
if( this.viewer ){ if( this.viewer ){
this.viewer.raiseEvent( 'reset-size', { /**
contentSize: contentSize, * Raised when the viewer's content size is reset (see {@link OpenSeadragon.Viewport#resetContentSize}).
viewer: this.viewer *
* @event reset-size
* @memberof OpenSeadragon.Viewer
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {OpenSeadragon.Point} contentSize
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.viewer.raiseEvent( 'reset-size', {
contentSize: contentSize
}); });
} }
return this; return this;
}, },
@ -99,14 +148,14 @@ $.Viewport.prototype = {
* @function * @function
*/ */
getHomeZoom: function() { getHomeZoom: function() {
var aspectFactor = var aspectFactor =
this.contentAspectX / this.getAspectRatio(); this.contentAspectX / this.getAspectRatio();
if( this.defaultZoomLevel ){ if( this.defaultZoomLevel ){
return this.defaultZoomLevel; return this.defaultZoomLevel;
} else { } else {
return ( aspectFactor >= 1 ) ? return ( aspectFactor >= 1 ) ?
1 : 1 :
aspectFactor; aspectFactor;
} }
}, },
@ -120,9 +169,9 @@ $.Viewport.prototype = {
height = width / this.getAspectRatio(); height = width / this.getAspectRatio();
return new $.Rect( return new $.Rect(
center.x - ( width / 2.0 ), center.x - ( width / 2.0 ),
center.y - ( height / 2.0 ), center.y - ( height / 2.0 ),
width, width,
height height
); );
}, },
@ -130,12 +179,22 @@ $.Viewport.prototype = {
/** /**
* @function * @function
* @param {Boolean} immediately * @param {Boolean} immediately
* @fires OpenSeadragon.Viewer.event:home
*/ */
goHome: function( immediately ) { goHome: function( immediately ) {
if( this.viewer ){ if( this.viewer ){
this.viewer.raiseEvent( 'home', { /**
immediately: immediately, * Raised when the "home" operation occurs (see {@link OpenSeadragon.Viewport#goHome}).
viewer: this.viewer *
* @event home
* @memberof OpenSeadragon.Viewer
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {Boolean} immediately
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.viewer.raiseEvent( 'home', {
immediately: immediately
}); });
} }
return this.fitBounds( this.getHomeBounds(), immediately ); return this.fitBounds( this.getHomeBounds(), immediately );
@ -146,8 +205,8 @@ $.Viewport.prototype = {
*/ */
getMinZoom: function() { getMinZoom: function() {
var homeZoom = this.getHomeZoom(), var homeZoom = this.getHomeZoom(),
zoom = this.minZoomLevel ? zoom = this.minZoomLevel ?
this.minZoomLevel : this.minZoomLevel :
this.minZoomImageRatio * homeZoom; this.minZoomImageRatio * homeZoom;
return Math.min( zoom, homeZoom ); return Math.min( zoom, homeZoom );
@ -176,13 +235,14 @@ $.Viewport.prototype = {
*/ */
getContainerSize: function() { getContainerSize: function() {
return new $.Point( return new $.Point(
this.containerSize.x, this.containerSize.x,
this.containerSize.y this.containerSize.y
); );
}, },
/** /**
* @function * @function
* @param {Boolean} current - Pass true for the current location; defaults to false (target location).
*/ */
getBounds: function( current ) { getBounds: function( current ) {
var center = this.getCenter( current ), var center = this.getCenter( current ),
@ -190,15 +250,16 @@ $.Viewport.prototype = {
height = width / this.getAspectRatio(); height = width / this.getAspectRatio();
return new $.Rect( return new $.Rect(
center.x - ( width / 2.0 ), center.x - ( width / 2.0 ),
center.y - ( height / 2.0 ), center.y - ( height / 2.0 ),
width, width,
height height
); );
}, },
/** /**
* @function * @function
* @param {Boolean} current - Pass true for the current location; defaults to false (target location).
*/ */
getCenter: function( current ) { getCenter: function( current ) {
var centerCurrent = new $.Point( var centerCurrent = new $.Point(
@ -231,8 +292,8 @@ $.Viewport.prototype = {
height = width / this.getAspectRatio(); height = width / this.getAspectRatio();
bounds = new $.Rect( bounds = new $.Rect(
centerCurrent.x - width / 2.0, centerCurrent.x - width / 2.0,
centerCurrent.y - height / 2.0, centerCurrent.y - height / 2.0,
width, width,
height height
); );
@ -249,6 +310,7 @@ $.Viewport.prototype = {
/** /**
* @function * @function
* @param {Boolean} current - Pass true for the current location; defaults to false (target location).
*/ */
getZoom: function( current ) { getZoom: function( current ) {
if ( current ) { if ( current ) {
@ -261,11 +323,12 @@ $.Viewport.prototype = {
/** /**
* @function * @function
* @return {OpenSeadragon.Viewport} Chainable. * @return {OpenSeadragon.Viewport} Chainable.
* @fires OpenSeadragon.Viewer.event:constrain
*/ */
applyConstraints: function( immediately ) { applyConstraints: function( immediately ) {
var actualZoom = this.getZoom(), var actualZoom = this.getZoom(),
constrainedZoom = Math.max( constrainedZoom = Math.max(
Math.min( actualZoom, this.getMaxZoom() ), Math.min( actualZoom, this.getMaxZoom() ),
this.getMinZoom() this.getMinZoom()
), ),
bounds, bounds,
@ -275,10 +338,8 @@ $.Viewport.prototype = {
right, right,
top, top,
bottom, bottom,
center,
dx = 0, dx = 0,
dy = 0, dy = 0;
dx1 = 0, dx2 = 0, dy1 = 0, dy2 = 0;
if ( actualZoom != constrainedZoom ) { if ( actualZoom != constrainedZoom ) {
this.zoomTo( constrainedZoom, this.zoomPoint, immediately ); this.zoomTo( constrainedZoom, this.zoomPoint, immediately );
@ -299,9 +360,9 @@ $.Viewport.prototype = {
} else { } else {
if ( left < horizontalThreshold ) { if ( left < horizontalThreshold ) {
dx = horizontalThreshold - left; dx = horizontalThreshold - left;
} }
if ( right < horizontalThreshold ) { if ( right < horizontalThreshold ) {
dx = dx ? dx = dx ?
( dx + right - horizontalThreshold ) / 2 : ( dx + right - horizontalThreshold ) / 2 :
( right - horizontalThreshold ); ( right - horizontalThreshold );
} }
@ -312,9 +373,9 @@ $.Viewport.prototype = {
} else { } else {
if ( top < verticalThreshold ) { if ( top < verticalThreshold ) {
dy = ( verticalThreshold - top ); dy = ( verticalThreshold - top );
} }
if ( bottom < verticalThreshold ) { if ( bottom < verticalThreshold ) {
dy = dy ? dy = dy ?
( dy + bottom - verticalThreshold ) / 2 : ( dy + bottom - verticalThreshold ) / 2 :
( bottom - verticalThreshold ); ( bottom - verticalThreshold );
} }
@ -333,9 +394,18 @@ $.Viewport.prototype = {
} }
if( this.viewer ){ if( this.viewer ){
this.viewer.raiseEvent( 'constrain', { /**
immediately: immediately, * Raised when the viewport constraints are applied (see {@link OpenSeadragon.Viewport#applyConstraints}).
viewer: this.viewer *
* @event constrain
* @memberof OpenSeadragon.Viewer
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {Boolean} immediately
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.viewer.raiseEvent( 'constrain', {
immediately: immediately
}); });
} }
@ -360,9 +430,9 @@ $.Viewport.prototype = {
var aspect = this.getAspectRatio(), var aspect = this.getAspectRatio(),
center = bounds.getCenter(), center = bounds.getCenter(),
newBounds = new $.Rect( newBounds = new $.Rect(
bounds.x, bounds.x,
bounds.y, bounds.y,
bounds.width, bounds.width,
bounds.height bounds.height
), ),
oldBounds, oldBounds,
@ -388,20 +458,20 @@ $.Viewport.prototype = {
return this.panTo( center, immediately ); return this.panTo( center, immediately );
} }
referencePoint = oldBounds.getTopLeft().times( referencePoint = oldBounds.getTopLeft().times(
this.containerSize.x / oldBounds.width this.containerSize.x / oldBounds.width
).minus( ).minus(
newBounds.getTopLeft().times( newBounds.getTopLeft().times(
this.containerSize.x / newBounds.width this.containerSize.x / newBounds.width
) )
).divide( ).divide(
this.containerSize.x / oldBounds.width - this.containerSize.x / oldBounds.width -
this.containerSize.x / newBounds.width this.containerSize.x / newBounds.width
); );
return this.zoomTo( newZoom, referencePoint, immediately ); return this.zoomTo( newZoom, referencePoint, immediately );
}, },
/** /**
* @function * @function
@ -437,8 +507,8 @@ $.Viewport.prototype = {
var center = this.getCenter(); var center = this.getCenter();
if ( this.wrapHorizontal ) { if ( this.wrapHorizontal ) {
center.x = ( center.x = (
this.contentAspectX + ( center.x % this.contentAspectX ) this.contentAspectX + ( center.x % this.contentAspectX )
) % this.contentAspectX; ) % this.contentAspectX;
this.centerSpringX.resetTo( center.x ); this.centerSpringX.resetTo( center.x );
this.centerSpringX.update(); this.centerSpringX.update();
@ -459,12 +529,14 @@ $.Viewport.prototype = {
* @param {OpenSeadragon.Point} delta * @param {OpenSeadragon.Point} delta
* @param {Boolean} immediately * @param {Boolean} immediately
* @return {OpenSeadragon.Viewport} Chainable. * @return {OpenSeadragon.Viewport} Chainable.
* @fires OpenSeadragon.Viewer.event:pan
*/ */
panBy: function( delta, immediately ) { panBy: function( delta, immediately ) {
var center = new $.Point( var center = new $.Point(
this.centerSpringX.target.value, this.centerSpringX.target.value,
this.centerSpringY.target.value this.centerSpringY.target.value
); );
delta = delta.rotate( -this.degrees, new $.Point( 0, 0 ) );
return this.panTo( center.plus( delta ), immediately ); return this.panTo( center.plus( delta ), immediately );
}, },
@ -473,6 +545,7 @@ $.Viewport.prototype = {
* @param {OpenSeadragon.Point} center * @param {OpenSeadragon.Point} center
* @param {Boolean} immediately * @param {Boolean} immediately
* @return {OpenSeadragon.Viewport} Chainable. * @return {OpenSeadragon.Viewport} Chainable.
* @fires OpenSeadragon.Viewer.event:pan
*/ */
panTo: function( center, immediately ) { panTo: function( center, immediately ) {
if ( immediately ) { if ( immediately ) {
@ -484,10 +557,20 @@ $.Viewport.prototype = {
} }
if( this.viewer ){ if( this.viewer ){
this.viewer.raiseEvent( 'pan', { /**
* Raised when the viewport is panned (see {@link OpenSeadragon.Viewport#panBy} and {@link OpenSeadragon.Viewport#panTo}).
*
* @event pan
* @memberof OpenSeadragon.Viewer
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {OpenSeadragon.Point} center
* @property {Boolean} immediately
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.viewer.raiseEvent( 'pan', {
center: center, center: center,
immediately: immediately, immediately: immediately
viewer: this.viewer
}); });
} }
@ -497,33 +580,54 @@ $.Viewport.prototype = {
/** /**
* @function * @function
* @return {OpenSeadragon.Viewport} Chainable. * @return {OpenSeadragon.Viewport} Chainable.
* @fires OpenSeadragon.Viewer.event:zoom
*/ */
zoomBy: function( factor, refPoint, immediately ) { zoomBy: function( factor, refPoint, immediately ) {
if( refPoint instanceof $.Point && !isNaN( refPoint.x ) && !isNaN( refPoint.y ) ) {
refPoint = refPoint.rotate(
-this.degrees,
new $.Point( this.centerSpringX.target.value, this.centerSpringY.target.value )
);
}
return this.zoomTo( this.zoomSpring.target.value * factor, refPoint, immediately ); return this.zoomTo( this.zoomSpring.target.value * factor, refPoint, immediately );
}, },
/** /**
* @function * @function
* @return {OpenSeadragon.Viewport} Chainable. * @return {OpenSeadragon.Viewport} Chainable.
* @fires OpenSeadragon.Viewer.event:zoom
*/ */
zoomTo: function( zoom, refPoint, immediately ) { zoomTo: function( zoom, refPoint, immediately ) {
this.zoomPoint = refPoint instanceof $.Point ? this.zoomPoint = refPoint instanceof $.Point &&
refPoint : !isNaN(refPoint.x) &&
!isNaN(refPoint.y) ?
refPoint :
null; null;
if ( immediately ) { if ( immediately ) {
this.zoomSpring.resetTo( zoom ); this.zoomSpring.resetTo( zoom );
} else { } else {
this.zoomSpring.springTo( zoom ); this.zoomSpring.springTo( zoom );
} }
if( this.viewer ){ if( this.viewer ){
this.viewer.raiseEvent( 'zoom', { /**
* Raised when the viewport zoom level changes (see {@link OpenSeadragon.Viewport#zoomBy} and {@link OpenSeadragon.Viewport#zoomTo}).
*
* @event zoom
* @memberof OpenSeadragon.Viewer
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {Number} zoom
* @property {OpenSeadragon.Point} refPoint
* @property {Boolean} immediately
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.viewer.raiseEvent( 'zoom', {
zoom: zoom, zoom: zoom,
refPoint: refPoint, refPoint: refPoint,
immediately: immediately, immediately: immediately
viewer: this.viewer
}); });
} }
@ -531,29 +635,73 @@ $.Viewport.prototype = {
}, },
/** /**
* Currently only 90 degree rotation is supported and it only works
* with the canvas. Additionally, the navigator does not rotate yet,
* debug mode doesn't rotate yet, and overlay rotation is only
* partially supported.
* @function * @function
* @return {OpenSeadragon.Viewport} Chainable. * @return {OpenSeadragon.Viewport} Chainable.
*/ */
setRotation: function( degrees ) {
if( !( this.viewer && this.viewer.drawer.canRotate() ) ) {
return this;
}
degrees = ( degrees + 360 ) % 360;
if( degrees % 90 !== 0 ) {
throw new Error('Currently only 0, 90, 180, and 270 degrees are supported.');
}
this.degrees = degrees;
this.viewer.forceRedraw();
return this;
},
/**
* Gets the current rotation in degrees.
* @function
* @return {Number} The current rotation in degrees.
*/
getRotation: function() {
return this.degrees;
},
/**
* @function
* @return {OpenSeadragon.Viewport} Chainable.
* @fires OpenSeadragon.Viewer.event:resize
*/
resize: function( newContainerSize, maintain ) { resize: function( newContainerSize, maintain ) {
var oldBounds = this.getBounds(), var oldBounds = this.getBounds(),
newBounds = oldBounds, newBounds = oldBounds,
widthDeltaFactor = newContainerSize.x / this.containerSize.x; widthDeltaFactor;
this.containerSize = new $.Point( this.containerSize = new $.Point(
newContainerSize.x, newContainerSize.x,
newContainerSize.y newContainerSize.y
); );
if (maintain) { if ( maintain ) {
widthDeltaFactor = newContainerSize.x / this.containerSize.x;
newBounds.width = oldBounds.width * widthDeltaFactor; newBounds.width = oldBounds.width * widthDeltaFactor;
newBounds.height = newBounds.width / this.getAspectRatio(); newBounds.height = newBounds.width / this.getAspectRatio();
} }
if( this.viewer ){ if( this.viewer ){
this.viewer.raiseEvent( 'resize', { /**
* Raised when the viewer is resized (see {@link OpenSeadragon.Viewport#resize}).
*
* @event resize
* @memberof OpenSeadragon.Viewer
* @type {object}
* @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.
* @property {OpenSeadragon.Point} newContainerSize
* @property {Boolean} maintain
* @property {?Object} userData - Arbitrary subscriber-defined object.
*/
this.viewer.raiseEvent( 'resize', {
newContainerSize: newContainerSize, newContainerSize: newContainerSize,
maintain: maintain, maintain: maintain
viewer: this.viewer
}); });
} }
@ -599,7 +747,9 @@ $.Viewport.prototype = {
/** /**
* Convert a delta (translation vector) from pixels coordinates to viewport coordinates
* @function * @function
* @param {Boolean} current - Pass true for the current location; defaults to false (target location).
*/ */
deltaPixelsFromPoints: function( deltaPoints, current ) { deltaPixelsFromPoints: function( deltaPoints, current ) {
return deltaPoints.times( return deltaPoints.times(
@ -608,7 +758,9 @@ $.Viewport.prototype = {
}, },
/** /**
* Convert a delta (translation vector) from viewport coordinates to pixels coordinates.
* @function * @function
* @param {Boolean} current - Pass true for the current location; defaults to false (target location).
*/ */
deltaPointsFromPixels: function( deltaPixels, current ) { deltaPointsFromPixels: function( deltaPixels, current ) {
return deltaPixels.divide( return deltaPixels.divide(
@ -617,7 +769,9 @@ $.Viewport.prototype = {
}, },
/** /**
* Convert image pixel coordinates to viewport coordinates.
* @function * @function
* @param {Boolean} current - Pass true for the current location; defaults to false (target location).
*/ */
pixelFromPoint: function( point, current ) { pixelFromPoint: function( point, current ) {
var bounds = this.getBounds( current ); var bounds = this.getBounds( current );
@ -629,7 +783,9 @@ $.Viewport.prototype = {
}, },
/** /**
* Convert viewport coordinates to image pixel coordinates.
* @function * @function
* @param {Boolean} current - Pass true for the current location; defaults to false (target location).
*/ */
pointFromPixel: function( pixel, current ) { pointFromPixel: function( pixel, current ) {
var bounds = this.getBounds( current ); var bounds = this.getBounds( current );
@ -641,34 +797,65 @@ $.Viewport.prototype = {
}, },
/** /**
* Translates from Seajax viewer coordinate * Translates from OpenSeadragon viewer coordinate system to image coordinate system.
* system to image coordinate system * This method can be called either by passing X,Y coordinates or an
* OpenSeadragon.Point
* @function
* @param {OpenSeadragon.Point} viewerX the point in viewport coordinate system.
* @param {Number} viewerX X coordinate in viewport coordinate system.
* @param {Number} viewerY Y coordinate in viewport coordinate system.
* @return {OpenSeadragon.Point} a point representing the coordinates in the image.
*/ */
viewportToImageCoordinates: function(viewerX, viewerY) { viewportToImageCoordinates: function( viewerX, viewerY ) {
return new $.Point(viewerX * this.contentSize.x, viewerY * this.contentSize.y * this.contentAspectX); if ( arguments.length == 1 ) {
//they passed a point instead of individual components
return this.viewportToImageCoordinates( viewerX.x, viewerX.y );
}
return new $.Point( viewerX * this.contentSize.x, viewerY * this.contentSize.y * this.contentAspectX );
}, },
/** /**
* Translates from image coordinate system to * Translates from image coordinate system to OpenSeadragon viewer coordinate system
* Seajax viewer coordinate system * This method can be called either by passing X,Y coordinates or an
* OpenSeadragon.Point
* @function
* @param {OpenSeadragon.Point} imageX the point in image coordinate system.
* @param {Number} imageX X coordinate in image coordinate system.
* @param {Number} imageY Y coordinate in image coordinate system.
* @return {OpenSeadragon.Point} a point representing the coordinates in the viewport.
*/ */
imageToViewportCoordinates: function( imageX, imageY ) { imageToViewportCoordinates: function( imageX, imageY ) {
return new $.Point( imageX / this.contentSize.x, imageY / this.contentSize.y / this.contentAspectX); if ( arguments.length == 1 ) {
//they passed a point instead of individual components
return this.imageToViewportCoordinates( imageX.x, imageX.y );
}
return new $.Point( imageX / this.contentSize.x, imageY / this.contentSize.y / this.contentAspectX );
}, },
/** /**
* Translates from a rectanlge which describes a portion of * Translates from a rectangle which describes a portion of the image in
* the image in pixel coordinates to OpenSeadragon viewport * pixel coordinates to OpenSeadragon viewport rectangle coordinates.
* rectangle coordinates. * This method can be called either by passing X,Y,width,height or an
* OpenSeadragon.Rect
* @function
* @param {OpenSeadragon.Rect} imageX the rectangle in image coordinate system.
* @param {Number} imageX the X coordinate of the top left corner of the rectangle
* in image coordinate system.
* @param {Number} imageY the Y coordinate of the top left corner of the rectangle
* in image coordinate system.
* @param {Number} pixelWidth the width in pixel of the rectangle.
* @param {Number} pixelHeight the height in pixel of the rectangle.
*/ */
imageToViewportRectangle: function( imageX, imageY, pixelWidth, pixelHeight ) { imageToViewportRectangle: function( imageX, imageY, pixelWidth, pixelHeight ) {
var coordA, var coordA,
coordB, coordB,
rect; rect;
if( arguments.length == 1 ){ if( arguments.length == 1 ) {
//they passed a rectangle instead of individual components //they passed a rectangle instead of individual components
rect = imageX; rect = imageX;
return this.imageToViewportRectangle(rect.x, rect.y, rect.width, rect.height); return this.imageToViewportRectangle(
rect.x, rect.y, rect.width, rect.height
);
} }
coordA = this.imageToViewportCoordinates( coordA = this.imageToViewportCoordinates(
imageX, imageY imageX, imageY
@ -676,12 +863,169 @@ $.Viewport.prototype = {
coordB = this.imageToViewportCoordinates( coordB = this.imageToViewportCoordinates(
pixelWidth, pixelHeight pixelWidth, pixelHeight
); );
return new $.Rect( return new $.Rect(
coordA.x, coordA.x,
coordA.y, coordA.y,
coordA.x + coordB.x, coordB.x,
coordA.y + coordB.y coordB.y
); );
},
/**
* Translates from a rectangle which describes a portion of
* the viewport in point coordinates to image rectangle coordinates.
* This method can be called either by passing X,Y,width,height or an
* OpenSeadragon.Rect
* @function
* @param {OpenSeadragon.Rect} viewerX the rectangle in viewport coordinate system.
* @param {Number} viewerX the X coordinate of the top left corner of the rectangle
* in viewport coordinate system.
* @param {Number} imageY the Y coordinate of the top left corner of the rectangle
* in viewport coordinate system.
* @param {Number} pointWidth the width of the rectangle in viewport coordinate system.
* @param {Number} pointHeight the height of the rectangle in viewport coordinate system.
*/
viewportToImageRectangle: function( viewerX, viewerY, pointWidth, pointHeight ) {
var coordA,
coordB,
rect;
if ( arguments.length == 1 ) {
//they passed a rectangle instead of individual components
rect = viewerX;
return this.viewportToImageRectangle(
rect.x, rect.y, rect.width, rect.height
);
}
coordA = this.viewportToImageCoordinates( viewerX, viewerY );
coordB = this.viewportToImageCoordinates( pointWidth, pointHeight );
return new $.Rect(
coordA.x,
coordA.y,
coordB.x,
coordB.y
);
},
/**
* Convert pixel coordinates relative to the viewer element to image
* coordinates.
* @param {OpenSeadragon.Point} pixel
* @returns {OpenSeadragon.Point}
*/
viewerElementToImageCoordinates: function( pixel ) {
var point = this.pointFromPixel( pixel, true );
return this.viewportToImageCoordinates( point );
},
/**
* Convert pixel coordinates relative to the image to
* viewer element coordinates.
* @param {OpenSeadragon.Point} pixel
* @returns {OpenSeadragon.Point}
*/
imageToViewerElementCoordinates: function( pixel ) {
var point = this.imageToViewportCoordinates( pixel );
return this.pixelFromPoint( point, true );
},
/**
* Convert pixel coordinates relative to the window to image coordinates.
* @param {OpenSeadragon.Point} pixel
* @returns {OpenSeadragon.Point}
*/
windowToImageCoordinates: function( pixel ) {
var viewerCoordinates = pixel.minus(
OpenSeadragon.getElementPosition( this.viewer.element ));
return this.viewerElementToImageCoordinates( viewerCoordinates );
},
/**
* Convert image coordinates to pixel coordinates relative to the window.
* @param {OpenSeadragon.Point} pixel
* @returns {OpenSeadragon.Point}
*/
imageToWindowCoordinates: function( pixel ) {
var viewerCoordinates = this.imageToViewerElementCoordinates( pixel );
return viewerCoordinates.plus(
OpenSeadragon.getElementPosition( this.viewer.element ));
},
/**
* Convert pixel coordinates relative to the viewer element to viewport
* coordinates.
* @param {OpenSeadragon.Point} pixel
* @returns {OpenSeadragon.Point}
*/
viewerElementToViewportCoordinates: function( pixel ) {
return this.pointFromPixel( pixel, true );
},
/**
* Convert viewport coordinates to pixel coordinates relative to the
* viewer element.
* @param {OpenSeadragon.Point} point
* @returns {OpenSeadragon.Point}
*/
viewportToViewerElementCoordinates: function( point ) {
return this.pixelFromPoint( point, true );
},
/**
* Convert pixel coordinates relative to the window to viewport coordinates.
* @param {OpenSeadragon.Point} pixel
* @returns {OpenSeadragon.Point}
*/
windowToViewportCoordinates: function( pixel ) {
var viewerCoordinates = pixel.minus(
OpenSeadragon.getElementPosition( this.viewer.element ));
return this.viewerElementToViewportCoordinates( viewerCoordinates );
},
/**
* Convert viewport coordinates to pixel coordinates relative to the window.
* @param {OpenSeadragon.Point} point
* @returns {OpenSeadragon.Point}
*/
viewportToWindowCoordinates: function( point ) {
var viewerCoordinates = this.viewportToViewerElementCoordinates( point );
return viewerCoordinates.plus(
OpenSeadragon.getElementPosition( this.viewer.element ));
},
/**
* Convert a viewport zoom to an image zoom.
* Image zoom: ratio of the original image size to displayed image size.
* 1 means original image size, 0.5 half size...
* Viewport zoom: ratio of the displayed image's width to viewport's width.
* 1 means identical width, 2 means image's width is twice the viewport's width...
* @function
* @param {Number} viewportZoom The viewport zoom
* target zoom.
* @returns {Number} imageZoom The image zoom
*/
viewportToImageZoom: function( viewportZoom ) {
var imageWidth = this.viewer.source.dimensions.x;
var containerWidth = this.getContainerSize().x;
var viewportToImageZoomRatio = containerWidth / imageWidth;
return viewportZoom * viewportToImageZoomRatio;
},
/**
* Convert an image zoom to a viewport zoom.
* Image zoom: ratio of the original image size to displayed image size.
* 1 means original image size, 0.5 half size...
* Viewport zoom: ratio of the displayed image's width to viewport's width.
* 1 means identical width, 2 means image's width is twice the viewport's width...
* @function
* @param {Number} imageZoom The image zoom
* target zoom.
* @returns {Number} viewportZoom The viewport zoom
*/
imageToViewportZoom: function( imageZoom ) {
var imageWidth = this.viewer.source.dimensions.x;
var containerWidth = this.getContainerSize().x;
var viewportToImageZoomRatio = imageWidth / containerWidth;
return imageZoom * viewportToImageZoomRatio;
} }
}; };

View File

@ -1,115 +1,303 @@
/* global module, asyncTest, $, ok, equal, notEqual, start, test, Util, testLog */
(function() { (function() {
var viewer;
// TODO: Tighten up springs and use "immediate" where possible, so tests run faster module('Basic', {
// TODO: Test drag setup: function () {
var example = $('<div id="example"></div>').appendTo("#qunit-fixture");
var viewer = null; testLog.reset();
// ----------
asyncTest('Open', function() {
$(document).ready(function() {
viewer = OpenSeadragon({ viewer = OpenSeadragon({
id: 'example', id: 'example',
prefixUrl: '/build/openseadragon/images/', prefixUrl: '/build/openseadragon/images/',
tileSources: '/test/data/testpattern.dzi', springStiffness: 100 // Faster animation = faster tests
showNavigator: true
}); });
},
teardown: function () {
if (viewer && viewer.close) {
viewer.close();
}
ok(viewer, 'Viewer exists'); viewer = null;
}
});
var openHandler = function(eventSender, eventData) { // ----------
viewer.removeHandler('open', openHandler); asyncTest('Open', function() {
ok(true, 'Open event was sent'); ok(viewer, 'Viewer exists');
equal(eventSender, viewer, 'Sender of open event was viewer');
ok(eventData, 'Handler also received event data');
ok(viewer.viewport, 'Viewport exists');
start();
};
viewer.addHandler('open', openHandler); var openHandler = function(event) {
viewer.removeHandler('open', openHandler);
ok(true, 'Open event was sent');
ok(event, 'Handler received event data');
equal(event.eventSource, viewer, 'Sender of open event was viewer');
ok(viewer.viewport, 'Viewport exists');
ok(viewer.source, 'source exists');
ok(viewer._updateRequestId, 'timer is on');
start();
};
viewer.addHandler('open', openHandler);
viewer.open('/test/data/testpattern.dzi');
});
asyncTest('Open Error Handling', function() {
ok(viewer, 'Viewer exists');
viewer.addHandler('open', function(event) {
ok(false, "The open event should not fire for failed opens");
start();
}); });
viewer.addHandler('open-failed', function(event) {
ok(true, "The open-failed event should be fired when the source 404s");
equal($(".openseadragon-message").length, 1, "Open failures should display a message");
ok(testLog.log.contains('["AJAX request returned %s: %s",404,"/test/data/not-a-real-file"]'),
"AJAX failures should be logged to the console");
start();
});
viewer.open('/test/data/not-a-real-file');
}); });
// ---------- // ----------
asyncTest('Zoom', function() { asyncTest('Zoom', function() {
var viewport = viewer.viewport; viewer.addHandler("open", function () {
equal(viewport.getZoom(), 1, 'We start out unzoomed'); var viewport = viewer.viewport;
var zoomHandler = function() { equal(viewport.getZoom(), 1, 'We start out unzoomed');
viewer.removeHandler('animationfinish', zoomHandler);
equal(viewport.getZoom(), 2, 'Zoomed correctly');
start();
};
viewer.addHandler('animationfinish', zoomHandler); var zoomHandler = function() {
viewport.zoomTo(2); viewer.removeHandler('animation-finish', zoomHandler);
equal(viewport.getZoom(), 2, 'Zoomed correctly');
start();
};
viewer.addHandler('animation-finish', zoomHandler);
viewport.zoomTo(2);
});
viewer.open('/test/data/testpattern.dzi');
}); });
// ---------- // ----------
asyncTest('Pan', function() { asyncTest('Pan', function() {
var viewport = viewer.viewport; viewer.addHandler("open", function () {
var center = viewport.getCenter(); var viewport = viewer.viewport,
ok(center.x === 0.5 && center.y === 0.5, 'We start out unpanned'); center = viewport.getCenter();
var panHandler = function() { ok(center.x === 0.5 && center.y === 0.5, 'We start out unpanned');
viewer.removeHandler('animationfinish', panHandler);
center = viewport.getCenter();
ok(center.x === 0.1 && center.y === 0.1, 'Panned correctly');
start();
};
viewer.addHandler('animationfinish', panHandler); var panHandler = function() {
viewport.panTo(new OpenSeadragon.Point(0.1, 0.1)); viewer.removeHandler('animation-finish', panHandler);
center = viewport.getCenter();
ok(center.x === 0.1 && center.y === 0.1, 'Panned correctly');
start();
};
viewer.addHandler('animation-finish', panHandler);
viewport.panTo(new OpenSeadragon.Point(0.1, 0.1));
});
viewer.open('/test/data/testpattern.dzi');
}); });
// ---------- // ----------
asyncTest('Home', function() { asyncTest('Home', function() {
var viewport = viewer.viewport; // Test setup:
var center = viewport.getCenter(); function opener() {
ok(center.x !== 0.5 && center.y !== 0.5, 'We start out panned'); var viewport = viewer.viewport;
notEqual(viewport.getZoom(), 1, 'We start out zoomed'); viewport.panTo(new OpenSeadragon.Point(0.1, 0.1));
viewport.zoomTo(2);
}
var homeHandler = function() { function stage1() {
viewer.removeHandler('animationfinish', homeHandler); var viewport = viewer.viewport,
center = viewport.getCenter(); center = viewport.getCenter();
ok(center.x === 0.5 && center.y === 0.5, 'We end up unpanned');
equal(viewport.getZoom(), 1, 'We end up unzoomed');
start();
};
viewer.addHandler('animationfinish', homeHandler); viewer.removeHandler('animation-finish', stage1);
viewport.goHome(true);
ok(center.x !== 0.5 && center.y !== 0.5, 'We start out panned');
notEqual(viewport.getZoom(), 1, 'We start out zoomed');
var homeHandler = function() {
viewer.removeHandler('animation-finish', homeHandler);
center = viewport.getCenter();
ok(center.x === 0.5 && center.y === 0.5, 'We end up unpanned');
equal(viewport.getZoom(), 1, 'We end up unzoomed');
start();
};
viewer.addHandler('animation-finish', homeHandler);
viewport.goHome(true);
}
viewer.addHandler("open", opener);
viewer.addHandler("animation-finish", stage1);
viewer.open('/test/data/testpattern.dzi');
}); });
// ---------- // ----------
asyncTest('Click', function() { asyncTest('Click', function() {
var viewport = viewer.viewport; viewer.addHandler("open", function () {
center = viewport.getCenter(); var viewport = viewer.viewport,
ok(center.x === 0.5 && center.y === 0.5, 'We start out unpanned');
equal(viewport.getZoom(), 1, 'We start out unzoomed');
var clickHandler = function() {
viewer.removeHandler('animationfinish', clickHandler);
center = viewport.getCenter(); center = viewport.getCenter();
ok(center.x > 0.37 && center.x < 0.38 && center.y > 0.37 && center.y < 0.38, 'Panned correctly');
equal(viewport.getZoom(), 2, 'Zoomed correctly');
start();
};
viewer.addHandler('animationfinish', clickHandler); ok(center.x === 0.5 && center.y === 0.5, 'We start out unpanned');
Util.simulateViewerClick(viewer, 0.25, 0.25); equal(viewport.getZoom(), 1, 'We start out unzoomed');
var clickHandler = function() {
viewer.removeHandler('animation-finish', clickHandler);
center = viewport.getCenter();
ok(center.x > 0.37 && center.x < 0.38 && center.y > 0.37 && center.y < 0.38, 'Panned correctly');
equal(viewport.getZoom(), 2, 'Zoomed correctly');
start();
};
viewer.addHandler('animation-finish', clickHandler);
Util.simulateViewerClickWithDrag( {
viewer: viewer,
widthFactor: 0.25,
heightFactor: 0.25,
dragCount: 0,
dragDx: 0,
dragDy: 0
} );
} );
viewer.open('/test/data/testpattern.dzi');
});
// ----------
asyncTest('FullPage', function() {
viewer.addHandler("open", function () {
ok(!viewer.isFullPage(), 'Started out not fullpage');
ok(!$(viewer.element).hasClass('fullpage'),
'No fullpage class on div');
var checkEnteringPreFullPage = function(event) {
viewer.removeHandler('pre-full-page', checkEnteringPreFullPage);
ok(event.fullPage, 'Switching to fullpage');
ok(!viewer.isFullPage(), 'Not yet fullpage');
};
var checkEnteringFullPage = function(event) {
viewer.removeHandler('full-page', checkEnteringFullPage);
ok(event.fullPage, 'Switched to fullpage');
ok(viewer.isFullPage(), 'Enabled fullpage');
ok($(viewer.element).hasClass('fullpage'),
'Fullpage class added to div');
var checkExitingPreFullPage = function(event) {
viewer.removeHandler('pre-full-page', checkExitingPreFullPage);
ok(!event.fullPage, 'Exiting fullpage');
ok(viewer.isFullPage(), 'Still fullpage');
};
var checkExitingFullPage = function(event) {
viewer.removeHandler('full-page', checkExitingFullPage);
ok(!event.fullPage, 'Exiting fullpage');
ok(!viewer.isFullPage(), 'Disabled fullpage');
ok(!$(viewer.element).hasClass('fullpage'),
'Fullpage class removed from div');
start();
};
viewer.addHandler("pre-full-page", checkExitingPreFullPage);
viewer.addHandler("full-page", checkExitingFullPage);
viewer.setFullPage(false);
};
viewer.addHandler("pre-full-page", checkEnteringPreFullPage);
viewer.addHandler("full-page", checkEnteringFullPage);
viewer.setFullPage(true);
});
viewer.open('/test/data/testpattern.dzi');
});
asyncTest('FullScreen', function() {
if (!OpenSeadragon.supportsFullScreen) {
expect(0);
start();
return;
}
viewer.addHandler("open", function () {
ok(!OpenSeadragon.isFullScreen(), 'Started out not fullscreen');
var checkEnteringPreFullScreen = function(event) {
viewer.removeHandler('pre-full-screen', checkEnteringPreFullScreen);
ok(event.fullScreen, 'Switching to fullscreen');
ok(!OpenSeadragon.isFullScreen(), 'Not yet fullscreen');
};
// The fullscreen mode is always denied during tests so we are
// exiting directly.
var checkExitingFullScreen = function(event) {
viewer.removeHandler('full-screen', checkExitingFullScreen);
ok(!event.fullScreen, 'Exiting fullscreen');
ok(!OpenSeadragon.isFullScreen(), 'Disabled fullscreen');
start();
};
viewer.addHandler("pre-full-screen", checkEnteringPreFullScreen);
viewer.addHandler("full-screen", checkExitingFullScreen);
viewer.setFullScreen(true);
});
viewer.open('/test/data/testpattern.dzi');
}); });
// ---------- // ----------
asyncTest('Close', function() { asyncTest('Close', function() {
var closeHandler = function() { viewer.addHandler("open", function () {
viewer.removeHandler('close', closeHandler); var closeHandler = function() {
ok(true, 'Close event was sent'); viewer.removeHandler('close', closeHandler);
start(); ok(!viewer.source, 'no source');
}; ok(true, 'Close event was sent');
ok(!viewer._updateRequestId, 'timer is off');
setTimeout(function() {
ok(!viewer._updateRequestId, 'timer is still off');
start();
}, 100);
};
viewer.addHandler('close', closeHandler); viewer.addHandler('close', closeHandler);
viewer.close(); viewer.close();
});
viewer.open('/test/data/testpattern.dzi');
}); });
// ----------
asyncTest('Destroy', function() {
viewer.addHandler("open", function () {
// Check that the DOM has been modified
notEqual(0, $('#example').children().length);
var closeCalled = false;
var closeHandler = function() {
viewer.removeHandler('close', closeHandler);
closeCalled = true;
};
viewer.addHandler('close', closeHandler);
viewer.destroy();
// Check that the DOM has been cleaned up
equal(0, $('#example').children().length);
equal(null, viewer.canvas);
equal(null, viewer.keyboardCommandArea);
equal(null, viewer.container);
equal(null, viewer.element);
equal(true, closeCalled);
start();
});
viewer.open('/test/data/testpattern.dzi');
});
})(); })();

383
test/controls.js vendored Normal file
View File

@ -0,0 +1,383 @@
/* global module, asyncTest, $, ok, equal, notEqual, start, test, Util, testLog */
(function () {
var viewer;
module('Controls', {
setup: function () {
var example = $('<div id="controlsTests"></div>').appendTo("#qunit-fixture");
testLog.reset();
},
teardown: function () {
if (viewer && viewer.close) {
viewer.close();
}
viewer = null;
}
});
asyncTest('ZoomControlOff', function () {
var openHandler = function () {
viewer.removeHandler('open', openHandler);
ok(!viewer.showZoomControl, 'showZoomControl should be off');
ok(!viewer.zoomInButton, "zoomIn button should be null");
ok(!viewer.zoomOutButton, "zoomOut button should be null");
viewer.close();
start();
};
viewer = OpenSeadragon({
id: 'controlsTests',
prefixUrl: '/build/openseadragon/images/',
springStiffness: 100, // Faster animation = faster tests
showZoomControl: false
});
viewer.addHandler('open', openHandler);
viewer.open('/test/data/testpattern.dzi');
});
asyncTest('ZoomControlOn', function () {
var openHandler = function () {
viewer.removeHandler('open', openHandler);
ok(viewer.showZoomControl, 'showZoomControl should be on');
ok(!!viewer.zoomInButton, "zoomIn button should not be null");
ok(!!viewer.zoomOutButton, "zoomOut button should not be null");
notEqual(viewer.buttons.buttons.indexOf(viewer.zoomInButton), -1,
"The zoomIn button should be present");
notEqual(viewer.buttons.buttons.indexOf(viewer.zoomOutButton), -1,
"The zoomOut button should be present");
var oldZoom = viewer.viewport.getZoom();
viewer.zoomInButton.onClick();
var newZoom = viewer.viewport.getZoom();
ok(oldZoom < newZoom, "OSD should have zoomed in.");
oldZoom = newZoom;
viewer.zoomOutButton.onClick();
newZoom = viewer.viewport.getZoom();
ok(oldZoom > newZoom, "OSD should have zoomed out.");
viewer.close();
start();
};
viewer = OpenSeadragon({
id: 'controlsTests',
prefixUrl: '/build/openseadragon/images/',
springStiffness: 100, // Faster animation = faster tests
showZoomControl: true
});
viewer.addHandler('open', openHandler);
viewer.open('/test/data/testpattern.dzi');
});
asyncTest('HomeControlOff', function () {
var openHandler = function () {
viewer.removeHandler('open', openHandler);
ok(!viewer.showHomeControl, 'showHomeControl should be off');
ok(!viewer.homeButton, "Home button should be null");
viewer.close();
start();
};
viewer = OpenSeadragon({
id: 'controlsTests',
prefixUrl: '/build/openseadragon/images/',
springStiffness: 100, // Faster animation = faster tests
showHomeControl: false
});
viewer.addHandler('open', openHandler);
viewer.open('/test/data/testpattern.dzi');
});
asyncTest('HomeControlOn', function () {
var openHandler = function () {
viewer.removeHandler('open', openHandler);
ok(viewer.showHomeControl, 'showHomeControl should be on');
ok(!!viewer.homeButton, "Home button should not be null");
notEqual(viewer.buttons.buttons.indexOf(viewer.homeButton), -1,
"The home button should be present");
viewer.viewport.zoomBy(1.1);
var bounds = viewer.viewport.getBounds();
var homeBounds = viewer.viewport.getHomeBounds();
ok(bounds.x !== homeBounds.x ||
bounds.y !== homeBounds.y ||
bounds.width !== homeBounds.width ||
bounds.height !== homeBounds.height,
"OSD should not be at home.");
viewer.homeButton.onRelease();
bounds = viewer.viewport.getBounds();
ok(bounds.x === homeBounds.x &&
bounds.y === homeBounds.y &&
bounds.width === homeBounds.width &&
bounds.height === homeBounds.height, "OSD should have get home.");
viewer.close();
start();
};
viewer = OpenSeadragon({
id: 'controlsTests',
prefixUrl: '/build/openseadragon/images/',
springStiffness: 100, // Faster animation = faster tests
showHomeControl: true
});
viewer.addHandler('open', openHandler);
viewer.open('/test/data/testpattern.dzi');
});
asyncTest('FullPageControlOff', function () {
var openHandler = function () {
viewer.removeHandler('open', openHandler);
ok(!viewer.showFullPageControl, 'showFullPageControl should be off');
ok(!viewer.fullPageButton, "FullPage button should be null");
viewer.close();
start();
};
viewer = OpenSeadragon({
id: 'controlsTests',
prefixUrl: '/build/openseadragon/images/',
springStiffness: 100, // Faster animation = faster tests
showFullPageControl: false
});
viewer.addHandler('open', openHandler);
viewer.open('/test/data/testpattern.dzi');
});
asyncTest('FullPageControlOn', function () {
var openHandler = function () {
viewer.removeHandler('open', openHandler);
ok(viewer.showHomeControl, 'showFullPageControl should be on');
ok(!!viewer.fullPageButton, "FullPage button should not be null");
notEqual(viewer.buttons.buttons.indexOf(viewer.fullPageButton), -1,
"The full page button should be present");
ok(!viewer.isFullPage(), "OSD should not be in full page.");
viewer.fullPageButton.onRelease();
ok(viewer.isFullPage(), "OSD should be in full page.");
viewer.fullPageButton.onRelease();
ok(!viewer.isFullPage(), "OSD should not be in full page.");
viewer.close();
start();
};
viewer = OpenSeadragon({
id: 'controlsTests',
prefixUrl: '/build/openseadragon/images/',
springStiffness: 100, // Faster animation = faster tests
showHomeControl: true
});
viewer.addHandler('open', openHandler);
viewer.open('/test/data/testpattern.dzi');
});
asyncTest('RotateControlOff', function () {
var openHandler = function (event) {
viewer.removeHandler('open', openHandler);
ok(true, 'Open event was sent');
ok(viewer.drawer, 'Drawer exists');
ok(viewer.drawer.canRotate(), 'drawer.canRotate needs to be true');
ok(!viewer.showRotationControl, 'showRotationControl should be off');
ok(!viewer.rotateLeftButton, "rotateLeft button should be null");
ok(!viewer.rotateRightButton, "rotateRight button should be null");
viewer.close();
start();
};
viewer = OpenSeadragon({
id: 'controlsTests',
prefixUrl: '/build/openseadragon/images/',
springStiffness: 100, // Faster animation = faster tests
showRotationControl: false
});
viewer.addHandler('open', openHandler);
viewer.open('/test/data/testpattern.dzi');
});
asyncTest('RotateControlOn', function () {
var openHandler = function (event) {
viewer.removeHandler('open', openHandler);
ok(true, 'Open event was sent');
ok(viewer.drawer, 'Drawer exists');
ok(viewer.drawer.canRotate(), 'drawer.canRotate needs to be true');
ok(viewer.showRotationControl, 'showRotationControl should be true');
notEqual(viewer.buttons.buttons.indexOf(viewer.rotateLeftButton), -1,
"rotateLeft should be found");
notEqual(viewer.buttons.buttons.indexOf(viewer.rotateRightButton), -1,
"rotateRight should be found");
// Now simulate the left/right button clicks.
// TODO: re-factor simulateViewerClickWithDrag so it'll accept any element, and use that.
equal(viewer.viewport.degrees, 0, "Image should start at 0 degrees rotation");
viewer.rotateLeftButton.onRelease();
equal(viewer.viewport.degrees, 270, "Image should be 270 degrees rotation (left)");
viewer.rotateRightButton.onRelease();
equal(viewer.viewport.degrees, 0, "Image should be 270 degrees rotation (right)");
viewer.close();
start();
};
viewer = OpenSeadragon({
id: 'controlsTests',
prefixUrl: '/build/openseadragon/images/',
springStiffness: 100, // Faster animation = faster tests
showRotationControl: true
});
viewer.addHandler('open', openHandler);
viewer.open('/test/data/testpattern.dzi');
});
asyncTest('SequenceControlOff', function () {
var openHandler = function () {
viewer.removeHandler('open', openHandler);
ok(!viewer.showSequenceControl, 'showSequenceControl should be off');
ok(!viewer.previousButton, "Previous button should be null");
ok(!viewer.nextButton, "Next button should be null");
viewer.close();
start();
};
viewer = OpenSeadragon({
id: 'controlsTests',
prefixUrl: '/build/openseadragon/images/',
tileSources: [
'/test/data/testpattern.dzi',
'/test/data/testpattern.dzi',
'/test/data/testpattern.dzi'
],
springStiffness: 100, // Faster animation = faster tests
showSequenceControl: false
});
viewer.addHandler('open', openHandler);
});
asyncTest('SequenceControlOnPrevNextWrapOff', function () {
var openHandler = function () {
viewer.removeHandler('open', openHandler);
ok(viewer.showSequenceControl, 'showSequenceControl should be on');
ok(!!viewer.previousButton, "Previous button should not be null");
ok(!!viewer.nextButton, "Next button should not be null");
notEqual(viewer.paging.buttons.indexOf(viewer.previousButton), -1,
"The previous button should be present");
notEqual(viewer.paging.buttons.indexOf(viewer.nextButton), -1,
"The next button should be present");
equal(viewer.currentPage(), 0, "OSD should open on first page.");
ok(viewer.previousButton.element.disabled,
"Previous should be disabled on first page.");
ok(!viewer.nextButton.element.disabled,
"Next should be enabled on first page.");
viewer.nextButton.onRelease();
equal(viewer.currentPage(), 1, "OSD should be on second page.");
ok(!viewer.previousButton.element.disabled,
"Previous should be enabled on second page.");
ok(!viewer.nextButton.element.disabled,
"Next should be enabled on second page.");
viewer.nextButton.onRelease();
equal(viewer.currentPage(), 2, "OSD should be on third page.");
ok(!viewer.previousButton.element.disabled,
"Previous should be enabled on third page.");
ok(viewer.nextButton.element.disabled,
"Next should be disabled on third page.");
viewer.previousButton.onRelease();
equal(viewer.currentPage(), 1, "OSD should be on second page.");
ok(!viewer.previousButton.element.disabled,
"Previous should be enabled on second page.");
ok(!viewer.nextButton.element.disabled,
"Next should be enabled on second page.");
viewer.close();
start();
};
viewer = OpenSeadragon({
id: 'controlsTests',
prefixUrl: '/build/openseadragon/images/',
tileSources: [
'/test/data/testpattern.dzi',
'/test/data/testpattern.dzi',
'/test/data/testpattern.dzi'
],
springStiffness: 100, // Faster animation = faster tests
showSequenceControl: true,
navPrevNextWrap: false
});
viewer.addHandler('open', openHandler);
});
asyncTest('SequenceControlOnPrevNextWrapOn', function () {
var openHandler = function () {
viewer.removeHandler('open', openHandler);
ok(viewer.showSequenceControl, 'showSequenceControl should be on');
ok(!!viewer.previousButton, "Previous button should not be null");
ok(!!viewer.nextButton, "Next button should not be null");
notEqual(viewer.paging.buttons.indexOf(viewer.previousButton), -1,
"The previous button should be present");
notEqual(viewer.paging.buttons.indexOf(viewer.nextButton), -1,
"The next button should be present");
equal(viewer.currentPage(), 0, "OSD should open on first page.");
ok(!viewer.previousButton.element.disabled,
"Previous should be enabled on first page.");
ok(!viewer.nextButton.element.disabled,
"Next should be enabled on first page.");
viewer.previousButton.onRelease();
equal(viewer.currentPage(), 2, "OSD should be on third page.");
ok(!viewer.previousButton.element.disabled,
"Previous should be enabled on third page.");
ok(!viewer.nextButton.element.disabled,
"Next should be enabled on third page.");
viewer.nextButton.onRelease();
equal(viewer.currentPage(), 0, "OSD should be on first page.");
ok(!viewer.previousButton.element.disabled,
"Previous should be enabled on first page.");
ok(!viewer.nextButton.element.disabled,
"Next should be enabled on first page.");
viewer.close();
start();
};
viewer = OpenSeadragon({
id: 'controlsTests',
prefixUrl: '/build/openseadragon/images/',
tileSources: [
'/test/data/testpattern.dzi',
'/test/data/testpattern.dzi',
'/test/data/testpattern.dzi'
],
springStiffness: 100, // Faster animation = faster tests
showSequenceControl: true,
navPrevNextWrap: true
});
viewer.addHandler('open', openHandler);
});
})();

BIN
test/data/A.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
test/data/BBlue.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
test/data/CCyan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
test/data/DDandelion.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

26
test/data/iiif1_0.json Normal file
View File

@ -0,0 +1,26 @@
{
"identifier": "iiif_1_0_files",
"width": 775,
"height": 1024,
"scale_factors": [
1,
2,
3,
4,
5,
6
],
"tile_width": 256,
"tile_height": 256,
"formats": [
"jpg",
"png"
],
"qualities": [
"native",
"bitonal",
"grey",
"color"
],
"profile": "http://library.stanford.edu/iiif/image-api/compliance.html#level1"
}

26
test/data/iiif1_0.xml Normal file
View File

@ -0,0 +1,26 @@
<info xmlns="http://library.stanford.edu/iiif/image-api/ns/">
<identifier>iiif_1_0_files</identifier>
<width>775</width>
<height>1024</height>
<scale_factors>
<scale_factor>1</scale_factor>
<scale_factor>2</scale_factor>
<scale_factor>3</scale_factor>
<scale_factor>4</scale_factor>
<scale_factor>5</scale_factor>
<scale_factor>6</scale_factor>
</scale_factors>
<tile_width>256</tile_width>
<tile_height>256</tile_height>
<formats>
<format>jpg</format>
<format>png</format>
</formats>
<qualities>
<quality>native</quality>
<quality>bitonal</quality>
<quality>grey</quality>
<quality>color</quality>
</qualities>
<profile>http://library.stanford.edu/iiif/image-api/compliance.html#level1</profile>
</info>

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 716 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 712 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 633 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 810 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 683 B

Some files were not shown because too many files have changed in this diff Show More