From 75bb765ca0668e960db3e576c5d3056045f46de1 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Thu, 14 Feb 2013 12:06:59 -0800 Subject: [PATCH] Removing built openseadragon.js from repo --- openseadragon.js | 10504 --------------------------------------------- 1 file changed, 10504 deletions(-) delete mode 100644 openseadragon.js diff --git a/openseadragon.js b/openseadragon.js deleted file mode 100644 index 1fa5bdda..00000000 --- a/openseadragon.js +++ /dev/null @@ -1,10504 +0,0 @@ -/*globals OpenSeadragon*/ - -/** - * @version OpenSeadragon 0.9.120 - * - * @fileOverview - *

- * - * OpenSeadragon - Javascript Deep Zooming - * - *

- *

- * OpenSeadragon is provides an html interface for creating - * deep zoom user interfaces. The simplest examples include deep - * zoom for large resolution images, and complex examples include - * zoomable map interfaces driven by SVG files. - *

- * - * @author
(c) 2011, 2012 Christopher Thatcher - * @author
(c) 2010 OpenSeadragon Team - * @author
(c) 2010 CodePlex Foundation - * - *

- * Original license preserved 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.
- * 
- * ---------------------------------------------------------------------------
- * 
- *

- *

- * Work done by Chris Thatcher adds an 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.
- * ---------------------------------------------------------------------------
- * 
- *

- **/ - - /** - * The root namespace for OpenSeadragon, this function also serves as a single - * point of instantiation for an {@link OpenSeadragon.Viewer}, including all - * combinations of out-of-the-box configurable features. All utility methods - * and classes are defined on or below this namespace. - * - * @namespace - * @function - * @name OpenSeadragon - * @exports $ as OpenSeadragon - * - * @param {Object} options All required and optional settings for instantiating - * a new instance of an OpenSeadragon image viewer. - * - * @param {String} options.xmlPath - * DEPRECATED. A relative path to load a DZI file from the server. - * Prefer the newer options.tileSources. - * - * @param {Array|String|Function|Object[]|Array[]|String[]|Function[]} options.tileSources - * As an Array, the tileSource can hold either be all Objects or mixed - * types of Arrays of Objects, String, Function. When a value is a String, - * the tileSource is used to create a {@link OpenSeadragon.DziTileSource}. - * When a value is a Function, the function is used to create a new - * {@link OpenSeadragon.TileSource} whose abstract method - * getUrl( level, x, y ) is implemented by the function. Finally, when it - * is an Array of objects, it is used to create a - * {@link OpenSeadragon.LegacyTileSource}. - * - * @param {Boolean} [options.debugMode=true] - * Currently does nothing. TODO: provide an in-screen panel providing event - * detail feedback. - * - * @param {Number} [options.animationTime=1.5] - * Specifies the animation duration per each {@link OpenSeadragon.Spring} - * which occur when the image is dragged or zoomed. - * - * @param {Number} [options.blendTime=0.5] - * Specifies the duration of animation as higher or lower level tiles are - * replacing the existing tile. - * - * @param {Boolean} [options.alwaysBlend=false] - * Forces the tile to always blend. By default the tiles skip blending - * when the blendTime is surpassed and the current animation frame would - * not complete the blend. - * - * @param {Boolean} [options.autoHideControls=true] - * If the user stops interacting with the viewport, fade the navigation - * controls. Useful for presentation since the controls are by default - * floated on top of the image the user is viewing. - * - * @param {Boolean} [options.immediateRender=false] - * Render the best closest level first, ignoring the lowering levels which - * provide the effect of very blurry to sharp. It is recommended to change - * setting to true for mobile devices. - * - * @param {Boolean} [options.wrapHorizontal=false] - * Set to true to force the image to wrap horizontally within the viewport. - * Useful for maps or images representing the surface of a sphere or cylinder. - * - * @param {Boolean} [options.wrapVertical=false] - * Set to true to force the image to wrap vertically within the viewport. - * Useful for maps or images representing the surface of a sphere or cylinder. - * - * @param {Number} [options.minZoomImageRatio=0.8] - * The minimum percentage ( expressed as a number between 0 and 1 ) of - * the viewport height or width at which the zoom out will be constrained. - * Setting it to 0, for example will allow you to zoom out infinitly. - * - * @param {Number} [options.maxZoomPixelRatio=2] - * The maximum ratio to allow a zoom-in to affect the highest level pixel - * ratio. This can be set to Infinity to allow 'infinite' zooming into the - * image though it is less effective visually if the HTML5 Canvas is not - * availble on the viewing device. - * - * @param {Number} [options.visibilityRatio=0.5] - * The percentage ( as a number from 0 to 1 ) of the source image which - * must be kept within the viewport. If the image is dragged beyond that - * limit, it will 'bounce' back until the minimum visibility ration is - * achieved. Setting this to 0 and wrapHorizontal ( or wrapVertical ) to - * true will provide the effect of an infinitely scrolling viewport. - * - * @param {Number} [options.springStiffness=5.0] - * - * @param {Number} [options.imageLoaderLimit=0] - * The maximum number of image requests to make concurrently. By default - * it is set to 0 allowing the browser to make the maximum number of - * image requests in parallel as allowed by the browsers policy. - * - * @param {Number} [options.clickTimeThreshold=200] - * If multiple mouse clicks occurs within less than this number of - * milliseconds, treat them as a single click. - * - * @param {Number} [options.clickDistThreshold=5] - * If a mouse or touch drag occurs and the distance to the starting drag - * point is less than this many pixels, ignore the drag event. - * - * @param {Number} [options.zoomPerClick=2.0] - * The "zoom distance" per mouse click or touch tap. - * - * @param {Number} [options.zoomPerScroll=1.2] - * The "zoom distance" per mouse scroll or touch pinch. - * - * @param {Number} [options.zoomPerSecond=2.0] - * The number of seconds to animate a single zoom event over. - * - * @param {Boolean} [options.showNavigationControl=true] - * Set to false to prevent the appearance of the default navigation controls. - * - * @param {Number} [options.controlsFadeDelay=2000] - * The number of milliseconds to wait once the user has stopped interacting - * with the interface before begining to fade the controls. Assumes - * showNavigationControl and autoHideControls are both true. - * - * @param {Number} [options.controlsFadeLength=1500] - * The number of milliseconds to animate the controls fading out. - * - * @param {Number} [options.maxImageCacheCount=100] - * The max number of images we should keep in memory (per drawer). - * - * @param {Number} [options.minPixelRatio=0.5] - * The higher the minPixelRatio, the lower the quality of the image that - * is considered sufficient to stop rendering a given zoom level. For - * example, if you are targeting mobile devices with less bandwith you may - * try setting this to 1.5 or higher. - * - * @param {Boolean} [options.mouseNavEnabled=true] - * Is the user able to interact with the image via mouse or touch. Default - * interactions include draging the image in a plane, and zooming in toward - * and away from the image. - * - * @param {Boolean} [options.preserveViewport=false] - * If the viewer has been configured with a sequence of tile sources, then - * normally navigating to through each image resets the viewport to 'home' - * position. If preserveViewport is set to true, then the viewport position - * is preserved when navigating between images in the sequence. - * - * @param {String} [options.prefixUrl='/images/'] - * Prepends the prefixUrl to navImages paths, which is very useful - * since the default paths are rarely useful for production - * environments. - * - * @param {Object} [options.navImages=] - * An object with a property for each button or other built-in navigation - * control, eg the current 'zoomIn', 'zoomOut', 'home', and 'fullpage'. - * Each of those in turn provides an image path for each state of the botton - * or navigation control, eg 'REST', 'GROUP', 'HOVER', 'PRESS'. Finally the - * image paths, by default assume there is a folder on the servers root path - * called '/images', eg '/images/zoomin_rest.png'. If you need to adjust - * these paths, prefer setting the option.prefixUrl rather than overriding - * every image path directly through this setting. - * - * @returns {OpenSeadragon.Viewer} - */ -window.OpenSeadragon = window.OpenSeadragon || function( options ){ - - return new OpenSeadragon.Viewer( options ); - -}; - -(function( $ ){ - - - /** - * Taken from jquery 1.6.1 - * [[Class]] -> type pairs - * @private - */ - var class2type = { - '[object Boolean]': 'boolean', - '[object Number]': 'number', - '[object String]': 'string', - '[object Function]': 'function', - '[object Array]': 'array', - '[object Date]': 'date', - '[object RegExp]': 'regexp', - '[object Object]': 'object' - }, - // Save a reference to some core methods - toString = Object.prototype.toString, - hasOwn = Object.prototype.hasOwnProperty, - push = Array.prototype.push, - slice = Array.prototype.slice, - trim = String.prototype.trim, - indexOf = Array.prototype.indexOf; - - - /** - * Taken from jQuery 1.6.1 - * @name $.isFunction - * @function - * @see jQuery - */ - $.isFunction = function( obj ) { - return $.type(obj) === "function"; - }; - - - /** - * Taken from jQuery 1.6.1 - * @name $.isArray - * @function - * @see jQuery - */ - $.isArray = Array.isArray || function( obj ) { - return $.type(obj) === "array"; - }; - - - /** - * A crude way of determining if an object is a window. - * Taken from jQuery 1.6.1 - * @name $.isWindow - * @function - * @see jQuery - */ - $.isWindow = function( obj ) { - return obj && typeof obj === "object" && "setInterval" in obj; - }; - - - /** - * Taken from jQuery 1.6.1 - * @name $.type - * @function - * @see jQuery - */ - $.type = function( obj ) { - return ( obj === null ) || ( obj === undefined ) ? - String( obj ) : - class2type[ toString.call(obj) ] || "object"; - }; - - - /** - * Taken from jQuery 1.6.1 - * @name $.isPlainObject - * @function - * @see jQuery - */ - $.isPlainObject = function( obj ) { - // Must be an Object. - // Because of IE, we also have to check the presence of the constructor property. - // Make sure that DOM nodes and window objects don't pass through, as well - if ( !obj || OpenSeadragon.type(obj) !== "object" || obj.nodeType || $.isWindow( obj ) ) { - return false; - } - - // Not own constructor property must be Object - if ( obj.constructor && - !hasOwn.call(obj, "constructor") && - !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { - return false; - } - - // Own properties are enumerated firstly, so to speed up, - // if last one is own, then all properties are own. - - var key; - for ( key in obj ) {} - - return key === undefined || hasOwn.call( obj, key ); - }; - - - /** - * Taken from jQuery 1.6.1 - * @name $.isEmptyObject - * @function - * @see jQuery - */ - $.isEmptyObject = function( obj ) { - for ( var name in obj ) { - return false; - } - return true; - }; - - -}( OpenSeadragon )); - -/** - * This closure defines all static methods available to the OpenSeadragon - * namespace. Many, if not most, are taked directly from jQuery for use - * to simplify and reduce common programming patterns. More static methods - * from jQuery may eventually make their way into this though we are - * attempting to avoid substaintial plagarism or the more explicit dependency - * on jQuery only because OpenSeadragon is a broadly useful code base and - * would be made less broad by requiring jQuery fully. - * - * Some static methods have also been refactored from the original OpenSeadragon - * project. - */ -(function( $ ){ - - /** - * Taken from jQuery 1.6.1 - * @see jQuery - */ - $.extend = function() { - var options, - name, - src, - copy, - copyIsArray, - clone, - target = arguments[ 0 ] || {}, - length = arguments.length, - deep = false, - i = 1; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - target = arguments[ 1 ] || {}; - // skip the boolean and the target - i = 2; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !OpenSeadragon.isFunction( target ) ) { - target = {}; - } - - // extend jQuery itself if only one argument is passed - if ( length === i ) { - target = this; - --i; - } - - for ( ; i < length; i++ ) { - // Only deal with non-null/undefined values - options = arguments[ i ]; - if ( options !== null || options !== undefined ) { - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( OpenSeadragon.isPlainObject( copy ) || ( copyIsArray = OpenSeadragon.isArray( copy ) ) ) ) { - if ( copyIsArray ) { - copyIsArray = false; - clone = src && OpenSeadragon.isArray( src ) ? src : []; - - } else { - clone = src && OpenSeadragon.isPlainObject( src ) ? src : {}; - } - - // Never move original objects, clone them - target[ name ] = OpenSeadragon.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; - }; - - - $.extend( $, { - /** - * These are the default values for the optional settings documented - * in the {@link OpenSeadragon} constructor detail. - * @name $.DEFAULT_SETTINGS - * @static - */ - DEFAULT_SETTINGS: { - //DATA SOURCE DETAILS - xmlPath: null, - tileSources: null, - tileHost: null, - - //PAN AND ZOOM SETTINGS AND CONSTRAINTS - panHorizontal: true, - panVertical: true, - constrainDuringPan: false, - wrapHorizontal: false, - wrapVertical: false, - visibilityRatio: 0.5, - minPixelRatio: 0.5, - minZoomImageRatio: 0.8, - maxZoomPixelRatio: 2, - defaultZoomLevel: 0, - minZoomLevel: null, - maxZoomLevel: null, - - //UI RESPONSIVENESS AND FEEL - springStiffness: 5.0, - clickTimeThreshold: 300, - clickDistThreshold: 5, - zoomPerClick: 2.0, - zoomPerScroll: 1.2, - zoomPerSecond: 2.0, - animationTime: 1.5, - blendTime: 0.5, - alwaysBlend: false, - autoHideControls: true, - immediateRender: false, - - //DEFAULT CONTROL SETTINGS - showSequenceControl: true, //SEQUENCE - preserveViewport: false, //SEQUENCE - showNavigationControl: true, //ZOOM/HOME/FULL/SEQUENCE - controlsFadeDelay: 2000, //ZOOM/HOME/FULL/SEQUENCE - controlsFadeLength: 1500, //ZOOM/HOME/FULL/SEQUENCE - mouseNavEnabled: true, //GENERAL MOUSE INTERACTIVITY - - //VIEWPORT NAVIGATOR SETTINGS - showNavigator: true, //promoted to default in 0.9.64 - navigatorElement: null, - navigatorHeight: null, - navigatorWidth: null, - navigatorPosition: null, - navigatorSizeRatio: 0.2, - - //REFERENCE STRIP SETTINGS - showReferenceStrip: false, - referenceStripScroll: 'horizontal', - referenceStripElement: null, - referenceStripHeight: null, - referenceStripWidth: null, - referenceStripPosition: 'BOTTOM_LEFT', - referenceStripSizeRatio: 0.2, - - //COLLECTION VISUALIZATION SETTINGS - collectionRows: 3, //or columns depending on layout - collectionLayout: 'horizontal', //vertical - collectionMode: false, - collectionTileSize: 800, - - //EVENT RELATED CALLBACKS - onPageChange: null, - - //PERFORMANCE SETTINGS - imageLoaderLimit: 0, - maxImageCacheCount: 200, - timeout: 5000, - - //INTERFACE RESOURCE SETTINGS - prefixUrl: "/images/", - navImages: { - zoomIn: { - REST: 'zoomin_rest.png', - GROUP: 'zoomin_grouphover.png', - HOVER: 'zoomin_hover.png', - DOWN: 'zoomin_pressed.png' - }, - zoomOut: { - REST: 'zoomout_rest.png', - GROUP: 'zoomout_grouphover.png', - HOVER: 'zoomout_hover.png', - DOWN: 'zoomout_pressed.png' - }, - home: { - REST: 'home_rest.png', - GROUP: 'home_grouphover.png', - HOVER: 'home_hover.png', - DOWN: 'home_pressed.png' - }, - fullpage: { - REST: 'fullpage_rest.png', - GROUP: 'fullpage_grouphover.png', - HOVER: 'fullpage_hover.png', - DOWN: 'fullpage_pressed.png' - }, - previous: { - REST: 'previous_rest.png', - GROUP: 'previous_grouphover.png', - HOVER: 'previous_hover.png', - DOWN: 'previous_pressed.png' - }, - next: { - REST: 'next_rest.png', - GROUP: 'next_grouphover.png', - HOVER: 'next_hover.png', - DOWN: 'next_pressed.png' - } - }, - - //DEVELOPER SETTINGS - debugMode: false, - debugGridColor: '#437AB2' - }, - - - /** - * TODO: get rid of this. I can't see how it's required at all. Looks - * like an early legacy code artifact. - * @static - * @ignore - */ - SIGNAL: "----seadragon----", - - - /** - * Invokes the the method as if it where a method belonging to the object. - * @name $.delegate - * @function - * @param {Object} object - * @param {Function} method - */ - delegate: function( object, method ) { - return function(){ - var args = arguments; - if ( args === undefined ){ - args = []; - } - return method.apply( object, args ); - }; - }, - - - /** - * An enumeration of Browser vendors including UNKNOWN, IE, FIREFOX, - * SAFARI, CHROME, and OPERA. - * @name $.BROWSERS - * @static - */ - BROWSERS: { - UNKNOWN: 0, - IE: 1, - FIREFOX: 2, - SAFARI: 3, - CHROME: 4, - OPERA: 5 - }, - - - /** - * Returns a DOM Element for the given id or element. - * @function - * @name OpenSeadragon.getElement - * @param {String|Element} element Accepts an id or element. - * @returns {Element} The element with the given id, null, or the element itself. - */ - getElement: function( element ) { - if ( typeof ( element ) == "string" ) { - element = document.getElementById( element ); - } - return element; - }, - - - /** - * Determines the position of the upper-left corner of the element. - * @function - * @name OpenSeadragon.getElementPosition - * @param {Element|String} element - the elemenet we want the position for. - * @returns {Point} - the position of the upper left corner of the element. - */ - getElementPosition: function( element ) { - var result = new $.Point(), - isFixed, - offsetParent; - - element = $.getElement( element ); - isFixed = $.getElementStyle( element ).position == "fixed"; - offsetParent = getOffsetParent( element, isFixed ); - - while ( offsetParent ) { - - result.x += element.offsetLeft; - result.y += element.offsetTop; - - if ( isFixed ) { - result = result.plus( $.getPageScroll() ); - } - - element = offsetParent; - isFixed = $.getElementStyle( element ).position == "fixed"; - offsetParent = getOffsetParent( element, isFixed ); - } - - return result; - }, - - - /** - * Determines the height and width of the given element. - * @function - * @name OpenSeadragon.getElementSize - * @param {Element|String} element - * @returns {Point} - */ - getElementSize: function( element ) { - element = $.getElement( element ); - - return new $.Point( - element.clientWidth, - element.clientHeight - ); - }, - - - /** - * Returns the CSSStyle object for the given element. - * @function - * @name OpenSeadragon.getElementStyle - * @param {Element|String} element - * @returns {CSSStyle} - */ - getElementStyle: - document.documentElement.currentStyle ? - function( element ) { - element = $.getElement( element ); - return element.currentStyle; - } : - function( element ) { - element = $.getElement( element ); - return window.getComputedStyle( element, "" ); - }, - - - /** - * Gets the latest event, really only useful internally since its - * specific to IE behavior. TODO: Deprecate this from the api and - * use it internally. - * @function - * @name OpenSeadragon.getEvent - * @param {Event} [event] - * @returns {Event} - */ - getEvent: function( event ) { - if( event ){ - $.getEvent = function( event ){ - return event; - }; - } else { - $.getEvent = function( event ){ - return window.event; - }; - } - return $.getEvent( event ); - }, - - - /** - * Gets the position of the mouse on the screen for a given event. - * @function - * @name OpenSeadragon.getMousePosition - * @param {Event} [event] - * @returns {Point} - */ - getMousePosition: function( event ) { - - if ( typeof( event.pageX ) == "number" ) { - $.getMousePosition = function( event ){ - var result = new $.Point(); - - event = $.getEvent( event ); - result.x = event.pageX; - result.y = event.pageY; - - return result; - }; - } else if ( typeof( event.clientX ) == "number" ) { - $.getMousePosition = function( event ){ - var result = new $.Point(); - - event = $.getEvent( event ); - result.x = - event.clientX + - document.body.scrollLeft + - document.documentElement.scrollLeft; - result.y = - event.clientY + - document.body.scrollTop + - document.documentElement.scrollTop; - - return result; - }; - } else { - throw new Error( - "Unknown event mouse position, no known technique." - ); - } - - return $.getMousePosition( event ); - }, - - - /** - * Determines the pages current scroll position. - * @function - * @name OpenSeadragon.getPageScroll - * @returns {Point} - */ - getPageScroll: function() { - var docElement = document.documentElement || {}, - body = document.body || {}; - - if ( typeof( window.pageXOffset ) == "number" ) { - $.getPageScroll = function(){ - return new $.Point( - window.pageXOffset, - window.pageYOffset - ); - }; - } else if ( body.scrollLeft || body.scrollTop ) { - $.getPageScroll = function(){ - return new $.Point( - document.body.scrollLeft, - document.body.scrollTop - ); - }; - } else if ( docElement.scrollLeft || docElement.scrollTop ) { - $.getPageScroll = function(){ - return new $.Point( - document.documentElement.scrollLeft, - document.documentElement.scrollTop - ); - }; - } else { - $.getPageScroll = function(){ - return new $.Point(0,0); - }; - } - - return $.getPageScroll(); - }, - - - /** - * Determines the size of the browsers window. - * @function - * @name OpenSeadragon.getWindowSize - * @returns {Point} - */ - getWindowSize: function() { - var docElement = document.documentElement || {}, - body = document.body || {}; - - if ( typeof( window.innerWidth ) == 'number' ) { - $.getWindowSize = function(){ - return new $.Point( - window.innerWidth, - window.innerHeight - ); - }; - } else if ( docElement.clientWidth || docElement.clientHeight ) { - $.getWindowSize = function(){ - return new $.Point( - document.documentElement.clientWidth, - document.documentElement.clientHeight - ); - }; - } else if ( body.clientWidth || body.clientHeight ) { - $.getWindowSize = function(){ - return new $.Point( - document.body.clientWidth, - document.body.clientHeight - ); - }; - } else { - throw new Error("Unknown window size, no known technique."); - } - - return $.getWindowSize(); - }, - - - /** - * Wraps the given element in a nest of divs so that the element can - * be easily centered. - * @function - * @name OpenSeadragon.makeCenteredNode - * @param {Element|String} element - * @returns {Element} - */ - makeCenteredNode: function( element ) { - - var div = $.makeNeutralElement( "div" ), - html = [], - innerDiv, - innerDivs; - - element = $.getElement( element ); - - //TODO: I dont understand the use of # inside the style attributes - // below. Invetigate the results of the constructed html in - // the browser and clean up the mark-up to make this clearer. - html.push('
'); - html.push('
'); - html.push('
'); - - div.innerHTML = html.join( '' ); - div = div.firstChild; - - innerDiv = div; - innerDivs = div.getElementsByTagName( "div" ); - while ( innerDivs.length > 0 ) { - innerDiv = innerDivs[ 0 ]; - innerDivs = innerDiv.getElementsByTagName( "div" ); - } - - innerDiv.appendChild( element ); - - return div; - }, - - - /** - * Creates an easily positionable element of the given type that therefor - * serves as an excellent container element. - * @function - * @name OpenSeadragon.makeNeutralElement - * @param {String} tagName - * @returns {Element} - */ - makeNeutralElement: function( tagName ) { - var element = document.createElement( tagName ), - style = element.style; - - style.background = "transparent none"; - style.border = "none"; - style.margin = "0px"; - style.padding = "0px"; - style.position = "static"; - - return element; - }, - - - /** - * Ensures an image is loaded correctly to support alpha transparency. - * Generally only IE has issues doing this correctly for formats like - * png. - * @function - * @name OpenSeadragon.makeTransparentImage - * @param {String} src - * @returns {Element} - */ - makeTransparentImage: function( src ) { - - $.makeTransparentImage = function( src ){ - var img = $.makeNeutralElement( "img" ); - - img.src = src; - - return img; - }; - - if ( $.Browser.vendor == $.BROWSERS.IE && $.Browser.version < 7 ) { - - $.makeTransparentImage = function( src ){ - var img = $.makeNeutralElement( "img" ), - element = null; - - element = $.makeNeutralElement("span"); - element.style.display = "inline-block"; - - img.onload = function() { - element.style.width = element.style.width || img.width + "px"; - element.style.height = element.style.height || img.height + "px"; - - img.onload = null; - img = null; // to prevent memory leaks in IE - }; - - img.src = src; - element.style.filter = - "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + - src + - "', sizingMethod='scale')"; - - return element; - }; - - } - - return $.makeTransparentImage( src ); - }, - - - /** - * Sets the opacity of the specified element. - * @function - * @name OpenSeadragon.setElementOpacity - * @param {Element|String} element - * @param {Number} opacity - * @param {Boolean} [usesAlpha] - */ - setElementOpacity: function( element, opacity, usesAlpha ) { - - var previousFilter, - ieOpacity, - ieFilter; - - element = $.getElement( element ); - - if ( usesAlpha && !$.Browser.alpha ) { - opacity = Math.round( opacity ); - } - - if ( opacity < 1 ) { - element.style.opacity = opacity; - } else { - element.style.opacity = ""; - } - - if ( opacity == 1 ) { - prevFilter = element.style.filter || ""; - element.style.filter = prevFilter.replace(/alpha\(.*?\)/g, ""); - return; - } - - ieOpacity = Math.round( 100 * opacity ); - ieFilter = " alpha(opacity=" + ieOpacity + ") "; - - //TODO: find out why this uses a try/catch instead of a predetermined - // routine or at least an if/elseif/else - try { - if ( element.filters && element.filters.alpha ) { - element.filters.alpha.opacity = ieOpacity; - } else { - element.style.filter += ieFilter; - } - } catch ( e ) { - element.style.filter += ieFilter; - } - }, - - - /** - * Adds an event listener for the given element, eventName and handler. - * @function - * @name OpenSeadragon.addEvent - * @param {Element|String} element - * @param {String} eventName - * @param {Function} handler - * @param {Boolean} [useCapture] - * @throws {Error} - */ - addEvent: function( element, eventName, handler, useCapture ) { - element = $.getElement( element ); - - //TODO: Why do this if/else on every method call instead of just - // defining this function once based on the same logic - if ( element.addEventListener ) { - $.addEvent = function( element, eventName, handler, useCapture ){ - element = $.getElement( element ); - element.addEventListener( eventName, handler, useCapture ); - }; - } else if ( element.attachEvent ) { - $.addEvent = function( element, eventName, handler, useCapture ){ - element = $.getElement( element ); - element.attachEvent( "on" + eventName, handler ); - if ( useCapture && element.setCapture ) { - element.setCapture(); - } - }; - } else { - throw new Error( - "Unable to attach event handler, no known technique." - ); - } - - return $.addEvent( element, eventName, handler, useCapture ); - }, - - - /** - * Remove a given event listener for the given element, event type and - * handler. - * @function - * @name OpenSeadragon.removeEvent - * @param {Element|String} element - * @param {String} eventName - * @param {Function} handler - * @param {Boolean} [useCapture] - * @throws {Error} - */ - removeEvent: function( element, eventName, handler, useCapture ) { - element = $.getElement( element ); - - //TODO: Why do this if/else on every method call instead of just - // defining this function once based on the same logic - if ( element.removeEventListener ) { - $.removeEvent = function( element, eventName, handler, useCapture ) { - element = $.getElement( element ); - element.removeEventListener( eventName, handler, useCapture ); - }; - } else if ( element.detachEvent ) { - $.removeEvent = function( element, eventName, handler, useCapture ) { - element = $.getElement( element ); - element.detachEvent("on" + eventName, handler); - if ( useCapture && element.releaseCapture ) { - element.releaseCapture(); - } - }; - } else { - throw new Error( - "Unable to detach event handler, no known technique." - ); - } - return $.removeEvent( element, eventName, handler, useCapture ); - }, - - - /** - * Cancels the default browser behavior had the event propagated all - * the way up the DOM to the window object. - * @function - * @name OpenSeadragon.cancelEvent - * @param {Event} [event] - */ - cancelEvent: function( event ) { - event = $.getEvent( event ); - - if ( event.preventDefault ) { - $.cancelEvent = function( event ){ - // W3C for preventing default - event.preventDefault(); - }; - } else { - $.cancelEvent = function( event ){ - event = $.getEvent( event ); - // legacy for preventing default - event.cancel = true; - // IE for preventing default - event.returnValue = false; - }; - } - $.cancelEvent( event ); - }, - - - /** - * Stops the propagation of the event up the DOM. - * @function - * @name OpenSeadragon.stopEvent - * @param {Event} [event] - */ - stopEvent: function( event ) { - event = $.getEvent( event ); - - if ( event.stopPropagation ) { - // W3C for stopping propagation - $.stopEvent = function( event ){ - event.stopPropagation(); - }; - } else { - // IE for stopping propagation - $.stopEvent = function( event ){ - event = $.getEvent( event ); - event.cancelBubble = true; - }; - - } - - $.stopEvent( event ); - }, - - - /** - * Similar to OpenSeadragon.delegate, but it does not immediately call - * the method on the object, returning a function which can be called - * repeatedly to delegate the method. It also allows additonal arguments - * to be passed during construction which will be added during each - * invocation, and each invocation can add additional arguments as well. - * - * @function - * @name OpenSeadragon.createCallback - * @param {Object} object - * @param {Function} method - * @param [args] any additional arguments are passed as arguments to the - * created callback - * @returns {Function} - */ - createCallback: function( object, method ) { - //TODO: This pattern is painful to use and debug. It's much cleaner - // to use pinning plus anonymous functions. Get rid of this - // pattern! - var initialArgs = [], - i; - for ( i = 2; i < arguments.length; i++ ) { - initialArgs.push( arguments[ i ] ); - } - - return function() { - var args = initialArgs.concat( [] ), - i; - for ( i = 0; i < arguments.length; i++ ) { - args.push( arguments[ i ] ); - } - - return method.apply( object, args ); - }; - }, - - - /** - * Retreives the value of a url parameter from the window.location string. - * @function - * @name OpenSeadragon.getUrlParameter - * @param {String} key - * @returns {String} The value of the url parameter or null if no param matches. - */ - getUrlParameter: function( key ) { - var value = URLPARAMS[ key ]; - return value ? value : null; - }, - - - createAjaxRequest: function(){ - var request; - - if ( window.ActiveXObject ) { - //TODO: very bad...Why check every time using try/catch when - // we could determine once at startup which activeX object - // was supported. This will have significant impact on - // performance for IE Browsers DONE - /*jshint loopfunc:true*/ - for ( i = 0; i < ACTIVEX.length; i++ ) { - try { - request = new ActiveXObject( ACTIVEX[ i ] ); - $.createAjaxRequest = function( ){ - return new ActiveXObject( ACTIVEX[ i ] ); - }; - break; - } catch (e) { - continue; - } - } - } else if ( window.XMLHttpRequest ) { - $.createAjaxRequest = function( ){ - return new XMLHttpRequest(); - }; - request = new XMLHttpRequest(); - } - - if ( !request ) { - throw new Error( "Browser doesn't support XMLHttpRequest." ); - } - - return request; - }, - /** - * Makes an AJAX request. - * @function - * @name OpenSeadragon.makeAjaxRequest - * @param {String} url - the url to request - * @param {Function} [callback] - a function to call when complete - * @throws {Error} - */ - makeAjaxRequest: function( url, callback ) { - - var async = true, - request = $.createAjaxRequest(), - actual, - options, - i; - - - if( $.isPlainObject( url ) ){ - options.async = options.async || async; - }else{ - options = { - url: url, - async: $.isFunction( callback ), - success: callback, - error: null - }; - } - - if ( options.async ) { - /** @ignore */ - request.onreadystatechange = function() { - if ( request.readyState == 4) { - request.onreadystatechange = function(){}; - options.success( request ); - } - }; - } - - try { - request.open( "GET", options.url, options.async ); - request.send( null ); - } catch (e) { - $.console.log( - "%s while making AJAX request: %s", - e.name, - e.message - ); - - request.onreadystatechange = null; - request = null; - - if ( options.error && $.isFunction( options.error ) ) { - options.error( request ); - } - } - - if( !options.async && $.isFunction( options.success ) ){ - options.success( request ); - } - - return options.async ? null : request; - }, - - - /** - * Taken from jQuery 1.6.1 - * @function - * @name OpenSeadragon.jsonp - * @param {Object} options - * @param {String} options.url - * @param {Function} options.callback - * @param {String} [options.param='callback'] The name of the url parameter - * to request the jsonp provider with. - * @param {String} [options.callbackName=] The name of the callback to - * request the jsonp provider with. - */ - jsonp: function( options ){ - var script, - url = options.url, - head = document.head || - document.getElementsByTagName( "head" )[ 0 ] || - document.documentElement, - jsonpCallback = options.callbackName || 'openseadragon' + (+new Date()), - previous = window[ jsonpCallback ], - replace = "$1" + jsonpCallback + "$2", - callbackParam = options.param || 'callback', - callback = options.callback; - - url = url.replace( /(\=)\?(&|$)|\?\?/i, replace ); - // Add callback manually - url += (/\?/.test( url ) ? "&" : "?") + callbackParam + "=" + jsonpCallback; - - // Install callback - window[ jsonpCallback ] = function( response ) { - if ( !previous ){ - try{ - delete window[ jsonpCallback ]; - }catch(e){ - //swallow - } - } else { - window[ jsonpCallback ] = previous; - } - if( callback && $.isFunction( callback ) ){ - callback( response ); - } - }; - - script = document.createElement( "script" ); - - //TODO: having an issue with async info requests - if( undefined !== options.async || false !== options.async ){ - script.async = "async"; - } - - if ( options.scriptCharset ) { - script.charset = options.scriptCharset; - } - - script.src = url; - - // Attach handlers for all browsers - script.onload = script.onreadystatechange = function( _, isAbort ) { - - if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) { - - // Handle memory leak in IE - script.onload = script.onreadystatechange = null; - - // Remove the script - if ( head && script.parentNode ) { - head.removeChild( script ); - } - - // Dereference the script - script = undefined; - } - }; - // Use insertBefore instead of appendChild to circumvent an IE6 bug. - // This arises when a base node is used (#2709 and #4378). - head.insertBefore( script, head.firstChild ); - - }, - - - /** - * Loads a Deep Zoom Image description from a url, XML string or JSON string - * and provides a callback hook for the resulting Document - * @function - * @name OpenSeadragon.createFromDZI - * @param {String} xmlUrl - * @param {String} xmlString - * @param {Function} callback - * @deprecated - */ - createFromDZI: function( dzi, callback, tileHost ) { - var async = typeof ( callback ) == "function", - dziUrl = ( - dzi.substring(0,1) != '<' && - dzi.substring(0,1) != '{' - ) ? dzi : null, - dziString = dziUrl ? null : dzi, - error = null, - urlParts, - filename, - lastDot, - tilesUrl, - callbackName; - - - if( tileHost ){ - - tilesUrl = tileHost + "/_files/"; - - } else if( dziUrl ) { - - urlParts = dziUrl.split( '/' ); - filename = urlParts[ urlParts.length - 1 ]; - if( filename.match(/_dzi\.js$/) ){ - //for jsonp dzi specification, the '_dzi' needs to be removed - //from the filename to be consistent with the spec - filename = filename.replace('_dzi.js', '.js'); - } - - lastDot = filename.lastIndexOf( '.' ); - - if ( lastDot > -1 ) { - urlParts[ urlParts.length - 1 ] = filename.slice( 0, lastDot ); - } - - - tilesUrl = urlParts.join( '/' ) + "_files/"; - - } - - function finish( func, obj ) { - try { - return func( obj, tilesUrl ); - } catch ( e ) { - if ( async ) { - return null; - } else { - throw e; - } - } - } - - if ( async ) { - if ( dziString ) { - window.setTimeout( function() { - var source = finish( processDZIXml, $.parseXml( xmlString ) ); - // call after finish sets error - callback( source, error ); - }, 1); - } else { - if( dziUrl.match(/_dzi\.js$/) ){ - callbackName = dziUrl.split( '/' ).pop().replace('.js',''); - $.jsonp({ - url: dziUrl, - callbackName: callbackName, - callback: function( imageData ){ - var source = finish( processDZIJSON, imageData.Image ); - callback( source ); - } - }); - } else { - $.makeAjaxRequest( dziUrl, function( xhr ) { - var source = finish( processDZIResponse, xhr ); - // call after finish sets error - callback( source, error ); - }); - } - } - - return null; - } - - if ( dziString ) { - return finish( - processDZIXml, - $.parseXml( dziString ) - ); - } else { - return finish( - processDZIResponse, - $.makeAjaxRequest( dziUrl ) - ); - } - }, - - /** - * Parses an XML string into a DOM Document. - * @function - * @name OpenSeadragon.parseXml - * @param {String} string - * @returns {Document} - */ - parseXml: function( string ) { - //TODO: yet another example where we can determine the correct - // implementation once at start-up instead of everytime we use - // the function. DONE. - if ( window.ActiveXObject ) { - - $.parseXml = function( string ){ - var xmlDoc = null, - parser; - - xmlDoc = new ActiveXObject( "Microsoft.XMLDOM" ); - xmlDoc.async = false; - xmlDoc.loadXML( string ); - return xmlDoc; - }; - - } else if ( window.DOMParser ) { - - $.parseXml = function( string ){ - var xmlDoc = null, - parser; - - parser = new DOMParser(); - xmlDoc = parser.parseFromString( string, "text/xml" ); - return xmlDoc; - }; - - } else { - throw new Error( "Browser doesn't support XML DOM." ); - } - - return $.parseXml( string ); - }, - - - /** - * Reports whether the image format is supported for tiling in this - * version. - * @function - * @name OpenSeadragon.imageFormatSupported - * @param {String} [extension] - * @returns {Boolean} - */ - imageFormatSupported: function( extension ) { - extension = extension ? extension : ""; - return !!FILEFORMATS[ extension.toLowerCase() ]; - } - - }); - - - /** - * The current browser vendor, version, and related information regarding - * detected features. Features include
- * 'alpha' - Does the browser support image alpha - * transparency.
- * @name $.Browser - * @static - */ - $.Browser = { - vendor: $.BROWSERS.UNKNOWN, - version: 0, - alpha: true - }; - - - var ACTIVEX = [ - "Msxml2.XMLHTTP", - "Msxml3.XMLHTTP", - "Microsoft.XMLHTTP" - ], - FILEFORMATS = { - "bmp": false, - "jpeg": true, - "jpg": true, - "png": true, - "tif": false, - "wdp": false - }, - URLPARAMS = {}; - - (function() { - //A small auto-executing routine to determine the browser vendor, - //version and supporting feature sets. - var app = navigator.appName, - ver = navigator.appVersion, - ua = navigator.userAgent; - - //console.error( 'appName: ' + navigator.appName ); - //console.error( 'appVersion: ' + navigator.appVersion ); - //console.error( 'userAgent: ' + navigator.userAgent ); - - switch( navigator.appName ){ - case "Microsoft Internet Explorer": - if( !!window.attachEvent && - !!window.ActiveXObject ) { - - $.Browser.vendor = $.BROWSERS.IE; - $.Browser.version = parseFloat( - ua.substring( - ua.indexOf( "MSIE" ) + 5, - ua.indexOf( ";", ua.indexOf( "MSIE" ) ) ) - ); - } - break; - case "Netscape": - if( !!window.addEventListener ){ - if ( ua.indexOf( "Firefox" ) >= 0 ) { - $.Browser.vendor = $.BROWSERS.FIREFOX; - $.Browser.version = parseFloat( - ua.substring( ua.indexOf( "Firefox" ) + 8 ) - ); - } else if ( ua.indexOf( "Safari" ) >= 0 ) { - $.Browser.vendor = ua.indexOf( "Chrome" ) >= 0 ? - $.BROWSERS.CHROME : - $.BROWSERS.SAFARI; - $.Browser.version = parseFloat( - ua.substring( - ua.substring( 0, ua.indexOf( "Safari" ) ).lastIndexOf( "/" ) + 1, - ua.indexOf( "Safari" ) - ) - ); - } - } - break; - case "Opera": - $.Browser.vendor = $.BROWSERS.OPERA; - $.Browser.version = parseFloat( ver ); - break; - } - - // ignore '?' portion of query string - var query = window.location.search.substring( 1 ), - parts = query.split('&'), - part, - sep, - i; - - for ( i = 0; i < parts.length; i++ ) { - part = parts[ i ]; - sep = part.indexOf( '=' ); - - if ( sep > 0 ) { - URLPARAMS[ part.substring( 0, sep ) ] = - decodeURIComponent( part.substring( sep + 1 ) ); - } - } - - //determine if this browser supports image alpha transparency - $.Browser.alpha = !( - ( - $.Browser.vendor == $.BROWSERS.IE && - $.Browser.version < 9 - ) || ( - $.Browser.vendor == $.BROWSERS.CHROME && - $.Browser.version < 2 - ) - ); - - })(); - - //TODO: $.console is often used inside a try/catch block which generally - // prevents allowings errors to occur with detection until a debugger - // is attached. Although I've been guilty of the same anti-pattern - // I eventually was convinced that errors should naturally propogate in - // all but the most special cases. - /** - * A convenient alias for console when available, and a simple null - * function when console is unavailable. - * @static - * @private - */ - var nullfunction = function( msg ){ - //document.location.hash = msg; - }; - - $.console = window.console || { - log: nullfunction, - debug: nullfunction, - info: nullfunction, - warn: nullfunction, - error: nullfunction - }; - - - /** - * @private - * @inner - * @function - * @param {Element} element - * @param {Boolean} [isFixed] - * @returns {Element} - */ - function getOffsetParent( element, isFixed ) { - if ( isFixed && element != document.body ) { - return document.body; - } else { - return element.offsetParent; - } - } - - /** - * @private - * @inner - * @function - * @param {XMLHttpRequest} xhr - * @param {String} tilesUrl - * @deprecated - */ - function processDZIResponse( xhr, tilesUrl ) { - var status, - statusText, - doc = null; - - if ( !xhr ) { - throw new Error( $.getString( "Errors.Security" ) ); - } else if ( xhr.status !== 200 && xhr.status !== 0 ) { - status = xhr.status; - statusText = ( status == 404 ) ? - "Not Found" : - xhr.statusText; - throw new Error( $.getString( "Errors.Status", status, statusText ) ); - } - - if ( xhr.responseXML && xhr.responseXML.documentElement ) { - doc = xhr.responseXML; - } else if ( xhr.responseText ) { - doc = $.parseXml( xhr.responseText ); - } - - return processDZIXml( doc, tilesUrl ); - } - - /** - * @private - * @inner - * @function - * @param {Document} xmlDoc - * @param {String} tilesUrl - * @deprecated - */ - function processDZIXml( xmlDoc, tilesUrl ) { - - if ( !xmlDoc || !xmlDoc.documentElement ) { - throw new Error( $.getString( "Errors.Xml" ) ); - } - - var root = xmlDoc.documentElement, - rootName = root.tagName; - - if ( rootName == "Image" ) { - try { - return processDZI( root, tilesUrl ); - } catch ( e ) { - throw (e instanceof Error) ? - e : - new Error( $.getString("Errors.Dzi") ); - } - } else if ( rootName == "Collection" ) { - throw new Error( $.getString( "Errors.Dzc" ) ); - } else if ( rootName == "Error" ) { - return processDZIError( root ); - } - - throw new Error( $.getString( "Errors.Dzi" ) ); - } - - /** - * @private - * @inner - * @function - * @param {Element} imageNode - * @param {String} tilesUrl - * @deprecated - */ - function processDZI( imageNode, tilesUrl ) { - var fileFormat = imageNode.getAttribute( "Format" ), - sizeNode = imageNode.getElementsByTagName( "Size" )[ 0 ], - dispRectNodes = imageNode.getElementsByTagName( "DisplayRect" ), - width = parseInt( sizeNode.getAttribute( "Width" ), 10 ), - height = parseInt( sizeNode.getAttribute( "Height" ), 10 ), - tileSize = parseInt( imageNode.getAttribute( "TileSize" ), 10 ), - tileOverlap = parseInt( imageNode.getAttribute( "Overlap" ), 10 ), - dispRects = [], - dispRectNode, - rectNode, - i; - - if ( !imageFormatSupported( fileFormat ) ) { - throw new Error( - $.getString( "Errors.ImageFormat", fileFormat.toUpperCase() ) - ); - } - - for ( i = 0; i < dispRectNodes.length; i++ ) { - dispRectNode = dispRectNodes[ i ]; - rectNode = dispRectNode.getElementsByTagName( "Rect" )[ 0 ]; - - dispRects.push( new $.DisplayRect( - parseInt( rectNode.getAttribute( "X" ), 10 ), - parseInt( rectNode.getAttribute( "Y" ), 10 ), - parseInt( rectNode.getAttribute( "Width" ), 10 ), - parseInt( rectNode.getAttribute( "Height" ), 10 ), - 0, // ignore MinLevel attribute, bug in Deep Zoom Composer - parseInt( dispRectNode.getAttribute( "MaxLevel" ), 10 ) - )); - } - return new $.DziTileSource( - width, - height, - tileSize, - tileOverlap, - tilesUrl, - fileFormat, - dispRects - ); - } - - /** - * @private - * @inner - * @function - * @param {Element} imageNode - * @param {String} tilesUrl - * @deprecated - */ - function processDZIJSON( imageData, tilesUrl ) { - var fileFormat = imageData.Format, - sizeData = imageData.Size, - dispRectData = imageData.DisplayRect || [], - width = parseInt( sizeData.Width, 10 ), - height = parseInt( sizeData.Height, 10 ), - tileSize = parseInt( imageData.TileSize, 10 ), - tileOverlap = parseInt( imageData.Overlap, 10 ), - dispRects = [], - rectData, - i; - - if ( !imageFormatSupported( fileFormat ) ) { - throw new Error( - $.getString( "Errors.ImageFormat", fileFormat.toUpperCase() ) - ); - } - - for ( i = 0; i < dispRectData.length; i++ ) { - rectData = dispRectData[ i ].Rect; - - dispRects.push( new $.DisplayRect( - parseInt( rectData.X, 10 ), - parseInt( rectData.Y, 10 ), - parseInt( rectData.Width, 10 ), - parseInt( rectData.Height, 10 ), - 0, // ignore MinLevel attribute, bug in Deep Zoom Composer - parseInt( rectData.MaxLevel, 10 ) - )); - } - return new $.DziTileSource( - width, - height, - tileSize, - tileOverlap, - tilesUrl, - fileFormat, - dispRects - ); - } - - /** - * @private - * @inner - * @function - * @param {Document} errorNode - * @throws {Error} - * @deprecated - */ - function processDZIError( errorNode ) { - var messageNode = errorNode.getElementsByTagName( "Message" )[ 0 ], - message = messageNode.firstChild.nodeValue; - - throw new Error(message); - } - -}( OpenSeadragon )); -/*globals OpenSeadragon */ - -(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 )); -/*globals OpenSeadragon */ - -(function( $ ){ - - // is any button currently being pressed while mouse events occur - var IS_BUTTON_DOWN = false, - // is any tracker currently capturing? - IS_CAPTURING = false, - // dictionary from hash to MouseTracker - ACTIVE = {}, - // list of trackers interested in capture - CAPTURING = [], - // dictionary from hash to private properties - THIS = {}; - - /** - * The MouseTracker allows other classes to set handlers for common mouse - * events on a specific element like, 'enter', 'exit', 'press', 'release', - * 'scroll', 'click', and 'drag'. - * @class - * @param {Object} options - * Allows configurable properties to be entirely specified by passing - * an options object to the constructor. The constructor also supports - * the original positional arguments 'elements', 'clickTimeThreshold', - * and 'clickDistThreshold' in that order. - * @param {Element|String} options.element - * A reference to an element or an element id for which the mouse - * events will be monitored. - * @param {Number} options.clickTimeThreshold - * The number of milliseconds within which mutliple mouse clicks - * will be treated as a single event. - * @param {Number} options.clickDistThreshold - * The distance between mouse click within multiple mouse clicks - * will be treated as a single event. - * @param {Function} options.enterHandler - * An optional handler for mouse enter. - * @param {Function} options.exitHandler - * An optional handler for mouse exit. - * @param {Function} options.pressHandler - * An optional handler for mouse press. - * @param {Function} options.releaseHandler - * An optional handler for mouse release. - * @param {Function} options.scrollHandler - * An optional handler for mouse scroll. - * @param {Function} options.clickHandler - * An optional handler for mouse click. - * @param {Function} options.dragHandler - * An optional handler for mouse drag. - * @property {Number} hash - * An unique hash for this tracker. - * @property {Element} element - * The element for which mouse event are being monitored. - * @property {Number} clickTimeThreshold - * The number of milliseconds within which mutliple mouse clicks - * will be treated as a single event. - * @property {Number} clickDistThreshold - * The distance between mouse click within multiple mouse clicks - * will be treated as a single event. - */ - $.MouseTracker = function ( options ) { - - var args = arguments; - - if( !$.isPlainObject( options ) ){ - options = { - element: args[ 0 ], - clickTimeThreshold: args[ 1 ], - clickDistThreshold: args[ 2 ] - }; - } - - this.hash = Math.random(); - this.element = $.getElement( options.element ); - this.clickTimeThreshold = options.clickTimeThreshold; - this.clickDistThreshold = options.clickDistThreshold; - - - this.enterHandler = options.enterHandler || null; - this.exitHandler = options.exitHandler || null; - this.pressHandler = options.pressHandler || null; - this.releaseHandler = options.releaseHandler || null; - this.scrollHandler = options.scrollHandler || null; - this.clickHandler = options.clickHandler || null; - this.dragHandler = options.dragHandler || null; - this.keyHandler = options.keyHandler || null; - this.focusHandler = options.focusHandler || null; - this.blurHandler = options.blurHandler || null; - - //Store private properties in a scope sealed hash map - var _this = this; - - /** - * @private - * @property {Boolean} tracking - * Are we currently tracking mouse events. - * @property {Boolean} capturing - * Are we curruently capturing mouse events. - * @property {Boolean} buttonDown - * True if the left mouse button is currently being pressed and was - * initiated inside the tracked element, otherwise false. - * @property {Boolean} insideElement - * Are we currently inside the screen area of the tracked element. - * @property {OpenSeadragon.Point} lastPoint - * Position of last mouse down/move - * @property {Number} lastMouseDownTime - * Time of last mouse down. - * @property {OpenSeadragon.Point} lastMouseDownPoint - * Position of last mouse down - */ - THIS[ this.hash ] = { - mouseover: function( event ){ onMouseOver( _this, event ); }, - mouseout: function( event ){ onMouseOut( _this, event ); }, - mousedown: function( event ){ onMouseDown( _this, event ); }, - mouseup: function( event ){ onMouseUp( _this, event ); }, - click: function( event ){ onMouseClick( _this, event ); }, - DOMMouseScroll: function( event ){ onMouseWheelSpin( _this, event ); }, - mousewheel: function( event ){ onMouseWheelSpin( _this, event ); }, - mouseupie: function( event ){ onMouseUpIE( _this, event ); }, - mousemoveie: function( event ){ onMouseMoveIE( _this, event ); }, - mouseupwindow: function( event ){ onMouseUpWindow( _this, event ); }, - mousemove: function( event ){ onMouseMove( _this, event ); }, - touchstart: function( event ){ onTouchStart( _this, event ); }, - touchmove: function( event ){ onTouchMove( _this, event ); }, - touchend: function( event ){ onTouchEnd( _this, event ); }, - keypress: function( event ){ onKeyPress( _this, event ); }, - focus: function( event ){ onFocus( _this, event ); }, - blur: function( event ){ onBlur( _this, event ); }, - tracking: false, - capturing: false, - buttonDown: false, - insideElement: false, - lastPoint: null, - lastMouseDownTime: null, - lastMouseDownPoint: null, - lastPinchDelta: 0 - }; - - }; - - $.MouseTracker.prototype = { - - /** - * Are we currently tracking events on this element. - * @deprecated Just use this.tracking - * @function - * @returns {Boolean} Are we currently tracking events on this element. - */ - isTracking: function () { - return THIS[ this.hash ].tracking; - }, - - /** - * Enable or disable whether or not we are tracking events on this element. - * @function - * @param {Boolean} track True to start tracking, false to stop tracking. - * @returns {OpenSeadragon.MouseTracker} Chainable. - */ - setTracking: function ( track ) { - if ( track ) { - startTracking( this ); - } else { - stopTracking( this ); - } - //chain - return this; - }, - - /** - * Implement or assign implmentation to these handlers during or after - * calling the constructor. - * @function - * @param {OpenSeadragon.MouseTracker} tracker - * A reference to the tracker instance. - * @param {OpenSeadragon.Point} position - * The poistion of the event on the screen. - * @param {Boolean} buttonDown - * True if the left mouse button is currently being pressed and was - * initiated inside the tracked element, otherwise false. - * @param {Boolean} buttonDownAny - * Was the button down anywhere in the screen during the event. - */ - enterHandler: function(){}, - - /** - * Implement or assign implmentation to these handlers during or after - * calling the constructor. - * @function - * @param {OpenSeadragon.MouseTracker} tracker - * A reference to the tracker instance. - * @param {OpenSeadragon.Point} position - * The poistion of the event on the screen. - * @param {Boolean} buttonDown - * True if the left mouse button is currently being pressed and was - * initiated inside the tracked element, otherwise false. - * @param {Boolean} buttonDownAny - * Was the button down anywhere in the screen during the event. - */ - exitHandler: function(){}, - - /** - * Implement or assign implmentation to these handlers during or after - * calling the constructor. - * @function - * @param {OpenSeadragon.MouseTracker} tracker - * A reference to the tracker instance. - * @param {OpenSeadragon.Point} position - * The poistion of the event on the screen. - */ - pressHandler: function(){}, - - /** - * Implement or assign implmentation to these handlers during or after - * calling the constructor. - * @function - * @param {OpenSeadragon.MouseTracker} tracker - * A reference to the tracker instance. - * @param {OpenSeadragon.Point} position - * The poistion of the event on the screen. - * @param {Boolean} buttonDown - * True if the left mouse button is currently being pressed and was - * initiated inside the tracked element, otherwise false. - * @param {Boolean} insideElementRelease - * Was the mouse still inside the tracked element when the button - * was released. - */ - releaseHandler: function(){}, - - /** - * Implement or assign implmentation to these handlers during or after - * calling the constructor. - * @function - * @param {OpenSeadragon.MouseTracker} tracker - * A reference to the tracker instance. - * @param {OpenSeadragon.Point} position - * The poistion of the event on the screen. - * @param {Number} scroll - * The scroll delta for the event. - * @param {Boolean} shift - * Was the shift key being pressed during this event? - */ - scrollHandler: function(){}, - - /** - * Implement or assign implmentation to these handlers during or after - * calling the constructor. - * @function - * @param {OpenSeadragon.MouseTracker} tracker - * A reference to the tracker instance. - * @param {OpenSeadragon.Point} position - * The poistion of the event on the screen. - * @param {Boolean} quick - * True only if the clickDistThreshold and clickDeltaThreshold are - * both pased. Useful for ignoring events. - * @param {Boolean} shift - * Was the shift key being pressed during this event? - */ - clickHandler: function(){}, - - /** - * Implement or assign implmentation to these handlers during or after - * calling the constructor. - * @function - * @param {OpenSeadragon.MouseTracker} tracker - * A reference to the tracker instance. - * @param {OpenSeadragon.Point} position - * The poistion of the event on the screen. - * @param {OpenSeadragon.Point} delta - * The x,y components of the difference between start drag and - * end drag. Usefule for ignoring or weighting the events. - * @param {Boolean} shift - * Was the shift key being pressed during this event? - */ - dragHandler: function(){}, - - /** - * Implement or assign implmentation to these handlers during or after - * calling the constructor. - * @function - * @param {OpenSeadragon.MouseTracker} tracker - * A reference to the tracker instance. - * @param {Number} keyCode - * The key code that was pressed. - * @param {Boolean} shift - * Was the shift key being pressed during this event? - */ - keyHandler: function(){}, - - focusHandler: function(){}, - - blurHandler: function(){} - }; - - /** - * Starts tracking mouse events on this element. - * @private - * @inner - */ - function startTracking( tracker ) { - var events = [ - "mouseover", "mouseout", "mousedown", "mouseup", - "click", - "DOMMouseScroll", "mousewheel", - "touchstart", "touchmove", "touchend", - "keypress", - "focus", "blur" - ], - delegate = THIS[ tracker.hash ], - event, - i; - - if ( !delegate.tracking ) { - for( i = 0; i < events.length; i++ ){ - event = events[ i ]; - $.addEvent( - tracker.element, - event, - delegate[ event ], - false - ); - } - delegate.tracking = true; - ACTIVE[ tracker.hash ] = tracker; - } - } - - /** - * Stops tracking mouse events on this element. - * @private - * @inner - */ - function stopTracking( tracker ) { - var events = [ - "mouseover", "mouseout", "mousedown", "mouseup", - "click", - "DOMMouseScroll", "mousewheel", - "touchstart", "touchmove", "touchend", - "keypress", - "focus", "blur" - ], - delegate = THIS[ tracker.hash ], - event, - i; - - if ( delegate.tracking ) { - for( i = 0; i < events.length; i++ ){ - event = events[ i ]; - $.removeEvent( - tracker.element, - event, - delegate[ event ], - false - ); - } - - releaseMouse( tracker ); - delegate.tracking = false; - delete ACTIVE[ tracker.hash ]; - } - } - - /** - * @private - * @inner - */ - function hasMouse( tracker ) { - return THIS[ tracker.hash ].insideElement; - } - - /** - * Begin capturing mouse events on this element. - * @private - * @inner - */ - function captureMouse( tracker ) { - var delegate = THIS[ tracker.hash ]; - if ( !delegate.capturing ) { - - if ( $.Browser.vendor == $.BROWSERS.IE && $.Browser.version < 9 ) { - $.removeEvent( - tracker.element, - "mouseup", - delegate.mouseup, - false - ); - $.addEvent( - tracker.element, - "mouseup", - delegate.mouseupie, - true - ); - $.addEvent( - tracker.element, - "mousemove", - delegate.mousemoveie, - true - ); - } else { - $.addEvent( - window, - "mouseup", - delegate.mouseupwindow, - true - ); - $.addEvent( - window, - "mousemove", - delegate.mousemove, - true - ); - } - delegate.capturing = true; - } - } - - - /** - * Stop capturing mouse events on this element. - * @private - * @inner - */ - function releaseMouse( tracker ) { - var delegate = THIS[ tracker.hash ]; - if ( delegate.capturing ) { - - if ( $.Browser.vendor == $.BROWSERS.IE && $.Browser.version < 9 ) { - $.removeEvent( - tracker.element, - "mousemove", - delegate.mousemoveie, - true - ); - $.removeEvent( - tracker.element, - "mouseup", - delegate.mouseupie, - true - ); - $.addEvent( - tracker.element, - "mouseup", - delegate.mouseup, - false - ); - } else { - $.removeEvent( - window, - "mousemove", - delegate.mousemove, - true - ); - $.removeEvent( - window, - "mouseup", - delegate.mouseupwindow, - true - ); - } - delegate.capturing = false; - } - } - - - /** - * @private - * @inner - */ - function triggerOthers( tracker, handler, event ) { - var otherHash; - for ( otherHash in ACTIVE ) { - if ( ACTIVE.hasOwnProperty( otherHash ) && tracker.hash != otherHash ) { - handler( ACTIVE[ otherHash ], event ); - } - } - } - - - /** - * @private - * @inner - */ - function onFocus( tracker, event ){ - //console.log( "focus %s", event ); - var propagate; - if ( tracker.focusHandler ) { - propagate = tracker.focusHandler( - tracker, - event - ); - if( propagate === false ){ - $.cancelEvent( event ); - } - } - } - - - /** - * @private - * @inner - */ - function onBlur( tracker, event ){ - //console.log( "blur %s", event ); - var propagate; - if ( tracker.blurHandler ) { - propagate = tracker.blurHandler( - tracker, - event - ); - if( propagate === false ){ - $.cancelEvent( event ); - } - } - } - - - /** - * @private - * @inner - */ - function onKeyPress( tracker, event ){ - //console.log( "keypress %s %s %s %s %s", event.keyCode, event.charCode, event.ctrlKey, event.shiftKey, event.altKey ); - var propagate; - if ( tracker.keyHandler ) { - propagate = tracker.keyHandler( - tracker, - event.keyCode ? event.keyCode : event.charCode, - event.shiftKey - ); - if( !propagate ){ - $.cancelEvent( event ); - } - } - } - - - /** - * @private - * @inner - */ - function onMouseOver( tracker, event ) { - - var delegate = THIS[ tracker.hash ], - propagate; - - event = $.getEvent( event ); - - if ( $.Browser.vendor == $.BROWSERS.IE && - $.Browser.version < 9 && - delegate.capturing && - !isChild( event.srcElement, tracker.element ) ) { - - triggerOthers( tracker, onMouseOver, event ); - } - - var to = event.target ? - event.target : - event.srcElement, - from = event.relatedTarget ? - event.relatedTarget : - event.fromElement; - - if ( !isChild( tracker.element, to ) || - isChild( tracker.element, from ) ) { - return; - } - - delegate.insideElement = true; - - if ( tracker.enterHandler ) { - propagate = tracker.enterHandler( - tracker, - getMouseRelative( event, tracker.element ), - delegate.buttonDown, - IS_BUTTON_DOWN - ); - if( propagate === false ){ - $.cancelEvent( event ); - } - } - } - - - /** - * @private - * @inner - */ - function onMouseOut( tracker, event ) { - var delegate = THIS[ tracker.hash ], - propagate; - - event = $.getEvent( event ); - - if ( $.Browser.vendor == $.BROWSERS.IE && - $.Browser.version < 9 && - delegate.capturing && - !isChild( event.srcElement, tracker.element ) ) { - - triggerOthers( tracker, onMouseOut, event ); - - } - - var from = event.target ? - event.target : - event.srcElement, - to = event.relatedTarget ? - event.relatedTarget : - event.toElement; - - if ( !isChild( tracker.element, from ) || - isChild( tracker.element, to ) ) { - return; - } - - delegate.insideElement = false; - - if ( tracker.exitHandler ) { - propagate = tracker.exitHandler( - tracker, - getMouseRelative( event, tracker.element ), - delegate.buttonDown, - IS_BUTTON_DOWN - ); - - if( propagate === false ){ - $.cancelEvent( event ); - } - } - } - - - /** - * @private - * @inner - */ - function onMouseDown( tracker, event ) { - var delegate = THIS[ tracker.hash ], - propagate; - - event = $.getEvent( event ); - - if ( event.button == 2 ) { - return; - } - - delegate.buttonDown = true; - - delegate.lastPoint = getMouseAbsolute( event ); - delegate.lastMouseDownPoint = delegate.lastPoint; - delegate.lastMouseDownTime = +new Date(); - - if ( tracker.pressHandler ) { - propagate = tracker.pressHandler( - tracker, - getMouseRelative( event, tracker.element ) - ); - if( propagate === false ){ - $.cancelEvent( event ); - } - } - - if ( tracker.pressHandler || tracker.dragHandler ) { - $.cancelEvent( event ); - } - - if ( !( $.Browser.vendor == $.BROWSERS.IE && $.Browser.version < 9 ) || - !IS_CAPTURING ) { - captureMouse( tracker ); - IS_CAPTURING = true; - // reset to empty & add us - CAPTURING = [ tracker ]; - } else if ( $.Browser.vendor == $.BROWSERS.IE && $.Browser.version < 9 ) { - // add us to the list - CAPTURING.push( tracker ); - } - } - - /** - * @private - * @inner - */ - function onTouchStart( tracker, event ) { - var touchA, - touchB; - - if( event.touches.length == 1 && - event.targetTouches.length == 1 && - event.changedTouches.length == 1 ){ - - THIS[ tracker.hash ].lastTouch = event.touches[ 0 ]; - onMouseOver( tracker, event.changedTouches[ 0 ] ); - onMouseDown( tracker, event.touches[ 0 ] ); - } - - if( event.touches.length == 2 ){ - - touchA = getMouseAbsolute( event.touches[ 0 ] ); - touchB = getMouseAbsolute( event.touches[ 1 ] ); - THIS[ tracker.hash ].lastPinchDelta = - Math.abs( touchA.x - touchB.x ) + - Math.abs( touchA.y - touchB.y ); - //$.console.debug("pinch start : "+THIS[ tracker.hash ].lastPinchDelta); - } - - event.preventDefault(); - } - - - /** - * @private - * @inner - */ - function onMouseUp( tracker, event ) { - var delegate = THIS[ tracker.hash ], - //were we inside the tracked element when we were pressed - insideElementPress = delegate.buttonDown, - //are we still inside the tracked element when we released - insideElementRelease = delegate.insideElement, - propagate; - - event = $.getEvent( event ); - - if ( event.button == 2 ) { - return; - } - - delegate.buttonDown = false; - - if ( tracker.releaseHandler ) { - propagate = tracker.releaseHandler( - tracker, - getMouseRelative( event, tracker.element ), - insideElementPress, - insideElementRelease - ); - if( propagate === false ){ - $.cancelEvent( event ); - } - } - - if ( insideElementPress && insideElementRelease ) { - handleMouseClick( tracker, event ); - } - } - - - /** - * @private - * @inner - */ - function onTouchEnd( tracker, event ) { - - if( event.touches.length === 0 && - event.targetTouches.length === 0 && - event.changedTouches.length == 1 ){ - - THIS[ tracker.hash ].lastTouch = null; - onMouseUp( tracker, event.changedTouches[ 0 ] ); - onMouseOut( tracker, event.changedTouches[ 0 ] ); - } - if( event.touches.length + event.changedTouches.length == 2 ){ - THIS[ tracker.hash ].lastPinchDelta = null; - //$.console.debug("pinch end"); - } - event.preventDefault(); - } - - - /** - * Only triggered once by the deepest element that initially received - * the mouse down event. We want to make sure THIS event doesn't bubble. - * Instead, we want to trigger the elements that initially received the - * mouse down event (including this one) only if the mouse is no longer - * inside them. Then, we want to release capture, and emulate a regular - * mouseup on the event that this event was meant for. - * @private - * @inner - */ - function onMouseUpIE( tracker, event ) { - var othertracker, - i; - - event = $.getEvent( event ); - - if ( event.button == 2 ) { - return; - } - - for ( i = 0; i < CAPTURING.length; i++ ) { - othertracker = CAPTURING[ i ]; - if ( !hasMouse( othertracker ) ) { - onMouseUp( othertracker, event ); - } - } - - releaseMouse( tracker ); - IS_CAPTURING = false; - event.srcElement.fireEvent( - "on" + event.type, - document.createEventObject( event ) - ); - - $.stopEvent( event ); - } - - - /** - * Only triggered in W3C browsers by elements within which the mouse was - * initially pressed, since they are now listening to the window for - * mouseup during the capture phase. We shouldn't handle the mouseup - * here if the mouse is still inside this element, since the regular - * mouseup handler will still fire. - * @private - * @inner - */ - function onMouseUpWindow( tracker, event ) { - if ( ! THIS[ tracker.hash ].insideElement ) { - onMouseUp( tracker, event ); - } - releaseMouse( tracker ); - } - - - /** - * @private - * @inner - */ - function onMouseClick( tracker, event ) { - if ( tracker.clickHandler ) { - $.cancelEvent( event ); - } - } - - - /** - * @private - * @inner - */ - function onMouseWheelSpin( tracker, event ) { - var nDelta = 0, - propagate; - - if ( !event ) { // For IE, access the global (window) event object - event = window.event; - } - - if ( event.wheelDelta ) { // IE and Opera - nDelta = event.wheelDelta; - if ( window.opera ) { // Opera has the values reversed - nDelta = -nDelta; - } - } else if (event.detail) { // Mozilla FireFox - nDelta = -event.detail; - } - //The nDelta variable is gated to provide smooth z-index scrolling - //since the mouse wheel allows for substantial deltas meant for rapid - //y-index scrolling. - nDelta = nDelta > 0 ? 1 : -1; - - if ( tracker.scrollHandler ) { - propagate = tracker.scrollHandler( - tracker, - getMouseRelative( event, tracker.element ), - nDelta, - event.shiftKey - ); - if( propagate === false ){ - $.cancelEvent( event ); - } - } - } - - - /** - * @private - * @inner - */ - function handleMouseClick( tracker, event ) { - var delegate = THIS[ tracker.hash ], - propagate; - - event = $.getEvent( event ); - - if ( event.button == 2 ) { - return; - } - - var time = +new Date() - delegate.lastMouseDownTime, - point = getMouseAbsolute( event ), - distance = delegate.lastMouseDownPoint.distanceTo( point ), - quick = time <= tracker.clickTimeThreshold && - distance <= tracker.clickDistThreshold; - - if ( tracker.clickHandler ) { - propagate = tracker.clickHandler( - tracker, - getMouseRelative( event, tracker.element ), - quick, - event.shiftKey - ); - if( propagate === false ){ - $.cancelEvent( event ); - } - } - } - - - /** - * @private - * @inner - */ - function onMouseMove( tracker, event ) { - var delegate = THIS[ tracker.hash ], - delta, - propagate, - point; - - event = $.getEvent( event ); - point = getMouseAbsolute( event ); - delta = point.minus( delegate.lastPoint ); - - delegate.lastPoint = point; - - if ( tracker.dragHandler ) { - propagate = tracker.dragHandler( - tracker, - getMouseRelative( event, tracker.element ), - delta, - event.shiftKey - ); - if( propagate === false ){ - $.cancelEvent( event ); - } - } - } - - - /** - * @private - * @inner - */ - function onTouchMove( tracker, event ) { - var touchA, - touchB, - pinchDelta; - - if( event.touches.length === 1 && - event.targetTouches.length === 1 && - event.changedTouches.length === 1 && - THIS[ tracker.hash ].lastTouch === event.touches[ 0 ]){ - - onMouseMove( tracker, event.touches[ 0 ] ); - - } else if ( event.touches.length === 2 ){ - - touchA = getMouseAbsolute( event.touches[ 0 ] ); - touchB = getMouseAbsolute( event.touches[ 1 ] ); - pinchDelta = - Math.abs( touchA.x - touchB.x ) + - Math.abs( touchA.y - touchB.y ); - - //TODO: make the 75px pinch threshold configurable - if( Math.abs( THIS[ tracker.hash ].lastPinchDelta - pinchDelta ) > 75 ){ - //$.console.debug( "pinch delta : " + pinchDelta + " | previous : " + THIS[ tracker.hash ].lastPinchDelta); - - onMouseWheelSpin( tracker, { - shift: false, - pageX: ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX ) / 2, - pageY: ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY ) / 2, - detail:( - THIS[ tracker.hash ].lastPinchDelta > pinchDelta - ) ? 1 : -1 - }); - - THIS[ tracker.hash ].lastPinchDelta = pinchDelta; - } - } - event.preventDefault(); - } - - /** - * Only triggered once by the deepest element that initially received - * the mouse down event. Since no other element has captured the mouse, - * we want to trigger the elements that initially received the mouse - * down event (including this one). The the param tracker isn't used - * but for consistency with the other event handlers we include it. - * @private - * @inner - */ - function onMouseMoveIE( tracker, event ) { - var i; - for ( i = 0; i < CAPTURING.length; i++ ) { - onMouseMove( CAPTURING[ i ], event ); - } - - $.stopEvent( event ); - } - - /** - * @private - * @inner - */ - function getMouseAbsolute( event ) { - return $.getMousePosition( event ); - } - - /** - * @private - * @inner - */ - function getMouseRelative( event, element ) { - var mouse = $.getMousePosition( event ), - offset = $.getElementPosition( element ); - - return mouse.minus( offset ); - } - - /** - * @private - * @inner - * Returns true if elementB is a child node of elementA, or if they're equal. - */ - function isChild( elementA, elementB ) { - var body = document.body; - while ( elementB && elementA != elementB && body != elementB ) { - try { - elementB = elementB.parentNode; - } catch (e) { - return false; - } - } - return elementA == elementB; - } - - /** - * @private - * @inner - */ - function onGlobalMouseDown() { - IS_BUTTON_DOWN = true; - } - - /** - * @private - * @inner - */ - function onGlobalMouseUp() { - IS_BUTTON_DOWN = false; - } - - - (function () { - if ( $.Browser.vendor == $.BROWSERS.IE && $.Browser.version < 9 ) { - $.addEvent( document, "mousedown", onGlobalMouseDown, false ); - $.addEvent( document, "mouseup", onGlobalMouseUp, false ); - } else { - $.addEvent( window, "mousedown", onGlobalMouseDown, true ); - $.addEvent( window, "mouseup", onGlobalMouseUp, true ); - } - })(); - -}( OpenSeadragon )); -/*globals OpenSeadragon */ - -(function( $ ){ - -/** - * 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 - * @static - */ -$.ControlAnchor = { - NONE: 0, - TOP_LEFT: 1, - TOP_RIGHT: 2, - BOTTOM_RIGHT: 3, - BOTTOM_LEFT: 4 -}; - -/** - * 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. - * @class - * @param {Element} element - the contol element to be anchored in the container. - * @param {OpenSeadragon.ControlAnchor} anchor - the location to anchor at. - * @param {Element} container - the element to control will be anchored too. - * - * @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. - * @property {Element} container - the element within with the control is - * positioned. - * @property {Element} wrapper - a nuetral element surrounding the control - * element. - */ -$.Control = function ( element, anchor, container ) { - this.element = element; - this.anchor = anchor; - this.container = container; - this.wrapper = $.makeNeutralElement( "span" ); - this.wrapper.style.display = "inline-block"; - this.wrapper.appendChild( this.element ); - - if ( this.anchor == $.ControlAnchor.NONE ) { - // IE6 fix - this.wrapper.style.width = this.wrapper.style.height = "100%"; - } - - if ( this.anchor == $.ControlAnchor.TOP_RIGHT || - this.anchor == $.ControlAnchor.BOTTOM_RIGHT ) { - this.container.insertBefore( - this.wrapper, - this.container.firstChild - ); - } else { - this.container.appendChild( this.wrapper ); - } -}; - -$.Control.prototype = { - - /** - * Removes the control from the container. - * @function - */ - destroy: function() { - this.wrapper.removeChild( this.element ); - this.container.removeChild( this.wrapper ); - }, - - /** - * Determines if the control is currently visible. - * @function - * @return {Boolean} true if currenly visible, false otherwise. - */ - isVisible: function() { - return this.wrapper.style.display != "none"; - }, - - /** - * Toggles the visibility of the control. - * @function - * @param {Boolean} visible - true to make visible, false to hide. - */ - setVisible: function( visible ) { - this.wrapper.style.display = visible ? - "inline-block" : - "none"; - }, - - /** - * Sets the opacity level for the control. - * @function - * @param {Number} opactiy - a value between 1 and 0 inclusively. - */ - setOpacity: function( opacity ) { - if ( this.element[ $.SIGNAL ] && $.Browser.vendor == $.BROWSERS.IE ) { - $.setElementOpacity( this.element, opacity, true ); - } else { - $.setElementOpacity( this.wrapper, opacity, true ); - } - } -}; - -}( OpenSeadragon )); -/*globals OpenSeadragon */ - -(function( $ ){ - - //id hash for private properties; - var THIS = {}; - - /** - * @class - */ - $.ControlDock = function( options ){ - var layouts = [ 'topleft', 'topright', 'bottomright', 'bottomleft'], - layout, - i; - - $.extend( true, this, { - id: 'controldock-'+(+new Date())+'-'+Math.floor(Math.random()*1000000), - container: $.makeNeutralElement('form'), - controls: [] - }, options ); - - if( this.element ){ - this.element = $.getElement( this.element ); - this.element.appendChild( this.container ); - this.element.style.position = 'relative'; - this.container.style.width = '100%'; - this.container.style.height = '100%'; - } - - for( i = 0; i < layouts.length; i++ ){ - layout = layouts[ i ]; - this.controls[ layout ] = $.makeNeutralElement( "div" ); - this.controls[ layout ].style.position = 'absolute'; - if ( layout.match( 'left' ) ){ - this.controls[ layout ].style.left = '0px'; - } - if ( layout.match( 'right' ) ){ - this.controls[ layout ].style.right = '0px'; - } - if ( layout.match( 'top' ) ){ - this.controls[ layout ].style.top = '0px'; - } - if ( layout.match( 'bottom' ) ){ - this.controls[ layout ].style.bottom = '0px'; - } - } - - this.container.appendChild( this.controls.topleft ); - this.container.appendChild( this.controls.topright ); - this.container.appendChild( this.controls.bottomright ); - this.container.appendChild( this.controls.bottomleft ); - }; - - $.ControlDock.prototype = { - - /** - * @function - */ - addControl: function ( element, anchor ) { - element = $.getElement( element ); - var div = null; - - if ( getControlIndex( this, element ) >= 0 ) { - return; // they're trying to add a duplicate control - } - - switch ( anchor ) { - case $.ControlAnchor.TOP_RIGHT: - div = this.controls.topright; - element.style.position = "relative"; - element.style.paddingRight = "0px"; - element.style.paddingTop = "0px"; - break; - case $.ControlAnchor.BOTTOM_RIGHT: - div = this.controls.bottomright; - element.style.position = "relative"; - element.style.paddingRight = "0px"; - element.style.paddingBottom = "0px"; - break; - case $.ControlAnchor.BOTTOM_LEFT: - div = this.controls.bottomleft; - element.style.position = "relative"; - element.style.paddingLeft = "0px"; - element.style.paddingBottom = "0px"; - break; - case $.ControlAnchor.TOP_LEFT: - div = this.controls.topleft; - element.style.position = "relative"; - element.style.paddingLeft = "0px"; - element.style.paddingTop = "0px"; - break; - default: - case $.ControlAnchor.NONE: - div = this.container; - element.style.margin = "0px"; - element.style.padding = "0px"; - break; - } - - this.controls.push( - new $.Control( element, anchor, div ) - ); - element.style.display = "inline-block"; - }, - - - /** - * @function - * @return {OpenSeadragon.ControlDock} Chainable. - */ - removeControl: function ( element ) { - element = $.getElement( element ); - var i = getControlIndex( this, element ); - - if ( i >= 0 ) { - this.controls[ i ].destroy(); - this.controls.splice( i, 1 ); - } - - return this; - }, - - /** - * @function - * @return {OpenSeadragon.ControlDock} Chainable. - */ - clearControls: function () { - while ( this.controls.length > 0 ) { - this.controls.pop().destroy(); - } - - return this; - }, - - - /** - * @function - * @return {Boolean} - */ - areControlsEnabled: function () { - var i; - - for ( i = this.controls.length - 1; i >= 0; i-- ) { - if ( this.controls[ i ].isVisible() ) { - return true; - } - } - - return false; - }, - - - /** - * @function - * @return {OpenSeadragon.ControlDock} Chainable. - */ - setControlsEnabled: function( enabled ) { - var i; - - for ( i = this.controls.length - 1; i >= 0; i-- ) { - this.controls[ i ].setVisible( enabled ); - } - - return this; - } - - }; - - - /////////////////////////////////////////////////////////////////////////////// - // Utility methods - /////////////////////////////////////////////////////////////////////////////// - function getControlIndex( dock, element ) { - var controls = dock.controls, - i; - - for ( i = controls.length - 1; i >= 0; i-- ) { - if ( controls[ i ].element == element ) { - return i; - } - } - - return -1; - } - -}( OpenSeadragon ));/*globals OpenSeadragon */ - -(function( $ ){ - -// dictionary from hash to private properties -var THIS = {}, -// We keep a list of viewers so we can 'wake-up' each viewer on -// a page after toggling between fullpage modes - VIEWERS = {}; - -/** - * - * The main point of entry into creating a zoomable image on the page. - * - * We have provided an idiomatic javascript constructor which takes - * a single object, but still support the legacy positional arguments. - * - * The options below are given in order that they appeared in the constructor - * as arguments and we translate a positional call into an idiomatic call. - * - * @class - * @extends OpenSeadragon.EventHandler - * @extends OpenSeadragon.ControlDock - * @param {Object} options - * @param {String} options.element Id of Element to attach to, - * @param {String} options.xmlPath Xpath ( TODO: not sure! ), - * @param {String} options.prefixUrl Url used to prepend to paths, eg button - * images, etc. - * @param {Seadragon.Controls[]} options.controls Array of Seadragon.Controls, - * @param {Seadragon.Overlays[]} options.overlays Array of Seadragon.Overlays, - * @param {Seadragon.Controls[]} options.overlayControls An Array of ( TODO: - * not sure! ) - * - **/ -$.Viewer = function( options ) { - - var args = arguments, - _this = this, - i; - - - //backward compatibility for positional args while prefering more - //idiomatic javascript options object as the only argument - if( !$.isPlainObject( options ) ){ - options = { - id: args[ 0 ], - xmlPath: args.length > 1 ? args[ 1 ] : undefined, - prefixUrl: args.length > 2 ? args[ 2 ] : undefined, - controls: args.length > 3 ? args[ 3 ] : undefined, - overlays: args.length > 4 ? args[ 4 ] : undefined, - overlayControls: args.length > 5 ? args[ 5 ] : undefined - }; - } - - //options.config and the general config argument are deprecated - //in favor of the more direct specification of optional settings - //being pass directly on the options object - if ( options.config ){ - $.extend( true, options, options.config ); - delete options.config; - } - - //Public properties - //Allow the options object to override global defaults - $.extend( true, this, { - - //internal state and dom identifiers - id: options.id, - hash: options.id, - - //dom nodes - element: null, - canvas: null, - container: null, - - //TODO: not sure how to best describe these - overlays: [], - overlayControls:[], - - //private state properties - previousBody: [], - - //This was originally initialized in the constructor and so could never - //have anything in it. now it can because we allow it to be specified - //in the options and is only empty by default if not specified. Also - //this array was returned from get_controls which I find confusing - //since this object has a controls property which is treated in other - //functions like clearControls. I'm removing the accessors. - customControls: [], - - //These are originally not part options but declared as members - //in initialize. Its still considered idiomatic to put them here - source: null, - drawer: null, - drawers: [], - viewport: null, - navigator: null, - - //A collection viewport is a seperate viewport used to provide - //simultanious rendering of sets of tiless - collectionViewport: null, - collectionDrawer: null, - - //UI image resources - //TODO: rename navImages to uiImages - navImages: null, - - //interface button controls - buttons: null, - - //TODO: this is defunct so safely remove it - profiler: null - - }, $.DEFAULT_SETTINGS, options ); - - //Private state properties - THIS[ this.hash ] = { - "fsBoundsDelta": new $.Point( 1, 1 ), - "prevContainerSize": null, - "lastOpenStartTime": 0, - "lastOpenEndTime": 0, - "animating": false, - "forceRedraw": false, - "mouseInside": false, - "group": null, - // whether we should be continuously zooming - "zooming": false, - // how much we should be continuously zooming by - "zoomFactor": null, - "lastZoomTime": null, - // did we decide this viewer has a sequence of tile sources - "sequenced": false, - "sequence": 0 - }; - - //Inherit some behaviors and properties - $.EventHandler.call( this ); - $.ControlDock.call( this, options ); - - //Deal with tile sources - var initialTileSource, - customTileSource; - - if ( this.xmlPath ){ - //Deprecated option. Now it is preferred to use the tileSources option - this.tileSources = [ this.xmlPath ]; - } - - if ( this.tileSources ){ - // tileSources is a complex option... - // - // It can be a string, object, or an array of any of strings and objects. - // At this point we only care about if it is an Array or not. - // - if( $.isArray( this.tileSources ) ){ - - //must be a sequence of tileSource since the first item - //is a legacy tile source - if( this.tileSources.length > 1 ){ - THIS[ this.hash ].sequenced = true; - } - initialTileSource = this.tileSources[ 0 ]; - } else { - initialTileSource = this.tileSources; - } - - this.openTileSource( initialTileSource ); - } - - this.element = this.element || document.getElementById( this.id ); - this.canvas = $.makeNeutralElement( "div" ); - - this.canvas.className = "openseadragon-canvas"; - (function( canvas ){ - canvas.width = "100%"; - canvas.height = "100%"; - canvas.overflow = "hidden"; - canvas.position = "absolute"; - canvas.top = "0px"; - canvas.left = "0px"; - }( this.canvas.style )); - - //the container is created through applying the ControlDock constructor above - this.container.className = "openseadragon-container"; - (function( container ){ - container.width = "100%"; - container.height = "100%"; - container.position = "relative"; - container.overflow = "hidden"; - container.left = "0px"; - container.top = "0px"; - container.textAlign = "left"; // needed to protect against - }( this.container.style )); - - this.container.insertBefore( this.canvas, this.container.firstChild ); - this.element.appendChild( this.container ); - - //Used for toggling between fullscreen and default container size - //TODO: these can be closure private and shared across Viewer - // instances. - this.bodyWidth = document.body.style.width; - this.bodyHeight = document.body.style.height; - this.bodyOverflow = document.body.style.overflow; - this.docOverflow = document.documentElement.style.overflow; - - this.innerTracker = new $.MouseTracker({ - element: this.canvas, - clickTimeThreshold: this.clickTimeThreshold, - clickDistThreshold: this.clickDistThreshold, - clickHandler: $.delegate( this, onCanvasClick ), - dragHandler: $.delegate( this, onCanvasDrag ), - releaseHandler: $.delegate( this, onCanvasRelease ), - scrollHandler: $.delegate( this, onCanvasScroll ) - }).setTracking( this.mouseNavEnabled ? true : false ); // default state - - this.outerTracker = new $.MouseTracker({ - element: this.container, - clickTimeThreshold: this.clickTimeThreshold, - clickDistThreshold: this.clickDistThreshold, - enterHandler: $.delegate( this, onContainerEnter ), - exitHandler: $.delegate( this, onContainerExit ), - releaseHandler: $.delegate( this, onContainerRelease ) - }).setTracking( this.mouseNavEnabled ? true : false ); // always tracking - - if( this.toolbar ){ - this.toolbar = new $.ControlDock({ element: this.toolbar }); - } - - this.bindStandardControls(); - this.bindSequenceControls(); - - for ( i = 0; i < this.customControls.length; i++ ) { - this.addControl( - this.customControls[ i ].id, - this.customControls[ i ].anchor - ); - } - - window.setTimeout( function(){ - beginControlsAutoHide( _this ); - }, 1 ); // initial fade out - -}; - -$.extend( $.Viewer.prototype, $.EventHandler.prototype, $.ControlDock.prototype, { - - - /** - * @function - * @name OpenSeadragon.Viewer.prototype.isOpen - * @return {Boolean} - */ - isOpen: function () { - return !!this.source; - }, - - /** - * If the string is xml is simply parsed and opened, otherwise the string - * is treated as an URL and an xml document is requested via ajax, parsed - * and then opened in the viewer. - * @function - * @name OpenSeadragon.Viewer.prototype.openDzi - * @param {String} dzi and xml string or the url to a DZI xml document. - * @return {OpenSeadragon.Viewer} Chainable. - * - * @deprecated - use 'open' instead. - */ - openDzi: function ( dzi ) { - var _this = this; - $.createFromDZI( - dzi, - function( source ){ - _this.open( source ); - }, - this.tileHost - ); - return this; - }, - - /** - * tileSources is a complex option... - * - * It can be a string, object, function, or an array of any of these: - * - * - A String implies a url used to determine the tileSource implementation - * based on the file extension of url. JSONP is implied by *.js, - * otherwise the url is retrieved as text and the resulting text is - * introspected to determine if its json, xml, or text and parsed. - * - An Object implies an inline configuration which has a single - * property sufficient for being able to determine tileSource - * implementation. If the object has a property which is a function - * named 'getTileUrl', it is treated as a custom TileSource. - * @function - * @name OpenSeadragon.Viewer.prototype.openTileSource - * @return {OpenSeadragon.Viewer} Chainable. - */ - openTileSource: function ( tileSource ) { - var _this = this, - customTileSource, - readySource, - $TileSource, - options; - - setTimeout(function(){ - if ( $.type( tileSource ) == 'string') { - //TODO: We cant assume a string implies a dzi since all - //complete TileSource implementations should have a getInfo - //which allows them to be configured via AJAX. Im not sure - //if its better to use file extension or url pattern, or to - //inspect the resulting info object. - tileSource = new $.TileSource( tileSource, function( readySource ){ - _this.open( readySource ); - }); - - } else if ( $.isPlainObject( tileSource ) ){ - if( $.isFunction( tileSource.getTileUrl ) ){ - //Custom tile source - customTileSource = new $.TileSource(tileSource); - customTileSource.getTileUrl = tileSource.getTileUrl; - _this.open( customTileSource ); - } else { - //inline configuration - $TileSource = $.TileSource.determineType( _this, tileSource ); - options = $TileSource.prototype.configure.apply( _this, [ tileSource ]); - readySource = new $TileSource( options ); - _this.open( readySource ); - } - } else { - //can assume it's already a tile source implementation - _this.open( tileSource ); - } - }, 1); - - return this; - }, - - /** - * @function - * @name OpenSeadragon.Viewer.prototype.open - * @return {OpenSeadragon.Viewer} Chainable. - */ - open: function( source ) { - var _this = this, - overlay, - i; - - if ( this.source ) { - this.close( ); - } - - // to ignore earlier opens - THIS[ this.hash ].lastOpenStartTime = +new Date(); - - window.setTimeout( function () { - if ( THIS[ _this.hash ].lastOpenStartTime > THIS[ _this.hash ].lastOpenEndTime ) { - THIS[ _this.hash ].setMessage( $.getString( "Messages.Loading" ) ); - } - }, 2000); - - THIS[ this.hash ].lastOpenEndTime = +new Date(); - this.canvas.innerHTML = ""; - THIS[ this.hash ].prevContainerSize = $.getElementSize( this.container ); - - - if( this.collectionMode ){ - this.source = new $.TileSourceCollection({ - rows: this.collectionRows, - layout: this.collectionLayout, - tileSize: this.collectionTileSize, - tileSources: this.tileSources, - tileMargin: this.collectionTileMargin - }); - this.viewport = this.viewport ? this.viewport : new $.Viewport({ - collectionMode: true, - collectionTileSource: this.source, - containerSize: THIS[ this.hash ].prevContainerSize, - contentSize: this.source.dimensions, - springStiffness: this.springStiffness, - animationTime: this.animationTime, - showNavigator: false, - minZoomImageRatio: 1, - maxZoomPixelRatio: 1, - viewer: this //, - //TODO: figure out how to support these in a way that makes sense - //minZoomLevel: this.minZoomLevel, - //maxZoomLevel: this.maxZoomLevel - }); - }else{ - if( source ){ - this.source = source; - } - this.viewport = this.viewport ? this.viewport : new $.Viewport({ - containerSize: THIS[ this.hash ].prevContainerSize, - contentSize: this.source.dimensions, - springStiffness: this.springStiffness, - animationTime: this.animationTime, - minZoomImageRatio: this.minZoomImageRatio, - maxZoomPixelRatio: this.maxZoomPixelRatio, - visibilityRatio: this.visibilityRatio, - wrapHorizontal: this.wrapHorizontal, - wrapVertical: this.wrapVertical, - defaultZoomLevel: this.defaultZoomLevel, - minZoomLevel: this.minZoomLevel, - maxZoomLevel: this.maxZoomLevel, - viewer: this - }); - } - - if( this.preserveVewport ){ - this.viewport.resetContentSize( this.source.dimensions ); - } - - this.source.overlays = this.source.overlays || []; - - this.drawer = new $.Drawer({ - source: this.source, - viewport: this.viewport, - element: this.canvas, - overlays: [].concat( this.overlays ).concat( this.source.overlays ), - maxImageCacheCount: this.maxImageCacheCount, - imageLoaderLimit: this.imageLoaderLimit, - minZoomImageRatio: this.minZoomImageRatio, - wrapHorizontal: this.wrapHorizontal, - wrapVertical: this.wrapVertical, - immediateRender: this.immediateRender, - blendTime: this.blendTime, - alwaysBlend: this.alwaysBlend, - minPixelRatio: this.collectionMode ? 0 : this.minPixelRatio, - timeout: this.timeout, - debugMode: this.debugMode, - debugGridColor: this.debugGridColor - }); - - //Instantiate a navigator if configured - if ( this.showNavigator && ! this.navigator && !this.collectionMode ){ - this.navigator = new $.Navigator({ - id: this.navigatorElement, - position: this.navigatorPosition, - sizeRatio: this.navigatorSizeRatio, - height: this.navigatorHeight, - width: this.navigatorWidth, - tileSources: this.tileSources, - tileHost: this.tileHost, - prefixUrl: this.prefixUrl, - overlays: this.overlays, - viewer: this - }); - } - - //Instantiate a referencestrip if configured - if ( this.showReferenceStrip && ! this.referenceStrip ){ - this.referenceStrip = new $.ReferenceStrip({ - id: this.referenceStripElement, - position: this.referenceStripPosition, - sizeRatio: this.referenceStripSizeRatio, - scroll: this.referenceStripScroll, - height: this.referenceStripHeight, - width: this.referenceStripWidth, - tileSources: this.tileSources, - tileHost: this.tileHost, - prefixUrl: this.prefixUrl, - overlays: this.overlays, - viewer: this - }); - } - - //this.profiler = new $.Profiler(); - - THIS[ this.hash ].animating = false; - THIS[ this.hash ].forceRedraw = true; - scheduleUpdate( this, updateMulti ); - - //Assuming you had programatically created a bunch of overlays - //and added them via configuration - for ( i = 0; i < this.overlayControls.length; i++ ) { - - overlay = this.overlayControls[ i ]; - - if ( overlay.point ) { - - this.drawer.addOverlay( - overlay.id, - new $.Point( - overlay.point.X, - overlay.point.Y - ), - $.OverlayPlacement.TOP_LEFT - ); - - } else { - - this.drawer.addOverlay( - overlay.id, - new $.Rect( - overlay.rect.Point.X, - overlay.rect.Point.Y, - overlay.rect.Width, - overlay.rect.Height - ), - overlay.placement - ); - - } - } - VIEWERS[ this.hash ] = this; - - if( this.navigator ){ - this.navigator.open( source ); - } - - this.raiseEvent( 'open', { source: source, viewer: this } ); - - return this; - }, - - /** - * @function - * @name OpenSeadragon.Viewer.prototype.close - * @return {OpenSeadragon.Viewer} Chainable. - */ - close: function ( ) { - - if( this.drawer ){ - this.drawer.clearOverlays(); - } - - this.source = null; - this.drawer = null; - - this.viewport = this.preserveViewport ? this.viewport : null; - //this.profiler = null; - this.canvas.innerHTML = ""; - - VIEWERS[ this.hash ] = null; - delete VIEWERS[ this.hash ]; - - this.raiseEvent( 'close', { viewer: this } ); - - return this; - }, - - - /** - * @function - * @name OpenSeadragon.Viewer.prototype.isMouseNavEnabled - * @return {Boolean} - */ - isMouseNavEnabled: function () { - return this.innerTracker.isTracking(); - }, - - /** - * @function - * @name OpenSeadragon.Viewer.prototype.setMouseNavEnabled - * @return {OpenSeadragon.Viewer} Chainable. - */ - setMouseNavEnabled: function( enabled ){ - this.innerTracker.setTracking( enabled ); - this.raiseEvent( 'mouse-enabled', { enabled: enabled, viewer: this } ); - return this; - }, - - - /** - * @function - * @name OpenSeadragon.Viewer.prototype.areControlsEnabled - * @return {Boolean} - */ - areControlsEnabled: function () { - var enabled = this.controls.length, - i; - for( i = 0; i < this.controls.length; i++ ){ - enabled = enabled && this.controls[ i ].isVisibile(); - } - return enabled; - }, - - - /** - * @function - * @name OpenSeadragon.Viewer.prototype.setDashboardEnabled - * @return {OpenSeadragon.Viewer} Chainable. - */ - setControlsEnabled: function( enabled ) { - if( enabled ){ - abortControlsAutoHide( this ); - } else { - beginControlsAutoHide( this ); - } - this.raiseEvent( 'controls-enabled', { enabled: enabled, viewer: this } ); - return this; - }, - - - /** - * @function - * @name OpenSeadragon.Viewer.prototype.isFullPage - * @return {Boolean} - */ - isFullPage: function () { - return this.element.parentNode == document.body; - }, - - - /** - * Toggle full page mode. - * @function - * @name OpenSeadragon.Viewer.prototype.setFullPage - * @param {Boolean} fullPage - * If true, enter full page mode. If false, exit full page mode. - * @return {OpenSeadragon.Viewer} Chainable. - */ - setFullPage: function( fullPage ) { - - var body = document.body, - bodyStyle = body.style, - docStyle = document.documentElement.style, - containerStyle = this.element.style, - canvasStyle = this.canvas.style, - oldBounds, - newBounds, - viewer, - hash, - nodes, - i; - - //dont bother modifying the DOM if we are already in full page mode. - if ( fullPage == this.isFullPage() ) { - return; - } - - if ( fullPage ) { - - this.bodyOverflow = bodyStyle.overflow; - this.docOverflow = docStyle.overflow; - bodyStyle.overflow = "hidden"; - docStyle.overflow = "hidden"; - - this.bodyWidth = bodyStyle.width; - this.bodyHeight = bodyStyle.height; - bodyStyle.width = "100%"; - bodyStyle.height = "100%"; - - //canvasStyle.backgroundColor = "black"; - //canvasStyle.color = "white"; - - //containerStyle.position = "fixed"; - - //when entering full screen on the ipad it wasnt sufficient to leave - //the body intact as only only the top half of the screen would - //respond to touch events on the canvas, while the bottom half treated - //them as touch events on the document body. Thus we remove and store - //the bodies elements and replace them when we leave full screen. - this.previousBody = []; - THIS[ this.hash ].prevElementParent = this.element.parentNode; - THIS[ this.hash ].prevNextSibling = this.element.prevNextSibling; - THIS[ this.hash ].prevElementSize = $.getElementSize( this.element ); - nodes = body.childNodes.length; - for ( i = 0; i < nodes; i ++ ){ - this.previousBody.push( body.childNodes[ 0 ] ); - body.removeChild( body.childNodes[ 0 ] ); - } - - //If we've got a toolbar, we need to enable the user to use css to - //preserve it in fullpage mode - if( this.toolbar && this.toolbar.element ){ - //save a reference to the parent so we can put it back - //in the long run we need a better strategy - this.toolbar.parentNode = this.toolbar.element.parentNode; - this.toolbar.nextSibling = this.toolbar.element.nextSibling; - body.appendChild( this.toolbar.element ); - - //Make sure the user has some ability to style the toolbar based - //on the mode - this.toolbar.element.setAttribute( - 'class', - this.toolbar.element.className +" fullpage" - ); - //this.toolbar.element.style.position = 'fixed'; - - //this.container.style.top = $.getElementSize( - // this.toolbar.element - //).y + 'px'; - } - body.appendChild( this.element ); - if( this.toolbar && this.toolbar.element ){ - this.element.style.height = ( - $.getWindowSize().y - $.getElementSize( this.toolbar.element ).y - ) + 'px'; - }else{ - this.element.style.height = $.getWindowSize().y + 'px'; - } - this.element.style.width = $.getWindowSize().x + 'px'; - - // mouse will be inside container now - $.delegate( this, onContainerEnter )(); - - - } else { - - bodyStyle.overflow = this.bodyOverflow; - docStyle.overflow = this.docOverflow; - - bodyStyle.width = this.bodyWidth; - bodyStyle.height = this.bodyHeight; - - canvasStyle.backgroundColor = ""; - canvasStyle.color = ""; - - //containerStyle.position = "relative"; - //containerStyle.zIndex = ""; - - body.removeChild( this.element ); - nodes = this.previousBody.length; - for ( i = 0; i < nodes; i++ ){ - body.appendChild( this.previousBody.shift() ); - } - THIS[ this.hash ].prevElementParent.insertBefore( - this.element, - THIS[ this.hash ].prevNextSibling - ); - - //If we've got a toolbar, we need to enable the user to use css to - //reset it to its original state - if( this.toolbar && this.toolbar.element ){ - body.removeChild( this.toolbar.element ); - - //Make sure the user has some ability to style the toolbar based - //on the mode - this.toolbar.element.setAttribute( - 'class', - this.toolbar.element.className.replace('fullpage','') - ); - //this.toolbar.element.style.position = 'relative'; - this.toolbar.parentNode.insertBefore( - this.toolbar.element, - this.toolbar.nextSibling - ); - delete this.toolbar.parentNode; - delete this.toolbar.nextSibling; - - //this.container.style.top = 'auto'; - } - - this.element.style.height = THIS[ this.hash ].prevElementSize.y + 'px'; - this.element.style.width = THIS[ this.hash ].prevElementSize.x + 'px'; - - // mouse will likely be outside now - $.delegate( this, onContainerExit )(); - - } - this.raiseEvent( 'fullpage', { fullpage: fullPage, viewer: this } ); - - if ( this.viewport ) { - oldBounds = this.viewport.getBounds(); - this.viewport.resize( THIS[ this.hash ].prevContainerSize ); - newBounds = this.viewport.getBounds(); - - if ( fullPage ) { - THIS[ this.hash ].fsBoundsDelta = new $.Point( - newBounds.width / oldBounds.width, - newBounds.height / oldBounds.height - ); - } else { - this.viewport.update(); - this.viewport.zoomBy( - Math.max( - THIS[ this.hash ].fsBoundsDelta.x, - THIS[ this.hash ].fsBoundsDelta.y - ), - null, - true - ); - //Ensures that if multiple viewers are on a page, the viewers that - //were hidden during fullpage are 'reopened' - for( hash in VIEWERS ){ - viewer = VIEWERS[ hash ]; - if( viewer !== this && viewer != this.navigator ){ - viewer.open( viewer.source ); - if( viewer.navigator ){ - viewer.navigator.open( viewer.source ); - } - } - } - } - - THIS[ this.hash ].forceRedraw = true; - updateOnce( this ); - - } - return this; - }, - - - /** - * @function - * @name OpenSeadragon.Viewer.prototype.isVisible - * @return {Boolean} - */ - isVisible: function () { - return this.container.style.visibility != "hidden"; - }, - - - /** - * @function - * @name OpenSeadragon.Viewer.prototype.setVisible - * @return {OpenSeadragon.Viewer} Chainable. - */ - setVisible: function( visible ){ - this.container.style.visibility = visible ? "" : "hidden"; - this.raiseEvent( 'visible', { visible: visible, viewer: this } ); - return this; - }, - - - /** - * @function - * @name OpenSeadragon.Viewer.prototype.bindSequenceControls - * @return {OpenSeadragon.Viewer} Chainable. - */ - bindSequenceControls: function(){ - - ////////////////////////////////////////////////////////////////////////// - // Image Sequence Controls - ////////////////////////////////////////////////////////////////////////// - var onFocusHandler = $.delegate( this, onFocus ), - onBlurHandler = $.delegate( this, onBlur ), - onNextHandler = $.delegate( this, onNext ), - onPreviousHandler = $.delegate( this, onPrevious ), - navImages = this.navImages, - buttons = [], - useGroup = true ; - - if( this.showSequenceControl && THIS[ this.hash ].sequenced ){ - - if( this.previousButton || this.nextButton ){ - //if we are binding to custom buttons then layout and - //grouping is the responsibility of the page author - useGroup = false; - } - - this.previousButton = new $.Button({ - element: this.previousButton ? $.getElement( this.previousButton ) : null, - clickTimeThreshold: this.clickTimeThreshold, - clickDistThreshold: this.clickDistThreshold, - tooltip: $.getString( "Tooltips.PreviousPage" ), - srcRest: resolveUrl( this.prefixUrl, navImages.previous.REST ), - srcGroup: resolveUrl( this.prefixUrl, navImages.previous.GROUP ), - srcHover: resolveUrl( this.prefixUrl, navImages.previous.HOVER ), - srcDown: resolveUrl( this.prefixUrl, navImages.previous.DOWN ), - onRelease: onPreviousHandler, - onFocus: onFocusHandler, - onBlur: onBlurHandler - }); - - this.nextButton = new $.Button({ - element: this.nextButton ? $.getElement( this.nextButton ) : null, - clickTimeThreshold: this.clickTimeThreshold, - clickDistThreshold: this.clickDistThreshold, - tooltip: $.getString( "Tooltips.NextPage" ), - srcRest: resolveUrl( this.prefixUrl, navImages.next.REST ), - srcGroup: resolveUrl( this.prefixUrl, navImages.next.GROUP ), - srcHover: resolveUrl( this.prefixUrl, navImages.next.HOVER ), - srcDown: resolveUrl( this.prefixUrl, navImages.next.DOWN ), - onRelease: onNextHandler, - onFocus: onFocusHandler, - onBlur: onBlurHandler - }); - - this.previousButton.disable(); - - if( useGroup ){ - this.paging = new $.ButtonGroup({ - buttons: [ - this.previousButton, - this.nextButton - ], - clickTimeThreshold: this.clickTimeThreshold, - clickDistThreshold: this.clickDistThreshold - }); - - this.pagingControl = this.paging.element; - - if( this.toolbar ){ - this.toolbar.addControl( - this.pagingControl, - $.ControlAnchor.BOTTOM_RIGHT - ); - }else{ - this.addControl( - this.pagingControl, - $.ControlAnchor.TOP_LEFT - ); - } - } - } - return this; - }, - - - /** - * @function - * @name OpenSeadragon.Viewer.prototype.bindStandardControls - * @return {OpenSeadragon.Viewer} Chainable. - */ - bindStandardControls: function(){ - ////////////////////////////////////////////////////////////////////////// - // Navigation Controls - ////////////////////////////////////////////////////////////////////////// - var beginZoomingInHandler = $.delegate( this, beginZoomingIn ), - endZoomingHandler = $.delegate( this, endZooming ), - doSingleZoomInHandler = $.delegate( this, doSingleZoomIn ), - beginZoomingOutHandler = $.delegate( this, beginZoomingOut ), - doSingleZoomOutHandler = $.delegate( this, doSingleZoomOut ), - onHomeHandler = $.delegate( this, onHome ), - onFullPageHandler = $.delegate( this, onFullPage ), - onFocusHandler = $.delegate( this, onFocus ), - onBlurHandler = $.delegate( this, onBlur ), - navImages = this.navImages, - buttons = [], - useGroup = true ; - - - if( this.showNavigationControl ){ - - if( this.zoomInButton || this.zoomOutButton || this.homeButton || this.fullPageButton ){ - //if we are binding to custom buttons then layout and - //grouping is the responsibility of the page author - useGroup = false; - } - - buttons.push( this.zoomInButton = new $.Button({ - element: this.zoomInButton ? $.getElement( this.zoomInButton ) : null, - clickTimeThreshold: this.clickTimeThreshold, - clickDistThreshold: this.clickDistThreshold, - tooltip: $.getString( "Tooltips.ZoomIn" ), - srcRest: resolveUrl( this.prefixUrl, navImages.zoomIn.REST ), - srcGroup: resolveUrl( this.prefixUrl, navImages.zoomIn.GROUP ), - srcHover: resolveUrl( this.prefixUrl, navImages.zoomIn.HOVER ), - srcDown: resolveUrl( this.prefixUrl, navImages.zoomIn.DOWN ), - onPress: beginZoomingInHandler, - onRelease: endZoomingHandler, - onClick: doSingleZoomInHandler, - onEnter: beginZoomingInHandler, - onExit: endZoomingHandler, - onFocus: onFocusHandler, - onBlur: onBlurHandler - })); - - buttons.push( this.zoomOutButton = new $.Button({ - element: this.zoomOutButton ? $.getElement( this.zoomOutButton ) : null, - clickTimeThreshold: this.clickTimeThreshold, - clickDistThreshold: this.clickDistThreshold, - tooltip: $.getString( "Tooltips.ZoomOut" ), - srcRest: resolveUrl( this.prefixUrl, navImages.zoomOut.REST ), - srcGroup: resolveUrl( this.prefixUrl, navImages.zoomOut.GROUP ), - srcHover: resolveUrl( this.prefixUrl, navImages.zoomOut.HOVER ), - srcDown: resolveUrl( this.prefixUrl, navImages.zoomOut.DOWN ), - onPress: beginZoomingOutHandler, - onRelease: endZoomingHandler, - onClick: doSingleZoomOutHandler, - onEnter: beginZoomingOutHandler, - onExit: endZoomingHandler, - onFocus: onFocusHandler, - onBlur: onBlurHandler - })); - - buttons.push( this.homeButton = new $.Button({ - element: this.homeButton ? $.getElement( this.homeButton ) : null, - clickTimeThreshold: this.clickTimeThreshold, - clickDistThreshold: this.clickDistThreshold, - tooltip: $.getString( "Tooltips.Home" ), - srcRest: resolveUrl( this.prefixUrl, navImages.home.REST ), - srcGroup: resolveUrl( this.prefixUrl, navImages.home.GROUP ), - srcHover: resolveUrl( this.prefixUrl, navImages.home.HOVER ), - srcDown: resolveUrl( this.prefixUrl, navImages.home.DOWN ), - onRelease: onHomeHandler, - onFocus: onFocusHandler, - onBlur: onBlurHandler - })); - - buttons.push( this.fullPageButton = new $.Button({ - element: this.fullPageButton ? $.getElement( this.fullPageButton ) : null, - clickTimeThreshold: this.clickTimeThreshold, - clickDistThreshold: this.clickDistThreshold, - tooltip: $.getString( "Tooltips.FullPage" ), - srcRest: resolveUrl( this.prefixUrl, navImages.fullpage.REST ), - srcGroup: resolveUrl( this.prefixUrl, navImages.fullpage.GROUP ), - srcHover: resolveUrl( this.prefixUrl, navImages.fullpage.HOVER ), - srcDown: resolveUrl( this.prefixUrl, navImages.fullpage.DOWN ), - onRelease: onFullPageHandler, - onFocus: onFocusHandler, - onBlur: onBlurHandler - })); - - if( useGroup ){ - this.buttons = new $.ButtonGroup({ - buttons: buttons, - clickTimeThreshold: this.clickTimeThreshold, - clickDistThreshold: this.clickDistThreshold - }); - - this.navControl = this.buttons.element; - this.addHandler( 'open', $.delegate( this, lightUp ) ); - - if( this.toolbar ){ - this.toolbar.addControl( - this.navControl, - $.ControlAnchor.TOP_LEFT - ); - }else{ - this.addControl( - this.navControl, - $.ControlAnchor.TOP_LEFT - ); - } - } - - } - return this; - }, - - - /** - * @function - * @name OpenSeadragon.Viewer.prototype.goToPage - * @return {OpenSeadragon.Viewer} Chainable. - */ - goToPage: function( page ){ - //page is a 1 based index so normalize now - //page = page; - this.raiseEvent( 'page', { page: page, viewer: this } ); - - if( this.tileSources.length > page ){ - - THIS[ this.hash ].sequence = page; - - if( this.nextButton ){ - if( ( this.tileSources.length - 1 ) === page ){ - //Disable next button - this.nextButton.disable(); - } else { - this.nextButton.enable(); - } - } - if( this.previousButton ){ - if( page > 0 ){ - //Enable previous button - this.previousButton.enable(); - } else { - this.previousButton.disable(); - } - } - - this.openTileSource( this.tileSources[ page ] ); - } - - if( $.isFunction( this.onPageChange ) ){ - this.onPageChange({ - page: page, - viewer: this - }); - } - if( this.referenceStrip ){ - this.referenceStrip.setFocus( page ); - } - return this; - } - -}); - - - -/////////////////////////////////////////////////////////////////////////////// -// Schedulers provide the general engine for animation -/////////////////////////////////////////////////////////////////////////////// -function scheduleUpdate( viewer, updateFunc, prevUpdateTime ){ - var currentTime, - targetTime, - deltaTime; - - if ( THIS[ viewer.hash ].animating ) { - return window.setTimeout( function(){ - updateFunc( viewer ); - }, 1 ); - } - - currentTime = +new Date(); - prevUpdateTime = prevUpdateTime ? prevUpdateTime : currentTime; - // 60 frames per second is ideal - targetTime = prevUpdateTime + 1000 / 60; - deltaTime = Math.max( 1, targetTime - currentTime ); - - return window.setTimeout( function(){ - updateFunc( viewer ); - }, deltaTime ); -} - - -//provides a sequence in the fade animation -function scheduleControlsFade( viewer ) { - window.setTimeout( function(){ - updateControlsFade( viewer ); - }, 20); -} - - -//initiates an animation to hide the controls -function beginControlsAutoHide( viewer ) { - if ( !viewer.autoHideControls ) { - return; - } - viewer.controlsShouldFade = true; - viewer.controlsFadeBeginTime = - +new Date() + - viewer.controlsFadeDelay; - - window.setTimeout( function(){ - scheduleControlsFade( viewer ); - }, viewer.controlsFadeDelay ); -} - - -//determines if fade animation is done or continues the animation -function updateControlsFade( viewer ) { - var currentTime, - deltaTime, - opacity, - i; - if ( viewer.controlsShouldFade ) { - currentTime = new Date().getTime(); - deltaTime = currentTime - viewer.controlsFadeBeginTime; - opacity = 1.0 - deltaTime / viewer.controlsFadeLength; - - opacity = Math.min( 1.0, opacity ); - opacity = Math.max( 0.0, opacity ); - - for ( i = viewer.controls.length - 1; i >= 0; i--) { - viewer.controls[ i ].setOpacity( opacity ); - } - - if ( opacity > 0 ) { - // fade again - scheduleControlsFade( viewer ); - } - } -} - - -//stop the fade animation on the controls and show them -function abortControlsAutoHide( viewer ) { - var i; - viewer.controlsShouldFade = false; - for ( i = viewer.controls.length - 1; i >= 0; i-- ) { - viewer.controls[ i ].setOpacity( 1.0 ); - } -} - - - -/////////////////////////////////////////////////////////////////////////////// -// Default view event handlers. -/////////////////////////////////////////////////////////////////////////////// -function onFocus(){ - abortControlsAutoHide( this ); -} - -function onBlur(){ - beginControlsAutoHide( this ); - -} - -function onCanvasClick( tracker, position, quick, shift ) { - var zoomPreClick, - factor; - if ( this.viewport && quick ) { // ignore clicks where mouse moved - zoomPerClick = this.zoomPerClick; - factor = shift ? 1.0 / zoomPerClick : zoomPerClick; - this.viewport.zoomBy( - factor, - this.viewport.pointFromPixel( position, true ) - ); - this.viewport.applyConstraints(); - } - this.raiseEvent( 'canvas-click', { - tracker: tracker, - position: position, - quick: quick, - shift: shift - }); -} - -function onCanvasDrag( tracker, position, delta, shift ) { - if ( this.viewport ) { - if( !this.panHorizontal ){ - delta.x = 0; - } - if( !this.panVertical ){ - delta.y = 0; - } - this.viewport.panBy( - this.viewport.deltaPointsFromPixels( - delta.negate() - ) - ); - if( this.constrainDuringPan ){ - this.viewport.applyConstraints(); - } - } - this.raiseEvent( 'canvas-click', { - tracker: tracker, - position: position, - delta: delta, - shift: shift - }); -} - -function onCanvasRelease( tracker, position, insideElementPress, insideElementRelease ) { - if ( insideElementPress && this.viewport ) { - this.viewport.applyConstraints(); - } - this.raiseEvent( 'canvas-release', { - tracker: tracker, - position: position, - insideElementPress: insideElementPress, - insideElementRelease: insideElementRelease - }); -} - -function onCanvasScroll( tracker, position, scroll, shift ) { - var factor; - if ( this.viewport ) { - factor = Math.pow( this.zoomPerScroll, scroll ); - this.viewport.zoomBy( - factor, - this.viewport.pointFromPixel( position, true ) - ); - this.viewport.applyConstraints(); - } - this.raiseEvent( 'canvas-scroll', { - tracker: tracker, - position: position, - scroll: scroll, - shift: shift - }); - //cancels event - return false; -} - -function onContainerExit( tracker, position, buttonDownElement, buttonDownAny ) { - if ( !buttonDownElement ) { - THIS[ this.hash ].mouseInside = false; - if ( !THIS[ this.hash ].animating ) { - beginControlsAutoHide( this ); - } - } - this.raiseEvent( 'container-exit', { - tracker: tracker, - position: position, - buttonDownElement: buttonDownElement, - buttonDownAny: buttonDownAny - }); -} - -function onContainerRelease( tracker, position, insideElementPress, insideElementRelease ) { - if ( !insideElementRelease ) { - THIS[ this.hash ].mouseInside = false; - if ( !THIS[ this.hash ].animating ) { - beginControlsAutoHide( this ); - } - } - this.raiseEvent( 'container-release', { - tracker: tracker, - position: position, - insideElementPress: insideElementPress, - insideElementRelease: insideElementRelease - }); -} - -function onContainerEnter( tracker, position, buttonDownElement, buttonDownAny ) { - THIS[ this.hash ].mouseInside = true; - abortControlsAutoHide( this ); - this.raiseEvent( 'container-enter', { - tracker: tracker, - position: position, - buttonDownElement: buttonDownElement, - buttonDownAny: buttonDownAny - }); -} - - -/////////////////////////////////////////////////////////////////////////////// -// Page update routines ( aka Views - for future reference ) -/////////////////////////////////////////////////////////////////////////////// - -function updateMulti( viewer ) { - - var beginTime; - - if ( !viewer.source ) { - return; - } - - beginTime = +new Date(); - updateOnce( viewer ); - scheduleUpdate( viewer, arguments.callee, beginTime ); -} - -function updateOnce( viewer ) { - - var containerSize, - animated; - - if ( !viewer.source ) { - return; - } - - //viewer.profiler.beginUpdate(); - - containerSize = $.getElementSize( viewer.container ); - if ( !containerSize.equals( THIS[ viewer.hash ].prevContainerSize ) ) { - // maintain image position - viewer.viewport.resize( containerSize, true ); - THIS[ viewer.hash ].prevContainerSize = containerSize; - } - - animated = viewer.viewport.update(); - - if( viewer.referenceStrip ){ - animated = viewer.referenceStrip.update( viewer.viewport ) || animated; - } - - if ( !THIS[ viewer.hash ].animating && animated ) { - viewer.raiseEvent( "animationstart" ); - abortControlsAutoHide( viewer ); - } - - if ( animated ) { - viewer.drawer.update(); - if( viewer.navigator ){ - viewer.navigator.update( viewer.viewport ); - } - viewer.raiseEvent( "animation" ); - } else if ( THIS[ viewer.hash ].forceRedraw || viewer.drawer.needsUpdate() ) { - viewer.drawer.update(); - if( viewer.navigator ){ - viewer.navigator.update( viewer.viewport ); - } - THIS[ viewer.hash ].forceRedraw = false; - } - - if ( THIS[ viewer.hash ].animating && !animated ) { - viewer.raiseEvent( "animationfinish" ); - - if ( !THIS[ viewer.hash ].mouseInside ) { - beginControlsAutoHide( viewer ); - } - } - - THIS[ viewer.hash ].animating = animated; - - //viewer.profiler.endUpdate(); -} - - - -/////////////////////////////////////////////////////////////////////////////// -// Navigation Controls -/////////////////////////////////////////////////////////////////////////////// -function resolveUrl( prefix, url ) { - return prefix ? prefix + url : url; -} - - - -function beginZoomingIn() { - THIS[ this.hash ].lastZoomTime = +new Date(); - THIS[ this.hash ].zoomFactor = this.zoomPerSecond; - THIS[ this.hash ].zooming = true; - scheduleZoom( this ); -} - - -function beginZoomingOut() { - THIS[ this.hash ].lastZoomTime = +new Date(); - THIS[ this.hash ].zoomFactor = 1.0 / this.zoomPerSecond; - THIS[ this.hash ].zooming = true; - scheduleZoom( this ); -} - - -function endZooming() { - THIS[ this.hash ].zooming = false; -} - - -function scheduleZoom( viewer ) { - window.setTimeout( $.delegate( viewer, doZoom ), 10 ); -} - - -function doZoom() { - var currentTime, - deltaTime, - adjustFactor; - - if ( THIS[ this.hash ].zooming && this.viewport) { - currentTime = +new Date(); - deltaTime = currentTime - THIS[ this.hash ].lastZoomTime; - adjustedFactor = Math.pow( THIS[ this.hash ].zoomFactor, deltaTime / 1000 ); - - this.viewport.zoomBy( adjustedFactor ); - this.viewport.applyConstraints(); - THIS[ this.hash ].lastZoomTime = currentTime; - scheduleZoom( this ); - } -} - - -function doSingleZoomIn() { - if ( this.viewport ) { - THIS[ this.hash ].zooming = false; - this.viewport.zoomBy( - this.zoomPerClick / 1.0 - ); - this.viewport.applyConstraints(); - } -} - - -function doSingleZoomOut() { - if ( this.viewport ) { - THIS[ this.hash ].zooming = false; - this.viewport.zoomBy( - 1.0 / this.zoomPerClick - ); - this.viewport.applyConstraints(); - } -} - - -function lightUp() { - this.buttons.emulateEnter(); - this.buttons.emulateExit(); -} - - -function onHome() { - if ( this.viewport ) { - this.viewport.goHome(); - } -} - - -function onFullPage() { - this.setFullPage( !this.isFullPage() ); - // correct for no mouseout event on change - if( this.buttons ){ - this.buttons.emulateExit(); - } - this.fullPageButton.element.focus(); - if ( this.viewport ) { - this.viewport.applyConstraints(); - } -} - - -function onPrevious(){ - var previous = THIS[ this.hash ].sequence - 1; - this.goToPage( previous ); -} - - -function onNext(){ - var next = THIS[ this.hash ].sequence + 1; - this.goToPage( next ); -} - - -}( OpenSeadragon )); -/*globals OpenSeadragon */ - -(function( $ ){ - -/** - * The Navigator provides a small view of the current image as fixed - * 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 - * is currently being examined. The navigator's viewport can be interacted - * with using the keyboard or the mouse. - * @class - * @name OpenSeadragon.Navigator - * @extends OpenSeadragon.Viewer - * @extends OpenSeadragon.EventHandler - * @param {Object} options - * @param {String} options.viewerId - */ -$.Navigator = function( options ){ - - var _this = this, - viewer = options.viewer, - viewerSize = $.getElementSize( viewer.element ); - - //We may need to create a new element and id if they did not - //provide the id for the existing element - if( !options.id ){ - options.id = 'navigator-' + (+new Date()); - this.element = $.makeNeutralElement( "div" ); - this.element.id = options.id; - this.element.className = 'navigator'; - } - - options = $.extend( true, { - sizeRatio: $.DEFAULT_SETTINGS.navigatorSizeRatio - }, options, { - element: this.element, - //These need to be overridden to prevent recursion since - //the navigator is a viewer and a viewer has a navigator - showNavigator: false, - mouseNavEnabled: false, - showNavigationControl: false, - showSequenceControl: false - }); - - options.minPixelRatio = this.minPixelRatio = viewer.minPixelRatio; - - (function( style ){ - style.marginTop = '0px'; - style.marginRight = '0px'; - style.marginBottom = '0px'; - style.marginLeft = '0px'; - style.border = '2px solid #555'; - style.background = '#000'; - style.opacity = 0.8; - style.overflow = 'hidden'; - }( this.element.style )); - - this.displayRegion = $.makeNeutralElement( "textarea" ); - this.displayRegion.id = this.element.id + '-displayregion'; - this.displayRegion.className = 'displayregion'; - - (function( style ){ - style.position = 'relative'; - style.top = '0px'; - style.left = '0px'; - style.fontSize = '0px'; - style.overflow = 'hidden'; - style.border = '2px solid #900'; - - //TODO: IE doesnt like this property being set - //try{ style.outline = '2px auto #909'; }catch(e){/*ignore*/} - - style.background = 'transparent'; - - // 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. - /*jshint sub:true */ - style['float'] = 'left'; //Webkit - - style.cssFloat = 'left'; //Firefox - style.styleFloat = 'left'; //IE - style.zIndex = 999999999; - style.cursor = 'default'; - }( this.displayRegion.style )); - - this.element.innerTracker = new $.MouseTracker({ - element: this.element, - scrollHandler: function(){ - //dont scroll the page up and down if the user is scrolling - //in the navigator - return false; - } - }).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({ - element: this.container, - clickTimeThreshold: this.clickTimeThreshold, - clickDistThreshold: this.clickDistThreshold, - enterHandler: $.delegate( this, onContainerEnter ), - exitHandler: $.delegate( this, onContainerExit ), - releaseHandler: $.delegate( this, onContainerRelease ) - }).setTracking( this.mouseNavEnabled ? true : false ); // always tracking*/ - - - viewer.addControl( - this.element, - $.ControlAnchor.TOP_RIGHT - ); - - if( options.width && options.height ){ - this.element.style.width = options.width + 'px'; - this.element.style.height = options.height + 'px'; - } else { - this.element.style.width = ( viewerSize.x * options.sizeRatio ) + 'px'; - this.element.style.height = ( viewerSize.y * options.sizeRatio ) + 'px'; - } - - $.Viewer.apply( this, [ options ] ); - - this.element.getElementsByTagName('form')[0].appendChild( this.displayRegion ); - -}; - -$.extend( $.Navigator.prototype, $.EventHandler.prototype, $.Viewer.prototype, { - - /** - * @function - * @name OpenSeadragon.Navigator.prototype.update - */ - update: function( viewport ){ - - var bounds, - topleft, - bottomright; - - if( viewport && this.viewport ){ - bounds = viewport.getBounds( true ); - topleft = this.viewport.pixelFromPoint( bounds.getTopLeft() ); - bottomright = this.viewport.pixelFromPoint( bounds.getBottomRight() ); - - //update style for navigator-box - (function(style){ - - style.top = topleft.y + 'px'; - style.left = topleft.x + 'px'; - - var width = Math.abs( topleft.x - bottomright.x ) - 3; // TODO: What does this magic number mean? - var height = Math.abs( topleft.y - bottomright.y ) - 3; - // make sure width and height are non-negative so IE doesn't throw - style.width = Math.max( width, 0 ) + 'px'; - style.height = Math.max( height, 0 ) + 'px'; - - }( this.displayRegion.style )); - } - - } - -}); - - -/** - * @private - * @inner - * @function - */ -function onCanvasClick( tracker, position, quick, shift ) { - this.displayRegion.focus(); -} - - -/** - * @private - * @inner - * @function - */ -function onCanvasDrag( tracker, position, delta, shift ) { - if ( this.viewer.viewport ) { - if( !this.panHorizontal ){ - delta.x = 0; - } - if( !this.panVertical ){ - delta.y = 0; - } - this.viewer.viewport.panBy( - this.viewport.deltaPointsFromPixels( - delta - ) - ); - } -} - - -/** - * @private - * @inner - * @function - */ -function onCanvasRelease( tracker, position, insideElementPress, insideElementRelease ) { - if ( insideElementPress && this.viewer.viewport ) { - this.viewer.viewport.applyConstraints(); - } -} - - -/** - * @private - * @inner - * @function - */ -function onCanvasScroll( tracker, position, scroll, shift ) { - var factor; - if ( this.viewer.viewport ) { - factor = Math.pow( this.zoomPerScroll, scroll ); - this.viewer.viewport.zoomBy( - factor, - //this.viewport.pointFromPixel( position, true ) - this.viewport.getCenter() - ); - this.viewer.viewport.applyConstraints(); - } - //cancels event - return false; -} - - -}( OpenSeadragon )); - -(function( $ ){ - -//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 -// pythons gettext might be a reasonable approach. -var I18N = { - Errors: { - Failure: "Sorry, but Seadragon Ajax can't run on your browser!\n" + - "Please try using IE 7 or Firefox 3.\n", - Dzc: "Sorry, we don't support Deep Zoom Collections!", - Dzi: "Hmm, this doesn't appear to be a valid Deep Zoom Image.", - Xml: "Hmm, this doesn't appear to be a valid Deep Zoom Image.", - Empty: "You asked us to open nothing, so we did just that.", - ImageFormat: "Sorry, we don't support {0}-based Deep Zoom Images.", - Security: "It looks like a security restriction stopped us from " + - "loading this Deep Zoom Image.", - Status: "This space unintentionally left blank ({0} {1}).", - Unknown: "Whoops, something inexplicably went wrong. Sorry!" - }, - - Messages: { - Loading: "Loading..." - }, - - Tooltips: { - FullPage: "Toggle full page", - Home: "Go home", - ZoomIn: "Zoom in", - ZoomOut: "Zoom out", - NextPage: "Next page", - PreviousPage: "Previous page" - } -}; - -$.extend( $, { - - /** - * @function - * @name OpenSeadragon.getString - * @param {String} property - */ - getString: function( prop ) { - - var props = prop.split('.'), - string = null, - args = arguments, - i; - - for ( i = 0; i < props.length; i++ ) { - // in case not a subproperty - string = I18N[ props[ i ] ] || {}; - } - - if ( typeof( string ) != "string" ) { - string = ""; - } - - return string.replace(/\{\d+\}/g, function(capture) { - var i = parseInt( capture.match( /\d+/ ), 10 ) + 1; - return i < args.length ? - args[ i ] : - ""; - }); - }, - - /** - * @function - * @name OpenSeadragon.setString - * @param {String} property - * @param {*} value - */ - setString: function( prop, value ) { - - var props = prop.split('.'), - container = $.Strings, - i; - - for ( i = 0; i < props.length - 1; i++ ) { - if ( !container[ props[ i ] ] ) { - container[ props[ i ] ] = {}; - } - container = container[ props[ i ] ]; - } - - container[ props[ i ] ] = value; - } - -}); - -}( OpenSeadragon )); -/*globals OpenSeadragon */ - -(function( $ ){ - -/** - * 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 - * not requiring any other frame of reference. - * @class - * @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. - * @property {Number} [x] The vector component 'x'. - * @property {Number} [y] The vector component 'y'. - */ -$.Point = function( x, y ) { - this.x = typeof ( x ) == "number" ? x : 0; - this.y = typeof ( y ) == "number" ? y : 0; -}; - -$.Point.prototype = { - - /** - * Add another Point to this point and return a new Point. - * @function - * @param {OpenSeadragon.Point} point The point to add vector components. - * @returns {OpenSeadragon.Point} A new point representing the sum of the - * vector components - */ - plus: function( point ) { - return new $.Point( - this.x + point.x, - this.y + point.y - ); - }, - - /** - * Add another Point to this point and return a new Point. - * @function - * @param {OpenSeadragon.Point} point The point to add vector components. - * @returns {OpenSeadragon.Point} A new point representing the sum of the - * vector components - */ - minus: function( point ) { - return new $.Point( - this.x - point.x, - this.y - point.y - ); - }, - - /** - * Add another Point to this point and return a new Point. - * @function - * @param {OpenSeadragon.Point} point The point to add vector components. - * @returns {OpenSeadragon.Point} A new point representing the sum of the - * vector components - */ - times: function( factor ) { - return new $.Point( - this.x * factor, - this.y * factor - ); - }, - - /** - * Add another Point to this point and return a new Point. - * @function - * @param {OpenSeadragon.Point} point The point to add vector components. - * @returns {OpenSeadragon.Point} A new point representing the sum of the - * vector components - */ - divide: function( factor ) { - return new $.Point( - this.x / factor, - this.y / factor - ); - }, - - /** - * Add another Point to this point and return a new Point. - * @function - * @param {OpenSeadragon.Point} point The point to add vector components. - * @returns {OpenSeadragon.Point} A new point representing the sum of the - * vector components - */ - negate: function() { - return new $.Point( -this.x, -this.y ); - }, - - /** - * Add another Point to this point and return a new Point. - * @function - * @param {OpenSeadragon.Point} point The point to add vector components. - * @returns {OpenSeadragon.Point} A new point representing the sum of the - * vector components - */ - distanceTo: function( point ) { - return Math.sqrt( - Math.pow( this.x - point.x, 2 ) + - Math.pow( this.y - point.y, 2 ) - ); - }, - - /** - * Add another Point to this point and return a new Point. - * @function - * @param {OpenSeadragon.Point} point The point to add vector components. - * @returns {OpenSeadragon.Point} A new point representing the sum of the - * vector components - */ - apply: function( func ) { - return new $.Point( func( this.x ), func( this.y ) ); - }, - - /** - * Add another Point to this point and return a new Point. - * @function - * @param {OpenSeadragon.Point} point The point to add vector components. - * @returns {OpenSeadragon.Point} A new point representing the sum of the - * vector components - */ - equals: function( point ) { - return ( - point instanceof $.Point - ) && ( - this.x === point.x - ) && ( - this.y === point.y - ); - }, - - /** - * Add another Point to this point and return a new Point. - * @function - * @param {OpenSeadragon.Point} point The point to add vector components. - * @returns {OpenSeadragon.Point} A new point representing the sum of the - * vector components - */ - toString: function() { - return "(" + Math.round(this.x) + "," + Math.round(this.y) + ")"; - } -}; - -}( OpenSeadragon )); -/*globals OpenSeadragon */ - -(function( $ ){ - - -/** - * The TileSource contains the most basic implementation required to create a - * smooth transition between layer in an image pyramid. It has only a single key - * 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 - * 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, - * ('getImageInfo'). - *
- * 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 - * 2^(N+1) >= M. - * @class - * @extends OpenSeadragon.EventHandler - * @param {Number|Object|Array|String} width - * If more than a single argument is supplied, the traditional use of - * positional parameters is supplied and width is expected to be the width - * source image at it's max resolution in pixels. If a single argument is supplied and - * it is an Object or Array, the construction is assumed to occur through - * the extending classes implementation of 'configure'. Finally if only a - * single argument is supplied and it is a String, the extending class is - * expected to implement 'getImageInfo' and 'configure'. - * @param {Number} height - * Width of the source image at max resolution in pixels. - * @param {Number} tileSize - * 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 - * divided into a matrix of smaller images. - * @param {Number} tileOverlap - * The number of pixels each tile is expected to overlap touching tiles. - * @param {Number} minLevel - * The minimum level to attempt to load. - * @param {Number} maxLevel - * 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 ) { - var _this = this, - callback = null, - readyHandler = null, - args = arguments, - options, - i; - - if( $.isPlainObject( width ) ){ - options = width; - }else{ - options = { - width: args[0], - height: args[1], - tileSize: args[2], - tileOverlap: args[3], - minlevel: args[4], - maxLevel: args[5] - }; - } - - //Tile sources supply some events, namely 'ready' when they must be configured - //by asyncronously fetching their configuration data. - $.EventHandler.call( this ); - - //we allow options to override anything we dont treat as - //required via idiomatic options or which is functionally - //set depending on the state of the readiness of this tile - //source - $.extend( true, this, options ); - - //Any functions that are passed as arguments are bound to the ready callback - /*jshint loopfunc:true*/ - for( i = 0; i < arguments.length; i++ ){ - if( $.isFunction( arguments[i] ) ){ - callback = arguments[ i ]; - this.addHandler( 'ready', function( placeHolderSource, readySource ){ - callback( readySource ); - }); - //only one callback per constructor - break; - } - } - - if( 'string' == $.type( arguments[ 0 ] ) ){ - //in case the getImageInfo method is overriden and/or implies an - //async mechanism set some safe defaults first - this.aspectRatio = 1; - this.dimensions = new $.Point( 10, 10 ); - this.tileSize = 0; - this.tileOverlap = 0; - this.minLevel = 0; - this.maxLevel = 0; - this.ready = false; - //configuration via url implies the extending class - //implements and 'configure' - this.getImageInfo( arguments[ 0 ] ); - - } else { - - //explicit configuration via positional args in constructor - //or the more idiomatic 'options' object - this.ready = true; - this.aspectRatio = ( options.width && options.height ) ? - ( options.width / options.height ) : 1; - this.dimensions = new $.Point( options.width, options.height ); - this.tileSize = options.tileSize ? options.tileSize : 0; - this.tileOverlap = options.tileOverlap ? options.tileOverlap : 0; - this.minLevel = options.minLevel ? options.minLevel : 0; - this.maxLevel = ( undefined !== options.maxLevel && null !== options.maxLevel ) ? - options.maxLevel : ( - ( options.width && options.height ) ? Math.ceil( - Math.log( Math.max( options.width, options.height ) ) / - Math.log( 2 ) - ) : 0 - ); - if( callback && $.isFunction( callback ) ){ - callback( this ); - } - } - - -}; - - -$.TileSource.prototype = { - - /** - * @function - * @param {Number} level - */ - getLevelScale: function( level ) { - return 1 / ( 1 << ( this.maxLevel - level ) ); - }, - - /** - * @function - * @param {Number} level - */ - getNumTiles: function( level ) { - var scale = this.getLevelScale( level ), - x = Math.ceil( scale * this.dimensions.x / this.tileSize ), - y = Math.ceil( scale * this.dimensions.y / this.tileSize ); - - return new $.Point( x, y ); - }, - - /** - * @function - * @param {Number} level - */ - getPixelRatio: function( level ) { - var imageSizeScaled = this.dimensions.times( this.getLevelScale( level ) ), - rx = 1.0 / imageSizeScaled.x, - ry = 1.0 / imageSizeScaled.y; - - return new $.Point(rx, ry); - }, - - /** - * @function - * @param {Number} level - * @param {OpenSeadragon.Point} point - */ - getTileAtPoint: function( level, point ) { - var pixel = point.times( this.dimensions.x ).times( this.getLevelScale(level ) ), - tx = Math.floor( pixel.x / this.tileSize ), - ty = Math.floor( pixel.y / this.tileSize ); - - return new $.Point( tx, ty ); - }, - - /** - * @function - * @param {Number} level - * @param {Number} x - * @param {Number} y - */ - getTileBounds: function( level, x, y ) { - var dimensionsScaled = this.dimensions.times( this.getLevelScale( level ) ), - px = ( x === 0 ) ? 0 : this.tileSize * x - this.tileOverlap, - py = ( y === 0 ) ? 0 : this.tileSize * y - this.tileOverlap, - sx = this.tileSize + ( x === 0 ? 1 : 2 ) * this.tileOverlap, - sy = this.tileSize + ( y === 0 ? 1 : 2 ) * this.tileOverlap, - scale = 1.0 / dimensionsScaled.x; - - sx = Math.min( sx, dimensionsScaled.x - px ); - sy = Math.min( sy, dimensionsScaled.y - py ); - - return new $.Rect( px * scale, py * scale, sx * scale, sy * scale ); - }, - - - /** - * Responsible for retrieving, and caching the - * image metadata pertinent to this TileSources implementation. - * @function - * @param {String} url - * @throws {Error} - */ - getImageInfo: function( url ) { - var _this = this, - error, - callbackName, - callback, - readySource, - options, - urlParts, - filename, - lastDot, - tilesUrl; - - - if( url ) { - urlParts = url.split( '/' ); - filename = urlParts[ urlParts.length - 1 ]; - lastDot = filename.lastIndexOf( '.' ); - if ( lastDot > -1 ) { - urlParts[ urlParts.length - 1 ] = filename.slice( 0, lastDot ); - } - } - - callback = function( data ){ - var $TileSource = $.TileSource.determineType( _this, data, url ); - options = $TileSource.prototype.configure.apply( _this, [ data, url ]); - readySource = new $TileSource( options ); - _this.ready = true; - _this.raiseEvent( 'ready', readySource ); - }; - - if( url.match(/\.js$/) ){ - //TODO: Its not very flexible to require tile sources to end jsonp - // request for info with a url that ends with '.js' but for - // now it's the only way I see to distinguish uniformly. - callbackName = url.split( '/' ).pop().replace('.js',''); - $.jsonp({ - url: url, - async: false, - callbackName: callbackName, - callback: callback - }); - } else { - //TODO: struggling a little with TileSource rewrite to make info - // requests work asyncronously. For now I'm opting to make - // all xhr info request syncronous. - $.makeAjaxRequest( url, function( xhr ) { - var data = processResponse( xhr ); - callback( data ); - }); - } - - }, - - /** - * Responsible determining if a the particular TileSource supports the - * data format ( and allowed to apply logic against the url the data was - * loaded from, if any ). Overriding implementations are expected to do - * something smart with data and / or url to determine support. Also - * understand that iteration order of TileSources is not guarunteed so - * please make sure your data or url is expressive enough to ensure a simple - * and sufficient mechanisim for clear determination. - * @function - * @param {String|Object|Array|Document} data - * @param {String} url - the url the data was loaded - * from if any. - * @return {Boolean} - */ - supports: function( data, url ) { - return false; - }, - - /** - * Responsible for parsing and configuring the - * image metadata pertinent to this TileSources implementation. - * 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 - * server technologies, and various specifications for building image - * pyramids, this method is here to allow easy integration. - * @function - * @param {String|Object|Array|Document} data - * @param {String} url - the url the data was loaded - * from if any. - * @return {Object} options - A dictionary of keyword arguments sufficient - * to configure this tile sources constructor. - * @throws {Error} - */ - configure: function( data, url ) { - throw new Error( "Method not implemented." ); - }, - - /** - * Responsible for retriving the url which will return an image for the - * region speified by the given x, y, and level components. - * 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 - * server technologies, and various specifications for building image - * pyramids, this method is here to allow easy integration. - * @function - * @param {Number} level - * @param {Number} x - * @param {Number} y - * @throws {Error} - */ - getTileUrl: function( level, x, y ) { - throw new Error( "Method not implemented." ); - }, - - /** - * @function - * @param {Number} level - * @param {Number} x - * @param {Number} y - */ - tileExists: function( level, x, y ) { - var numTiles = this.getNumTiles( level ); - return level >= this.minLevel && - level <= this.maxLevel && - x >= 0 && - y >= 0 && - x < numTiles.x && - y < numTiles.y; - } -}; - - -$.extend( true, $.TileSource.prototype, $.EventHandler.prototype ); - - -/** - * Decides whether to try to process the response as xml, json, or hand back - * the text - * @eprivate - * @inner - * @function - * @param {XMLHttpRequest} xhr - the completed network request - */ -function processResponse( xhr ){ - var responseText = xhr.responseText, - status = xhr.status, - statusText, - data; - - if ( !xhr ) { - throw new Error( $.getString( "Errors.Security" ) ); - } else if ( xhr.status !== 200 && xhr.status !== 0 ) { - status = xhr.status; - statusText = ( status == 404 ) ? - "Not Found" : - xhr.statusText; - throw new Error( $.getString( "Errors.Status", status, statusText ) ); - } - - if( responseText.match(/\s*<.*/) ){ - try{ - data = ( xhr.responseXML && xhr.responseXML.documentElement ) ? - xhr.responseXML : - $.parseXml( responseText ); - } catch (e){ - data = xhr.responseText; - } - }else if( responseText.match(/\s*[\{\[].*/) ){ - /*jshint evil:true*/ - data = eval( '('+responseText+')' ); - }else{ - data = responseText; - } - return data; -} - - -/** - * Determines the TileSource Implementation by introspection of OpenSeadragon - * namespace, calling each TileSource implementation of 'isType' - * @eprivate - * @inner - * @function - * @param {Object|Array} data - the tile source configuration object - * @param {String} url - the url where the tile source configuration object was - * loaded from, if any. - */ -$.TileSource.determineType = function( tileSource, data, url ){ - var property; - for( property in OpenSeadragon ){ - if( property.match(/.+TileSource$/) && - $.isFunction( OpenSeadragon[ property ] ) && - $.isFunction( OpenSeadragon[ property ].prototype.supports ) && - OpenSeadragon[ property ].prototype.supports.call( tileSource, data, url ) - ){ - return OpenSeadragon[ property ]; - } - } -}; - - -}( OpenSeadragon )); - -(function( $ ){ - -/** - * @class - * @extends OpenSeadragon.TileSource - * @param {Number|Object} width - the pixel width of the image or the idiomatic - * options object which is used instead of positional arguments. - * @param {Number} height - * @param {Number} tileSize - * @param {Number} tileOverlap - * @param {String} tilesUrl - * @param {String} fileFormat - * @param {OpenSeadragon.DisplayRect[]} displayRects - * @property {String} tilesUrl - * @property {String} fileFormat - * @property {OpenSeadragon.DisplayRect[]} displayRects - */ -$.DziTileSource = function( width, height, tileSize, tileOverlap, tilesUrl, fileFormat, displayRects, minLevel, maxLevel ) { - var i, - rect, - level, - options; - - if( $.isPlainObject( width ) ){ - options = width; - }else{ - options = { - width: arguments[ 0 ], - height: arguments[ 1 ], - tileSize: arguments[ 2 ], - tileOverlap: arguments[ 3 ], - tilesUrl: arguments[ 4 ], - fileFormat: arguments[ 5 ], - displayRects: arguments[ 6 ], - minLevel: arguments[ 7 ], - maxLevel: arguments[ 8 ] - }; - } - - this._levelRects = {}; - this.tilesUrl = options.tilesUrl; - this.fileFormat = options.fileFormat; - this.displayRects = options.displayRects; - - if ( this.displayRects ) { - for ( i = this.displayRects.length - 1; i >= 0; i-- ) { - rect = this.displayRects[ i ]; - for ( level = rect.minLevel; level <= rect.maxLevel; level++ ) { - if ( !this._levelRects[ level ] ) { - this._levelRects[ level ] = []; - } - this._levelRects[ level ].push( rect ); - } - } - } - - $.TileSource.apply( this, [ options ] ); - -}; - -$.extend( $.DziTileSource.prototype, $.TileSource.prototype, { - - - /** - * Determine if the data and/or url imply the image service is supported by - * this tile source. - * @function - * @name OpenSeadragon.DziTileSource.prototype.supports - * @param {Object|Array} data - * @param {String} optional - url - */ - supports: function( data, url ){ - var ns; - if ( data.Image ) { - ns = data.Image.xmlns; - } else if ( data.documentElement && "Image" == data.documentElement.tagName ) { - ns = data.documentElement.namespaceURI; - } - - return ( "http://schemas.microsoft.com/deepzoom/2008" == ns || - "http://schemas.microsoft.com/deepzoom/2009" == ns ); - }, - - /** - * - * @function - * @name OpenSeadragon.DziTileSource.prototype.configure - * @param {Object|XMLDocument} data - the raw configuration - * @param {String} url - the url the data was retreived from if any. - * @return {Object} options - A dictionary of keyword arguments sufficient - * to configure this tile sources constructor. - */ - configure: function( data, url ){ - - var dziPath, - dziName, - tilesUrl, - options, - host; - - if( !$.isPlainObject(data) ){ - - options = configureFromXML( this, data ); - - }else{ - - options = configureFromObject( this, data ); - } - - if( url && !options.tilesUrl ){ - if( 'http' !== url.substring( 0, 4 ) ){ - host = location.protocol + '//' + location.host; - } - dziPath = url.split('/'); - dziName = dziPath.pop(); - dziName = dziName.substring(0, dziName.lastIndexOf('.')); - dziPath = '/' + dziPath.join('/') + '/' + dziName + '_files/'; - tilesUrl = dziPath; - if( host ){ - tilesUrl = host + tilesUrl; - } - options.tilesUrl = tilesUrl; - } - - return options; - }, - - - /** - * @function - * @name OpenSeadragon.DziTileSource.prototype.getTileUrl - * @param {Number} level - * @param {Number} x - * @param {Number} y - */ - getTileUrl: function( level, x, y ) { - return [ this.tilesUrl, level, '/', x, '_', y, '.', this.fileFormat ].join( '' ); - }, - - - /** - * @function - * @name OpenSeadragon.DziTileSource.prototype.tileExists - * @param {Number} level - * @param {Number} x - * @param {Number} y - */ - tileExists: function( level, x, y ) { - var rects = this._levelRects[ level ], - rect, - scale, - xMin, - yMin, - xMax, - yMax, - i; - - if ( !rects || !rects.length ) { - return true; - } - - for ( i = rects.length - 1; i >= 0; i-- ) { - rect = rects[ i ]; - - if ( level < rect.minLevel || level > rect.maxLevel ) { - continue; - } - - scale = this.getLevelScale( level ); - xMin = rect.x * scale; - yMin = rect.y * scale; - xMax = xMin + rect.width * scale; - yMax = yMin + rect.height * scale; - - xMin = Math.floor( xMin / this.tileSize ); - yMin = Math.floor( yMin / this.tileSize ); - xMax = Math.ceil( xMax / this.tileSize ); - yMax = Math.ceil( yMax / this.tileSize ); - - if ( xMin <= x && x < xMax && yMin <= y && y < yMax ) { - return true; - } - } - - return false; - } -}); - - -/** - * @private - * @inner - * @function - */ -function configureFromXML( tileSource, xmlDoc ){ - - if ( !xmlDoc || !xmlDoc.documentElement ) { - throw new Error( $.getString( "Errors.Xml" ) ); - } - - var root = xmlDoc.documentElement, - rootName = root.tagName, - configuration = null, - displayRects = [], - dispRectNodes, - dispRectNode, - rectNode, - sizeNode, - i; - - if ( rootName == "Image" ) { - - try { - sizeNode = root.getElementsByTagName( "Size" )[ 0 ]; - configuration = { - Image: { - xmlns: "http://schemas.microsoft.com/deepzoom/2008", - Format: root.getAttribute( "Format" ), - DisplayRect: null, - Overlap: parseInt( root.getAttribute( "Overlap" ), 10 ), - TileSize: parseInt( root.getAttribute( "TileSize" ), 10 ), - Size: { - Height: parseInt( sizeNode.getAttribute( "Height" ), 10 ), - Width: parseInt( sizeNode.getAttribute( "Width" ), 10 ) - } - } - }; - - if ( !$.imageFormatSupported( configuration.Image.Format ) ) { - throw new Error( - $.getString( "Errors.ImageFormat", configuration.Image.Format.toUpperCase() ) - ); - } - - dispRectNodes = root.getElementsByTagName( "DisplayRect" ); - for ( i = 0; i < dispRectNodes.length; i++ ) { - dispRectNode = dispRectNodes[ i ]; - rectNode = dispRectNode.getElementsByTagName( "Rect" )[ 0 ]; - - displayRects.push({ - Rect: { - X: parseInt( rectNode.getAttribute( "X" ), 10 ), - Y: parseInt( rectNode.getAttribute( "Y" ), 10 ), - Width: parseInt( rectNode.getAttribute( "Width" ), 10 ), - Height: parseInt( rectNode.getAttribute( "Height" ), 10 ), - MinLevel: 0, // ignore MinLevel attribute, bug in Deep Zoom Composer - MaxLevel: parseInt( dispRectNode.getAttribute( "MaxLevel" ), 10 ) - } - }); - } - - if( displayRects.length ){ - configuration.Image.DisplayRect = displayRects; - } - - return configureFromObject( tileSource, configuration ); - - } catch ( e ) { - throw (e instanceof Error) ? - e : - new Error( $.getString("Errors.Dzi") ); - } - } else if ( rootName == "Collection" ) { - throw new Error( $.getString( "Errors.Dzc" ) ); - } else if ( rootName == "Error" ) { - return processDZIError( root ); - } - - throw new Error( $.getString( "Errors.Dzi" ) ); -} - -/** - * @private - * @inner - * @function - */ -function configureFromObject( tileSource, configuration ){ - var imageData = configuration.Image, - tilesUrl = imageData.Url, - fileFormat = imageData.Format, - sizeData = imageData.Size, - dispRectData = imageData.DisplayRect || [], - width = parseInt( sizeData.Width, 10 ), - height = parseInt( sizeData.Height, 10 ), - tileSize = parseInt( imageData.TileSize, 10 ), - tileOverlap = parseInt( imageData.Overlap, 10 ), - displayRects = [], - rectData, - i; - - //TODO: need to figure out out to better handle image format compatibility - // which actually includes additional file formats like xml and pdf - // and plain text for various tilesource implementations to avoid low - // level errors. - // - // For now, just don't perform the check. - // - /*if ( !imageFormatSupported( fileFormat ) ) { - throw new Error( - $.getString( "Errors.ImageFormat", fileFormat.toUpperCase() ) - ); - }*/ - - for ( i = 0; i < dispRectData.length; i++ ) { - rectData = dispRectData[ i ].Rect; - - displayRects.push( new $.DisplayRect( - parseInt( rectData.X, 10 ), - parseInt( rectData.Y, 10 ), - parseInt( rectData.Width, 10 ), - parseInt( rectData.Height, 10 ), - 0, // ignore MinLevel attribute, bug in Deep Zoom Composer - parseInt( rectData.MaxLevel, 10 ) - )); - } - - - return { - width: width, /* width *required */ - height: height, /* height *required */ - tileSize: tileSize, /* tileSize *required */ - tileOverlap: tileOverlap, /* tileOverlap *required */ - minLevel: null, /* minLevel */ - maxLevel: null, /* maxLevel */ - tilesUrl: tilesUrl, /* tilesUrl */ - fileFormat: fileFormat, /* fileFormat */ - displayRects: displayRects /* displayRects */ - }; - -} - -}( OpenSeadragon )); -(function( $ ){ - -/** - * 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: - * https://gist.github.com/jpstroop/4624253 - * - * @class - * @name OpenSeadragon.IIIFTileSource - * @see http://library.stanford.edu/iiif/image-api/ - */ -$.IIIFTileSource = function( options ){ - - $.extend( true, this, options ); - - if( !(this.height && this.width && this.identifier && this.tilesUrl ) ){ - throw new Error('IIIF required parameters not provided.'); - } - - //TODO: at this point the base tile source implementation assumes - // a tile is a square and so only has one property tileSize - // to store it. It may be possible to make tileSize a vector - // OpenSeadraon.Point but would require careful implementation - // to preserve backward compatibility. - options.tileSize = this.tile_width; - - options.maxLevel = options.maxLevel ? options.maxLevel : Number( - Math.ceil( Math.log( Math.max( this.width, this.height ), 2 ) ) - ); - - $.TileSource.apply( this, [ options ] ); -}; - -$.extend( $.IIIFTileSource.prototype, $.TileSource.prototype, { - - - /** - * Determine if the data and/or url imply the image service is supported by - * this tile source. - * @function - * @name OpenSeadragon.IIIFTileSource.prototype.supports - * @param {Object|Array} data - * @param {String} optional - url - */ - supports: function( data, url ){ - return ( - data.ns && - "http://library.stanford.edu/iiif/image-api/ns/" == data.ns - ) || ( - 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#level3" == data.profile || - "http://library.stanford.edu/iiif/image-api/compliance.html" == data.profile - ) - ) || ( - data.documentElement && - "info" == data.documentElement.tagName && - "http://library.stanford.edu/iiif/image-api/ns/" == - data.documentElement.namespaceURI - ); - }, - - /** - * - * @function - * @name OpenSeadragon.IIIFTileSource.prototype.configure - * @param {Object|XMLDocument} data - the raw configuration - * @param {String} url - the url the data was retreived from if any. - * @return {Object} options - A dictionary of keyword arguments sufficient - * to configure this tile source via it's constructor. - */ - configure: function( data, url ){ - var service, - identifier, - options, - host; - - if( !$.isPlainObject(data) ){ - - options = configureFromXml( this, data ); - - }else{ - - options = configureFromObject( this, data ); - } - - if( url && !options.tilesUrl ){ - service = url.split('/'); - service.pop(); //info.json or info.xml - service = service.join('/'); - if( 'http' !== url.substring( 0, 4 ) ){ - host = location.protocol + '//' + location.host; - service = host + service; - } - options.tilesUrl = service.replace( - data.identifier, - '' - ); - } - - return options; - }, - - /** - * Responsible for retreiving the url which will return an image for the - * region speified by the given x, y, and level components. - * @function - * @name OpenSeadragon.IIIFTileSource.prototype.getTileUrl - * @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 ), - - //## get iiif size - iiif_size = 'pct:' + ( scale * 100 ), - - //# image dimensions at this level - level_width = Math.ceil( this.width * scale ), - level_height = Math.ceil( this.height * scale ), - - //## iiif region - iiif_tile_size_width = Math.ceil( this.tileSize / scale ), - iiif_tile_size_height = Math.ceil( this.tileSize / scale ), - iiif_region, - iiif_tile_x, - iiif_tile_y, - iiif_tile_w, - iiif_tile_h; - - - if ( level_width < this.tile_width || level_height < this.tile_height ){ - iiif_region = 'full'; - } else { - iiif_tile_x = x * iiif_tile_size_width; - iiif_tile_y = y * iiif_tile_size_height; - 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_region = [ iiif_tile_x, iiif_tile_y, iiif_tile_w, iiif_tile_h ].join(','); - } - - return [ - this.tilesUrl, - this.identifier, - iiif_region, - iiif_size, - IIIF_ROTATION, - IIIF_QUALITY - ].join('/'); - } - - -}); - -/** - * @private - * @inner - * @function - * - - - 1E34750D-38DB-4825-A38A-B60A345E591C - 6000 - 4000 - - 1 - 2 - 4 - - 1024 - 1024 - - jpg - png - - - native - grey - - - */ -function configureFromXml( tileSource, xmlDoc ){ - - //parse the xml - if ( !xmlDoc || !xmlDoc.documentElement ) { - throw new Error( $.getString( "Errors.Xml" ) ); - } - - var root = xmlDoc.documentElement, - rootName = root.tagName, - configuration = null, - scale_factors, - formats, - qualities, - i; - - if ( rootName == "info" ) { - - try { - - configuration = { - "ns": root.namespaceURI - }; - - parseXML( root, configuration ); - - return configureFromObject( tileSource, configuration ); - - } catch ( e ) { - throw (e instanceof Error) ? - e : - new Error( $.getString("Errors.IIIF") ); - } - } - - throw new Error( $.getString( "Errors.IIIF" ) ); - -} - - -/** - * @private - * @inner - * @function - */ -function parseXML( node, configuration, property ){ - var i, - value; - if( node.nodeType == 3 && property ){//text node - value = node.nodeValue.trim(); - if( value.match(/^\d*$/)){ - value = Number( value ); - } - if( !configuration[ property ] ){ - configuration[ property ] = value; - }else{ - if( !$.isArray( configuration[ property ] ) ){ - configuration[ property ] = [ configuration[ property ] ]; - } - configuration[ property ].push( value ); - } - } else if( node.nodeType == 1 ){ - for( i = 0; i < node.childNodes.length; i++ ){ - parseXML( node.childNodes[ i ], configuration, node.nodeName ); - } - } -} - - -/** - * @private - * @inner - * @function - * - { - "profile" : "http://library.stanford.edu/iiif/image-api/compliance.html#level1", - "identifier" : "1E34750D-38DB-4825-A38A-B60A345E591C", - "width" : 6000, - "height" : 4000, - "scale_factors" : [ 1, 2, 4 ], - "tile_width" : 1024, - "tile_height" : 1024, - "formats" : [ "jpg", "png" ], - "quality" : [ "native", "grey" ] - } - */ -function configureFromObject( tileSource, configuration ){ - //the image_host property is not part of the iiif standard but is included here to - //allow the info.json and info.xml specify a different server to load the - //images from so we can test the implementation. - if( configuration.image_host ){ - configuration.tilesUrl = configuration.image_host; - } - return configuration; -} - -}( OpenSeadragon ));(function( $ ){ - -/** - * A tilesource implementation for OpenStreetMap. Adopted from Rainer Simon - * project http://github.com/rsimon/seajax-utils. - * - * 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 - * 256x256 levels (see http://gasi.ch/blog/inside-deep-zoom-2). I.e. there is a - * difference of log2(256)=8 levels. - * - * Note 2. Image dimension. According to the OSM Wiki - * (http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Zoom_levels) - * 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 - * pixels. - * - * @class - * @extends OpenSeadragon.TileSource - * @param {Number|Object} width - the pixel width of the image or the idiomatic - * options object which is used instead of positional arguments. - * @param {Number} height - * @param {Number} tileSize - * @param {Number} tileOverlap - * @param {String} tilesUrl - */ -$.OsmTileSource = function( width, height, tileSize, tileOverlap, tilesUrl ) { - var options; - - if( $.isPlainObject( width ) ){ - options = width; - }else{ - options = { - width: arguments[0], - height: arguments[1], - tileSize: arguments[2], - tileOverlap: arguments[3], - tilesUrl: arguments[4] - }; - } - //apply default setting for standard public OpenStreatMaps service - //but allow them to be specified so fliks can host there own instance - //or apply against other services supportting the same standard - if( !options.width || !options.height ){ - options.width = 65572864; - options.height = 65572864; - } - if( !options.tileSize ){ - options.tileSize = 256; - options.tileOverlap = 0; - } - if( !options.tilesUrl ){ - options.tilesUrl = "http://tile.openstreetmap.org/"; - } - options.minLevel = 8; - - $.TileSource.apply( this, [ options ] ); - -}; - -$.extend( $.OsmTileSource.prototype, $.TileSource.prototype, { - - - /** - * Determine if the data and/or url imply the image service is supported by - * this tile source. - * @function - * @name OpenSeadragon.DziTileSource.prototype.supports - * @param {Object|Array} data - * @param {String} optional - url - */ - supports: function( data, url ){ - return ( - data.type && - "openstreetmaps" == data.type - ); - }, - - /** - * - * @function - * @name OpenSeadragon.OsmTileSource.prototype.configure - * @param {Object} data - the raw configuration - * @param {String} url - the url the data was retreived from if any. - * @return {Object} options - A dictionary of keyword arguments sufficient - * to configure this tile sources constructor. - */ - configure: function( data, url ){ - return data; - }, - - - /** - * @function - * @name OpenSeadragon.OsmTileSource.prototype.getTileUrl - * @param {Number} level - * @param {Number} x - * @param {Number} y - */ - getTileUrl: function( level, x, y ) { - return this.tilesUrl + (level - 8) + "/" + x + "/" + y + ".png"; - } -}); - - -}( OpenSeadragon )); -(function( $ ){ - -/** - * 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 - * @extends OpenSeadragon.TileSource - * @param {Number|Object} width - the pixel width of the image or the idiomatic - * options object which is used instead of positional arguments. - * @param {Number} height - * @param {Number} tileSize - * @param {Number} tileOverlap - * @param {String} tilesUrl - */ -$.TmsTileSource = function( width, height, tileSize, tileOverlap, tilesUrl ) { - var options; - - if( $.isPlainObject( width ) ){ - options = width; - }else{ - options = { - width: arguments[0], - height: arguments[1], - tileSize: arguments[2], - tileOverlap: arguments[3], - tilesUrl: arguments[4] - }; - } - // TMS has integer multiples of 256 for width/height and adds buffer - // if necessary -> account for this! - var bufferedWidth = Math.ceil(options.width / 256) * 256, - bufferedHeight = Math.ceil(options.height / 256) * 256, - max; - - // Compute number of zoomlevels in this tileset - if (bufferedWidth > bufferedHeight) { - max = bufferedWidth / 256; - } else { - max = bufferedHeight / 256; - } - options.maxLevel = Math.ceil(Math.log(max)/Math.log(2)) - 1; - options.tileSize = 256; - options.width = bufferedWidth; - options.height = bufferedHeight; - - $.TileSource.apply( this, [ options ] ); - -}; - -$.extend( $.TmsTileSource.prototype, $.TileSource.prototype, { - - - /** - * Determine if the data and/or url imply the image service is supported by - * this tile source. - * @function - * @name OpenSeadragon.TmsTileSource.prototype.supports - * @param {Object|Array} data - * @param {String} optional - url - */ - supports: function( data, url ){ - return ( data.type && "tiledmapservice" == data.type ); - }, - - /** - * - * @function - * @name OpenSeadragon.TmsTileSource.prototype.configure - * @param {Object} data - the raw configuration - * @param {String} url - the url the data was retreived from if any. - * @return {Object} options - A dictionary of keyword arguments sufficient - * to configure this tile sources constructor. - */ - configure: function( data, url ){ - return data; - }, - - - /** - * @function - * @name OpenSeadragon.TmsTileSource.prototype.getTileUrl - * @param {Number} level - * @param {Number} x - * @param {Number} y - */ - getTileUrl: function( level, x, y ) { - // Convert from Deep Zoom definition to TMS zoom definition - var yTiles = this.getNumTiles( level ).y - 1; - - return this.tilesUrl + level + "/" + x + "/" + (yTiles - y) + ".png"; - } -}); - - -}( OpenSeadragon ));/*globals OpenSeadragon */ - -(function( $ ){ - - -/** - * The LegacyTileSource allows simple, traditional image pyramids to be loaded - * into an OpenSeadragon Viewer. Basically, this translates to the historically - * 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 - * resolution image and a high resolution image in standard web formats like - * png or jpg. - * @class - * @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 - * properties but these properties are sufficient for this implementation. - * Additionally, the levels are required to be listed in order from - * smallest to largest. - * @property {Number} aspectRatio - * @property {Number} dimensions - * @property {Number} tileSize - * @property {Number} tileOverlap - * @property {Number} minLevel - * @property {Number} maxLevel - * @property {Array} levels - */ -$.LegacyTileSource = function( levels ) { - - var options, - width, - height; - - if( $.isArray( levels ) ){ - options = { - type: 'legacy-image-pyramid', - levels: levels - }; - } - - //clean up the levels to make sure we support all formats - options.levels = filterFiles( options.levels ); - width = options.levels[ options.levels.length - 1 ].width; - height = options.levels[ options.levels.length - 1 ].height; - - $.extend( true, options, { - width: width, - height: height, - tileSize: Math.max( height, width ), - tileOverlap: 0, - minLevel: 0, - maxLevel: options.levels.length - 1 - }); - - $.TileSource.apply( this, [ options ] ); - - this.levels = options.levels; -}; - -$.extend( $.LegacyTileSource.prototype, $.TileSource.prototype, { - /** - * Determine if the data and/or url imply the image service is supported by - * this tile source. - * @function - * @name OpenSeadragon.DziTileSource.prototype.supports - * @param {Object|Array} data - * @param {String} optional - url - */ - supports: function( data, url ){ - return ( - data.type && - "legacy-image-pyramid" == data.type - ) || ( - data.documentElement && - "legacy-image-pyramid" == data.documentElement.getAttribute('type') - ); - }, - - - /** - * - * @function - * @name OpenSeadragon.DziTileSource.prototype.configure - * @param {Object|XMLDocument} configuration - the raw configuration - * @param {String} dataUrl - the url the data was retreived from if any. - * @return {Object} options - A dictionary of keyword arguments sufficient - * to configure this tile sources constructor. - */ - configure: function( configuration, dataUrl ){ - - var options; - - if( !$.isPlainObject(configuration) ){ - - options = configureFromXML( this, configuration ); - - }else{ - - options = configureFromObject( this, configuration ); - } - - return options; - - }, - - /** - * @function - * @param {Number} level - */ - getLevelScale: function( level ) { - var levelScale = NaN; - if ( level >= this.minLevel && level <= this.maxLevel ){ - levelScale = - this.levels[ level ].width / - this.levels[ this.maxLevel ].width; - } - return levelScale; - }, - - /** - * @function - * @param {Number} level - */ - getNumTiles: function( level ) { - var scale = this.getLevelScale( level ); - if ( scale ){ - return new $.Point( 1, 1 ); - } else { - return new $.Point( 0, 0 ); - } - }, - - /** - * @function - * @param {Number} level - * @param {OpenSeadragon.Point} point - */ - getTileAtPoint: function( level, point ) { - return new $.Point( 0, 0 ); - }, - - - /** - * 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 - * server technologies, and various specifications for building image - * pyramids, this method is here to allow easy integration. - * @function - * @param {Number} level - * @param {Number} x - * @param {Number} y - * @throws {Error} - */ - getTileUrl: function( level, x, y ) { - var url = null; - if( level >= this.minLevel && level <= this.maxLevel ){ - url = this.levels[ level ].url; - } - return url; - } -}); - -/** - * This method removes any files from the Array which dont conform to our - * basic requirements for a 'level' in the LegacyTileSource. - * @private - * @inner - * @function - */ -function filterFiles( files ){ - var filtered = [], - file, - i; - for( i = 0; i < files.length; i++ ){ - file = files[ i ]; - if( file.height && - file.width && - file.url && ( - file.url.toLowerCase().match(/^.*\.(png|jpg|jpeg|gif)$/) || ( - file.mimetype && - file.mimetype.toLowerCase().match(/^.*\/(png|jpg|jpeg|gif)$/) - ) - ) ){ - //This is sufficient to serve as a level - filtered.push({ - url: file.url, - width: Number( file.width ), - height: Number( file.height ) - }); - } - } - - return filtered.sort(function(a,b){ - return a.height - b.height; - }); - -} - -/** - * @private - * @inner - * @function - */ -function configureFromXML( tileSource, xmlDoc ){ - - if ( !xmlDoc || !xmlDoc.documentElement ) { - throw new Error( $.getString( "Errors.Xml" ) ); - } - - var root = xmlDoc.documentElement, - rootName = root.tagName, - conf = null, - levels = [], - level, - i; - - if ( rootName == "image" ) { - - try { - conf = { - type: root.getAttribute( "type" ), - levels: [] - }; - - levels = root.getElementsByTagName( "level" ); - for ( i = 0; i < levels.length; i++ ) { - level = levels[ i ]; - - conf.levels .push({ - url: level.getAttribute( "url" ), - width: parseInt( level.getAttribute( "width" ), 10 ), - height: parseInt( level.getAttribute( "height" ), 10 ) - }); - } - - return configureFromObject( tileSource, conf ); - - } catch ( e ) { - throw (e instanceof Error) ? - e : - new Error( 'Unknown error parsing Legacy Image Pyramid XML.' ); - } - } else if ( rootName == "collection" ) { - throw new Error( 'Legacy Image Pyramid Collections not yet supported.' ); - } else if ( rootName == "error" ) { - throw new Error( 'Error: ' + xmlDoc ); - } - - throw new Error( 'Unknown element ' + rootName ); -} - -/** - * @private - * @inner - * @function - */ -function configureFromObject( tileSource, configuration ){ - - return configuration.levels; - -} - -}( OpenSeadragon )); -/*globals OpenSeadragon */ - -(function( $ ){ - -/** - * @class - * @extends OpenSeadragon.TileSourceCollection - */ -$.TileSourceCollection = function( tileSize, tileSources, rows, layout ) { - - - if( $.isPlainObject( tileSize ) ){ - options = tileSize; - }else{ - options = { - tileSize: arguments[ 0 ], - tileSources: arguments[ 1 ], - rows: arguments[ 2 ], - layout: arguments[ 3 ] - }; - } - - if( !options.layout ){ - options.layout = 'horizontal'; - } - - var minLevel = 0, - levelSize = 1.0, - tilesPerRow = Math.ceil( options.tileSources.length / options.rows ), - longSide = tilesPerRow >= options.rows ? - tilesPerRow : - options.rows; - - if( 'horizontal' == options.layout ){ - options.width = ( options.tileSize ) * tilesPerRow; - options.height = ( options.tileSize ) * options.rows; - } else { - options.height = ( options.tileSize ) * tilesPerRow; - options.width = ( options.tileSize ) * options.rows; - } - - options.tileOverlap = -options.tileMargin; - options.tilesPerRow = tilesPerRow; - - //Set min level to avoid loading sublevels since collection is a - //different kind of abstraction - - while( levelSize < ( options.tileSize ) * longSide ){ - //$.console.log( '%s levelSize %s minLevel %s', options.tileSize * longSide, levelSize, minLevel ); - levelSize = levelSize * 2.0; - minLevel++; - } - options.minLevel = minLevel; - - //for( var name in options ){ - // $.console.log( 'Collection %s %s', name, options[ name ] ); - //} - - $.TileSource.apply( this, [ options ] ); - -}; - -$.extend( $.TileSourceCollection.prototype, $.TileSource.prototype, { - - /** - * @function - * @param {Number} level - * @param {Number} x - * @param {Number} y - */ - getTileBounds: function( level, x, y ) { - var dimensionsScaled = this.dimensions.times( this.getLevelScale( level ) ), - px = this.tileSize * x - this.tileOverlap, - py = this.tileSize * y - this.tileOverlap, - sx = this.tileSize + 1 * this.tileOverlap, - sy = this.tileSize + 1 * this.tileOverlap, - scale = 1.0 / dimensionsScaled.x; - - sx = Math.min( sx, dimensionsScaled.x - px ); - sy = Math.min( sy, dimensionsScaled.y - py ); - - return new $.Rect( px * scale, py * scale, sx * scale, sy * scale ); - }, - - /** - * - * @function - * @name OpenSeadragon.TileSourceCollection.prototype.configure - */ - configure: function( data, url ){ - return; - }, - - - /** - * @function - * @name OpenSeadragon.TileSourceCollection.prototype.getTileUrl - * @param {Number} level - * @param {Number} x - * @param {Number} y - */ - getTileUrl: function( level, x, y ) { - //$.console.log([ level, '/', x, '_', y ].join( '' )); - return null; - } - - - -}); - - -}( OpenSeadragon )); -/*globals OpenSeadragon */ - -(function( $ ){ - -/** - * An enumeration of button states including, REST, GROUP, HOVER, and DOWN - * @static - */ -$.ButtonState = { - REST: 0, - GROUP: 1, - HOVER: 2, - DOWN: 3 -}; - -/** - * Manages events, hover states for individual buttons, tool-tips, as well - * as fading the bottons out when the user has not interacted with them - * for a specified period. - * @class - * @extends OpenSeadragon.EventHandler - * @param {Object} options - * @param {String} options.tooltip Provides context help for the button we the - * user hovers over it. - * @param {String} options.srcRest URL of image to use in 'rest' state - * @param {String} options.srcGroup URL of image to use in 'up' state - * @param {String} options.srcHover URL of image to use in 'hover' state - * @param {String} options.srcDown URL of image to use in 'domn' state - * @param {Element} [options.element] Element to use as a container for the - * button. - * @property {String} tooltip Provides context help for the button we the - * user hovers over it. - * @property {String} srcRest URL of image to use in 'rest' state - * @property {String} srcGroup URL of image to use in 'up' state - * @property {String} srcHover URL of image to use in 'hover' state - * @property {String} srcDown URL of image to use in 'domn' state - * @property {Object} config Configurable settings for this button. DEPRECATED. - * @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 ) { - - var _this = this; - - $.EventHandler.call( this ); - - $.extend( true, this, { - - tooltip: null, - srcRest: null, - srcGroup: null, - srcHover: null, - srcDown: null, - clickTimeThreshold: $.DEFAULT_SETTINGS.clickTimeThreshold, - clickDistThreshold: $.DEFAULT_SETTINGS.clickDistThreshold, - // begin fading immediately - fadeDelay: 0, - // fade over a period of 2 seconds - fadeLength: 2000, - onPress: null, - onRelease: null, - onClick: null, - onEnter: null, - onExit: null, - onFocus: null, - onBlur: null - - }, options ); - - this.element = options.element || $.makeNeutralElement( "button" ); - this.element.href = this.element.href || '#'; - - //if the user has specified the element to bind the control to explicitly - //then do not add the default control images - if( !options.element ){ - this.imgRest = $.makeTransparentImage( this.srcRest ); - this.imgGroup = $.makeTransparentImage( this.srcGroup ); - this.imgHover = $.makeTransparentImage( this.srcHover ); - this.imgDown = $.makeTransparentImage( this.srcDown ); - - this.element.appendChild( this.imgRest ); - this.element.appendChild( this.imgGroup ); - this.element.appendChild( this.imgHover ); - 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( "onRelease", this.onRelease ); - this.addHandler( "onClick", this.onClick ); - this.addHandler( "onEnter", this.onEnter ); - this.addHandler( "onExit", this.onExit ); - this.addHandler( "onFocus", this.onFocus ); - this.addHandler( "onBlur", this.onBlur ); - - this.currentState = $.ButtonState.GROUP; - - this.fadeBeginTime = null; - this.shouldFade = false; - - this.element.style.display = "inline-block"; - this.element.style.position = "relative"; - this.element.title = this.tooltip; - - this.tracker = new $.MouseTracker({ - - element: this.element, - clickTimeThreshold: this.clickTimeThreshold, - clickDistThreshold: this.clickDistThreshold, - - enterHandler: function( tracker, position, buttonDownElement, buttonDownAny ) { - if ( buttonDownElement ) { - inTo( _this, $.ButtonState.DOWN ); - _this.raiseEvent( "onEnter", _this ); - } else if ( !buttonDownAny ) { - inTo( _this, $.ButtonState.HOVER ); - } - }, - - focusHandler: function( tracker, position, buttonDownElement, buttonDownAny ) { - this.enterHandler( tracker, position, buttonDownElement, buttonDownAny ); - _this.raiseEvent( "onFocus", _this ); - }, - - exitHandler: function( tracker, position, buttonDownElement, buttonDownAny ) { - outTo( _this, $.ButtonState.GROUP ); - if ( buttonDownElement ) { - _this.raiseEvent( "onExit", _this ); - } - }, - - blurHandler: function( tracker, position, buttonDownElement, buttonDownAny ) { - this.exitHandler( tracker, position, buttonDownElement, buttonDownAny ); - _this.raiseEvent( "onBlur", _this ); - }, - - pressHandler: function( tracker, position ) { - inTo( _this, $.ButtonState.DOWN ); - _this.raiseEvent( "onPress", _this ); - }, - - releaseHandler: function( tracker, position, insideElementPress, insideElementRelease ) { - if ( insideElementPress && insideElementRelease ) { - outTo( _this, $.ButtonState.HOVER ); - _this.raiseEvent( "onRelease", _this ); - } else if ( insideElementPress ) { - outTo( _this, $.ButtonState.GROUP ); - } else { - inTo( _this, $.ButtonState.HOVER ); - } - }, - - clickHandler: function( tracker, position, quick, shift ) { - if ( quick ) { - _this.raiseEvent("onClick", _this); - } - }, - - keyHandler: function( tracker, key ){ - //console.log( "%s : handling key %s!", _this.tooltip, key); - if( 13 === key ){ - _this.raiseEvent( "onClick", _this ); - _this.raiseEvent( "onRelease", _this ); - return false; - } - return true; - } - - }).setTracking( true ); - - outTo( this, $.ButtonState.REST ); -}; - -$.extend( $.Button.prototype, $.EventHandler.prototype, { - - /** - * TODO: Determine what this function is intended to do and if it's actually - * useful as an API point. - * @function - * @name OpenSeadragon.Button.prototype.notifyGroupEnter - */ - notifyGroupEnter: function() { - inTo( this, $.ButtonState.GROUP ); - }, - - /** - * TODO: Determine what this function is intended to do and if it's actually - * useful as an API point. - * @function - * @name OpenSeadragon.Button.prototype.notifyGroupExit - */ - notifyGroupExit: function() { - outTo( this, $.ButtonState.REST ); - }, - - disable: function(){ - this.notifyGroupExit(); - this.element.disabled = true; - $.setElementOpacity( this.element, 0.2, true ); - }, - - enable: function(){ - this.element.disabled = false; - $.setElementOpacity( this.element, 1.0, true ); - this.notifyGroupEnter(); - } - -}); - - -function scheduleFade( button ) { - window.setTimeout(function(){ - updateFade( button ); - }, 20 ); -} - -function updateFade( button ) { - var currentTime, - deltaTime, - opacity; - - if ( button.shouldFade ) { - currentTime = +new Date(); - deltaTime = currentTime - button.fadeBeginTime; - opacity = 1.0 - deltaTime / button.fadeLength; - opacity = Math.min( 1.0, opacity ); - opacity = Math.max( 0.0, opacity ); - - if( button.imgGroup ){ - $.setElementOpacity( button.imgGroup, opacity, true ); - } - if ( opacity > 0 ) { - // fade again - scheduleFade( button ); - } - } -} - -function beginFading( button ) { - button.shouldFade = true; - button.fadeBeginTime = +new Date() + button.fadeDelay; - window.setTimeout( function(){ - scheduleFade( button ); - }, button.fadeDelay ); -} - -function stopFading( button ) { - button.shouldFade = false; - if( button.imgGroup ){ - $.setElementOpacity( button.imgGroup, 1.0, true ); - } -} - -function inTo( button, newState ) { - - if( button.element.disabled ){ - return; - } - - if ( newState >= $.ButtonState.GROUP && - button.currentState == $.ButtonState.REST ) { - stopFading( button ); - button.currentState = $.ButtonState.GROUP; - } - - if ( newState >= $.ButtonState.HOVER && - button.currentState == $.ButtonState.GROUP ) { - if( button.imgHover ){ - button.imgHover.style.visibility = ""; - } - button.currentState = $.ButtonState.HOVER; - } - - if ( newState >= $.ButtonState.DOWN && - button.currentState == $.ButtonState.HOVER ) { - if( button.imgDown ){ - button.imgDown.style.visibility = ""; - } - button.currentState = $.ButtonState.DOWN; - } -} - - -function outTo( button, newState ) { - - if( button.element.disabled ){ - return; - } - - if ( newState <= $.ButtonState.HOVER && - button.currentState == $.ButtonState.DOWN ) { - if( button.imgDown ){ - button.imgDown.style.visibility = "hidden"; - } - button.currentState = $.ButtonState.HOVER; - } - - if ( newState <= $.ButtonState.GROUP && - button.currentState == $.ButtonState.HOVER ) { - if( button.imgHover ){ - button.imgHover.style.visibility = "hidden"; - } - button.currentState = $.ButtonState.GROUP; - } - - if ( newState <= $.ButtonState.REST && - button.currentState == $.ButtonState.GROUP ) { - beginFading( button ); - button.currentState = $.ButtonState.REST; - } -} - - - -}( OpenSeadragon )); -/*globals OpenSeadragon */ - -(function( $ ){ -/** - * Manages events on groups of buttons. - * @class - * @param {Object} options - a dictionary of settings applied against the entire - * group of buttons - * @param {Array} options.buttons Array of buttons - * @param {Element} [options.group] Element to use as the container, - * @param {Object} options.config Object with Viewer settings ( TODO: is - * 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 ) { - - $.extend( true, this, { - buttons: [], - clickTimeThreshold: $.DEFAULT_SETTINGS.clickTimeThreshold, - clickDistThreshold: $.DEFAULT_SETTINGS.clickDistThreshold, - labelText: "" - }, options ); - - // copy the botton elements - var buttons = this.buttons.concat([]), - _this = this, - i; - - this.element = options.element || $.makeNeutralElement( "fieldgroup" ); - - if( !options.group ){ - this.label = $.makeNeutralElement( "label" ); - //TODO: support labels for ButtonGroups - //this.label.innerHTML = this.labelText; - this.element.style.display = "inline-block"; - this.element.appendChild( this.label ); - for ( i = 0; i < buttons.length; i++ ) { - this.element.appendChild( buttons[ i ].element ); - } - } - - this.tracker = new $.MouseTracker({ - element: this.element, - clickTimeThreshold: this.clickTimeThreshold, - clickDistThreshold: this.clickDistThreshold, - enterHandler: function() { - var i; - for ( i = 0; i < _this.buttons.length; i++ ) { - _this.buttons[ i ].notifyGroupEnter(); - } - }, - exitHandler: function() { - var i, - buttonDownElement = arguments.length > 2 ? - arguments[ 2 ] : - null; - if ( !buttonDownElement ) { - for ( i = 0; i < _this.buttons.length; i++ ) { - _this.buttons[ i ].notifyGroupExit(); - } - } - }, - releaseHandler: function() { - var i, - insideElementRelease = arguments.length > 3 ? - arguments[ 3 ] : - null; - if ( !insideElementRelease ) { - for ( i = 0; i < _this.buttons.length; i++ ) { - _this.buttons[ i ].notifyGroupExit(); - } - } - } - }).setTracking( true ); -}; - -$.ButtonGroup.prototype = { - - /** - * TODO: Figure out why this is used on the public API and if a more useful - * api can be created. - * @function - * @name OpenSeadragon.ButtonGroup.prototype.emulateEnter - */ - emulateEnter: function() { - this.tracker.enterHandler(); - }, - - /** - * TODO: Figure out why this is used on the public API and if a more useful - * api can be created. - * @function - * @name OpenSeadragon.ButtonGroup.prototype.emulateExit - */ - emulateExit: function() { - this.tracker.exitHandler(); - } -}; - - -}( OpenSeadragon )); -/*globals OpenSeadragon */ - -(function( $ ){ - -/** - * A Rectangle really represents a 2x2 matrix where each row represents a - * 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. - * - * @class - * @param {Number} x The vector component 'x'. - * @param {Number} y The vector component 'y'. - * @param {Number} width The vector component 'height'. - * @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 ) { - this.x = typeof ( x ) == "number" ? x : 0; - this.y = typeof ( y ) == "number" ? y : 0; - this.width = typeof ( width ) == "number" ? width : 0; - this.height = typeof ( height ) == "number" ? height : 0; -}; - -$.Rect.prototype = { - - /** - * The aspect ratio is simply the ratio of width to height. - * @function - * @returns {Number} The ratio of width to height. - */ - getAspectRatio: function() { - return this.width / this.height; - }, - - /** - * Provides the coordinates of the upper-left corner of the rectanglea s a - * point. - * @function - * @returns {OpenSeadragon.Point} The coordinate of the upper-left corner of - * the rectangle. - */ - getTopLeft: function() { - return new $.Point( this.x, this.y ); - }, - - /** - * Provides the coordinates of the bottom-right corner of the rectangle as a - * point. - * @function - * @returns {OpenSeadragon.Point} The coordinate of the bottom-right corner of - * the rectangle. - */ - getBottomRight: function() { - return new $.Point( - this.x + this.width, - this.y + this.height - ); - }, - - /** - * Computes the center of the rectangle. - * @function - * @returns {OpenSeadragon.Point} The center of the rectangle as represnted - * as represented by a 2-dimensional vector (x,y) - */ - getCenter: function() { - return new $.Point( - this.x + this.width / 2.0, - this.y + this.height / 2.0 - ); - }, - - /** - * Returns the width and height component as a vector OpenSeadragon.Point - * @function - * @returns {OpenSeadragon.Point} The 2 dimensional vector represnting the - * the width and height of the rectangle. - */ - getSize: function() { - return new $.Point( this.width, this.height ); - }, - - /** - * Determines if two Rectanlges have equivalent components. - * @function - * @param {OpenSeadragon.Rect} rectangle The Rectangle to compare to. - * @return {Boolean} 'true' if all components are equal, otherwise 'false'. - */ - equals: function( other ) { - return ( other instanceof $.Rect ) && - ( this.x === other.x ) && - ( this.y === other.y ) && - ( this.width === other.width ) && - ( this.height === other.height ); - }, - - /** - * Provides a string representation of the retangle which is useful for - * debugging. - * @function - * @returns {String} A string representation of the rectangle. - */ - toString: function() { - return "[" + - Math.round(this.x*100) + "," + - Math.round(this.y*100) + "," + - Math.round(this.width*100) + "x" + - Math.round(this.height*100) + - "]"; - } -}; - - -}( OpenSeadragon )); - -(function( $ ){ - -// dictionary from id to private properties -var THIS = {}; - -/** - * The CollectionDrawer is a reimplementation if the Drawer API that - * focuses on allowing a viewport to be redefined as a collection - * 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 - * source. - * - * This idea is a reexpression of the idea of dzi collections - * which allows a clearer algorithm to reuse the tile sources already - * supported by OpenSeadragon, in heterogenious or homogenious - * sequences just like mixed groups already supported by the viewer - * for the purpose of image sequnces. - * - * TODO: The difficult part of this feature is figuring out how to express - * this functionality as a combination of the functionality already - * provided by Drawer, Viewport, TileSource, and Navigator. It may - * require better abstraction at those points in order to effeciently - * reuse those paradigms. - */ -$.ReferenceStrip = function( options ){ - - var _this = this, - viewer = options.viewer, - viewerSize = $.getElementSize( viewer.element ), - miniViewer, - minPixelRatio, - element, - style, - i; - - //We may need to create a new element and id if they did not - //provide the id for the existing element - if( !options.id ){ - options.id = 'referencestrip-' + (+new Date()); - this.element = $.makeNeutralElement( "div" ); - this.element.id = options.id; - this.element.className = 'referencestrip'; - } - - options = $.extend( true, { - sizeRatio: $.DEFAULT_SETTINGS.referenceStripSizeRatio, - position: $.DEFAULT_SETTINGS.referenceStripPosition, - scroll: $.DEFAULT_SETTINGS.referenceStripScroll, - clickTimeThreshold: $.DEFAULT_SETTINGS.clickTimeThreshold - }, options, { - //required overrides - element: this.element, - //These need to be overridden to prevent recursion since - //the navigator is a viewer and a viewer has a navigator - showNavigator: false, - mouseNavEnabled: false, - showNavigationControl: false, - showSequenceControl: false - }); - - $.extend( this, options ); - //Private state properties - THIS[ this.id ] = { - "animating": false - }; - - this.minPixelRatio = this.viewer.minPixelRatio; - - style = thie.element.style; - style.marginTop = '0px'; - style.marginRight = '0px'; - style.marginBottom = '0px'; - style.marginLeft = '0px'; - style.left = '0px'; - style.bottom = '0px'; - style.border = '0px'; - style.background = '#000'; - style.position = 'relative'; - - $.setElementOpacity( this.element, 0.8 ); - - this.viewer = viewer; - this.innerTracker = new $.MouseTracker({ - element: this.element, - dragHandler: $.delegate( this, onStripDrag ), - scrollHandler: $.delegate( this, onStripScroll ), - enterHandler: $.delegate( this, onStripEnter ), - exitHandler: $.delegate( this, onStripExit ), - keyHandler: $.delegate( this, onKeyPress ) - }).setTracking( true ); - - //Controls the position and orientation of the reference strip and sets the - //appropriate width and height - if( options.width && options.height ){ - this.element.style.width = options.width + 'px'; - this.element.style.height = options.height + 'px'; - viewer.addControl( - this.element, - $.ControlAnchor.BOTTOM_LEFT - ); - } else { - if( "horizontal" == options.scroll ){ - this.element.style.width = ( - viewerSize.x * - options.sizeRatio * - viewer.tileSources.length - ) + ( 12 * viewer.tileSources.length ) + 'px'; - - this.element.style.height = ( - viewerSize.y * - options.sizeRatio - ) + 'px'; - - viewer.addControl( - this.element, - $.ControlAnchor.BOTTOM_LEFT - ); - }else { - this.element.style.height = ( - viewerSize.y * - options.sizeRatio * - viewer.tileSources.length - ) + ( 12 * viewer.tileSources.length ) + 'px'; - - this.element.style.width = ( - viewerSize.x * - options.sizeRatio - ) + 'px'; - - viewer.addControl( - this.element, - $.ControlAnchor.TOP_LEFT - ); - - } - } - - this.panelWidth = ( viewerSize.x * this.sizeRatio ) + 8; - this.panelHeight = ( viewerSize.y * this.sizeRatio ) + 8; - this.panels = []; - - /*jshint loopfunc:true*/ - for( i = 0; i < viewer.tileSources.length; i++ ){ - - element = $.makeNeutralElement('div'); - element.id = this.element.id + "-" + i; - - element.style.width = _this.panelWidth + 'px'; - element.style.height = _this.panelHeight + 'px'; - element.style.display = 'inline'; - element.style.float = 'left'; //Webkit - element.style.cssFloat = 'left'; //Firefox - element.style.styleFloat = 'left'; //IE - element.style.padding = '2px'; - - element.innerTracker = new $.MouseTracker({ - element: element, - clickTimeThreshold: this.clickTimeThreshold, - clickDistThreshold: this.clickDistThreshold, - pressHandler: function( tracker ){ - tracker.dragging = +new Date(); - }, - releaseHandler: function( tracker, position, insideElementPress, insideElementRelease ){ - var id = tracker.element.id, - page = Number( id.split( '-' )[ 2 ] ), - now = +new Date(); - - if ( insideElementPress && - insideElementRelease && - tracker.dragging && - ( now - tracker.dragging ) < tracker.clickTimeThreshold ){ - tracker.dragging = null; - viewer.goToPage( page ); - } - } - }).setTracking( true ); - - this.element.appendChild( element ); - - element.activePanel = false; - - this.panels.push( element ); - - } - loadPanels( this, this.scroll == 'vertical' ? viewerSize.y : viewerSize.y, 0); - this.setFocus( 0 ); - -}; - -$.extend( $.ReferenceStrip.prototype, $.EventHandler.prototype, $.Viewer.prototype, { - - setFocus: function( page ){ - var element = $.getElement( this.element.id + '-' + page ), - viewerSize = $.getElementSize( this.viewer.canvas ), - 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; - - if ( this.currentSelected !== element ){ - if( this.currentSelected ){ - this.currentSelected.style.background = '#000'; - } - this.currentSelected = element; - this.currentSelected.style.background = '#999'; - - if( 'horizontal' == this.scroll ){ - //right left - offset = (Number(page)) * ( this.panelWidth + 3 ); - if( offset > offsetLeft + viewerSize.x - this.panelWidth){ - offset = Math.min(offset, (scrollWidth - viewerSize.x)); - this.element.style.marginLeft = -offset + 'px'; - loadPanels( this, viewerSize.x, -offset ); - }else if( offset < offsetLeft ){ - offset = Math.max(0, offset - viewerSize.x / 2); - this.element.style.marginLeft = -offset + 'px'; - loadPanels( this, viewerSize.x, -offset ); - } - }else{ - offset = (Number(page) ) * ( this.panelHeight + 3 ); - if( offset > offsetTop + viewerSize.y - this.panelHeight){ - offset = Math.min(offset, (scrollHeight - viewerSize.y)); - this.element.style.marginTop = -offset + 'px'; - loadPanels( this, viewerSize.y, -offset ); - }else if( offset < offsetTop ){ - offset = Math.max(0, offset - viewerSize.y / 2); - this.element.style.marginTop = -offset + 'px'; - loadPanels( this, viewerSize.y, -offset ); - } - } - - this.currentPage = page; - $.getElement( element.id + '-displayregion' ).focus(); - onStripEnter.call( this, this.innerTracker ); - } - }, - /** - * @function - * @name OpenSeadragon.Navigator.prototype.update - */ - update: function( viewport ){ - - if( THIS[ this.id ].animating ){ - $.console.log('image reference strip update'); - return true; - } - return false; - - } - -}); - - - - -/** - * @private - * @inner - * @function - */ -function onStripDrag( tracker, position, delta, shift ) { - - var offsetLeft = Number(this.element.style.marginLeft.replace('px','')), - offsetTop = Number(this.element.style.marginTop.replace('px','')), - scrollWidth = Number(this.element.style.width.replace('px','')), - scrollHeight = Number(this.element.style.height.replace('px','')), - viewerSize = $.getElementSize( this.viewer.canvas ); - this.dragging = true; - if ( this.element ) { - if( 'horizontal' == this.scroll ){ - if ( -delta.x > 0 ) { - //forward - if( offsetLeft > -(scrollWidth - viewerSize.x)){ - this.element.style.marginLeft = ( offsetLeft + (delta.x * 2) ) + 'px'; - loadPanels( this, viewerSize.x, offsetLeft + (delta.x * 2) ); - } - } else if ( -delta.x < 0 ) { - //reverse - if( offsetLeft < 0 ){ - this.element.style.marginLeft = ( offsetLeft + (delta.x * 2) ) + 'px'; - loadPanels( this, viewerSize.x, offsetLeft + (delta.x * 2) ); - } - } - }else{ - if ( -delta.y > 0 ) { - //forward - if( offsetTop > -(scrollHeight - viewerSize.y)){ - this.element.style.marginTop = ( offsetTop + (delta.y * 2) ) + 'px'; - loadPanels( this, viewerSize.y, offsetTop + (delta.y * 2) ); - } - } else if ( -delta.y < 0 ) { - //reverse - if( offsetTop < 0 ){ - this.element.style.marginTop = ( offsetTop + (delta.y * 2) ) + 'px'; - loadPanels( this, viewerSize.y, offsetTop + (delta.y * 2) ); - } - } - } - } - return false; - -} - - - -/** - * @private - * @inner - * @function - */ -function onStripScroll( tracker, position, scroll, shift ) { - var offsetLeft = Number(this.element.style.marginLeft.replace('px','')), - offsetTop = Number(this.element.style.marginTop.replace('px','')), - scrollWidth = Number(this.element.style.width.replace('px','')), - scrollHeight = Number(this.element.style.height.replace('px','')), - viewerSize = $.getElementSize( this.viewer.canvas ); - if ( this.element ) { - if( 'horizontal' == this.scroll ){ - if ( scroll > 0 ) { - //forward - if( offsetLeft > -(scrollWidth - viewerSize.x)){ - this.element.style.marginLeft = ( offsetLeft - (scroll * 60) ) + 'px'; - loadPanels( this, viewerSize.x, offsetLeft - (scroll * 60) ); - } - } else if ( scroll < 0 ) { - //reverse - if( offsetLeft < 0 ){ - this.element.style.marginLeft = ( offsetLeft - (scroll * 60) ) + 'px'; - loadPanels( this, viewerSize.x, offsetLeft - (scroll * 60) ); - } - } - }else{ - if ( scroll < 0 ) { - //scroll up - if( offsetTop > viewerSize.y - scrollHeight ){ - this.element.style.marginTop = ( offsetTop + (scroll * 60) ) + 'px'; - loadPanels( this, viewerSize.y, offsetTop + (scroll * 60) ); - } - } else if ( scroll > 0 ) { - //scroll dowm - if( offsetTop < 0 ){ - this.element.style.marginTop = ( offsetTop + (scroll * 60) ) + 'px'; - loadPanels( this, viewerSize.y, offsetTop + (scroll * 60) ); - } - } - } - } - //cancels event - return false; -} - - -function loadPanels(strip, viewerSize, scroll){ - var panelSize, - activePanelsStart, - activePanelsEnd, - miniViewer, - style, - i; - if( 'horizontal' == strip.scroll ){ - panelSize = strip.panelWidth; - }else{ - panelSize = strip.panelHeight; - } - activePanelsStart = Math.ceil( viewerSize / panelSize ) + 5; - activePanelsEnd = Math.ceil( (Math.abs(scroll) + viewerSize ) / panelSize ) + 1; - activePanelsStart = activePanelsEnd - activePanelsStart; - activePanelsStart = activePanelsStart < 0 ? 0 : activePanelsStart; - - for( i = activePanelsStart; i < activePanelsEnd && i < strip.panels.length; i++ ){ - element = strip.panels[ i ]; - if ( !element.activePanel ){ - miniViewer = new $.Viewer( { - id: element.id, - tileSources: [ strip.viewer.tileSources[ i ] ], - element: element, - navigatorSizeRatio: strip.sizeRatio, - minPixelRatio: strip.minPixelRatio, - showNavigator: false, - mouseNavEnabled: false, - showNavigationControl: false, - showSequenceControl: false - } ); - - miniViewer.displayRegion = $.makeNeutralElement( "textarea" ); - miniViewer.displayRegion.id = element.id + '-displayregion'; - miniViewer.displayRegion.className = 'displayregion'; - - style = miniViewer.displayRegion.style; - style.position = 'relative'; - style.top = '0px'; - style.left = '0px'; - style.fontSize = '0px'; - style.overflow = 'hidden'; - style.float = 'left'; //Webkit - style.cssFloat = 'left'; //Firefox - style.styleFloat = 'left'; //IE - style.zIndex = 999999999; - style.cursor = 'default'; - style.width = ( strip.panelWidth - 4 ) + 'px'; - style.height = ( strip.panelHeight - 4 ) + 'px'; - - miniViewer.displayRegion.innerTracker = new $.MouseTracker({ - element: miniViewer.displayRegion - }); - - element.getElementsByTagName('form')[ 0 ].appendChild( - miniViewer.displayRegion - ); - - element.activePanel = true; - } - } -} - - -/** - * @private - * @inner - * @function - */ -function onStripEnter( tracker ) { - - //$.setElementOpacity(tracker.element, 0.8); - - //tracker.element.style.border = '1px solid #555'; - //tracker.element.style.background = '#000'; - - if( 'horizontal' == this.scroll ){ - - //tracker.element.style.paddingTop = "0px"; - tracker.element.style.marginBottom = "0px"; - - } else { - - //tracker.element.style.paddingRight = "0px"; - tracker.element.style.marginLeft = "0px"; - - } - return false; -} - - -/** - * @private - * @inner - * @function - */ -function onStripExit( tracker ) { - - 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 ){ - - //tracker.element.style.paddingTop = "10px"; - tracker.element.style.marginBottom = "-" + ( $.getElementSize( tracker.element ).y / 2 ) + "px"; - - } else { - - //tracker.element.style.paddingRight = "10px"; - tracker.element.style.marginLeft = "-" + ( $.getElementSize( tracker.element ).x / 2 )+ "px"; - - } - return false; -} - - - -/** - * @private - * @inner - * @function - */ -function onKeyPress( tracker, keyCode, shiftKey ){ - //console.log( keyCode ); - - switch( keyCode ){ - case 61://=|+ - onStripScroll.call(this, this.tracker, null, 1, null); - return false; - case 45://-|_ - onStripScroll.call(this, this.tracker, null, -1, null); - return false; - case 48://0|) - case 119://w - case 87://W - case 38://up arrow - onStripScroll.call(this, this.tracker, null, 1, null); - return false; - case 115://s - case 83://S - case 40://down arrow - onStripScroll.call(this, this.tracker, null, -1, null); - return false; - case 97://a - case 37://left arrow - onStripScroll.call(this, this.tracker, null, -1, null); - return false; - case 100://d - case 39://right arrow - onStripScroll.call(this, this.tracker, null, 1, null); - return false; - default: - //console.log( 'navigator keycode %s', keyCode ); - return true; - } -} - - - -}( OpenSeadragon ));/*globals OpenSeadragon */ - -(function( $ ){ - -/** - * A display rectanlge is very similar to the OpenSeadragon.Rect but adds two - * fields, 'minLevel' and 'maxLevel' which denote the supported zoom levels - * for this rectangle. - * @class - * @extends OpenSeadragon.Rect - * @param {Number} x The vector component 'x'. - * @param {Number} y The vector component 'y'. - * @param {Number} width The vector component 'height'. - * @param {Number} height The vector component 'width'. - * @param {Number} minLevel The lowest 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 ) { - $.Rect.apply( this, [ x, y, width, height ] ); - - this.minLevel = minLevel; - this.maxLevel = maxLevel; -}; - -$.extend( $.DisplayRect.prototype, $.Rect.prototype ); - -}( OpenSeadragon )); -/*globals OpenSeadragon */ - -(function( $ ){ - -/** - * @class - * @param {Object} options - Spring configuration settings. - * @param {Number} options.initial - Initial value of spring, default to 0 so - * spring is not in motion initally by default. - * @param {Number} options.springStiffness - Spring stiffness. - * @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 ) { - var args = arguments; - - if( typeof( options ) != 'object' ){ - //allows backward compatible use of ( initialValue, config ) as - //constructor parameters - options = { - initial: args.length && typeof ( args[ 0 ] ) == "number" ? - args[ 0 ] : - 0, - springStiffness: args.length > 1 ? - args[ 1 ].springStiffness : - 5.0, - animationTime: args.length > 1 ? - args[ 1 ].animationTime : - 1.5 - }; - } - - $.extend( true, this, options); - - - this.current = { - value: typeof ( this.initial ) == "number" ? - this.initial : - 0, - time: new Date().getTime() // always work in milliseconds - }; - - this.start = { - value: this.current.value, - time: this.current.time - }; - - this.target = { - value: this.current.value, - time: this.current.time - }; -}; - -$.Spring.prototype = { - - /** - * @function - * @param {Number} target - */ - resetTo: function( target ) { - this.target.value = target; - this.target.time = this.current.time; - this.start.value = this.target.value; - this.start.time = this.target.time; - }, - - /** - * @function - * @param {Number} target - */ - springTo: function( target ) { - this.start.value = this.current.value; - this.start.time = this.current.time; - this.target.value = target; - this.target.time = this.start.time + 1000 * this.animationTime; - }, - - /** - * @function - * @param {Number} delta - */ - shiftBy: function( delta ) { - this.start.value += delta; - this.target.value += delta; - }, - - /** - * @function - */ - update: function() { - this.current.time = new Date().getTime(); - this.current.value = (this.current.time >= this.target.time) ? - this.target.value : - this.start.value + - ( this.target.value - this.start.value ) * - transform( - this.springStiffness, - ( this.current.time - this.start.time ) / - ( this.target.time - this.start.time ) - ); - } -}; - -/** - * @private - */ -function transform( stiffness, x ) { - return ( 1.0 - Math.exp( stiffness * -x ) ) / - ( 1.0 - Math.exp( -stiffness ) ); -} - -}( OpenSeadragon )); -/*globals OpenSeadragon */ - -(function( $ ){ - -/** - * @class - * @param {Number} level The zoom level this tile belongs to. - * @param {Number} x The vector component 'x'. - * @param {Number} y The vector component 'y'. - * @param {OpenSeadragon.Point} bounds Where this tile fits, in normalized - * coordinates. - * @param {Boolean} exists Is this tile a part of a sparse image? ( Also has - * this tile failed to load? ) - * @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) { - this.level = level; - this.x = x; - this.y = y; - this.bounds = bounds; - this.exists = exists; - this.url = url; - this.loaded = false; - this.loading = false; - - this.element = null; - this.image = null; - - this.style = null; - this.position = null; - this.size = null; - this.blendStart = null; - this.opacity = null; - this.distance = null; - this.visibility = null; - - this.beingDrawn = false; - this.lastTouchTime = 0; -}; - -$.Tile.prototype = { - - /** - * Provides a string representation of this tiles level and (x,y) - * components. - * @function - * @returns {String} - */ - toString: function() { - return this.level + "/" + this.x + "_" + this.y; - }, - - /** - * Renders the tile in an html container. - * @function - * @param {Element} container - */ - drawHTML: function( container ) { - - var containerSize = $.getElementSize( container ); - - if ( !this.loaded || !this.image ) { - $.console.warn( - "Attempting to draw tile %s when it's not yet loaded.", - this.toString() - ); - 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 - // content during animation of the container size. - - if ( !this.element ) { - this.element = $.makeNeutralElement("img"); - this.element.src = this.url; - this.element.style.msInterpolationMode = "nearest-neighbor"; - - this.style = this.element.style; - this.style.position = "absolute"; - } - if ( this.element.parentNode != container ) { - container.appendChild( this.element ); - } - - 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 ); - - - }, - - /** - * Renders the tile in a canvas-based context. - * @function - * @param {Canvas} context - */ - drawCanvas: function( context ) { - - var position = this.position, - size = this.size; - - if ( !this.loaded || !this.image ) { - $.console.warn( - "Attempting to draw tile %s when it's not yet loaded.", - this.toString() - ); - return; - } - context.globalAlpha = this.opacity; - - context.save(); - - //if we are supposed to b rendering fully opaque rectangle, - //ie its done fading or fading is turned off, and if we are drawing - //an image with an alpha channel, then the only way - //to avoid seeing the tile underneath is to clear the rectangle - if( context.globalAlpha == 1 && this.image.src.match('.png') ){ - //clearing only the inside of the rectangle occupied - //by the png prevents edge flikering - context.clearRect( - position.x+1, - position.y+1, - size.x-2, - size.y-2 - ); - - } - - context.drawImage( this.image, position.x, position.y, size.x, size.y ); - - context.restore(); - }, - - /** - * Removes tile from it's contianer. - * @function - */ - unload: function() { - if ( this.element && this.element.parentNode ) { - this.element.parentNode.removeChild( this.element ); - } - - this.element = null; - this.image = null; - this.loaded = false; - this.loading = false; - } -}; - -}( OpenSeadragon )); -/*globals OpenSeadragon */ - -(function( $ ){ - - /** - * An enumeration of positions that an overlay may be assigned relative - * to the viewport including CENTER, TOP_LEFT (default), TOP, TOP_RIGHT, - * RIGHT, BOTTOM_RIGHT, BOTTOM, BOTTOM_LEFT, and LEFT. - * @static - */ - $.OverlayPlacement = { - CENTER: 0, - TOP_LEFT: 1, - TOP: 2, - TOP_RIGHT: 3, - RIGHT: 4, - BOTTOM_RIGHT: 5, - BOTTOM: 6, - BOTTOM_LEFT: 7, - LEFT: 8 - }; - - /** - * An Overlay provides a - * @class - */ - $.Overlay = function( element, location, placement ) { - this.element = element; - this.scales = location instanceof $.Rect; - this.bounds = new $.Rect( - location.x, - location.y, - location.width, - location.height - ); - this.position = new $.Point( - location.x, - location.y - ); - this.size = new $.Point( - location.width, - location.height - ); - this.style = element.style; - // rects are always top-left - this.placement = location instanceof $.Point ? - placement : - $.OverlayPlacement.TOP_LEFT; - }; - - $.Overlay.prototype = { - - /** - * @function - * @param {OpenSeadragon.OverlayPlacement} position - * @param {OpenSeadragon.Point} size - */ - adjust: function( position, size ) { - switch ( this.placement ) { - case $.OverlayPlacement.TOP_LEFT: - break; - case $.OverlayPlacement.TOP: - position.x -= size.x / 2; - break; - case $.OverlayPlacement.TOP_RIGHT: - position.x -= size.x; - break; - case $.OverlayPlacement.RIGHT: - position.x -= size.x; - position.y -= size.y / 2; - break; - case $.OverlayPlacement.BOTTOM_RIGHT: - position.x -= size.x; - position.y -= size.y; - break; - case $.OverlayPlacement.BOTTOM: - position.x -= size.x / 2; - position.y -= size.y; - break; - case $.OverlayPlacement.BOTTOM_LEFT: - position.y -= size.y; - break; - case $.OverlayPlacement.LEFT: - position.y -= size.y / 2; - break; - default: - case $.OverlayPlacement.CENTER: - position.x -= size.x / 2; - position.y -= size.y / 2; - break; - } - }, - - /** - * @function - */ - destroy: function() { - var element = this.element, - style = this.style; - - if ( element.parentNode ) { - element.parentNode.removeChild( element ); - //this should allow us to preserve overlays when required between - //pages - if( element.prevElementParent ){ - style.display = 'none'; - //element.prevElementParent.insertBefore( - // element, - // element.prevNextSibling - //); - document.body.appendChild( element ); - } - } - - style.top = ""; - style.left = ""; - style.position = ""; - - if ( this.scales ) { - style.width = ""; - style.height = ""; - } - }, - - /** - * @function - * @param {Element} container - */ - drawHTML: function( container ) { - var element = this.element, - style = this.style, - scales = this.scales, - position, - size; - - if ( element.parentNode != container ) { - //save the source parent for later if we need it - element.prevElementParent = element.parentNode; - element.prevNextSibling = element.nextSibling; - container.appendChild( element ); - } - - if ( !scales ) { - this.size = $.getElementSize( element ); - } - - position = this.position; - size = this.size; - - this.adjust( position, size ); - - position = position.apply( Math.floor ); - size = size.apply( Math.ceil ); - - 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"; - } - }, - - /** - * @function - * @param {OpenSeadragon.Point|OpenSeadragon.Rect} location - * @param {OpenSeadragon.OverlayPlacement} position - */ - update: function( location, placement ) { - this.scales = location instanceof $.Rect; - this.bounds = new $.Rect( - location.x, - location.y, - location.width, - location.height - ); - // rects are always top-left - this.placement = location instanceof $.Point ? - placement : - $.OverlayPlacement.TOP_LEFT; - } - - }; - -}( OpenSeadragon )); -/*globals OpenSeadragon */ - -(function( $ ){ - -var DEVICE_SCREEN = $.getWindowSize(), - BROWSER = $.Browser.vendor, - BROWSER_VERSION = $.Browser.version, - - SUBPIXEL_RENDERING = ( - ( BROWSER == $.BROWSERS.FIREFOX ) || - ( BROWSER == $.BROWSERS.OPERA ) || - ( BROWSER == $.BROWSERS.SAFARI && BROWSER_VERSION >= 4 ) || - ( BROWSER == $.BROWSERS.CHROME && BROWSER_VERSION >= 2 ) || - ( BROWSER == $.BROWSERS.IE && BROWSER_VERSION >= 9 ) - ), - - USE_CANVAS = SUBPIXEL_RENDERING && - !( DEVICE_SCREEN.x <= 400 || DEVICE_SCREEN.y <= 400 ) && - !( navigator.appVersion.match( 'Mobile' ) ) && - $.isFunction( document.createElement( "canvas" ).getContext ); - -//console.error( 'USE_CANVAS ' + USE_CANVAS ); - -/** - * @class - * @param {OpenSeadragon.TileSource} source - Reference to Viewer tile source. - * @param {OpenSeadragon.Viewport} viewport - Reference to Viewer viewport. - * @param {Element} element - Reference to Viewer 'canvas'. - * @property {OpenSeadragon.TileSource} source - Reference to Viewer tile source. - * @property {OpenSeadragon.Viewport} viewport - Reference to Viewer viewport. - * @property {Element} container - Reference to Viewer 'canvas'. - * @property {Element|Canvas} canvas - TODO - * @property {CanvasContext} context - TODO - * @property {Object} config - Reference to Viewer config. - * @property {Number} downloading - How many images are currently being loaded in parallel. - * @property {Number} normHeight - Ratio of zoomable image height to width. - * @property {Object} tilesMatrix - A '3d' dictionary [level][x][y] --> Tile. - * @property {Array} tilesLoaded - An unordered list of Tiles with loaded images. - * @property {Object} coverage - A '3d' dictionary [level][x][y] --> Boolean. - * @property {Array} overlays - An unordered list of Overlays added. - * @property {Array} lastDrawn - An unordered list of Tiles drawn last frame. - * @property {Number} lastResetTime - Last time for which the drawer was reset. - * @property {Boolean} midUpdate - Is the drawer currently updating the viewport? - * @property {Boolean} updateAgain - Does the drawer need to update the viewort again? - * @property {Element} element - DEPRECATED Alias for container. - */ -$.Drawer = function( options ) { - - //backward compatibility for positional args while prefering more - //idiomatic javascript options object as the only argument - var args = arguments, - i; - - if( !$.isPlainObject( options ) ){ - options = { - source: args[ 0 ], - viewport: args[ 1 ], - element: args[ 2 ] - }; - } - - $.extend( true, this, { - - //internal state properties - downloading: 0, - tilesMatrix: {}, - tilesLoaded: [], - coverage: {}, - lastDrawn: [], - lastResetTime: 0, - midUpdate: false, - updateAgain: true, - - - //internal state / configurable settings - overlays: [], - collectionOverlays: {}, - - //configurable settings - maxImageCacheCount: $.DEFAULT_SETTINGS.maxImageCacheCount, - imageLoaderLimit: $.DEFAULT_SETTINGS.imageLoaderLimit, - minZoomImageRatio: $.DEFAULT_SETTINGS.minZoomImageRatio, - wrapHorizontal: $.DEFAULT_SETTINGS.wrapHorizontal, - wrapVertical: $.DEFAULT_SETTINGS.wrapVertical, - immediateRender: $.DEFAULT_SETTINGS.immediateRender, - blendTime: $.DEFAULT_SETTINGS.blendTime, - alwaysBlend: $.DEFAULT_SETTINGS.alwaysBlend, - minPixelRatio: $.DEFAULT_SETTINGS.minPixelRatio, - debugMode: $.DEFAULT_SETTINGS.debugMode, - timeout: $.DEFAULT_SETTINGS.timeout - - }, options ); - - this.container = $.getElement( this.element ); - this.canvas = $.makeNeutralElement( USE_CANVAS ? "canvas" : "div" ); - this.context = USE_CANVAS ? this.canvas.getContext( "2d" ) : null; - this.normHeight = this.source.dimensions.y / this.source.dimensions.x; - this.element = this.container; - - - this.canvas.style.width = "100%"; - this.canvas.style.height = "100%"; - this.canvas.style.position = "absolute"; - - // explicit left-align - this.container.style.textAlign = "left"; - this.container.appendChild( this.canvas ); - - //create the correct type of overlay by convention if the overlays - //are not already OpenSeadragon.Overlays - for( i = 0; i < this.overlays.length; i++ ){ - if( $.isPlainObject( this.overlays[ i ] ) ){ - - this.overlays[ i ] = addOverlayFromConfiguration( this, this.overlays[ i ]); - - } else if ( $.isFunction( this.overlays[ i ] ) ){ - //TODO - } - } - - //this.profiler = new $.Profiler(); -}; - -$.Drawer.prototype = { - - /** - * Adds an html element as an overlay to the current viewport. Useful for - * highlighting words or areas of interest on an image or other zoomable - * interface. - * @method - * @param {Element|String} element - A reference to an element or an id for - * the element which will overlayed. - * @param {OpenSeadragon.Point|OpenSeadragon.Rect} location - The point or - * rectangle which will be overlayed. - * @param {OpenSeadragon.OverlayPlacement} placement - The position of the - * viewport which the location coordinates will be treated as relative - * to. - */ - addOverlay: function( element, location, placement ) { - element = $.getElement( element ); - - if ( getOverlayIndex( this.overlays, element ) >= 0 ) { - // they're trying to add a duplicate overlay - return; - } - - this.overlays.push( new $.Overlay( element, location, placement ) ); - this.updateAgain = true; - if( this.viewer ){ - this.viewer.raiseEvent( 'add-overlay', { - viewer: this.viewer, - element: element, - location: location, - placement: placement - }); - } - return this; - }, - - /** - * Updates the overlay represented by the reference to the element or - * element id moving it to the new location, relative to the new placement. - * @method - * @param {OpenSeadragon.Point|OpenSeadragon.Rect} location - The point or - * rectangle which will be overlayed. - * @param {OpenSeadragon.OverlayPlacement} placement - The position of the - * viewport which the location coordinates will be treated as relative - * to. - * @return {OpenSeadragon.Drawer} Chainable. - */ - updateOverlay: function( element, location, placement ) { - var i; - - element = $.getElement( element ); - i = getOverlayIndex( this.overlays, element ); - - if ( i >= 0 ) { - this.overlays[ i ].update( location, placement ); - this.updateAgain = true; - } - if( this.viewer ){ - this.viewer.raiseEvent( 'update-overlay', { - viewer: this.viewer, - element: element, - location: location, - placement: placement - }); - } - return this; - }, - - /** - * Removes and overlay identified by the reference element or element id - * and schedules and update. - * @method - * @param {Element|String} element - A reference to the element or an - * element id which represent the ovelay content to be removed. - * @return {OpenSeadragon.Drawer} Chainable. - */ - removeOverlay: function( element ) { - var i; - - element = $.getElement( element ); - i = getOverlayIndex( this.overlays, element ); - - if ( i >= 0 ) { - this.overlays[ i ].destroy(); - this.overlays.splice( i, 1 ); - this.updateAgain = true; - } - if( this.viewer ){ - this.viewer.raiseEvent( 'remove-overlay', { - viewer: this.viewer, - element: element - }); - } - return this; - }, - - /** - * Removes all currently configured Overlays from this Drawer and schedules - * and update. - * @method - * @return {OpenSeadragon.Drawer} Chainable. - */ - clearOverlays: function() { - while ( this.overlays.length > 0 ) { - this.overlays.pop().destroy(); - this.updateAgain = true; - } - if( this.viewer ){ - this.viewer.raiseEvent( 'clear-overlay', { - viewer: this.viewer, - element: element - }); - } - return this; - }, - - - /** - * Returns whether the Drawer is scheduled for an update at the - * soonest possible opportunity. - * @method - * @returns {Boolean} - Whether the Drawer is scheduled for an update at the - * soonest possible opportunity. - */ - needsUpdate: function() { - return this.updateAgain; - }, - - /** - * Returns the total number of tiles that have been loaded by this Drawer. - * @method - * @returns {Number} - The total number of tiles that have been loaded by - * this Drawer. - */ - numTilesLoaded: function() { - return this.tilesLoaded.length; - }, - - /** - * Clears all tiles and triggers an update on the next call to - * Drawer.prototype.update(). - * @method - * @return {OpenSeadragon.Drawer} Chainable. - */ - reset: function() { - clearTiles( this ); - this.lastResetTime = +new Date(); - this.updateAgain = true; - return this; - }, - - /** - * Forces the Drawer to update. - * @method - * @return {OpenSeadragon.Drawer} Chainable. - */ - update: function() { - //this.profiler.beginUpdate(); - this.midUpdate = true; - updateViewport( this ); - this.midUpdate = false; - //this.profiler.endUpdate(); - return this; - }, - - /** - * Used internally to load images when required. May also be used to - * preload a set of images so the browser will have them available in - * the local cache to optimize user experience in certain cases. Because - * the number of parallel image loads is configurable, if too many images - * are currently being loaded, the request will be ignored. Since by - * default drawer.imageLoaderLimit is 0, the native browser parallel - * image loading policy will be used. - * @method - * @param {String} src - The url of the image to load. - * @param {Function} callback - The function that will be called with the - * Image object as the only parameter if it was loaded successfully. - * If an error occured, or the request timed out or was aborted, - * the parameter is null instead. - * @return {Boolean} loading - Whether the request was submitted or ignored - * based on OpenSeadragon.DEFAULT_SETTINGS.imageLoaderLimit. - */ - loadImage: function( src, callback ) { - var _this = this, - loading = false, - image, - jobid, - complete; - - if ( !this.imageLoaderLimit || - this.downloading < this.imageLoaderLimit ) { - - this.downloading++; - - image = new Image(); - - complete = function( imagesrc, resultingImage ){ - _this.downloading--; - if (typeof ( callback ) == "function") { - try { - callback( resultingImage ); - } catch ( e ) { - $.console.error( - "%s while executing %s callback: %s", - e.name, - src, - e.message, - e - ); - } - } - }; - - image.onload = function(){ - finishLoadingImage( image, complete, true, jobid ); - }; - - image.onabort = image.onerror = function(){ - finishLoadingImage( image, complete, false, jobid ); - }; - - jobid = window.setTimeout( function(){ - finishLoadingImage( image, complete, false, jobid ); - }, this.timeout ); - - loading = true; - image.src = src; - } - - return loading; - } -}; - -/** - * @private - * @inner - */ - function addOverlayFromConfiguration( drawer, overlay ){ - - var element = null, - rect = ( overlay.height && overlay.width ) ? new $.Rect( - overlay.x || overlay.px, - overlay.y || overlay.py, - overlay.width, - overlay.height - ) : new $.Point( - overlay.x || overlay.px, - overlay.y || overlay.py - ), - id = overlay.id ? - overlay.id : - "openseadragon-overlay-"+Math.floor(Math.random()*10000000); - - element = $.getElement(overlay.id); - if( !element ){ - element = document.createElement("a"); - element.href = "#/overlay/"+id; - } - element.id = id; - element.className = element.className + " " + ( overlay.className ? - overlay.className : - "openseadragon-overlay" - ); - - - if(overlay.px !== undefined){ - //if they specified 'px' so its in pixel coordinates so - //we need to translate to viewport coordinates - rect = drawer.viewport.imageToViewportRectangle( rect ); - } - if( overlay.placement ){ - return new $.Overlay( - element, - drawer.viewport.pointFromPixel(rect), - $.OverlayPlacement[overlay.placement.toUpperCase()] - ); - }else{ - return new $.Overlay( element, rect ); - } - -} - -/** - * @private - * @inner - * Pretty much every other line in this needs to be documented so its clear - * how each piece of this routine contributes to the drawing process. That's - * why there are so many TODO's inside this function. - */ -function updateViewport( drawer ) { - - drawer.updateAgain = false; - - if( drawer.viewer ){ - drawer.viewer.raiseEvent( 'update-viewport', { - viewer: drawer.viewer - }); - } - - var tile, - level, - best = null, - haveDrawn = false, - currentTime = +new Date(), - viewportSize = drawer.viewport.getContainerSize(), - viewportBounds = drawer.viewport.getBounds( true ), - viewportTL = viewportBounds.getTopLeft(), - viewportBR = viewportBounds.getBottomRight(), - zeroRatioC = drawer.viewport.deltaPixelsFromPoints( - drawer.source.getPixelRatio( 0 ), - true - ).x, - lowestLevel = Math.max( - drawer.source.minLevel, - Math.floor( - Math.log( drawer.minZoomImageRatio ) / - Math.log( 2 ) - ) - ), - highestLevel = Math.min( - Math.abs(drawer.source.maxLevel), - Math.abs(Math.floor( - Math.log( zeroRatioC / drawer.minPixelRatio ) / - Math.log( 2 ) - )) - ), - renderPixelRatioC, - renderPixelRatioT, - zeroRatioT, - optimalRatio, - levelOpacity, - levelVisibility; - - //TODO - while ( drawer.lastDrawn.length > 0 ) { - tile = drawer.lastDrawn.pop(); - tile.beingDrawn = false; - } - - //TODO - drawer.canvas.innerHTML = ""; - if ( USE_CANVAS ) { - if( drawer.canvas.width != viewportSize.x || - drawer.canvas.height != viewportSize.y ){ - drawer.canvas.width = viewportSize.x; - drawer.canvas.height = viewportSize.y; - } - drawer.context.clearRect( 0, 0, viewportSize.x, viewportSize.y ); - } - - //TODO - if ( !drawer.wrapHorizontal && - ( viewportBR.x < 0 || viewportTL.x > 1 ) ) { - return; - } else if - ( !drawer.wrapVertical && - ( viewportBR.y < 0 || viewportTL.y > drawer.normHeight ) ) { - return; - } - - //TODO - if ( !drawer.wrapHorizontal ) { - viewportTL.x = Math.max( viewportTL.x, 0 ); - viewportBR.x = Math.min( viewportBR.x, 1 ); - } - if ( !drawer.wrapVertical ) { - viewportTL.y = Math.max( viewportTL.y, 0 ); - viewportBR.y = Math.min( viewportBR.y, drawer.normHeight ); - } - - //TODO - lowestLevel = Math.min( lowestLevel, highestLevel ); - - //TODO - for ( level = highestLevel; level >= lowestLevel; level-- ) { - - //Avoid calculations for draw if we have already drawn this - renderPixelRatioC = drawer.viewport.deltaPixelsFromPoints( - drawer.source.getPixelRatio( level ), - true - ).x; - - if ( ( !haveDrawn && renderPixelRatioC >= drawer.minPixelRatio ) || - ( level == lowestLevel ) ) { - drawLevel = true; - haveDrawn = true; - } else if ( !haveDrawn ) { - continue; - } - - renderPixelRatioT = drawer.viewport.deltaPixelsFromPoints( - drawer.source.getPixelRatio( level ), - false - ).x; - - zeroRatioT = drawer.viewport.deltaPixelsFromPoints( - drawer.source.getPixelRatio( 0 ), - false - ).x; - - optimalRatio = drawer.immediateRender ? - 1 : - zeroRatioT; - - levelOpacity = Math.min( 1, ( renderPixelRatioC - 0.5 ) / 0.5 ); - - levelVisibility = optimalRatio / Math.abs( - optimalRatio - renderPixelRatioT - ); - - //TODO - best = updateLevel( - drawer, - haveDrawn, - level, - levelOpacity, - levelVisibility, - viewportTL, - viewportBR, - currentTime, - best - ); - - //TODO - if ( providesCoverage( drawer.coverage, level ) ) { - break; - } - } - - //TODO - drawTiles( drawer, drawer.lastDrawn ); - drawOverlays( drawer.viewport, drawer.overlays, drawer.container ); - - //TODO - if ( best ) { - loadTile( drawer, best, currentTime ); - // because we haven't finished drawing, so - drawer.updateAgain = true; - } - -} - - -function updateLevel( drawer, haveDrawn, level, levelOpacity, levelVisibility, viewportTL, viewportBR, currentTime, best ){ - - var x, y, - tileTL, - tileBR, - numberOfTiles, - viewportCenter = drawer.viewport.pixelFromPoint( drawer.viewport.getCenter() ); - - - if( drawer.viewer ){ - drawer.viewer.raiseEvent( 'update-level', { - viewer: drawer.viewer, - havedrawn: haveDrawn, - level: level, - opacity: levelOpacity, - visibility: levelVisibility, - topleft: viewportTopLeft, - bottomright: viewportBottomRight, - currenttime: currentTime, - best: best - }); - } - - //OK, a new drawing so do your calculations - tileTL = drawer.source.getTileAtPoint( level, viewportTL ); - tileBR = drawer.source.getTileAtPoint( level, viewportBR ); - numberOfTiles = drawer.source.getNumTiles( level ); - - resetCoverage( drawer.coverage, level ); - - if ( !drawer.wrapHorizontal ) { - tileBR.x = Math.min( tileBR.x, numberOfTiles.x - 1 ); - } - if ( !drawer.wrapVertical ) { - tileBR.y = Math.min( tileBR.y, numberOfTiles.y - 1 ); - } - - for ( x = tileTL.x; x <= tileBR.x; x++ ) { - for ( y = tileTL.y; y <= tileBR.y; y++ ) { - - best = updateTile( - drawer, - drawLevel, - haveDrawn, - x, y, - level, - levelOpacity, - levelVisibility, - viewportCenter, - numberOfTiles, - currentTime, - best - ); - - } - } - - return best; -} - -function updateTile( drawer, drawLevel, haveDrawn, x, y, level, levelOpacity, levelVisibility, viewportCenter, numberOfTiles, currentTime, best){ - - var tile = getTile( - x, y, - level, - drawer.source, - drawer.tilesMatrix, - currentTime, - numberOfTiles, - drawer.normHeight - ), - drawTile = drawLevel, - newbest; - - if( drawer.viewer ){ - drawer.viewer.raiseEvent( 'update-tile', { - viewer: drawer.viewer, - tile: tile - }); - } - - setCoverage( drawer.coverage, level, x, y, false ); - - if ( !tile.exists ) { - return best; - } - - if ( haveDrawn && !drawTile ) { - if ( isCovered( drawer.coverage, level, x, y ) ) { - setCoverage( drawer.coverage, level, x, y, true ); - } else { - drawTile = true; - } - } - - if ( !drawTile ) { - return best; - } - - positionTile( - tile, - drawer.source.tileOverlap, - drawer.viewport, - viewportCenter, - levelVisibility - ); - - if ( tile.loaded ) { - - drawer.updateAgain = blendTile( - drawer, - tile, - x, y, - level, - levelOpacity, - currentTime - ); - } else if ( tile.loading ) { - // the tile is already in the download queue - // thanks josh1093 for finally translating this typo - } else { - best = compareTiles( best, tile ); - } - - return best; -} - -function getTile( x, y, level, tileSource, tilesMatrix, time, numTiles, normHeight ) { - var xMod, - yMod, - bounds, - exists, - url, - tile; - - if ( !tilesMatrix[ level ] ) { - tilesMatrix[ level ] = {}; - } - if ( !tilesMatrix[ level ][ x ] ) { - tilesMatrix[ level ][ x ] = {}; - } - - if ( !tilesMatrix[ level ][ x ][ y ] ) { - xMod = ( numTiles.x + ( x % numTiles.x ) ) % numTiles.x; - yMod = ( numTiles.y + ( y % numTiles.y ) ) % numTiles.y; - bounds = tileSource.getTileBounds( level, xMod, yMod ); - exists = tileSource.tileExists( level, xMod, yMod ); - url = tileSource.getTileUrl( level, xMod, yMod ); - - bounds.x += 1.0 * ( x - xMod ) / numTiles.x; - bounds.y += normHeight * ( y - yMod ) / numTiles.y; - - tilesMatrix[ level ][ x ][ y ] = new $.Tile( - level, - x, - y, - bounds, - exists, - url - ); - } - - tile = tilesMatrix[ level ][ x ][ y ]; - tile.lastTouchTime = time; - - return tile; -} - - -function loadTile( drawer, tile, time ) { - if( drawer.viewport.collectionMode ){ - drawer.midUpdate = false; - onTileLoad( drawer, tile, time ); - } else { - tile.loading = drawer.loadImage( - tile.url, - function( image ){ - onTileLoad( drawer, tile, time, image ); - } - ); - } -} - -function onTileLoad( drawer, tile, time, image ) { - var insertionIndex, - cutoff, - worstTile, - worstTime, - worstLevel, - worstTileIndex, - prevTile, - prevTime, - prevLevel, - i; - - tile.loading = false; - - if ( drawer.midUpdate ) { - $.console.warn( "Tile load callback in middle of drawing routine." ); - return; - } else if ( !image && !drawer.viewport.collectionMode ) { - $.console.log( "Tile %s failed to load: %s", tile, tile.url ); - if( !drawer.debugMode ){ - tile.exists = false; - return; - } - } else if ( time < drawer.lastResetTime ) { - $.console.log( "Ignoring tile %s loaded before reset: %s", tile, tile.url ); - return; - } - - tile.loaded = true; - tile.image = image; - - - insertionIndex = drawer.tilesLoaded.length; - - if ( drawer.tilesLoaded.length >= drawer.maxImageCacheCount ) { - cutoff = Math.ceil( Math.log( drawer.source.tileSize ) / Math.log( 2 ) ); - - worstTile = null; - worstTileIndex = -1; - - for ( i = drawer.tilesLoaded.length - 1; i >= 0; i-- ) { - prevTile = drawer.tilesLoaded[ i ]; - - if ( prevTile.level <= drawer.cutoff || prevTile.beingDrawn ) { - continue; - } else if ( !worstTile ) { - worstTile = prevTile; - worstTileIndex = i; - continue; - } - - prevTime = prevTile.lastTouchTime; - worstTime = worstTile.lastTouchTime; - prevLevel = prevTile.level; - worstLevel = worstTile.level; - - if ( prevTime < worstTime || - ( prevTime == worstTime && prevLevel > worstLevel ) ) { - worstTile = prevTile; - worstTileIndex = i; - } - } - - if ( worstTile && worstTileIndex >= 0 ) { - worstTile.unload(); - insertionIndex = worstTileIndex; - } - } - - drawer.tilesLoaded[ insertionIndex ] = tile; - drawer.updateAgain = true; -} - - -function positionTile( tile, overlap, viewport, viewportCenter, levelVisibility ){ - var boundsTL = tile.bounds.getTopLeft(), - boundsSize = tile.bounds.getSize(), - positionC = viewport.pixelFromPoint( boundsTL, true ), - positionT = viewport.pixelFromPoint( boundsTL, false ), - sizeC = viewport.deltaPixelsFromPoints( boundsSize, true ), - sizeT = viewport.deltaPixelsFromPoints( boundsSize, false ), - tileCenter = positionT.plus( sizeT.divide( 2 ) ), - tileDistance = viewportCenter.distanceTo( tileCenter ); - - if ( !overlap ) { - sizeC = sizeC.plus( new $.Point( 1, 1 ) ); - } - - tile.position = positionC; - tile.size = sizeC; - tile.distance = tileDistance; - tile.visibility = levelVisibility; -} - - -function blendTile( drawer, tile, x, y, level, levelOpacity, currentTime ){ - var blendTimeMillis = 1000 * drawer.blendTime, - deltaTime, - opacity; - - if ( !tile.blendStart ) { - tile.blendStart = currentTime; - } - - deltaTime = currentTime - tile.blendStart; - opacity = Math.min( 1, deltaTime / ( blendTimeMillis || 1 ) ); - - if ( drawer.alwaysBlend ) { - opacity *= levelOpacity; - } - - tile.opacity = opacity; - - drawer.lastDrawn.push( tile ); - - if ( opacity == 1 ) { - setCoverage( drawer.coverage, level, x, y, true ); - } else if ( deltaTime < blendTimeMillis ) { - return true; - } - - return false; -} - - -function clearTiles( drawer ) { - drawer.tilesMatrix = {}; - drawer.tilesLoaded = []; -} - -/** - * @private - * @inner - * Returns true if the given tile provides coverage to lower-level tiles of - * lower resolution representing the same content. If neither x nor y is - * given, returns true if the entire visible level provides coverage. - * - * Note that out-of-bounds tiles provide coverage in this sense, since - * there's no content that they would need to cover. Tiles at non-existent - * levels that are within the image bounds, however, do not. - */ -function providesCoverage( coverage, level, x, y ) { - var rows, - cols, - i, j; - - if ( !coverage[ level ] ) { - return false; - } - - if ( x === undefined || y === undefined ) { - rows = coverage[ level ]; - for ( i in rows ) { - if ( rows.hasOwnProperty( i ) ) { - cols = rows[ i ]; - for ( j in cols ) { - if ( cols.hasOwnProperty( j ) && !cols[ j ] ) { - return false; - } - } - } - } - - return true; - } - - return ( - coverage[ level ][ x] === undefined || - coverage[ level ][ x ][ y ] === undefined || - coverage[ level ][ x ][ y ] === true - ); -} - -/** - * @private - * @inner - * Returns true if the given tile is completely covered by higher-level - * tiles of higher resolution representing the same content. If neither x - * nor y is given, returns true if the entire visible level is covered. - */ -function isCovered( coverage, level, x, y ) { - if ( x === undefined || y === undefined ) { - return providesCoverage( coverage, level + 1 ); - } else { - return ( - providesCoverage( coverage, level + 1, 2 * x, 2 * y ) && - providesCoverage( coverage, level + 1, 2 * x, 2 * y + 1 ) && - providesCoverage( coverage, level + 1, 2 * x + 1, 2 * y ) && - providesCoverage( coverage, level + 1, 2 * x + 1, 2 * y + 1 ) - ); - } -} - -/** - * @private - * @inner - * Sets whether the given tile provides coverage or not. - */ -function setCoverage( coverage, level, x, y, covers ) { - if ( !coverage[ level ] ) { - $.console.warn( - "Setting coverage for a tile before its level's coverage has been reset: %s", - level - ); - return; - } - - if ( !coverage[ level ][ x ] ) { - coverage[ level ][ x ] = {}; - } - - coverage[ level ][ x ][ y ] = covers; -} - -/** - * @private - * @inner - * Resets coverage information for the given level. This should be called - * after every draw routine. Note that at the beginning of the next draw - * routine, coverage for every visible tile should be explicitly set. - */ -function resetCoverage( coverage, level ) { - coverage[ level ] = {}; -} - -/** - * @private - * @inner - * Determines the 'z-index' of the given overlay. Overlays are ordered in - * a z-index based on the order they are added to the Drawer. - */ -function getOverlayIndex( overlays, element ) { - var i; - for ( i = overlays.length - 1; i >= 0; i-- ) { - if ( overlays[ i ].element == element ) { - return i; - } - } - - return -1; -} - -/** - * @private - * @inner - * Determines whether the 'last best' tile for the area is better than the - * tile in question. - */ -function compareTiles( previousBest, tile ) { - if ( !previousBest ) { - return tile; - } - - if ( tile.visibility > previousBest.visibility ) { - return tile; - } else if ( tile.visibility == previousBest.visibility ) { - if ( tile.distance < previousBest.distance ) { - return tile; - } - } - - return previousBest; -} - -function finishLoadingImage( image, callback, successful, jobid ){ - - image.onload = null; - image.onabort = null; - image.onerror = null; - - if ( jobid ) { - window.clearTimeout( jobid ); - } - window.setTimeout( function() { - callback( image.src, successful ? image : null); - }, 1 ); - -} - - -function drawOverlays( viewport, overlays, container ){ - var i, - length = overlays.length; - for ( i = 0; i < length; i++ ) { - drawOverlay( viewport, overlays[ i ], container ); - } -} - -function drawOverlay( viewport, overlay, container ){ - - overlay.position = viewport.pixelFromPoint( - overlay.bounds.getTopLeft(), - true - ); - overlay.size = viewport.deltaPixelsFromPoints( - overlay.bounds.getSize(), - true - ); - overlay.drawHTML( container ); -} - -function drawTiles( drawer, lastDrawn ){ - var i, - tile, - tileKey, - viewer, - viewport, - position, - tileSource, - collectionTileSource; - - for ( i = lastDrawn.length - 1; i >= 0; i-- ) { - tile = lastDrawn[ i ]; - - //We dont actually 'draw' a collection tile, rather its used to house - //an overlay which does the drawing in its own viewport - if( drawer.viewport.collectionMode ){ - - tileKey = tile.x + '/' + tile.y; - viewport = drawer.viewport; - collectionTileSource = viewport.collectionTileSource; - - if( !drawer.collectionOverlays[ tileKey ] ){ - - position = collectionTileSource.layout == 'horizontal' ? - tile.y + ( tile.x * collectionTileSource.rows ) : - tile.x + ( tile.y * collectionTileSource.rows ), - - tileSource = position < collectionTileSource.tileSources.length ? - collectionTileSource.tileSources[ position ] : - null; - - //$.console.log("Rendering collection tile %s | %s | %s", tile.y, tile.y, position); - if( tileSource ){ - drawer.collectionOverlays[ tileKey ] = viewer = new $.Viewer({ - element: $.makeNeutralElement( "div" ), - mouseNavEnabled: false, - showNavigator: false, - showSequenceControl: false, - showNavigationControl: false, - //visibilityRatio: 1, - //debugMode: true, - //debugGridColor: 'red', - tileSources: [ - tileSource - ] - }); - - //TODO: IE seems to barf on this, not sure if its just the border - // but we probably need to clear this up with a better - // test of support for various css features - if( SUBPIXEL_RENDERING ){ - viewer.element.style.border = '1px solid rgba(255,255,255,0.38)'; - viewer.element.style['-webkit-box-reflect'] = - 'below 0px -webkit-gradient('+ - 'linear,left '+ - 'top,left '+ - 'bottom,from(transparent),color-stop(62%,transparent),to(rgba(255,255,255,0.62))'+ - ')'; - } - - drawer.addOverlay( - viewer.element, - tile.bounds - ); - } - - }else{ - viewer = drawer.collectionOverlays[ tileKey ]; - if( viewer.viewport ){ - viewer.viewport.resize( tile.size, true ); - viewer.viewport.goHome( true ); - } - } - - } else { - - if ( USE_CANVAS ) { - tile.drawCanvas( drawer.context ); - } else { - tile.drawHTML( drawer.canvas ); - } - - - tile.beingDrawn = true; - } - - if( drawer.debugMode ){ - try{ - drawDebugInfo( drawer, tile, lastDrawn.length, i ); - }catch(e){ - $.console.error(e); - } - } - - if( drawer.viewer ){ - drawer.viewer.raiseEvent( 'tile-drawn', { - viewer: drawer.viewer, - tile: tile - }); - } - } -} - - -function drawDebugInfo( drawer, tile, count, i ){ - - if ( USE_CANVAS ) { - drawer.context.lineWidth = 2; - drawer.context.font = 'small-caps bold 13px ariel'; - drawer.context.strokeStyle = drawer.debugGridColor; - drawer.context.fillStyle = drawer.debugGridColor; - drawer.context.strokeRect( - tile.position.x, - tile.position.y, - tile.size.x, - tile.size.y - ); - if( tile.x === 0 && tile.y === 0 ){ - drawer.context.fillText( - "Zoom: " + drawer.viewport.getZoom(), - tile.position.x, - tile.position.y - 30 - ); - drawer.context.fillText( - "Pan: " + drawer.viewport.getBounds().toString(), - tile.position.x, - tile.position.y - 20 - ); - } - drawer.context.fillText( - "Level: " + tile.level, - tile.position.x + 10, - tile.position.y + 20 - ); - drawer.context.fillText( - "Column: " + tile.x, - tile.position.x + 10, - tile.position.y + 30 - ); - drawer.context.fillText( - "Row: " + tile.y, - tile.position.x + 10, - tile.position.y + 40 - ); - drawer.context.fillText( - "Order: " + i + " of " + count, - tile.position.x + 10, - tile.position.y + 50 - ); - drawer.context.fillText( - "Size: " + tile.size.toString(), - tile.position.x + 10, - tile.position.y + 60 - ); - drawer.context.fillText( - "Position: " + tile.position.toString(), - tile.position.x + 10, - tile.position.y + 70 - ); - } -} - - -}( OpenSeadragon )); -/*globals OpenSeadragon */ - -(function( $ ){ - - -/** - * @class - */ -$.Viewport = function( options ) { - - //backward compatibility for positional args while prefering more - //idiomatic javascript options object as the only argument - var args = arguments; - if( args.length && args[ 0 ] instanceof $.Point ){ - options = { - containerSize: args[ 0 ], - contentSize: args[ 1 ], - config: args[ 2 ] - }; - } - - //options.config and the general config argument are deprecated - //in favor of the more direct specification of optional settings - //being pass directly on the options object - if ( options.config ){ - $.extend( true, options, options.config ); - delete options.config; - } - - $.extend( true, this, { - - //required settings - containerSize: null, - contentSize: null, - - //internal state properties - zoomPoint: null, - viewer: null, - - //configurable options - springStiffness: $.DEFAULT_SETTINGS.springStiffness, - animationTime: $.DEFAULT_SETTINGS.animationTime, - minZoomImageRatio: $.DEFAULT_SETTINGS.minZoomImageRatio, - maxZoomPixelRatio: $.DEFAULT_SETTINGS.maxZoomPixelRatio, - visibilityRatio: $.DEFAULT_SETTINGS.visibilityRatio, - wrapHorizontal: $.DEFAULT_SETTINGS.wrapHorizontal, - wrapVertical: $.DEFAULT_SETTINGS.wrapVertical, - defaultZoomLevel: $.DEFAULT_SETTINGS.defaultZoomLevel, - minZoomLevel: $.DEFAULT_SETTINGS.minZoomLevel, - maxZoomLevel: $.DEFAULT_SETTINGS.maxZoomLevel - - }, options ); - - this.centerSpringX = new $.Spring({ - initial: 0, - springStiffness: this.springStiffness, - animationTime: this.animationTime - }); - this.centerSpringY = new $.Spring({ - initial: 0, - springStiffness: this.springStiffness, - animationTime: this.animationTime - }); - this.zoomSpring = new $.Spring({ - initial: 1, - springStiffness: this.springStiffness, - animationTime: this.animationTime - }); - - this.resetContentSize( this.contentSize ); - this.goHome( true ); - this.update(); -}; - -$.Viewport.prototype = { - - /** - * @function - * @return {OpenSeadragon.Viewport} Chainable. - */ - resetContentSize: function( contentSize ){ - this.contentSize = contentSize; - this.contentAspectX = this.contentSize.x / this.contentSize.y; - this.contentAspectY = this.contentSize.y / this.contentSize.x; - this.fitWidthBounds = new $.Rect( 0, 0, 1, this.contentAspectY ); - this.fitHeightBounds = new $.Rect( 0, 0, this.contentAspectY, this.contentAspectY); - - this.homeBounds = new $.Rect( 0, 0, 1, this.contentAspectY ); - - if( this.viewer ){ - this.viewer.raiseEvent( 'reset-size', { - contentSize: contentSize, - viewer: this.viewer - }); - } - - return this; - }, - - /** - * @function - */ - getHomeZoom: function() { - var aspectFactor = - this.contentAspectX / this.getAspectRatio(); - - if( this.defaultZoomLevel ){ - return this.defaultZoomLevel; - } else { - return ( aspectFactor >= 1 ) ? - 1 : - aspectFactor; - } - }, - - /** - * @function - */ - getHomeBounds: function() { - var center = this.homeBounds.getCenter( ), - width = 1.0 / this.getHomeZoom( ), - height = width / this.getAspectRatio(); - - return new $.Rect( - center.x - ( width / 2.0 ), - center.y - ( height / 2.0 ), - width, - height - ); - }, - - /** - * @function - * @param {Boolean} immediately - */ - goHome: function( immediately ) { - if( this.viewer ){ - this.viewer.raiseEvent( 'home', { - immediately: immediately, - viewer: this.viewer - }); - } - return this.fitBounds( this.getHomeBounds(), immediately ); - }, - - /** - * @function - */ - getMinZoom: function() { - var homeZoom = this.getHomeZoom(), - zoom = this.minZoomLevel ? - this.minZoomLevel : - this.minZoomImageRatio * homeZoom; - - return Math.min( zoom, homeZoom ); - }, - - /** - * @function - */ - getMaxZoom: function() { - var zoom = this.maxZoomLevel ? - this.maxZoomLevel : - ( this.contentSize.x * this.maxZoomPixelRatio / this.containerSize.x ); - - return Math.max( zoom, this.getHomeZoom() ); - }, - - /** - * @function - */ - getAspectRatio: function() { - return this.containerSize.x / this.containerSize.y; - }, - - /** - * @function - */ - getContainerSize: function() { - return new $.Point( - this.containerSize.x, - this.containerSize.y - ); - }, - - /** - * @function - */ - getBounds: function( current ) { - var center = this.getCenter( current ), - width = 1.0 / this.getZoom( current ), - height = width / this.getAspectRatio(); - - return new $.Rect( - center.x - ( width / 2.0 ), - center.y - ( height / 2.0 ), - width, - height - ); - }, - - /** - * @function - */ - getCenter: function( current ) { - var centerCurrent = new $.Point( - this.centerSpringX.current.value, - this.centerSpringY.current.value - ), - centerTarget = new $.Point( - this.centerSpringX.target.value, - this.centerSpringY.target.value - ), - oldZoomPixel, - zoom, - width, - height, - bounds, - newZoomPixel, - deltaZoomPixels, - deltaZoomPoints; - - if ( current ) { - return centerCurrent; - } else if ( !this.zoomPoint ) { - return centerTarget; - } - - oldZoomPixel = this.pixelFromPoint(this.zoomPoint, true); - - zoom = this.getZoom(); - width = 1.0 / zoom; - height = width / this.getAspectRatio(); - bounds = new $.Rect( - centerCurrent.x - width / 2.0, - centerCurrent.y - height / 2.0, - width, - height - ); - - newZoomPixel = this.zoomPoint.minus( - bounds.getTopLeft() - ).times( - this.containerSize.x / bounds.width - ); - deltaZoomPixels = newZoomPixel.minus( oldZoomPixel ); - deltaZoomPoints = deltaZoomPixels.divide( this.containerSize.x * zoom ); - - return centerTarget.plus( deltaZoomPoints ); - }, - - /** - * @function - */ - getZoom: function( current ) { - if ( current ) { - return this.zoomSpring.current.value; - } else { - return this.zoomSpring.target.value; - } - }, - - /** - * @function - * @return {OpenSeadragon.Viewport} Chainable. - */ - applyConstraints: function( immediately ) { - var actualZoom = this.getZoom(), - constrainedZoom = Math.max( - Math.min( actualZoom, this.getMaxZoom() ), - this.getMinZoom() - ), - bounds, - horizontalThreshold, - verticalThreshold, - left, - right, - top, - bottom, - center, - dx = 0, - dy = 0, - dx1 = 0, dx2 = 0, dy1 = 0, dy2 = 0; - - if ( actualZoom != constrainedZoom ) { - this.zoomTo( constrainedZoom, this.zoomPoint, immediately ); - } - - bounds = this.getBounds(); - - horizontalThreshold = this.visibilityRatio * bounds.width; - verticalThreshold = this.visibilityRatio * bounds.height; - - left = bounds.x + bounds.width; - right = 1 - bounds.x; - top = bounds.y + bounds.height; - bottom = this.contentAspectY - bounds.y; - - if ( this.wrapHorizontal ) { - //do nothing - } else { - if ( left < horizontalThreshold ) { - dx = horizontalThreshold - left; - } - if ( right < horizontalThreshold ) { - dx = dx ? - ( dx + right - horizontalThreshold ) / 2 : - ( right - horizontalThreshold ); - } - } - - if ( this.wrapVertical ) { - //do nothing - } else { - if ( top < verticalThreshold ) { - dy = ( verticalThreshold - top ); - } - if ( bottom < verticalThreshold ) { - dy = dy ? - ( dy + bottom - verticalThreshold ) / 2 : - ( bottom - verticalThreshold ); - } - } - - if ( dx || dy || immediately ) { - bounds.x += dx; - bounds.y += dy; - if( bounds.width > 1 ){ - bounds.x = 0.5 - bounds.width/2; - } - if( bounds.height > this.contentAspectY ){ - bounds.y = this.contentAspectY/2 - bounds.height/2; - } - this.fitBounds( bounds, immediately ); - } - - if( this.viewer ){ - this.viewer.raiseEvent( 'constrain', { - immediately: immediately, - viewer: this.viewer - }); - } - - return this; - }, - - /** - * @function - * @param {Boolean} immediately - */ - ensureVisible: function( immediately ) { - return this.applyConstraints( immediately ); - }, - - /** - * @function - * @param {OpenSeadragon.Rect} bounds - * @param {Boolean} immediately - * @return {OpenSeadragon.Viewport} Chainable. - */ - fitBounds: function( bounds, immediately ) { - var aspect = this.getAspectRatio(), - center = bounds.getCenter(), - newBounds = new $.Rect( - bounds.x, - bounds.y, - bounds.width, - bounds.height - ), - oldBounds, - oldZoom, - newZoom, - referencePoint; - - if ( newBounds.getAspectRatio() >= aspect ) { - newBounds.height = bounds.width / aspect; - newBounds.y = center.y - newBounds.height / 2; - } else { - newBounds.width = bounds.height * aspect; - newBounds.x = center.x - newBounds.width / 2; - } - - this.panTo( this.getCenter( true ), true ); - this.zoomTo( this.getZoom( true ), null, true ); - - oldBounds = this.getBounds(); - oldZoom = this.getZoom(); - newZoom = 1.0 / newBounds.width; - if ( newZoom == oldZoom || newBounds.width == oldBounds.width ) { - return this.panTo( center, immediately ); - } - - referencePoint = oldBounds.getTopLeft().times( - this.containerSize.x / oldBounds.width - ).minus( - newBounds.getTopLeft().times( - this.containerSize.x / newBounds.width - ) - ).divide( - this.containerSize.x / oldBounds.width - - this.containerSize.x / newBounds.width - ); - - return this.zoomTo( newZoom, referencePoint, immediately ); - }, - - - /** - * @function - * @param {Boolean} immediately - * @return {OpenSeadragon.Viewport} Chainable. - */ - fitVertically: function( immediately ) { - var center = this.getCenter(); - - if ( this.wrapHorizontal ) { - center.x = ( 1 + ( center.x % 1 ) ) % 1; - this.centerSpringX.resetTo( center.x ); - this.centerSpringX.update(); - } - - if ( this.wrapVertical ) { - center.y = ( - this.contentAspectY + ( center.y % this.contentAspectY ) - ) % this.contentAspectY; - this.centerSpringY.resetTo( center.y ); - this.centerSpringY.update(); - } - - return this.fitBounds( this.fitHeightBounds, immediately ); - }, - - /** - * @function - * @param {Boolean} immediately - * @return {OpenSeadragon.Viewport} Chainable. - */ - fitHorizontally: function( immediately ) { - var center = this.getCenter(); - - if ( this.wrapHorizontal ) { - center.x = ( - this.contentAspectX + ( center.x % this.contentAspectX ) - ) % this.contentAspectX; - this.centerSpringX.resetTo( center.x ); - this.centerSpringX.update(); - } - - if ( this.wrapVertical ) { - center.y = ( 1 + ( center.y % 1 ) ) % 1; - this.centerSpringY.resetTo( center.y ); - this.centerSpringY.update(); - } - - return this.fitBounds( this.fitWidthBounds, immediately ); - }, - - - /** - * @function - * @param {OpenSeadragon.Point} delta - * @param {Boolean} immediately - * @return {OpenSeadragon.Viewport} Chainable. - */ - panBy: function( delta, immediately ) { - var center = new $.Point( - this.centerSpringX.target.value, - this.centerSpringY.target.value - ); - return this.panTo( center.plus( delta ), immediately ); - }, - - /** - * @function - * @param {OpenSeadragon.Point} center - * @param {Boolean} immediately - * @return {OpenSeadragon.Viewport} Chainable. - */ - panTo: function( center, immediately ) { - if ( immediately ) { - this.centerSpringX.resetTo( center.x ); - this.centerSpringY.resetTo( center.y ); - } else { - this.centerSpringX.springTo( center.x ); - this.centerSpringY.springTo( center.y ); - } - - if( this.viewer ){ - this.viewer.raiseEvent( 'pan', { - center: center, - immediately: immediately, - viewer: this.viewer - }); - } - - return this; - }, - - /** - * @function - * @return {OpenSeadragon.Viewport} Chainable. - */ - zoomBy: function( factor, refPoint, immediately ) { - return this.zoomTo( this.zoomSpring.target.value * factor, refPoint, immediately ); - }, - - /** - * @function - * @return {OpenSeadragon.Viewport} Chainable. - */ - zoomTo: function( zoom, refPoint, immediately ) { - - this.zoomPoint = refPoint instanceof $.Point ? - refPoint : - null; - - if ( immediately ) { - this.zoomSpring.resetTo( zoom ); - } else { - this.zoomSpring.springTo( zoom ); - } - - if( this.viewer ){ - this.viewer.raiseEvent( 'zoom', { - zoom: zoom, - refPoint: refPoint, - immediately: immediately, - viewer: this.viewer - }); - } - - return this; - }, - - /** - * @function - * @return {OpenSeadragon.Viewport} Chainable. - */ - resize: function( newContainerSize, maintain ) { - var oldBounds = this.getBounds(), - newBounds = oldBounds, - widthDeltaFactor = newContainerSize.x / this.containerSize.x; - - this.containerSize = new $.Point( - newContainerSize.x, - newContainerSize.y - ); - - if (maintain) { - newBounds.width = oldBounds.width * widthDeltaFactor; - newBounds.height = newBounds.width / this.getAspectRatio(); - } - - if( this.viewer ){ - this.viewer.raiseEvent( 'resize', { - newContainerSize: newContainerSize, - maintain: maintain, - viewer: this.viewer - }); - } - - return this.fitBounds( newBounds, true ); - }, - - /** - * @function - */ - update: function() { - var oldCenterX = this.centerSpringX.current.value, - oldCenterY = this.centerSpringY.current.value, - oldZoom = this.zoomSpring.current.value, - oldZoomPixel, - newZoomPixel, - deltaZoomPixels, - deltaZoomPoints; - - if (this.zoomPoint) { - oldZoomPixel = this.pixelFromPoint( this.zoomPoint, true ); - } - - this.zoomSpring.update(); - - if (this.zoomPoint && this.zoomSpring.current.value != oldZoom) { - newZoomPixel = this.pixelFromPoint( this.zoomPoint, true ); - deltaZoomPixels = newZoomPixel.minus( oldZoomPixel ); - deltaZoomPoints = this.deltaPointsFromPixels( deltaZoomPixels, true ); - - this.centerSpringX.shiftBy( deltaZoomPoints.x ); - this.centerSpringY.shiftBy( deltaZoomPoints.y ); - } else { - this.zoomPoint = null; - } - - this.centerSpringX.update(); - this.centerSpringY.update(); - - return this.centerSpringX.current.value != oldCenterX || - this.centerSpringY.current.value != oldCenterY || - this.zoomSpring.current.value != oldZoom; - }, - - - /** - * @function - */ - deltaPixelsFromPoints: function( deltaPoints, current ) { - return deltaPoints.times( - this.containerSize.x * this.getZoom( current ) - ); - }, - - /** - * @function - */ - deltaPointsFromPixels: function( deltaPixels, current ) { - return deltaPixels.divide( - this.containerSize.x * this.getZoom( current ) - ); - }, - - /** - * @function - */ - pixelFromPoint: function( point, current ) { - var bounds = this.getBounds( current ); - return point.minus( - bounds.getTopLeft() - ).times( - this.containerSize.x / bounds.width - ); - }, - - /** - * @function - */ - pointFromPixel: function( pixel, current ) { - var bounds = this.getBounds( current ); - return pixel.divide( - this.containerSize.x / bounds.width - ).plus( - bounds.getTopLeft() - ); - }, - - /** - * Translates from Seajax viewer coordinate - * system to image coordinate system - */ - viewportToImageCoordinates: function(viewerX, viewerY) { - return new $.Point(viewerX * this.contentSize.x, viewerY * this.contentSize.y * this.contentAspectX); - }, - - /** - * Translates from image coordinate system to - * Seajax viewer coordinate system - */ - imageToViewportCoordinates: function( imageX, imageY ) { - return new $.Point( imageX / this.contentSize.x, imageY / this.contentSize.y / this.contentAspectX); - }, - - /** - * Translates from a rectanlge which describes a portion of - * the image in pixel coordinates to OpenSeadragon viewport - * rectangle coordinates. - */ - imageToViewportRectangle: function( imageX, imageY, pixelWidth, pixelHeight ) { - var coordA, - coordB, - rect; - if( arguments.length == 1 ){ - //they passed a rectangle instead of individual components - rect = imageX; - return this.imageToViewportRectangle(rect.x, rect.y, rect.width, rect.height); - } - coordA = this.imageToViewportCoordinates( - imageX, imageY - ); - coordB = this.imageToViewportCoordinates( - pixelWidth, pixelHeight - ); - return new $.Rect( - coordA.x, - coordA.y, - coordA.x + coordB.x, - coordA.y + coordB.y - ); - } -}; - -}( OpenSeadragon ));