/src/openseadragon.js
JavaScript | 2472 lines | 1106 code | 272 blank | 1094 comment | 219 complexity | 53c050f174221960935c175fb6a8684c MD5 | raw file
Possible License(s): BSD-3-Clause
- /*
- * 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
- *
- * @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
- *
- * @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 {Array} overlays Array of objects defining permanent overlays of
- * the viewer. The overlays added via this option and later removed with
- * {@link OpenSeadragon.Viewer#removeOverlay} will be added back when a new
- * image is opened.
- * To add overlays which can be definitively removed, one must use
- * {@link OpenSeadragon.Viewer#addOverlay}
- * If displaying a sequence of images, the overlays can be associated
- * with a specific page by passing the overlays array to the page's
- * tile source configuration.
- * Expected properties:
- * * x, y, (or px, py for pixel coordinates) to define the location.
- * * width, height in point if using x,y or in pixels if using px,py. If width
- * and height are specified, the overlay size is adjusted when zooming,
- * otherwise the size stays the size of the content (or the size defined by CSS).
- * * className to associate a class to the overlay
- * * id to set the overlay element. If an element with this id already exists,
- * it is reused, otherwise it is created. If not specified, a new element is
- * created.
- * * placement a string to define the relative position to the viewport.
- * Only used if no width and height are specified. Default: 'TOP_LEFT'.
- * See {@link OpenSeadragon.OverlayPlacement} for possible values.
- *
- * @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'.
- * 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} [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} [opacity=1]
- * Opacity of the drawer (1=opaque, 0=transparent)
- *
- * @property {Number} [layersAspectRatioEpsilon=0.0001]
- * Maximum aspectRatio mismatch between 2 layers.
- *
- * @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]
- * 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} [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} [springStiffness=5.0]
- *
- * @property {Number} [animationTime=1.2]
- * Specifies the animation duration per each {@link OpenSeadragon.Spring}
- * which occur when the image is dragged or zoomed.
- *
- * @property {OpenSeadragon.GestureSettings} [gestureSettingsMouse]
- * Settings for gestures generated by a mouse pointer device. (See {@link OpenSeadragon.GestureSettings})
- * @property {Boolean} [gestureSettingsMouse.scrollToZoom=true] - Zoom on scroll gesture
- * @property {Boolean} [gestureSettingsMouse.clickToZoom=true] - Zoom on click gesture
- * @property {Boolean} [gestureSettingsMouse.pinchToZoom=false] - Zoom on pinch gesture
- * @property {Boolean} [gestureSettingsMouse.flickEnabled=false] - Enable flick gesture
- * @property {Number} [gestureSettingsMouse.flickMinSpeed=20] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)
- * @property {Number} [gestureSettingsMouse.flickMomentum=0.40] - If flickEnabled is true, the momentum factor for the flick gesture
- *
- * @property {OpenSeadragon.GestureSettings} [gestureSettingsTouch]
- * Settings for gestures generated by a touch pointer device. (See {@link OpenSeadragon.GestureSettings})
- * @property {Boolean} [gestureSettingsTouch.scrollToZoom=false] - Zoom on scroll gesture
- * @property {Boolean} [gestureSettingsTouch.clickToZoom=false] - Zoom on click gesture
- * @property {Boolean} [gestureSettingsTouch.pinchToZoom=true] - Zoom on pinch gesture
- * @property {Boolean} [gestureSettingsTouch.flickEnabled=true] - Enable flick gesture
- * @property {Number} [gestureSettingsTouch.flickMinSpeed=20] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)
- * @property {Number} [gestureSettingsTouch.flickMomentum=0.40] - If flickEnabled is true, the momentum factor for the flick gesture
- *
- * @property {OpenSeadragon.GestureSettings} [gestureSettingsPen]
- * Settings for gestures generated by a pen pointer device. (See {@link OpenSeadragon.GestureSettings})
- * @property {Boolean} [gestureSettingsPen.scrollToZoom=false] - Zoom on scroll gesture
- * @property {Boolean} [gestureSettingsPen.clickToZoom=true] - Zoom on click gesture
- * @property {Boolean} [gestureSettingsPen.pinchToZoom=false] - Zoom on pinch gesture
- * @property {Boolean} [gestureSettingsPen.flickEnabled=false] - Enable flick gesture
- * @property {Number} [gestureSettingsPen.flickMinSpeed=20] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)
- * @property {Number} [gestureSettingsPen.flickMomentum=0.40] - If flickEnabled is true, the momentum factor for the flick gesture
- *
- * @property {OpenSeadragon.GestureSettings} [gestureSettingsUnknown]
- * Settings for gestures generated by unknown pointer devices. (See {@link OpenSeadragon.GestureSettings})
- * @property {Boolean} [gestureSettingsUnknown.scrollToZoom=true] - Zoom on scroll gesture
- * @property {Boolean} [gestureSettingsUnknown.clickToZoom=false] - Zoom on click gesture
- * @property {Boolean} [gestureSettingsUnknown.pinchToZoom=true] - Zoom on pinch gesture
- * @property {Boolean} [gestureSettingsUnknown.flickEnabled=true] - Enable flick gesture
- * @property {Number} [gestureSettingsUnknown.flickMinSpeed=20] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)
- * @property {Number} [gestureSettingsUnknown.flickMomentum=0.40] - If flickEnabled is true, the momentum factor for the flick gesture
- *
- * @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 (also see gestureSettings[Mouse|Touch|Pen].clickToZoom).</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 (also see gestureSettings[Mouse|Touch|Pen].scrollToZoom}).</em>
- *
- * @property {Number} [zoomPerSecond=1.0]
- * The number of seconds to animate a single zoom event over.
- *
- * @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.
- *
- * @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} [showNavigationControl=true]
- * Set to false to prevent the appearance of the default navigation controls.<br>
- * Note that if set to false, the customs buttons set by the options
- * zoomInButton, zoomOutButton etc, are rendered inactive.
- *
- * @property {OpenSeadragon.ControlAnchor} [navigationControlAnchor=TOP_LEFT]
- * Placement of the default navigation controls.
- * To set the placement of the sequence controls, see the
- * sequenceControlAnchor option.
- *
- * @property {Boolean} [showZoomControl=true]
- * If true then + and - buttons to zoom in and out are displayed.<br>
- * Note: {@link OpenSeadragon.Options.showNavigationControl} is overriding
- * this setting when set to false.
- *
- * @property {Boolean} [showHomeControl=true]
- * If true then the 'Go home' button is displayed to go back to the original
- * zoom and pan.<br>
- * Note: {@link OpenSeadragon.Options.showNavigationControl} is overriding
- * this setting when set to false.
- *
- * @property {Boolean} [showFullPageControl=true]
- * If true then the 'Toggle full page' button is displayed to switch
- * between full page and normal mode.<br>
- * Note: {@link OpenSeadragon.Options.showNavigationControl} is overriding
- * this setting when set to false.
- *
- * @property {Boolean} [showRotationControl=false]
- * If true then the rotate left/right controls will be displayed as part of the
- * standard controls. This is also subject to the browser support for rotate
- * (e.g. viewer.drawer.canRotate()).<br>
- * Note: {@link OpenSeadragon.Options.showNavigationControl} is overriding
- * this setting when set to false.
- *
- * @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 {Boolean} [navPrevNextWrap=false]
- * If true then the 'previous' button will wrap to the last image when
- * viewing the first image and the 'next' button will wrap to the first
- * image when viewing the last image.
- *
- * @property {String} zoomInButton
- * Set the id of the custom 'Zoom in' button to use.
- * This is useful to have a custom button anywhere in the web page.<br>
- * To only change the button images, consider using
- * {@link OpenSeadragon.Options.navImages}
- *
- * @property {String} zoomOutButton
- * Set the id of the custom 'Zoom out' button to use.
- * This is useful to have a custom button anywhere in the web page.<br>
- * To only change the button images, consider using
- * {@link OpenSeadragon.Options.navImages}
- *
- * @property {String} homeButton
- * Set the id of the custom 'Go home' button to use.
- * This is useful to have a custom button anywhere in the web page.<br>
- * To only change the button images, consider using
- * {@link OpenSeadragon.Options.navImages}
- *
- * @property {String} fullPageButton
- * Set the id of the custom 'Toggle full page' button to use.
- * This is useful to have a custom button anywhere in the web page.<br>
- * To only change the button images, consider using
- * {@link OpenSeadragon.Options.navImages}
- *
- * @property {String} rotateLeftButton
- * Set the id of the custom 'Rotate left' button to use.
- * This is useful to have a custom button anywhere in the web page.<br>
- * To only change the button images, consider using
- * {@link OpenSeadragon.Options.navImages}
- *
- * @property {String} rotateRightButton
- * Set the id of the custom 'Rotate right' button to use.
- * This is useful to have a custom button anywhere in the web page.<br>
- * To only change the button images, consider using
- * {@link OpenSeadragon.Options.navImages}
- *
- * @property {String} previousButton
- * Set the id of the custom 'Previous page' button to use.
- * This is useful to have a custom button anywhere in the web page.<br>
- * To only change the button images, consider using
- * {@link OpenSeadragon.Options.navImages}
- *
- * @property {String} nextButton
- * Set the id of the custom 'Next page' button to use.
- * This is useful to have a custom button anywhere in the web page.<br>
- * To only change the button images, consider using
- * {@link OpenSeadragon.Options.navImages}
- *
- * @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]
- *
- * @property {String|Boolean} [crossOriginPolicy=false]
- * Valid values are 'Anonymous', 'use-credentials', and false. If false, canvas requests will
- * not use CORS, and the canvas will be tainted.
- *
- */
- /**
- * Settings for gestures generated by a pointer device.
- *
- * @typedef {Object} GestureSettings
- * @memberof OpenSeadragon
- *
- * @property {Boolean} scrollToZoom
- * Set to false to disable zooming on scroll gestures.
- *
- * @property {Boolean} clickToZoom
- * Set to false to disable zooming on click gestures.
- *
- * @property {Boolean} pinchToZoom
- * Set to false to disable zooming on pinch gestures.
- *
- * @property {Boolean} flickEnabled
- * Set to false to disable the kinetic panning effect (flick) at the end of a drag gesture.
- *
- * @property {Number} flickMinSpeed
- * If flickEnabled is true, the minimum speed (in pixels-per-second) required to cause the kinetic panning effect (flick) at the end of a drag gesture.
- *
- * @property {Number} flickMomentum
- * If flickEnabled is true, a constant multiplied by the velocity to determine the distance of the kinetic panning effect (flick) at the end of a drag gesture.
- * A larger value will make the flick feel "lighter", while a smaller value will make the flick feel "heavier".
- * Note: springStiffness and animationTime also affect the "spring" used to stop the flick animation.
- *
- */
- /**
- * 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} rotateleft - Images for the rotate left button.
- * @property {String} rotateleft.REST
- * @property {String} rotateleft.GROUP
- * @property {String} rotateleft.HOVER
- * @property {String} rotateleft.DOWN
- *
- * @property {Object} rotateright - Images for the rotate right button.
- * @property {String} rotateright.REST
- * @property {String} rotateright.GROUP
- * @property {String} rotateright.HOVER
- * @property {String} rotateright.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
- */
- $.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 */{
- /**
- * The default values for the optional settings documented at {@link OpenSeadragon.Options}.
- * @static
- * @type {Object}
- */
- DEFAULT_SETTINGS: {
- //DATA SOURCE DETAILS
- xmlPath: null,
- tileSources: null,
- tileHost: null,
- initialPage: 0,
- crossOriginPolicy: false,
- //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
- clickTimeThreshold: 300,
- clickDistThreshold: 5,
- springStiffness: 5.0,
- animationTime: 1.2,
- gestureSettingsMouse: { scrollToZoom: true, clickToZoom: true, pinchToZoom: false, flickEnabled: false, flickMinSpeed: 20, flickMomentum: 0.40 },
- gestureSettingsTouch: { scrollToZoom: false, clickToZoom: false, pinchToZoom: true, flickEnabled: true, flickMinSpeed: 20, flickMomentum: 0.40 },
- gestureSettingsPen: { scrollToZoom: false, clickToZoom: true, pinchToZoom: false, flickEnabled: false, flickMinSpeed: 20, flickMomentum: 0.40 },
- gestureSettingsUnknown: { scrollToZoom: false, clickToZoom: false, pinchToZoom: true, flickEnabled: true, flickMinSpeed: 20, flickMomentum: 0.40 },
- zoomPerClick: 2,
- zoomPerScroll: 1.2,
- zoomPerSecond: 1.0,
- 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,
- autoResize: true,
- //DEFAULT CONTROL SETTINGS
- showSequenceControl: true, //SEQUENCE
- sequenceControlAnchor: null, //SEQUENCE
- preserveViewport: false, //SEQUENCE
- navPrevNextWrap: false, //SEQUENCE
- showNavigationControl: true, //ZOOM/HOME/FULL/ROTATION
- navigationControlAnchor: null, //ZOOM/HOME/FULL/ROTATION
- showZoomControl: true, //ZOOM
- showHomeControl: true, //HOME
- showFullPageControl: true, //FULL
- showRotationControl: false, //ROTATION
- controlsFadeDelay: 2000, //ZOOM/HOME/FULL/SEQUENCE
- controlsFadeLength: 1500, //ZOOM/HOME/FULL/SEQUENCE
- mouseNavEnabled: true, //GENERAL MOUSE INTERACTIVITY
- //VIEWPORT NAVIGATOR SETTINGS
- 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,
- // APPEARANCE
- opacity: 1,
- // LAYERS SETTINGS
- layersAspectRatioEpsilon: 0.0001,
- //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'
- },
- rotateleft: {
- REST: 'rotateleft_rest.png',
- GROUP: 'rotateleft_grouphover.png',
- HOVER: 'rotateleft_hover.png',
- DOWN: 'rotateleft_pressed.png'
- },
- rotateright: {
- REST: 'rotateright_rest.png',
- GROUP: 'rotateright_grouphover.png',
- HOVER: 'rotateright_hover.png',
- DOWN: 'rotateright_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----",
- /**
- * Returns a function which invokes the method as if it were a method belonging to the object.
- * @function
- * @param {Object} object
- * @param {Function} method
- * @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, "" );
- },
- /**
- * Determines if a point is within the bounding rectangle of the given element (hit-test).
- * @function
- * @param {Element|String} element
- * @param {OpenSeadragon.Point} point
- * @returns {Boolean}
- */
- pointInElement: function( element, point ) {
- element = $.getElement( element );
- var offset = $.getElementOffset( element ),
- size = $.getElementSize( element );
- return point.x >= offset.x && point.x < offset.x + size.x && point.y < offset.y + size.y && point.y >= offset.y;
- },
- /**
- * Gets the latest event, really only useful internally since its
- * specific to IE behavior.
- * @function
- * @param {Event} [event]
- * @returns {Event}
- * @deprecated For internal use only
- * @private
- */
- getEvent: function( event ) {
- if( event ){
- $.getEvent = function( event ) {
- return event;
- };
- } else {
- $.getEvent = function() {
- return window.event;
- };
- }
- return $.getEvent( event );
- },
- /**
- * Gets the position of the mouse on the screen for a given event.
- * @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."
- );
- }
- return $.getMousePosition( event );
- },
- /**
- * 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 {
- 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;
- 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();
- 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;
- },
- /**
- * Returns the current milliseconds, using Date.now() if available
- * @function
- */
- now: function( ) {
- if (Date.now) {
- $.now = Date.now;
- } else {
- $.now = function() { return new Date().getTime(); };
- }
- return $.now();
- },
- /**
- * 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;
- }
- },
- /**
- * Find the first index at which an element is found in an array or -1
- * if not present.
- *
- * Code taken and adapted from
- * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Compatibility
- *
- * @function
- * @param {Array} array The array from which to find the element
- * @param {Object} searchElement The element to find
- * @param {Number} [fromIndex=0] Index to start research.
- * @returns {Number} The index of the element in the array.
- */
- indexOf: function( array, searchElement, fromIndex ) {
- if ( Array.prototype.indexOf ) {
- this.indexOf = function( array, searchElement, fromIndex ) {
- return array.indexOf( searchElement, fromIndex );
- };
- } else {
- this.indexOf = function( array, searchElement, fromIndex ) {
- var i,
- pivot = ( fromIndex ) ? fromIndex : 0,
- length;
- if ( !array ) {
- throw new TypeError( );
- }
- length = array.length;
- if ( length === 0 || pivot >= length ) {
- return -1;
- }
- if ( pivot < 0 ) {
- pivot = length - Math.abs( pivot );
- }
- for ( i = pivot; i < length; i++ ) {
- if ( array[i] === searchElement ) {
- return i;
- }
- }
- return -1;
- };
- }
- return this.indexOf( array, searchElement, fromIndex );
- },
- /**
- * 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]
- */
- 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 );
- };
- } 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]
- */
- 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 );
- };
- } 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*/
- /* 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();
- 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)
- if ( request.readyState == 4 ) {
- request.onreadystatechange = function(){};
- if ( request.status == 200 ) {
- onSuccess( request );
- } else {
- $.console.log( "AJAX request returned %s: %s", request.status, url );
- 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(){};
- 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,
- 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() ];
- }
- });
- /**
- * The current browser vendor, version, and related information regarding detected features.
- * @member {Object} Browser
- * @memberof OpenSeadragon
- * @static
- * @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:
- // http://mattsnider.com/cross-browser-and-legacy-supported-requestframeanimation/
- (function( w ) {
- // most browsers have an implementation
- var requestAnimationFrame = w.requestAnimationFrame ||
- w.mozRequestAnimationFrame ||
- w.webkitRequestAnimationFrame ||
- w.msRequestAnimationFrame;
- var cancelAnimationFrame = w.cancelAnimationFrame ||
- w.mozCancelAnimationFrame ||
- 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.
- $.requestAnimationFrame = function(){
- return requestAnimationFrame.apply( w, arguments );
- };
- $.cancelAnimationFrame = function(){
- return cancelAnimationFrame.apply( w, arguments );
- };
- } else {
- var aAnimQueue = [],
- processing = [],
- 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
- // requests, but none that get added during the
- // processing.
- // Swap the arrays so we don't have to create a new
- // array every frame.
- var temp = processing;
- processing = aAnimQueue;
- aAnimQueue = temp;
- while ( processing.length ) {
- processing.shift()[ 1 ]( time );
- }
- } 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 ) {
- 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
- // processing (if cancelAnimationFrame is called from within a
- // requestAnimationFrame callback).
- for ( i = 0, j = processing.length; i < j; i += 1 ) {
- if ( processing[ i ][ 0 ] === requestId ) {
- processing.splice( i, 1 );
- return;
- }
- }
- };
- }
- })( 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 ));