openseadragon/src/openseadragon.js

2207 lines
78 KiB
JavaScript
Raw Normal View History

/*
* OpenSeadragon
*
* Copyright (C) 2009 CodePlex Foundation
* Copyright (C) 2010-2013 OpenSeadragon contributors
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of CodePlex Foundation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Portions of this source file taken from jQuery:
*
* Copyright 2011 John Resig
*
* 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.
*/
/*
* Portions of this source file taken from mattsnider.com:
*
* Copyright (c) 2006-2013 Matt Snider
*
* 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.
*/
/**
* @version <%= pkg.name %> <%= pkg.version %>
*
* @file
* <h2><strong>OpenSeadragon - Javascript Deep Zooming</strong></h2>
* <p>
* OpenSeadragon 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.
* </p>
*
*/
/**
* @module OpenSeadragon
*
*/
/**
* @namespace OpenSeadragon
*
2013-11-30 00:10:15 +04:00
* @classdesc The root namespace for OpenSeadragon. All utility methods
* and classes are defined on or below this namespace.
*
*/
// Typedefs
/**
* All required and optional settings for instantiating a new instance of an OpenSeadragon image viewer.
*
* @typedef {Object} Options
* @memberof OpenSeadragon
*
2013-11-25 20:48:44 +04:00
* @property {String} id
* Id of the element to append the viewer's container element to. If not provided, the 'element' property must be provided.
* If both the element and id properties are specified, the viewer is appended to the element provided in the element property.
*
* @property {Element} element
* The element to append the viewer's container element to. If not provided, the 'id' property must be provided.
* If both the element and id properties are specified, the viewer is appended to the element provided in the element property.
*
* @property {Array|String|Function|Object[]|Array[]|String[]|Function[]} [tileSources=null]
* As an Array, the tileSource can hold either Objects or mixed
* types of Arrays of Objects, Strings, or Functions. 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}.
*
* @property {String} [xmlPath=null]
* <strong>DEPRECATED</strong>. A relative path to load a DZI file from the server.
* Prefer the newer Options.tileSources.
*
* @property {String} [prefixUrl='/images/']
* Prepends the prefixUrl to navImages paths, which is very useful
* since the default paths are rarely useful for production
* environments.
*
* @property {OpenSeadragon.NavImages} [navImages]
* An object with a property for each button or other built-in navigation
* control, eg the current 'zoomIn', 'zoomOut', 'home', and 'fullpage'.
2013-11-25 22:38:50 +04:00
* Each of those in turn provides an image path for each state of the button
* 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.
*
* @property {Object} [tileHost=null]
* TODO: Implement this. Currently not used.
*
* @property {Boolean} [debugMode=false]
* TODO: provide an in-screen panel providing event detail feedback.
*
* @property {String} [debugGridColor='#437AB2']
*
* @property {Number} [animationTime=1.2]
* Specifies the animation duration per each {@link OpenSeadragon.Spring}
* which occur when the image is dragged or zoomed.
*
* @property {Number} [blendTime=0]
* Specifies the duration of animation as higher or lower level tiles are
* replacing the existing tile.
*
* @property {Boolean} [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.
*
* @property {Boolean} [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.
*
* @property {Boolean} [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.
*
* @property {Number} [defaultZoomLevel=0]
* Zoom level to use when image is first opened or the home button is clicked.
* If 0, adjusts to fit viewer.
*
* @property {Number} [degrees=0]
* Initial rotation.
*
* @property {Number} [minZoomLevel=null]
*
* @property {Number} [maxZoomLevel=null]
*
* @property {Boolean} [panHorizontal=true]
* Allow horizontal pan.
*
* @property {Boolean} [panVertical=true]
* Allow vertical pan.
*
* @property {Boolean} [constrainDuringPan=false]
*
* @property {Boolean} [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.
*
* @property {Boolean} [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.
*
* @property {Number} [minZoomImageRatio=0.9]
* 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.
*
* @property {Number} [maxZoomPixelRatio=1.1]
* 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.
*
* @property {Boolean} [autoResize=true]
2013-11-26 00:10:19 +04:00
* Set to false to prevent polling for viewer size changes. Useful for providing custom resize behavior.
*
* @property {Number} [pixelsPerWheelLine=40]
* For pixel-resolution scrolling devices, the number of pixels equal to one scroll line.
*
* @property {Number} [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.
*
* @property {Number} [springStiffness=7.0]
*
* @property {Number} [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.
*
* @property {Number} [clickTimeThreshold=300]
* If multiple mouse clicks occurs within less than this number of
* milliseconds, treat them as a single click.
*
* @property {Number} [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.
*
* @property {Number} [zoomPerClick=2.0]
* The "zoom distance" per mouse click or touch tap. <em><strong>Note:</strong> Setting this to 1.0 effectively disables the click-to-zoom feature.</em>
*
* @property {Number} [zoomPerScroll=1.2]
* The "zoom distance" per mouse scroll or touch pinch. <em><strong>Note:</strong> Setting this to 1.0 effectively disables the mouse-wheel zoom feature.</em>
*
* @property {Number} [zoomPerSecond=1.0]
* The number of seconds to animate a single zoom event over.
*
* @property {Boolean} [showNavigationControl=true]
* Set to false to prevent the appearance of the default navigation controls.
*
* @property {OpenSeadragon.ControlAnchor} [navigationControlAnchor=TOP_LEFT]
* Placement of the default navigation controls.
*
* @property {Boolean} [showNavigator=false]
* Set to true to make the navigator minimap appear.
*
* @property {Boolean} [navigatorId=navigator-GENERATED DATE]
Enhanced Navigator Resizability (#280, #296) New navigator options: * @property {Boolean} [showNavigator=false] * Set to true to make the navigator minimap appear. * * @property {Boolean} [navigatorId=navigator-GENERATED DATE] * The ID of a div to hold the navigator minimap. * If an ID is specified, the navigatorPosition, navigatorSizeRatio, navigatorMaintainSizeRatio, and navigatorTop|Left|Height|Width options will be ignored. * If an ID is not specified, a div element will be generated and placed on top of the main image. * * @property {String} [navigatorPosition='TOP_RIGHT'] * Valid values are 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', 'BOTTOM_RIGHT', or 'ABSOLUTE'.<br> * If 'ABSOLUTE' is specified, then navigatorTop|Left|Height|Width determines the size and position of the navigator minimap in the viewer, and navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.<br> * For 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', and 'BOTTOM_RIGHT', the navigatorSizeRatio or navigatorHeight|Width values determine the size of the navigator minimap. * * @property {Number} [navigatorSizeRatio=0.2] * Ratio of navigator size to viewer size. Ignored if navigatorHeight|Width are specified. * * @property {Boolean} [navigatorMaintainSizeRatio=false] * If true, the navigator minimap is resized (using navigatorSizeRatio) when the viewer size changes. * * @property {Number|String} [navigatorTop=null] * Specifies the location of the navigator minimap (see navigatorPosition). * * @property {Number|String} [navigatorLeft=null] * Specifies the location of the navigator minimap (see navigatorPosition). * * @property {Number|String} [navigatorHeight=null] * Specifies the size of the navigator minimap (see navigatorPosition). * If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored. * * @property {Number|String} [navigatorWidth=null] * Specifies the size of the navigator minimap (see navigatorPosition). * If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored. Fixes #280 and #296
2013-12-13 21:23:56 +04:00
* The ID of a div to hold the navigator minimap.
* If an ID is specified, the navigatorPosition, navigatorSizeRatio, navigatorMaintainSizeRatio, and navigatorTop|Left|Height|Width options will be ignored.
* If an ID is not specified, a div element will be generated and placed on top of the main image.
*
Enhanced Navigator Resizability (#280, #296) New navigator options: * @property {Boolean} [showNavigator=false] * Set to true to make the navigator minimap appear. * * @property {Boolean} [navigatorId=navigator-GENERATED DATE] * The ID of a div to hold the navigator minimap. * If an ID is specified, the navigatorPosition, navigatorSizeRatio, navigatorMaintainSizeRatio, and navigatorTop|Left|Height|Width options will be ignored. * If an ID is not specified, a div element will be generated and placed on top of the main image. * * @property {String} [navigatorPosition='TOP_RIGHT'] * Valid values are 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', 'BOTTOM_RIGHT', or 'ABSOLUTE'.<br> * If 'ABSOLUTE' is specified, then navigatorTop|Left|Height|Width determines the size and position of the navigator minimap in the viewer, and navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.<br> * For 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', and 'BOTTOM_RIGHT', the navigatorSizeRatio or navigatorHeight|Width values determine the size of the navigator minimap. * * @property {Number} [navigatorSizeRatio=0.2] * Ratio of navigator size to viewer size. Ignored if navigatorHeight|Width are specified. * * @property {Boolean} [navigatorMaintainSizeRatio=false] * If true, the navigator minimap is resized (using navigatorSizeRatio) when the viewer size changes. * * @property {Number|String} [navigatorTop=null] * Specifies the location of the navigator minimap (see navigatorPosition). * * @property {Number|String} [navigatorLeft=null] * Specifies the location of the navigator minimap (see navigatorPosition). * * @property {Number|String} [navigatorHeight=null] * Specifies the size of the navigator minimap (see navigatorPosition). * If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored. * * @property {Number|String} [navigatorWidth=null] * Specifies the size of the navigator minimap (see navigatorPosition). * If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored. Fixes #280 and #296
2013-12-13 21:23:56 +04:00
* @property {String} [navigatorPosition='TOP_RIGHT']
* Valid values are 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', 'BOTTOM_RIGHT', or 'ABSOLUTE'.<br>
* If 'ABSOLUTE' is specified, then navigatorTop|Left|Height|Width determines the size and position of the navigator minimap in the viewer, and navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.<br>
* For 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', and 'BOTTOM_RIGHT', the navigatorSizeRatio or navigatorHeight|Width values determine the size of the navigator minimap.
*
Enhanced Navigator Resizability (#280, #296) New navigator options: * @property {Boolean} [showNavigator=false] * Set to true to make the navigator minimap appear. * * @property {Boolean} [navigatorId=navigator-GENERATED DATE] * The ID of a div to hold the navigator minimap. * If an ID is specified, the navigatorPosition, navigatorSizeRatio, navigatorMaintainSizeRatio, and navigatorTop|Left|Height|Width options will be ignored. * If an ID is not specified, a div element will be generated and placed on top of the main image. * * @property {String} [navigatorPosition='TOP_RIGHT'] * Valid values are 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', 'BOTTOM_RIGHT', or 'ABSOLUTE'.<br> * If 'ABSOLUTE' is specified, then navigatorTop|Left|Height|Width determines the size and position of the navigator minimap in the viewer, and navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.<br> * For 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', and 'BOTTOM_RIGHT', the navigatorSizeRatio or navigatorHeight|Width values determine the size of the navigator minimap. * * @property {Number} [navigatorSizeRatio=0.2] * Ratio of navigator size to viewer size. Ignored if navigatorHeight|Width are specified. * * @property {Boolean} [navigatorMaintainSizeRatio=false] * If true, the navigator minimap is resized (using navigatorSizeRatio) when the viewer size changes. * * @property {Number|String} [navigatorTop=null] * Specifies the location of the navigator minimap (see navigatorPosition). * * @property {Number|String} [navigatorLeft=null] * Specifies the location of the navigator minimap (see navigatorPosition). * * @property {Number|String} [navigatorHeight=null] * Specifies the size of the navigator minimap (see navigatorPosition). * If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored. * * @property {Number|String} [navigatorWidth=null] * Specifies the size of the navigator minimap (see navigatorPosition). * If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored. Fixes #280 and #296
2013-12-13 21:23:56 +04:00
* @property {Number} [navigatorSizeRatio=0.2]
* Ratio of navigator size to viewer size. Ignored if navigatorHeight|Width are specified.
*
Enhanced Navigator Resizability (#280, #296) New navigator options: * @property {Boolean} [showNavigator=false] * Set to true to make the navigator minimap appear. * * @property {Boolean} [navigatorId=navigator-GENERATED DATE] * The ID of a div to hold the navigator minimap. * If an ID is specified, the navigatorPosition, navigatorSizeRatio, navigatorMaintainSizeRatio, and navigatorTop|Left|Height|Width options will be ignored. * If an ID is not specified, a div element will be generated and placed on top of the main image. * * @property {String} [navigatorPosition='TOP_RIGHT'] * Valid values are 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', 'BOTTOM_RIGHT', or 'ABSOLUTE'.<br> * If 'ABSOLUTE' is specified, then navigatorTop|Left|Height|Width determines the size and position of the navigator minimap in the viewer, and navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.<br> * For 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', and 'BOTTOM_RIGHT', the navigatorSizeRatio or navigatorHeight|Width values determine the size of the navigator minimap. * * @property {Number} [navigatorSizeRatio=0.2] * Ratio of navigator size to viewer size. Ignored if navigatorHeight|Width are specified. * * @property {Boolean} [navigatorMaintainSizeRatio=false] * If true, the navigator minimap is resized (using navigatorSizeRatio) when the viewer size changes. * * @property {Number|String} [navigatorTop=null] * Specifies the location of the navigator minimap (see navigatorPosition). * * @property {Number|String} [navigatorLeft=null] * Specifies the location of the navigator minimap (see navigatorPosition). * * @property {Number|String} [navigatorHeight=null] * Specifies the size of the navigator minimap (see navigatorPosition). * If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored. * * @property {Number|String} [navigatorWidth=null] * Specifies the size of the navigator minimap (see navigatorPosition). * If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored. Fixes #280 and #296
2013-12-13 21:23:56 +04:00
* @property {Boolean} [navigatorMaintainSizeRatio=false]
* If true, the navigator minimap is resized (using navigatorSizeRatio) when the viewer size changes.
*
Enhanced Navigator Resizability (#280, #296) New navigator options: * @property {Boolean} [showNavigator=false] * Set to true to make the navigator minimap appear. * * @property {Boolean} [navigatorId=navigator-GENERATED DATE] * The ID of a div to hold the navigator minimap. * If an ID is specified, the navigatorPosition, navigatorSizeRatio, navigatorMaintainSizeRatio, and navigatorTop|Left|Height|Width options will be ignored. * If an ID is not specified, a div element will be generated and placed on top of the main image. * * @property {String} [navigatorPosition='TOP_RIGHT'] * Valid values are 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', 'BOTTOM_RIGHT', or 'ABSOLUTE'.<br> * If 'ABSOLUTE' is specified, then navigatorTop|Left|Height|Width determines the size and position of the navigator minimap in the viewer, and navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.<br> * For 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', and 'BOTTOM_RIGHT', the navigatorSizeRatio or navigatorHeight|Width values determine the size of the navigator minimap. * * @property {Number} [navigatorSizeRatio=0.2] * Ratio of navigator size to viewer size. Ignored if navigatorHeight|Width are specified. * * @property {Boolean} [navigatorMaintainSizeRatio=false] * If true, the navigator minimap is resized (using navigatorSizeRatio) when the viewer size changes. * * @property {Number|String} [navigatorTop=null] * Specifies the location of the navigator minimap (see navigatorPosition). * * @property {Number|String} [navigatorLeft=null] * Specifies the location of the navigator minimap (see navigatorPosition). * * @property {Number|String} [navigatorHeight=null] * Specifies the size of the navigator minimap (see navigatorPosition). * If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored. * * @property {Number|String} [navigatorWidth=null] * Specifies the size of the navigator minimap (see navigatorPosition). * If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored. Fixes #280 and #296
2013-12-13 21:23:56 +04:00
* @property {Number|String} [navigatorTop=null]
* Specifies the location of the navigator minimap (see navigatorPosition).
*
* @property {Number|String} [navigatorLeft=null]
* Specifies the location of the navigator minimap (see navigatorPosition).
*
* @property {Number|String} [navigatorHeight=null]
* Specifies the size of the navigator minimap (see navigatorPosition).
* If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.
*
* @property {Number|String} [navigatorWidth=null]
* Specifies the size of the navigator minimap (see navigatorPosition).
* If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.
*
* @property {Boolean} [navigatorAutoResize=true]
* Set to false to prevent polling for navigator size changes. Useful for providing custom resize behavior.
* Setting to false can also improve performance when the navigator is configured to a fixed size.
*
* @property {Number} [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.
*
* @property {Number} [controlsFadeLength=1500]
* The number of milliseconds to animate the controls fading out.
*
* @property {Number} [maxImageCacheCount=200]
* The max number of images we should keep in memory (per drawer).
*
* @property {Number} [timeout=30000]
*
* @property {Boolean} [useCanvas=true]
* Set to false to not use an HTML canvas element for image rendering even if canvas is supported.
*
* @property {Number} [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.
*
* @property {Boolean} [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.
*
* @property {Boolean} [navPrevNextWrap=false]
* If the 'previous' button will wrap to the last image when viewing the first
* image and if the 'next' button will wrap to the first image when viewing
* the last image.
*
* @property {Boolean} [showSequenceControl=true]
* If the viewer has been configured with a sequence of tile sources, then
* provide buttons for navigating forward and backward through the images.
*
* @property {OpenSeadragon.ControlAnchor} [sequenceControlAnchor=TOP_LEFT]
* Placement of the default sequence controls.
*
* @property {Number} [initialPage=0]
* If the viewer has been configured with a sequence of tile sources, display this page initially.
*
* @property {Boolean} [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.
*
* @property {Boolean} [showReferenceStrip=false]
* If the viewer has been configured with a sequence of tile sources, then
* display a scrolling strip of image thumbnails for navigating through the images.
*
* @property {String} [referenceStripScroll='horizontal']
*
* @property {Element} [referenceStripElement=null]
*
* @property {Number} [referenceStripHeight=null]
*
* @property {Number} [referenceStripWidth=null]
*
* @property {String} [referenceStripPosition='BOTTOM_LEFT']
*
* @property {Number} [referenceStripSizeRatio=0.2]
*
* @property {Boolean} [collectionMode=false]
*
* @property {Number} [collectionRows=3]
*
* @property {String} [collectionLayout='horizontal']
*
* @property {Number} [collectionTileSize=800]
*
*/
/**
* The names for the image resources used for the image navigation buttons.
*
* @typedef {Object} NavImages
* @memberof OpenSeadragon
*
* @property {Object} zoomIn - Images for the zoom-in button.
* @property {String} zoomIn.REST
* @property {String} zoomIn.GROUP
* @property {String} zoomIn.HOVER
* @property {String} zoomIn.DOWN
*
* @property {Object} zoomOut - Images for the zoom-out button.
* @property {String} zoomOut.REST
* @property {String} zoomOut.GROUP
* @property {String} zoomOut.HOVER
* @property {String} zoomOut.DOWN
*
* @property {Object} home - Images for the home button.
* @property {String} home.REST
* @property {String} home.GROUP
* @property {String} home.HOVER
* @property {String} home.DOWN
*
* @property {Object} fullpage - Images for the full-page button.
* @property {String} fullpage.REST
* @property {String} fullpage.GROUP
* @property {String} fullpage.HOVER
* @property {String} fullpage.DOWN
*
* @property {Object} previous - Images for the previous button.
* @property {String} previous.REST
* @property {String} previous.GROUP
* @property {String} previous.HOVER
* @property {String} previous.DOWN
*
* @property {Object} next - Images for the next button.
* @property {String} next.REST
* @property {String} next.GROUP
* @property {String} next.HOVER
* @property {String} next.DOWN
*
*/
/**
* This function serves as a single point of instantiation for an {@link OpenSeadragon.Viewer}, including all
* combinations of out-of-the-box configurable features.
*
* @function OpenSeadragon
* @memberof module:OpenSeadragon
* @param {OpenSeadragon.Options} options - Viewer options.
* @returns {OpenSeadragon.Viewer}
*/
window.OpenSeadragon = window.OpenSeadragon || function( options ){
return new OpenSeadragon.Viewer( options );
};
(function( $ ){
/**
* The OpenSeadragon version.
*
* @member {Object} OpenSeadragon.version
* @property {String} versionStr - The version number as a string ('major.minor.revision').
* @property {Number} major - The major version number.
* @property {Number} minor - The minor version number.
* @property {Number} revision - The revision number.
* @since 1.0.0
*/
/* jshint ignore:start */
$.version = {
versionStr: '<%= osdVersion.versionStr %>',
major: <%= osdVersion.major %>,
minor: <%= osdVersion.minor %>,
revision: <%= osdVersion.revision %>
};
/* jshint ignore:end */
/**
* 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;
/**
* Taken from jQuery 1.6.1
* @function isFunction
* @memberof OpenSeadragon
* @see {@link http://www.jquery.com/ jQuery}
*/
$.isFunction = function( obj ) {
return $.type(obj) === "function";
};
/**
* Taken from jQuery 1.6.1
* @function isArray
* @memberof OpenSeadragon
* @see {@link http://www.jquery.com/ 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
* @function isWindow
* @memberof OpenSeadragon
* @see {@link http://www.jquery.com/ jQuery}
*/
$.isWindow = function( obj ) {
return obj && typeof obj === "object" && "setInterval" in obj;
};
/**
* Taken from jQuery 1.6.1
* @function type
* @memberof OpenSeadragon
* @see {@link http://www.jquery.com/ jQuery}
*/
$.type = function( obj ) {
return ( obj === null ) || ( obj === undefined ) ?
String( obj ) :
class2type[ toString.call(obj) ] || "object";
};
/**
* Taken from jQuery 1.6.1
* @function isPlainObject
* @memberof OpenSeadragon
* @see {@link http://www.jquery.com/ 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
* @function isEmptyObject
* @memberof OpenSeadragon
* @see {@link http://www.jquery.com/ jQuery}
*/
$.isEmptyObject = function( obj ) {
for ( var name in obj ) {
return false;
}
return true;
};
/**
* True if the browser supports the HTML5 canvas element
* @member {Boolean} supportsCanvas
* @memberof OpenSeadragon
*/
2013-11-12 01:33:27 +04:00
$.supportsCanvas = (function () {
var canvasElement = document.createElement( 'canvas' );
return !!( $.isFunction( canvasElement.getContext ) &&
canvasElement.getContext( '2d' ) );
}());
}( 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 an 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
* @function extend
* @memberof OpenSeadragon
* @see {@link http://www.jquery.com/ 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( $, /** @lends OpenSeadragon */{
/**
2013-11-25 20:48:44 +04:00
* The default values for the optional settings documented at {@link OpenSeadragon.Options}.
* @static
2013-11-25 20:48:44 +04:00
* @type {Object}
*/
DEFAULT_SETTINGS: {
//DATA SOURCE DETAILS
xmlPath: null,
tileSources: null,
tileHost: null,
initialPage: 0,
//PAN AND ZOOM SETTINGS AND CONSTRAINTS
panHorizontal: true,
panVertical: true,
constrainDuringPan: false,
wrapHorizontal: false,
wrapVertical: false,
visibilityRatio: 0.5, //-> how much of the viewer can be negative space
minPixelRatio: 0.5, //->closer to 0 draws tiles meant for a higher zoom at this zoom
defaultZoomLevel: 0,
minZoomLevel: null,
maxZoomLevel: null,
//UI RESPONSIVENESS AND FEEL
springStiffness: 7.0,
clickTimeThreshold: 300,
clickDistThreshold: 5,
zoomPerClick: 2,
zoomPerScroll: 1.2,
zoomPerSecond: 1.0,
animationTime: 1.2,
blendTime: 0,
alwaysBlend: false,
autoHideControls: true,
immediateRender: false,
minZoomImageRatio: 0.9, //-> closer to 0 allows zoom out to infinity
maxZoomPixelRatio: 1.1, //-> higher allows 'over zoom' into pixels
pixelsPerWheelLine: 40,
2013-11-25 22:27:03 +04:00
autoResize: true,
//DEFAULT CONTROL SETTINGS
showSequenceControl: true, //SEQUENCE
sequenceControlAnchor: null, //SEQUENCE
preserveViewport: false, //SEQUENCE
showNavigationControl: true, //ZOOM/HOME/FULL/SEQUENCE
navigationControlAnchor: null, //ZOOM/HOME/FULL
controlsFadeDelay: 2000, //ZOOM/HOME/FULL/SEQUENCE
controlsFadeLength: 1500, //ZOOM/HOME/FULL/SEQUENCE
mouseNavEnabled: true, //GENERAL MOUSE INTERACTIVITY
//VIEWPORT NAVIGATOR SETTINGS
Enhanced Navigator Resizability (#280, #296) New navigator options: * @property {Boolean} [showNavigator=false] * Set to true to make the navigator minimap appear. * * @property {Boolean} [navigatorId=navigator-GENERATED DATE] * The ID of a div to hold the navigator minimap. * If an ID is specified, the navigatorPosition, navigatorSizeRatio, navigatorMaintainSizeRatio, and navigatorTop|Left|Height|Width options will be ignored. * If an ID is not specified, a div element will be generated and placed on top of the main image. * * @property {String} [navigatorPosition='TOP_RIGHT'] * Valid values are 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', 'BOTTOM_RIGHT', or 'ABSOLUTE'.<br> * If 'ABSOLUTE' is specified, then navigatorTop|Left|Height|Width determines the size and position of the navigator minimap in the viewer, and navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.<br> * For 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', and 'BOTTOM_RIGHT', the navigatorSizeRatio or navigatorHeight|Width values determine the size of the navigator minimap. * * @property {Number} [navigatorSizeRatio=0.2] * Ratio of navigator size to viewer size. Ignored if navigatorHeight|Width are specified. * * @property {Boolean} [navigatorMaintainSizeRatio=false] * If true, the navigator minimap is resized (using navigatorSizeRatio) when the viewer size changes. * * @property {Number|String} [navigatorTop=null] * Specifies the location of the navigator minimap (see navigatorPosition). * * @property {Number|String} [navigatorLeft=null] * Specifies the location of the navigator minimap (see navigatorPosition). * * @property {Number|String} [navigatorHeight=null] * Specifies the size of the navigator minimap (see navigatorPosition). * If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored. * * @property {Number|String} [navigatorWidth=null] * Specifies the size of the navigator minimap (see navigatorPosition). * If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored. Fixes #280 and #296
2013-12-13 21:23:56 +04:00
showNavigator: false,
navigatorId: null,
navigatorPosition: null,
navigatorSizeRatio: 0.2,
navigatorMaintainSizeRatio: false,
navigatorTop: null,
navigatorLeft: null,
navigatorHeight: null,
navigatorWidth: null,
navigatorAutoResize: true,
// INITIAL ROTATION
degrees: 0,
//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,
//PERFORMANCE SETTINGS
imageLoaderLimit: 0,
maxImageCacheCount: 200,
timeout: 30000,
useCanvas: true, // Use canvas element for drawing if available
//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'
}
},
navPrevNextWrap: false,
//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----",
/**
2013-11-25 20:48:44 +04:00
* Returns a function which invokes the method as if it were a method belonging to the object.
* @function
* @param {Object} object
* @param {Function} method
2013-11-25 20:48:44 +04:00
* @returns {Function}
*/
delegate: function( object, method ) {
return function(){
var args = arguments;
if ( args === undefined ){
args = [];
}
return method.apply( object, args );
};
},
/**
* An enumeration of Browser vendors.
* @static
* @type {Object}
* @property {Number} UNKNOWN
* @property {Number} IE
* @property {Number} FIREFOX
* @property {Number} SAFARI
* @property {Number} CHROME
* @property {Number} OPERA
*/
BROWSERS: {
UNKNOWN: 0,
IE: 1,
FIREFOX: 2,
SAFARI: 3,
CHROME: 4,
OPERA: 5
},
/**
* Returns a DOM Element for the given id or element.
* @function
* @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
* @param {Element|String} element - the elemenet we want the position for.
* @returns {OpenSeadragon.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 position of the upper-left corner of the element adjusted for current page and/or element scroll.
* @function
* @param {Element|String} element - the element we want the position for.
* @returns {OpenSeadragon.Point} - the position of the upper left corner of the element adjusted for current page and/or element scroll.
*/
getElementOffset: function( element ) {
element = $.getElement( element );
var doc = element && element.ownerDocument,
docElement,
win,
boundingRect = { top: 0, left: 0 };
if ( !doc ) {
return new $.Point();
}
docElement = doc.documentElement;
if ( typeof element.getBoundingClientRect !== typeof undefined ) {
boundingRect = element.getBoundingClientRect();
}
win = ( doc == doc.window ) ?
doc :
( doc.nodeType === 9 ) ?
doc.defaultView || doc.parentWindow :
false;
return new $.Point(
boundingRect.left + ( win.pageXOffset || docElement.scrollLeft ) - ( docElement.clientLeft || 0 ),
boundingRect.top + ( win.pageYOffset || docElement.scrollTop ) - ( docElement.clientTop || 0 )
);
},
/**
* Determines the height and width of the given element.
* @function
* @param {Element|String} element
* @returns {OpenSeadragon.Point}
*/
getElementSize: function( element ) {
element = $.getElement( element );
return new $.Point(
element.clientWidth,
element.clientHeight
);
},
/**
* Returns the CSSStyle object for the given element.
* @function
* @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
2013-11-25 20:48:44 +04:00
* specific to IE behavior.
* @function
* @param {Event} [event]
* @returns {Event}
* @deprecated For internal use only
2013-11-25 20:48:44 +04:00
* @private
*/
getEvent: function( event ) {
if( event ){
$.getEvent = function( event ) {
return event;
};
} else {
$.getEvent = function() {
return window.event;
};
}
return $.getEvent( event );
},
/**
* Gets the position of the mouse on the screen for a given event.
* @function
* @param {Event} [event]
* @returns {OpenSeadragon.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."
);
}
2012-01-18 08:13:29 +04:00
return $.getMousePosition( event );
},
/**
2013-11-02 01:37:19 +04:00
* Determines the page's current scroll position.
* @function
* @returns {OpenSeadragon.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 {
// We can't reassign the function yet, as there was no scroll.
return new $.Point(0,0);
}
return $.getPageScroll();
},
/**
* Set the page scroll position.
* @function
* @returns {OpenSeadragon.Point}
*/
setPageScroll: function( scroll ) {
if ( typeof ( window.scrollTo ) !== "undefined" ) {
$.setPageScroll = function( scroll ) {
window.scrollTo( scroll.x, scroll.y );
};
} else {
2013-11-02 01:37:19 +04:00
var originalScroll = $.getPageScroll();
if ( originalScroll.x === scroll.x &&
originalScroll.y === scroll.y ) {
// We are already correctly positioned and there
// is no way to detect the correct method.
return;
}
document.body.scrollLeft = scroll.x;
document.body.scrollTop = scroll.y;
2013-11-02 01:37:19 +04:00
var currentScroll = $.getPageScroll();
if ( currentScroll.x !== originalScroll.x &&
currentScroll.y !== originalScroll.y ) {
$.setPageScroll = function( scroll ) {
document.body.scrollLeft = scroll.x;
document.body.scrollTop = scroll.y;
};
return;
}
document.documentElement.scrollLeft = scroll.x;
document.documentElement.scrollTop = scroll.y;
currentScroll = $.getPageScroll();
2013-11-02 01:37:19 +04:00
if ( currentScroll.x !== originalScroll.x &&
currentScroll.y !== originalScroll.y ) {
$.setPageScroll = function( scroll ) {
document.documentElement.scrollLeft = scroll.x;
document.documentElement.scrollTop = scroll.y;
};
return;
}
// We can't find anything working, so we do nothing.
$.setPageScroll = function( scroll ) {
};
}
return $.setPageScroll( scroll );
},
/**
* Determines the size of the browsers window.
* @function
* @returns {OpenSeadragon.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 using CSS tables
* @function
* @param {Element|String} element
* @returns {Element} outermost wrapper element
*/
makeCenteredNode: function( element ) {
// Convert a possible ID to an actual HTMLElement
element = $.getElement( element );
/*
CSS tables require you to have a display:table/row/cell hierarchy so we need to create
three nested wrapper divs:
*/
var wrappers = [
$.makeNeutralElement( 'div' ),
$.makeNeutralElement( 'div' ),
$.makeNeutralElement( 'div' )
];
// It feels like we should be able to pass style dicts to makeNeutralElement:
$.extend(wrappers[0].style, {
display: "table",
height: "100%",
width: "100%"
});
$.extend(wrappers[1].style, {
display: "table-row"
});
$.extend(wrappers[2].style, {
display: "table-cell",
verticalAlign: "middle",
textAlign: "center"
});
wrappers[0].appendChild(wrappers[1]);
wrappers[1].appendChild(wrappers[2]);
wrappers[2].appendChild(element);
return wrappers[0];
},
/**
* Creates an easily positionable element of the given type that therefor
* serves as an excellent container element.
* @function
* @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;
},
2013-06-19 22:16:45 +04:00
/**
* Returns the current milliseconds, using Date.now() if available
* @function
*/
now: function( ) {
if (Date.now) {
$.now = Date.now;
} else {
2013-06-20 20:08:21 +04:00
$.now = function() { return new Date().getTime(); };
2013-06-19 22:16:45 +04:00
}
return $.now();
2013-06-19 22:16:45 +04:00
},
/**
* Ensures an image is loaded correctly to support alpha transparency.
* Generally only IE has issues doing this correctly for formats like
* png.
* @function
* @param {String} src
* @returns {Element}
*/
makeTransparentImage: function( src ) {
$.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
* @param {Element|String} element
* @param {Number} opacity
* @param {Boolean} [usesAlpha]
*/
setElementOpacity: function( element, opacity, usesAlpha ) {
var ieOpacity,
ieFilter;
element = $.getElement( element );
if ( usesAlpha && !$.Browser.alpha ) {
opacity = Math.round( opacity );
}
if ( $.Browser.opacity ) {
element.style.opacity = opacity < 1 ? opacity : "";
} else {
if ( opacity < 1 ) {
ieOpacity = Math.round( 100 * opacity );
ieFilter = "alpha(opacity=" + ieOpacity + ")";
element.style.filter = ieFilter;
} else {
element.style.filter = "";
}
}
},
/**
* Add the specified CSS class to the element if not present.
* @function
* @param {Element|String} element
* @param {String} className
*/
addClass: function( element, className ) {
element = $.getElement( element );
if ( ! element.className ) {
element.className = className;
} else if ( ( ' ' + element.className + ' ' ).
indexOf( ' ' + className + ' ' ) === -1 ) {
element.className += ' ' + className;
}
},
/**
* Remove the specified CSS class from the element.
* @function
* @param {Element|String} element
* @param {String} className
*/
removeClass: function( element, className ) {
var oldClasses,
newClasses = [],
i;
element = $.getElement( element );
oldClasses = element.className.split( /\s+/ );
for ( i = 0; i < oldClasses.length; i++ ) {
if ( oldClasses[ i ] && oldClasses[ i ] !== className ) {
newClasses.push( oldClasses[ i ] );
}
}
element.className = newClasses.join(' ');
},
/**
* Adds an event listener for the given element, eventName and handler.
* @function
* @param {Element|String} element
* @param {String} eventName
* @param {Function} handler
* @param {Boolean} [useCapture]
*/
2013-11-12 01:33:27 +04:00
addEvent: (function () {
if ( window.addEventListener ) {
return function ( element, eventName, handler, useCapture ) {
element = $.getElement( element );
element.addEventListener( eventName, handler, useCapture );
};
} else if ( window.attachEvent ) {
return function ( element, eventName, handler, useCapture ) {
element = $.getElement( element );
element.attachEvent( 'on' + eventName, handler );
if ( useCapture && element.setCapture ) {
element.setCapture();
}
};
} else {
throw new Error( "No known event model." );
}
}()),
/**
* Remove a given event listener for the given element, event type and
* handler.
* @function
* @param {Element|String} element
* @param {String} eventName
* @param {Function} handler
* @param {Boolean} [useCapture]
*/
2013-11-12 01:33:27 +04:00
removeEvent: (function () {
if ( window.removeEventListener ) {
return function ( element, eventName, handler, useCapture ) {
element = $.getElement( element );
element.removeEventListener( eventName, handler, useCapture );
};
} else if ( window.detachEvent ) {
return function( element, eventName, handler, useCapture ) {
element = $.getElement( element );
element.detachEvent( 'on' + eventName, handler );
if ( useCapture && element.releaseCapture ) {
element.releaseCapture();
}
};
} else {
throw new Error( "No known event model." );
}
}()),
/**
* Cancels the default browser behavior had the event propagated all
* the way up the DOM to the window object.
* @function
* @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
* @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
* @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
* @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.XMLHttpRequest ) {
$.createAjaxRequest = function( ){
return new XMLHttpRequest();
};
request = new XMLHttpRequest();
} else if ( window.ActiveXObject ) {
/*jshint loopfunc:true*/
2013-06-19 02:05:00 +04:00
/* global ActiveXObject:true */
for ( var i = 0; i < ACTIVEX.length; i++ ) {
try {
request = new ActiveXObject( ACTIVEX[ i ] );
$.createAjaxRequest = function( ){
return new ActiveXObject( ACTIVEX[ i ] );
};
break;
} catch (e) {
continue;
}
}
}
if ( !request ) {
throw new Error( "Browser doesn't support XMLHttpRequest." );
}
return request;
},
/**
* Makes an AJAX request.
* @function
* @param {String} url - the url to request
* @param {Function} onSuccess - a function to call on a successful response
* @param {Function} onError - a function to call on when an error occurs
* @throws {Error}
*/
makeAjaxRequest: function( url, onSuccess, onError ) {
var request = $.createAjaxRequest();
2013-06-28 22:54:44 +04:00
if ( !$.isFunction( onSuccess ) ) {
throw new Error( "makeAjaxRequest requires a success callback" );
}
request.onreadystatechange = function() {
// 4 = DONE (https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#Properties)
2013-06-27 01:07:37 +04:00
if ( request.readyState == 4 ) {
request.onreadystatechange = function(){};
2013-06-27 01:07:37 +04:00
if ( request.status == 200 ) {
onSuccess( request );
} else {
2013-06-28 23:01:56 +04:00
$.console.log( "AJAX request returned %s: %s", request.status, url );
2013-06-28 22:54:44 +04:00
if ( $.isFunction( onError ) ) {
onError( request );
}
}
}
};
try {
request.open( "GET", url, true );
request.send( null );
} catch (e) {
var msg = e.message;
/*
IE < 10 does not support CORS and an XHR request to a different origin will fail as soon
as send() is called. This is particularly easy to miss during development and appear in
production if you use a CDN or domain sharding and the security policy is likely to break
exception handlers since any attempt to access a property of the request object will
raise an access denied TypeError inside the catch block.
To be friendlier, we'll check for this specific error and add a documentation pointer
to point developers in the right direction. We test the exception number because IE's
error messages are localized.
*/
var oldIE = $.Browser.vendor == $.BROWSERS.IE && $.Browser.version < 10;
if ( oldIE && typeof( e.number ) != "undefined" && e.number == -2147024891 ) {
msg += "\nSee http://msdn.microsoft.com/en-us/library/ms537505(v=vs.85).aspx#xdomain";
}
$.console.log( "%s while making AJAX request: %s", e.name, msg );
request.onreadystatechange = function(){};
2013-06-28 22:54:44 +04:00
if ( $.isFunction( onError ) ) {
onError( request, e );
}
}
},
/**
* Taken from jQuery 1.6.1
* @function
* @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,
2013-06-21 00:31:04 +04:00
jsonpCallback = options.callbackName || 'openseadragon' + $.now(),
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 );
},
/**
* Fully deprecated. Will throw an error.
* @function
* @deprecated use {@link OpenSeadragon.Viewer#open}
*/
createFromDZI: function() {
throw "OpenSeadragon.createFromDZI is deprecated, use Viewer.open.";
},
/**
* Parses an XML string into a DOM Document.
* @function
* @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;
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
* @param {String} [extension]
* @returns {Boolean}
*/
imageFormatSupported: function( extension ) {
extension = extension ? extension : "";
return !!FILEFORMATS[ extension.toLowerCase() ];
}
});
/**
2013-11-25 20:48:44 +04:00
* The current browser vendor, version, and related information regarding detected features.
* @member {Object} Browser
* @memberof OpenSeadragon
* @static
2013-11-25 20:48:44 +04:00
* @type {Object}
* @property {OpenSeadragon.BROWSERS} vendor - One of the {@link OpenSeadragon.BROWSERS} enumeration values.
* @property {Number} version
* @property {Boolean} alpha - Does the browser support image alpha transparency.
*/
$.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
)
);
//determine if this browser supports element.style.opacity
$.Browser.opacity = !(
$.Browser.vendor == $.BROWSERS.IE &&
$.Browser.version < 9
);
})();
//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
};
// Adding support for HTML5's requestAnimationFrame as suggested by acdha.
// Implementation taken from matt synder's post here:
2013-02-26 19:19:48 +04:00
// http://mattsnider.com/cross-browser-and-legacy-supported-requestframeanimation/
(function( w ) {
// most browsers have an implementation
var requestAnimationFrame = w.requestAnimationFrame ||
w.mozRequestAnimationFrame ||
2013-02-26 19:19:48 +04:00
w.webkitRequestAnimationFrame ||
w.msRequestAnimationFrame;
var cancelAnimationFrame = w.cancelAnimationFrame ||
w.mozCancelAnimationFrame ||
2013-02-26 19:19:48 +04:00
w.webkitCancelAnimationFrame ||
w.msCancelAnimationFrame;
// polyfill, when necessary
if ( requestAnimationFrame && cancelAnimationFrame ) {
// We can't assign these window methods directly to $ because they
// expect their "this" to be "window", so we call them in wrappers.
2013-05-24 20:17:26 +04:00
$.requestAnimationFrame = function(){
return requestAnimationFrame.apply( w, arguments );
2013-02-26 19:19:48 +04:00
};
2013-05-24 20:17:26 +04:00
$.cancelAnimationFrame = function(){
return cancelAnimationFrame.apply( w, arguments );
2013-02-26 19:19:48 +04:00
};
} else {
var aAnimQueue = [],
processing = [],
2013-02-26 19:19:48 +04:00
iRequestId = 0,
iIntervalId;
// create a mock requestAnimationFrame function
$.requestAnimationFrame = function( callback ) {
aAnimQueue.push( [ ++iRequestId, callback ] );
if ( !iIntervalId ) {
iIntervalId = setInterval( function() {
if ( aAnimQueue.length ) {
var time = $.now();
// Process all of the currently outstanding frame
2013-06-20 00:33:01 +04:00
// requests, but none that get added during the
// processing.
2013-06-19 20:58:22 +04:00
// Swap the arrays so we don't have to create a new
// array every frame.
var temp = processing;
processing = aAnimQueue;
2013-06-19 20:58:22 +04:00
aAnimQueue = temp;
while ( processing.length ) {
processing.shift()[ 1 ]( time );
}
2013-02-26 19:19:48 +04:00
} else {
// don't continue the interval, if unnecessary
clearInterval( iIntervalId );
iIntervalId = undefined;
}
}, 1000 / 50); // estimating support for 50 frames per second
}
return iRequestId;
};
// create a mock cancelAnimationFrame function
$.cancelAnimationFrame = function( requestId ) {
// find the request ID and remove it
var i, j;
for ( i = 0, j = aAnimQueue.length; i < j; i += 1 ) {
2013-02-26 19:19:48 +04:00
if ( aAnimQueue[ i ][ 0 ] === requestId ) {
aAnimQueue.splice( i, 1 );
return;
}
}
// If it's not in the queue, it may be in the set we're currently
2013-06-20 00:33:01 +04:00
// processing (if cancelAnimationFrame is called from within a
2013-06-19 20:58:22 +04:00
// requestAnimationFrame callback).
for ( i = 0, j = processing.length; i < j; i += 1 ) {
if ( processing[ i ][ 0 ] === requestId ) {
processing.splice( i, 1 );
return;
2013-02-26 19:19:48 +04:00
}
}
};
}
})( window );
/**
* @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
*/
$._processDZIError = function ( errorNode ) {
var messageNode = errorNode.getElementsByTagName( "Message" )[ 0 ],
message = messageNode.firstChild.nodeValue;
throw new Error(message);
};
}( OpenSeadragon ));