diff --git a/minerva_analysis/client/dist/vendor_bundle.js b/minerva_analysis/client/dist/vendor_bundle.js
index 438611c8b..8fe586452 100644
--- a/minerva_analysis/client/dist/vendor_bundle.js
+++ b/minerva_analysis/client/dist/vendor_bundle.js
@@ -12941,7 +12941,7 @@ eval("/*\nobject-assign\n(c) Sindre Sorhus\n@license MIT\n*/\n\n\n\n/* eslint-di
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
-eval("var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;function _typeof(obj){\"@babel/helpers - typeof\";return _typeof=\"function\"==typeof Symbol&&\"symbol\"==typeof Symbol.iterator?function(obj){return typeof obj;}:function(obj){return obj&&\"function\"==typeof Symbol&&obj.constructor===Symbol&&obj!==Symbol.prototype?\"symbol\":typeof obj;},_typeof(obj);}//! openseadragon 2.3.1\n//! Built on 2023-07-27\n//! Git commit: unknown\n//! http://openseadragon.github.io\n//! License: http://openseadragon.github.io/license/\n/*\n * OpenSeadragon\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */ /*\n * Portions of this source file taken from jQuery:\n *\n * Copyright 2011 John Resig\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */ /*\n * Portions of this source file taken from mattsnider.com:\n *\n * Copyright (c) 2006-2013 Matt Snider\n *\n * Permission is hereby granted, free of charge, to any person obtaining a\n * copy of this software and associated documentation files (the \"Software\"),\n * to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense,\n * and/or sell copies of the Software, and to permit persons to whom the\n * Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT\n * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR\n * THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */ /**\n * @namespace OpenSeadragon\n * @version openseadragon 2.3.1\n * @classdesc The root namespace for OpenSeadragon. All utility methods\n * and classes are defined on or below this namespace.\n *\n */ // Typedefs\n/**\n * All required and optional settings for instantiating a new instance of an OpenSeadragon image viewer.\n *\n * @typedef {Object} Options\n * @memberof OpenSeadragon\n *\n * @property {String} id\n * Id of the element to append the viewer's container element to. If not provided, the 'element' property must be provided.\n * If both the element and id properties are specified, the viewer is appended to the element provided in the element property.\n *\n * @property {Element} element\n * The element to append the viewer's container element to. If not provided, the 'id' property must be provided.\n * If both the element and id properties are specified, the viewer is appended to the element provided in the element property.\n *\n * @property {Array|String|Function|Object} [tileSources=null]\n * Tile source(s) to open initially. This is a complex parameter; see\n * {@link OpenSeadragon.Viewer#open} for details.\n *\n * @property {Number} [tabIndex=0]\n * Tabbing order index to assign to the viewer element. Positive values are selected in increasing order. When tabIndex is 0\n * source order is used. A negative value omits the viewer from the tabbing order.\n *\n * @property {Array} overlays Array of objects defining permanent overlays of\n * the viewer. The overlays added via this option and later removed with\n * {@link OpenSeadragon.Viewer#removeOverlay} will be added back when a new\n * image is opened.\n * To add overlays which can be definitively removed, one must use\n * {@link OpenSeadragon.Viewer#addOverlay}\n * If displaying a sequence of images, the overlays can be associated\n * with a specific page by passing the overlays array to the page's\n * tile source configuration.\n * Expected properties:\n * * x, y, (or px, py for pixel coordinates) to define the location.\n * * width, height in point if using x,y or in pixels if using px,py. If width\n * and height are specified, the overlay size is adjusted when zooming,\n * otherwise the size stays the size of the content (or the size defined by CSS).\n * * className to associate a class to the overlay\n * * id to set the overlay element. If an element with this id already exists,\n * it is reused, otherwise it is created. If not specified, a new element is\n * created.\n * * placement a string to define the relative position to the viewport.\n * Only used if no width and height are specified. Default: 'TOP_LEFT'.\n * See {@link OpenSeadragon.Placement} for possible values.\n *\n * @property {String} [xmlPath=null]\n * DEPRECATED. A relative path to load a DZI file from the server.\n * Prefer the newer Options.tileSources.\n *\n * @property {String} [prefixUrl='/images/']\n * Prepends the prefixUrl to navImages paths, which is very useful\n * since the default paths are rarely useful for production\n * environments.\n *\n * @property {OpenSeadragon.NavImages} [navImages]\n * An object with a property for each button or other built-in navigation\n * control, eg the current 'zoomIn', 'zoomOut', 'home', and 'fullpage'.\n * Each of those in turn provides an image path for each state of the button\n * or navigation control, eg 'REST', 'GROUP', 'HOVER', 'PRESS'. Finally the\n * image paths, by default assume there is a folder on the servers root path\n * called '/images', eg '/images/zoomin_rest.png'. If you need to adjust\n * these paths, prefer setting the option.prefixUrl rather than overriding\n * every image path directly through this setting.\n *\n * @property {Boolean} [debugMode=false]\n * TODO: provide an in-screen panel providing event detail feedback.\n *\n * @property {String} [debugGridColor=['#437AB2', '#1B9E77', '#D95F02', '#7570B3', '#E7298A', '#66A61E', '#E6AB02', '#A6761D', '#666666']]\n * The colors of grids in debug mode. Each tiled image's grid uses a consecutive color.\n * If there are more tiled images than provided colors, the color vector is recycled.\n *\n * @property {Number} [blendTime=0]\n * Specifies the duration of animation as higher or lower level tiles are\n * replacing the existing tile.\n *\n * @property {Boolean} [alwaysBlend=false]\n * Forces the tile to always blend. By default the tiles skip blending\n * when the blendTime is surpassed and the current animation frame would\n * not complete the blend.\n *\n * @property {Boolean} [autoHideControls=true]\n * If the user stops interacting with the viewport, fade the navigation\n * controls. Useful for presentation since the controls are by default\n * floated on top of the image the user is viewing.\n *\n * @property {Boolean} [immediateRender=false]\n * Render the best closest level first, ignoring the lowering levels which\n * provide the effect of very blurry to sharp. It is recommended to change\n * setting to true for mobile devices.\n *\n * @property {Number} [defaultZoomLevel=0]\n * Zoom level to use when image is first opened or the home button is clicked.\n * If 0, adjusts to fit viewer.\n *\n * @property {Number} [opacity=1]\n * Default proportional opacity of the tiled images (1=opaque, 0=hidden)\n * Hidden images do not draw and only load when preloading is allowed.\n *\n * @property {Boolean} [preload=false]\n * Default switch for loading hidden images (true loads, false blocks)\n *\n * @property {String} [compositeOperation=null]\n * Valid values are 'source-over', 'source-atop', 'source-in', 'source-out',\n * 'destination-over', 'destination-atop', 'destination-in',\n * 'destination-out', 'lighter', 'copy' or 'xor'\n *\n * @property {String|CanvasGradient|CanvasPattern|Function} [placeholderFillStyle=null]\n * Draws a colored rectangle behind the tile if it is not loaded yet.\n * You can pass a CSS color value like \"#FF8800\".\n * When passing a function the tiledImage and canvas context are available as argument which is useful when you draw a gradient or pattern.\n *\n * @property {Number} [degrees=0]\n * Initial rotation.\n *\n * @property {Boolean} [flipped=false]\n * Initial flip state.\n *\n * @property {Number} [minZoomLevel=null]\n *\n * @property {Number} [maxZoomLevel=null]\n *\n * @property {Boolean} [homeFillsViewer=false]\n * Make the 'home' button fill the viewer and clip the image, instead\n * of fitting the image to the viewer and letterboxing.\n *\n * @property {Boolean} [panHorizontal=true]\n * Allow horizontal pan.\n *\n * @property {Boolean} [panVertical=true]\n * Allow vertical pan.\n *\n * @property {Boolean} [constrainDuringPan=false]\n *\n * @property {Boolean} [wrapHorizontal=false]\n * Set to true to force the image to wrap horizontally within the viewport.\n * Useful for maps or images representing the surface of a sphere or cylinder.\n *\n * @property {Boolean} [wrapVertical=false]\n * Set to true to force the image to wrap vertically within the viewport.\n * Useful for maps or images representing the surface of a sphere or cylinder.\n *\n * @property {Number} [minZoomImageRatio=0.9]\n * The minimum percentage ( expressed as a number between 0 and 1 ) of\n * the viewport height or width at which the zoom out will be constrained.\n * Setting it to 0, for example will allow you to zoom out infinity.\n *\n * @property {Number} [maxZoomPixelRatio=1.1]\n * The maximum ratio to allow a zoom-in to affect the highest level pixel\n * ratio. This can be set to Infinity to allow 'infinite' zooming into the\n * image though it is less effective visually if the HTML5 Canvas is not\n * availble on the viewing device.\n *\n * @property {Number} [smoothTileEdgesMinZoom=1.1]\n * A zoom percentage ( where 1 is 100% ) of the highest resolution level.\n * When zoomed in beyond this value alternative compositing will be used to\n * smooth out the edges between tiles. This will have a performance impact.\n * Can be set to Infinity to turn it off.\n * Note: This setting is ignored on iOS devices due to a known bug (See {@link https://github.com/openseadragon/openseadragon/issues/952})\n *\n * @property {Boolean} [iOSDevice=?]\n * True if running on an iOS device, false otherwise.\n * Used to disable certain features that behave differently on iOS devices.\n *\n * @property {Boolean} [autoResize=true]\n * Set to false to prevent polling for viewer size changes. Useful for providing custom resize behavior.\n *\n * @property {Boolean} [preserveImageSizeOnResize=false]\n * Set to true to have the image size preserved when the viewer is resized. This requires autoResize=true (default).\n *\n * @property {Number} [minScrollDeltaTime=50]\n * Number of milliseconds between canvas-scroll events. This value helps normalize the rate of canvas-scroll\n * events between different devices, causing the faster devices to slow down enough to make the zoom control\n * more manageable.\n *\n * @property {Number} [pixelsPerWheelLine=40]\n * For pixel-resolution scrolling devices, the number of pixels equal to one scroll line.\n *\n * @property {Number} [pixelsPerArrowPress=40]\n * The number of pixels viewport moves when an arrow key is pressed.\n *\n * @property {Number} [visibilityRatio=0.5]\n * The percentage ( as a number from 0 to 1 ) of the source image which\n * must be kept within the viewport. If the image is dragged beyond that\n * limit, it will 'bounce' back until the minimum visibility ratio is\n * achieved. Setting this to 0 and wrapHorizontal ( or wrapVertical ) to\n * true will provide the effect of an infinitely scrolling viewport.\n *\n * @property {Object} [viewportMargins={}]\n * Pushes the \"home\" region in from the sides by the specified amounts.\n * Possible subproperties (Numbers, in screen coordinates): left, top, right, bottom.\n *\n * @property {Number} [imageLoaderLimit=0]\n * The maximum number of image requests to make concurrently. By default\n * it is set to 0 allowing the browser to make the maximum number of\n * image requests in parallel as allowed by the browsers policy.\n *\n * @property {Number} [clickTimeThreshold=300]\n * The number of milliseconds within which a pointer down-up event combination\n * will be treated as a click gesture.\n *\n * @property {Number} [clickDistThreshold=5]\n * The maximum distance allowed between a pointer down event and a pointer up event\n * to be treated as a click gesture.\n *\n * @property {Number} [dblClickTimeThreshold=300]\n * The number of milliseconds within which two pointer down-up event combinations\n * will be treated as a double-click gesture.\n *\n * @property {Number} [dblClickDistThreshold=20]\n * The maximum distance allowed between two pointer click events\n * to be treated as a double-click gesture.\n *\n * @property {Number} [springStiffness=6.5]\n *\n * @property {Number} [animationTime=1.2]\n * Specifies the animation duration per each {@link OpenSeadragon.Spring}\n * which occur when the image is dragged or zoomed.\n *\n * @property {OpenSeadragon.GestureSettings} [gestureSettingsMouse]\n * Settings for gestures generated by a mouse pointer device. (See {@link OpenSeadragon.GestureSettings})\n * @property {Boolean} [gestureSettingsMouse.scrollToZoom=true] - Zoom on scroll gesture\n * @property {Boolean} [gestureSettingsMouse.clickToZoom=true] - Zoom on click gesture\n * @property {Boolean} [gestureSettingsMouse.dblClickToZoom=false] - Zoom on double-click gesture. Note: If set to true\n * then clickToZoom should be set to false to prevent multiple zooms.\n * @property {Boolean} [gestureSettingsMouse.pinchToZoom=false] - Zoom on pinch gesture\n * @property {Boolean} [gestureSettingsMouse.zoomToRefPoint=true] - If zoomToRefPoint is true, the zoom is centered at the pointer position. Otherwise,\n * the zoom is centered at the canvas center.\n * @property {Boolean} [gestureSettingsMouse.flickEnabled=false] - Enable flick gesture\n * @property {Number} [gestureSettingsMouse.flickMinSpeed=120] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)\n * @property {Number} [gestureSettingsMouse.flickMomentum=0.25] - If flickEnabled is true, the momentum factor for the flick gesture\n * @property {Boolean} [gestureSettingsMouse.pinchRotate=false] - If pinchRotate is true, the user will have the ability to rotate the image using their fingers.\n *\n * @property {OpenSeadragon.GestureSettings} [gestureSettingsTouch]\n * Settings for gestures generated by a touch pointer device. (See {@link OpenSeadragon.GestureSettings})\n * @property {Boolean} [gestureSettingsTouch.scrollToZoom=false] - Zoom on scroll gesture\n * @property {Boolean} [gestureSettingsTouch.clickToZoom=false] - Zoom on click gesture\n * @property {Boolean} [gestureSettingsTouch.dblClickToZoom=true] - Zoom on double-click gesture. Note: If set to true\n * then clickToZoom should be set to false to prevent multiple zooms.\n * @property {Boolean} [gestureSettingsTouch.pinchToZoom=true] - Zoom on pinch gesture\n * @property {Boolean} [gestureSettingsTouch.zoomToRefPoint=true] - If zoomToRefPoint is true, the zoom is centered at the pointer position. Otherwise,\n * the zoom is centered at the canvas center.\n * @property {Boolean} [gestureSettingsTouch.flickEnabled=true] - Enable flick gesture\n * @property {Number} [gestureSettingsTouch.flickMinSpeed=120] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)\n * @property {Number} [gestureSettingsTouch.flickMomentum=0.25] - If flickEnabled is true, the momentum factor for the flick gesture\n * @property {Boolean} [gestureSettingsTouch.pinchRotate=false] - If pinchRotate is true, the user will have the ability to rotate the image using their fingers.\n *\n * @property {OpenSeadragon.GestureSettings} [gestureSettingsPen]\n * Settings for gestures generated by a pen pointer device. (See {@link OpenSeadragon.GestureSettings})\n * @property {Boolean} [gestureSettingsPen.scrollToZoom=false] - Zoom on scroll gesture\n * @property {Boolean} [gestureSettingsPen.clickToZoom=true] - Zoom on click gesture\n * @property {Boolean} [gestureSettingsPen.dblClickToZoom=false] - Zoom on double-click gesture. Note: If set to true\n * then clickToZoom should be set to false to prevent multiple zooms.\n * @property {Boolean} [gestureSettingsPen.pinchToZoom=false] - Zoom on pinch gesture\n * @property {Boolean} [gestureSettingsPan.zoomToRefPoint=true] - If zoomToRefPoint is true, the zoom is centered at the pointer position. Otherwise,\n * the zoom is centered at the canvas center.\n * @property {Boolean} [gestureSettingsPen.flickEnabled=false] - Enable flick gesture\n * @property {Number} [gestureSettingsPen.flickMinSpeed=120] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)\n * @property {Number} [gestureSettingsPen.flickMomentum=0.25] - If flickEnabled is true, the momentum factor for the flick gesture\n * @property {Boolean} [gestureSettingsPen.pinchRotate=false] - If pinchRotate is true, the user will have the ability to rotate the image using their fingers.\n *\n * @property {OpenSeadragon.GestureSettings} [gestureSettingsUnknown]\n * Settings for gestures generated by unknown pointer devices. (See {@link OpenSeadragon.GestureSettings})\n * @property {Boolean} [gestureSettingsUnknown.scrollToZoom=true] - Zoom on scroll gesture\n * @property {Boolean} [gestureSettingsUnknown.clickToZoom=false] - Zoom on click gesture\n * @property {Boolean} [gestureSettingsUnknown.dblClickToZoom=true] - Zoom on double-click gesture. Note: If set to true\n * then clickToZoom should be set to false to prevent multiple zooms.\n * @property {Boolean} [gestureSettingsUnknown.pinchToZoom=true] - Zoom on pinch gesture\n * @property {Boolean} [gestureSettingsUnknown.zoomToRefPoint=true] - If zoomToRefPoint is true, the zoom is centered at the pointer position. Otherwise,\n * the zoom is centered at the canvas center.\n * @property {Boolean} [gestureSettingsUnknown.flickEnabled=true] - Enable flick gesture\n * @property {Number} [gestureSettingsUnknown.flickMinSpeed=120] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)\n * @property {Number} [gestureSettingsUnknown.flickMomentum=0.25] - If flickEnabled is true, the momentum factor for the flick gesture\n * @property {Boolean} [gestureSettingsUnknown.pinchRotate=false] - If pinchRotate is true, the user will have the ability to rotate the image using their fingers.\n *\n * @property {Number} [zoomPerClick=2.0]\n * The \"zoom distance\" per mouse click or touch tap. Note: Setting this to 1.0 effectively disables the click-to-zoom feature (also see gestureSettings[Mouse|Touch|Pen].clickToZoom/dblClickToZoom).\n *\n * @property {Number} [zoomPerScroll=1.2]\n * The \"zoom distance\" per mouse scroll or touch pinch. Note: Setting this to 1.0 effectively disables the mouse-wheel zoom feature (also see gestureSettings[Mouse|Touch|Pen].scrollToZoom}).\n *\n * @property {Number} [zoomPerSecond=1.0]\n * The number of seconds to animate a single zoom event over.\n *\n * @property {Boolean} [showNavigator=false]\n * Set to true to make the navigator minimap appear.\n *\n * @property {String} [navigatorId=navigator-GENERATED DATE]\n * The ID of a div to hold the navigator minimap.\n * If an ID is specified, the navigatorPosition, navigatorSizeRatio, navigatorMaintainSizeRatio, navigator[Top|Left|Height|Width] and navigatorAutoFade options will be ignored.\n * If an ID is not specified, a div element will be generated and placed on top of the main image.\n *\n * @property {String} [navigatorPosition='TOP_RIGHT']\n * Valid values are 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', 'BOTTOM_RIGHT', or 'ABSOLUTE'.
\n * If 'ABSOLUTE' is specified, then navigator[Top|Left|Height|Width] determines the size and position of the navigator minimap in the viewer, and navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.
\n * For 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', and 'BOTTOM_RIGHT', the navigatorSizeRatio or navigator[Height|Width] values determine the size of the navigator minimap.\n *\n * @property {Number} [navigatorSizeRatio=0.2]\n * Ratio of navigator size to viewer size. Ignored if navigator[Height|Width] are specified.\n *\n * @property {Boolean} [navigatorMaintainSizeRatio=false]\n * If true, the navigator minimap is resized (using navigatorSizeRatio) when the viewer size changes.\n *\n * @property {Number|String} [navigatorTop=null]\n * Specifies the location of the navigator minimap (see navigatorPosition).\n *\n * @property {Number|String} [navigatorLeft=null]\n * Specifies the location of the navigator minimap (see navigatorPosition).\n *\n * @property {Number|String} [navigatorHeight=null]\n * Specifies the size of the navigator minimap (see navigatorPosition).\n * If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.\n *\n * @property {Number|String} [navigatorWidth=null]\n * Specifies the size of the navigator minimap (see navigatorPosition).\n * If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.\n *\n * @property {Boolean} [navigatorAutoResize=true]\n * Set to false to prevent polling for navigator size changes. Useful for providing custom resize behavior.\n * Setting to false can also improve performance when the navigator is configured to a fixed size.\n *\n * @property {Boolean} [navigatorAutoFade=true]\n * If the user stops interacting with the viewport, fade the navigator minimap.\n * Setting to false will make the navigator minimap always visible.\n *\n * @property {Boolean} [navigatorRotate=true]\n * If true, the navigator will be rotated together with the viewer.\n *\n * @property {Number} [controlsFadeDelay=2000]\n * The number of milliseconds to wait once the user has stopped interacting\n * with the interface before begining to fade the controls. Assumes\n * showNavigationControl and autoHideControls are both true.\n *\n * @property {Number} [controlsFadeLength=1500]\n * The number of milliseconds to animate the controls fading out.\n *\n * @property {Number} [maxImageCacheCount=200]\n * The max number of images we should keep in memory (per drawer).\n *\n * @property {Number} [timeout=30000]\n * The max number of milliseconds that an image job may take to complete.\n *\n * @property {Boolean} [useCanvas=true]\n * Set to false to not use an HTML canvas element for image rendering even if canvas is supported.\n *\n * @property {Number} [minPixelRatio=0.5]\n * The higher the minPixelRatio, the lower the quality of the image that\n * is considered sufficient to stop rendering a given zoom level. For\n * example, if you are targeting mobile devices with less bandwith you may\n * try setting this to 1.5 or higher.\n *\n * @property {Boolean} [mouseNavEnabled=true]\n * Is the user able to interact with the image via mouse or touch. Default\n * interactions include draging the image in a plane, and zooming in toward\n * and away from the image.\n *\n * @property {Boolean} [showNavigationControl=true]\n * Set to false to prevent the appearance of the default navigation controls.
\n * Note that if set to false, the customs buttons set by the options\n * zoomInButton, zoomOutButton etc, are rendered inactive.\n *\n * @property {OpenSeadragon.ControlAnchor} [navigationControlAnchor=TOP_LEFT]\n * Placement of the default navigation controls.\n * To set the placement of the sequence controls, see the\n * sequenceControlAnchor option.\n *\n * @property {Boolean} [showZoomControl=true]\n * If true then + and - buttons to zoom in and out are displayed.
\n * Note: {@link OpenSeadragon.Options.showNavigationControl} is overriding\n * this setting when set to false.\n *\n * @property {Boolean} [showHomeControl=true]\n * If true then the 'Go home' button is displayed to go back to the original\n * zoom and pan.
\n * Note: {@link OpenSeadragon.Options.showNavigationControl} is overriding\n * this setting when set to false.\n *\n * @property {Boolean} [showFullPageControl=true]\n * If true then the 'Toggle full page' button is displayed to switch\n * between full page and normal mode.
\n * Note: {@link OpenSeadragon.Options.showNavigationControl} is overriding\n * this setting when set to false.\n *\n * @property {Boolean} [showRotationControl=false]\n * If true then the rotate left/right controls will be displayed as part of the\n * standard controls. This is also subject to the browser support for rotate\n * (e.g. viewer.drawer.canRotate()).
\n * Note: {@link OpenSeadragon.Options.showNavigationControl} is overriding\n * this setting when set to false.\n *\n * @property {Boolean} [showFlipControl=false]\n * If true then the flip controls will be displayed as part of the\n * standard controls.\n *\n * @property {Boolean} [showSequenceControl=true]\n * If sequenceMode is true, then provide buttons for navigating forward and\n * backward through the images.\n *\n * @property {OpenSeadragon.ControlAnchor} [sequenceControlAnchor=TOP_LEFT]\n * Placement of the default sequence controls.\n *\n * @property {Boolean} [navPrevNextWrap=false]\n * If true then the 'previous' button will wrap to the last image when\n * viewing the first image and the 'next' button will wrap to the first\n * image when viewing the last image.\n *\n * @property {String} zoomInButton\n * Set the id of the custom 'Zoom in' button to use.\n * This is useful to have a custom button anywhere in the web page.
\n * To only change the button images, consider using\n * {@link OpenSeadragon.Options.navImages}\n *\n * @property {String} zoomOutButton\n * Set the id of the custom 'Zoom out' button to use.\n * This is useful to have a custom button anywhere in the web page.
\n * To only change the button images, consider using\n * {@link OpenSeadragon.Options.navImages}\n *\n * @property {String} homeButton\n * Set the id of the custom 'Go home' button to use.\n * This is useful to have a custom button anywhere in the web page.
\n * To only change the button images, consider using\n * {@link OpenSeadragon.Options.navImages}\n *\n * @property {String} fullPageButton\n * Set the id of the custom 'Toggle full page' button to use.\n * This is useful to have a custom button anywhere in the web page.
\n * To only change the button images, consider using\n * {@link OpenSeadragon.Options.navImages}\n *\n * @property {String} rotateLeftButton\n * Set the id of the custom 'Rotate left' button to use.\n * This is useful to have a custom button anywhere in the web page.
\n * To only change the button images, consider using\n * {@link OpenSeadragon.Options.navImages}\n *\n * @property {String} rotateRightButton\n * Set the id of the custom 'Rotate right' button to use.\n * This is useful to have a custom button anywhere in the web page.
\n * To only change the button images, consider using\n * {@link OpenSeadragon.Options.navImages}\n *\n * @property {String} previousButton\n * Set the id of the custom 'Previous page' button to use.\n * This is useful to have a custom button anywhere in the web page.
\n * To only change the button images, consider using\n * {@link OpenSeadragon.Options.navImages}\n *\n * @property {String} nextButton\n * Set the id of the custom 'Next page' button to use.\n * This is useful to have a custom button anywhere in the web page.
\n * To only change the button images, consider using\n * {@link OpenSeadragon.Options.navImages}\n *\n * @property {Boolean} [sequenceMode=false]\n * Set to true to have the viewer treat your tilesources as a sequence of images to\n * be opened one at a time rather than all at once.\n *\n * @property {Number} [initialPage=0]\n * If sequenceMode is true, display this page initially.\n *\n * @property {Boolean} [preserveViewport=false]\n * If sequenceMode is true, then normally navigating through each image resets the\n * viewport to 'home' position. If preserveViewport is set to true, then the viewport\n * position is preserved when navigating between images in the sequence.\n *\n * @property {Boolean} [preserveOverlays=false]\n * If sequenceMode is true, then normally navigating through each image\n * resets the overlays.\n * If preserveOverlays is set to true, then the overlays added with {@link OpenSeadragon.Viewer#addOverlay}\n * are preserved when navigating between images in the sequence.\n * Note: setting preserveOverlays overrides any overlays specified in the global\n * \"overlays\" option for the Viewer. It's also not compatible with specifying\n * per-tileSource overlays via the options, as those overlays will persist\n * even after the tileSource is closed.\n *\n * @property {Boolean} [showReferenceStrip=false]\n * If sequenceMode is true, then display a scrolling strip of image thumbnails for\n * navigating through the images.\n *\n * @property {String} [referenceStripScroll='horizontal']\n *\n * @property {Element} [referenceStripElement=null]\n *\n * @property {Number} [referenceStripHeight=null]\n *\n * @property {Number} [referenceStripWidth=null]\n *\n * @property {String} [referenceStripPosition='BOTTOM_LEFT']\n *\n * @property {Number} [referenceStripSizeRatio=0.2]\n *\n * @property {Boolean} [collectionMode=false]\n * Set to true to have the viewer arrange your TiledImages in a grid or line.\n *\n * @property {Number} [collectionRows=3]\n * If collectionMode is true, specifies how many rows the grid should have. Use 1 to make a line.\n * If collectionLayout is 'vertical', specifies how many columns instead.\n *\n * @property {Number} [collectionColumns=0]\n * If collectionMode is true, specifies how many columns the grid should have. Use 1 to make a line.\n * If collectionLayout is 'vertical', specifies how many rows instead. Ignored if collectionRows is not set to a falsy value.\n *\n * @property {String} [collectionLayout='horizontal']\n * If collectionMode is true, specifies whether to arrange vertically or horizontally.\n *\n * @property {Number} [collectionTileSize=800]\n * If collectionMode is true, specifies the size, in viewport coordinates, for each TiledImage to fit into.\n * The TiledImage will be centered within a square of the specified size.\n *\n * @property {Number} [collectionTileMargin=80]\n * If collectionMode is true, specifies the margin, in viewport coordinates, between each TiledImage.\n *\n * @property {String|Boolean} [crossOriginPolicy=false]\n * Valid values are 'Anonymous', 'use-credentials', and false. If false, canvas requests will\n * not use CORS, and the canvas will be tainted.\n *\n * @property {Boolean} [ajaxWithCredentials=false]\n * Whether to set the withCredentials XHR flag for AJAX requests.\n * Note that this can be overridden at the {@link OpenSeadragon.TileSource} level.\n *\n * @property {Boolean} [loadTilesWithAjax=false]\n * Whether to load tile data using AJAX requests.\n * Note that this can be overridden at the {@link OpenSeadragon.TileSource} level.\n *\n * @property {Object} [ajaxHeaders={}]\n * A set of headers to include when making AJAX requests for tile sources or tiles.\n *\n */ /**\n * Settings for gestures generated by a pointer device.\n *\n * @typedef {Object} GestureSettings\n * @memberof OpenSeadragon\n *\n * @property {Boolean} scrollToZoom\n * Set to false to disable zooming on scroll gestures.\n *\n * @property {Boolean} clickToZoom\n * Set to false to disable zooming on click gestures.\n *\n * @property {Boolean} dblClickToZoom\n * Set to false to disable zooming on double-click gestures. Note: If set to true\n * then clickToZoom should be set to false to prevent multiple zooms.\n *\n * @property {Boolean} pinchToZoom\n * Set to false to disable zooming on pinch gestures.\n *\n * @property {Boolean} flickEnabled\n * Set to false to disable the kinetic panning effect (flick) at the end of a drag gesture.\n *\n * @property {Number} flickMinSpeed\n * 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.\n *\n * @property {Number} flickMomentum\n * 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.\n * A larger value will make the flick feel \"lighter\", while a smaller value will make the flick feel \"heavier\".\n * Note: springStiffness and animationTime also affect the \"spring\" used to stop the flick animation.\n *\n */ /**\n * The names for the image resources used for the image navigation buttons.\n *\n * @typedef {Object} NavImages\n * @memberof OpenSeadragon\n *\n * @property {Object} zoomIn - Images for the zoom-in button.\n * @property {String} zoomIn.REST\n * @property {String} zoomIn.GROUP\n * @property {String} zoomIn.HOVER\n * @property {String} zoomIn.DOWN\n *\n * @property {Object} zoomOut - Images for the zoom-out button.\n * @property {String} zoomOut.REST\n * @property {String} zoomOut.GROUP\n * @property {String} zoomOut.HOVER\n * @property {String} zoomOut.DOWN\n *\n * @property {Object} home - Images for the home button.\n * @property {String} home.REST\n * @property {String} home.GROUP\n * @property {String} home.HOVER\n * @property {String} home.DOWN\n *\n * @property {Object} fullpage - Images for the full-page button.\n * @property {String} fullpage.REST\n * @property {String} fullpage.GROUP\n * @property {String} fullpage.HOVER\n * @property {String} fullpage.DOWN\n *\n * @property {Object} rotateleft - Images for the rotate left button.\n * @property {String} rotateleft.REST\n * @property {String} rotateleft.GROUP\n * @property {String} rotateleft.HOVER\n * @property {String} rotateleft.DOWN\n *\n * @property {Object} rotateright - Images for the rotate right button.\n * @property {String} rotateright.REST\n * @property {String} rotateright.GROUP\n * @property {String} rotateright.HOVER\n * @property {String} rotateright.DOWN\n *\n * @property {Object} flip - Images for the flip button.\n * @property {String} flip.REST\n * @property {String} flip.GROUP\n * @property {String} flip.HOVER\n * @property {String} flip.DOWN\n *\n * @property {Object} previous - Images for the previous button.\n * @property {String} previous.REST\n * @property {String} previous.GROUP\n * @property {String} previous.HOVER\n * @property {String} previous.DOWN\n *\n * @property {Object} next - Images for the next button.\n * @property {String} next.REST\n * @property {String} next.GROUP\n * @property {String} next.HOVER\n * @property {String} next.DOWN\n *\n */function OpenSeadragon(options){return new OpenSeadragon.Viewer(options);}(function($){/**\n * The OpenSeadragon version.\n *\n * @member {Object} OpenSeadragon.version\n * @property {String} versionStr - The version number as a string ('major.minor.revision').\n * @property {Number} major - The major version number.\n * @property {Number} minor - The minor version number.\n * @property {Number} revision - The revision number.\n * @since 1.0.0\n */$.version={versionStr:'2.3.1',major:parseInt('2',10),minor:parseInt('3',10),revision:parseInt('1',10)};/**\n * Taken from jquery 1.6.1\n * [[Class]] -> type pairs\n * @private\n */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\ntoString=Object.prototype.toString,hasOwn=Object.prototype.hasOwnProperty;/**\n * Taken from jQuery 1.6.1\n * @function isFunction\n * @memberof OpenSeadragon\n * @see {@link http://www.jquery.com/ jQuery}\n */$.isFunction=function(obj){return $.type(obj)===\"function\";};/**\n * Taken from jQuery 1.6.1\n * @function isArray\n * @memberof OpenSeadragon\n * @see {@link http://www.jquery.com/ jQuery}\n */$.isArray=Array.isArray||function(obj){return $.type(obj)===\"array\";};/**\n * A crude way of determining if an object is a window.\n * Taken from jQuery 1.6.1\n * @function isWindow\n * @memberof OpenSeadragon\n * @see {@link http://www.jquery.com/ jQuery}\n */$.isWindow=function(obj){return obj&&_typeof(obj)===\"object\"&&\"setInterval\"in obj;};/**\n * Taken from jQuery 1.6.1\n * @function type\n * @memberof OpenSeadragon\n * @see {@link http://www.jquery.com/ jQuery}\n */$.type=function(obj){return obj===null||obj===undefined?String(obj):class2type[toString.call(obj)]||\"object\";};/**\n * Taken from jQuery 1.6.1\n * @function isPlainObject\n * @memberof OpenSeadragon\n * @see {@link http://www.jquery.com/ jQuery}\n */$.isPlainObject=function(obj){// Must be an Object.\n// Because of IE, we also have to check the presence of the constructor property.\n// Make sure that DOM nodes and window objects don't pass through, as well\nif(!obj||OpenSeadragon.type(obj)!==\"object\"||obj.nodeType||$.isWindow(obj)){return false;}// Not own constructor property must be Object\nif(obj.constructor&&!hasOwn.call(obj,\"constructor\")&&!hasOwn.call(obj.constructor.prototype,\"isPrototypeOf\")){return false;}// Own properties are enumerated firstly, so to speed up,\n// if last one is own, then all properties are own.\nvar lastKey;for(var key in obj){lastKey=key;}return lastKey===undefined||hasOwn.call(obj,lastKey);};/**\n * Taken from jQuery 1.6.1\n * @function isEmptyObject\n * @memberof OpenSeadragon\n * @see {@link http://www.jquery.com/ jQuery}\n */$.isEmptyObject=function(obj){for(var name in obj){return false;}return true;};/**\n * Shim around Object.freeze. Does nothing if Object.freeze is not supported.\n * @param {Object} obj The object to freeze.\n * @return {Object} obj The frozen object.\n */$.freezeObject=function(obj){if(Object.freeze){$.freezeObject=Object.freeze;}else{$.freezeObject=function(obj){return obj;};}return $.freezeObject(obj);};/**\n * True if the browser supports the HTML5 canvas element\n * @member {Boolean} supportsCanvas\n * @memberof OpenSeadragon\n */$.supportsCanvas=function(){var canvasElement=document.createElement('canvas');return!!($.isFunction(canvasElement.getContext)&&canvasElement.getContext('2d'));}();/**\n * Test whether the submitted canvas is tainted or not.\n * @argument {Canvas} canvas The canvas to test.\n * @returns {Boolean} True if the canvas is tainted.\n */$.isCanvasTainted=function(canvas){var isTainted=false;try{// We test if the canvas is tainted by retrieving data from it.\n// An exception will be raised if the canvas is tainted.\ncanvas.getContext('2d').getImageData(0,0,1,1);}catch(e){isTainted=true;}return isTainted;};/**\n * A ratio comparing the device screen's pixel density to the canvas's backing store pixel density,\n * clamped to a minimum of 1. Defaults to 1 if canvas isn't supported by the browser.\n * @member {Number} pixelDensityRatio\n * @memberof OpenSeadragon\n */$.pixelDensityRatio=function(){if($.supportsCanvas){var context=document.createElement('canvas').getContext('2d');var devicePixelRatio=window.devicePixelRatio||1;var backingStoreRatio=context.webkitBackingStorePixelRatio||context.mozBackingStorePixelRatio||context.msBackingStorePixelRatio||context.oBackingStorePixelRatio||context.backingStorePixelRatio||1;return Math.max(devicePixelRatio,1)/backingStoreRatio;}else{return 1;}}();})(OpenSeadragon);/**\n * This closure defines all static methods available to the OpenSeadragon\n * namespace. Many, if not most, are taked directly from jQuery for use\n * to simplify and reduce common programming patterns. More static methods\n * from jQuery may eventually make their way into this though we are\n * attempting to avoid an explicit dependency on jQuery only because\n * OpenSeadragon is a broadly useful code base and would be made less broad\n * by requiring jQuery fully.\n *\n * Some static methods have also been refactored from the original OpenSeadragon\n * project.\n */(function($){/**\n * Taken from jQuery 1.6.1\n * @function extend\n * @memberof OpenSeadragon\n * @see {@link http://www.jquery.com/ jQuery}\n */$.extend=function(){var options,name,src,copy,copyIsArray,clone,target=arguments[0]||{},length=arguments.length,deep=false,i=1;// Handle a deep copy situation\nif(typeof target===\"boolean\"){deep=target;target=arguments[1]||{};// skip the boolean and the target\ni=2;}// Handle case when target is a string or something (possible in deep copy)\nif(_typeof(target)!==\"object\"&&!OpenSeadragon.isFunction(target)){target={};}// extend jQuery itself if only one argument is passed\nif(length===i){target=this;--i;}for(;i how much of the viewer can be negative space\nminPixelRatio:0.5,//->closer to 0 draws tiles meant for a higher zoom at this zoom\ndefaultZoomLevel:0,minZoomLevel:null,maxZoomLevel:null,homeFillsViewer:false,//UI RESPONSIVENESS AND FEEL\nclickTimeThreshold:300,clickDistThreshold:5,dblClickTimeThreshold:300,dblClickDistThreshold:20,springStiffness:6.5,animationTime:1.2,gestureSettingsMouse:{scrollToZoom:true,clickToZoom:true,dblClickToZoom:false,pinchToZoom:false,zoomToRefPoint:true,flickEnabled:false,flickMinSpeed:120,flickMomentum:0.25,pinchRotate:false},gestureSettingsTouch:{scrollToZoom:false,clickToZoom:false,dblClickToZoom:true,pinchToZoom:true,zoomToRefPoint:true,flickEnabled:true,flickMinSpeed:120,flickMomentum:0.25,pinchRotate:false},gestureSettingsPen:{scrollToZoom:false,clickToZoom:true,dblClickToZoom:false,pinchToZoom:false,zoomToRefPoint:true,flickEnabled:false,flickMinSpeed:120,flickMomentum:0.25,pinchRotate:false},gestureSettingsUnknown:{scrollToZoom:false,clickToZoom:false,dblClickToZoom:true,pinchToZoom:true,zoomToRefPoint:true,flickEnabled:true,flickMinSpeed:120,flickMomentum:0.25,pinchRotate:false},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\nmaxZoomPixelRatio:1.1,//-> higher allows 'over zoom' into pixels\nsmoothTileEdgesMinZoom:1.1,//-> higher than maxZoomPixelRatio disables it\niOSDevice:isIOSDevice(),pixelsPerWheelLine:40,pixelsPerArrowPress:40,autoResize:true,preserveImageSizeOnResize:false,// requires autoResize=true\nminScrollDeltaTime:50,//DEFAULT CONTROL SETTINGS\nshowSequenceControl:true,//SEQUENCE\nsequenceControlAnchor:null,//SEQUENCE\npreserveViewport:false,//SEQUENCE\npreserveOverlays:false,//SEQUENCE\nnavPrevNextWrap:false,//SEQUENCE\nshowNavigationControl:true,//ZOOM/HOME/FULL/ROTATION\nnavigationControlAnchor:null,//ZOOM/HOME/FULL/ROTATION\nshowZoomControl:true,//ZOOM\nshowHomeControl:true,//HOME\nshowFullPageControl:true,//FULL\nshowRotationControl:false,//ROTATION\nshowFlipControl:false,//FLIP\ncontrolsFadeDelay:2000,//ZOOM/HOME/FULL/SEQUENCE\ncontrolsFadeLength:1500,//ZOOM/HOME/FULL/SEQUENCE\nmouseNavEnabled:true,//GENERAL MOUSE INTERACTIVITY\n//VIEWPORT NAVIGATOR SETTINGS\nshowNavigator:false,navigatorId:null,navigatorPosition:null,navigatorSizeRatio:0.2,navigatorMaintainSizeRatio:false,navigatorTop:null,navigatorLeft:null,navigatorHeight:null,navigatorWidth:null,navigatorAutoResize:true,navigatorAutoFade:true,navigatorRotate:true,// INITIAL ROTATION\ndegrees:0,// INITIAL FLIP STATE\nflipped:false,// APPEARANCE\nopacity:1,preload:false,compositeOperation:null,placeholderFillStyle:null,//REFERENCE STRIP SETTINGS\nshowReferenceStrip:false,referenceStripScroll:'horizontal',referenceStripElement:null,referenceStripHeight:null,referenceStripWidth:null,referenceStripPosition:'BOTTOM_LEFT',referenceStripSizeRatio:0.2,//COLLECTION VISUALIZATION SETTINGS\ncollectionRows:3,//or columns depending on layout\ncollectionColumns:0,//columns in horizontal layout, rows in vertical layout\ncollectionLayout:'horizontal',//vertical\ncollectionMode:false,collectionTileSize:800,collectionTileMargin:80,//PERFORMANCE SETTINGS\nimageLoaderLimit:0,maxImageCacheCount:200,timeout:30000,useCanvas:true,// Use canvas element for drawing if available\n//INTERFACE RESOURCE SETTINGS\nprefixUrl:\"/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'},flip:{// Flip icon designed by Yaroslav Samoylov from the Noun Project and modified by Nelson Campos ncampos@criteriamarathon.com, https://thenounproject.com/term/flip/136289/\nREST:'flip_rest.png',GROUP:'flip_grouphover.png',HOVER:'flip_hover.png',DOWN:'flip_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\ndebugMode:false,debugGridColor:['#437AB2','#1B9E77','#D95F02','#7570B3','#E7298A','#66A61E','#E6AB02','#A6761D','#666666']},/**\n * TODO: get rid of this. I can't see how it's required at all. Looks\n * like an early legacy code artifact.\n * @static\n * @ignore\n */SIGNAL:\"----seadragon----\",/**\n * Returns a function which invokes the method as if it were a method belonging to the object.\n * @function\n * @param {Object} object\n * @param {Function} method\n * @returns {Function}\n */delegate:function delegate(object,method){return function(){var args=arguments;if(args===undefined){args=[];}return method.apply(object,args);};},/**\n * An enumeration of Browser vendors.\n * @static\n * @type {Object}\n * @property {Number} UNKNOWN\n * @property {Number} IE\n * @property {Number} FIREFOX\n * @property {Number} SAFARI\n * @property {Number} CHROME\n * @property {Number} OPERA\n */BROWSERS:{UNKNOWN:0,IE:1,FIREFOX:2,SAFARI:3,CHROME:4,OPERA:5},/**\n * Returns a DOM Element for the given id or element.\n * @function\n * @param {String|Element} element Accepts an id or element.\n * @returns {Element} The element with the given id, null, or the element itself.\n */getElement:function getElement(element){if(typeof element==\"string\"){element=document.getElementById(element);}return element;},/**\n * Determines the position of the upper-left corner of the element.\n * @function\n * @param {Element|String} element - the elemenet we want the position for.\n * @returns {OpenSeadragon.Point} - the position of the upper left corner of the element.\n */getElementPosition:function getElementPosition(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;},/**\n * Determines the position of the upper-left corner of the element adjusted for current page and/or element scroll.\n * @function\n * @param {Element|String} element - the element we want the position for.\n * @returns {OpenSeadragon.Point} - the position of the upper left corner of the element adjusted for current page and/or element scroll.\n */getElementOffset:function getElementOffset(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)!==( true?\"undefined\":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));},/**\n * Determines the height and width of the given element.\n * @function\n * @param {Element|String} element\n * @returns {OpenSeadragon.Point}\n */getElementSize:function getElementSize(element){element=$.getElement(element);return new $.Point(element.clientWidth,element.clientHeight);},/**\n * Returns the CSSStyle object for the given element.\n * @function\n * @param {Element|String} element\n * @returns {CSSStyle}\n */getElementStyle:document.documentElement.currentStyle?function(element){element=$.getElement(element);return element.currentStyle;}:function(element){element=$.getElement(element);return window.getComputedStyle(element,\"\");},/**\n * Returns the property with the correct vendor prefix appended.\n * @param {String} property the property name\n * @returns {String} the property with the correct prefix or null if not\n * supported.\n */getCssPropertyWithVendorPrefix:function getCssPropertyWithVendorPrefix(property){var memo={};$.getCssPropertyWithVendorPrefix=function(property){if(memo[property]!==undefined){return memo[property];}var style=document.createElement('div').style;var result=null;if(style[property]!==undefined){result=property;}else{var prefixes=['Webkit','Moz','MS','O','webkit','moz','ms','o'];var suffix=$.capitalizeFirstLetter(property);for(var i=0;i=offset.x&&point.x=offset.y;},/**\n * Gets the latest event, really only useful internally since its\n * specific to IE behavior.\n * @function\n * @param {Event} [event]\n * @returns {Event}\n * @deprecated For internal use only\n * @private\n */getEvent:function getEvent(event){if(event){$.getEvent=function(event){return event;};}else{$.getEvent=function(){return window.event;};}return $.getEvent(event);},/**\n * Gets the position of the mouse on the screen for a given event.\n * @function\n * @param {Event} [event]\n * @returns {OpenSeadragon.Point}\n */getMousePosition:function getMousePosition(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);},/**\n * Determines the page's current scroll position.\n * @function\n * @returns {OpenSeadragon.Point}\n */getPageScroll:function getPageScroll(){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.\nreturn new $.Point(0,0);}return $.getPageScroll();},/**\n * Set the page scroll position.\n * @function\n * @returns {OpenSeadragon.Point}\n */setPageScroll:function setPageScroll(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\n// is no way to detect the correct method.\nreturn;}document.body.scrollLeft=scroll.x;document.body.scrollTop=scroll.y;var currentScroll=$.getPageScroll();if(currentScroll.x!==originalScroll.x&¤tScroll.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&¤tScroll.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.\n$.setPageScroll=function(scroll){};}return $.setPageScroll(scroll);},/**\n * Determines the size of the browsers window.\n * @function\n * @returns {OpenSeadragon.Point}\n */getWindowSize:function getWindowSize(){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();},/**\n * Wraps the given element in a nest of divs so that the element can\n * be easily centered using CSS tables\n * @function\n * @param {Element|String} element\n * @returns {Element} outermost wrapper element\n */makeCenteredNode:function makeCenteredNode(element){// Convert a possible ID to an actual HTMLElement\nelement=$.getElement(element);/*\n CSS tables require you to have a display:table/row/cell hierarchy so we need to create\n three nested wrapper divs:\n */var wrappers=[$.makeNeutralElement('div'),$.makeNeutralElement('div'),$.makeNeutralElement('div')];// It feels like we should be able to pass style dicts to makeNeutralElement:\n$.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];},/**\n * Creates an easily positionable element of the given type that therefor\n * serves as an excellent container element.\n * @function\n * @param {String} tagName\n * @returns {Element}\n */makeNeutralElement:function makeNeutralElement(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;},/**\n * Returns the current milliseconds, using Date.now() if available\n * @function\n */now:function now(){if(Date.now){$.now=Date.now;}else{$.now=function(){return new Date().getTime();};}return $.now();},/**\n * Ensures an image is loaded correctly to support alpha transparency.\n * Generally only IE has issues doing this correctly for formats like\n * png.\n * @function\n * @param {String} src\n * @returns {Element}\n */makeTransparentImage:function makeTransparentImage(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\n};img.src=src;element.style.filter=\"progid:DXImageTransform.Microsoft.AlphaImageLoader(src='\"+src+\"', sizingMethod='scale')\";return element;};}return $.makeTransparentImage(src);},/**\n * Sets the opacity of the specified element.\n * @function\n * @param {Element|String} element\n * @param {Number} opacity\n * @param {Boolean} [usesAlpha]\n */setElementOpacity:function setElementOpacity(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=\"\";}}},/**\n * Sets the specified element's touch-action style attribute to 'none'.\n * @function\n * @param {Element|String} element\n */setElementTouchActionNone:function setElementTouchActionNone(element){element=$.getElement(element);if(typeof element.style.touchAction!=='undefined'){element.style.touchAction='none';}else if(typeof element.style.msTouchAction!=='undefined'){element.style.msTouchAction='none';}},/**\n * Add the specified CSS class to the element if not present.\n * @function\n * @param {Element|String} element\n * @param {String} className\n */addClass:function addClass(element,className){element=$.getElement(element);if(!element.className){element.className=className;}else if((' '+element.className+' ').indexOf(' '+className+' ')===-1){element.className+=' '+className;}},/**\n * Find the first index at which an element is found in an array or -1\n * if not present.\n *\n * Code taken and adapted from\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Compatibility\n *\n * @function\n * @param {Array} array The array from which to find the element\n * @param {Object} searchElement The element to find\n * @param {Number} [fromIndex=0] Index to start research.\n * @returns {Number} The index of the element in the array.\n */indexOf:function indexOf(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=200&&request.status<300||request.status===0&&protocol!==\"http:\"&&protocol!==\"https:\"){onSuccess(request);}else{$.console.log(\"AJAX request returned %d: %s\",request.status,url);if($.isFunction(onError)){onError(request);}}}};try{request.open(\"GET\",url,true);if(responseType){request.responseType=responseType;}if(headers){for(var headerName in headers){if(headers.hasOwnProperty(headerName)&&headers[headerName]){request.setRequestHeader(headerName,headers[headerName]);}}}if(withCredentials){request.withCredentials=true;}request.send(null);}catch(e){var msg=e.message;/*\n IE < 10 does not support CORS and an XHR request to a different origin will fail as soon\n as send() is called. This is particularly easy to miss during development and appear in\n production if you use a CDN or domain sharding and the security policy is likely to break\n exception handlers since any attempt to access a property of the request object will\n raise an access denied TypeError inside the catch block.\n\n To be friendlier, we'll check for this specific error and add a documentation pointer\n to point developers in the right direction. We test the exception number because IE's\n error messages are localized.\n */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(window.XDomainRequest){// IE9 or IE8 might as well try to use XDomainRequest\nvar xdr=new XDomainRequest();if(xdr){xdr.onload=function(e){if($.isFunction(onSuccess)){onSuccess({// Faking an xhr object\nresponseText:xdr.responseText,status:200,// XDomainRequest doesn't support status codes, so we just fake one! :/\nstatusText:'OK'});}};xdr.onerror=function(e){if($.isFunction(onError)){onError({// Faking an xhr object\nresponseText:xdr.responseText,status:444,// 444 No Response\nstatusText:'An error happened. Due to an XDomainRequest deficiency we can not extract any information about this error. Upgrade your browser.'});}};try{xdr.open('GET',url);xdr.send();}catch(e2){if($.isFunction(onError)){onError(request,e);}}}}else{if($.isFunction(onError)){onError(request,e);}}}return request;},/**\n * Taken from jQuery 1.6.1\n * @function\n * @param {Object} options\n * @param {String} options.url\n * @param {Function} options.callback\n * @param {String} [options.param='callback'] The name of the url parameter\n * to request the jsonp provider with.\n * @param {String} [options.callbackName=] The name of the callback to\n * request the jsonp provider with.\n */jsonp:function jsonp(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\nurl+=(/\\?/.test(url)?\"&\":\"?\")+callbackParam+\"=\"+jsonpCallback;// Install callback\nwindow[jsonpCallback]=function(response){if(!previous){try{delete window[jsonpCallback];}catch(e){//swallow\n}}else{window[jsonpCallback]=previous;}if(callback&&$.isFunction(callback)){callback(response);}};script=document.createElement(\"script\");//TODO: having an issue with async info requests\nif(undefined!==options.async||false!==options.async){script.async=\"async\";}if(options.scriptCharset){script.charset=options.scriptCharset;}script.src=url;// Attach handlers for all browsers\nscript.onload=script.onreadystatechange=function(_,isAbort){if(isAbort||!script.readyState||/loaded|complete/.test(script.readyState)){// Handle memory leak in IE\nscript.onload=script.onreadystatechange=null;// Remove the script\nif(head&&script.parentNode){head.removeChild(script);}// Dereference the script\nscript=undefined;}};// Use insertBefore instead of appendChild to circumvent an IE6 bug.\n// This arises when a base node is used (#2709 and #4378).\nhead.insertBefore(script,head.firstChild);},/**\n * Fully deprecated. Will throw an error.\n * @function\n * @deprecated use {@link OpenSeadragon.Viewer#open}\n */createFromDZI:function createFromDZI(){throw\"OpenSeadragon.createFromDZI is deprecated, use Viewer.open.\";},/**\n * Parses an XML string into a DOM Document.\n * @function\n * @param {String} string\n * @returns {Document}\n */parseXml:function parseXml(string){if(window.DOMParser){$.parseXml=function(string){var xmlDoc=null,parser;parser=new DOMParser();xmlDoc=parser.parseFromString(string,\"text/xml\");return xmlDoc;};}else if(window.ActiveXObject){$.parseXml=function(string){var xmlDoc=null;xmlDoc=new ActiveXObject(\"Microsoft.XMLDOM\");xmlDoc.async=false;xmlDoc.loadXML(string);return xmlDoc;};}else{throw new Error(\"Browser doesn't support XML DOM.\");}return $.parseXml(string);},/**\n * Parses a JSON string into a Javascript object.\n * @function\n * @param {String} string\n * @returns {Object}\n */parseJSON:function parseJSON(string){if(window.JSON&&window.JSON.parse){$.parseJSON=window.JSON.parse;}else{// Should only be used by IE8 in non standards mode\n$.parseJSON=function(string){/*jshint evil:true*/ //eslint-disable-next-line no-eval\nreturn eval('('+string+')');};}return $.parseJSON(string);},/**\n * Reports whether the image format is supported for tiling in this\n * version.\n * @function\n * @param {String} [extension]\n * @returns {Boolean}\n */imageFormatSupported:function imageFormatSupported(extension){extension=extension?extension:\"\";// eslint-disable-next-line no-use-before-define\nreturn!!FILEFORMATS[extension.toLowerCase()];}});/**\n * The current browser vendor, version, and related information regarding detected features.\n * @member {Object} Browser\n * @memberof OpenSeadragon\n * @static\n * @type {Object}\n * @property {OpenSeadragon.BROWSERS} vendor - One of the {@link OpenSeadragon.BROWSERS} enumeration values.\n * @property {Number} version\n * @property {Boolean} alpha - Does the browser support image alpha transparency.\n */$.Browser={vendor:$.BROWSERS.UNKNOWN,version:0,alpha:true};var 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,\n//version and supporting feature sets.\nvar ver=navigator.appVersion,ua=navigator.userAgent,regex;//console.error( 'appName: ' + navigator.appName );\n//console.error( 'appVersion: ' + navigator.appVersion );\n//console.error( 'userAgent: ' + navigator.userAgent );\nswitch(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\")));}else{regex=new RegExp(\"Trident/.*rv:([0-9]{1,}[.0-9]{0,})\");if(regex.exec(ua)!==null){$.Browser.vendor=$.BROWSERS.IE;$.Browser.version=parseFloat(RegExp.$1);}}}break;case\"Opera\":$.Browser.vendor=$.BROWSERS.OPERA;$.Browser.version=parseFloat(ver);break;}// ignore '?' portion of query string\nvar query=window.location.search.substring(1),parts=query.split('&'),part,sep,i;for(i=0;i0){URLPARAMS[part.substring(0,sep)]=decodeURIComponent(part.substring(sep+1));}}//determine if this browser supports image alpha transparency\n$.Browser.alpha=!($.Browser.vendor==$.BROWSERS.IE&&$.Browser.version<9||$.Browser.vendor==$.BROWSERS.CHROME&&$.Browser.version<2);//determine if this browser supports element.style.opacity\n$.Browser.opacity=!($.Browser.vendor==$.BROWSERS.IE&&$.Browser.version<9);})();//TODO: $.console is often used inside a try/catch block which generally\n// prevents allowings errors to occur with detection until a debugger\n// is attached. Although I've been guilty of the same anti-pattern\n// I eventually was convinced that errors should naturally propogate in\n// all but the most special cases.\n/**\n * A convenient alias for console when available, and a simple null\n * function when console is unavailable.\n * @static\n * @private\n */var nullfunction=function nullfunction(msg){//document.location.hash = msg;\n};$.console=window.console||{log:nullfunction,debug:nullfunction,info:nullfunction,warn:nullfunction,error:nullfunction,assert:nullfunction};// Adding support for HTML5's requestAnimationFrame as suggested by acdha.\n// Implementation taken from matt synder's post here:\n// http://mattsnider.com/cross-browser-and-legacy-supported-requestframeanimation/\n(function(w){// most browsers have an implementation\nvar requestAnimationFrame=w.requestAnimationFrame||w.mozRequestAnimationFrame||w.webkitRequestAnimationFrame||w.msRequestAnimationFrame;var cancelAnimationFrame=w.cancelAnimationFrame||w.mozCancelAnimationFrame||w.webkitCancelAnimationFrame||w.msCancelAnimationFrame;// polyfill, when necessary\nif(requestAnimationFrame&&cancelAnimationFrame){// We can't assign these window methods directly to $ because they\n// expect their \"this\" to be \"window\", so we call them in wrappers.\n$.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\n$.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\n// requests, but none that get added during the\n// processing.\n// Swap the arrays so we don't have to create a new\n// array every frame.\nvar temp=processing;processing=aAnimQueue;aAnimQueue=temp;while(processing.length){processing.shift()[1](time);}}else{// don't continue the interval, if unnecessary\nclearInterval(iIntervalId);iIntervalId=undefined;}},1000/50);// estimating support for 50 frames per second\n}return iRequestId;};// create a mock cancelAnimationFrame function\n$.cancelAnimationFrame=function(requestId){// find the request ID and remove it\nvar i,j;for(i=0,j=aAnimQueue.length;i}\n */getActivePointersListsExceptType:function getActivePointersListsExceptType(type){var delegate=THIS[this.hash];var listArray=[];for(var i=0;iDeprecated. Use buttons instead.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */enterHandler:function enterHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Number} event.buttons\n * Current buttons pressed.\n * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @param {Number} event.pointers\n * Number of pointers (all types) active in the tracked element.\n * @param {Boolean} event.insideElementPressed\n * True if the left mouse button is currently being pressed and was\n * initiated inside the tracked element, otherwise false.\n * @param {Boolean} event.buttonDownAny\n * Was the button down anywhere in the screen during the event. Deprecated. Use buttons instead.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */exitHandler:function exitHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Number} event.buttons\n * Current buttons pressed.\n * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */pressHandler:function pressHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Number} event.button\n * Button which caused the event.\n * -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.\n * @param {Number} event.buttons\n * Current buttons pressed.\n * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */nonPrimaryPressHandler:function nonPrimaryPressHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Number} event.buttons\n * Current buttons pressed.\n * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @param {Boolean} event.insideElementPressed\n * True if the left mouse button is currently being pressed and was\n * initiated inside the tracked element, otherwise false.\n * @param {Boolean} event.insideElementReleased\n * True if the cursor inside the tracked element when the button was released.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */releaseHandler:function releaseHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Number} event.button\n * Button which caused the event.\n * -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.\n * @param {Number} event.buttons\n * Current buttons pressed.\n * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */nonPrimaryReleaseHandler:function nonPrimaryReleaseHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Number} event.buttons\n * Current buttons pressed.\n * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */moveHandler:function moveHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Number} event.scroll\n * The scroll delta for the event.\n * @param {Boolean} event.shift\n * True if the shift key was pressed during this event.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead. Touch devices no longer generate scroll event.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */scrollHandler:function scrollHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Boolean} event.quick\n * True only if the clickDistThreshold and clickTimeThreshold are both passed. Useful for ignoring drag events.\n * @param {Boolean} event.shift\n * True if the shift key was pressed during this event.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */clickHandler:function clickHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Boolean} event.shift\n * True if the shift key was pressed during this event.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */dblClickHandler:function dblClickHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Number} event.buttons\n * Current buttons pressed.\n * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @param {OpenSeadragon.Point} event.delta\n * The x,y components of the difference between the current position and the last drag event position. Useful for ignoring or weighting the events.\n * @param {Number} event.speed\n * Current computed speed, in pixels per second.\n * @param {Number} event.direction\n * Current computed direction, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.\n * @param {Boolean} event.shift\n * True if the shift key was pressed during this event.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */dragHandler:function dragHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Number} event.speed\n * Speed at the end of a drag gesture, in pixels per second.\n * @param {Number} event.direction\n * Direction at the end of a drag gesture, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.\n * @param {Boolean} event.shift\n * True if the shift key was pressed during this event.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */dragEndHandler:function dragEndHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {Array.} event.gesturePoints\n * Gesture points associated with the gesture. Velocity data can be found here.\n * @param {OpenSeadragon.Point} event.lastCenter\n * The previous center point of the two pinch contact points relative to the tracked element.\n * @param {OpenSeadragon.Point} event.center\n * The center point of the two pinch contact points relative to the tracked element.\n * @param {Number} event.lastDistance\n * The previous distance between the two pinch contact points in CSS pixels.\n * @param {Number} event.distance\n * The distance between the two pinch contact points in CSS pixels.\n * @param {Boolean} event.shift\n * True if the shift key was pressed during this event.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */pinchHandler:function pinchHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Number} event.buttons\n * Current buttons pressed.\n * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */stopHandler:function stopHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {Number} event.keyCode\n * The key code that was pressed.\n * @param {Boolean} event.ctrl\n * True if the ctrl key was pressed during this event.\n * @param {Boolean} event.shift\n * True if the shift key was pressed during this event.\n * @param {Boolean} event.alt\n * True if the alt key was pressed during this event.\n * @param {Boolean} event.meta\n * True if the meta key was pressed during this event.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */keyDownHandler:function keyDownHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {Number} event.keyCode\n * The key code that was pressed.\n * @param {Boolean} event.ctrl\n * True if the ctrl key was pressed during this event.\n * @param {Boolean} event.shift\n * True if the shift key was pressed during this event.\n * @param {Boolean} event.alt\n * True if the alt key was pressed during this event.\n * @param {Boolean} event.meta\n * True if the meta key was pressed during this event.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */keyUpHandler:function keyUpHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {Number} event.keyCode\n * The key code that was pressed.\n * @param {Boolean} event.ctrl\n * True if the ctrl key was pressed during this event.\n * @param {Boolean} event.shift\n * True if the shift key was pressed during this event.\n * @param {Boolean} event.alt\n * True if the alt key was pressed during this event.\n * @param {Boolean} event.meta\n * True if the meta key was pressed during this event.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */keyHandler:function keyHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */focusHandler:function focusHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */blurHandler:function blurHandler(){}};/**\n * Resets all active mousetrakers. (Added to patch issue #697 \"Mouse up outside map will cause \"canvas-drag\" event to stick\")\n *\n * @private\n * @member resetAllMouseTrackers\n * @memberof OpenSeadragon.MouseTracker\n */$.MouseTracker.resetAllMouseTrackers=function(){for(var i=0;i8||'onwheel'in document.createElement('div')?'wheel':// Modern browsers support 'wheel'\ndocument.onmousewheel!==undefined?'mousewheel':// Webkit and IE support at least 'mousewheel'\n'DOMMouseScroll';// Assume old Firefox\n/**\n * Detect legacy mouse capture support.\n */$.MouseTracker.supportsMouseCapture=function(){var divElement=document.createElement('div');return $.isFunction(divElement.setCapture)&&$.isFunction(divElement.releaseCapture);}();/**\n * Detect browser pointer device event model(s) and build appropriate list of events to subscribe to.\n */$.MouseTracker.subscribeEvents=[\"click\",\"dblclick\",\"keydown\",\"keyup\",\"keypress\",\"focus\",\"blur\",$.MouseTracker.wheelEventName];if($.MouseTracker.wheelEventName==\"DOMMouseScroll\"){// Older Firefox\n$.MouseTracker.subscribeEvents.push(\"MozMousePixelScroll\");}// Note: window.navigator.pointerEnable is deprecated on IE 11 and not part of W3C spec.\nif(window.PointerEvent&&(window.navigator.pointerEnabled||$.Browser.vendor!==$.BROWSERS.IE)){// IE11 and other W3C Pointer Event implementations (see http://www.w3.org/TR/pointerevents)\n$.MouseTracker.havePointerEvents=true;$.MouseTracker.subscribeEvents.push(\"pointerover\",\"pointerout\",\"pointerdown\",\"pointerup\",\"pointermove\",\"pointercancel\");$.MouseTracker.unprefixedPointerEvents=true;if(navigator.maxTouchPoints){$.MouseTracker.maxTouchPoints=navigator.maxTouchPoints;}else{$.MouseTracker.maxTouchPoints=0;}$.MouseTracker.haveMouseEnter=false;}else if(window.MSPointerEvent&&window.navigator.msPointerEnabled){// IE10\n$.MouseTracker.havePointerEvents=true;$.MouseTracker.subscribeEvents.push(\"MSPointerOver\",\"MSPointerOut\",\"MSPointerDown\",\"MSPointerUp\",\"MSPointerMove\",\"MSPointerCancel\");$.MouseTracker.unprefixedPointerEvents=false;if(navigator.msMaxTouchPoints){$.MouseTracker.maxTouchPoints=navigator.msMaxTouchPoints;}else{$.MouseTracker.maxTouchPoints=0;}$.MouseTracker.haveMouseEnter=false;}else{// Legacy W3C mouse events\n$.MouseTracker.havePointerEvents=false;if($.Browser.vendor===$.BROWSERS.IE&&$.Browser.version<9){$.MouseTracker.subscribeEvents.push(\"mouseenter\",\"mouseleave\");$.MouseTracker.haveMouseEnter=true;}else{$.MouseTracker.subscribeEvents.push(\"mouseover\",\"mouseout\");$.MouseTracker.haveMouseEnter=false;}$.MouseTracker.subscribeEvents.push(\"mousedown\",\"mouseup\",\"mousemove\");if('ontouchstart'in window){// iOS, Android, and other W3c Touch Event implementations\n// (see http://www.w3.org/TR/touch-events/)\n// (see https://developer.apple.com/library/ios/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html)\n// (see https://developer.apple.com/library/safari/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html)\n$.MouseTracker.subscribeEvents.push(\"touchstart\",\"touchend\",\"touchmove\",\"touchcancel\");}if('ongesturestart'in window){// iOS (see https://developer.apple.com/library/ios/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html)\n// Subscribe to these to prevent default gesture handling\n$.MouseTracker.subscribeEvents.push(\"gesturestart\",\"gesturechange\");}$.MouseTracker.mousePointerId=\"legacy-mouse\";$.MouseTracker.maxTouchPoints=10;}///////////////////////////////////////////////////////////////////////////////\n// Classes and typedefs\n///////////////////////////////////////////////////////////////////////////////\n/**\n * Represents a point of contact on the screen made by a mouse cursor, pen, touch, or other pointer device.\n *\n * @typedef {Object} GesturePoint\n * @memberof OpenSeadragon.MouseTracker\n *\n * @property {Number} id\n * Identifier unique from all other active GesturePoints for a given pointer device.\n * @property {String} type\n * The pointer device type: \"mouse\", \"touch\", \"pen\", etc.\n * @property {Boolean} captured\n * True if events for the gesture point are captured to the tracked element.\n * @property {Boolean} isPrimary\n * True if the gesture point is a master pointer amongst the set of active pointers for each pointer type. True for mouse and primary (first) touch/pen pointers.\n * @property {Boolean} insideElementPressed\n * True if button pressed or contact point initiated inside the screen area of the tracked element.\n * @property {Boolean} insideElement\n * True if pointer or contact point is currently inside the bounds of the tracked element.\n * @property {Number} speed\n * Current computed speed, in pixels per second.\n * @property {Number} direction\n * Current computed direction, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.\n * @property {OpenSeadragon.Point} contactPos\n * The initial pointer contact position, relative to the page including any scrolling. Only valid if the pointer has contact (pressed, touch contact, pen contact).\n * @property {Number} contactTime\n * The initial pointer contact time, in milliseconds. Only valid if the pointer has contact (pressed, touch contact, pen contact).\n * @property {OpenSeadragon.Point} lastPos\n * The last pointer position, relative to the page including any scrolling.\n * @property {Number} lastTime\n * The last pointer contact time, in milliseconds.\n * @property {OpenSeadragon.Point} currentPos\n * The current pointer position, relative to the page including any scrolling.\n * @property {Number} currentTime\n * The current pointer contact time, in milliseconds.\n */ /**\n * @class GesturePointList\n * @classdesc Provides an abstraction for a set of active {@link OpenSeadragon.MouseTracker.GesturePoint|GesturePoint} objects for a given pointer device type.\n * Active pointers are any pointer being tracked for this element which are in the hit-test area\n * of the element (for hover-capable devices) and/or have contact or a button press initiated in the element.\n * @memberof OpenSeadragon.MouseTracker\n * @param {String} type - The pointer device type: \"mouse\", \"touch\", \"pen\", etc.\n */$.MouseTracker.GesturePointList=function(type){this._gPoints=[];/**\n * The pointer device type: \"mouse\", \"touch\", \"pen\", etc.\n * @member {String} type\n * @memberof OpenSeadragon.MouseTracker.GesturePointList#\n */this.type=type;/**\n * Current buttons pressed for the device.\n * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @member {Number} buttons\n * @memberof OpenSeadragon.MouseTracker.GesturePointList#\n */this.buttons=0;/**\n * Current number of contact points (touch points, mouse down, etc.) for the device.\n * @member {Number} contacts\n * @memberof OpenSeadragon.MouseTracker.GesturePointList#\n */this.contacts=0;/**\n * Current number of clicks for the device. Used for multiple click gesture tracking.\n * @member {Number} clicks\n * @memberof OpenSeadragon.MouseTracker.GesturePointList#\n */this.clicks=0;/**\n * Current number of captured pointers for the device.\n * @member {Number} captureCount\n * @memberof OpenSeadragon.MouseTracker.GesturePointList#\n */this.captureCount=0;};/** @lends OpenSeadragon.MouseTracker.GesturePointList.prototype */$.MouseTracker.GesturePointList.prototype={/**\n * @function\n * @returns {Number} Number of gesture points in the list.\n */getLength:function getLength(){return this._gPoints.length;},/**\n * @function\n * @returns {Array.} The list of gesture points in the list as an array (read-only).\n */asArray:function asArray(){return this._gPoints;},/**\n * @function\n * @param {OpenSeadragon.MouseTracker.GesturePoint} gesturePoint - A gesture point to add to the list.\n * @returns {Number} Number of gesture points in the list.\n */add:function add(gp){return this._gPoints.push(gp);},/**\n * @function\n * @param {Number} id - The id of the gesture point to remove from the list.\n * @returns {Number} Number of gesture points in the list.\n */removeById:function removeById(id){var i,len=this._gPoints.length;for(i=0;i1&&(this.type===\"mouse\"||this.type===\"pen\")){this.contacts=1;}},/**\n * Decrement this pointer's contact count.\n * It will make sure the count does not go below 0.\n * @function\n */removeContact:function removeContact(){--this.contacts;if(this.contacts<0){this.contacts=0;}}};///////////////////////////////////////////////////////////////////////////////\n// Utility functions\n///////////////////////////////////////////////////////////////////////////////\n/**\n * Removes all tracked pointers.\n * @private\n * @inner\n */function clearTrackedPointers(tracker){var delegate=THIS[tracker.hash],i,pointerListCount=delegate.activePointersLists.length;for(i=0;i0){$.removeEvent($.MouseTracker.captureElement,'mousemove',delegate.mousemovecaptured,true);$.removeEvent($.MouseTracker.captureElement,'mouseup',delegate.mouseupcaptured,true);$.removeEvent($.MouseTracker.captureElement,$.MouseTracker.unprefixedPointerEvents?'pointermove':'MSPointerMove',delegate.pointermovecaptured,true);$.removeEvent($.MouseTracker.captureElement,$.MouseTracker.unprefixedPointerEvents?'pointerup':'MSPointerUp',delegate.pointerupcaptured,true);$.removeEvent($.MouseTracker.captureElement,'touchmove',delegate.touchmovecaptured,true);$.removeEvent($.MouseTracker.captureElement,'touchend',delegate.touchendcaptured,true);delegate.activePointersLists[i].captureCount=0;}}for(i=0;i0){for(i=0;i0){// simulate touchend/mouseup\nupdatePointersUp(tracker,event,abortGPoints,0);// 0 means primary button press/release or touch contact\n// release pointer capture\npointsList.captureCount=1;releasePointer(tracker,pointsList.type);// simulate touchleave/mouseout\nupdatePointersExit(tracker,event,abortGPoints);}}}/**\n * @private\n * @inner\n */function onTouchStart(tracker,event){var time,i,j,touchCount=event.changedTouches.length,gPoints=[],parentGPoints,pointsList=tracker.getActivePointersListByType('touch');time=$.now();if(pointsList.getLength()>event.touches.length-touchCount){$.console.warn('Tracked touch contact count doesn\\'t match event.touches.length. Removing all tracked touch pointers.');abortContacts(tracker,event,pointsList);}for(i=0;i} gPoints\n * Gesture points associated with the event.\n */function updatePointersEnter(tracker,event,gPoints){var pointsList=tracker.getActivePointersListByType(gPoints[0].type),i,gPointCount=gPoints.length,curGPoint,updateGPoint,propagate;for(i=0;i} gPoints\n * Gesture points associated with the event.\n */function updatePointersExit(tracker,event,gPoints){var pointsList=tracker.getActivePointersListByType(gPoints[0].type),i,gPointCount=gPoints.length,curGPoint,updateGPoint,propagate;for(i=0;i} gPoints\n * Gesture points associated with the event.\n * @param {Number} buttonChanged\n * The button involved in the event: -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.\n * Note on chorded button presses (a button pressed when another button is already pressed): In the W3C Pointer Events model,\n * only one pointerdown/pointerup event combo is fired. Chorded button state changes instead fire pointermove events.\n *\n * @returns {Boolean} True if pointers should be captured to the tracked element, otherwise false.\n */function updatePointersDown(tracker,event,gPoints,buttonChanged){var delegate=THIS[tracker.hash],propagate,pointsList=tracker.getActivePointersListByType(gPoints[0].type),i,gPointCount=gPoints.length,curGPoint,updateGPoint;if(typeof event.buttons!=='undefined'){pointsList.buttons=event.buttons;}else{if($.Browser.vendor===$.BROWSERS.IE&&$.Browser.version<9){if(buttonChanged===0){// Primary\npointsList.buttons+=1;}else if(buttonChanged===1){// Aux\npointsList.buttons+=4;}else if(buttonChanged===2){// Secondary\npointsList.buttons+=2;}else if(buttonChanged===3){// X1 (Back)\npointsList.buttons+=8;}else if(buttonChanged===4){// X2 (Forward)\npointsList.buttons+=16;}else if(buttonChanged===5){// Pen Eraser\npointsList.buttons+=32;}}else{if(buttonChanged===0){// Primary\npointsList.buttons|=1;}else if(buttonChanged===1){// Aux\npointsList.buttons|=4;}else if(buttonChanged===2){// Secondary\npointsList.buttons|=2;}else if(buttonChanged===3){// X1 (Back)\npointsList.buttons|=8;}else if(buttonChanged===4){// X2 (Forward)\npointsList.buttons|=16;}else if(buttonChanged===5){// Pen Eraser\npointsList.buttons|=32;}}}// Some pointers may steal control from another pointer without firing the appropriate release events\n// e.g. Touching a screen while click-dragging with certain mice.\nvar otherPointsLists=tracker.getActivePointersListsExceptType(gPoints[0].type);for(i=0;i} gPoints\n * Gesture points associated with the event.\n * @param {Number} buttonChanged\n * The button involved in the event: -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.\n * Note on chorded button presses (a button pressed when another button is already pressed): In the W3C Pointer Events model,\n * only one pointerdown/pointerup event combo is fired. Chorded button state changes instead fire pointermove events.\n *\n * @returns {Boolean} True if pointer capture should be released from the tracked element, otherwise false.\n */function updatePointersUp(tracker,event,gPoints,buttonChanged){var delegate=THIS[tracker.hash],pointsList=tracker.getActivePointersListByType(gPoints[0].type),propagate,releasePoint,releaseTime,i,gPointCount=gPoints.length,curGPoint,updateGPoint,releaseCapture=false,wasCaptured=false,quick;if(typeof event.buttons!=='undefined'){pointsList.buttons=event.buttons;}else{if($.Browser.vendor===$.BROWSERS.IE&&$.Browser.version<9){if(buttonChanged===0){// Primary\npointsList.buttons-=1;}else if(buttonChanged===1){// Aux\npointsList.buttons-=4;}else if(buttonChanged===2){// Secondary\npointsList.buttons-=2;}else if(buttonChanged===3){// X1 (Back)\npointsList.buttons-=8;}else if(buttonChanged===4){// X2 (Forward)\npointsList.buttons-=16;}else if(buttonChanged===5){// Pen Eraser\npointsList.buttons-=32;}}else{if(buttonChanged===0){// Primary\npointsList.buttons^=~1;}else if(buttonChanged===1){// Aux\npointsList.buttons^=~4;}else if(buttonChanged===2){// Secondary\npointsList.buttons^=~2;}else if(buttonChanged===3){// X1 (Back)\npointsList.buttons^=~8;}else if(buttonChanged===4){// X2 (Forward)\npointsList.buttons^=~16;}else if(buttonChanged===5){// Pen Eraser\npointsList.buttons^=~32;}}}// Only capture and track primary button, pen, and touch contacts\nif(buttonChanged!==0){// Aux Release\nif(tracker.nonPrimaryReleaseHandler){propagate=tracker.nonPrimaryReleaseHandler({eventSource:tracker,pointerType:gPoints[0].type,position:getPointRelativeToAbsolute(gPoints[0].currentPos,tracker.element),button:buttonChanged,buttons:pointsList.buttons,isTouchEvent:gPoints[0].type==='touch',originalEvent:event,preventDefaultAction:false,userData:tracker.userData});if(propagate===false){$.cancelEvent(event);}}// A primary mouse button may have been released while the non-primary button was down\nvar otherPointsList=tracker.getActivePointersListByType(\"mouse\");// Stop tracking the mouse; see https://github.com/openseadragon/openseadragon/pull/1223\nabortContacts(tracker,event,otherPointsList);// No-op if no active pointer\nreturn false;}for(i=0;i} gPoints\n * Gesture points associated with the event.\n */function updatePointersMove(tracker,event,gPoints){var delegate=THIS[tracker.hash],pointsList=tracker.getActivePointersListByType(gPoints[0].type),i,gPointCount=gPoints.length,curGPoint,updateGPoint,gPointArray,delta,propagate;if(typeof event.buttons!=='undefined'){pointsList.buttons=event.buttons;}for(i=0;i} gPoints\n * Gesture points associated with the event.\n */function updatePointersCancel(tracker,event,gPoints){updatePointersUp(tracker,event,gPoints,0);updatePointersExit(tracker,event,gPoints);}/**\n * @private\n * @inner\n */function handlePointerStop(tracker,originalMoveEvent,pointerType){if(tracker.stopHandler){tracker.stopHandler({eventSource:tracker,pointerType:pointerType,position:getMouseRelative(originalMoveEvent,tracker.element),buttons:tracker.getActivePointersListByType(pointerType).buttons,isTouchEvent:pointerType==='touch',originalEvent:originalMoveEvent,preventDefaultAction:false,userData:tracker.userData});}}/**\n * True if inside an iframe, otherwise false.\n * @member {Boolean} isInIframe\n * @private\n * @inner\n */var isInIframe=function(){try{return window.self!==window.top;}catch(e){return true;}}();/**\n * @function\n * @private\n * @inner\n * @returns {Boolean} True if the target has access rights to events, otherwise false.\n */function canAccessEvents(target){try{return target.addEventListener&&target.removeEventListener;}catch(e){return false;}}})(OpenSeadragon);/*\n * OpenSeadragon - Control\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * An enumeration of supported locations where controls can be anchored.\n * The anchoring is always relative to the container.\n * @member ControlAnchor\n * @memberof OpenSeadragon\n * @static\n * @type {Object}\n * @property {Number} NONE\n * @property {Number} TOP_LEFT\n * @property {Number} TOP_RIGHT\n * @property {Number} BOTTOM_LEFT\n * @property {Number} BOTTOM_RIGHT\n * @property {Number} ABSOLUTE\n */$.ControlAnchor={NONE:0,TOP_LEFT:1,TOP_RIGHT:2,BOTTOM_RIGHT:3,BOTTOM_LEFT:4,ABSOLUTE:5};/**\n * @class Control\n * @classdesc A Control represents any interface element which is meant to allow the user\n * to interact with the zoomable interface. Any control can be anchored to any\n * element.\n *\n * @memberof OpenSeadragon\n * @param {Element} element - the control element to be anchored in the container.\n * @param {Object } options - All required and optional settings for configuring a control element.\n * @param {OpenSeadragon.ControlAnchor} [options.anchor=OpenSeadragon.ControlAnchor.NONE] - the position of the control\n * relative to the container.\n * @param {Boolean} [options.attachToViewer=true] - Whether the control should be added directly to the viewer, or\n * directly to the container\n * @param {Boolean} [options.autoFade=true] - Whether the control should have the autofade behavior\n * @param {Element} container - the element to control will be anchored too.\n */$.Control=function(element,options,container){var parent=element.parentNode;if(typeof options==='number'){$.console.error(\"Passing an anchor directly into the OpenSeadragon.Control constructor is deprecated; \"+\"please use an options object instead. \"+\"Support for this deprecated variant is scheduled for removal in December 2013\");options={anchor:options};}options.attachToViewer=typeof options.attachToViewer==='undefined'?true:options.attachToViewer;/**\n * True if the control should have autofade behavior.\n * @member {Boolean} autoFade\n * @memberof OpenSeadragon.Control#\n */this.autoFade=typeof options.autoFade==='undefined'?true:options.autoFade;/**\n * The element providing the user interface with some type of control (e.g. a zoom-in button).\n * @member {Element} element\n * @memberof OpenSeadragon.Control#\n */this.element=element;/**\n * The position of the Control relative to its container.\n * @member {OpenSeadragon.ControlAnchor} anchor\n * @memberof OpenSeadragon.Control#\n */this.anchor=options.anchor;/**\n * The Control's containing element.\n * @member {Element} container\n * @memberof OpenSeadragon.Control#\n */this.container=container;/**\n * A neutral element surrounding the control element.\n * @member {Element} wrapper\n * @memberof OpenSeadragon.Control#\n */if(this.anchor==$.ControlAnchor.ABSOLUTE){this.wrapper=$.makeNeutralElement(\"div\");this.wrapper.style.position=\"absolute\";this.wrapper.style.top=typeof options.top==\"number\"?options.top+'px':options.top;this.wrapper.style.left=typeof options.left==\"number\"?options.left+'px':options.left;this.wrapper.style.height=typeof options.height==\"number\"?options.height+'px':options.height;this.wrapper.style.width=typeof options.width==\"number\"?options.width+'px':options.width;this.wrapper.style.margin=\"0px\";this.wrapper.style.padding=\"0px\";this.element.style.position=\"relative\";this.element.style.top=\"0px\";this.element.style.left=\"0px\";this.element.style.height=\"100%\";this.element.style.width=\"100%\";}else{this.wrapper=$.makeNeutralElement(\"div\");this.wrapper.style.display=\"inline-block\";if(this.anchor==$.ControlAnchor.NONE){// IE6 fix\nthis.wrapper.style.width=this.wrapper.style.height=\"100%\";}}this.wrapper.appendChild(this.element);if(options.attachToViewer){if(this.anchor==$.ControlAnchor.TOP_RIGHT||this.anchor==$.ControlAnchor.BOTTOM_RIGHT){this.container.insertBefore(this.wrapper,this.container.firstChild);}else{this.container.appendChild(this.wrapper);}}else{parent.appendChild(this.wrapper);}};/** @lends OpenSeadragon.Control.prototype */$.Control.prototype={/**\n * Removes the control from the container.\n * @function\n */destroy:function destroy(){this.wrapper.removeChild(this.element);this.container.removeChild(this.wrapper);},/**\n * Determines if the control is currently visible.\n * @function\n * @return {Boolean} true if currenly visible, false otherwise.\n */isVisible:function isVisible(){return this.wrapper.style.display!=\"none\";},/**\n * Toggles the visibility of the control.\n * @function\n * @param {Boolean} visible - true to make visible, false to hide.\n */setVisible:function setVisible(visible){this.wrapper.style.display=visible?this.anchor==$.ControlAnchor.ABSOLUTE?'block':'inline-block':\"none\";},/**\n * Sets the opacity level for the control.\n * @function\n * @param {Number} opactiy - a value between 1 and 0 inclusively.\n */setOpacity:function setOpacity(opacity){if(this.element[$.SIGNAL]&&$.Browser.vendor==$.BROWSERS.IE){$.setElementOpacity(this.element,opacity,true);}else{$.setElementOpacity(this.wrapper,opacity,true);}}};})(OpenSeadragon);/*\n * OpenSeadragon - ControlDock\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * @class ControlDock\n * @classdesc Provides a container element (a <form> element) with support for the layout of control elements.\n *\n * @memberof OpenSeadragon\n */$.ControlDock=function(options){var layouts=['topleft','topright','bottomright','bottomleft'],layout,i;$.extend(true,this,{id:'controldock-'+$.now()+'-'+Math.floor(Math.random()*1000000),container:$.makeNeutralElement('div'),controls:[]},options);// Disable the form's submit; otherwise button clicks and return keys\n// can trigger it.\nthis.container.onsubmit=function(){return false;};if(this.element){this.element=$.getElement(this.element);this.element.appendChild(this.container);this.element.style.position='relative';this.container.style.width='100%';this.container.style.height='100%';}for(i=0;i=0){return;// they're trying to add a duplicate control\n}switch(controlOptions.anchor){case $.ControlAnchor.TOP_RIGHT:div=this.controls.topright;element.style.position=\"relative\";element.style.paddingRight=\"0px\";element.style.paddingTop=\"0px\";break;case $.ControlAnchor.BOTTOM_RIGHT:div=this.controls.bottomright;element.style.position=\"relative\";element.style.paddingRight=\"0px\";element.style.paddingBottom=\"0px\";break;case $.ControlAnchor.BOTTOM_LEFT:div=this.controls.bottomleft;element.style.position=\"relative\";element.style.paddingLeft=\"0px\";element.style.paddingBottom=\"0px\";break;case $.ControlAnchor.TOP_LEFT:div=this.controls.topleft;element.style.position=\"relative\";element.style.paddingLeft=\"0px\";element.style.paddingTop=\"0px\";break;case $.ControlAnchor.ABSOLUTE:div=this.container;element.style.margin=\"0px\";element.style.padding=\"0px\";break;default:case $.ControlAnchor.NONE:div=this.container;element.style.margin=\"0px\";element.style.padding=\"0px\";break;}this.controls.push(new $.Control(element,controlOptions,div));element.style.display=\"inline-block\";},/**\n * @function\n * @return {OpenSeadragon.ControlDock} Chainable.\n */removeControl:function removeControl(element){element=$.getElement(element);var i=getControlIndex(this,element);if(i>=0){this.controls[i].destroy();this.controls.splice(i,1);}return this;},/**\n * @function\n * @return {OpenSeadragon.ControlDock} Chainable.\n */clearControls:function clearControls(){while(this.controls.length>0){this.controls.pop().destroy();}return this;},/**\n * @function\n * @return {Boolean}\n */areControlsEnabled:function areControlsEnabled(){var i;for(i=this.controls.length-1;i>=0;i--){if(this.controls[i].isVisible()){return true;}}return false;},/**\n * @function\n * @return {OpenSeadragon.ControlDock} Chainable.\n */setControlsEnabled:function setControlsEnabled(enabled){var i;for(i=this.controls.length-1;i>=0;i--){this.controls[i].setVisible(enabled);}return this;}};///////////////////////////////////////////////////////////////////////////////\n// Utility methods\n///////////////////////////////////////////////////////////////////////////////\nfunction getControlIndex(dock,element){var controls=dock.controls,i;for(i=controls.length-1;i>=0;i--){if(controls[i].element==element){return i;}}return-1;}})(OpenSeadragon);/*\n * OpenSeadragon - Placement\n *\n * Copyright (C) 2010-2016 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * An enumeration of positions to anchor an element.\n * @member Placement\n * @memberOf OpenSeadragon\n * @static\n * @readonly\n * @property {OpenSeadragon.Placement} CENTER\n * @property {OpenSeadragon.Placement} TOP_LEFT\n * @property {OpenSeadragon.Placement} TOP\n * @property {OpenSeadragon.Placement} TOP_RIGHT\n * @property {OpenSeadragon.Placement} RIGHT\n * @property {OpenSeadragon.Placement} BOTTOM_RIGHT\n * @property {OpenSeadragon.Placement} BOTTOM\n * @property {OpenSeadragon.Placement} BOTTOM_LEFT\n * @property {OpenSeadragon.Placement} LEFT\n */$.Placement=$.freezeObject({CENTER:0,TOP_LEFT:1,TOP:2,TOP_RIGHT:3,RIGHT:4,BOTTOM_RIGHT:5,BOTTOM:6,BOTTOM_LEFT:7,LEFT:8,properties:{0:{isLeft:false,isHorizontallyCentered:true,isRight:false,isTop:false,isVerticallyCentered:true,isBottom:false},1:{isLeft:true,isHorizontallyCentered:false,isRight:false,isTop:true,isVerticallyCentered:false,isBottom:false},2:{isLeft:false,isHorizontallyCentered:true,isRight:false,isTop:true,isVerticallyCentered:false,isBottom:false},3:{isLeft:false,isHorizontallyCentered:false,isRight:true,isTop:true,isVerticallyCentered:false,isBottom:false},4:{isLeft:false,isHorizontallyCentered:false,isRight:true,isTop:false,isVerticallyCentered:true,isBottom:false},5:{isLeft:false,isHorizontallyCentered:false,isRight:true,isTop:false,isVerticallyCentered:false,isBottom:true},6:{isLeft:false,isHorizontallyCentered:true,isRight:false,isTop:false,isVerticallyCentered:false,isBottom:true},7:{isLeft:true,isHorizontallyCentered:false,isRight:false,isTop:false,isVerticallyCentered:false,isBottom:true},8:{isLeft:true,isHorizontallyCentered:false,isRight:false,isTop:false,isVerticallyCentered:true,isBottom:false}}});})(OpenSeadragon);/*\n * OpenSeadragon - Viewer\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){// dictionary from hash to private properties\nvar THIS={};var nextHash=1;/**\n *\n * The main point of entry into creating a zoomable image on the page.
\n *
\n * We have provided an idiomatic javascript constructor which takes\n * a single object, but still support the legacy positional arguments.
\n *
\n * The options below are given in order that they appeared in the constructor\n * as arguments and we translate a positional call into an idiomatic call.
\n *
\n * To create a viewer, you can use either of this methods:
\n * \n * var viewer = new OpenSeadragon.Viewer(options);
\n * var viewer = OpenSeadragon(options);
\n *
\n * @class Viewer\n * @classdesc The main OpenSeadragon viewer class.\n *\n * @memberof OpenSeadragon\n * @extends OpenSeadragon.EventSource\n * @extends OpenSeadragon.ControlDock\n * @param {OpenSeadragon.Options} options - Viewer options.\n *\n **/$.Viewer=function(options){var args=arguments,_this=this,i;//backward compatibility for positional args while prefering more\n//idiomatic javascript options object as the only argument\nif(!$.isPlainObject(options)){options={id:args[0],xmlPath:args.length>1?args[1]:undefined,prefixUrl:args.length>2?args[2]:undefined,controls:args.length>3?args[3]:undefined,overlays:args.length>4?args[4]:undefined};}//options.config and the general config argument are deprecated\n//in favor of the more direct specification of optional settings\n//being pass directly on the options object\nif(options.config){$.extend(true,options,options.config);delete options.config;}//Public properties\n//Allow the options object to override global defaults\n$.extend(true,this,{//internal state and dom identifiers\nid:options.id,hash:options.hash||nextHash++,/**\n * Index for page to be shown first next time open() is called (only used in sequenceMode).\n * @member {Number} initialPage\n * @memberof OpenSeadragon.Viewer#\n */initialPage:0,//dom nodes\n/**\n * The parent element of this Viewer instance, passed in when the Viewer was created.\n * @member {Element} element\n * @memberof OpenSeadragon.Viewer#\n */element:null,/**\n * A <div> element (provided by {@link OpenSeadragon.ControlDock}), the base element of this Viewer instance.
\n * Child element of {@link OpenSeadragon.Viewer#element}.\n * @member {Element} container\n * @memberof OpenSeadragon.Viewer#\n */container:null,/**\n * A <div> element, the element where user-input events are handled for panning and zooming.
\n * Child element of {@link OpenSeadragon.Viewer#container},\n * positioned on top of {@link OpenSeadragon.Viewer#keyboardCommandArea}.
\n * The parent of {@link OpenSeadragon.Drawer#canvas} instances.\n * @member {Element} canvas\n * @memberof OpenSeadragon.Viewer#\n */canvas:null,// Overlays list. An overlay allows to add html on top of the viewer.\noverlays:[],// Container inside the canvas where overlays are drawn.\noverlaysContainer:null,//private state properties\npreviousBody:[],//This was originally initialized in the constructor and so could never\n//have anything in it. now it can because we allow it to be specified\n//in the options and is only empty by default if not specified. Also\n//this array was returned from get_controls which I find confusing\n//since this object has a controls property which is treated in other\n//functions like clearControls. I'm removing the accessors.\ncustomControls:[],//These are originally not part options but declared as members\n//in initialize. It's still considered idiomatic to put them here\nsource:null,/**\n * Handles rendering of tiles in the viewer. Created for each TileSource opened.\n * @member {OpenSeadragon.Drawer} drawer\n * @memberof OpenSeadragon.Viewer#\n */drawer:null,world:null,/**\n * Handles coordinate-related functionality - zoom, pan, rotation, etc. Created for each TileSource opened.\n * @member {OpenSeadragon.Viewport} viewport\n * @memberof OpenSeadragon.Viewer#\n */viewport:null,/**\n * @member {OpenSeadragon.Navigator} navigator\n * @memberof OpenSeadragon.Viewer#\n */navigator:null,//A collection viewport is a separate viewport used to provide\n//simultaneous rendering of sets of tiles\ncollectionViewport:null,collectionDrawer:null,//UI image resources\n//TODO: rename navImages to uiImages\nnavImages:null,//interface button controls\nbuttons:null,//TODO: this is defunct so safely remove it\nprofiler:null},$.DEFAULT_SETTINGS,options);if(typeof this.hash===\"undefined\"){throw new Error(\"A hash must be defined, either by specifying options.id or options.hash.\");}if(typeof THIS[this.hash]!==\"undefined\"){// We don't want to throw an error here, as the user might have discarded\n// the previous viewer with the same hash and now want to recreate it.\n$.console.warn(\"Hash \"+this.hash+\" has already been used.\");}//Private state properties\nTHIS[this.hash]={\"fsBoundsDelta\":new $.Point(1,1),\"prevContainerSize\":null,\"animating\":false,\"forceRedraw\":false,\"mouseInside\":false,\"group\":null,// whether we should be continuously zooming\n\"zooming\":false,// how much we should be continuously zooming by\n\"zoomFactor\":null,\"lastZoomTime\":null,\"fullPage\":false,\"onfullscreenchange\":null};this._sequenceIndex=0;this._firstOpen=true;this._updateRequestId=null;this._loadQueue=[];this.currentOverlays=[];this._lastScrollTime=$.now();// variable used to help normalize the scroll event speed of different devices\n//Inherit some behaviors and properties\n$.EventSource.call(this);this.addHandler('open-failed',function(event){var msg=$.getString(\"Errors.OpenFailed\",event.eventSource,event.message);_this._showMessage(msg);});$.ControlDock.call(this,options);//Deal with tile sources\nif(this.xmlPath){//Deprecated option. Now it is preferred to use the tileSources option\nthis.tileSources=[this.xmlPath];}this.element=this.element||document.getElementById(this.id);this.canvas=$.makeNeutralElement(\"div\");this.canvas.className=\"openseadragon-canvas\";(function(style){style.width=\"100%\";style.height=\"100%\";style.overflow=\"hidden\";style.position=\"absolute\";style.top=\"0px\";style.left=\"0px\";})(this.canvas.style);$.setElementTouchActionNone(this.canvas);if(options.tabIndex!==\"\"){this.canvas.tabIndex=options.tabIndex===undefined?0:options.tabIndex;}//the container is created through applying the ControlDock constructor above\nthis.container.className=\"openseadragon-container\";(function(style){style.width=\"100%\";style.height=\"100%\";style.position=\"relative\";style.overflow=\"hidden\";style.left=\"0px\";style.top=\"0px\";style.textAlign=\"left\";// needed to protect against\n})(this.container.style);this.container.insertBefore(this.canvas,this.container.firstChild);this.element.appendChild(this.container);//Used for toggling between fullscreen and default container size\n//TODO: these can be closure private and shared across Viewer\n// instances.\nthis.bodyWidth=document.body.style.width;this.bodyHeight=document.body.style.height;this.bodyOverflow=document.body.style.overflow;this.docOverflow=document.documentElement.style.overflow;this.innerTracker=new $.MouseTracker({element:this.canvas,startDisabled:!this.mouseNavEnabled,clickTimeThreshold:this.clickTimeThreshold,clickDistThreshold:this.clickDistThreshold,dblClickTimeThreshold:this.dblClickTimeThreshold,dblClickDistThreshold:this.dblClickDistThreshold,keyDownHandler:$.delegate(this,onCanvasKeyDown),keyHandler:$.delegate(this,onCanvasKeyPress),clickHandler:$.delegate(this,onCanvasClick),dblClickHandler:$.delegate(this,onCanvasDblClick),dragHandler:$.delegate(this,onCanvasDrag),dragEndHandler:$.delegate(this,onCanvasDragEnd),enterHandler:$.delegate(this,onCanvasEnter),exitHandler:$.delegate(this,onCanvasExit),pressHandler:$.delegate(this,onCanvasPress),releaseHandler:$.delegate(this,onCanvasRelease),nonPrimaryPressHandler:$.delegate(this,onCanvasNonPrimaryPress),nonPrimaryReleaseHandler:$.delegate(this,onCanvasNonPrimaryRelease),scrollHandler:$.delegate(this,onCanvasScroll),pinchHandler:$.delegate(this,onCanvasPinch)});this.outerTracker=new $.MouseTracker({element:this.container,startDisabled:!this.mouseNavEnabled,clickTimeThreshold:this.clickTimeThreshold,clickDistThreshold:this.clickDistThreshold,dblClickTimeThreshold:this.dblClickTimeThreshold,dblClickDistThreshold:this.dblClickDistThreshold,enterHandler:$.delegate(this,onContainerEnter),exitHandler:$.delegate(this,onContainerExit)});if(this.toolbar){this.toolbar=new $.ControlDock({element:this.toolbar});}this.bindStandardControls();THIS[this.hash].prevContainerSize=_getSafeElemSize(this.container);// Create the world\nthis.world=new $.World({viewer:this});this.world.addHandler('add-item',function(event){// For backwards compatibility, we maintain the source property\n_this.source=_this.world.getItemAt(0).source;THIS[_this.hash].forceRedraw=true;if(!_this._updateRequestId){_this._updateRequestId=scheduleUpdate(_this,updateMulti);}});this.world.addHandler('remove-item',function(event){// For backwards compatibility, we maintain the source property\nif(_this.world.getItemCount()){_this.source=_this.world.getItemAt(0).source;}else{_this.source=null;}THIS[_this.hash].forceRedraw=true;});this.world.addHandler('metrics-change',function(event){if(_this.viewport){_this.viewport._setContentBounds(_this.world.getHomeBounds(),_this.world.getContentFactor());}});this.world.addHandler('item-index-change',function(event){// For backwards compatibility, we maintain the source property\n_this.source=_this.world.getItemAt(0).source;});// Create the viewport\nthis.viewport=new $.Viewport({containerSize:THIS[this.hash].prevContainerSize,springStiffness:this.springStiffness,animationTime:this.animationTime,minZoomImageRatio:this.minZoomImageRatio,maxZoomPixelRatio:this.maxZoomPixelRatio,visibilityRatio:this.visibilityRatio,wrapHorizontal:this.wrapHorizontal,wrapVertical:this.wrapVertical,defaultZoomLevel:this.defaultZoomLevel,minZoomLevel:this.minZoomLevel,maxZoomLevel:this.maxZoomLevel,viewer:this,degrees:this.degrees,flipped:this.flipped,navigatorRotate:this.navigatorRotate,homeFillsViewer:this.homeFillsViewer,margins:this.viewportMargins});this.viewport._setContentBounds(this.world.getHomeBounds(),this.world.getContentFactor());// Create the image loader\nthis.imageLoader=new $.ImageLoader({jobLimit:this.imageLoaderLimit,timeout:options.timeout});// Create the tile cache\nthis.tileCache=new $.TileCache({maxImageCacheCount:this.maxImageCacheCount});// Create the drawer\nthis.drawer=new $.Drawer({viewer:this,viewport:this.viewport,element:this.canvas,debugGridColor:this.debugGridColor});// Overlay container\nthis.overlaysContainer=$.makeNeutralElement(\"div\");this.canvas.appendChild(this.overlaysContainer);// Now that we have a drawer, see if it supports rotate. If not we need to remove the rotate buttons\nif(!this.drawer.canRotate()){// Disable/remove the rotate left/right buttons since they aren't supported\nif(this.rotateLeft){i=this.buttons.buttons.indexOf(this.rotateLeft);this.buttons.buttons.splice(i,1);this.buttons.element.removeChild(this.rotateLeft.element);}if(this.rotateRight){i=this.buttons.buttons.indexOf(this.rotateRight);this.buttons.buttons.splice(i,1);this.buttons.element.removeChild(this.rotateRight.element);}}//Instantiate a navigator if configured\nif(this.showNavigator){this.navigator=new $.Navigator({id:this.navigatorId,position:this.navigatorPosition,sizeRatio:this.navigatorSizeRatio,maintainSizeRatio:this.navigatorMaintainSizeRatio,top:this.navigatorTop,left:this.navigatorLeft,width:this.navigatorWidth,height:this.navigatorHeight,autoResize:this.navigatorAutoResize,autoFade:this.navigatorAutoFade,prefixUrl:this.prefixUrl,viewer:this,navigatorRotate:this.navigatorRotate,crossOriginPolicy:this.crossOriginPolicy});}// Sequence mode\nif(this.sequenceMode){this.bindSequenceControls();}// Open initial tilesources\nif(this.tileSources){this.open(this.tileSources);}// Add custom controls\nfor(i=0;i-1&&options.index=0&&page=0){// they're trying to add a duplicate overlay\nreturn this;}var overlay=getOverlayObject(this,options);this.currentOverlays.push(overlay);overlay.drawHTML(this.overlaysContainer,this.viewport);/**\n * Raised when an overlay is added to the viewer (see {@link OpenSeadragon.Viewer#addOverlay}).\n *\n * @event add-overlay\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.\n * @property {Element} element - The overlay element.\n * @property {OpenSeadragon.Point|OpenSeadragon.Rect} location\n * @property {OpenSeadragon.Placement} placement\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('add-overlay',{element:element,location:options.location,placement:options.placement});return this;},/**\n * Updates the overlay represented by the reference to the element or\n * element id moving it to the new location, relative to the new placement.\n * @method\n * @param {Element|String} element - A reference to an element or an id for\n * the element which is overlayed.\n * @param {OpenSeadragon.Point|OpenSeadragon.Rect} location - The point or\n * rectangle which will be overlayed. This is a viewport relative location.\n * @param {OpenSeadragon.Placement} placement - The position of the\n * viewport which the location coordinates will be treated as relative\n * to.\n * @return {OpenSeadragon.Viewer} Chainable.\n * @fires OpenSeadragon.Viewer.event:update-overlay\n */updateOverlay:function updateOverlay(element,location,placement){var i;element=$.getElement(element);i=getOverlayIndex(this.currentOverlays,element);if(i>=0){this.currentOverlays[i].update(location,placement);THIS[this.hash].forceRedraw=true;/**\n * Raised when an overlay's location or placement changes\n * (see {@link OpenSeadragon.Viewer#updateOverlay}).\n *\n * @event update-overlay\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the\n * Viewer which raised the event.\n * @property {Element} element\n * @property {OpenSeadragon.Point|OpenSeadragon.Rect} location\n * @property {OpenSeadragon.Placement} placement\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('update-overlay',{element:element,location:location,placement:placement});}return this;},/**\n * Removes an overlay identified by the reference element or element id\n * and schedules an update.\n * @method\n * @param {Element|String} element - A reference to the element or an\n * element id which represent the ovelay content to be removed.\n * @return {OpenSeadragon.Viewer} Chainable.\n * @fires OpenSeadragon.Viewer.event:remove-overlay\n */removeOverlay:function removeOverlay(element){var i;element=$.getElement(element);i=getOverlayIndex(this.currentOverlays,element);if(i>=0){this.currentOverlays[i].destroy();this.currentOverlays.splice(i,1);THIS[this.hash].forceRedraw=true;/**\n * Raised when an overlay is removed from the viewer\n * (see {@link OpenSeadragon.Viewer#removeOverlay}).\n *\n * @event remove-overlay\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the\n * Viewer which raised the event.\n * @property {Element} element - The overlay element.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('remove-overlay',{element:element});}return this;},/**\n * Removes all currently configured Overlays from this Viewer and schedules\n * an update.\n * @method\n * @return {OpenSeadragon.Viewer} Chainable.\n * @fires OpenSeadragon.Viewer.event:clear-overlay\n */clearOverlays:function clearOverlays(){while(this.currentOverlays.length>0){this.currentOverlays.pop().destroy();}THIS[this.hash].forceRedraw=true;/**\n * Raised when all overlays are removed from the viewer (see {@link OpenSeadragon.Drawer#clearOverlays}).\n *\n * @event clear-overlay\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('clear-overlay',{});return this;},/**\n * Finds an overlay identified by the reference element or element id\n * and returns it as an object, return null if not found.\n * @method\n * @param {Element|String} element - A reference to the element or an\n * element id which represents the overlay content.\n * @return {OpenSeadragon.Overlay} the matching overlay or null if none found.\n */getOverlayById:function getOverlayById(element){var i;element=$.getElement(element);i=getOverlayIndex(this.currentOverlays,element);if(i>=0){return this.currentOverlays[i];}else{return null;}},/**\n * Updates the sequence buttons.\n * @function OpenSeadragon.Viewer.prototype._updateSequenceButtons\n * @private\n * @param {Number} Sequence Value\n */_updateSequenceButtons:function _updateSequenceButtons(page){if(this.nextButton){if(!this.tileSources||this.tileSources.length-1===page){//Disable next button\nif(!this.navPrevNextWrap){this.nextButton.disable();}}else{this.nextButton.enable();}}if(this.previousButton){if(page>0){//Enable previous button\nthis.previousButton.enable();}else{if(!this.navPrevNextWrap){this.previousButton.disable();}}}},/**\n * Display a message in the viewport\n * @function OpenSeadragon.Viewer.prototype._showMessage\n * @private\n * @param {String} text message\n */_showMessage:function _showMessage(message){this._hideMessage();var div=$.makeNeutralElement(\"div\");div.appendChild(document.createTextNode(message));this.messageDiv=$.makeCenteredNode(div);$.addClass(this.messageDiv,\"openseadragon-message\");this.container.appendChild(this.messageDiv);},/**\n * Hide any currently displayed viewport message\n * @function OpenSeadragon.Viewer.prototype._hideMessage\n * @private\n */_hideMessage:function _hideMessage(){var div=this.messageDiv;if(div){div.parentNode.removeChild(div);delete this.messageDiv;}},/**\n * Gets this viewer's gesture settings for the given pointer device type.\n * @method\n * @param {String} type - The pointer device type to get the gesture settings for (\"mouse\", \"touch\", \"pen\", etc.).\n * @return {OpenSeadragon.GestureSettings}\n */gestureSettingsByDeviceType:function gestureSettingsByDeviceType(type){switch(type){case'mouse':return this.gestureSettingsMouse;case'touch':return this.gestureSettingsTouch;case'pen':return this.gestureSettingsPen;default:return this.gestureSettingsUnknown;}},// private\n_drawOverlays:function _drawOverlays(){var i,length=this.currentOverlays.length;for(i=0;i1){this.referenceStrip=new $.ReferenceStrip({id:this.referenceStripElement,position:this.referenceStripPosition,sizeRatio:this.referenceStripSizeRatio,scroll:this.referenceStripScroll,height:this.referenceStripHeight,width:this.referenceStripWidth,tileSources:this.tileSources,prefixUrl:this.prefixUrl,viewer:this});this.referenceStrip.setFocus(this._sequenceIndex);}}else{$.console.warn('Attempting to display a reference strip while \"sequenceMode\" is off.');}}});/**\n * _getSafeElemSize is like getElementSize(), but refuses to return 0 for x or y,\n * which was causing some calling operations to return NaN.\n * @returns {Point}\n * @private\n */function _getSafeElemSize(oElement){oElement=$.getElement(oElement);return new $.Point(oElement.clientWidth===0?1:oElement.clientWidth,oElement.clientHeight===0?1:oElement.clientHeight);}/**\n * @function\n * @private\n */function getTileSourceImplementation(viewer,tileSource,imgOptions,successCallback,failCallback){var _this=viewer;//allow plain xml strings or json strings to be parsed here\nif($.type(tileSource)=='string'){//xml should start with \"<\" and end with \">\"\nif(tileSource.match(/^\\s*<.*>\\s*$/)){tileSource=$.parseXml(tileSource);//json should start with \"{\" or \"[\" and end with \"}\" or \"]\"\n}else if(tileSource.match(/^\\s*[\\{\\[].*[\\}\\]]\\s*$/)){try{var tileSourceJ=$.parseJSON(tileSource);tileSource=tileSourceJ;}catch(e){//tileSource = tileSource;\n}}}function waitUntilReady(tileSource,originalTileSource){if(tileSource.ready){successCallback(tileSource);}else{tileSource.addHandler('ready',function(){successCallback(tileSource);});tileSource.addHandler('open-failed',function(event){failCallback({message:event.message,source:originalTileSource});});}}setTimeout(function(){if($.type(tileSource)=='string'){//If its still a string it means it must be a url at this point\ntileSource=new $.TileSource({url:tileSource,crossOriginPolicy:imgOptions.crossOriginPolicy!==undefined?imgOptions.crossOriginPolicy:viewer.crossOriginPolicy,ajaxWithCredentials:viewer.ajaxWithCredentials,ajaxHeaders:viewer.ajaxHeaders,useCanvas:viewer.useCanvas,success:function success(event){successCallback(event.tileSource);}});tileSource.addHandler('open-failed',function(event){failCallback(event);});}else if($.isPlainObject(tileSource)||tileSource.nodeType){if(tileSource.crossOriginPolicy===undefined&&(imgOptions.crossOriginPolicy!==undefined||viewer.crossOriginPolicy!==undefined)){tileSource.crossOriginPolicy=imgOptions.crossOriginPolicy!==undefined?imgOptions.crossOriginPolicy:viewer.crossOriginPolicy;}if(tileSource.ajaxWithCredentials===undefined){tileSource.ajaxWithCredentials=viewer.ajaxWithCredentials;}if(tileSource.useCanvas===undefined){tileSource.useCanvas=viewer.useCanvas;}if($.isFunction(tileSource.getTileUrl)){//Custom tile source\nvar customTileSource=new $.TileSource(tileSource);customTileSource.getTileUrl=tileSource.getTileUrl;successCallback(customTileSource);}else{//inline configuration\nvar $TileSource=$.TileSource.determineType(_this,tileSource);if(!$TileSource){failCallback({message:\"Unable to load TileSource\",source:tileSource});return;}var options=$TileSource.prototype.configure.apply(_this,[tileSource]);waitUntilReady(new $TileSource(options),tileSource);}}else{//can assume it's already a tile source implementation\nwaitUntilReady(tileSource,tileSource);}});}function getOverlayObject(viewer,overlay){if(overlay instanceof $.Overlay){return overlay;}var element=null;if(overlay.element){element=$.getElement(overlay.element);}else{var id=overlay.id?overlay.id:\"openseadragon-overlay-\"+Math.floor(Math.random()*10000000);element=$.getElement(overlay.id);if(!element){element=document.createElement(\"a\");element.href=\"#/overlay/\"+id;}element.id=id;$.addClass(element,overlay.className?overlay.className:\"openseadragon-overlay\");}var location=overlay.location;var width=overlay.width;var height=overlay.height;if(!location){var x=overlay.x;var y=overlay.y;if(overlay.px!==undefined){var rect=viewer.viewport.imageToViewportRectangle(new $.Rect(overlay.px,overlay.py,width||0,height||0));x=rect.x;y=rect.y;width=width!==undefined?rect.width:undefined;height=height!==undefined?rect.height:undefined;}location=new $.Point(x,y);}var placement=overlay.placement;if(placement&&$.type(placement)===\"string\"){placement=$.Placement[overlay.placement.toUpperCase()];}return new $.Overlay({element:element,location:location,placement:placement,onDraw:overlay.onDraw,checkResize:overlay.checkResize,width:width,height:height,rotationMode:overlay.rotationMode});}/**\n * @private\n * @inner\n * Determines the index of the given overlay in the given overlays array.\n */function getOverlayIndex(overlays,element){var i;for(i=overlays.length-1;i>=0;i--){if(overlays[i].element===element){return i;}}return-1;}///////////////////////////////////////////////////////////////////////////////\n// Schedulers provide the general engine for animation\n///////////////////////////////////////////////////////////////////////////////\nfunction scheduleUpdate(viewer,updateFunc){return $.requestAnimationFrame(function(){updateFunc(viewer);});}//provides a sequence in the fade animation\nfunction scheduleControlsFade(viewer){$.requestAnimationFrame(function(){updateControlsFade(viewer);});}//initiates an animation to hide the controls\nfunction beginControlsAutoHide(viewer){if(!viewer.autoHideControls){return;}viewer.controlsShouldFade=true;viewer.controlsFadeBeginTime=$.now()+viewer.controlsFadeDelay;window.setTimeout(function(){scheduleControlsFade(viewer);},viewer.controlsFadeDelay);}//determines if fade animation is done or continues the animation\nfunction updateControlsFade(viewer){var currentTime,deltaTime,opacity,i;if(viewer.controlsShouldFade){currentTime=$.now();deltaTime=currentTime-viewer.controlsFadeBeginTime;opacity=1.0-deltaTime/viewer.controlsFadeLength;opacity=Math.min(1.0,opacity);opacity=Math.max(0.0,opacity);for(i=viewer.controls.length-1;i>=0;i--){if(viewer.controls[i].autoFade){viewer.controls[i].setOpacity(opacity);}}if(opacity>0){// fade again\nscheduleControlsFade(viewer);}}}//stop the fade animation on the controls and show them\nfunction abortControlsAutoHide(viewer){var i;viewer.controlsShouldFade=false;for(i=viewer.controls.length-1;i>=0;i--){viewer.controls[i].setOpacity(1.0);}}///////////////////////////////////////////////////////////////////////////////\n// Default view event handlers.\n///////////////////////////////////////////////////////////////////////////////\nfunction onFocus(){abortControlsAutoHide(this);}function onBlur(){beginControlsAutoHide(this);}function onCanvasKeyDown(event){var canvasKeyDownEventArgs={originalEvent:event.originalEvent,preventDefaultAction:event.preventDefaultAction,preventVerticalPan:event.preventVerticalPan,preventHorizontalPan:event.preventHorizontalPan};/**\n * Raised when a keyboard key is pressed and the focus is on the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-key\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {Boolean} preventDefaultAction - Set to true to prevent default keyboard behaviour. Default: false.\n * @property {Boolean} preventVerticalPan - Set to true to prevent keyboard vertical panning. Default: false.\n * @property {Boolean} preventHorizontalPan - Set to true to prevent keyboard horizontal panning. Default: false.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-key',canvasKeyDownEventArgs);if(!canvasKeyDownEventArgs.preventDefaultAction&&!event.ctrl&&!event.alt&&!event.meta){switch(event.keyCode){case 38://up arrow\nif(!canvasKeyDownEventArgs.preventVerticalPan){if(event.shift){this.viewport.zoomBy(1.1);}else{this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(0,-this.pixelsPerArrowPress)));}this.viewport.applyConstraints();}return false;case 40://down arrow\nif(!canvasKeyDownEventArgs.preventVerticalPan){if(event.shift){this.viewport.zoomBy(0.9);}else{this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(0,this.pixelsPerArrowPress)));}this.viewport.applyConstraints();}return false;case 37://left arrow\nif(!canvasKeyDownEventArgs.preventHorizontalPan){this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(-this.pixelsPerArrowPress,0)));this.viewport.applyConstraints();}return false;case 39://right arrow\nif(!canvasKeyDownEventArgs.preventHorizontalPan){this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(this.pixelsPerArrowPress,0)));this.viewport.applyConstraints();}return false;default://console.log( 'navigator keycode %s', event.keyCode );\nreturn true;}}else{return true;}}function onCanvasKeyPress(event){var canvasKeyPressEventArgs={originalEvent:event.originalEvent,preventDefaultAction:event.preventDefaultAction,preventVerticalPan:event.preventVerticalPan,preventHorizontalPan:event.preventHorizontalPan};// This event is documented in onCanvasKeyDown\nthis.raiseEvent('canvas-key',canvasKeyPressEventArgs);if(!canvasKeyPressEventArgs.preventDefaultAction&&!event.ctrl&&!event.alt&&!event.meta){switch(event.keyCode){case 43://=|+\ncase 61://=|+\nthis.viewport.zoomBy(1.1);this.viewport.applyConstraints();return false;case 45://-|_\nthis.viewport.zoomBy(0.9);this.viewport.applyConstraints();return false;case 48://0|)\nthis.viewport.goHome();this.viewport.applyConstraints();return false;case 119://w\ncase 87://W\nif(!canvasKeyPressEventArgs.preventVerticalPan){if(event.shift){this.viewport.zoomBy(1.1);}else{this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(0,-40)));}this.viewport.applyConstraints();}return false;case 115://s\ncase 83://S\nif(!canvasKeyPressEventArgs.preventVerticalPan){if(event.shift){this.viewport.zoomBy(0.9);}else{this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(0,40)));}this.viewport.applyConstraints();}return false;case 97://a\nif(!canvasKeyPressEventArgs.preventHorizontalPan){this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(-40,0)));this.viewport.applyConstraints();}return false;case 100://d\nif(!canvasKeyPressEventArgs.preventHorizontalPan){this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(40,0)));this.viewport.applyConstraints();}return false;case 114://r - 90 degrees clockwise rotation\nif(this.viewport.flipped){this.viewport.setRotation(this.viewport.degrees-90);}else{this.viewport.setRotation(this.viewport.degrees+90);}this.viewport.applyConstraints();return false;case 82://R - 90 degrees counterclockwise rotation\nif(this.viewport.flipped){this.viewport.setRotation(this.viewport.degrees+90);}else{this.viewport.setRotation(this.viewport.degrees-90);}this.viewport.applyConstraints();return false;case 102://f\nthis.viewport.toggleFlip();return false;default:// console.log( 'navigator keycode %s', event.keyCode );\nreturn true;}}else{return true;}}function onCanvasClick(event){var gestureSettings;var haveKeyboardFocus=document.activeElement==this.canvas;// If we don't have keyboard focus, request it.\nif(!haveKeyboardFocus){this.canvas.focus();}if(this.viewport.flipped){event.position.x=this.viewport.getContainerSize().x-event.position.x;}var canvasClickEventArgs={tracker:event.eventSource,position:event.position,quick:event.quick,shift:event.shift,originalEvent:event.originalEvent,preventDefaultAction:event.preventDefaultAction};/**\n * Raised when a mouse press/release or touch/remove occurs on the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-click\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {Boolean} quick - True only if the clickDistThreshold and clickTimeThreshold are both passed. Useful for differentiating between clicks and drags.\n * @property {Boolean} shift - True if the shift key was pressed during this event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {Boolean} preventDefaultAction - Set to true to prevent default click to zoom behaviour. Default: false.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-click',canvasClickEventArgs);if(!canvasClickEventArgs.preventDefaultAction&&this.viewport&&event.quick){gestureSettings=this.gestureSettingsByDeviceType(event.pointerType);if(gestureSettings.clickToZoom){this.viewport.zoomBy(event.shift?1.0/this.zoomPerClick:this.zoomPerClick,gestureSettings.zoomToRefPoint?this.viewport.pointFromPixel(event.position,true):null);this.viewport.applyConstraints();}}}function onCanvasDblClick(event){var gestureSettings;var canvasDblClickEventArgs={tracker:event.eventSource,position:event.position,shift:event.shift,originalEvent:event.originalEvent,preventDefaultAction:event.preventDefaultAction};/**\n * Raised when a double mouse press/release or touch/remove occurs on the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-double-click\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {Boolean} shift - True if the shift key was pressed during this event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {Boolean} preventDefaultAction - Set to true to prevent default double tap to zoom behaviour. Default: false.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-double-click',canvasDblClickEventArgs);if(!canvasDblClickEventArgs.preventDefaultAction&&this.viewport){gestureSettings=this.gestureSettingsByDeviceType(event.pointerType);if(gestureSettings.dblClickToZoom){this.viewport.zoomBy(event.shift?1.0/this.zoomPerClick:this.zoomPerClick,gestureSettings.zoomToRefPoint?this.viewport.pointFromPixel(event.position,true):null);this.viewport.applyConstraints();}}}function onCanvasDrag(event){var gestureSettings;var canvasDragEventArgs={tracker:event.eventSource,position:event.position,delta:event.delta,speed:event.speed,direction:event.direction,shift:event.shift,originalEvent:event.originalEvent,preventDefaultAction:event.preventDefaultAction};/**\n * Raised when a mouse or touch drag operation occurs on the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-drag\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {OpenSeadragon.Point} delta - The x,y components of the difference between start drag and end drag.\n * @property {Number} speed - Current computed speed, in pixels per second.\n * @property {Number} direction - Current computed direction, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.\n * @property {Boolean} shift - True if the shift key was pressed during this event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {Boolean} preventDefaultAction - Set to true to prevent default drag behaviour. Default: false.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-drag',canvasDragEventArgs);if(!canvasDragEventArgs.preventDefaultAction&&this.viewport){gestureSettings=this.gestureSettingsByDeviceType(event.pointerType);if(!this.panHorizontal){event.delta.x=0;}if(!this.panVertical){event.delta.y=0;}if(this.viewport.flipped){event.delta.x=-event.delta.x;}if(this.constrainDuringPan){var delta=this.viewport.deltaPointsFromPixels(event.delta.negate());this.viewport.centerSpringX.target.value+=delta.x;this.viewport.centerSpringY.target.value+=delta.y;var bounds=this.viewport.getBounds();var constrainedBounds=this.viewport.getConstrainedBounds();this.viewport.centerSpringX.target.value-=delta.x;this.viewport.centerSpringY.target.value-=delta.y;if(bounds.x!=constrainedBounds.x){event.delta.x=0;}if(bounds.y!=constrainedBounds.y){event.delta.y=0;}}this.viewport.panBy(this.viewport.deltaPointsFromPixels(event.delta.negate()),gestureSettings.flickEnabled&&!this.constrainDuringPan);}}function onCanvasDragEnd(event){if(!event.preventDefaultAction&&this.viewport){var gestureSettings=this.gestureSettingsByDeviceType(event.pointerType);if(gestureSettings.flickEnabled&&event.speed>=gestureSettings.flickMinSpeed){var amplitudeX=0;if(this.panHorizontal){amplitudeX=gestureSettings.flickMomentum*event.speed*Math.cos(event.direction);}var amplitudeY=0;if(this.panVertical){amplitudeY=gestureSettings.flickMomentum*event.speed*Math.sin(event.direction);}var center=this.viewport.pixelFromPoint(this.viewport.getCenter(true));var target=this.viewport.pointFromPixel(new $.Point(center.x-amplitudeX,center.y-amplitudeY));this.viewport.panTo(target,false);}this.viewport.applyConstraints();}/**\n * Raised when a mouse or touch drag operation ends on the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-drag-end\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {Number} speed - Speed at the end of a drag gesture, in pixels per second.\n * @property {Number} direction - Direction at the end of a drag gesture, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.\n * @property {Boolean} shift - True if the shift key was pressed during this event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-drag-end',{tracker:event.eventSource,position:event.position,speed:event.speed,direction:event.direction,shift:event.shift,originalEvent:event.originalEvent});}function onCanvasEnter(event){/**\n * Raised when a pointer enters the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-enter\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {String} pointerType - \"mouse\", \"touch\", \"pen\", etc.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {Number} buttons - Current buttons pressed. A combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @property {Number} pointers - Number of pointers (all types) active in the tracked element.\n * @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.\n * @property {Boolean} buttonDownAny - Was the button down anywhere in the screen during the event. Deprecated. Use buttons instead.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-enter',{tracker:event.eventSource,pointerType:event.pointerType,position:event.position,buttons:event.buttons,pointers:event.pointers,insideElementPressed:event.insideElementPressed,buttonDownAny:event.buttonDownAny,originalEvent:event.originalEvent});}function onCanvasExit(event){if(window.location!=window.parent.location){$.MouseTracker.resetAllMouseTrackers();}/**\n * Raised when a pointer leaves the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-exit\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {String} pointerType - \"mouse\", \"touch\", \"pen\", etc.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {Number} buttons - Current buttons pressed. A combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @property {Number} pointers - Number of pointers (all types) active in the tracked element.\n * @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.\n * @property {Boolean} buttonDownAny - Was the button down anywhere in the screen during the event. Deprecated. Use buttons instead.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-exit',{tracker:event.eventSource,pointerType:event.pointerType,position:event.position,buttons:event.buttons,pointers:event.pointers,insideElementPressed:event.insideElementPressed,buttonDownAny:event.buttonDownAny,originalEvent:event.originalEvent});}function onCanvasPress(event){/**\n * Raised when the primary mouse button is pressed or touch starts on the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-press\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {String} pointerType - \"mouse\", \"touch\", \"pen\", etc.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.\n * @property {Boolean} insideElementReleased - True if the cursor still inside the tracked element when the button was released.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-press',{tracker:event.eventSource,pointerType:event.pointerType,position:event.position,insideElementPressed:event.insideElementPressed,insideElementReleased:event.insideElementReleased,originalEvent:event.originalEvent});}function onCanvasRelease(event){/**\n * Raised when the primary mouse button is released or touch ends on the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-release\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {String} pointerType - \"mouse\", \"touch\", \"pen\", etc.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.\n * @property {Boolean} insideElementReleased - True if the cursor still inside the tracked element when the button was released.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-release',{tracker:event.eventSource,pointerType:event.pointerType,position:event.position,insideElementPressed:event.insideElementPressed,insideElementReleased:event.insideElementReleased,originalEvent:event.originalEvent});}function onCanvasNonPrimaryPress(event){/**\n * Raised when any non-primary pointer button is pressed on the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-nonprimary-press\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {String} pointerType - \"mouse\", \"touch\", \"pen\", etc.\n * @property {Number} button - Button which caused the event.\n * -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.\n * @property {Number} buttons - Current buttons pressed.\n * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-nonprimary-press',{tracker:event.eventSource,position:event.position,pointerType:event.pointerType,button:event.button,buttons:event.buttons,originalEvent:event.originalEvent});}function onCanvasNonPrimaryRelease(event){/**\n * Raised when any non-primary pointer button is released on the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-nonprimary-release\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {String} pointerType - \"mouse\", \"touch\", \"pen\", etc.\n * @property {Number} button - Button which caused the event.\n * -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.\n * @property {Number} buttons - Current buttons pressed.\n * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-nonprimary-release',{tracker:event.eventSource,position:event.position,pointerType:event.pointerType,button:event.button,buttons:event.buttons,originalEvent:event.originalEvent});}function onCanvasPinch(event){var gestureSettings,centerPt,lastCenterPt,panByPt;if(!event.preventDefaultAction&&this.viewport){gestureSettings=this.gestureSettingsByDeviceType(event.pointerType);if(gestureSettings.pinchToZoom){centerPt=this.viewport.pointFromPixel(event.center,true);lastCenterPt=this.viewport.pointFromPixel(event.lastCenter,true);panByPt=lastCenterPt.minus(centerPt);if(!this.panHorizontal){panByPt.x=0;}if(!this.panVertical){panByPt.y=0;}this.viewport.zoomBy(event.distance/event.lastDistance,centerPt,true);if(gestureSettings.zoomToRefPoint){this.viewport.panBy(panByPt,true);}this.viewport.applyConstraints();}if(gestureSettings.pinchRotate){// Pinch rotate\nvar angle1=Math.atan2(event.gesturePoints[0].currentPos.y-event.gesturePoints[1].currentPos.y,event.gesturePoints[0].currentPos.x-event.gesturePoints[1].currentPos.x);var angle2=Math.atan2(event.gesturePoints[0].lastPos.y-event.gesturePoints[1].lastPos.y,event.gesturePoints[0].lastPos.x-event.gesturePoints[1].lastPos.x);this.viewport.setRotation(this.viewport.getRotation()+(angle1-angle2)*(180/Math.PI));}}/**\n * Raised when a pinch event occurs on the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-pinch\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {Array.} gesturePoints - Gesture points associated with the gesture. Velocity data can be found here.\n * @property {OpenSeadragon.Point} lastCenter - The previous center point of the two pinch contact points relative to the tracked element.\n * @property {OpenSeadragon.Point} center - The center point of the two pinch contact points relative to the tracked element.\n * @property {Number} lastDistance - The previous distance between the two pinch contact points in CSS pixels.\n * @property {Number} distance - The distance between the two pinch contact points in CSS pixels.\n * @property {Boolean} shift - True if the shift key was pressed during this event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-pinch',{tracker:event.eventSource,gesturePoints:event.gesturePoints,lastCenter:event.lastCenter,center:event.center,lastDistance:event.lastDistance,distance:event.distance,shift:event.shift,originalEvent:event.originalEvent});//cancels event\nreturn false;}function onCanvasScroll(event){var gestureSettings,factor,thisScrollTime,deltaScrollTime;/* Certain scroll devices fire the scroll event way too fast so we are injecting a simple adjustment to keep things\n * partially normalized. If we have already fired an event within the last 'minScrollDelta' milliseconds we skip\n * this one and wait for the next event. */thisScrollTime=$.now();deltaScrollTime=thisScrollTime-this._lastScrollTime;if(deltaScrollTime>this.minScrollDeltaTime){this._lastScrollTime=thisScrollTime;if(this.viewport.flipped){event.position.x=this.viewport.getContainerSize().x-event.position.x;}if(!event.preventDefaultAction&&this.viewport){gestureSettings=this.gestureSettingsByDeviceType(event.pointerType);if(gestureSettings.scrollToZoom){factor=Math.pow(this.zoomPerScroll,event.scroll);this.viewport.zoomBy(factor,gestureSettings.zoomToRefPoint?this.viewport.pointFromPixel(event.position,true):null);this.viewport.applyConstraints();}}/**\n * Raised when a scroll event occurs on the {@link OpenSeadragon.Viewer#canvas} element (mouse wheel).\n *\n * @event canvas-scroll\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {Number} scroll - The scroll delta for the event.\n * @property {Boolean} shift - True if the shift key was pressed during this event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-scroll',{tracker:event.eventSource,position:event.position,scroll:event.scroll,shift:event.shift,originalEvent:event.originalEvent});if(gestureSettings&&gestureSettings.scrollToZoom){//cancels event\nreturn false;}}else{gestureSettings=this.gestureSettingsByDeviceType(event.pointerType);if(gestureSettings&&gestureSettings.scrollToZoom){return false;// We are swallowing this event\n}}}function onContainerEnter(event){THIS[this.hash].mouseInside=true;abortControlsAutoHide(this);/**\n * Raised when the cursor enters the {@link OpenSeadragon.Viewer#container} element.\n *\n * @event container-enter\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {Number} buttons - Current buttons pressed. A combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @property {Number} pointers - Number of pointers (all types) active in the tracked element.\n * @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.\n * @property {Boolean} buttonDownAny - Was the button down anywhere in the screen during the event. Deprecated. Use buttons instead.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('container-enter',{tracker:event.eventSource,position:event.position,buttons:event.buttons,pointers:event.pointers,insideElementPressed:event.insideElementPressed,buttonDownAny:event.buttonDownAny,originalEvent:event.originalEvent});}function onContainerExit(event){if(event.pointers<1){THIS[this.hash].mouseInside=false;if(!THIS[this.hash].animating){beginControlsAutoHide(this);}}/**\n * Raised when the cursor leaves the {@link OpenSeadragon.Viewer#container} element.\n *\n * @event container-exit\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {Number} buttons - Current buttons pressed. A combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @property {Number} pointers - Number of pointers (all types) active in the tracked element.\n * @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.\n * @property {Boolean} buttonDownAny - Was the button down anywhere in the screen during the event. Deprecated. Use buttons instead.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('container-exit',{tracker:event.eventSource,position:event.position,buttons:event.buttons,pointers:event.pointers,insideElementPressed:event.insideElementPressed,buttonDownAny:event.buttonDownAny,originalEvent:event.originalEvent});}///////////////////////////////////////////////////////////////////////////////\n// Page update routines ( aka Views - for future reference )\n///////////////////////////////////////////////////////////////////////////////\nfunction updateMulti(viewer){updateOnce(viewer);// Request the next frame, unless we've been closed\nif(viewer.isOpen()){viewer._updateRequestId=scheduleUpdate(viewer,updateMulti);}else{viewer._updateRequestId=false;}}function updateOnce(viewer){//viewer.profiler.beginUpdate();\nif(viewer._opening){return;}if(viewer.autoResize){var containerSize=_getSafeElemSize(viewer.container);var prevContainerSize=THIS[viewer.hash].prevContainerSize;if(!containerSize.equals(prevContainerSize)){var viewport=viewer.viewport;if(viewer.preserveImageSizeOnResize){var resizeRatio=prevContainerSize.x/containerSize.x;var zoom=viewport.getZoom()*resizeRatio;var center=viewport.getCenter();viewport.resize(containerSize,false);viewport.zoomTo(zoom,null,true);viewport.panTo(center,true);}else{// maintain image position\nvar oldBounds=viewport.getBounds();viewport.resize(containerSize,true);viewport.fitBoundsWithConstraints(oldBounds,true);}THIS[viewer.hash].prevContainerSize=containerSize;THIS[viewer.hash].forceRedraw=true;}}var viewportChange=viewer.viewport.update();var animated=viewer.world.update()||viewportChange;if(viewportChange){/**\n * Raised when any spring animation update occurs (zoom, pan, etc.),\n * before the viewer has drawn the new location.\n *\n * @event viewport-change\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */viewer.raiseEvent('viewport-change');}if(viewer.referenceStrip){animated=viewer.referenceStrip.update(viewer.viewport)||animated;}if(!THIS[viewer.hash].animating&&animated){/**\n * Raised when any spring animation starts (zoom, pan, etc.).\n *\n * @event animation-start\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */viewer.raiseEvent(\"animation-start\");abortControlsAutoHide(viewer);}if(animated||THIS[viewer.hash].forceRedraw||viewer.world.needsDraw()){drawWorld(viewer);viewer._drawOverlays();if(viewer.navigator){viewer.navigator.update(viewer.viewport);}THIS[viewer.hash].forceRedraw=false;if(animated){/**\n * Raised when any spring animation update occurs (zoom, pan, etc.),\n * after the viewer has drawn the new location.\n *\n * @event animation\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */viewer.raiseEvent(\"animation\");}}if(THIS[viewer.hash].animating&&!animated){/**\n * Raised when any spring animation ends (zoom, pan, etc.).\n *\n * @event animation-finish\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */viewer.raiseEvent(\"animation-finish\");if(!THIS[viewer.hash].mouseInside){beginControlsAutoHide(viewer);}}THIS[viewer.hash].animating=animated;//viewer.profiler.endUpdate();\n}function drawWorld(viewer){viewer.imageLoader.clear();viewer.drawer.clear();viewer.world.draw();/**\n * - Needs documentation -\n *\n * @event update-viewport\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */viewer.raiseEvent('update-viewport',{});}///////////////////////////////////////////////////////////////////////////////\n// Navigation Controls\n///////////////////////////////////////////////////////////////////////////////\nfunction resolveUrl(prefix,url){return prefix?prefix+url:url;}function beginZoomingIn(){THIS[this.hash].lastZoomTime=$.now();THIS[this.hash].zoomFactor=this.zoomPerSecond;THIS[this.hash].zooming=true;scheduleZoom(this);}function beginZoomingOut(){THIS[this.hash].lastZoomTime=$.now();THIS[this.hash].zoomFactor=1.0/this.zoomPerSecond;THIS[this.hash].zooming=true;scheduleZoom(this);}function endZooming(){THIS[this.hash].zooming=false;}function scheduleZoom(viewer){$.requestAnimationFrame($.delegate(viewer,doZoom));}function doZoom(){var currentTime,deltaTime,adjustedFactor;if(THIS[this.hash].zooming&&this.viewport){currentTime=$.now();deltaTime=currentTime-THIS[this.hash].lastZoomTime;adjustedFactor=Math.pow(THIS[this.hash].zoomFactor,deltaTime/1000);this.viewport.zoomBy(adjustedFactor);this.viewport.applyConstraints();THIS[this.hash].lastZoomTime=currentTime;scheduleZoom(this);}}function doSingleZoomIn(){if(this.viewport){THIS[this.hash].zooming=false;this.viewport.zoomBy(this.zoomPerClick/1.0);this.viewport.applyConstraints();}}function doSingleZoomOut(){if(this.viewport){THIS[this.hash].zooming=false;this.viewport.zoomBy(1.0/this.zoomPerClick);this.viewport.applyConstraints();}}function lightUp(){this.buttons.emulateEnter();this.buttons.emulateExit();}function onHome(){if(this.viewport){this.viewport.goHome();}}function onFullScreen(){if(this.isFullPage()&&!$.isFullScreen()){// Is fullPage but not fullScreen\nthis.setFullPage(false);}else{this.setFullScreen(!this.isFullPage());}// correct for no mouseout event on change\nif(this.buttons){this.buttons.emulateExit();}this.fullPageButton.element.focus();if(this.viewport){this.viewport.applyConstraints();}}/**\n * Note: The current rotation feature is limited to 90 degree turns.\n */function onRotateLeft(){if(this.viewport){var currRotation=this.viewport.getRotation();if(this.viewport.flipped){currRotation=$.positiveModulo(currRotation+90,360);}else{currRotation=$.positiveModulo(currRotation-90,360);}this.viewport.setRotation(currRotation);}}/**\n * Note: The current rotation feature is limited to 90 degree turns.\n */function onRotateRight(){if(this.viewport){var currRotation=this.viewport.getRotation();if(this.viewport.flipped){currRotation=$.positiveModulo(currRotation-90,360);}else{currRotation=$.positiveModulo(currRotation+90,360);}this.viewport.setRotation(currRotation);}}/**\n * Note: When pressed flip control button\n */function onFlip(){this.viewport.toggleFlip();}function onPrevious(){var previous=this._sequenceIndex-1;if(this.navPrevNextWrap&&previous<0){previous+=this.tileSources.length;}this.goToPage(previous);}function onNext(){var next=this._sequenceIndex+1;if(this.navPrevNextWrap&&next>=this.tileSources.length){next=0;}this.goToPage(next);}})(OpenSeadragon);/*\n * OpenSeadragon - Navigator\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * @class Navigator\n * @classdesc The Navigator provides a small view of the current image as fixed\n * while representing the viewport as a moving box serving as a frame\n * of reference in the larger viewport as to which portion of the image\n * is currently being examined. The navigator's viewport can be interacted\n * with using the keyboard or the mouse.\n *\n * @memberof OpenSeadragon\n * @extends OpenSeadragon.Viewer\n * @extends OpenSeadragon.EventSource\n * @param {Object} options\n */$.Navigator=function(options){var viewer=options.viewer,_this=this,viewerSize,navigatorSize;//We may need to create a new element and id if they did not\n//provide the id for the existing element\nif(!options.id){options.id='navigator-'+$.now();this.element=$.makeNeutralElement(\"div\");options.controlOptions={anchor:$.ControlAnchor.TOP_RIGHT,attachToViewer:true,autoFade:options.autoFade};if(options.position){if('BOTTOM_RIGHT'==options.position){options.controlOptions.anchor=$.ControlAnchor.BOTTOM_RIGHT;}else if('BOTTOM_LEFT'==options.position){options.controlOptions.anchor=$.ControlAnchor.BOTTOM_LEFT;}else if('TOP_RIGHT'==options.position){options.controlOptions.anchor=$.ControlAnchor.TOP_RIGHT;}else if('TOP_LEFT'==options.position){options.controlOptions.anchor=$.ControlAnchor.TOP_LEFT;}else if('ABSOLUTE'==options.position){options.controlOptions.anchor=$.ControlAnchor.ABSOLUTE;options.controlOptions.top=options.top;options.controlOptions.left=options.left;options.controlOptions.height=options.height;options.controlOptions.width=options.width;}}}else{this.element=document.getElementById(options.id);options.controlOptions={anchor:$.ControlAnchor.NONE,attachToViewer:false,autoFade:false};}this.element.id=options.id;this.element.className+=' navigator';options=$.extend(true,{sizeRatio:$.DEFAULT_SETTINGS.navigatorSizeRatio},options,{element:this.element,tabIndex:-1,// No keyboard navigation, omit from tab order\n//These need to be overridden to prevent recursion since\n//the navigator is a viewer and a viewer has a navigator\nshowNavigator:false,mouseNavEnabled:false,showNavigationControl:false,showSequenceControl:false,immediateRender:true,blendTime:0,animationTime:0,autoResize:options.autoResize,// prevent resizing the navigator from adding unwanted space around the image\nminZoomImageRatio:1.0});options.minPixelRatio=this.minPixelRatio=viewer.minPixelRatio;$.setElementTouchActionNone(this.element);this.borderWidth=2;//At some browser magnification levels the display regions lines up correctly, but at some there appears to\n//be a one pixel gap.\nthis.fudge=new $.Point(1,1);this.totalBorderWidths=new $.Point(this.borderWidth*2,this.borderWidth*2).minus(this.fudge);if(options.controlOptions.anchor!=$.ControlAnchor.NONE){(function(style,borderWidth){style.margin='0px';style.border=borderWidth+'px solid #555';style.padding='0px';style.background='#000';style.opacity=0.8;style.overflow='hidden';})(this.element.style,this.borderWidth);}this.displayRegion=$.makeNeutralElement(\"div\");this.displayRegion.id=this.element.id+'-displayregion';this.displayRegion.className='displayregion';(function(style,borderWidth){style.position='relative';style.top='0px';style.left='0px';style.fontSize='0px';style.overflow='hidden';style.border=borderWidth+'px solid #900';style.margin='0px';style.padding='0px';//TODO: IE doesnt like this property being set\n//try{ style.outline = '2px auto #909'; }catch(e){/*ignore*/}\nstyle.background='transparent';// We use square bracket notation on the statement below, because float is a keyword.\n// This is important for the Google Closure compiler, if nothing else.\n/*jshint sub:true */style['float']='left';//Webkit\nstyle.cssFloat='left';//Firefox\nstyle.styleFloat='left';//IE\nstyle.zIndex=999999999;style.cursor='default';})(this.displayRegion.style,this.borderWidth);this.displayRegionContainer=$.makeNeutralElement(\"div\");this.displayRegionContainer.id=this.element.id+'-displayregioncontainer';this.displayRegionContainer.className=\"displayregioncontainer\";this.displayRegionContainer.style.width=\"100%\";this.displayRegionContainer.style.height=\"100%\";viewer.addControl(this.element,options.controlOptions);this._resizeWithViewer=options.controlOptions.anchor!=$.ControlAnchor.ABSOLUTE&&options.controlOptions.anchor!=$.ControlAnchor.NONE;if(this._resizeWithViewer){if(options.width&&options.height){this.element.style.height=typeof options.height==\"number\"?options.height+'px':options.height;this.element.style.width=typeof options.width==\"number\"?options.width+'px':options.width;}else{viewerSize=$.getElementSize(viewer.element);this.element.style.height=Math.round(viewerSize.y*options.sizeRatio)+'px';this.element.style.width=Math.round(viewerSize.x*options.sizeRatio)+'px';this.oldViewerSize=viewerSize;}navigatorSize=$.getElementSize(this.element);this.elementArea=navigatorSize.x*navigatorSize.y;}this.oldContainerSize=new $.Point(0,0);$.Viewer.apply(this,[options]);this.displayRegionContainer.appendChild(this.displayRegion);this.element.getElementsByTagName('div')[0].appendChild(this.displayRegionContainer);function rotate(degrees){_setTransformRotate(_this.displayRegionContainer,degrees);_setTransformRotate(_this.displayRegion,-degrees);_this.viewport.setRotation(degrees);}if(options.navigatorRotate){var degrees=options.viewer.viewport?options.viewer.viewport.getRotation():options.viewer.degrees||0;rotate(degrees);options.viewer.addHandler(\"rotate\",function(args){rotate(args.degrees);});}// Remove the base class' (Viewer's) innerTracker and replace it with our own\nthis.innerTracker.destroy();this.innerTracker=new $.MouseTracker({element:this.element,dragHandler:$.delegate(this,onCanvasDrag),clickHandler:$.delegate(this,onCanvasClick),releaseHandler:$.delegate(this,onCanvasRelease),scrollHandler:$.delegate(this,onCanvasScroll)});this.addHandler(\"reset-size\",function(){if(_this.viewport){_this.viewport.goHome(true);}});viewer.world.addHandler(\"item-index-change\",function(event){window.setTimeout(function(){var item=_this.world.getItemAt(event.previousIndex);_this.world.setItemIndex(item,event.newIndex);},1);});viewer.world.addHandler(\"remove-item\",function(event){var theirItem=event.item;var myItem=_this._getMatchingItem(theirItem);if(myItem){_this.world.removeItem(myItem);}});this.update(viewer.viewport);};$.extend($.Navigator.prototype,$.EventSource.prototype,$.Viewer.prototype,/** @lends OpenSeadragon.Navigator.prototype */{/**\n * Used to notify the navigator when its size has changed.\n * Especially useful when {@link OpenSeadragon.Options}.navigatorAutoResize is set to false and the navigator is resizable.\n * @function\n */updateSize:function updateSize(){if(this.viewport){var containerSize=new $.Point(this.container.clientWidth===0?1:this.container.clientWidth,this.container.clientHeight===0?1:this.container.clientHeight);if(!containerSize.equals(this.oldContainerSize)){this.viewport.resize(containerSize,true);this.viewport.goHome(true);this.oldContainerSize=containerSize;this.drawer.clear();this.world.draw();}}},/**\n /* Flip navigator element\n * @param {Boolean} state - Flip state to set.\n */setFlip:function setFlip(state){this.viewport.setFlip(state);this.setDisplayTransform(this.viewer.viewport.getFlip()?\"scale(-1,1)\":\"scale(1,1)\");return this;},setDisplayTransform:function setDisplayTransform(rule){setElementTransform(this.displayRegion,rule);setElementTransform(this.canvas,rule);setElementTransform(this.element,rule);},/**\n * Used to update the navigator minimap's viewport rectangle when a change in the viewer's viewport occurs.\n * @function\n * @param {OpenSeadragon.Viewport} The viewport this navigator is tracking.\n */update:function update(viewport){var viewerSize,newWidth,newHeight,bounds,topleft,bottomright;viewerSize=$.getElementSize(this.viewer.element);if(this._resizeWithViewer&&viewerSize.x&&viewerSize.y&&!viewerSize.equals(this.oldViewerSize)){this.oldViewerSize=viewerSize;if(this.maintainSizeRatio||!this.elementArea){newWidth=viewerSize.x*this.sizeRatio;newHeight=viewerSize.y*this.sizeRatio;}else{newWidth=Math.sqrt(this.elementArea*(viewerSize.x/viewerSize.y));newHeight=this.elementArea/newWidth;}this.element.style.width=Math.round(newWidth)+'px';this.element.style.height=Math.round(newHeight)+'px';if(!this.elementArea){this.elementArea=newWidth*newHeight;}this.updateSize();}if(viewport&&this.viewport){bounds=viewport.getBoundsNoRotate(true);topleft=this.viewport.pixelFromPointNoRotate(bounds.getTopLeft(),false);bottomright=this.viewport.pixelFromPointNoRotate(bounds.getBottomRight(),false).minus(this.totalBorderWidths);//update style for navigator-box\nvar style=this.displayRegion.style;style.display=this.world.getItemCount()?'block':'none';style.top=Math.round(topleft.y)+'px';style.left=Math.round(topleft.x)+'px';var width=Math.abs(topleft.x-bottomright.x);var height=Math.abs(topleft.y-bottomright.y);// make sure width and height are non-negative so IE doesn't throw\nstyle.width=Math.round(Math.max(width,0))+'px';style.height=Math.round(Math.max(height,0))+'px';}},// overrides Viewer.addTiledImage\naddTiledImage:function addTiledImage(options){var _this=this;var original=options.originalTiledImage;delete options.original;var optionsClone=$.extend({},options,{success:function success(event){var myItem=event.item;myItem._originalForNavigator=original;_this._matchBounds(myItem,original,true);function matchBounds(){_this._matchBounds(myItem,original);}function matchOpacity(){_this._matchOpacity(myItem,original);}function matchCompositeOperation(){_this._matchCompositeOperation(myItem,original);}original.addHandler('bounds-change',matchBounds);original.addHandler('clip-change',matchBounds);original.addHandler('opacity-change',matchOpacity);original.addHandler('composite-operation-change',matchCompositeOperation);}});return $.Viewer.prototype.addTiledImage.apply(this,[optionsClone]);},// private\n_getMatchingItem:function _getMatchingItem(theirItem){var count=this.world.getItemCount();var item;for(var i=0;i\n * By default the image pyramid is split into N layers where the image's longest\n * side in M (in pixels), where N is the smallest integer which satisfies\n * 2^(N+1) >= M.\n *\n * @memberof OpenSeadragon\n * @extends OpenSeadragon.EventSource\n * @param {Object} options\n * You can either specify a URL, or literally define the TileSource (by specifying\n * width, height, tileSize, tileOverlap, minLevel, and maxLevel). For the former,\n * the extending class is expected to implement 'getImageInfo' and 'configure'.\n * For the latter, the construction is assumed to occur through\n * the extending classes implementation of 'configure'.\n * @param {String} [options.url]\n * The URL for the data necessary for this TileSource.\n * @param {String} [options.referenceStripThumbnailUrl]\n * The URL for a thumbnail image to be used by the reference strip\n * @param {Function} [options.success]\n * A function to be called upon successful creation.\n * @param {Boolean} [options.ajaxWithCredentials]\n * If this TileSource needs to make an AJAX call, this specifies whether to set\n * the XHR's withCredentials (for accessing secure data).\n * @param {Object} [options.ajaxHeaders]\n * A set of headers to include in AJAX requests.\n * @param {Number} [options.width]\n * Width of the source image at max resolution in pixels.\n * @param {Number} [options.height]\n * Height of the source image at max resolution in pixels.\n * @param {Number} [options.tileSize]\n * The size of the tiles to assumed to make up each pyramid layer in pixels.\n * Tile size determines the point at which the image pyramid must be\n * divided into a matrix of smaller images.\n * Use options.tileWidth and options.tileHeight to support non-square tiles.\n * @param {Number} [options.tileWidth]\n * The width of the tiles to assumed to make up each pyramid layer in pixels.\n * @param {Number} [options.tileHeight]\n * The height of the tiles to assumed to make up each pyramid layer in pixels.\n * @param {Number} [options.tileOverlap]\n * The number of pixels each tile is expected to overlap touching tiles.\n * @param {Number} [options.minLevel]\n * The minimum level to attempt to load.\n * @param {Number} [options.maxLevel]\n * The maximum level to attempt to load.\n */$.TileSource=function(width,height,tileSize,tileOverlap,minLevel,maxLevel){var _this=this;var args=arguments,options,i;if($.isPlainObject(width)){options=width;}else{options={width:args[0],height:args[1],tileSize:args[2],tileOverlap:args[3],minLevel:args[4],maxLevel:args[5]};}//Tile sources supply some events, namely 'ready' when they must be configured\n//by asynchronously fetching their configuration data.\n$.EventSource.call(this);//we allow options to override anything we dont treat as\n//required via idiomatic options or which is functionally\n//set depending on the state of the readiness of this tile\n//source\n$.extend(true,this,options);if(!this.success){//Any functions that are passed as arguments are bound to the ready callback\nfor(i=0;i1||tiles.y>1){break;}}return i-1;},/**\n * @function\n * @param {Number} level\n * @param {OpenSeadragon.Point} point\n */getTileAtPoint:function getTileAtPoint(level,point){var validPoint=point.x>=0&&point.x<=1&&point.y>=0&&point.y<=1/this.aspectRatio;$.console.assert(validPoint,\"[TileSource.getTileAtPoint] must be called with a valid point.\");var widthScaled=this.dimensions.x*this.getLevelScale(level);var pixelX=point.x*widthScaled;var pixelY=point.y*widthScaled;var x=Math.floor(pixelX/this.getTileWidth(level));var y=Math.floor(pixelY/this.getTileHeight(level));// When point.x == 1 or point.y == 1 / this.aspectRatio we want to\n// return the last tile of the row/column\nif(point.x>=1){x=this.getNumTiles(level).x-1;}var EPSILON=1e-15;if(point.y>=1/this.aspectRatio-EPSILON){y=this.getNumTiles(level).y-1;}return new $.Point(x,y);},/**\n * @function\n * @param {Number} level\n * @param {Number} x\n * @param {Number} y\n * @param {Boolean} [isSource=false] Whether to return the source bounds of the tile.\n * @returns {OpenSeadragon.Rect} Either where this tile fits (in normalized coordinates) or the\n * portion of the tile to use as the source of the drawing operation (in pixels), depending on\n * the isSource parameter.\n */getTileBounds:function getTileBounds(level,x,y,isSource){var dimensionsScaled=this.dimensions.times(this.getLevelScale(level)),tileWidth=this.getTileWidth(level),tileHeight=this.getTileHeight(level),px=x===0?0:tileWidth*x-this.tileOverlap,py=y===0?0:tileHeight*y-this.tileOverlap,sx=tileWidth+(x===0?1:2)*this.tileOverlap,sy=tileHeight+(y===0?1:2)*this.tileOverlap,scale=1.0/dimensionsScaled.x;sx=Math.min(sx,dimensionsScaled.x-px);sy=Math.min(sy,dimensionsScaled.y-py);if(isSource){return new $.Rect(0,0,sx,sy);}return new $.Rect(px*scale,py*scale,sx*scale,sy*scale);},/**\n * Responsible for retrieving, and caching the\n * image metadata pertinent to this TileSources implementation.\n * @function\n * @param {String} url\n * @throws {Error}\n */getImageInfo:function getImageInfo(url){var _this=this,callbackName,callback,readySource,options,urlParts,filename,lastDot;if(url){urlParts=url.split('/');filename=urlParts[urlParts.length-1];lastDot=filename.lastIndexOf('.');if(lastDot>-1){urlParts[urlParts.length-1]=filename.slice(0,lastDot);}}callback=function callback(data){if(typeof data===\"string\"){data=$.parseXml(data);}var $TileSource=$.TileSource.determineType(_this,data,url);if(!$TileSource){/**\n * Raised when an error occurs loading a TileSource.\n *\n * @event open-failed\n * @memberof OpenSeadragon.TileSource\n * @type {object}\n * @property {OpenSeadragon.TileSource} eventSource - A reference to the TileSource which raised the event.\n * @property {String} message\n * @property {String} source\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent('open-failed',{message:\"Unable to load TileSource\",source:url});return;}options=$TileSource.prototype.configure.apply(_this,[data,url]);if(options.ajaxWithCredentials===undefined){options.ajaxWithCredentials=_this.ajaxWithCredentials;}readySource=new $TileSource(options);_this.ready=true;/**\n * Raised when a TileSource is opened and initialized.\n *\n * @event ready\n * @memberof OpenSeadragon.TileSource\n * @type {object}\n * @property {OpenSeadragon.TileSource} eventSource - A reference to the TileSource which raised the event.\n * @property {Object} tileSource\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent('ready',{tileSource:readySource});};if(url.match(/\\.js$/)){//TODO: Its not very flexible to require tile sources to end jsonp\n// request for info with a url that ends with '.js' but for\n// now it's the only way I see to distinguish uniformly.\ncallbackName=url.split('/').pop().replace('.js','');$.jsonp({url:url,async:false,callbackName:callbackName,callback:callback});}else{// request info via xhr asynchronously.\n$.makeAjaxRequest({url:url,withCredentials:this.ajaxWithCredentials,headers:this.ajaxHeaders,success:function success(xhr){var data=processResponse(xhr);callback(data);},error:function error(xhr,exc){var msg;/*\n IE < 10 will block XHR requests to different origins. Any property access on the request\n object will raise an exception which we'll attempt to handle by formatting the original\n exception rather than the second one raised when we try to access xhr.status\n */try{msg=\"HTTP \"+xhr.status+\" attempting to load TileSource\";}catch(e){var formattedExc;if(typeof exc==\"undefined\"||!exc.toString){formattedExc=\"Unknown error\";}else{formattedExc=exc.toString();}msg=formattedExc+\" attempting to load TileSource\";}/***\n * Raised when an error occurs loading a TileSource.\n *\n * @event open-failed\n * @memberof OpenSeadragon.TileSource\n * @type {object}\n * @property {OpenSeadragon.TileSource} eventSource - A reference to the TileSource which raised the event.\n * @property {String} message\n * @property {String} source\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent('open-failed',{message:msg,source:url});}});}},/**\n * Responsible determining if a the particular TileSource supports the\n * data format ( and allowed to apply logic against the url the data was\n * loaded from, if any ). Overriding implementations are expected to do\n * something smart with data and / or url to determine support. Also\n * understand that iteration order of TileSources is not guarunteed so\n * please make sure your data or url is expressive enough to ensure a simple\n * and sufficient mechanisim for clear determination.\n * @function\n * @param {String|Object|Array|Document} data\n * @param {String} url - the url the data was loaded\n * from if any.\n * @return {Boolean}\n */supports:function supports(data,url){return false;},/**\n * Responsible for parsing and configuring the\n * image metadata pertinent to this TileSources implementation.\n * This method is not implemented by this class other than to throw an Error\n * announcing you have to implement it. Because of the variety of tile\n * server technologies, and various specifications for building image\n * pyramids, this method is here to allow easy integration.\n * @function\n * @param {String|Object|Array|Document} data\n * @param {String} url - the url the data was loaded\n * from if any.\n * @return {Object} options - A dictionary of keyword arguments sufficient\n * to configure this tile sources constructor.\n * @throws {Error}\n */configure:function configure(data,url){throw new Error(\"Method not implemented.\");},/**\n * Responsible for retrieving the url which will return an image for the\n * region specified by the given x, y, and level components.\n * This method is not implemented by this class other than to throw an Error\n * announcing you have to implement it. Because of the variety of tile\n * server technologies, and various specifications for building image\n * pyramids, this method is here to allow easy integration.\n * @function\n * @param {Number} level\n * @param {Number} x\n * @param {Number} y\n * @throws {Error}\n */getTileUrl:function getTileUrl(level,x,y){throw new Error(\"Method not implemented.\");},/**\n * Responsible for retrieving the headers which will be attached to the image request for the\n * region specified by the given x, y, and level components.\n * This option is only relevant if {@link OpenSeadragon.Options}.loadTilesWithAjax is set to true.\n * The headers returned here will override headers specified at the Viewer or TiledImage level.\n * Specifying a falsy value for a header will clear its existing value set at the Viewer or\n * TiledImage level (if any).\n * @function\n * @param {Number} level\n * @param {Number} x\n * @param {Number} y\n * @returns {Object}\n */getTileAjaxHeaders:function getTileAjaxHeaders(level,x,y){return{};},/**\n * @function\n * @param {Number} level\n * @param {Number} x\n * @param {Number} y\n */tileExists:function tileExists(level,x,y){var numTiles=this.getNumTiles(level);return level>=this.minLevel&&level<=this.maxLevel&&x>=0&&y>=0&&x=0;i--){rect=this.displayRects[i];for(level=rect.minLevel;level<=rect.maxLevel;level++){if(!this._levelRects[level]){this._levelRects[level]=[];}this._levelRects[level].push(rect);}}}$.TileSource.apply(this,[options]);};$.extend($.DziTileSource.prototype,$.TileSource.prototype,/** @lends OpenSeadragon.DziTileSource.prototype */{/**\n * Determine if the data and/or url imply the image service is supported by\n * this tile source.\n * @function\n * @param {Object|Array} data\n * @param {String} optional - url\n */supports:function supports(data,url){var ns;if(data.Image){ns=data.Image.xmlns;}else if(data.documentElement){if(\"Image\"==data.documentElement.localName||\"Image\"==data.documentElement.tagName){ns=data.documentElement.namespaceURI;}}ns=(ns||'').toLowerCase();return ns.indexOf('schemas.microsoft.com/deepzoom/2008')!==-1||ns.indexOf('schemas.microsoft.com/deepzoom/2009')!==-1;},/**\n *\n * @function\n * @param {Object|XMLDocument} data - the raw configuration\n * @param {String} url - the url the data was retreived from if any.\n * @return {Object} options - A dictionary of keyword arguments sufficient\n * to configure this tile sources constructor.\n */configure:function configure(data,url){var options;if(!$.isPlainObject(data)){options=configureFromXML(this,data);}else{options=configureFromObject(this,data);}if(url&&!options.tilesUrl){options.tilesUrl=url.replace(/([^\\/]+?)(\\.(dzi|xml|js)?(\\?[^\\/]*)?)?\\/?$/,'$1_files/');if(url.search(/\\.(dzi|xml|js)\\?/)!=-1){options.queryParams=url.match(/\\?.*/);}else{options.queryParams='';}}return options;},/**\n * @function\n * @param {Number} level\n * @param {Number} x\n * @param {Number} y\n */getTileUrl:function getTileUrl(level,x,y){return[this.tilesUrl,level,'/',x,'_',y,'.',this.fileFormat,this.queryParams].join('');},/**\n * @function\n * @param {Number} level\n * @param {Number} x\n * @param {Number} y\n */tileExists:function tileExists(level,x,y){var rects=this._levelRects[level],rect,scale,xMin,yMin,xMax,yMax,i;if(!rects||!rects.length){return true;}for(i=rects.length-1;i>=0;i--){rect=rects[i];if(levelrect.maxLevel){continue;}scale=this.getLevelScale(level);xMin=rect.x*scale;yMin=rect.y*scale;xMax=xMin+rect.width*scale;yMax=yMin+rect.height*scale;xMin=Math.floor(xMin/this._tileWidth);yMin=Math.floor(yMin/this._tileWidth);// DZI tiles are square, so we just use _tileWidth\nxMax=Math.ceil(xMax/this._tileWidth);yMax=Math.ceil(yMax/this._tileWidth);if(xMin<=x&&x0){options.tileSize=Math.max.apply(null,smallerTiles);}else{// If we're smaller than 256, just use the short side.\noptions.tileSize=shortDim;}}else if(this.sizes&&this.sizes.length>0){// This info.json can't be tiled, but we can still construct a legacy pyramid from the sizes array.\n// In this mode, IIIFTileSource will call functions from the abstract baseTileSource or the\n// LegacyTileSource instead of performing IIIF tiling.\nthis.emulateLegacyImagePyramid=true;options.levels=constructLevels(this);// use the largest available size to define tiles\n$.extend(true,options,{width:options.levels[options.levels.length-1].width,height:options.levels[options.levels.length-1].height,tileSize:Math.max(options.height,options.width),tileOverlap:0,minLevel:0,maxLevel:options.levels.length-1});this.levels=options.levels;}else{$.console.error(\"Nothing in the info.json to construct image pyramids from\");}if(!options.maxLevel&&!this.emulateLegacyImagePyramid){if(!this.scale_factors){options.maxLevel=Number(Math.ceil(Math.log(Math.max(this.width,this.height),2)));}else{var maxScaleFactor=Math.max.apply(null,this.scale_factors);options.maxLevel=Math.round(Math.log(maxScaleFactor)*Math.LOG2E);}}$.TileSource.apply(this,[options]);};$.extend($.IIIFTileSource.prototype,$.TileSource.prototype,/** @lends OpenSeadragon.IIIFTileSource.prototype */{/**\n * Determine if the data and/or url imply the image service is supported by\n * this tile source.\n * @function\n * @param {Object|Array} data\n * @param {String} optional - url\n */supports:function supports(data,url){// Version 2.0 and forwards\nif(data.protocol&&data.protocol=='http://iiif.io/api/image'){return true;// Version 1.1\n}else if(data['@context']&&(data['@context']==\"http://library.stanford.edu/iiif/image-api/1.1/context.json\"||data['@context']==\"http://iiif.io/api/image/1/context.json\")){// N.B. the iiif.io context is wrong, but where the representation lives so likely to be used\nreturn true;// Version 1.0\n}else if(data.profile&&data.profile.indexOf(\"http://library.stanford.edu/iiif/image-api/compliance.html\")===0){return true;}else if(data.identifier&&data.width&&data.height){return true;}else if(data.documentElement&&\"info\"==data.documentElement.tagName&&\"http://library.stanford.edu/iiif/image-api/ns/\"==data.documentElement.namespaceURI){return true;// Not IIIF\n}else{return false;}},/**\n *\n * @function\n * @param {Object} data - the raw configuration\n * @example IIIF 1.1 Info Looks like this\n * {\n * \"@context\" : \"http://library.stanford.edu/iiif/image-api/1.1/context.json\",\n * \"@id\" : \"http://iiif.example.com/prefix/1E34750D-38DB-4825-A38A-B60A345E591C\",\n * \"width\" : 6000,\n * \"height\" : 4000,\n * \"scale_factors\" : [ 1, 2, 4 ],\n * \"tile_width\" : 1024,\n * \"tile_height\" : 1024,\n * \"formats\" : [ \"jpg\", \"png\" ],\n * \"qualities\" : [ \"native\", \"grey\" ],\n * \"profile\" : \"http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level0\"\n * }\n */configure:function configure(data,url){// Try to deduce our version and fake it upwards if needed\nif(!$.isPlainObject(data)){var options=configureFromXml10(data);options['@context']=\"http://iiif.io/api/image/1.0/context.json\";options['@id']=url.replace('/info.xml','');return options;}else if(!data['@context']){data['@context']='http://iiif.io/api/image/1.0/context.json';data['@id']=url.replace('/info.json','');return data;}else{return data;}},/**\n * Return the tileWidth for the given level.\n * @function\n * @param {Number} level\n */getTileWidth:function getTileWidth(level){if(this.emulateLegacyImagePyramid){return $.TileSource.prototype.getTileWidth.call(this,level);}var scaleFactor=Math.pow(2,this.maxLevel-level);if(this.tileSizePerScaleFactor&&this.tileSizePerScaleFactor[scaleFactor]){return this.tileSizePerScaleFactor[scaleFactor].width;}return this._tileWidth;},/**\n * Return the tileHeight for the given level.\n * @function\n * @param {Number} level\n */getTileHeight:function getTileHeight(level){if(this.emulateLegacyImagePyramid){return $.TileSource.prototype.getTileHeight.call(this,level);}var scaleFactor=Math.pow(2,this.maxLevel-level);if(this.tileSizePerScaleFactor&&this.tileSizePerScaleFactor[scaleFactor]){return this.tileSizePerScaleFactor[scaleFactor].height;}return this._tileHeight;},/**\n * @function\n * @param {Number} level\n */getLevelScale:function getLevelScale(level){if(this.emulateLegacyImagePyramid){var levelScale=NaN;if(this.levels.length>0&&level>=this.minLevel&&level<=this.maxLevel){levelScale=this.levels[level].width/this.levels[this.maxLevel].width;}return levelScale;}return $.TileSource.prototype.getLevelScale.call(this,level);},/**\n * @function\n * @param {Number} level\n */getNumTiles:function getNumTiles(level){if(this.emulateLegacyImagePyramid){var scale=this.getLevelScale(level);if(scale){return new $.Point(1,1);}else{return new $.Point(0,0);}}return $.TileSource.prototype.getNumTiles.call(this,level);},/**\n * @function\n * @param {Number} level\n * @param {OpenSeadragon.Point} point\n */getTileAtPoint:function getTileAtPoint(level,point){if(this.emulateLegacyImagePyramid){return new $.Point(0,0);}return $.TileSource.prototype.getTileAtPoint.call(this,level,point);},/**\n * Responsible for retrieving the url which will return an image for the\n * region specified by the given x, y, and level components.\n * @function\n * @param {Number} level - z index\n * @param {Number} x\n * @param {Number} y\n * @throws {Error}\n */getTileUrl:function getTileUrl(level,x,y){if(this.emulateLegacyImagePyramid){var url=null;if(this.levels.length>0&&level>=this.minLevel&&level<=this.maxLevel){url=this.levels[level].url;}return url;}//# constants\nvar IIIF_ROTATION='0',//## get the scale (level as a decimal)\nscale=Math.pow(0.5,this.maxLevel-level),//# image dimensions at this level\nlevelWidth=Math.ceil(this.width*scale),levelHeight=Math.ceil(this.height*scale),//## iiif region\ntileWidth,tileHeight,iiifTileSizeWidth,iiifTileSizeHeight,iiifRegion,iiifTileX,iiifTileY,iiifTileW,iiifTileH,iiifSize,iiifQuality,uri;tileWidth=this.getTileWidth(level);tileHeight=this.getTileHeight(level);iiifTileSizeWidth=Math.ceil(tileWidth/scale);iiifTileSizeHeight=Math.ceil(tileHeight/scale);if(this['@context'].indexOf('/1.0/context.json')>-1||this['@context'].indexOf('/1.1/context.json')>-1||this['@context'].indexOf('/1/context.json')>-1){iiifQuality=\"native.jpg\";}else{iiifQuality=\"default.jpg\";}if(levelWidth. Rainer Simon has contributed\n * the included code to the OpenSeadragon project under the New BSD license;\n * see .\n */(function($){/**\n * @class OsmTileSource\n * @classdesc A tilesource implementation for OpenStreetMap.
\n *\n * Note 1. Zoomlevels. Deep Zoom and OSM define zoom levels differently. In Deep\n * Zoom, level 0 equals an image of 1x1 pixels. In OSM, level 0 equals an image of\n * 256x256 levels (see http://gasi.ch/blog/inside-deep-zoom-2). I.e. there is a\n * difference of log2(256)=8 levels.
\n *\n * Note 2. Image dimension. According to the OSM Wiki\n * (http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Zoom_levels)\n * the highest Mapnik zoom level has 256.144x256.144 tiles, with a 256x256\n * pixel size. I.e. the Deep Zoom image dimension is 65.572.864x65.572.864\n * pixels.\n *\n * @memberof OpenSeadragon\n * @extends OpenSeadragon.TileSource\n * @param {Number|Object} width - the pixel width of the image or the idiomatic\n * options object which is used instead of positional arguments.\n * @param {Number} height\n * @param {Number} tileSize\n * @param {Number} tileOverlap\n * @param {String} tilesUrl\n */$.OsmTileSource=function(width,height,tileSize,tileOverlap,tilesUrl){var options;if($.isPlainObject(width)){options=width;}else{options={width:arguments[0],height:arguments[1],tileSize:arguments[2],tileOverlap:arguments[3],tilesUrl:arguments[4]};}//apply default setting for standard public OpenStreatMaps service\n//but allow them to be specified so fliks can host there own instance\n//or apply against other services supportting the same standard\nif(!options.width||!options.height){options.width=65572864;options.height=65572864;}if(!options.tileSize){options.tileSize=256;options.tileOverlap=0;}if(!options.tilesUrl){options.tilesUrl=\"http://tile.openstreetmap.org/\";}options.minLevel=8;$.TileSource.apply(this,[options]);};$.extend($.OsmTileSource.prototype,$.TileSource.prototype,/** @lends OpenSeadragon.OsmTileSource.prototype */{/**\n * Determine if the data and/or url imply the image service is supported by\n * this tile source.\n * @function\n * @param {Object|Array} data\n * @param {String} optional - url\n */supports:function supports(data,url){return data.type&&\"openstreetmaps\"==data.type;},/**\n *\n * @function\n * @param {Object} data - the raw configuration\n * @param {String} url - the url the data was retreived from if any.\n * @return {Object} options - A dictionary of keyword arguments sufficient\n * to configure this tile sources constructor.\n */configure:function configure(data,url){return data;},/**\n * @function\n * @param {Number} level\n * @param {Number} x\n * @param {Number} y\n */getTileUrl:function getTileUrl(level,x,y){return this.tilesUrl+(level-8)+\"/\"+x+\"/\"+y+\".png\";}});})(OpenSeadragon);/*\n * OpenSeadragon - TmsTileSource\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */ /*\n * Derived from the TMS tile source in Rainer Simon's seajax-utils project\n * . Rainer Simon has contributed\n * the included code to the OpenSeadragon project under the New BSD license;\n * see .\n */(function($){/**\n * @class TmsTileSource\n * @classdesc A tilesource implementation for Tiled Map Services (TMS).\n * TMS tile scheme ( [ as supported by OpenLayers ] is described here\n * ( http://openlayers.org/dev/examples/tms.html ).\n *\n * @memberof OpenSeadragon\n * @extends OpenSeadragon.TileSource\n * @param {Number|Object} width - the pixel width of the image or the idiomatic\n * options object which is used instead of positional arguments.\n * @param {Number} height\n * @param {Number} tileSize\n * @param {Number} tileOverlap\n * @param {String} tilesUrl\n */$.TmsTileSource=function(width,height,tileSize,tileOverlap,tilesUrl){var options;if($.isPlainObject(width)){options=width;}else{options={width:arguments[0],height:arguments[1],tileSize:arguments[2],tileOverlap:arguments[3],tilesUrl:arguments[4]};}// TMS has integer multiples of 256 for width/height and adds buffer\n// if necessary -> account for this!\nvar bufferedWidth=Math.ceil(options.width/256)*256,bufferedHeight=Math.ceil(options.height/256)*256,max;// Compute number of zoomlevels in this tileset\nif(bufferedWidth>bufferedHeight){max=bufferedWidth/256;}else{max=bufferedHeight/256;}options.maxLevel=Math.ceil(Math.log(max)/Math.log(2))-1;options.tileSize=256;options.width=bufferedWidth;options.height=bufferedHeight;$.TileSource.apply(this,[options]);};$.extend($.TmsTileSource.prototype,$.TileSource.prototype,/** @lends OpenSeadragon.TmsTileSource.prototype */{/**\n * Determine if the data and/or url imply the image service is supported by\n * this tile source.\n * @function\n * @param {Object|Array} data\n * @param {String} optional - url\n */supports:function supports(data,url){return data.type&&\"tiledmapservice\"==data.type;},/**\n *\n * @function\n * @param {Object} data - the raw configuration\n * @param {String} url - the url the data was retreived from if any.\n * @return {Object} options - A dictionary of keyword arguments sufficient\n * to configure this tile sources constructor.\n */configure:function configure(data,url){return data;},/**\n * @function\n * @param {Number} level\n * @param {Number} x\n * @param {Number} y\n */getTileUrl:function getTileUrl(level,x,y){// Convert from Deep Zoom definition to TMS zoom definition\nvar yTiles=this.getNumTiles(level).y-1;return this.tilesUrl+level+\"/\"+x+\"/\"+(yTiles-y)+\".png\";}});})(OpenSeadragon);(function($){/**\n * @class ZoomifyTileSource\n * @classdesc A tilesource implementation for the zoomify format.\n *\n * A description of the format can be found here:\n * https://ecommons.cornell.edu/bitstream/handle/1813/5410/Introducing_Zoomify_Image.pdf\n *\n * There are two ways of creating a zoomify tilesource for openseadragon\n *\n * 1) Supplying all necessary information in the tilesource object. A minimal example object for this method looks like this:\n *\n * {\n * type: \"zoomifytileservice\",\n * width: 1000,\n * height: 1000,\n * tilesUrl: \"/test/data/zoomify/\"\n * }\n *\n * The tileSize is currently hardcoded to 256 (the usual Zoomify default). The tileUrl must the path to the image _directory_.\n *\n * 2) Loading image metadata from xml file: (CURRENTLY NOT SUPPORTED)\n *\n * When creating zoomify formatted images one \"xml\" like file with name ImageProperties.xml\n * will be created as well. Here is an example of such a file:\n *\n * \n *\n * To use this xml file as metadata source you must supply the path to the ImageProperties.xml file and leave out all other parameters:\n * As stated above, this method of loading a zoomify tilesource is currently not supported\n *\n * {\n * type: \"zoomifytileservice\",\n * tilesUrl: \"/test/data/zoomify/ImageProperties.xml\"\n * }\n\n *\n * @memberof OpenSeadragon\n * @extends OpenSeadragon.TileSource\n * @param {Number} width - the pixel width of the image.\n * @param {Number} height\n * @param {Number} tileSize\n * @param {String} tilesUrl\n */$.ZoomifyTileSource=function(options){options.tileSize=256;var currentImageSize={x:options.width,y:options.height};options.imageSizes=[{x:options.width,y:options.height}];options.gridSize=[this._getGridSize(options.width,options.height,options.tileSize)];while(parseInt(currentImageSize.x,10)>options.tileSize||parseInt(currentImageSize.y,10)>options.tileSize){currentImageSize.x=Math.floor(currentImageSize.x/2);currentImageSize.y=Math.floor(currentImageSize.y/2);options.imageSizes.push({x:currentImageSize.x,y:currentImageSize.y});options.gridSize.push(this._getGridSize(currentImageSize.x,currentImageSize.y,options.tileSize));}options.imageSizes.reverse();options.gridSize.reverse();options.minLevel=0;options.maxLevel=options.gridSize.length-1;OpenSeadragon.TileSource.apply(this,[options]);};$.extend($.ZoomifyTileSource.prototype,$.TileSource.prototype,/** @lends OpenSeadragon.ZoomifyTileSource.prototype */{//private\n_getGridSize:function _getGridSize(width,height,tileSize){return{x:Math.ceil(width/tileSize),y:Math.ceil(height/tileSize)};},//private\n_calculateAbsoluteTileNumber:function _calculateAbsoluteTileNumber(level,x,y){var num=0;var size={};//Sum up all tiles below the level we want the number of tiles\nfor(var z=0;z0){width=options.levels[options.levels.length-1].width;height=options.levels[options.levels.length-1].height;}else{width=0;height=0;$.console.error(\"No supported image formats found\");}$.extend(true,options,{width:width,height:height,tileSize:Math.max(height,width),tileOverlap:0,minLevel:0,maxLevel:options.levels.length>0?options.levels.length-1:0});$.TileSource.apply(this,[options]);this.levels=options.levels;};$.extend($.LegacyTileSource.prototype,$.TileSource.prototype,/** @lends OpenSeadragon.LegacyTileSource.prototype */{/**\n * Determine if the data and/or url imply the image service is supported by\n * this tile source.\n * @function\n * @param {Object|Array} data\n * @param {String} optional - url\n */supports:function supports(data,url){return data.type&&\"legacy-image-pyramid\"==data.type||data.documentElement&&\"legacy-image-pyramid\"==data.documentElement.getAttribute('type');},/**\n *\n * @function\n * @param {Object|XMLDocument} configuration - the raw configuration\n * @param {String} dataUrl - the url the data was retreived from if any.\n * @return {Object} options - A dictionary of keyword arguments sufficient\n * to configure this tile sources constructor.\n */configure:function configure(configuration,dataUrl){var options;if(!$.isPlainObject(configuration)){options=configureFromXML(this,configuration);}else{options=configureFromObject(this,configuration);}return options;},/**\n * @function\n * @param {Number} level\n */getLevelScale:function getLevelScale(level){var levelScale=NaN;if(this.levels.length>0&&level>=this.minLevel&&level<=this.maxLevel){levelScale=this.levels[level].width/this.levels[this.maxLevel].width;}return levelScale;},/**\n * @function\n * @param {Number} level\n */getNumTiles:function getNumTiles(level){var scale=this.getLevelScale(level);if(scale){return new $.Point(1,1);}else{return new $.Point(0,0);}},/**\n * This method is not implemented by this class other than to throw an Error\n * announcing you have to implement it. Because of the variety of tile\n * server technologies, and various specifications for building image\n * pyramids, this method is here to allow easy integration.\n * @function\n * @param {Number} level\n * @param {Number} x\n * @param {Number} y\n * @throws {Error}\n */getTileUrl:function getTileUrl(level,x,y){var url=null;if(this.levels.length>0&&level>=this.minLevel&&level<=this.maxLevel){url=this.levels[level].url;}return url;}});/**\n * This method removes any files from the Array which dont conform to our\n * basic requirements for a 'level' in the LegacyTileSource.\n * @private\n * @inner\n * @function\n */function filterFiles(files){var filtered=[],file,i;for(i=0;i');}}return filtered.sort(function(a,b){return a.height-b.height;});}/**\n * @private\n * @inner\n * @function\n */function configureFromXML(tileSource,xmlDoc){if(!xmlDoc||!xmlDoc.documentElement){throw new Error($.getString(\"Errors.Xml\"));}var root=xmlDoc.documentElement,rootName=root.tagName,conf=null,levels=[],level,i;if(rootName==\"image\"){try{conf={type:root.getAttribute(\"type\"),levels:[]};levels=root.getElementsByTagName(\"level\");for(i=0;i=this.minLevel&&level<=this.maxLevel){levelScale=this.levels[level].width/this.levels[this.maxLevel].width;}return levelScale;},/**\n * @function\n * @param {Number} level\n */getNumTiles:function getNumTiles(level){var scale=this.getLevelScale(level);if(scale){return new $.Point(1,1);}else{return new $.Point(0,0);}},/**\n * Retrieves a tile url\n * @function\n * @param {Number} level Level of the tile\n * @param {Number} x x coordinate of the tile\n * @param {Number} y y coordinate of the tile\n */getTileUrl:function getTileUrl(level,x,y){var url=null;if(level>=this.minLevel&&level<=this.maxLevel){url=this.levels[level].url;}return url;},/**\n * Retrieves a tile context 2D\n * @function\n * @param {Number} level Level of the tile\n * @param {Number} x x coordinate of the tile\n * @param {Number} y y coordinate of the tile\n */getContext2D:function getContext2D(level,x,y){var context=null;if(level>=this.minLevel&&level<=this.maxLevel){context=this.levels[level].context2D;}return context;},// private\n//\n// Builds the differents levels of the pyramid if possible\n// (i.e. if canvas API enabled and no canvas tainting issue).\n_buildLevels:function _buildLevels(){var levels=[{url:this._image.src,/* IE8 fix since it has no naturalWidth and naturalHeight */width:Object.prototype.hasOwnProperty.call(this._image,'naturalWidth')?this._image.naturalWidth:this._image.width,height:Object.prototype.hasOwnProperty.call(this._image,'naturalHeight')?this._image.naturalHeight:this._image.height}];if(!this.buildPyramid||!$.supportsCanvas||!this.useCanvas){// We don't need the image anymore. Allows it to be GC.\ndelete this._image;return levels;}/* IE8 fix since it has no naturalWidth and naturalHeight */var currentWidth=Object.prototype.hasOwnProperty.call(this._image,'naturalWidth')?this._image.naturalWidth:this._image.width;var currentHeight=Object.prototype.hasOwnProperty.call(this._image,'naturalHeight')?this._image.naturalHeight:this._image.height;var bigCanvas=document.createElement(\"canvas\");var bigContext=bigCanvas.getContext(\"2d\");bigCanvas.width=currentWidth;bigCanvas.height=currentHeight;bigContext.drawImage(this._image,0,0,currentWidth,currentHeight);// We cache the context of the highest level because the browser\n// is a lot faster at downsampling something it already has\n// downsampled before.\nlevels[0].context2D=bigContext;// We don't need the image anymore. Allows it to be GC.\ndelete this._image;if($.isCanvasTainted(bigCanvas)){// If the canvas is tainted, we can't compute the pyramid.\nreturn levels;}// We build smaller levels until either width or height becomes\n// 1 pixel wide.\nwhile(currentWidth>=2&¤tHeight>=2){currentWidth=Math.floor(currentWidth/2);currentHeight=Math.floor(currentHeight/2);var smallCanvas=document.createElement(\"canvas\");var smallContext=smallCanvas.getContext(\"2d\");smallCanvas.width=currentWidth;smallCanvas.height=currentHeight;smallContext.drawImage(bigCanvas,0,0,currentWidth,currentHeight);levels.splice(0,0,{context2D:smallContext,width:currentWidth,height:currentHeight});bigCanvas=smallCanvas;bigContext=smallContext;}return levels;}});})(OpenSeadragon);/*\n * OpenSeadragon - TileSourceCollection\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){// deprecated\n$.TileSourceCollection=function(tileSize,tileSources,rows,layout){$.console.error('TileSourceCollection is deprecated; use World instead');};})(OpenSeadragon);/*\n * OpenSeadragon - Button\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * An enumeration of button states\n * @member ButtonState\n * @memberof OpenSeadragon\n * @static\n * @type {Object}\n * @property {Number} REST\n * @property {Number} GROUP\n * @property {Number} HOVER\n * @property {Number} DOWN\n */$.ButtonState={REST:0,GROUP:1,HOVER:2,DOWN:3};/**\n * @class Button\n * @classdesc Manages events, hover states for individual buttons, tool-tips, as well\n * as fading the buttons out when the user has not interacted with them\n * for a specified period.\n *\n * @memberof OpenSeadragon\n * @extends OpenSeadragon.EventSource\n * @param {Object} options\n * @param {Element} [options.element=null] Element to use as the button. If not specified, an HTML <div> element is created.\n * @param {String} [options.tooltip=null] Provides context help for the button when the\n * user hovers over it.\n * @param {String} [options.srcRest=null] URL of image to use in 'rest' state.\n * @param {String} [options.srcGroup=null] URL of image to use in 'up' state.\n * @param {String} [options.srcHover=null] URL of image to use in 'hover' state.\n * @param {String} [options.srcDown=null] URL of image to use in 'down' state.\n * @param {Number} [options.fadeDelay=0] How long to wait before fading.\n * @param {Number} [options.fadeLength=2000] How long should it take to fade the button.\n * @param {OpenSeadragon.EventHandler} [options.onPress=null] Event handler callback for {@link OpenSeadragon.Button.event:press}.\n * @param {OpenSeadragon.EventHandler} [options.onRelease=null] Event handler callback for {@link OpenSeadragon.Button.event:release}.\n * @param {OpenSeadragon.EventHandler} [options.onClick=null] Event handler callback for {@link OpenSeadragon.Button.event:click}.\n * @param {OpenSeadragon.EventHandler} [options.onEnter=null] Event handler callback for {@link OpenSeadragon.Button.event:enter}.\n * @param {OpenSeadragon.EventHandler} [options.onExit=null] Event handler callback for {@link OpenSeadragon.Button.event:exit}.\n * @param {OpenSeadragon.EventHandler} [options.onFocus=null] Event handler callback for {@link OpenSeadragon.Button.event:focus}.\n * @param {OpenSeadragon.EventHandler} [options.onBlur=null] Event handler callback for {@link OpenSeadragon.Button.event:blur}.\n */$.Button=function(options){var _this=this;$.EventSource.call(this);$.extend(true,this,{tooltip:null,srcRest:null,srcGroup:null,srcHover:null,srcDown:null,clickTimeThreshold:$.DEFAULT_SETTINGS.clickTimeThreshold,clickDistThreshold:$.DEFAULT_SETTINGS.clickDistThreshold,/**\n * How long to wait before fading.\n * @member {Number} fadeDelay\n * @memberof OpenSeadragon.Button#\n */fadeDelay:0,/**\n * How long should it take to fade the button.\n * @member {Number} fadeLength\n * @memberof OpenSeadragon.Button#\n */fadeLength:2000,onPress:null,onRelease:null,onClick:null,onEnter:null,onExit:null,onFocus:null,onBlur:null},options);/**\n * The button element.\n * @member {Element} element\n * @memberof OpenSeadragon.Button#\n */this.element=options.element||$.makeNeutralElement(\"div\");//if the user has specified the element to bind the control to explicitly\n//then do not add the default control images\nif(!options.element){this.imgRest=$.makeTransparentImage(this.srcRest);this.imgGroup=$.makeTransparentImage(this.srcGroup);this.imgHover=$.makeTransparentImage(this.srcHover);this.imgDown=$.makeTransparentImage(this.srcDown);this.imgRest.alt=this.imgGroup.alt=this.imgHover.alt=this.imgDown.alt=this.tooltip;this.element.style.position=\"relative\";$.setElementTouchActionNone(this.element);this.imgGroup.style.position=this.imgHover.style.position=this.imgDown.style.position=\"absolute\";this.imgGroup.style.top=this.imgHover.style.top=this.imgDown.style.top=\"0px\";this.imgGroup.style.left=this.imgHover.style.left=this.imgDown.style.left=\"0px\";this.imgHover.style.visibility=this.imgDown.style.visibility=\"hidden\";if($.Browser.vendor==$.BROWSERS.FIREFOX&&$.Browser.version<3){this.imgGroup.style.top=this.imgHover.style.top=this.imgDown.style.top=\"\";}this.element.appendChild(this.imgRest);this.element.appendChild(this.imgGroup);this.element.appendChild(this.imgHover);this.element.appendChild(this.imgDown);}this.addHandler(\"press\",this.onPress);this.addHandler(\"release\",this.onRelease);this.addHandler(\"click\",this.onClick);this.addHandler(\"enter\",this.onEnter);this.addHandler(\"exit\",this.onExit);this.addHandler(\"focus\",this.onFocus);this.addHandler(\"blur\",this.onBlur);/**\n * The button's current state.\n * @member {OpenSeadragon.ButtonState} currentState\n * @memberof OpenSeadragon.Button#\n */this.currentState=$.ButtonState.GROUP;// When the button last began to fade.\nthis.fadeBeginTime=null;// Whether this button should fade after user stops interacting with the viewport.\nthis.shouldFade=false;this.element.style.display=\"inline-block\";this.element.style.position=\"relative\";this.element.title=this.tooltip;/**\n * Tracks mouse/touch/key events on the button.\n * @member {OpenSeadragon.MouseTracker} tracker\n * @memberof OpenSeadragon.Button#\n */this.tracker=new $.MouseTracker({element:this.element,clickTimeThreshold:this.clickTimeThreshold,clickDistThreshold:this.clickDistThreshold,enterHandler:function enterHandler(event){if(event.insideElementPressed){inTo(_this,$.ButtonState.DOWN);/**\n * Raised when the cursor enters the Button element.\n *\n * @event enter\n * @memberof OpenSeadragon.Button\n * @type {object}\n * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent(\"enter\",{originalEvent:event.originalEvent});}else if(!event.buttonDownAny){inTo(_this,$.ButtonState.HOVER);}},focusHandler:function focusHandler(event){this.enterHandler(event);/**\n * Raised when the Button element receives focus.\n *\n * @event focus\n * @memberof OpenSeadragon.Button\n * @type {object}\n * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent(\"focus\",{originalEvent:event.originalEvent});},exitHandler:function exitHandler(event){outTo(_this,$.ButtonState.GROUP);if(event.insideElementPressed){/**\n * Raised when the cursor leaves the Button element.\n *\n * @event exit\n * @memberof OpenSeadragon.Button\n * @type {object}\n * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent(\"exit\",{originalEvent:event.originalEvent});}},blurHandler:function blurHandler(event){this.exitHandler(event);/**\n * Raised when the Button element loses focus.\n *\n * @event blur\n * @memberof OpenSeadragon.Button\n * @type {object}\n * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent(\"blur\",{originalEvent:event.originalEvent});},pressHandler:function pressHandler(event){inTo(_this,$.ButtonState.DOWN);/**\n * Raised when a mouse button is pressed or touch occurs in the Button element.\n *\n * @event press\n * @memberof OpenSeadragon.Button\n * @type {object}\n * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent(\"press\",{originalEvent:event.originalEvent});},releaseHandler:function releaseHandler(event){if(event.insideElementPressed&&event.insideElementReleased){outTo(_this,$.ButtonState.HOVER);/**\n * Raised when the mouse button is released or touch ends in the Button element.\n *\n * @event release\n * @memberof OpenSeadragon.Button\n * @type {object}\n * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent(\"release\",{originalEvent:event.originalEvent});}else if(event.insideElementPressed){outTo(_this,$.ButtonState.GROUP);}else{inTo(_this,$.ButtonState.HOVER);}},clickHandler:function clickHandler(event){if(event.quick){/**\n * Raised when a mouse button is pressed and released or touch is initiated and ended in the Button element within the time and distance threshold.\n *\n * @event click\n * @memberof OpenSeadragon.Button\n * @type {object}\n * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent(\"click\",{originalEvent:event.originalEvent});}},keyHandler:function keyHandler(event){//console.log( \"%s : handling key %s!\", _this.tooltip, event.keyCode);\nif(13===event.keyCode){/***\n * Raised when a mouse button is pressed and released or touch is initiated and ended in the Button element within the time and distance threshold.\n *\n * @event click\n * @memberof OpenSeadragon.Button\n * @type {object}\n * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent(\"click\",{originalEvent:event.originalEvent});/***\n * Raised when the mouse button is released or touch ends in the Button element.\n *\n * @event release\n * @memberof OpenSeadragon.Button\n * @type {object}\n * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent(\"release\",{originalEvent:event.originalEvent});return false;}return true;}});outTo(this,$.ButtonState.REST);};$.extend($.Button.prototype,$.EventSource.prototype,/** @lends OpenSeadragon.Button.prototype */{/**\n * TODO: Determine what this function is intended to do and if it's actually\n * useful as an API point.\n * @function\n */notifyGroupEnter:function notifyGroupEnter(){inTo(this,$.ButtonState.GROUP);},/**\n * TODO: Determine what this function is intended to do and if it's actually\n * useful as an API point.\n * @function\n */notifyGroupExit:function notifyGroupExit(){outTo(this,$.ButtonState.REST);},/**\n * @function\n */disable:function disable(){this.notifyGroupExit();this.element.disabled=true;$.setElementOpacity(this.element,0.2,true);},/**\n * @function\n */enable:function enable(){this.element.disabled=false;$.setElementOpacity(this.element,1.0,true);this.notifyGroupEnter();}});function scheduleFade(button){$.requestAnimationFrame(function(){updateFade(button);});}function updateFade(button){var currentTime,deltaTime,opacity;if(button.shouldFade){currentTime=$.now();deltaTime=currentTime-button.fadeBeginTime;opacity=1.0-deltaTime/button.fadeLength;opacity=Math.min(1.0,opacity);opacity=Math.max(0.0,opacity);if(button.imgGroup){$.setElementOpacity(button.imgGroup,opacity,true);}if(opacity>0){// fade again\nscheduleFade(button);}}}function beginFading(button){button.shouldFade=true;button.fadeBeginTime=$.now()+button.fadeDelay;window.setTimeout(function(){scheduleFade(button);},button.fadeDelay);}function stopFading(button){button.shouldFade=false;if(button.imgGroup){$.setElementOpacity(button.imgGroup,1.0,true);}}function inTo(button,newState){if(button.element.disabled){return;}if(newState>=$.ButtonState.GROUP&&button.currentState==$.ButtonState.REST){stopFading(button);button.currentState=$.ButtonState.GROUP;}if(newState>=$.ButtonState.HOVER&&button.currentState==$.ButtonState.GROUP){if(button.imgHover){button.imgHover.style.visibility=\"\";}button.currentState=$.ButtonState.HOVER;}if(newState>=$.ButtonState.DOWN&&button.currentState==$.ButtonState.HOVER){if(button.imgDown){button.imgDown.style.visibility=\"\";}button.currentState=$.ButtonState.DOWN;}}function outTo(button,newState){if(button.element.disabled){return;}if(newState<=$.ButtonState.HOVER&&button.currentState==$.ButtonState.DOWN){if(button.imgDown){button.imgDown.style.visibility=\"hidden\";}button.currentState=$.ButtonState.HOVER;}if(newState<=$.ButtonState.GROUP&&button.currentState==$.ButtonState.HOVER){if(button.imgHover){button.imgHover.style.visibility=\"hidden\";}button.currentState=$.ButtonState.GROUP;}if(newState<=$.ButtonState.REST&&button.currentState==$.ButtonState.GROUP){beginFading(button);button.currentState=$.ButtonState.REST;}}})(OpenSeadragon);/*\n * OpenSeadragon - ButtonGroup\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * @class ButtonGroup\n * @classdesc Manages events on groups of buttons.\n *\n * @memberof OpenSeadragon\n * @param {Object} options - A dictionary of settings applied against the entire group of buttons.\n * @param {Array} options.buttons Array of buttons\n * @param {Element} [options.element] Element to use as the container\n **/$.ButtonGroup=function(options){$.extend(true,this,{/**\n * An array containing the buttons themselves.\n * @member {Array} buttons\n * @memberof OpenSeadragon.ButtonGroup#\n */buttons:[],clickTimeThreshold:$.DEFAULT_SETTINGS.clickTimeThreshold,clickDistThreshold:$.DEFAULT_SETTINGS.clickDistThreshold,labelText:\"\"},options);// copy the button elements TODO: Why?\nvar buttons=this.buttons.concat([]),_this=this,i;/**\n * The shared container for the buttons.\n * @member {Element} element\n * @memberof OpenSeadragon.ButtonGroup#\n */this.element=options.element||$.makeNeutralElement(\"div\");// TODO What if there IS an options.group specified?\nif(!options.group){this.label=$.makeNeutralElement(\"label\");//TODO: support labels for ButtonGroups\n//this.label.innerHTML = this.labelText;\nthis.element.style.display=\"inline-block\";this.element.appendChild(this.label);for(i=0;i=270){newTopLeft=this.getTopRight();this.x=newTopLeft.x;this.y=newTopLeft.y;newWidth=this.height;this.height=this.width;this.width=newWidth;this.degrees-=270;}else if(this.degrees>=180){newTopLeft=this.getBottomRight();this.x=newTopLeft.x;this.y=newTopLeft.y;this.degrees-=180;}else if(this.degrees>=90){newTopLeft=this.getBottomLeft();this.x=newTopLeft.x;this.y=newTopLeft.y;newWidth=this.height;this.height=this.width;this.width=newWidth;this.degrees-=90;}};/**\n * Builds a rectangle having the 3 specified points as summits.\n * @static\n * @memberof OpenSeadragon.Rect\n * @param {OpenSeadragon.Point} topLeft\n * @param {OpenSeadragon.Point} topRight\n * @param {OpenSeadragon.Point} bottomLeft\n * @returns {OpenSeadragon.Rect}\n */$.Rect.fromSummits=function(topLeft,topRight,bottomLeft){var width=topLeft.distanceTo(topRight);var height=topLeft.distanceTo(bottomLeft);var diff=topRight.minus(topLeft);var radians=Math.atan(diff.y/diff.x);if(diff.x<0){radians+=Math.PI;}else if(diff.y<0){radians+=2*Math.PI;}return new $.Rect(topLeft.x,topLeft.y,width,height,radians/Math.PI*180);};/** @lends OpenSeadragon.Rect.prototype */$.Rect.prototype={/**\n * @function\n * @returns {OpenSeadragon.Rect} a duplicate of this Rect\n */clone:function clone(){return new $.Rect(this.x,this.y,this.width,this.height,this.degrees);},/**\n * The aspect ratio is simply the ratio of width to height.\n * @function\n * @returns {Number} The ratio of width to height.\n */getAspectRatio:function getAspectRatio(){return this.width/this.height;},/**\n * Provides the coordinates of the upper-left corner of the rectangle as a\n * point.\n * @function\n * @returns {OpenSeadragon.Point} The coordinate of the upper-left corner of\n * the rectangle.\n */getTopLeft:function getTopLeft(){return new $.Point(this.x,this.y);},/**\n * Provides the coordinates of the bottom-right corner of the rectangle as a\n * point.\n * @function\n * @returns {OpenSeadragon.Point} The coordinate of the bottom-right corner of\n * the rectangle.\n */getBottomRight:function getBottomRight(){return new $.Point(this.x+this.width,this.y+this.height).rotate(this.degrees,this.getTopLeft());},/**\n * Provides the coordinates of the top-right corner of the rectangle as a\n * point.\n * @function\n * @returns {OpenSeadragon.Point} The coordinate of the top-right corner of\n * the rectangle.\n */getTopRight:function getTopRight(){return new $.Point(this.x+this.width,this.y).rotate(this.degrees,this.getTopLeft());},/**\n * Provides the coordinates of the bottom-left corner of the rectangle as a\n * point.\n * @function\n * @returns {OpenSeadragon.Point} The coordinate of the bottom-left corner of\n * the rectangle.\n */getBottomLeft:function getBottomLeft(){return new $.Point(this.x,this.y+this.height).rotate(this.degrees,this.getTopLeft());},/**\n * Computes the center of the rectangle.\n * @function\n * @returns {OpenSeadragon.Point} The center of the rectangle as represented\n * as represented by a 2-dimensional vector (x,y)\n */getCenter:function getCenter(){return new $.Point(this.x+this.width/2.0,this.y+this.height/2.0).rotate(this.degrees,this.getTopLeft());},/**\n * Returns the width and height component as a vector OpenSeadragon.Point\n * @function\n * @returns {OpenSeadragon.Point} The 2 dimensional vector representing the\n * the width and height of the rectangle.\n */getSize:function getSize(){return new $.Point(this.width,this.height);},/**\n * Determines if two Rectangles have equivalent components.\n * @function\n * @param {OpenSeadragon.Rect} rectangle The Rectangle to compare to.\n * @return {Boolean} 'true' if all components are equal, otherwise 'false'.\n */equals:function equals(other){return other instanceof $.Rect&&this.x===other.x&&this.y===other.y&&this.width===other.width&&this.height===other.height&&this.degrees===other.degrees;},/**\n * Multiply all dimensions (except degrees) in this Rect by a factor and\n * return a new Rect.\n * @function\n * @param {Number} factor The factor to multiply vector components.\n * @returns {OpenSeadragon.Rect} A new rect representing the multiplication\n * of the vector components by the factor\n */times:function times(factor){return new $.Rect(this.x*factor,this.y*factor,this.width*factor,this.height*factor,this.degrees);},/**\n * Translate/move this Rect by a vector and return new Rect.\n * @function\n * @param {OpenSeadragon.Point} delta The translation vector.\n * @returns {OpenSeadragon.Rect} A new rect with altered position\n */translate:function translate(delta){return new $.Rect(this.x+delta.x,this.y+delta.y,this.width,this.height,this.degrees);},/**\n * Returns the smallest rectangle that will contain this and the given\n * rectangle bounding boxes.\n * @param {OpenSeadragon.Rect} rect\n * @return {OpenSeadragon.Rect} The new rectangle.\n */union:function union(rect){var thisBoundingBox=this.getBoundingBox();var otherBoundingBox=rect.getBoundingBox();var left=Math.min(thisBoundingBox.x,otherBoundingBox.x);var top=Math.min(thisBoundingBox.y,otherBoundingBox.y);var right=Math.max(thisBoundingBox.x+thisBoundingBox.width,otherBoundingBox.x+otherBoundingBox.width);var bottom=Math.max(thisBoundingBox.y+thisBoundingBox.height,otherBoundingBox.y+otherBoundingBox.height);return new $.Rect(left,top,right-left,bottom-top);},/**\n * Returns the bounding box of the intersection of this rectangle with the\n * given rectangle.\n * @param {OpenSeadragon.Rect} rect\n * @return {OpenSeadragon.Rect} the bounding box of the intersection\n * or null if the rectangles don't intersect.\n */intersection:function intersection(rect){// Simplified version of Weiler Atherton clipping algorithm\n// https://en.wikipedia.org/wiki/Weiler%E2%80%93Atherton_clipping_algorithm\n// Because we just want the bounding box of the intersection,\n// we can just compute the bounding box of:\n// 1. all the summits of this which are inside rect\n// 2. all the summits of rect which are inside this\n// 3. all the intersections of rect and this\nvar EPSILON=0.0000000001;var intersectionPoints=[];var thisTopLeft=this.getTopLeft();if(rect.containsPoint(thisTopLeft,EPSILON)){intersectionPoints.push(thisTopLeft);}var thisTopRight=this.getTopRight();if(rect.containsPoint(thisTopRight,EPSILON)){intersectionPoints.push(thisTopRight);}var thisBottomLeft=this.getBottomLeft();if(rect.containsPoint(thisBottomLeft,EPSILON)){intersectionPoints.push(thisBottomLeft);}var thisBottomRight=this.getBottomRight();if(rect.containsPoint(thisBottomRight,EPSILON)){intersectionPoints.push(thisBottomRight);}var rectTopLeft=rect.getTopLeft();if(this.containsPoint(rectTopLeft,EPSILON)){intersectionPoints.push(rectTopLeft);}var rectTopRight=rect.getTopRight();if(this.containsPoint(rectTopRight,EPSILON)){intersectionPoints.push(rectTopRight);}var rectBottomLeft=rect.getBottomLeft();if(this.containsPoint(rectBottomLeft,EPSILON)){intersectionPoints.push(rectBottomLeft);}var rectBottomRight=rect.getBottomRight();if(this.containsPoint(rectBottomRight,EPSILON)){intersectionPoints.push(rectBottomRight);}var thisSegments=this._getSegments();var rectSegments=rect._getSegments();for(var i=0;imaxX){maxX=point.x;}if(point.ymaxY){maxY=point.y;}}return new $.Rect(minX,minY,maxX-minX,maxY-minY);},// private\n_getSegments:function _getSegments(){var topLeft=this.getTopLeft();var topRight=this.getTopRight();var bottomLeft=this.getBottomLeft();var bottomRight=this.getBottomRight();return[[topLeft,topRight],[topRight,bottomRight],[bottomRight,bottomLeft],[bottomLeft,topLeft]];},/**\n * Rotates a rectangle around a point.\n * @function\n * @param {Number} degrees The angle in degrees to rotate.\n * @param {OpenSeadragon.Point} [pivot] The point about which to rotate.\n * Defaults to the center of the rectangle.\n * @return {OpenSeadragon.Rect}\n */rotate:function rotate(degrees,pivot){degrees=$.positiveModulo(degrees,360);if(degrees===0){return this.clone();}pivot=pivot||this.getCenter();var newTopLeft=this.getTopLeft().rotate(degrees,pivot);var newTopRight=this.getTopRight().rotate(degrees,pivot);var diff=newTopRight.minus(newTopLeft);// Handle floating point error\ndiff=diff.apply(function(x){var EPSILON=1e-15;return Math.abs(x)=-epsilon&&(point.x-topRight.x)*topDiff.x+(point.y-topRight.y)*topDiff.y<=epsilon&&(point.x-topLeft.x)*leftDiff.x+(point.y-topLeft.y)*leftDiff.y>=-epsilon&&(point.x-bottomLeft.x)*leftDiff.x+(point.y-bottomLeft.y)*leftDiff.y<=epsilon;},/**\n * Provides a string representation of the rectangle which is useful for\n * debugging.\n * @function\n * @returns {String} A string representation of the rectangle.\n */toString:function toString(){return\"[\"+Math.round(this.x*100)/100+\", \"+Math.round(this.y*100)/100+\", \"+Math.round(this.width*100)/100+\"x\"+Math.round(this.height*100)/100+\", \"+Math.round(this.degrees*100)/100+\"deg\"+\"]\";}};})(OpenSeadragon);/*\n * OpenSeadragon - ReferenceStrip\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){// dictionary from id to private properties\nvar THIS={};/**\n * The CollectionDrawer is a reimplementation if the Drawer API that\n * focuses on allowing a viewport to be redefined as a collection\n * of smaller viewports, defined by a clear number of rows and / or\n * columns of which each item in the matrix of viewports has its own\n * source.\n *\n * This idea is a reexpression of the idea of dzi collections\n * which allows a clearer algorithm to reuse the tile sources already\n * supported by OpenSeadragon, in heterogenious or homogenious\n * sequences just like mixed groups already supported by the viewer\n * for the purpose of image sequnces.\n *\n * TODO: The difficult part of this feature is figuring out how to express\n * this functionality as a combination of the functionality already\n * provided by Drawer, Viewport, TileSource, and Navigator. It may\n * require better abstraction at those points in order to effeciently\n * reuse those paradigms.\n */ /**\n * @class ReferenceStrip\n * @memberof OpenSeadragon\n * @param {Object} options\n */$.ReferenceStrip=function(options){var _this=this,viewer=options.viewer,viewerSize=$.getElementSize(viewer.element),element,style,i;//We may need to create a new element and id if they did not\n//provide the id for the existing element\nif(!options.id){options.id='referencestrip-'+$.now();this.element=$.makeNeutralElement(\"div\");this.element.id=options.id;this.element.className='referencestrip';}options=$.extend(true,{sizeRatio:$.DEFAULT_SETTINGS.referenceStripSizeRatio,position:$.DEFAULT_SETTINGS.referenceStripPosition,scroll:$.DEFAULT_SETTINGS.referenceStripScroll,clickTimeThreshold:$.DEFAULT_SETTINGS.clickTimeThreshold},options,{//required overrides\nelement:this.element,//These need to be overridden to prevent recursion since\n//the navigator is a viewer and a viewer has a navigator\nshowNavigator:false,mouseNavEnabled:false,showNavigationControl:false,showSequenceControl:false});$.extend(this,options);//Private state properties\nTHIS[this.id]={\"animating\":false};this.minPixelRatio=this.viewer.minPixelRatio;style=this.element.style;style.marginTop='0px';style.marginRight='0px';style.marginBottom='0px';style.marginLeft='0px';style.left='0px';style.bottom='0px';style.border='0px';style.background='#000';style.position='relative';$.setElementTouchActionNone(this.element);$.setElementOpacity(this.element,0.8);this.viewer=viewer;this.innerTracker=new $.MouseTracker({element:this.element,dragHandler:$.delegate(this,onStripDrag),scrollHandler:$.delegate(this,onStripScroll),enterHandler:$.delegate(this,onStripEnter),exitHandler:$.delegate(this,onStripExit),keyDownHandler:$.delegate(this,onKeyDown),keyHandler:$.delegate(this,onKeyPress)});//Controls the position and orientation of the reference strip and sets the\n//appropriate width and height\nif(options.width&&options.height){this.element.style.width=options.width+'px';this.element.style.height=options.height+'px';viewer.addControl(this.element,{anchor:$.ControlAnchor.BOTTOM_LEFT});}else{if(\"horizontal\"==options.scroll){this.element.style.width=viewerSize.x*options.sizeRatio*viewer.tileSources.length+12*viewer.tileSources.length+'px';this.element.style.height=viewerSize.y*options.sizeRatio+'px';viewer.addControl(this.element,{anchor:$.ControlAnchor.BOTTOM_LEFT});}else{this.element.style.height=viewerSize.y*options.sizeRatio*viewer.tileSources.length+12*viewer.tileSources.length+'px';this.element.style.width=viewerSize.x*options.sizeRatio+'px';viewer.addControl(this.element,{anchor:$.ControlAnchor.TOP_LEFT});}}this.panelWidth=viewerSize.x*this.sizeRatio+8;this.panelHeight=viewerSize.y*this.sizeRatio+8;this.panels=[];this.miniViewers={};/*jshint loopfunc:true*/for(i=0;ioffsetLeft+viewerSize.x-this.panelWidth){offset=Math.min(offset,scrollWidth-viewerSize.x);this.element.style.marginLeft=-offset+'px';loadPanels(this,viewerSize.x,-offset);}else if(offsetoffsetTop+viewerSize.y-this.panelHeight){offset=Math.min(offset,scrollHeight-viewerSize.y);this.element.style.marginTop=-offset+'px';loadPanels(this,viewerSize.y,-offset);}else if(offset0){//forward\nif(offsetLeft>-(scrollWidth-viewerSize.x)){this.element.style.marginLeft=offsetLeft+event.delta.x*2+'px';loadPanels(this,viewerSize.x,offsetLeft+event.delta.x*2);}}else if(-event.delta.x<0){//reverse\nif(offsetLeft<0){this.element.style.marginLeft=offsetLeft+event.delta.x*2+'px';loadPanels(this,viewerSize.x,offsetLeft+event.delta.x*2);}}}else{if(-event.delta.y>0){//forward\nif(offsetTop>-(scrollHeight-viewerSize.y)){this.element.style.marginTop=offsetTop+event.delta.y*2+'px';loadPanels(this,viewerSize.y,offsetTop+event.delta.y*2);}}else if(-event.delta.y<0){//reverse\nif(offsetTop<0){this.element.style.marginTop=offsetTop+event.delta.y*2+'px';loadPanels(this,viewerSize.y,offsetTop+event.delta.y*2);}}}}return false;}/**\n * @private\n * @inner\n * @function\n */function onStripScroll(event){var offsetLeft=Number(this.element.style.marginLeft.replace('px','')),offsetTop=Number(this.element.style.marginTop.replace('px','')),scrollWidth=Number(this.element.style.width.replace('px','')),scrollHeight=Number(this.element.style.height.replace('px','')),viewerSize=$.getElementSize(this.viewer.canvas);if(this.element){if('horizontal'==this.scroll){if(event.scroll>0){//forward\nif(offsetLeft>-(scrollWidth-viewerSize.x)){this.element.style.marginLeft=offsetLeft-event.scroll*60+'px';loadPanels(this,viewerSize.x,offsetLeft-event.scroll*60);}}else if(event.scroll<0){//reverse\nif(offsetLeft<0){this.element.style.marginLeft=offsetLeft-event.scroll*60+'px';loadPanels(this,viewerSize.x,offsetLeft-event.scroll*60);}}}else{if(event.scroll<0){//scroll up\nif(offsetTop>viewerSize.y-scrollHeight){this.element.style.marginTop=offsetTop+event.scroll*60+'px';loadPanels(this,viewerSize.y,offsetTop+event.scroll*60);}}else if(event.scroll>0){//scroll dowm\nif(offsetTop<0){this.element.style.marginTop=offsetTop+event.scroll*60+'px';loadPanels(this,viewerSize.y,offsetTop+event.scroll*60);}}}}//cancels event\nreturn false;}function loadPanels(strip,viewerSize,scroll){var panelSize,activePanelsStart,activePanelsEnd,miniViewer,style,i,element;if('horizontal'==strip.scroll){panelSize=strip.panelWidth;}else{panelSize=strip.panelHeight;}activePanelsStart=Math.ceil(viewerSize/panelSize)+5;activePanelsEnd=Math.ceil((Math.abs(scroll)+viewerSize)/panelSize)+1;activePanelsStart=activePanelsEnd-activePanelsStart;activePanelsStart=activePanelsStart<0?0:activePanelsStart;for(i=activePanelsStart;i1?args[1].springStiffness:5.0,/**\n * Animation duration per spring.\n * @member {Number} animationTime\n * @memberof OpenSeadragon.Spring#\n */animationTime:args.length>1?args[1].animationTime:1.5};}$.console.assert(typeof options.springStiffness===\"number\"&&options.springStiffness!==0,\"[OpenSeadragon.Spring] options.springStiffness must be a non-zero number\");$.console.assert(typeof options.animationTime===\"number\"&&options.animationTime>=0,\"[OpenSeadragon.Spring] options.animationTime must be a number greater than or equal to 0\");if(options.exponential){this._exponential=true;delete options.exponential;}$.extend(true,this,options);/**\n * @member {Object} current\n * @memberof OpenSeadragon.Spring#\n * @property {Number} value\n * @property {Number} time\n */this.current={value:typeof this.initial==\"number\"?this.initial:this._exponential?0:1,time:$.now()// always work in milliseconds\n};$.console.assert(!this._exponential||this.current.value!==0,\"[OpenSeadragon.Spring] value must be non-zero for exponential springs\");/**\n * @member {Object} start\n * @memberof OpenSeadragon.Spring#\n * @property {Number} value\n * @property {Number} time\n */this.start={value:this.current.value,time:this.current.time};/**\n * @member {Object} target\n * @memberof OpenSeadragon.Spring#\n * @property {Number} value\n * @property {Number} time\n */this.target={value:this.current.value,time:this.current.time};if(this._exponential){this.start._logValue=Math.log(this.start.value);this.target._logValue=Math.log(this.target.value);this.current._logValue=Math.log(this.current.value);}};/** @lends OpenSeadragon.Spring.prototype */$.Spring.prototype={/**\n * @function\n * @param {Number} target\n */resetTo:function resetTo(target){$.console.assert(!this._exponential||target!==0,\"[OpenSeadragon.Spring.resetTo] target must be non-zero for exponential springs\");this.start.value=this.target.value=this.current.value=target;this.start.time=this.target.time=this.current.time=$.now();if(this._exponential){this.start._logValue=Math.log(this.start.value);this.target._logValue=Math.log(this.target.value);this.current._logValue=Math.log(this.current.value);}},/**\n * @function\n * @param {Number} target\n */springTo:function springTo(target){$.console.assert(!this._exponential||target!==0,\"[OpenSeadragon.Spring.springTo] target must be non-zero for exponential springs\");this.start.value=this.current.value;this.start.time=this.current.time;this.target.value=target;this.target.time=this.start.time+1000*this.animationTime;if(this._exponential){this.start._logValue=Math.log(this.start.value);this.target._logValue=Math.log(this.target.value);}},/**\n * @function\n * @param {Number} delta\n */shiftBy:function shiftBy(delta){this.start.value+=delta;this.target.value+=delta;if(this._exponential){$.console.assert(this.target.value!==0&&this.start.value!==0,\"[OpenSeadragon.Spring.shiftBy] spring value must be non-zero for exponential springs\");this.start._logValue=Math.log(this.start.value);this.target._logValue=Math.log(this.target.value);}},setExponential:function setExponential(value){this._exponential=value;if(this._exponential){$.console.assert(this.current.value!==0&&this.target.value!==0&&this.start.value!==0,\"[OpenSeadragon.Spring.setExponential] spring value must be non-zero for exponential springs\");this.start._logValue=Math.log(this.start.value);this.target._logValue=Math.log(this.target.value);this.current._logValue=Math.log(this.current.value);}},/**\n * @function\n * @returns true if the value got updated, false otherwise\n */update:function update(){this.current.time=$.now();var startValue,targetValue;if(this._exponential){startValue=this.start._logValue;targetValue=this.target._logValue;}else{startValue=this.start.value;targetValue=this.target.value;}var currentValue=this.current.time>=this.target.time?targetValue:startValue+(targetValue-startValue)*transform(this.springStiffness,(this.current.time-this.start.time)/(this.target.time-this.start.time));var oldValue=this.current.value;if(this._exponential){this.current.value=Math.exp(currentValue);}else{this.current.value=currentValue;}return oldValue!=this.current.value;},/**\n * Returns whether the spring is at the target value\n * @function\n * @returns {Boolean} True if at target value, false otherwise\n */isAtTargetValue:function isAtTargetValue(){return this.current.value===this.target.value;}};/**\n * @private\n */function transform(stiffness,x){return(1.0-Math.exp(stiffness*-x))/(1.0-Math.exp(-stiffness));}})(OpenSeadragon);/*\n * OpenSeadragon - ImageLoader\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * @private\n * @class ImageJob\n * @classdesc Handles downloading of a single image.\n * @param {Object} options - Options for this ImageJob.\n * @param {String} [options.src] - URL of image to download.\n * @param {String} [options.loadWithAjax] - Whether to load this image with AJAX.\n * @param {String} [options.ajaxHeaders] - Headers to add to the image request if using AJAX.\n * @param {String} [options.crossOriginPolicy] - CORS policy to use for downloads\n * @param {Function} [options.callback] - Called once image has been downloaded.\n * @param {Function} [options.abort] - Called when this image job is aborted.\n * @param {Number} [options.timeout] - The max number of milliseconds that this image job may take to complete.\n */function ImageJob(options){$.extend(true,this,{timeout:$.DEFAULT_SETTINGS.timeout,makeAjaxRequest:$.makeAjaxRequest,jobId:null},options);/**\n * Image object which will contain downloaded image.\n * @member {Image} image\n * @memberof OpenSeadragon.ImageJob#\n */this.image=null;}ImageJob.prototype={errorMsg:null,/**\n * Starts the image job.\n * @method\n */start:function start(){var self=this;var selfAbort=this.abort;this.image=new Image();this.image.onload=function(){self.finish(true);};this.image.onabort=this.image.onerror=function(){self.errorMsg=\"Image load aborted\";self.finish(false);};this.jobId=window.setTimeout(function(){self.errorMsg=\"Image load exceeded timeout\";self.finish(false);},this.timeout);// Load the tile with an AJAX request if the loadWithAjax option is\n// set. Otherwise load the image by setting the source proprety of the image object.\nif(this.loadWithAjax){this.request=self.makeAjaxRequest({url:this.src,withCredentials:this.ajaxWithCredentials,headers:this.ajaxHeaders,responseType:\"arraybuffer\",success:function success(request){var blb;// Make the raw data into a blob.\n// BlobBuilder fallback adapted from\n// http://stackoverflow.com/questions/15293694/blob-constructor-browser-compatibility\ntry{// Store the original response\nself.image._array=request.response;blb=new window.Blob([request.response]);}catch(e){var BlobBuilder=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder||window.MSBlobBuilder;if(e.name==='TypeError'&&BlobBuilder){var bb=new BlobBuilder();bb.append(request.response);blb=bb.getBlob();}}// If the blob is empty for some reason consider the image load a failure.\nif(blb===undefined||blb.size===0){self.errorMsg=\"Empty image response.\";self.finish(false);}// Create a URL for the blob data and make it the source of the image object.\n// This will still trigger Image.onload to indicate a successful tile load.\nvar url=(window.URL||window.webkitURL).createObjectURL(blb);self.image.src=url;},error:function error(request){self.errorMsg=\"Image load aborted - XHR error\";self.finish(false);}});// Provide a function to properly abort the request.\nthis.abort=function(){self.request.abort();// Call the existing abort function if available\nif(typeof selfAbort===\"function\"){selfAbort();}};}else{if(this.crossOriginPolicy!==false){this.image.crossOrigin=this.crossOriginPolicy;}this.image.src=this.src;}},finish:function finish(successful){this.image.onload=this.image.onerror=this.image.onabort=null;if(!successful){this.image=null;}if(this.jobId){window.clearTimeout(this.jobId);}this.callback(this);}};/**\n * @class ImageLoader\n * @memberof OpenSeadragon\n * @classdesc Handles downloading of a set of images using asynchronous queue pattern.\n * You generally won't have to interact with the ImageLoader directly.\n * @param {Object} options - Options for this ImageLoader.\n * @param {Number} [options.jobLimit] - The number of concurrent image requests. See imageLoaderLimit in {@link OpenSeadragon.Options} for details.\n * @param {Number} [options.timeout] - The max number of milliseconds that an image job may take to complete.\n */$.ImageLoader=function(options){$.extend(true,this,{jobLimit:$.DEFAULT_SETTINGS.imageLoaderLimit,timeout:$.DEFAULT_SETTINGS.timeout,jobQueue:[],jobsInProgress:0},options);};/** @lends OpenSeadragon.ImageLoader.prototype */$.ImageLoader.prototype={/**\n * Add an unloaded image to the loader queue.\n * @method\n * @param {Object} options - Options for this job.\n * @param {String} [options.src] - URL of image to download.\n * @param {String} [options.loadWithAjax] - Whether to load this image with AJAX.\n * @param {String} [options.ajaxHeaders] - Headers to add to the image request if using AJAX.\n * @param {String|Boolean} [options.crossOriginPolicy] - CORS policy to use for downloads\n * @param {Boolean} [options.ajaxWithCredentials] - Whether to set withCredentials on AJAX\n * requests.\n * @param {Function} [options.callback] - Called once image has been downloaded.\n * @param {Function} [options.abort] - Called when this image job is aborted.\n */addJob:function addJob(options){var _this=this,complete=function complete(job){completeJob(_this,job,options.callback);},jobOptions={src:options.src,loadWithAjax:options.loadWithAjax,ajaxHeaders:options.loadWithAjax?options.ajaxHeaders:null,crossOriginPolicy:options.crossOriginPolicy,ajaxWithCredentials:options.ajaxWithCredentials,makeAjaxRequest:options.makeAjaxRequest,callback:complete,abort:options.abort,timeout:this.timeout},newJob=new ImageJob(jobOptions);if(!this.jobLimit||this.jobsInProgress0){nextJob=loader.jobQueue.shift();nextJob.start();loader.jobsInProgress++;}callback(job.image,job.errorMsg,job.request);}})(OpenSeadragon);/*\n * OpenSeadragon - Tile\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * @class Tile\n * @memberof OpenSeadragon\n * @param {Number} level The zoom level this tile belongs to.\n * @param {Number} x The vector component 'x'.\n * @param {Number} y The vector component 'y'.\n * @param {OpenSeadragon.Rect} bounds Where this tile fits, in normalized\n * coordinates.\n * @param {Boolean} exists Is this tile a part of a sparse image? ( Also has\n * this tile failed to load? )\n * @param {String} url The URL of this tile's image.\n * @param {CanvasRenderingContext2D} context2D The context2D of this tile if it\n * is provided directly by the tile source.\n * @param {Boolean} loadWithAjax Whether this tile image should be loaded with an AJAX request .\n * @param {Object} ajaxHeaders The headers to send with this tile's AJAX request (if applicable).\n * @param {OpenSeadragon.Rect} sourceBounds The portion of the tile to use as the source of the\n * drawing operation, in pixels. Note that this only works when drawing with canvas; when drawing\n * with HTML the entire tile is always used.\n */$.Tile=function(level,x,y,bounds,exists,url,context2D,loadWithAjax,ajaxHeaders,sourceBounds){/**\n * The zoom level this tile belongs to.\n * @member {Number} level\n * @memberof OpenSeadragon.Tile#\n */this.level=level;/**\n * The vector component 'x'.\n * @member {Number} x\n * @memberof OpenSeadragon.Tile#\n */this.x=x;/**\n * The vector component 'y'.\n * @member {Number} y\n * @memberof OpenSeadragon.Tile#\n */this.y=y;/**\n * Where this tile fits, in normalized coordinates\n * @member {OpenSeadragon.Rect} bounds\n * @memberof OpenSeadragon.Tile#\n */this.bounds=bounds;/**\n * The portion of the tile to use as the source of the drawing operation, in pixels. Note that\n * this only works when drawing with canvas; when drawing with HTML the entire tile is always used.\n * @member {OpenSeadragon.Rect} sourceBounds\n * @memberof OpenSeadragon.Tile#\n */this.sourceBounds=sourceBounds;/**\n * Is this tile a part of a sparse image? Also has this tile failed to load?\n * @member {Boolean} exists\n * @memberof OpenSeadragon.Tile#\n */this.exists=exists;/**\n * The URL of this tile's image.\n * @member {String} url\n * @memberof OpenSeadragon.Tile#\n */this.url=url;/**\n * The context2D of this tile if it is provided directly by the tile source.\n * @member {CanvasRenderingContext2D} context2D\n * @memberOf OpenSeadragon.Tile#\n */this.context2D=context2D;/**\n * Whether to load this tile's image with an AJAX request.\n * @member {Boolean} loadWithAjax\n * @memberof OpenSeadragon.Tile#\n */this.loadWithAjax=loadWithAjax;/**\n * The headers to be used in requesting this tile's image.\n * Only used if loadWithAjax is set to true.\n * @member {Object} ajaxHeaders\n * @memberof OpenSeadragon.Tile#\n */this.ajaxHeaders=ajaxHeaders;/**\n * The unique cache key for this tile.\n * @member {String} cacheKey\n * @memberof OpenSeadragon.Tile#\n */if(this.ajaxHeaders){this.cacheKey=this.url+\"+\"+JSON.stringify(this.ajaxHeaders);}else{this.cacheKey=this.url;}/**\n * Is this tile loaded?\n * @member {Boolean} loaded\n * @memberof OpenSeadragon.Tile#\n */this.loaded=false;/**\n * Is this tile loading?\n * @member {Boolean} loading\n * @memberof OpenSeadragon.Tile#\n */this.loading=false;/**\n * The HTML div element for this tile\n * @member {Element} element\n * @memberof OpenSeadragon.Tile#\n */this.element=null;/**\n * The HTML img element for this tile.\n * @member {Element} imgElement\n * @memberof OpenSeadragon.Tile#\n */this.imgElement=null;/**\n * The Image object for this tile.\n * @member {Object} image\n * @memberof OpenSeadragon.Tile#\n */this.image=null;/**\n * The alias of this.element.style.\n * @member {String} style\n * @memberof OpenSeadragon.Tile#\n */this.style=null;/**\n * This tile's position on screen, in pixels.\n * @member {OpenSeadragon.Point} position\n * @memberof OpenSeadragon.Tile#\n */this.position=null;/**\n * This tile's size on screen, in pixels.\n * @member {OpenSeadragon.Point} size\n * @memberof OpenSeadragon.Tile#\n */this.size=null;/**\n * The start time of this tile's blending.\n * @member {Number} blendStart\n * @memberof OpenSeadragon.Tile#\n */this.blendStart=null;/**\n * The current opacity this tile should be.\n * @member {Number} opacity\n * @memberof OpenSeadragon.Tile#\n */this.opacity=null;/**\n * The squared distance of this tile to the viewport center.\n * Use for comparing tiles.\n * @private\n * @member {Number} squaredDistance\n * @memberof OpenSeadragon.Tile#\n */this.squaredDistance=null;/**\n * The visibility score of this tile.\n * @member {Number} visibility\n * @memberof OpenSeadragon.Tile#\n */this.visibility=null;/**\n * Whether this tile is currently being drawn.\n * @member {Boolean} beingDrawn\n * @memberof OpenSeadragon.Tile#\n */this.beingDrawn=false;/**\n * Timestamp the tile was last touched.\n * @member {Number} lastTouchTime\n * @memberof OpenSeadragon.Tile#\n */this.lastTouchTime=0;/**\n * Whether this tile is in the right-most column for its level.\n * @member {Boolean} isRightMost\n * @memberof OpenSeadragon.Tile#\n */this.isRightMost=false;/**\n * Whether this tile is in the bottom-most row for its level.\n * @member {Boolean} isBottomMost\n * @memberof OpenSeadragon.Tile#\n */this.isBottomMost=false;};/** @lends OpenSeadragon.Tile.prototype */$.Tile.prototype={/**\n * Provides a string representation of this tiles level and (x,y)\n * components.\n * @function\n * @returns {String}\n */toString:function toString(){return this.level+\"/\"+this.x+\"_\"+this.y;},// private\n_hasTransparencyChannel:function _hasTransparencyChannel(){return!!this.context2D||this.url.match('.png');},/**\n * Renders the tile in an html container.\n * @function\n * @param {Element} container\n */drawHTML:function drawHTML(container){if(!this.cacheImageRecord){$.console.warn('[Tile.drawHTML] attempting to draw tile %s when it\\'s not cached',this.toString());return;}if(!this.loaded){$.console.warn(\"Attempting to draw tile %s when it's not yet loaded.\",this.toString());return;}//EXPERIMENTAL - trying to figure out how to scale the container\n// content during animation of the container size.\nif(!this.element){this.element=$.makeNeutralElement(\"div\");this.imgElement=this.cacheImageRecord.getImage().cloneNode();this.imgElement.style.msInterpolationMode=\"nearest-neighbor\";this.imgElement.style.width=\"100%\";this.imgElement.style.height=\"100%\";this.style=this.element.style;this.style.position=\"absolute\";}if(this.element.parentNode!=container){container.appendChild(this.element);}if(this.imgElement.parentNode!=this.element){this.element.appendChild(this.imgElement);}this.style.top=this.position.y+\"px\";this.style.left=this.position.x+\"px\";this.style.height=this.size.y+\"px\";this.style.width=this.size.x+\"px\";$.setElementOpacity(this.element,this.opacity);},/**\n * Renders the tile in a canvas-based context.\n * @function\n * @param {Canvas} context\n * @param {Function} drawingHandler - Method for firing the drawing event.\n * drawingHandler({context, tile, rendered})\n * where rendered
is the context with the pre-drawn image.\n * @param {Number} [scale=1] - Apply a scale to position and size\n * @param {OpenSeadragon.Point} [translate] - A translation vector\n */drawCanvas:function drawCanvas(context,drawingHandler,scale,translate){var position=this.position.times($.pixelDensityRatio),size=this.size.times($.pixelDensityRatio),rendered;if(!this.context2D&&!this.cacheImageRecord){$.console.warn('[Tile.drawCanvas] attempting to draw tile %s when it\\'s not cached',this.toString());return;}rendered=this.context2D||this.cacheImageRecord.getRenderedContext();if(!this.loaded||!rendered){$.console.warn(\"Attempting to draw tile %s when it's not yet loaded.\",this.toString());return;}context.save();context.globalAlpha=this.opacity;if(typeof scale==='number'&&scale!==1){// draw tile at a different scale\nposition=position.times(scale);size=size.times(scale);}if(translate instanceof $.Point){// shift tile position slightly\nposition=position.plus(translate);}//if we are supposed to be rendering fully opaque rectangle,\n//ie its done fading or fading is turned off, and if we are drawing\n//an image with an alpha channel, then the only way\n//to avoid seeing the tile underneath is to clear the rectangle\nif(context.globalAlpha===1&&this._hasTransparencyChannel()){//clearing only the inside of the rectangle occupied\n//by the png prevents edge flikering\ncontext.clearRect(position.x,position.y,size.x,size.y);}// This gives the application a chance to make image manipulation\n// changes as we are rendering the image\ndrawingHandler({context:context,tile:this,rendered:rendered});var sourceWidth,sourceHeight;if(this.sourceBounds){sourceWidth=Math.min(this.sourceBounds.width,rendered.canvas.width);sourceHeight=Math.min(this.sourceBounds.height,rendered.canvas.height);}else{sourceWidth=rendered.canvas.width;sourceHeight=rendered.canvas.height;}context.drawImage(rendered.canvas,0,0,sourceWidth,sourceHeight,position.x,position.y,size.x,size.y);context.restore();},/**\n * Get the ratio between current and original size.\n * @function\n * @return {Float}\n */getScaleForEdgeSmoothing:function getScaleForEdgeSmoothing(){var context;if(this.cacheImageRecord){context=this.cacheImageRecord.getRenderedContext();}else if(this.context2D){context=this.context2D;}else{$.console.warn('[Tile.drawCanvas] attempting to get tile scale %s when tile\\'s not cached',this.toString());return 1;}return context.canvas.width/(this.size.x*$.pixelDensityRatio);},/**\n * Get a translation vector that when applied to the tile position produces integer coordinates.\n * Needed to avoid swimming and twitching.\n * @function\n * @param {Number} [scale=1] - Scale to be applied to position.\n * @return {OpenSeadragon.Point}\n */getTranslationForEdgeSmoothing:function getTranslationForEdgeSmoothing(scale,canvasSize,sketchCanvasSize){// The translation vector must have positive values, otherwise the image goes a bit off\n// the sketch canvas to the top and left and we must use negative coordinates to repaint it\n// to the main canvas. In that case, some browsers throw:\n// INDEX_SIZE_ERR: DOM Exception 1: Index or size was negative, or greater than the allowed value.\nvar x=Math.max(1,Math.ceil((sketchCanvasSize.x-canvasSize.x)/2));var y=Math.max(1,Math.ceil((sketchCanvasSize.y-canvasSize.y)/2));return new $.Point(x,y).minus(this.position.times($.pixelDensityRatio).times(scale||1).apply(function(x){return x%1;}));},/**\n * Removes tile from its container.\n * @function\n */unload:function unload(){if(this.imgElement&&this.imgElement.parentNode){this.imgElement.parentNode.removeChild(this.imgElement);}if(this.element&&this.element.parentNode){this.element.parentNode.removeChild(this.element);}this.element=null;this.imgElement=null;this.loaded=false;this.loading=false;}};})(OpenSeadragon);/*\n * OpenSeadragon - Overlay\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * An enumeration of positions that an overlay may be assigned relative to\n * the viewport.\n * It is identical to OpenSeadragon.Placement but is kept for backward\n * compatibility.\n * @member OverlayPlacement\n * @memberof OpenSeadragon\n * @see OpenSeadragon.Placement\n * @static\n * @readonly\n * @type {Object}\n * @property {Number} CENTER\n * @property {Number} TOP_LEFT\n * @property {Number} TOP\n * @property {Number} TOP_RIGHT\n * @property {Number} RIGHT\n * @property {Number} BOTTOM_RIGHT\n * @property {Number} BOTTOM\n * @property {Number} BOTTOM_LEFT\n * @property {Number} LEFT\n */$.OverlayPlacement=$.Placement;/**\n * An enumeration of possible ways to handle overlays rotation\n * @member OverlayRotationMode\n * @memberOf OpenSeadragon\n * @static\n * @readonly\n * @property {Number} NO_ROTATION The overlay ignore the viewport rotation.\n * @property {Number} EXACT The overlay use CSS 3 transforms to rotate with\n * the viewport. If the overlay contains text, it will get rotated as well.\n * @property {Number} BOUNDING_BOX The overlay adjusts for rotation by\n * taking the size of the bounding box of the rotated bounds.\n * Only valid for overlays with Rect location and scalable in both directions.\n */$.OverlayRotationMode=$.freezeObject({NO_ROTATION:1,EXACT:2,BOUNDING_BOX:3});/**\n * @class Overlay\n * @classdesc Provides a way to float an HTML element on top of the viewer element.\n *\n * @memberof OpenSeadragon\n * @param {Object} options\n * @param {Element} options.element\n * @param {OpenSeadragon.Point|OpenSeadragon.Rect} options.location - The\n * location of the overlay on the image. If a {@link OpenSeadragon.Point}\n * is specified, the overlay will be located at this location with respect\n * to the placement option. If a {@link OpenSeadragon.Rect} is specified,\n * the overlay will be placed at this location with the corresponding width\n * and height and placement TOP_LEFT.\n * @param {OpenSeadragon.Placement} [options.placement=OpenSeadragon.Placement.TOP_LEFT]\n * Defines what part of the overlay should be at the specified options.location\n * @param {OpenSeadragon.Overlay.OnDrawCallback} [options.onDraw]\n * @param {Boolean} [options.checkResize=true] Set to false to avoid to\n * check the size of the overlay everytime it is drawn in the directions\n * which are not scaled. It will improve performances but will cause a\n * misalignment if the overlay size changes.\n * @param {Number} [options.width] The width of the overlay in viewport\n * coordinates. If specified, the width of the overlay will be adjusted when\n * the zoom changes.\n * @param {Number} [options.height] The height of the overlay in viewport\n * coordinates. If specified, the height of the overlay will be adjusted when\n * the zoom changes.\n * @param {Boolean} [options.rotationMode=OpenSeadragon.OverlayRotationMode.EXACT]\n * How to handle the rotation of the viewport.\n */$.Overlay=function(element,location,placement){/**\n * onDraw callback signature used by {@link OpenSeadragon.Overlay}.\n *\n * @callback OnDrawCallback\n * @memberof OpenSeadragon.Overlay\n * @param {OpenSeadragon.Point} position\n * @param {OpenSeadragon.Point} size\n * @param {Element} element\n */var options;if($.isPlainObject(element)){options=element;}else{options={element:element,location:location,placement:placement};}this.element=options.element;this.style=options.element.style;this._init(options);};/** @lends OpenSeadragon.Overlay.prototype */$.Overlay.prototype={// private\n_init:function _init(options){this.location=options.location;this.placement=options.placement===undefined?$.Placement.TOP_LEFT:options.placement;this.onDraw=options.onDraw;this.checkResize=options.checkResize===undefined?true:options.checkResize;// When this.width is not null, the overlay get scaled horizontally\nthis.width=options.width===undefined?null:options.width;// When this.height is not null, the overlay get scaled vertically\nthis.height=options.height===undefined?null:options.height;this.rotationMode=options.rotationMode||$.OverlayRotationMode.EXACT;// Having a rect as location is a syntactic sugar\nif(this.location instanceof $.Rect){this.width=this.location.width;this.height=this.location.height;this.location=this.location.getTopLeft();this.placement=$.Placement.TOP_LEFT;}// Deprecated properties kept for backward compatibility.\nthis.scales=this.width!==null&&this.height!==null;this.bounds=new $.Rect(this.location.x,this.location.y,this.width,this.height);this.position=this.location;},/**\n * Internal function to adjust the position of an overlay\n * depending on it size and placement.\n * @function\n * @param {OpenSeadragon.Point} position\n * @param {OpenSeadragon.Point} size\n */adjust:function adjust(position,size){var properties=$.Placement.properties[this.placement];if(!properties){return;}if(properties.isHorizontallyCentered){position.x-=size.x/2;}else if(properties.isRight){position.x-=size.x;}if(properties.isVerticallyCentered){position.y-=size.y/2;}else if(properties.isBottom){position.y-=size.y;}},/**\n * @function\n */destroy:function destroy(){var element=this.element;var style=this.style;if(element.parentNode){element.parentNode.removeChild(element);//this should allow us to preserve overlays when required between\n//pages\nif(element.prevElementParent){style.display='none';//element.prevElementParent.insertBefore(\n// element,\n// element.prevNextSibling\n//);\ndocument.body.appendChild(element);}}// clear the onDraw callback\nthis.onDraw=null;style.top=\"\";style.left=\"\";style.position=\"\";if(this.width!==null){style.width=\"\";}if(this.height!==null){style.height=\"\";}var transformOriginProp=$.getCssPropertyWithVendorPrefix('transformOrigin');var transformProp=$.getCssPropertyWithVendorPrefix('transform');if(transformOriginProp&&transformProp){style[transformOriginProp]=\"\";style[transformProp]=\"\";}},/**\n * @function\n * @param {Element} container\n */drawHTML:function drawHTML(container,viewport){var element=this.element;if(element.parentNode!==container){//save the source parent for later if we need it\nelement.prevElementParent=element.parentNode;element.prevNextSibling=element.nextSibling;container.appendChild(element);// have to set position before calculating size, fix #1116\nthis.style.position=\"absolute\";// this.size is used by overlays which don't get scaled in at\n// least one direction when this.checkResize is set to false.\nthis.size=$.getElementSize(element);}var positionAndSize=this._getOverlayPositionAndSize(viewport);var position=positionAndSize.position;var size=this.size=positionAndSize.size;var rotate=positionAndSize.rotate;// call the onDraw callback if it exists to allow one to overwrite\n// the drawing/positioning/sizing of the overlay\nif(this.onDraw){this.onDraw(position,size,this.element);}else{var style=this.style;style.left=position.x+\"px\";style.top=position.y+\"px\";if(this.width!==null){style.width=size.x+\"px\";}if(this.height!==null){style.height=size.y+\"px\";}var transformOriginProp=$.getCssPropertyWithVendorPrefix('transformOrigin');var transformProp=$.getCssPropertyWithVendorPrefix('transform');if(transformOriginProp&&transformProp){if(rotate){style[transformOriginProp]=this._getTransformOrigin();style[transformProp]=\"rotate(\"+rotate+\"deg)\";}else{style[transformOriginProp]=\"\";style[transformProp]=\"\";}}if(style.display!=='none'){style.display='block';}}},// private\n_getOverlayPositionAndSize:function _getOverlayPositionAndSize(viewport){var position=viewport.pixelFromPoint(this.location,true);var size=this._getSizeInPixels(viewport);this.adjust(position,size);var rotate=0;if(viewport.degrees&&this.rotationMode!==$.OverlayRotationMode.NO_ROTATION){// BOUNDING_BOX is only valid if both directions get scaled.\n// Get replaced by EXACT otherwise.\nif(this.rotationMode===$.OverlayRotationMode.BOUNDING_BOX&&this.width!==null&&this.height!==null){var rect=new $.Rect(position.x,position.y,size.x,size.y);var boundingBox=this._getBoundingBox(rect,viewport.degrees);position=boundingBox.getTopLeft();size=boundingBox.getSize();}else{rotate=viewport.degrees;}}return{position:position,size:size,rotate:rotate};},// private\n_getSizeInPixels:function _getSizeInPixels(viewport){var width=this.size.x;var height=this.size.y;if(this.width!==null||this.height!==null){var scaledSize=viewport.deltaPixelsFromPointsNoRotate(new $.Point(this.width||0,this.height||0),true);if(this.width!==null){width=scaledSize.x;}if(this.height!==null){height=scaledSize.y;}}if(this.checkResize&&(this.width===null||this.height===null)){var eltSize=this.size=$.getElementSize(this.element);if(this.width===null){width=eltSize.x;}if(this.height===null){height=eltSize.y;}}return new $.Point(width,height);},// private\n_getBoundingBox:function _getBoundingBox(rect,degrees){var refPoint=this._getPlacementPoint(rect);return rect.rotate(degrees,refPoint).getBoundingBox();},// private\n_getPlacementPoint:function _getPlacementPoint(rect){var result=new $.Point(rect.x,rect.y);var properties=$.Placement.properties[this.placement];if(properties){if(properties.isHorizontallyCentered){result.x+=rect.width/2;}else if(properties.isRight){result.x+=rect.width;}if(properties.isVerticallyCentered){result.y+=rect.height/2;}else if(properties.isBottom){result.y+=rect.height;}}return result;},// private\n_getTransformOrigin:function _getTransformOrigin(){var result=\"\";var properties=$.Placement.properties[this.placement];if(!properties){return result;}if(properties.isLeft){result=\"left\";}else if(properties.isRight){result=\"right\";}if(properties.isTop){result+=\" top\";}else if(properties.isBottom){result+=\" bottom\";}return result;},/**\n * Changes the overlay settings.\n * @function\n * @param {OpenSeadragon.Point|OpenSeadragon.Rect|Object} location\n * If an object is specified, the options are the same than the constructor\n * except for the element which can not be changed.\n * @param {OpenSeadragon.Placement} placement\n */update:function update(location,placement){var options=$.isPlainObject(location)?location:{location:location,placement:placement};this._init({location:options.location||this.location,placement:options.placement!==undefined?options.placement:this.placement,onDraw:options.onDraw||this.onDraw,checkResize:options.checkResize||this.checkResize,width:options.width!==undefined?options.width:this.width,height:options.height!==undefined?options.height:this.height,rotationMode:options.rotationMode||this.rotationMode});},/**\n * Returns the current bounds of the overlay in viewport coordinates\n * @function\n * @param {OpenSeadragon.Viewport} viewport the viewport\n * @returns {OpenSeadragon.Rect} overlay bounds\n */getBounds:function getBounds(viewport){$.console.assert(viewport,'A viewport must now be passed to Overlay.getBounds.');var width=this.width;var height=this.height;if(width===null||height===null){var size=viewport.deltaPointsFromPixelsNoRotate(this.size,true);if(width===null){width=size.x;}if(height===null){height=size.y;}}var location=this.location.clone();this.adjust(location,new $.Point(width,height));return this._adjustBoundsForRotation(viewport,new $.Rect(location.x,location.y,width,height));},// private\n_adjustBoundsForRotation:function _adjustBoundsForRotation(viewport,bounds){if(!viewport||viewport.degrees===0||this.rotationMode===$.OverlayRotationMode.EXACT){return bounds;}if(this.rotationMode===$.OverlayRotationMode.BOUNDING_BOX){// If overlay not fully scalable, BOUNDING_BOX falls back to EXACT\nif(this.width===null||this.height===null){return bounds;}// It is easier to just compute the position and size and\n// convert to viewport coordinates.\nvar positionAndSize=this._getOverlayPositionAndSize(viewport);return viewport.viewerElementToViewportRectangle(new $.Rect(positionAndSize.position.x,positionAndSize.position.y,positionAndSize.size.x,positionAndSize.size.y));}// NO_ROTATION case\nreturn bounds.rotate(-viewport.degrees,this._getPlacementPoint(bounds));}};})(OpenSeadragon);/*\n * OpenSeadragon - Drawer\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * @class Drawer\n * @memberof OpenSeadragon\n * @classdesc Handles rendering of tiles for an {@link OpenSeadragon.Viewer}.\n * @param {Object} options - Options for this Drawer.\n * @param {OpenSeadragon.Viewer} options.viewer - The Viewer that owns this Drawer.\n * @param {OpenSeadragon.Viewport} options.viewport - Reference to Viewer viewport.\n * @param {Element} options.element - Parent element.\n * @param {Number} [options.debugGridColor] - See debugGridColor in {@link OpenSeadragon.Options} for details.\n */$.Drawer=function(options){$.console.assert(options.viewer,\"[Drawer] options.viewer is required\");//backward compatibility for positional args while prefering more\n//idiomatic javascript options object as the only argument\nvar args=arguments;if(!$.isPlainObject(options)){options={source:args[0],// Reference to Viewer tile source.\nviewport:args[1],// Reference to Viewer viewport.\nelement:args[2]// Parent element.\n};}$.console.assert(options.viewport,\"[Drawer] options.viewport is required\");$.console.assert(options.element,\"[Drawer] options.element is required\");if(options.source){$.console.error(\"[Drawer] options.source is no longer accepted; use TiledImage instead\");}this.viewer=options.viewer;this.viewport=options.viewport;this.debugGridColor=typeof options.debugGridColor==='string'?[options.debugGridColor]:options.debugGridColor||$.DEFAULT_SETTINGS.debugGridColor;if(options.opacity){$.console.error(\"[Drawer] options.opacity is no longer accepted; set the opacity on the TiledImage instead\");}this.useCanvas=$.supportsCanvas&&(this.viewer?this.viewer.useCanvas:true);/**\n * The parent element of this Drawer instance, passed in when the Drawer was created.\n * The parent of {@link OpenSeadragon.Drawer#canvas}.\n * @member {Element} container\n * @memberof OpenSeadragon.Drawer#\n */this.container=$.getElement(options.element);/**\n * A <canvas> element if the browser supports them, otherwise a <div> element.\n * Child element of {@link OpenSeadragon.Drawer#container}.\n * @member {Element} canvas\n * @memberof OpenSeadragon.Drawer#\n */this.canvas=$.makeNeutralElement(this.useCanvas?\"canvas\":\"div\");/**\n * 2d drawing context for {@link OpenSeadragon.Drawer#canvas} if it's a <canvas> element, otherwise null.\n * @member {Object} context\n * @memberof OpenSeadragon.Drawer#\n */this.context=this.useCanvas?this.canvas.getContext(\"2d\"):null;/**\n * Sketch canvas used to temporarily draw tiles which cannot be drawn directly\n * to the main canvas due to opacity. Lazily initialized.\n */this.sketchCanvas=null;this.sketchContext=null;/**\n * @member {Element} element\n * @memberof OpenSeadragon.Drawer#\n * @deprecated Alias for {@link OpenSeadragon.Drawer#container}.\n */this.element=this.container;// We force our container to ltr because our drawing math doesn't work in rtl.\n// This issue only affects our canvas renderer, but we do it always for consistency.\n// Note that this means overlays you want to be rtl need to be explicitly set to rtl.\nthis.container.dir='ltr';// check canvas available width and height, set canvas width and height such that the canvas backing store is set to the proper pixel density\nif(this.useCanvas){var viewportSize=this._calculateCanvasSize();this.canvas.width=viewportSize.x;this.canvas.height=viewportSize.y;}this.canvas.style.width=\"100%\";this.canvas.style.height=\"100%\";this.canvas.style.position=\"absolute\";$.setElementOpacity(this.canvas,this.opacity,true);// explicit left-align\nthis.container.style.textAlign=\"left\";this.container.appendChild(this.canvas);};/** @lends OpenSeadragon.Drawer.prototype */$.Drawer.prototype={// deprecated\naddOverlay:function addOverlay(element,location,placement,onDraw){$.console.error(\"drawer.addOverlay is deprecated. Use viewer.addOverlay instead.\");this.viewer.addOverlay(element,location,placement,onDraw);return this;},// deprecated\nupdateOverlay:function updateOverlay(element,location,placement){$.console.error(\"drawer.updateOverlay is deprecated. Use viewer.updateOverlay instead.\");this.viewer.updateOverlay(element,location,placement);return this;},// deprecated\nremoveOverlay:function removeOverlay(element){$.console.error(\"drawer.removeOverlay is deprecated. Use viewer.removeOverlay instead.\");this.viewer.removeOverlay(element);return this;},// deprecated\nclearOverlays:function clearOverlays(){$.console.error(\"drawer.clearOverlays is deprecated. Use viewer.clearOverlays instead.\");this.viewer.clearOverlays();return this;},/**\n * Set the opacity of the drawer.\n * @param {Number} opacity\n * @return {OpenSeadragon.Drawer} Chainable.\n */setOpacity:function setOpacity(opacity){$.console.error(\"drawer.setOpacity is deprecated. Use tiledImage.setOpacity instead.\");var world=this.viewer.world;for(var i=0;imaxOpacity){maxOpacity=opacity;}}return maxOpacity;},// deprecated\nneedsUpdate:function needsUpdate(){$.console.error(\"[Drawer.needsUpdate] this function is deprecated. Use World.needsDraw instead.\");return this.viewer.world.needsDraw();},// deprecated\nnumTilesLoaded:function numTilesLoaded(){$.console.error(\"[Drawer.numTilesLoaded] this function is deprecated. Use TileCache.numTilesLoaded instead.\");return this.viewer.tileCache.numTilesLoaded();},// deprecated\nreset:function reset(){$.console.error(\"[Drawer.reset] this function is deprecated. Use World.resetItems instead.\");this.viewer.world.resetItems();return this;},// deprecated\nupdate:function update(){$.console.error(\"[Drawer.update] this function is deprecated. Use Drawer.clear and World.draw instead.\");this.clear();this.viewer.world.draw();return this;},/**\n * @return {Boolean} True if rotation is supported.\n */canRotate:function canRotate(){return this.useCanvas;},/**\n * Destroy the drawer (unload current loaded tiles)\n */destroy:function destroy(){//force unloading of current canvas (1x1 will be gc later, trick not necessarily needed)\nthis.canvas.width=1;this.canvas.height=1;this.sketchCanvas=null;this.sketchContext=null;},/**\n * Clears the Drawer so it's ready to draw another frame.\n */clear:function clear(){this.canvas.innerHTML=\"\";if(this.useCanvas){var viewportSize=this._calculateCanvasSize();if(this.canvas.width!=viewportSize.x||this.canvas.height!=viewportSize.y){this.canvas.width=viewportSize.x;this.canvas.height=viewportSize.y;if(this.sketchCanvas!==null){var sketchCanvasSize=this._calculateSketchCanvasSize();this.sketchCanvas.width=sketchCanvasSize.x;this.sketchCanvas.height=sketchCanvasSize.y;}}this._clear();}},_clear:function _clear(useSketch,bounds){if(!this.useCanvas){return;}var context=this._getContext(useSketch);if(bounds){context.clearRect(bounds.x,bounds.y,bounds.width,bounds.height);}else{var canvas=context.canvas;context.clearRect(0,0,canvas.width,canvas.height);}},/**\n * Scale from OpenSeadragon viewer rectangle to drawer rectangle\n * (ignoring rotation)\n * @param {OpenSeadragon.Rect} rectangle - The rectangle in viewport coordinate system.\n * @return {OpenSeadragon.Rect} Rectangle in drawer coordinate system.\n */viewportToDrawerRectangle:function viewportToDrawerRectangle(rectangle){var topLeft=this.viewport.pixelFromPointNoRotate(rectangle.getTopLeft(),true);var size=this.viewport.deltaPixelsFromPointsNoRotate(rectangle.getSize(),true);return new $.Rect(topLeft.x*$.pixelDensityRatio,topLeft.y*$.pixelDensityRatio,size.x*$.pixelDensityRatio,size.y*$.pixelDensityRatio);},/**\n * Draws the given tile.\n * @param {OpenSeadragon.Tile} tile - The tile to draw.\n * @param {Function} drawingHandler - Method for firing the drawing event if using canvas.\n * drawingHandler({context, tile, rendered})\n * @param {Boolean} useSketch - Whether to use the sketch canvas or not.\n * where rendered
is the context with the pre-drawn image.\n * @param {Float} [scale=1] - Apply a scale to tile position and size. Defaults to 1.\n * @param {OpenSeadragon.Point} [translate] A translation vector to offset tile position\n */drawTile:function drawTile(tile,drawingHandler,useSketch,scale,translate){$.console.assert(tile,'[Drawer.drawTile] tile is required');$.console.assert(drawingHandler,'[Drawer.drawTile] drawingHandler is required');if(this.useCanvas){var context=this._getContext(useSketch);scale=scale||1;tile.drawCanvas(context,drawingHandler,scale,translate);}else{tile.drawHTML(this.canvas);}},_getContext:function _getContext(useSketch){var context=this.context;if(useSketch){if(this.sketchCanvas===null){this.sketchCanvas=document.createElement(\"canvas\");var sketchCanvasSize=this._calculateSketchCanvasSize();this.sketchCanvas.width=sketchCanvasSize.x;this.sketchCanvas.height=sketchCanvasSize.y;this.sketchContext=this.sketchCanvas.getContext(\"2d\");// If the viewport is not currently rotated, the sketchCanvas\n// will have the same size as the main canvas. However, if\n// the viewport get rotated later on, we will need to resize it.\nif(this.viewport.getRotation()===0){var self=this;this.viewer.addHandler('rotate',function resizeSketchCanvas(){if(self.viewport.getRotation()===0){return;}self.viewer.removeHandler('rotate',resizeSketchCanvas);var sketchCanvasSize=self._calculateSketchCanvasSize();self.sketchCanvas.width=sketchCanvasSize.x;self.sketchCanvas.height=sketchCanvasSize.y;});}}context=this.sketchContext;}return context;},// private\nsaveContext:function saveContext(useSketch){if(!this.useCanvas){return;}this._getContext(useSketch).save();},// private\nrestoreContext:function restoreContext(useSketch){if(!this.useCanvas){return;}this._getContext(useSketch).restore();},// private\nsetClip:function setClip(rect,useSketch){if(!this.useCanvas){return;}var context=this._getContext(useSketch);context.beginPath();context.rect(rect.x,rect.y,rect.width,rect.height);context.clip();},// private\ndrawRectangle:function drawRectangle(rect,fillStyle,useSketch){if(!this.useCanvas){return;}var context=this._getContext(useSketch);context.save();context.fillStyle=fillStyle;context.fillRect(rect.x,rect.y,rect.width,rect.height);context.restore();},/**\n * Blends the sketch canvas in the main canvas.\n * @param {Object} options The options\n * @param {Float} options.opacity The opacity of the blending.\n * @param {Float} [options.scale=1] The scale at which tiles were drawn on\n * the sketch. Default is 1.\n * Use scale to draw at a lower scale and then enlarge onto the main canvas.\n * @param {OpenSeadragon.Point} [options.translate] A translation vector\n * that was used to draw the tiles\n * @param {String} [options.compositeOperation] - How the image is\n * composited onto other images; see compositeOperation in\n * {@link OpenSeadragon.Options} for possible values.\n * @param {OpenSeadragon.Rect} [options.bounds] The part of the sketch\n * canvas to blend in the main canvas. If specified, options.scale and\n * options.translate get ignored.\n */blendSketch:function blendSketch(opacity,scale,translate,compositeOperation){var options=opacity;if(!$.isPlainObject(options)){options={opacity:opacity,scale:scale,translate:translate,compositeOperation:compositeOperation};}if(!this.useCanvas||!this.sketchCanvas){return;}opacity=options.opacity;compositeOperation=options.compositeOperation;var bounds=options.bounds;this.context.save();this.context.globalAlpha=opacity;if(compositeOperation){this.context.globalCompositeOperation=compositeOperation;}if(bounds){// Internet Explorer, Microsoft Edge, and Safari have problems\n// when you call context.drawImage with negative x or y\n// or x + width or y + height greater than the canvas width or height respectively.\nif(bounds.x<0){bounds.width+=bounds.x;bounds.x=0;}if(bounds.x+bounds.width>this.canvas.width){bounds.width=this.canvas.width-bounds.x;}if(bounds.y<0){bounds.height+=bounds.y;bounds.y=0;}if(bounds.y+bounds.height>this.canvas.height){bounds.height=this.canvas.height-bounds.y;}this.context.drawImage(this.sketchCanvas,bounds.x,bounds.y,bounds.width,bounds.height,bounds.x,bounds.y,bounds.width,bounds.height);}else{scale=options.scale||1;translate=options.translate;var position=translate instanceof $.Point?translate:new $.Point(0,0);var widthExt=0;var heightExt=0;if(translate){var widthDiff=this.sketchCanvas.width-this.canvas.width;var heightDiff=this.sketchCanvas.height-this.canvas.height;widthExt=Math.round(widthDiff/2);heightExt=Math.round(heightDiff/2);}this.context.drawImage(this.sketchCanvas,position.x-widthExt*scale,position.y-heightExt*scale,(this.canvas.width+2*widthExt)*scale,(this.canvas.height+2*heightExt)*scale,-widthExt,-heightExt,this.canvas.width+2*widthExt,this.canvas.height+2*heightExt);}this.context.restore();},// private\ndrawDebugInfo:function drawDebugInfo(tile,count,i,tiledImage){if(!this.useCanvas){return;}var colorIndex=this.viewer.world.getIndexOfItem(tiledImage)%this.debugGridColor.length;var context=this.context;context.save();context.lineWidth=2*$.pixelDensityRatio;context.font='small-caps bold '+13*$.pixelDensityRatio+'px arial';context.strokeStyle=this.debugGridColor[colorIndex];context.fillStyle=this.debugGridColor[colorIndex];if(this.viewport.degrees!==0){this._offsetForRotation({degrees:this.viewport.degrees});}else{if(this.viewer.viewport.flipped){this._flip();}}if(tiledImage.getRotation(true)%360!==0){this._offsetForRotation({degrees:tiledImage.getRotation(true),point:tiledImage.viewport.pixelFromPointNoRotate(tiledImage._getRotationPoint(true),true)});}context.strokeRect(tile.position.x*$.pixelDensityRatio,tile.position.y*$.pixelDensityRatio,tile.size.x*$.pixelDensityRatio,tile.size.y*$.pixelDensityRatio);var tileCenterX=(tile.position.x+tile.size.x/2)*$.pixelDensityRatio;var tileCenterY=(tile.position.y+tile.size.y/2)*$.pixelDensityRatio;// Rotate the text the right way around.\ncontext.translate(tileCenterX,tileCenterY);context.rotate(Math.PI/180*-this.viewport.degrees);context.translate(-tileCenterX,-tileCenterY);if(tile.x===0&&tile.y===0){context.fillText(\"Zoom: \"+this.viewport.getZoom(),tile.position.x*$.pixelDensityRatio,(tile.position.y-30)*$.pixelDensityRatio);context.fillText(\"Pan: \"+this.viewport.getBounds().toString(),tile.position.x*$.pixelDensityRatio,(tile.position.y-20)*$.pixelDensityRatio);}context.fillText(\"Level: \"+tile.level,(tile.position.x+10)*$.pixelDensityRatio,(tile.position.y+20)*$.pixelDensityRatio);context.fillText(\"Column: \"+tile.x,(tile.position.x+10)*$.pixelDensityRatio,(tile.position.y+30)*$.pixelDensityRatio);context.fillText(\"Row: \"+tile.y,(tile.position.x+10)*$.pixelDensityRatio,(tile.position.y+40)*$.pixelDensityRatio);context.fillText(\"Order: \"+i+\" of \"+count,(tile.position.x+10)*$.pixelDensityRatio,(tile.position.y+50)*$.pixelDensityRatio);context.fillText(\"Size: \"+tile.size.toString(),(tile.position.x+10)*$.pixelDensityRatio,(tile.position.y+60)*$.pixelDensityRatio);context.fillText(\"Position: \"+tile.position.toString(),(tile.position.x+10)*$.pixelDensityRatio,(tile.position.y+70)*$.pixelDensityRatio);if(this.viewport.degrees!==0){this._restoreRotationChanges();}if(tiledImage.getRotation(true)%360!==0){this._restoreRotationChanges();}context.restore();},// private\ndebugRect:function debugRect(rect){if(this.useCanvas){var context=this.context;context.save();context.lineWidth=2*$.pixelDensityRatio;context.strokeStyle=this.debugGridColor[0];context.fillStyle=this.debugGridColor[0];context.strokeRect(rect.x*$.pixelDensityRatio,rect.y*$.pixelDensityRatio,rect.width*$.pixelDensityRatio,rect.height*$.pixelDensityRatio);context.restore();}},/**\n * Get the canvas size\n * @param {Boolean} sketch If set to true return the size of the sketch canvas\n * @returns {OpenSeadragon.Point} The size of the canvas\n */getCanvasSize:function getCanvasSize(sketch){var canvas=this._getContext(sketch).canvas;return new $.Point(canvas.width,canvas.height);},getCanvasCenter:function getCanvasCenter(){return new $.Point(this.canvas.width/2,this.canvas.height/2);},// private\n_offsetForRotation:function _offsetForRotation(options){var point=options.point?options.point.times($.pixelDensityRatio):this.getCanvasCenter();var context=this._getContext(options.useSketch);context.save();context.translate(point.x,point.y);if(this.viewer.viewport.flipped){context.rotate(Math.PI/180*-options.degrees);context.scale(-1,1);}else{context.rotate(Math.PI/180*options.degrees);}context.translate(-point.x,-point.y);},// private\n_flip:function _flip(options){options=options||{};var point=options.point?options.point.times($.pixelDensityRatio):this.getCanvasCenter();var context=this._getContext(options.useSketch);context.translate(point.x,0);context.scale(-1,1);context.translate(-point.x,0);},// private\n_restoreRotationChanges:function _restoreRotationChanges(useSketch){var context=this._getContext(useSketch);context.restore();},// private\n_calculateCanvasSize:function _calculateCanvasSize(){var pixelDensityRatio=$.pixelDensityRatio;var viewportSize=this.viewport.getContainerSize();return{x:viewportSize.x*pixelDensityRatio,y:viewportSize.y*pixelDensityRatio};},// private\n_calculateSketchCanvasSize:function _calculateSketchCanvasSize(){var canvasSize=this._calculateCanvasSize();if(this.viewport.getRotation()===0){return canvasSize;}// If the viewport is rotated, we need a larger sketch canvas in order\n// to support edge smoothing.\nvar sketchCanvasSize=Math.ceil(Math.sqrt(canvasSize.x*canvasSize.x+canvasSize.y*canvasSize.y));return{x:sketchCanvasSize,y:sketchCanvasSize};}};})(OpenSeadragon);/*\n * OpenSeadragon - Viewport\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * @class Viewport\n * @memberof OpenSeadragon\n * @classdesc Handles coordinate-related functionality (zoom, pan, rotation, etc.)\n * for an {@link OpenSeadragon.Viewer}.\n * @param {Object} options - Options for this Viewport.\n * @param {Object} [options.margins] - See viewportMargins in {@link OpenSeadragon.Options}.\n * @param {Number} [options.springStiffness] - See springStiffness in {@link OpenSeadragon.Options}.\n * @param {Number} [options.animationTime] - See animationTime in {@link OpenSeadragon.Options}.\n * @param {Number} [options.minZoomImageRatio] - See minZoomImageRatio in {@link OpenSeadragon.Options}.\n * @param {Number} [options.maxZoomPixelRatio] - See maxZoomPixelRatio in {@link OpenSeadragon.Options}.\n * @param {Number} [options.visibilityRatio] - See visibilityRatio in {@link OpenSeadragon.Options}.\n * @param {Boolean} [options.wrapHorizontal] - See wrapHorizontal in {@link OpenSeadragon.Options}.\n * @param {Boolean} [options.wrapVertical] - See wrapVertical in {@link OpenSeadragon.Options}.\n * @param {Number} [options.defaultZoomLevel] - See defaultZoomLevel in {@link OpenSeadragon.Options}.\n * @param {Number} [options.minZoomLevel] - See minZoomLevel in {@link OpenSeadragon.Options}.\n * @param {Number} [options.maxZoomLevel] - See maxZoomLevel in {@link OpenSeadragon.Options}.\n * @param {Number} [options.degrees] - See degrees in {@link OpenSeadragon.Options}.\n * @param {Boolean} [options.homeFillsViewer] - See homeFillsViewer in {@link OpenSeadragon.Options}.\n */$.Viewport=function(options){//backward compatibility for positional args while prefering more\n//idiomatic javascript options object as the only argument\nvar args=arguments;if(args.length&&args[0]instanceof $.Point){options={containerSize:args[0],contentSize:args[1],config:args[2]};}//options.config and the general config argument are deprecated\n//in favor of the more direct specification of optional settings\n//being passed directly on the options object\nif(options.config){$.extend(true,options,options.config);delete options.config;}this._margins=$.extend({left:0,top:0,right:0,bottom:0},options.margins||{});delete options.margins;$.extend(true,this,{//required settings\ncontainerSize:null,contentSize:null,//internal state properties\nzoomPoint:null,viewer:null,//configurable options\nspringStiffness:$.DEFAULT_SETTINGS.springStiffness,animationTime:$.DEFAULT_SETTINGS.animationTime,minZoomImageRatio:$.DEFAULT_SETTINGS.minZoomImageRatio,maxZoomPixelRatio:$.DEFAULT_SETTINGS.maxZoomPixelRatio,visibilityRatio:$.DEFAULT_SETTINGS.visibilityRatio,wrapHorizontal:$.DEFAULT_SETTINGS.wrapHorizontal,wrapVertical:$.DEFAULT_SETTINGS.wrapVertical,defaultZoomLevel:$.DEFAULT_SETTINGS.defaultZoomLevel,minZoomLevel:$.DEFAULT_SETTINGS.minZoomLevel,maxZoomLevel:$.DEFAULT_SETTINGS.maxZoomLevel,degrees:$.DEFAULT_SETTINGS.degrees,flipped:$.DEFAULT_SETTINGS.flipped,homeFillsViewer:$.DEFAULT_SETTINGS.homeFillsViewer},options);this._updateContainerInnerSize();this.centerSpringX=new $.Spring({initial:0,springStiffness:this.springStiffness,animationTime:this.animationTime});this.centerSpringY=new $.Spring({initial:0,springStiffness:this.springStiffness,animationTime:this.animationTime});this.zoomSpring=new $.Spring({exponential:true,initial:1,springStiffness:this.springStiffness,animationTime:this.animationTime});this._oldCenterX=this.centerSpringX.current.value;this._oldCenterY=this.centerSpringY.current.value;this._oldZoom=this.zoomSpring.current.value;this._setContentBounds(new $.Rect(0,0,1,1),1);this.goHome(true);this.update();};/** @lends OpenSeadragon.Viewport.prototype */$.Viewport.prototype={/**\n * Updates the viewport's home bounds and constraints for the given content size.\n * @function\n * @param {OpenSeadragon.Point} contentSize - size of the content in content units\n * @return {OpenSeadragon.Viewport} Chainable.\n * @fires OpenSeadragon.Viewer.event:reset-size\n */resetContentSize:function resetContentSize(contentSize){$.console.assert(contentSize,\"[Viewport.resetContentSize] contentSize is required\");$.console.assert(contentSize instanceof $.Point,\"[Viewport.resetContentSize] contentSize must be an OpenSeadragon.Point\");$.console.assert(contentSize.x>0,\"[Viewport.resetContentSize] contentSize.x must be greater than 0\");$.console.assert(contentSize.y>0,\"[Viewport.resetContentSize] contentSize.y must be greater than 0\");this._setContentBounds(new $.Rect(0,0,1,contentSize.y/contentSize.x),contentSize.x);return this;},// deprecated\nsetHomeBounds:function setHomeBounds(bounds,contentFactor){$.console.error(\"[Viewport.setHomeBounds] this function is deprecated; The content bounds should not be set manually.\");this._setContentBounds(bounds,contentFactor);},// Set the viewport's content bounds\n// @param {OpenSeadragon.Rect} bounds - the new bounds in viewport coordinates\n// without rotation\n// @param {Number} contentFactor - how many content units per viewport unit\n// @fires OpenSeadragon.Viewer.event:reset-size\n// @private\n_setContentBounds:function _setContentBounds(bounds,contentFactor){$.console.assert(bounds,\"[Viewport._setContentBounds] bounds is required\");$.console.assert(bounds instanceof $.Rect,\"[Viewport._setContentBounds] bounds must be an OpenSeadragon.Rect\");$.console.assert(bounds.width>0,\"[Viewport._setContentBounds] bounds.width must be greater than 0\");$.console.assert(bounds.height>0,\"[Viewport._setContentBounds] bounds.height must be greater than 0\");this._contentBoundsNoRotate=bounds.clone();this._contentSizeNoRotate=this._contentBoundsNoRotate.getSize().times(contentFactor);this._contentBounds=bounds.rotate(this.degrees).getBoundingBox();this._contentSize=this._contentBounds.getSize().times(contentFactor);this._contentAspectRatio=this._contentSize.x/this._contentSize.y;if(this.viewer){/**\n * Raised when the viewer's content size or home bounds are reset\n * (see {@link OpenSeadragon.Viewport#resetContentSize}).\n *\n * @event reset-size\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.Point} contentSize\n * @property {OpenSeadragon.Rect} contentBounds - Content bounds.\n * @property {OpenSeadragon.Rect} homeBounds - Content bounds.\n * Deprecated use contentBounds instead.\n * @property {Number} contentFactor\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.viewer.raiseEvent('reset-size',{contentSize:this._contentSizeNoRotate.clone(),contentFactor:contentFactor,homeBounds:this._contentBoundsNoRotate.clone(),contentBounds:this._contentBounds.clone()});}},/**\n * Returns the home zoom in \"viewport zoom\" value.\n * @function\n * @returns {Number} The home zoom in \"viewport zoom\".\n */getHomeZoom:function getHomeZoom(){if(this.defaultZoomLevel){return this.defaultZoomLevel;}var aspectFactor=this._contentAspectRatio/this.getAspectRatio();var output;if(this.homeFillsViewer){// fill the viewer and clip the image\noutput=aspectFactor>=1?aspectFactor:1;}else{output=aspectFactor>=1?1:aspectFactor;}return output/this._contentBounds.width;},/**\n * Returns the home bounds in viewport coordinates.\n * @function\n * @returns {OpenSeadragon.Rect} The home bounds in vewport coordinates.\n */getHomeBounds:function getHomeBounds(){return this.getHomeBoundsNoRotate().rotate(-this.getRotation());},/**\n * Returns the home bounds in viewport coordinates.\n * This method ignores the viewport rotation. Use\n * {@link OpenSeadragon.Viewport#getHomeBounds} to take it into account.\n * @function\n * @returns {OpenSeadragon.Rect} The home bounds in vewport coordinates.\n */getHomeBoundsNoRotate:function getHomeBoundsNoRotate(){var center=this._contentBounds.getCenter();var width=1.0/this.getHomeZoom();var height=width/this.getAspectRatio();return new $.Rect(center.x-width/2.0,center.y-height/2.0,width,height);},/**\n * @function\n * @param {Boolean} immediately\n * @fires OpenSeadragon.Viewer.event:home\n */goHome:function goHome(immediately){if(this.viewer){/**\n * Raised when the \"home\" operation occurs (see {@link OpenSeadragon.Viewport#goHome}).\n *\n * @event home\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {Boolean} immediately\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.viewer.raiseEvent('home',{immediately:immediately});}return this.fitBounds(this.getHomeBounds(),immediately);},/**\n * @function\n */getMinZoom:function getMinZoom(){var homeZoom=this.getHomeZoom(),zoom=this.minZoomLevel?this.minZoomLevel:this.minZoomImageRatio*homeZoom;return zoom;},/**\n * @function\n */getMaxZoom:function getMaxZoom(){var zoom=this.maxZoomLevel;if(!zoom){zoom=this._contentSize.x*this.maxZoomPixelRatio/this._containerInnerSize.x;zoom/=this._contentBounds.width;}return Math.max(zoom,this.getHomeZoom());},/**\n * @function\n */getAspectRatio:function getAspectRatio(){return this._containerInnerSize.x/this._containerInnerSize.y;},/**\n * @function\n * @returns {OpenSeadragon.Point} The size of the container, in screen coordinates.\n */getContainerSize:function getContainerSize(){return new $.Point(this.containerSize.x,this.containerSize.y);},/**\n * The margins push the \"home\" region in from the sides by the specified amounts.\n * @function\n * @returns {Object} Properties (Numbers, in screen coordinates): left, top, right, bottom.\n */getMargins:function getMargins(){return $.extend({},this._margins);// Make a copy so we are not returning our original\n},/**\n * The margins push the \"home\" region in from the sides by the specified amounts.\n * @function\n * @param {Object} margins - Properties (Numbers, in screen coordinates): left, top, right, bottom.\n */setMargins:function setMargins(margins){$.console.assert($.type(margins)==='object','[Viewport.setMargins] margins must be an object');this._margins=$.extend({left:0,top:0,right:0,bottom:0},margins);this._updateContainerInnerSize();if(this.viewer){this.viewer.forceRedraw();}},/**\n * Returns the bounds of the visible area in viewport coordinates.\n * @function\n * @param {Boolean} current - Pass true for the current location; defaults to false (target location).\n * @returns {OpenSeadragon.Rect} The location you are zoomed/panned to, in viewport coordinates.\n */getBounds:function getBounds(current){return this.getBoundsNoRotate(current).rotate(-this.getRotation());},/**\n * Returns the bounds of the visible area in viewport coordinates.\n * This method ignores the viewport rotation. Use\n * {@link OpenSeadragon.Viewport#getBounds} to take it into account.\n * @function\n * @param {Boolean} current - Pass true for the current location; defaults to false (target location).\n * @returns {OpenSeadragon.Rect} The location you are zoomed/panned to, in viewport coordinates.\n */getBoundsNoRotate:function getBoundsNoRotate(current){var center=this.getCenter(current);var width=1.0/this.getZoom(current);var height=width/this.getAspectRatio();return new $.Rect(center.x-width/2.0,center.y-height/2.0,width,height);},/**\n * @function\n * @param {Boolean} current - Pass true for the current location; defaults to false (target location).\n * @returns {OpenSeadragon.Rect} The location you are zoomed/panned to,\n * including the space taken by margins, in viewport coordinates.\n */getBoundsWithMargins:function getBoundsWithMargins(current){return this.getBoundsNoRotateWithMargins(current).rotate(-this.getRotation(),this.getCenter(current));},/**\n * @function\n * @param {Boolean} current - Pass true for the current location; defaults to false (target location).\n * @returns {OpenSeadragon.Rect} The location you are zoomed/panned to,\n * including the space taken by margins, in viewport coordinates.\n */getBoundsNoRotateWithMargins:function getBoundsNoRotateWithMargins(current){var bounds=this.getBoundsNoRotate(current);var factor=this._containerInnerSize.x*this.getZoom(current);bounds.x-=this._margins.left/factor;bounds.y-=this._margins.top/factor;bounds.width+=(this._margins.left+this._margins.right)/factor;bounds.height+=(this._margins.top+this._margins.bottom)/factor;return bounds;},/**\n * @function\n * @param {Boolean} current - Pass true for the current location; defaults to false (target location).\n */getCenter:function getCenter(current){var centerCurrent=new $.Point(this.centerSpringX.current.value,this.centerSpringY.current.value),centerTarget=new $.Point(this.centerSpringX.target.value,this.centerSpringY.target.value),oldZoomPixel,zoom,width,height,bounds,newZoomPixel,deltaZoomPixels,deltaZoomPoints;if(current){return centerCurrent;}else if(!this.zoomPoint){return centerTarget;}oldZoomPixel=this.pixelFromPoint(this.zoomPoint,true);zoom=this.getZoom();width=1.0/zoom;height=width/this.getAspectRatio();bounds=new $.Rect(centerCurrent.x-width/2.0,centerCurrent.y-height/2.0,width,height);newZoomPixel=this._pixelFromPoint(this.zoomPoint,bounds);deltaZoomPixels=newZoomPixel.minus(oldZoomPixel);deltaZoomPoints=deltaZoomPixels.divide(this._containerInnerSize.x*zoom);return centerTarget.plus(deltaZoomPoints);},/**\n * @function\n * @param {Boolean} current - Pass true for the current location; defaults to false (target location).\n */getZoom:function getZoom(current){if(current){return this.zoomSpring.current.value;}else{return this.zoomSpring.target.value;}},// private\n_applyZoomConstraints:function _applyZoomConstraints(zoom){return Math.max(Math.min(zoom,this.getMaxZoom()),this.getMinZoom());},/**\n * @function\n * @private\n * @param {OpenSeadragon.Rect} bounds\n * @return {OpenSeadragon.Rect} constrained bounds.\n */_applyBoundaryConstraints:function _applyBoundaryConstraints(bounds){var newBounds=new $.Rect(bounds.x,bounds.y,bounds.width,bounds.height);if(this.wrapHorizontal){//do nothing\n}else{var horizontalThreshold=this.visibilityRatio*newBounds.width;var boundsRight=newBounds.x+newBounds.width;var contentRight=this._contentBoundsNoRotate.x+this._contentBoundsNoRotate.width;var leftDx=this._contentBoundsNoRotate.x-boundsRight+horizontalThreshold;var rightDx=contentRight-newBounds.x-horizontalThreshold;if(horizontalThreshold>this._contentBoundsNoRotate.width){newBounds.x+=(leftDx+rightDx)/2;}else if(rightDx<0){newBounds.x+=rightDx;}else if(leftDx>0){newBounds.x+=leftDx;}}if(this.wrapVertical){//do nothing\n}else{var verticalThreshold=this.visibilityRatio*newBounds.height;var boundsBottom=newBounds.y+newBounds.height;var contentBottom=this._contentBoundsNoRotate.y+this._contentBoundsNoRotate.height;var topDy=this._contentBoundsNoRotate.y-boundsBottom+verticalThreshold;var bottomDy=contentBottom-newBounds.y-verticalThreshold;if(verticalThreshold>this._contentBoundsNoRotate.height){newBounds.y+=(topDy+bottomDy)/2;}else if(bottomDy<0){newBounds.y+=bottomDy;}else if(topDy>0){newBounds.y+=topDy;}}return newBounds;},/**\n * @function\n * @private\n * @param {Boolean} [immediately=false] - whether the function that triggered this event was\n * called with the \"immediately\" flag\n */_raiseConstraintsEvent:function _raiseConstraintsEvent(immediately){if(this.viewer){/**\n * Raised when the viewport constraints are applied (see {@link OpenSeadragon.Viewport#applyConstraints}).\n *\n * @event constrain\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {Boolean} immediately - whether the function that triggered this event was\n * called with the \"immediately\" flag\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.viewer.raiseEvent('constrain',{immediately:immediately});}},/**\n * Enforces the minZoom, maxZoom and visibilityRatio constraints by\n * zooming and panning to the closest acceptable zoom and location.\n * @function\n * @param {Boolean} [immediately=false]\n * @return {OpenSeadragon.Viewport} Chainable.\n * @fires OpenSeadragon.Viewer.event:constrain\n */applyConstraints:function applyConstraints(immediately){var actualZoom=this.getZoom();var constrainedZoom=this._applyZoomConstraints(actualZoom);if(actualZoom!==constrainedZoom){this.zoomTo(constrainedZoom,this.zoomPoint,immediately);}var bounds=this.getBoundsNoRotate();var constrainedBounds=this._applyBoundaryConstraints(bounds);this._raiseConstraintsEvent(immediately);if(bounds.x!==constrainedBounds.x||bounds.y!==constrainedBounds.y||immediately){this.fitBounds(constrainedBounds.rotate(-this.getRotation()),immediately);}return this;},/**\n * Equivalent to {@link OpenSeadragon.Viewport#applyConstraints}\n * @function\n * @param {Boolean} [immediately=false]\n * @return {OpenSeadragon.Viewport} Chainable.\n * @fires OpenSeadragon.Viewer.event:constrain\n */ensureVisible:function ensureVisible(immediately){return this.applyConstraints(immediately);},/**\n * @function\n * @private\n * @param {OpenSeadragon.Rect} bounds\n * @param {Object} options (immediately=false, constraints=false)\n * @return {OpenSeadragon.Viewport} Chainable.\n */_fitBounds:function _fitBounds(bounds,options){options=options||{};var immediately=options.immediately||false;var constraints=options.constraints||false;var aspect=this.getAspectRatio();var center=bounds.getCenter();// Compute width and height of bounding box.\nvar newBounds=new $.Rect(bounds.x,bounds.y,bounds.width,bounds.height,bounds.degrees+this.getRotation()).getBoundingBox();if(newBounds.getAspectRatio()>=aspect){newBounds.height=newBounds.width/aspect;}else{newBounds.width=newBounds.height*aspect;}// Compute x and y from width, height and center position\nnewBounds.x=center.x-newBounds.width/2;newBounds.y=center.y-newBounds.height/2;var newZoom=1.0/newBounds.width;if(constraints){var newBoundsAspectRatio=newBounds.getAspectRatio();var newConstrainedZoom=this._applyZoomConstraints(newZoom);if(newZoom!==newConstrainedZoom){newZoom=newConstrainedZoom;newBounds.width=1.0/newZoom;newBounds.x=center.x-newBounds.width/2;newBounds.height=newBounds.width/newBoundsAspectRatio;newBounds.y=center.y-newBounds.height/2;}newBounds=this._applyBoundaryConstraints(newBounds);center=newBounds.getCenter();this._raiseConstraintsEvent(immediately);}if(immediately){this.panTo(center,true);return this.zoomTo(newZoom,null,true);}this.panTo(this.getCenter(true),true);this.zoomTo(this.getZoom(true),null,true);var oldBounds=this.getBounds();var oldZoom=this.getZoom();if(oldZoom===0||Math.abs(newZoom/oldZoom-1)<0.00000001){this.zoomTo(newZoom,true);return this.panTo(center,immediately);}newBounds=newBounds.rotate(-this.getRotation());var referencePoint=newBounds.getTopLeft().times(newZoom).minus(oldBounds.getTopLeft().times(oldZoom)).divide(newZoom-oldZoom);return this.zoomTo(newZoom,referencePoint,immediately);},/**\n * Makes the viewport zoom and pan so that the specified bounds take\n * as much space as possible in the viewport.\n * Note: this method ignores the constraints (minZoom, maxZoom and\n * visibilityRatio).\n * Use {@link OpenSeadragon.Viewport#fitBoundsWithConstraints} to enforce\n * them.\n * @function\n * @param {OpenSeadragon.Rect} bounds\n * @param {Boolean} [immediately=false]\n * @return {OpenSeadragon.Viewport} Chainable.\n */fitBounds:function fitBounds(bounds,immediately){return this._fitBounds(bounds,{immediately:immediately,constraints:false});},/**\n * Makes the viewport zoom and pan so that the specified bounds take\n * as much space as possible in the viewport while enforcing the constraints\n * (minZoom, maxZoom and visibilityRatio).\n * Note: because this method enforces the constraints, part of the\n * provided bounds may end up outside of the viewport.\n * Use {@link OpenSeadragon.Viewport#fitBounds} to ignore them.\n * @function\n * @param {OpenSeadragon.Rect} bounds\n * @param {Boolean} [immediately=false]\n * @return {OpenSeadragon.Viewport} Chainable.\n */fitBoundsWithConstraints:function fitBoundsWithConstraints(bounds,immediately){return this._fitBounds(bounds,{immediately:immediately,constraints:true});},/**\n * Zooms so the image just fills the viewer vertically.\n * @param {Boolean} immediately\n * @return {OpenSeadragon.Viewport} Chainable.\n */fitVertically:function fitVertically(immediately){var box=new $.Rect(this._contentBounds.x+this._contentBounds.width/2,this._contentBounds.y,0,this._contentBounds.height);return this.fitBounds(box,immediately);},/**\n * Zooms so the image just fills the viewer horizontally.\n * @param {Boolean} immediately\n * @return {OpenSeadragon.Viewport} Chainable.\n */fitHorizontally:function fitHorizontally(immediately){var box=new $.Rect(this._contentBounds.x,this._contentBounds.y+this._contentBounds.height/2,this._contentBounds.width,0);return this.fitBounds(box,immediately);},/**\n * Returns bounds taking constraints into account\n * Added to improve constrained panning\n * @param {Boolean} current - Pass true for the current location; defaults to false (target location).\n * @return {OpenSeadragon.Viewport} Chainable.\n */getConstrainedBounds:function getConstrainedBounds(current){var bounds,constrainedBounds;bounds=this.getBounds(current);constrainedBounds=this._applyBoundaryConstraints(bounds);return constrainedBounds;},/**\n * @function\n * @param {OpenSeadragon.Point} delta\n * @param {Boolean} immediately\n * @return {OpenSeadragon.Viewport} Chainable.\n * @fires OpenSeadragon.Viewer.event:pan\n */panBy:function panBy(delta,immediately){var center=new $.Point(this.centerSpringX.target.value,this.centerSpringY.target.value);return this.panTo(center.plus(delta),immediately);},/**\n * @function\n * @param {OpenSeadragon.Point} center\n * @param {Boolean} immediately\n * @return {OpenSeadragon.Viewport} Chainable.\n * @fires OpenSeadragon.Viewer.event:pan\n */panTo:function panTo(center,immediately){if(immediately){this.centerSpringX.resetTo(center.x);this.centerSpringY.resetTo(center.y);}else{this.centerSpringX.springTo(center.x);this.centerSpringY.springTo(center.y);}if(this.viewer){/**\n * Raised when the viewport is panned (see {@link OpenSeadragon.Viewport#panBy} and {@link OpenSeadragon.Viewport#panTo}).\n *\n * @event pan\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.Point} center\n * @property {Boolean} immediately\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.viewer.raiseEvent('pan',{center:center,immediately:immediately});}return this;},/**\n * @function\n * @return {OpenSeadragon.Viewport} Chainable.\n * @fires OpenSeadragon.Viewer.event:zoom\n */zoomBy:function zoomBy(factor,refPoint,immediately){return this.zoomTo(this.zoomSpring.target.value*factor,refPoint,immediately);},/**\n * Zooms to the specified zoom level\n * @function\n * @param {Number} zoom The zoom level to zoom to.\n * @param {OpenSeadragon.Point} [refPoint] The point which will stay at\n * the same screen location. Defaults to the viewport center.\n * @param {Boolean} [immediately=false]\n * @return {OpenSeadragon.Viewport} Chainable.\n * @fires OpenSeadragon.Viewer.event:zoom\n */zoomTo:function zoomTo(zoom,refPoint,immediately){var _this=this;this.zoomPoint=refPoint instanceof $.Point&&!isNaN(refPoint.x)&&!isNaN(refPoint.y)?refPoint:null;if(immediately){this._adjustCenterSpringsForZoomPoint(function(){_this.zoomSpring.resetTo(zoom);});}else{this.zoomSpring.springTo(zoom);}if(this.viewer){/**\n * Raised when the viewport zoom level changes (see {@link OpenSeadragon.Viewport#zoomBy} and {@link OpenSeadragon.Viewport#zoomTo}).\n *\n * @event zoom\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {Number} zoom\n * @property {OpenSeadragon.Point} refPoint\n * @property {Boolean} immediately\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.viewer.raiseEvent('zoom',{zoom:zoom,refPoint:refPoint,immediately:immediately});}return this;},/**\n * Rotates this viewport to the angle specified.\n * @function\n * @return {OpenSeadragon.Viewport} Chainable.\n */setRotation:function setRotation(degrees){if(!this.viewer||!this.viewer.drawer.canRotate()){return this;}this.degrees=$.positiveModulo(degrees,360);this._setContentBounds(this.viewer.world.getHomeBounds(),this.viewer.world.getContentFactor());this.viewer.forceRedraw();/**\n * Raised when rotation has been changed.\n *\n * @event rotate\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.\n * @property {Number} degrees - The number of degrees the rotation was set to.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.viewer.raiseEvent('rotate',{\"degrees\":degrees});return this;},/**\n * Gets the current rotation in degrees.\n * @function\n * @return {Number} The current rotation in degrees.\n */getRotation:function getRotation(){return this.degrees;},/**\n * @function\n * @return {OpenSeadragon.Viewport} Chainable.\n * @fires OpenSeadragon.Viewer.event:resize\n */resize:function resize(newContainerSize,maintain){var oldBounds=this.getBoundsNoRotate(),newBounds=oldBounds,widthDeltaFactor;this.containerSize.x=newContainerSize.x;this.containerSize.y=newContainerSize.y;this._updateContainerInnerSize();if(maintain){// TODO: widthDeltaFactor will always be 1; probably not what's intended\nwidthDeltaFactor=newContainerSize.x/this.containerSize.x;newBounds.width=oldBounds.width*widthDeltaFactor;newBounds.height=newBounds.width/this.getAspectRatio();}if(this.viewer){/**\n * Raised when the viewer is resized (see {@link OpenSeadragon.Viewport#resize}).\n *\n * @event resize\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.Point} newContainerSize\n * @property {Boolean} maintain\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.viewer.raiseEvent('resize',{newContainerSize:newContainerSize,maintain:maintain});}return this.fitBounds(newBounds,true);},// private\n_updateContainerInnerSize:function _updateContainerInnerSize(){this._containerInnerSize=new $.Point(Math.max(1,this.containerSize.x-(this._margins.left+this._margins.right)),Math.max(1,this.containerSize.y-(this._margins.top+this._margins.bottom)));},/**\n * Update the zoom and center (X and Y) springs.\n * @function\n * @returns {Boolean} True if any change has been made, false otherwise.\n */update:function update(){var _this=this;this._adjustCenterSpringsForZoomPoint(function(){_this.zoomSpring.update();});this.centerSpringX.update();this.centerSpringY.update();var changed=this.centerSpringX.current.value!==this._oldCenterX||this.centerSpringY.current.value!==this._oldCenterY||this.zoomSpring.current.value!==this._oldZoom;this._oldCenterX=this.centerSpringX.current.value;this._oldCenterY=this.centerSpringY.current.value;this._oldZoom=this.zoomSpring.current.value;return changed;},_adjustCenterSpringsForZoomPoint:function _adjustCenterSpringsForZoomPoint(zoomSpringHandler){if(this.zoomPoint){var oldZoomPixel=this.pixelFromPoint(this.zoomPoint,true);zoomSpringHandler();var newZoomPixel=this.pixelFromPoint(this.zoomPoint,true);var deltaZoomPixels=newZoomPixel.minus(oldZoomPixel);var deltaZoomPoints=this.deltaPointsFromPixels(deltaZoomPixels,true);this.centerSpringX.shiftBy(deltaZoomPoints.x);this.centerSpringY.shiftBy(deltaZoomPoints.y);if(this.zoomSpring.isAtTargetValue()){this.zoomPoint=null;}}else{zoomSpringHandler();}},/**\n * Convert a delta (translation vector) from viewport coordinates to pixels\n * coordinates. This method does not take rotation into account.\n * Consider using deltaPixelsFromPoints if you need to account for rotation.\n * @param {OpenSeadragon.Point} deltaPoints - The translation vector to convert.\n * @param {Boolean} [current=false] - Pass true for the current location;\n * defaults to false (target location).\n * @returns {OpenSeadragon.Point}\n */deltaPixelsFromPointsNoRotate:function deltaPixelsFromPointsNoRotate(deltaPoints,current){return deltaPoints.times(this._containerInnerSize.x*this.getZoom(current));},/**\n * Convert a delta (translation vector) from viewport coordinates to pixels\n * coordinates.\n * @param {OpenSeadragon.Point} deltaPoints - The translation vector to convert.\n * @param {Boolean} [current=false] - Pass true for the current location;\n * defaults to false (target location).\n * @returns {OpenSeadragon.Point}\n */deltaPixelsFromPoints:function deltaPixelsFromPoints(deltaPoints,current){return this.deltaPixelsFromPointsNoRotate(deltaPoints.rotate(this.getRotation()),current);},/**\n * Convert a delta (translation vector) from pixels coordinates to viewport\n * coordinates. This method does not take rotation into account.\n * Consider using deltaPointsFromPixels if you need to account for rotation.\n * @param {OpenSeadragon.Point} deltaPixels - The translation vector to convert.\n * @param {Boolean} [current=false] - Pass true for the current location;\n * defaults to false (target location).\n * @returns {OpenSeadragon.Point}\n */deltaPointsFromPixelsNoRotate:function deltaPointsFromPixelsNoRotate(deltaPixels,current){return deltaPixels.divide(this._containerInnerSize.x*this.getZoom(current));},/**\n * Convert a delta (translation vector) from pixels coordinates to viewport\n * coordinates.\n * @param {OpenSeadragon.Point} deltaPixels - The translation vector to convert.\n * @param {Boolean} [current=false] - Pass true for the current location;\n * defaults to false (target location).\n * @returns {OpenSeadragon.Point}\n */deltaPointsFromPixels:function deltaPointsFromPixels(deltaPixels,current){return this.deltaPointsFromPixelsNoRotate(deltaPixels,current).rotate(-this.getRotation());},/**\n * Convert viewport coordinates to pixels coordinates.\n * This method does not take rotation into account.\n * Consider using pixelFromPoint if you need to account for rotation.\n * @param {OpenSeadragon.Point} point the viewport coordinates\n * @param {Boolean} [current=false] - Pass true for the current location;\n * defaults to false (target location).\n * @returns {OpenSeadragon.Point}\n */pixelFromPointNoRotate:function pixelFromPointNoRotate(point,current){return this._pixelFromPointNoRotate(point,this.getBoundsNoRotate(current));},/**\n * Convert viewport coordinates to pixel coordinates.\n * @param {OpenSeadragon.Point} point the viewport coordinates\n * @param {Boolean} [current=false] - Pass true for the current location;\n * defaults to false (target location).\n * @returns {OpenSeadragon.Point}\n */pixelFromPoint:function pixelFromPoint(point,current){return this._pixelFromPoint(point,this.getBoundsNoRotate(current));},// private\n_pixelFromPointNoRotate:function _pixelFromPointNoRotate(point,bounds){return point.minus(bounds.getTopLeft()).times(this._containerInnerSize.x/bounds.width).plus(new $.Point(this._margins.left,this._margins.top));},// private\n_pixelFromPoint:function _pixelFromPoint(point,bounds){return this._pixelFromPointNoRotate(point.rotate(this.getRotation(),this.getCenter(true)),bounds);},/**\n * Convert pixel coordinates to viewport coordinates.\n * This method does not take rotation into account.\n * Consider using pointFromPixel if you need to account for rotation.\n * @param {OpenSeadragon.Point} pixel Pixel coordinates\n * @param {Boolean} [current=false] - Pass true for the current location;\n * defaults to false (target location).\n * @returns {OpenSeadragon.Point}\n */pointFromPixelNoRotate:function pointFromPixelNoRotate(pixel,current){var bounds=this.getBoundsNoRotate(current);return pixel.minus(new $.Point(this._margins.left,this._margins.top)).divide(this._containerInnerSize.x/bounds.width).plus(bounds.getTopLeft());},/**\n * Convert pixel coordinates to viewport coordinates.\n * @param {OpenSeadragon.Point} pixel Pixel coordinates\n * @param {Boolean} [current=false] - Pass true for the current location;\n * defaults to false (target location).\n * @returns {OpenSeadragon.Point}\n */pointFromPixel:function pointFromPixel(pixel,current){return this.pointFromPixelNoRotate(pixel,current).rotate(-this.getRotation(),this.getCenter(true));},// private\n_viewportToImageDelta:function _viewportToImageDelta(viewerX,viewerY){var scale=this._contentBoundsNoRotate.width;return new $.Point(viewerX*this._contentSizeNoRotate.x/scale,viewerY*this._contentSizeNoRotate.x/scale);},/**\n * Translates from OpenSeadragon viewer coordinate system to image coordinate system.\n * This method can be called either by passing X,Y coordinates or an\n * OpenSeadragon.Point\n * Note: not accurate with multi-image; use TiledImage.viewportToImageCoordinates instead.\n * @function\n * @param {(OpenSeadragon.Point|Number)} viewerX either a point or the X\n * coordinate in viewport coordinate system.\n * @param {Number} [viewerY] Y coordinate in viewport coordinate system.\n * @return {OpenSeadragon.Point} a point representing the coordinates in the image.\n */viewportToImageCoordinates:function viewportToImageCoordinates(viewerX,viewerY){if(viewerX instanceof $.Point){//they passed a point instead of individual components\nreturn this.viewportToImageCoordinates(viewerX.x,viewerX.y);}if(this.viewer){var count=this.viewer.world.getItemCount();if(count>1){$.console.error('[Viewport.viewportToImageCoordinates] is not accurate '+'with multi-image; use TiledImage.viewportToImageCoordinates instead.');}else if(count===1){// It is better to use TiledImage.viewportToImageCoordinates\n// because this._contentBoundsNoRotate can not be relied on\n// with clipping.\nvar item=this.viewer.world.getItemAt(0);return item.viewportToImageCoordinates(viewerX,viewerY,true);}}return this._viewportToImageDelta(viewerX-this._contentBoundsNoRotate.x,viewerY-this._contentBoundsNoRotate.y);},// private\n_imageToViewportDelta:function _imageToViewportDelta(imageX,imageY){var scale=this._contentBoundsNoRotate.width;return new $.Point(imageX/this._contentSizeNoRotate.x*scale,imageY/this._contentSizeNoRotate.x*scale);},/**\n * Translates from image coordinate system to OpenSeadragon viewer coordinate system\n * This method can be called either by passing X,Y coordinates or an\n * OpenSeadragon.Point\n * Note: not accurate with multi-image; use TiledImage.imageToViewportCoordinates instead.\n * @function\n * @param {(OpenSeadragon.Point | Number)} imageX the point or the\n * X coordinate in image coordinate system.\n * @param {Number} [imageY] Y coordinate in image coordinate system.\n * @return {OpenSeadragon.Point} a point representing the coordinates in the viewport.\n */imageToViewportCoordinates:function imageToViewportCoordinates(imageX,imageY){if(imageX instanceof $.Point){//they passed a point instead of individual components\nreturn this.imageToViewportCoordinates(imageX.x,imageX.y);}if(this.viewer){var count=this.viewer.world.getItemCount();if(count>1){$.console.error('[Viewport.imageToViewportCoordinates] is not accurate '+'with multi-image; use TiledImage.imageToViewportCoordinates instead.');}else if(count===1){// It is better to use TiledImage.viewportToImageCoordinates\n// because this._contentBoundsNoRotate can not be relied on\n// with clipping.\nvar item=this.viewer.world.getItemAt(0);return item.imageToViewportCoordinates(imageX,imageY,true);}}var point=this._imageToViewportDelta(imageX,imageY);point.x+=this._contentBoundsNoRotate.x;point.y+=this._contentBoundsNoRotate.y;return point;},/**\n * Translates from a rectangle which describes a portion of the image in\n * pixel coordinates to OpenSeadragon viewport rectangle coordinates.\n * This method can be called either by passing X,Y,width,height or an\n * OpenSeadragon.Rect\n * Note: not accurate with multi-image; use TiledImage.imageToViewportRectangle instead.\n * @function\n * @param {(OpenSeadragon.Rect | Number)} imageX the rectangle or the X\n * coordinate of the top left corner of the rectangle in image coordinate system.\n * @param {Number} [imageY] the Y coordinate of the top left corner of the rectangle\n * in image coordinate system.\n * @param {Number} [pixelWidth] the width in pixel of the rectangle.\n * @param {Number} [pixelHeight] the height in pixel of the rectangle.\n * @returns {OpenSeadragon.Rect} This image's bounds in viewport coordinates\n */imageToViewportRectangle:function imageToViewportRectangle(imageX,imageY,pixelWidth,pixelHeight){var rect=imageX;if(!(rect instanceof $.Rect)){//they passed individual components instead of a rectangle\nrect=new $.Rect(imageX,imageY,pixelWidth,pixelHeight);}if(this.viewer){var count=this.viewer.world.getItemCount();if(count>1){$.console.error('[Viewport.imageToViewportRectangle] is not accurate '+'with multi-image; use TiledImage.imageToViewportRectangle instead.');}else if(count===1){// It is better to use TiledImage.imageToViewportRectangle\n// because this._contentBoundsNoRotate can not be relied on\n// with clipping.\nvar item=this.viewer.world.getItemAt(0);return item.imageToViewportRectangle(imageX,imageY,pixelWidth,pixelHeight,true);}}var coordA=this.imageToViewportCoordinates(rect.x,rect.y);var coordB=this._imageToViewportDelta(rect.width,rect.height);return new $.Rect(coordA.x,coordA.y,coordB.x,coordB.y,rect.degrees);},/**\n * Translates from a rectangle which describes a portion of\n * the viewport in point coordinates to image rectangle coordinates.\n * This method can be called either by passing X,Y,width,height or an\n * OpenSeadragon.Rect\n * Note: not accurate with multi-image; use TiledImage.viewportToImageRectangle instead.\n * @function\n * @param {(OpenSeadragon.Rect | Number)} viewerX either a rectangle or\n * the X coordinate of the top left corner of the rectangle in viewport\n * coordinate system.\n * @param {Number} [viewerY] the Y coordinate of the top left corner of the rectangle\n * in viewport coordinate system.\n * @param {Number} [pointWidth] the width of the rectangle in viewport coordinate system.\n * @param {Number} [pointHeight] the height of the rectangle in viewport coordinate system.\n */viewportToImageRectangle:function viewportToImageRectangle(viewerX,viewerY,pointWidth,pointHeight){var rect=viewerX;if(!(rect instanceof $.Rect)){//they passed individual components instead of a rectangle\nrect=new $.Rect(viewerX,viewerY,pointWidth,pointHeight);}if(this.viewer){var count=this.viewer.world.getItemCount();if(count>1){$.console.error('[Viewport.viewportToImageRectangle] is not accurate '+'with multi-image; use TiledImage.viewportToImageRectangle instead.');}else if(count===1){// It is better to use TiledImage.viewportToImageCoordinates\n// because this._contentBoundsNoRotate can not be relied on\n// with clipping.\nvar item=this.viewer.world.getItemAt(0);return item.viewportToImageRectangle(viewerX,viewerY,pointWidth,pointHeight,true);}}var coordA=this.viewportToImageCoordinates(rect.x,rect.y);var coordB=this._viewportToImageDelta(rect.width,rect.height);return new $.Rect(coordA.x,coordA.y,coordB.x,coordB.y,rect.degrees);},/**\n * Convert pixel coordinates relative to the viewer element to image\n * coordinates.\n * Note: not accurate with multi-image.\n * @param {OpenSeadragon.Point} pixel\n * @returns {OpenSeadragon.Point}\n */viewerElementToImageCoordinates:function viewerElementToImageCoordinates(pixel){var point=this.pointFromPixel(pixel,true);return this.viewportToImageCoordinates(point);},/**\n * Convert pixel coordinates relative to the image to\n * viewer element coordinates.\n * Note: not accurate with multi-image.\n * @param {OpenSeadragon.Point} pixel\n * @returns {OpenSeadragon.Point}\n */imageToViewerElementCoordinates:function imageToViewerElementCoordinates(pixel){var point=this.imageToViewportCoordinates(pixel);return this.pixelFromPoint(point,true);},/**\n * Convert pixel coordinates relative to the window to image coordinates.\n * Note: not accurate with multi-image.\n * @param {OpenSeadragon.Point} pixel\n * @returns {OpenSeadragon.Point}\n */windowToImageCoordinates:function windowToImageCoordinates(pixel){$.console.assert(this.viewer,\"[Viewport.windowToImageCoordinates] the viewport must have a viewer.\");var viewerCoordinates=pixel.minus($.getElementPosition(this.viewer.element));return this.viewerElementToImageCoordinates(viewerCoordinates);},/**\n * Convert image coordinates to pixel coordinates relative to the window.\n * Note: not accurate with multi-image.\n * @param {OpenSeadragon.Point} pixel\n * @returns {OpenSeadragon.Point}\n */imageToWindowCoordinates:function imageToWindowCoordinates(pixel){$.console.assert(this.viewer,\"[Viewport.imageToWindowCoordinates] the viewport must have a viewer.\");var viewerCoordinates=this.imageToViewerElementCoordinates(pixel);return viewerCoordinates.plus($.getElementPosition(this.viewer.element));},/**\n * Convert pixel coordinates relative to the viewer element to viewport\n * coordinates.\n * @param {OpenSeadragon.Point} pixel\n * @returns {OpenSeadragon.Point}\n */viewerElementToViewportCoordinates:function viewerElementToViewportCoordinates(pixel){return this.pointFromPixel(pixel,true);},/**\n * Convert viewport coordinates to pixel coordinates relative to the\n * viewer element.\n * @param {OpenSeadragon.Point} point\n * @returns {OpenSeadragon.Point}\n */viewportToViewerElementCoordinates:function viewportToViewerElementCoordinates(point){return this.pixelFromPoint(point,true);},/**\n * Convert a rectangle in pixel coordinates relative to the viewer element\n * to viewport coordinates.\n * @param {OpenSeadragon.Rect} rectangle the rectangle to convert\n * @returns {OpenSeadragon.Rect} the converted rectangle\n */viewerElementToViewportRectangle:function viewerElementToViewportRectangle(rectangle){return $.Rect.fromSummits(this.pointFromPixel(rectangle.getTopLeft(),true),this.pointFromPixel(rectangle.getTopRight(),true),this.pointFromPixel(rectangle.getBottomLeft(),true));},/**\n * Convert a rectangle in viewport coordinates to pixel coordinates relative\n * to the viewer element.\n * @param {OpenSeadragon.Rect} rectangle the rectangle to convert\n * @returns {OpenSeadragon.Rect} the converted rectangle\n */viewportToViewerElementRectangle:function viewportToViewerElementRectangle(rectangle){return $.Rect.fromSummits(this.pixelFromPoint(rectangle.getTopLeft(),true),this.pixelFromPoint(rectangle.getTopRight(),true),this.pixelFromPoint(rectangle.getBottomLeft(),true));},/**\n * Convert pixel coordinates relative to the window to viewport coordinates.\n * @param {OpenSeadragon.Point} pixel\n * @returns {OpenSeadragon.Point}\n */windowToViewportCoordinates:function windowToViewportCoordinates(pixel){$.console.assert(this.viewer,\"[Viewport.windowToViewportCoordinates] the viewport must have a viewer.\");var viewerCoordinates=pixel.minus($.getElementPosition(this.viewer.element));return this.viewerElementToViewportCoordinates(viewerCoordinates);},/**\n * Convert viewport coordinates to pixel coordinates relative to the window.\n * @param {OpenSeadragon.Point} point\n * @returns {OpenSeadragon.Point}\n */viewportToWindowCoordinates:function viewportToWindowCoordinates(point){$.console.assert(this.viewer,\"[Viewport.viewportToWindowCoordinates] the viewport must have a viewer.\");var viewerCoordinates=this.viewportToViewerElementCoordinates(point);return viewerCoordinates.plus($.getElementPosition(this.viewer.element));},/**\n * Convert a viewport zoom to an image zoom.\n * Image zoom: ratio of the original image size to displayed image size.\n * 1 means original image size, 0.5 half size...\n * Viewport zoom: ratio of the displayed image's width to viewport's width.\n * 1 means identical width, 2 means image's width is twice the viewport's width...\n * Note: not accurate with multi-image.\n * @function\n * @param {Number} viewportZoom The viewport zoom\n * target zoom.\n * @returns {Number} imageZoom The image zoom\n */viewportToImageZoom:function viewportToImageZoom(viewportZoom){if(this.viewer){var count=this.viewer.world.getItemCount();if(count>1){$.console.error('[Viewport.viewportToImageZoom] is not '+'accurate with multi-image.');}else if(count===1){// It is better to use TiledImage.viewportToImageZoom\n// because this._contentBoundsNoRotate can not be relied on\n// with clipping.\nvar item=this.viewer.world.getItemAt(0);return item.viewportToImageZoom(viewportZoom);}}var imageWidth=this._contentSizeNoRotate.x;var containerWidth=this._containerInnerSize.x;var scale=this._contentBoundsNoRotate.width;var viewportToImageZoomRatio=containerWidth/imageWidth*scale;return viewportZoom*viewportToImageZoomRatio;},/**\n * Convert an image zoom to a viewport zoom.\n * Image zoom: ratio of the original image size to displayed image size.\n * 1 means original image size, 0.5 half size...\n * Viewport zoom: ratio of the displayed image's width to viewport's width.\n * 1 means identical width, 2 means image's width is twice the viewport's width...\n * Note: not accurate with multi-image.\n * @function\n * @param {Number} imageZoom The image zoom\n * target zoom.\n * @returns {Number} viewportZoom The viewport zoom\n */imageToViewportZoom:function imageToViewportZoom(imageZoom){if(this.viewer){var count=this.viewer.world.getItemCount();if(count>1){$.console.error('[Viewport.imageToViewportZoom] is not accurate '+'with multi-image.');}else if(count===1){// It is better to use TiledImage.imageToViewportZoom\n// because this._contentBoundsNoRotate can not be relied on\n// with clipping.\nvar item=this.viewer.world.getItemAt(0);return item.imageToViewportZoom(imageZoom);}}var imageWidth=this._contentSizeNoRotate.x;var containerWidth=this._containerInnerSize.x;var scale=this._contentBoundsNoRotate.width;var viewportToImageZoomRatio=imageWidth/containerWidth/scale;return imageZoom*viewportToImageZoomRatio;},/**\n * Toggles flip state and demands a new drawing on navigator and viewer objects.\n * @function\n * @return {OpenSeadragon.Viewport} Chainable.\n */toggleFlip:function toggleFlip(){this.setFlip(!this.getFlip());return this;},/**\n * Gets flip state stored on viewport.\n * @function\n * @return {Boolean} Flip state.\n */getFlip:function getFlip(){return this.flipped;},/**\n * Sets flip state according to the state input argument.\n * @function\n * @param {Boolean} state - Flip state to set.\n * @return {OpenSeadragon.Viewport} Chainable.\n */setFlip:function setFlip(state){if(this.flipped===state){return this;}this.flipped=state;if(this.viewer.navigator){this.viewer.navigator.setFlip(this.getFlip());}this.viewer.forceRedraw();/**\n * Raised when flip state has been changed.\n *\n * @event flip\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.\n * @property {Number} flipped - The flip state after this change.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.viewer.raiseEvent('flip',{\"flipped\":state});return this;}};})(OpenSeadragon);/*\n * OpenSeadragon - TiledImage\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * You shouldn't have to create a TiledImage instance directly; get it asynchronously by\n * using {@link OpenSeadragon.Viewer#open} or {@link OpenSeadragon.Viewer#addTiledImage} instead.\n * @class TiledImage\n * @memberof OpenSeadragon\n * @extends OpenSeadragon.EventSource\n * @classdesc Handles rendering of tiles for an {@link OpenSeadragon.Viewer}.\n * A new instance is created for each TileSource opened.\n * @param {Object} options - Configuration for this TiledImage.\n * @param {OpenSeadragon.TileSource} options.source - The TileSource that defines this TiledImage.\n * @param {OpenSeadragon.Viewer} options.viewer - The Viewer that owns this TiledImage.\n * @param {OpenSeadragon.TileCache} options.tileCache - The TileCache for this TiledImage to use.\n * @param {OpenSeadragon.Drawer} options.drawer - The Drawer for this TiledImage to draw onto.\n * @param {OpenSeadragon.ImageLoader} options.imageLoader - The ImageLoader for this TiledImage to use.\n * @param {Number} [options.x=0] - Left position, in viewport coordinates.\n * @param {Number} [options.y=0] - Top position, in viewport coordinates.\n * @param {Number} [options.width=1] - Width, in viewport coordinates.\n * @param {Number} [options.height] - Height, in viewport coordinates.\n * @param {OpenSeadragon.Rect} [options.fitBounds] The bounds in viewport coordinates\n * to fit the image into. If specified, x, y, width and height get ignored.\n * @param {OpenSeadragon.Placement} [options.fitBoundsPlacement=OpenSeadragon.Placement.CENTER]\n * How to anchor the image in the bounds if options.fitBounds is set.\n * @param {OpenSeadragon.Rect} [options.clip] - An area, in image pixels, to clip to\n * (portions of the image outside of this area will not be visible). Only works on\n * browsers that support the HTML5 canvas.\n * @param {Number} [options.springStiffness] - See {@link OpenSeadragon.Options}.\n * @param {Boolean} [options.animationTime] - See {@link OpenSeadragon.Options}.\n * @param {Number} [options.minZoomImageRatio] - See {@link OpenSeadragon.Options}.\n * @param {Boolean} [options.wrapHorizontal] - See {@link OpenSeadragon.Options}.\n * @param {Boolean} [options.wrapVertical] - See {@link OpenSeadragon.Options}.\n * @param {Boolean} [options.immediateRender] - See {@link OpenSeadragon.Options}.\n * @param {Number} [options.blendTime] - See {@link OpenSeadragon.Options}.\n * @param {Boolean} [options.alwaysBlend] - See {@link OpenSeadragon.Options}.\n * @param {Number} [options.minPixelRatio] - See {@link OpenSeadragon.Options}.\n * @param {Number} [options.smoothTileEdgesMinZoom] - See {@link OpenSeadragon.Options}.\n * @param {Boolean} [options.iOSDevice] - See {@link OpenSeadragon.Options}.\n * @param {Number} [options.opacity=1] - Set to draw at proportional opacity. If zero, images will not draw.\n * @param {Boolean} [options.preload=false] - Set true to load even when the image is hidden by zero opacity.\n * @param {String} [options.compositeOperation] - How the image is composited onto other images; see compositeOperation in {@link OpenSeadragon.Options} for possible values.\n * @param {Boolean} [options.debugMode] - See {@link OpenSeadragon.Options}.\n * @param {String|CanvasGradient|CanvasPattern|Function} [options.placeholderFillStyle] - See {@link OpenSeadragon.Options}.\n * @param {String|Boolean} [options.crossOriginPolicy] - See {@link OpenSeadragon.Options}.\n * @param {Boolean} [options.ajaxWithCredentials] - See {@link OpenSeadragon.Options}.\n * @param {Boolean} [options.loadTilesWithAjax]\n * Whether to load tile data using AJAX requests.\n * Defaults to the setting in {@link OpenSeadragon.Options}.\n * @param {Object} [options.ajaxHeaders={}]\n * A set of headers to include when making tile AJAX requests.\n */$.TiledImage=function(options){var _this=this;$.console.assert(options.tileCache,\"[TiledImage] options.tileCache is required\");$.console.assert(options.drawer,\"[TiledImage] options.drawer is required\");$.console.assert(options.viewer,\"[TiledImage] options.viewer is required\");$.console.assert(options.imageLoader,\"[TiledImage] options.imageLoader is required\");$.console.assert(options.source,\"[TiledImage] options.source is required\");$.console.assert(!options.clip||options.clip instanceof $.Rect,\"[TiledImage] options.clip must be an OpenSeadragon.Rect if present\");$.EventSource.call(this);this._tileCache=options.tileCache;delete options.tileCache;this._drawer=options.drawer;delete options.drawer;this._imageLoader=options.imageLoader;delete options.imageLoader;if(options.clip instanceof $.Rect){this._clip=options.clip.clone();}delete options.clip;var x=options.x||0;delete options.x;var y=options.y||0;delete options.y;// Ratio of zoomable image height to width.\nthis.normHeight=options.source.dimensions.y/options.source.dimensions.x;this.contentAspectX=options.source.dimensions.x/options.source.dimensions.y;var scale=1;if(options.width){scale=options.width;delete options.width;if(options.height){$.console.error(\"specifying both width and height to a tiledImage is not supported\");delete options.height;}}else if(options.height){scale=options.height/this.normHeight;delete options.height;}var fitBounds=options.fitBounds;delete options.fitBounds;var fitBoundsPlacement=options.fitBoundsPlacement||OpenSeadragon.Placement.CENTER;delete options.fitBoundsPlacement;var degrees=options.degrees||0;delete options.degrees;$.extend(true,this,{//internal state properties\nviewer:null,tilesMatrix:{},// A '3d' dictionary [level][x][y] --> Tile.\ncoverage:{},// A '3d' dictionary [level][x][y] --> Boolean; shows what areas have been drawn.\nloadingCoverage:{},// A '3d' dictionary [level][x][y] --> Boolean; shows what areas are loaded or are being loaded/blended.\nlastDrawn:[],// An unordered list of Tiles drawn last frame.\nlastResetTime:0,// Last time for which the tiledImage was reset.\n_midDraw:false,// Is the tiledImage currently updating the viewport?\n_needsDraw:true,// Does the tiledImage need to update the viewport again?\n_hasOpaqueTile:false,// Do we have even one fully opaque tile?\n_tilesLoading:0,// The number of pending tile requests.\n//configurable settings\nspringStiffness:$.DEFAULT_SETTINGS.springStiffness,animationTime:$.DEFAULT_SETTINGS.animationTime,minZoomImageRatio:$.DEFAULT_SETTINGS.minZoomImageRatio,wrapHorizontal:$.DEFAULT_SETTINGS.wrapHorizontal,wrapVertical:$.DEFAULT_SETTINGS.wrapVertical,immediateRender:$.DEFAULT_SETTINGS.immediateRender,blendTime:$.DEFAULT_SETTINGS.blendTime,alwaysBlend:$.DEFAULT_SETTINGS.alwaysBlend,minPixelRatio:$.DEFAULT_SETTINGS.minPixelRatio,smoothTileEdgesMinZoom:$.DEFAULT_SETTINGS.smoothTileEdgesMinZoom,iOSDevice:$.DEFAULT_SETTINGS.iOSDevice,debugMode:$.DEFAULT_SETTINGS.debugMode,crossOriginPolicy:$.DEFAULT_SETTINGS.crossOriginPolicy,ajaxWithCredentials:$.DEFAULT_SETTINGS.ajaxWithCredentials,placeholderFillStyle:$.DEFAULT_SETTINGS.placeholderFillStyle,opacity:$.DEFAULT_SETTINGS.opacity,preload:$.DEFAULT_SETTINGS.preload,compositeOperation:$.DEFAULT_SETTINGS.compositeOperation},options);this._preload=this.preload;delete this.preload;this._fullyLoaded=false;this._xSpring=new $.Spring({initial:x,springStiffness:this.springStiffness,animationTime:this.animationTime});this._ySpring=new $.Spring({initial:y,springStiffness:this.springStiffness,animationTime:this.animationTime});this._scaleSpring=new $.Spring({initial:scale,springStiffness:this.springStiffness,animationTime:this.animationTime});this._degreesSpring=new $.Spring({initial:degrees,springStiffness:this.springStiffness,animationTime:this.animationTime});this._updateForScale();if(fitBounds){this.fitBounds(fitBounds,fitBoundsPlacement,true);}// We need a callback to give image manipulation a chance to happen\nthis._drawingHandler=function(args){/**\n * This event is fired just before the tile is drawn giving the application a chance to alter the image.\n *\n * NOTE: This event is only fired when the drawer is using a <canvas>.\n *\n * @event tile-drawing\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.\n * @property {OpenSeadragon.Tile} tile - The Tile being drawn.\n * @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.\n * @property {OpenSeadragon.Tile} context - The HTML canvas context being drawn into.\n * @property {OpenSeadragon.Tile} rendered - The HTML canvas context containing the tile imagery.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.viewer.raiseEvent('tile-drawing',$.extend({tiledImage:_this},args));};};$.extend($.TiledImage.prototype,$.EventSource.prototype,/** @lends OpenSeadragon.TiledImage.prototype */{/**\n * @returns {Boolean} Whether the TiledImage needs to be drawn.\n */needsDraw:function needsDraw(){return this._needsDraw;},/**\n * @returns {Boolean} Whether all tiles necessary for this TiledImage to draw at the current view have been loaded.\n */getFullyLoaded:function getFullyLoaded(){return this._fullyLoaded;},// private\n_setFullyLoaded:function _setFullyLoaded(flag){if(flag===this._fullyLoaded){return;}this._fullyLoaded=flag;/**\n * Fired when the TiledImage's \"fully loaded\" flag (whether all tiles necessary for this TiledImage\n * to draw at the current view have been loaded) changes.\n *\n * @event fully-loaded-change\n * @memberof OpenSeadragon.TiledImage\n * @type {object}\n * @property {Boolean} fullyLoaded - The new \"fully loaded\" value.\n * @property {OpenSeadragon.TiledImage} eventSource - A reference to the TiledImage which raised the event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('fully-loaded-change',{fullyLoaded:this._fullyLoaded});},/**\n * Clears all tiles and triggers an update on the next call to\n * {@link OpenSeadragon.TiledImage#update}.\n */reset:function reset(){this._tileCache.clearTilesFor(this);this.lastResetTime=$.now();this._needsDraw=true;},/**\n * Updates the TiledImage's bounds, animating if needed.\n * @returns {Boolean} Whether the TiledImage animated.\n */update:function update(){var xUpdated=this._xSpring.update();var yUpdated=this._ySpring.update();var scaleUpdated=this._scaleSpring.update();var degreesUpdated=this._degreesSpring.update();if(xUpdated||yUpdated||scaleUpdated||degreesUpdated){this._updateForScale();this._needsDraw=true;return true;}return false;},/**\n * Draws the TiledImage to its Drawer.\n */draw:function draw(){if(this.opacity!==0||this._preload){this._midDraw=true;this._updateViewport();this._midDraw=false;}// Images with opacity 0 should not need to be drawn in future. this._needsDraw = false is set in this._updateViewport() for other images.\nelse{this._needsDraw=false;}},/**\n * Destroy the TiledImage (unload current loaded tiles).\n */destroy:function destroy(){this.reset();},/**\n * Get this TiledImage's bounds in viewport coordinates.\n * @param {Boolean} [current=false] - Pass true for the current location;\n * false for target location.\n * @returns {OpenSeadragon.Rect} This TiledImage's bounds in viewport coordinates.\n */getBounds:function getBounds(current){return this.getBoundsNoRotate(current).rotate(this.getRotation(current),this._getRotationPoint(current));},/**\n * Get this TiledImage's bounds in viewport coordinates without taking\n * rotation into account.\n * @param {Boolean} [current=false] - Pass true for the current location;\n * false for target location.\n * @returns {OpenSeadragon.Rect} This TiledImage's bounds in viewport coordinates.\n */getBoundsNoRotate:function getBoundsNoRotate(current){return current?new $.Rect(this._xSpring.current.value,this._ySpring.current.value,this._worldWidthCurrent,this._worldHeightCurrent):new $.Rect(this._xSpring.target.value,this._ySpring.target.value,this._worldWidthTarget,this._worldHeightTarget);},// deprecated\ngetWorldBounds:function getWorldBounds(){$.console.error('[TiledImage.getWorldBounds] is deprecated; use TiledImage.getBounds instead');return this.getBounds();},/**\n * Get the bounds of the displayed part of the tiled image.\n * @param {Boolean} [current=false] Pass true for the current location,\n * false for the target location.\n * @returns {$.Rect} The clipped bounds in viewport coordinates.\n */getClippedBounds:function getClippedBounds(current){var bounds=this.getBoundsNoRotate(current);if(this._clip){var worldWidth=current?this._worldWidthCurrent:this._worldWidthTarget;var ratio=worldWidth/this.source.dimensions.x;var clip=this._clip.times(ratio);bounds=new $.Rect(bounds.x+clip.x,bounds.y+clip.y,clip.width,clip.height);}return bounds.rotate(this.getRotation(current),this._getRotationPoint(current));},/**\n * @returns {OpenSeadragon.Point} This TiledImage's content size, in original pixels.\n */getContentSize:function getContentSize(){return new $.Point(this.source.dimensions.x,this.source.dimensions.y);},// private\n_viewportToImageDelta:function _viewportToImageDelta(viewerX,viewerY,current){var scale=current?this._scaleSpring.current.value:this._scaleSpring.target.value;return new $.Point(viewerX*(this.source.dimensions.x/scale),viewerY*(this.source.dimensions.y*this.contentAspectX/scale));},/**\n * Translates from OpenSeadragon viewer coordinate system to image coordinate system.\n * This method can be called either by passing X,Y coordinates or an {@link OpenSeadragon.Point}.\n * @param {Number|OpenSeadragon.Point} viewerX - The X coordinate or point in viewport coordinate system.\n * @param {Number} [viewerY] - The Y coordinate in viewport coordinate system.\n * @param {Boolean} [current=false] - Pass true to use the current location; false for target location.\n * @return {OpenSeadragon.Point} A point representing the coordinates in the image.\n */viewportToImageCoordinates:function viewportToImageCoordinates(viewerX,viewerY,current){var point;if(viewerX instanceof $.Point){//they passed a point instead of individual components\ncurrent=viewerY;point=viewerX;}else{point=new $.Point(viewerX,viewerY);}point=point.rotate(-this.getRotation(current),this._getRotationPoint(current));return current?this._viewportToImageDelta(point.x-this._xSpring.current.value,point.y-this._ySpring.current.value):this._viewportToImageDelta(point.x-this._xSpring.target.value,point.y-this._ySpring.target.value);},// private\n_imageToViewportDelta:function _imageToViewportDelta(imageX,imageY,current){var scale=current?this._scaleSpring.current.value:this._scaleSpring.target.value;return new $.Point(imageX/this.source.dimensions.x*scale,imageY/this.source.dimensions.y/this.contentAspectX*scale);},/**\n * Translates from image coordinate system to OpenSeadragon viewer coordinate system\n * This method can be called either by passing X,Y coordinates or an {@link OpenSeadragon.Point}.\n * @param {Number|OpenSeadragon.Point} imageX - The X coordinate or point in image coordinate system.\n * @param {Number} [imageY] - The Y coordinate in image coordinate system.\n * @param {Boolean} [current=false] - Pass true to use the current location; false for target location.\n * @return {OpenSeadragon.Point} A point representing the coordinates in the viewport.\n */imageToViewportCoordinates:function imageToViewportCoordinates(imageX,imageY,current){if(imageX instanceof $.Point){//they passed a point instead of individual components\ncurrent=imageY;imageY=imageX.y;imageX=imageX.x;}var point=this._imageToViewportDelta(imageX,imageY);if(current){point.x+=this._xSpring.current.value;point.y+=this._ySpring.current.value;}else{point.x+=this._xSpring.target.value;point.y+=this._ySpring.target.value;}return point.rotate(this.getRotation(current),this._getRotationPoint(current));},/**\n * Translates from a rectangle which describes a portion of the image in\n * pixel coordinates to OpenSeadragon viewport rectangle coordinates.\n * This method can be called either by passing X,Y,width,height or an {@link OpenSeadragon.Rect}.\n * @param {Number|OpenSeadragon.Rect} imageX - The left coordinate or rectangle in image coordinate system.\n * @param {Number} [imageY] - The top coordinate in image coordinate system.\n * @param {Number} [pixelWidth] - The width in pixel of the rectangle.\n * @param {Number} [pixelHeight] - The height in pixel of the rectangle.\n * @param {Boolean} [current=false] - Pass true to use the current location; false for target location.\n * @return {OpenSeadragon.Rect} A rect representing the coordinates in the viewport.\n */imageToViewportRectangle:function imageToViewportRectangle(imageX,imageY,pixelWidth,pixelHeight,current){var rect=imageX;if(rect instanceof $.Rect){//they passed a rect instead of individual components\ncurrent=imageY;}else{rect=new $.Rect(imageX,imageY,pixelWidth,pixelHeight);}var coordA=this.imageToViewportCoordinates(rect.getTopLeft(),current);var coordB=this._imageToViewportDelta(rect.width,rect.height,current);return new $.Rect(coordA.x,coordA.y,coordB.x,coordB.y,rect.degrees+this.getRotation(current));},/**\n * Translates from a rectangle which describes a portion of\n * the viewport in point coordinates to image rectangle coordinates.\n * This method can be called either by passing X,Y,width,height or an {@link OpenSeadragon.Rect}.\n * @param {Number|OpenSeadragon.Rect} viewerX - The left coordinate or rectangle in viewport coordinate system.\n * @param {Number} [viewerY] - The top coordinate in viewport coordinate system.\n * @param {Number} [pointWidth] - The width in viewport coordinate system.\n * @param {Number} [pointHeight] - The height in viewport coordinate system.\n * @param {Boolean} [current=false] - Pass true to use the current location; false for target location.\n * @return {OpenSeadragon.Rect} A rect representing the coordinates in the image.\n */viewportToImageRectangle:function viewportToImageRectangle(viewerX,viewerY,pointWidth,pointHeight,current){var rect=viewerX;if(viewerX instanceof $.Rect){//they passed a rect instead of individual components\ncurrent=viewerY;}else{rect=new $.Rect(viewerX,viewerY,pointWidth,pointHeight);}var coordA=this.viewportToImageCoordinates(rect.getTopLeft(),current);var coordB=this._viewportToImageDelta(rect.width,rect.height,current);return new $.Rect(coordA.x,coordA.y,coordB.x,coordB.y,rect.degrees-this.getRotation(current));},/**\n * Convert pixel coordinates relative to the viewer element to image\n * coordinates.\n * @param {OpenSeadragon.Point} pixel\n * @returns {OpenSeadragon.Point}\n */viewerElementToImageCoordinates:function viewerElementToImageCoordinates(pixel){var point=this.viewport.pointFromPixel(pixel,true);return this.viewportToImageCoordinates(point);},/**\n * Convert pixel coordinates relative to the image to\n * viewer element coordinates.\n * @param {OpenSeadragon.Point} pixel\n * @returns {OpenSeadragon.Point}\n */imageToViewerElementCoordinates:function imageToViewerElementCoordinates(pixel){var point=this.imageToViewportCoordinates(pixel);return this.viewport.pixelFromPoint(point,true);},/**\n * Convert pixel coordinates relative to the window to image coordinates.\n * @param {OpenSeadragon.Point} pixel\n * @returns {OpenSeadragon.Point}\n */windowToImageCoordinates:function windowToImageCoordinates(pixel){var viewerCoordinates=pixel.minus(OpenSeadragon.getElementPosition(this.viewer.element));return this.viewerElementToImageCoordinates(viewerCoordinates);},/**\n * Convert image coordinates to pixel coordinates relative to the window.\n * @param {OpenSeadragon.Point} pixel\n * @returns {OpenSeadragon.Point}\n */imageToWindowCoordinates:function imageToWindowCoordinates(pixel){var viewerCoordinates=this.imageToViewerElementCoordinates(pixel);return viewerCoordinates.plus(OpenSeadragon.getElementPosition(this.viewer.element));},// private\n// Convert rectangle in viewport coordinates to this tiled image point\n// coordinates (x in [0, 1] and y in [0, aspectRatio])\n_viewportToTiledImageRectangle:function _viewportToTiledImageRectangle(rect){var scale=this._scaleSpring.current.value;rect=rect.rotate(-this.getRotation(true),this._getRotationPoint(true));return new $.Rect((rect.x-this._xSpring.current.value)/scale,(rect.y-this._ySpring.current.value)/scale,rect.width/scale,rect.height/scale,rect.degrees);},/**\n * Convert a viewport zoom to an image zoom.\n * Image zoom: ratio of the original image size to displayed image size.\n * 1 means original image size, 0.5 half size...\n * Viewport zoom: ratio of the displayed image's width to viewport's width.\n * 1 means identical width, 2 means image's width is twice the viewport's width...\n * @function\n * @param {Number} viewportZoom The viewport zoom\n * @returns {Number} imageZoom The image zoom\n */viewportToImageZoom:function viewportToImageZoom(viewportZoom){var ratio=this._scaleSpring.current.value*this.viewport._containerInnerSize.x/this.source.dimensions.x;return ratio*viewportZoom;},/**\n * Convert an image zoom to a viewport zoom.\n * Image zoom: ratio of the original image size to displayed image size.\n * 1 means original image size, 0.5 half size...\n * Viewport zoom: ratio of the displayed image's width to viewport's width.\n * 1 means identical width, 2 means image's width is twice the viewport's width...\n * Note: not accurate with multi-image.\n * @function\n * @param {Number} imageZoom The image zoom\n * @returns {Number} viewportZoom The viewport zoom\n */imageToViewportZoom:function imageToViewportZoom(imageZoom){var ratio=this._scaleSpring.current.value*this.viewport._containerInnerSize.x/this.source.dimensions.x;return imageZoom/ratio;},/**\n * Sets the TiledImage's position in the world.\n * @param {OpenSeadragon.Point} position - The new position, in viewport coordinates.\n * @param {Boolean} [immediately=false] - Whether to animate to the new position or snap immediately.\n * @fires OpenSeadragon.TiledImage.event:bounds-change\n */setPosition:function setPosition(position,immediately){var sameTarget=this._xSpring.target.value===position.x&&this._ySpring.target.value===position.y;if(immediately){if(sameTarget&&this._xSpring.current.value===position.x&&this._ySpring.current.value===position.y){return;}this._xSpring.resetTo(position.x);this._ySpring.resetTo(position.y);this._needsDraw=true;}else{if(sameTarget){return;}this._xSpring.springTo(position.x);this._ySpring.springTo(position.y);this._needsDraw=true;}if(!sameTarget){this._raiseBoundsChange();}},/**\n * Sets the TiledImage's width in the world, adjusting the height to match based on aspect ratio.\n * @param {Number} width - The new width, in viewport coordinates.\n * @param {Boolean} [immediately=false] - Whether to animate to the new size or snap immediately.\n * @fires OpenSeadragon.TiledImage.event:bounds-change\n */setWidth:function setWidth(width,immediately){this._setScale(width,immediately);},/**\n * Sets the TiledImage's height in the world, adjusting the width to match based on aspect ratio.\n * @param {Number} height - The new height, in viewport coordinates.\n * @param {Boolean} [immediately=false] - Whether to animate to the new size or snap immediately.\n * @fires OpenSeadragon.TiledImage.event:bounds-change\n */setHeight:function setHeight(height,immediately){this._setScale(height/this.normHeight,immediately);},/**\n * Positions and scales the TiledImage to fit in the specified bounds.\n * Note: this method fires OpenSeadragon.TiledImage.event:bounds-change\n * twice\n * @param {OpenSeadragon.Rect} bounds The bounds to fit the image into.\n * @param {OpenSeadragon.Placement} [anchor=OpenSeadragon.Placement.CENTER]\n * How to anchor the image in the bounds.\n * @param {Boolean} [immediately=false] Whether to animate to the new size\n * or snap immediately.\n * @fires OpenSeadragon.TiledImage.event:bounds-change\n */fitBounds:function fitBounds(bounds,anchor,immediately){anchor=anchor||$.Placement.CENTER;var anchorProperties=$.Placement.properties[anchor];var aspectRatio=this.contentAspectX;var xOffset=0;var yOffset=0;var displayedWidthRatio=1;var displayedHeightRatio=1;if(this._clip){aspectRatio=this._clip.getAspectRatio();displayedWidthRatio=this._clip.width/this.source.dimensions.x;displayedHeightRatio=this._clip.height/this.source.dimensions.y;if(bounds.getAspectRatio()>aspectRatio){xOffset=this._clip.x/this._clip.height*bounds.height;yOffset=this._clip.y/this._clip.height*bounds.height;}else{xOffset=this._clip.x/this._clip.width*bounds.width;yOffset=this._clip.y/this._clip.width*bounds.width;}}if(bounds.getAspectRatio()>aspectRatio){// We will have margins on the X axis\nvar height=bounds.height/displayedHeightRatio;var marginLeft=0;if(anchorProperties.isHorizontallyCentered){marginLeft=(bounds.width-bounds.height*aspectRatio)/2;}else if(anchorProperties.isRight){marginLeft=bounds.width-bounds.height*aspectRatio;}this.setPosition(new $.Point(bounds.x-xOffset+marginLeft,bounds.y-yOffset),immediately);this.setHeight(height,immediately);}else{// We will have margins on the Y axis\nvar width=bounds.width/displayedWidthRatio;var marginTop=0;if(anchorProperties.isVerticallyCentered){marginTop=(bounds.height-bounds.width/aspectRatio)/2;}else if(anchorProperties.isBottom){marginTop=bounds.height-bounds.width/aspectRatio;}this.setPosition(new $.Point(bounds.x-xOffset,bounds.y-yOffset+marginTop),immediately);this.setWidth(width,immediately);}},/**\n * @returns {OpenSeadragon.Rect|null} The TiledImage's current clip rectangle,\n * in image pixels, or null if none.\n */getClip:function getClip(){if(this._clip){return this._clip.clone();}return null;},/**\n * @param {OpenSeadragon.Rect|null} newClip - An area, in image pixels, to clip to\n * (portions of the image outside of this area will not be visible). Only works on\n * browsers that support the HTML5 canvas.\n * @fires OpenSeadragon.TiledImage.event:clip-change\n */setClip:function setClip(newClip){$.console.assert(!newClip||newClip instanceof $.Rect,\"[TiledImage.setClip] newClip must be an OpenSeadragon.Rect or null\");if(newClip instanceof $.Rect){this._clip=newClip.clone();}else{this._clip=null;}this._needsDraw=true;/**\n * Raised when the TiledImage's clip is changed.\n * @event clip-change\n * @memberOf OpenSeadragon.TiledImage\n * @type {object}\n * @property {OpenSeadragon.TiledImage} eventSource - A reference to the\n * TiledImage which raised the event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('clip-change');},/**\n * @returns {Number} The TiledImage's current opacity.\n */getOpacity:function getOpacity(){return this.opacity;},/**\n * @param {Number} opacity Opacity the tiled image should be drawn at.\n * @fires OpenSeadragon.TiledImage.event:opacity-change\n */setOpacity:function setOpacity(opacity){if(opacity===this.opacity){return;}this.opacity=opacity;this._needsDraw=true;/**\n * Raised when the TiledImage's opacity is changed.\n * @event opacity-change\n * @memberOf OpenSeadragon.TiledImage\n * @type {object}\n * @property {Number} opacity - The new opacity value.\n * @property {OpenSeadragon.TiledImage} eventSource - A reference to the\n * TiledImage which raised the event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('opacity-change',{opacity:this.opacity});},/**\n * @returns {Boolean} whether the tiledImage can load its tiles even when it has zero opacity.\n */getPreload:function getPreload(){return this._preload;},/**\n * Set true to load even when hidden. Set false to block loading when hidden.\n */setPreload:function setPreload(preload){this._preload=!!preload;this._needsDraw=true;},/**\n * Get the rotation of this tiled image in degrees.\n * @param {Boolean} [current=false] True for current rotation, false for target.\n * @returns {Number} the rotation of this tiled image in degrees.\n */getRotation:function getRotation(current){return current?this._degreesSpring.current.value:this._degreesSpring.target.value;},/**\n * Set the current rotation of this tiled image in degrees.\n * @param {Number} degrees the rotation in degrees.\n * @param {Boolean} [immediately=false] Whether to animate to the new angle\n * or rotate immediately.\n * @fires OpenSeadragon.TiledImage.event:bounds-change\n */setRotation:function setRotation(degrees,immediately){if(this._degreesSpring.target.value===degrees&&this._degreesSpring.isAtTargetValue()){return;}if(immediately){this._degreesSpring.resetTo(degrees);}else{this._degreesSpring.springTo(degrees);}this._needsDraw=true;this._raiseBoundsChange();},/**\n * Get the point around which this tiled image is rotated\n * @private\n * @param {Boolean} current True for current rotation point, false for target.\n * @returns {OpenSeadragon.Point}\n */_getRotationPoint:function _getRotationPoint(current){return this.getBoundsNoRotate(current).getCenter();},/**\n * @returns {String} The TiledImage's current compositeOperation.\n */getCompositeOperation:function getCompositeOperation(){return this.compositeOperation;},/**\n * @param {String} compositeOperation the tiled image should be drawn with this globalCompositeOperation.\n * @fires OpenSeadragon.TiledImage.event:composite-operation-change\n */setCompositeOperation:function setCompositeOperation(compositeOperation){if(compositeOperation===this.compositeOperation){return;}this.compositeOperation=compositeOperation;this._needsDraw=true;/**\n * Raised when the TiledImage's opacity is changed.\n * @event composite-operation-change\n * @memberOf OpenSeadragon.TiledImage\n * @type {object}\n * @property {String} compositeOperation - The new compositeOperation value.\n * @property {OpenSeadragon.TiledImage} eventSource - A reference to the\n * TiledImage which raised the event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('composite-operation-change',{compositeOperation:this.compositeOperation});},// private\n_setScale:function _setScale(scale,immediately){var sameTarget=this._scaleSpring.target.value===scale;if(immediately){if(sameTarget&&this._scaleSpring.current.value===scale){return;}this._scaleSpring.resetTo(scale);this._updateForScale();this._needsDraw=true;}else{if(sameTarget){return;}this._scaleSpring.springTo(scale);this._updateForScale();this._needsDraw=true;}if(!sameTarget){this._raiseBoundsChange();}},// private\n_updateForScale:function _updateForScale(){this._worldWidthTarget=this._scaleSpring.target.value;this._worldHeightTarget=this.normHeight*this._scaleSpring.target.value;this._worldWidthCurrent=this._scaleSpring.current.value;this._worldHeightCurrent=this.normHeight*this._scaleSpring.current.value;},// private\n_raiseBoundsChange:function _raiseBoundsChange(){/**\n * Raised when the TiledImage's bounds are changed.\n * Note that this event is triggered only when the animation target is changed;\n * not for every frame of animation.\n * @event bounds-change\n * @memberOf OpenSeadragon.TiledImage\n * @type {object}\n * @property {OpenSeadragon.TiledImage} eventSource - A reference to the\n * TiledImage which raised the event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('bounds-change');},// private\n_isBottomItem:function _isBottomItem(){return this.viewer.world.getItemAt(0)===this;},// private\n_getLevelsInterval:function _getLevelsInterval(){var lowestLevel=Math.max(this.source.minLevel,Math.floor(Math.log(this.minZoomImageRatio)/Math.log(2)));var currentZeroRatio=this.viewport.deltaPixelsFromPointsNoRotate(this.source.getPixelRatio(0),true).x*this._scaleSpring.current.value;var highestLevel=Math.min(Math.abs(this.source.maxLevel),Math.abs(Math.floor(Math.log(currentZeroRatio/this.minPixelRatio)/Math.log(2))));// Calculations for the interval of levels to draw\n// can return invalid intervals; fix that here if necessary\nlowestLevel=Math.min(lowestLevel,highestLevel);return{lowestLevel:lowestLevel,highestLevel:highestLevel};},/**\n * @private\n * @inner\n * Pretty much every other line in this needs to be documented so it's clear\n * how each piece of this routine contributes to the drawing process. That's\n * why there are so many TODO's inside this function.\n */_updateViewport:function _updateViewport(){this._needsDraw=false;this._tilesLoading=0;this.loadingCoverage={};// Reset tile's internal drawn state\nwhile(this.lastDrawn.length>0){var tile=this.lastDrawn.pop();tile.beingDrawn=false;}var viewport=this.viewport;var drawArea=this._viewportToTiledImageRectangle(viewport.getBoundsWithMargins(true));if(!this.wrapHorizontal&&!this.wrapVertical){var tiledImageBounds=this._viewportToTiledImageRectangle(this.getClippedBounds(true));drawArea=drawArea.intersection(tiledImageBounds);if(drawArea===null){return;}}var levelsInterval=this._getLevelsInterval();var lowestLevel=levelsInterval.lowestLevel;var highestLevel=levelsInterval.highestLevel;var bestTile=null;var haveDrawn=false;var currentTime=$.now();// Update any level that will be drawn\nfor(var level=highestLevel;level>=lowestLevel;level--){var drawLevel=false;//Avoid calculations for draw if we have already drawn this\nvar currentRenderPixelRatio=viewport.deltaPixelsFromPointsNoRotate(this.source.getPixelRatio(level),true).x*this._scaleSpring.current.value;if(level===lowestLevel||!haveDrawn&¤tRenderPixelRatio>=this.minPixelRatio){drawLevel=true;haveDrawn=true;}else if(!haveDrawn){continue;}//Perform calculations for draw if we haven't drawn this\nvar targetRenderPixelRatio=viewport.deltaPixelsFromPointsNoRotate(this.source.getPixelRatio(level),false).x*this._scaleSpring.current.value;var targetZeroRatio=viewport.deltaPixelsFromPointsNoRotate(this.source.getPixelRatio(Math.max(this.source.getClosestLevel(),0)),false).x*this._scaleSpring.current.value;var optimalRatio=this.immediateRender?1:targetZeroRatio;var levelOpacity=Math.min(1,(currentRenderPixelRatio-0.5)/0.5);var levelVisibility=optimalRatio/Math.abs(optimalRatio-targetRenderPixelRatio);// Update the level and keep track of 'best' tile to load\nbestTile=updateLevel(this,haveDrawn,drawLevel,level,levelOpacity,levelVisibility,drawArea,currentTime,bestTile);// Stop the loop if lower-res tiles would all be covered by\n// already drawn tiles\nif(providesCoverage(this.coverage,level)){break;}}// Perform the actual drawing\ndrawTiles(this,this.lastDrawn);// Load the new 'best' tile\nif(bestTile&&!bestTile.context2D){loadTile(this,bestTile,currentTime);this._needsDraw=true;this._setFullyLoaded(false);}else{this._setFullyLoaded(this._tilesLoading===0);}},// private\n_getCornerTiles:function _getCornerTiles(level,topLeftBound,bottomRightBound){var leftX;var rightX;if(this.wrapHorizontal){leftX=$.positiveModulo(topLeftBound.x,1);rightX=$.positiveModulo(bottomRightBound.x,1);}else{leftX=Math.max(0,topLeftBound.x);rightX=Math.min(1,bottomRightBound.x);}var topY;var bottomY;var aspectRatio=1/this.source.aspectRatio;if(this.wrapVertical){topY=$.positiveModulo(topLeftBound.y,aspectRatio);bottomY=$.positiveModulo(bottomRightBound.y,aspectRatio);}else{topY=Math.max(0,topLeftBound.y);bottomY=Math.min(aspectRatio,bottomRightBound.y);}var topLeftTile=this.source.getTileAtPoint(level,new $.Point(leftX,topY));var bottomRightTile=this.source.getTileAtPoint(level,new $.Point(rightX,bottomY));var numTiles=this.source.getNumTiles(level);if(this.wrapHorizontal){topLeftTile.x+=numTiles.x*Math.floor(topLeftBound.x);bottomRightTile.x+=numTiles.x*Math.floor(bottomRightBound.x);}if(this.wrapVertical){topLeftTile.y+=numTiles.y*Math.floor(topLeftBound.y/aspectRatio);bottomRightTile.y+=numTiles.y*Math.floor(bottomRightBound.y/aspectRatio);}return{topLeft:topLeftTile,bottomRight:bottomRightTile};}});/**\n * @private\n * @inner\n * Updates all tiles at a given resolution level.\n * @param {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.\n * @param {Boolean} haveDrawn\n * @param {Boolean} drawLevel\n * @param {Number} level\n * @param {Number} levelOpacity\n * @param {Number} levelVisibility\n * @param {OpenSeadragon.Point} viewportTL - The index of the most top-left visible tile.\n * @param {OpenSeadragon.Point} viewportBR - The index of the most bottom-right visible tile.\n * @param {Number} currentTime\n * @param {OpenSeadragon.Tile} best - The current \"best\" tile to draw.\n */function updateLevel(tiledImage,haveDrawn,drawLevel,level,levelOpacity,levelVisibility,drawArea,currentTime,best){var topLeftBound=drawArea.getBoundingBox().getTopLeft();var bottomRightBound=drawArea.getBoundingBox().getBottomRight();if(tiledImage.viewer){/**\n * - Needs documentation -\n *\n * @event update-level\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.\n * @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.\n * @property {Object} havedrawn\n * @property {Object} level\n * @property {Object} opacity\n * @property {Object} visibility\n * @property {OpenSeadragon.Rect} drawArea\n * @property {Object} topleft deprecated, use drawArea instead\n * @property {Object} bottomright deprecated, use drawArea instead\n * @property {Object} currenttime\n * @property {Object} best\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */tiledImage.viewer.raiseEvent('update-level',{tiledImage:tiledImage,havedrawn:haveDrawn,level:level,opacity:levelOpacity,visibility:levelVisibility,drawArea:drawArea,topleft:topLeftBound,bottomright:bottomRightBound,currenttime:currentTime,best:best});}resetCoverage(tiledImage.coverage,level);resetCoverage(tiledImage.loadingCoverage,level);//OK, a new drawing so do your calculations\nvar cornerTiles=tiledImage._getCornerTiles(level,topLeftBound,bottomRightBound);var topLeftTile=cornerTiles.topLeft;var bottomRightTile=cornerTiles.bottomRight;var numberOfTiles=tiledImage.source.getNumTiles(level);var viewportCenter=tiledImage.viewport.pixelFromPoint(tiledImage.viewport.getCenter());for(var x=topLeftTile.x;x<=bottomRightTile.x;x++){for(var y=topLeftTile.y;y<=bottomRightTile.y;y++){// Optimisation disabled with wrapping because getTileBounds does not\n// work correctly with x and y outside of the number of tiles\nif(!tiledImage.wrapHorizontal&&!tiledImage.wrapVertical){var tileBounds=tiledImage.source.getTileBounds(level,x,y);if(drawArea.intersection(tileBounds)===null){// This tile is outside of the viewport, no need to draw it\ncontinue;}}best=updateTile(tiledImage,drawLevel,haveDrawn,x,y,level,levelOpacity,levelVisibility,viewportCenter,numberOfTiles,currentTime,best);}}return best;}/**\n * @private\n * @inner\n * Update a single tile at a particular resolution level.\n * @param {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.\n * @param {Boolean} haveDrawn\n * @param {Boolean} drawLevel\n * @param {Number} x\n * @param {Number} y\n * @param {Number} level\n * @param {Number} levelOpacity\n * @param {Number} levelVisibility\n * @param {OpenSeadragon.Point} viewportCenter\n * @param {Number} numberOfTiles\n * @param {Number} currentTime\n * @param {OpenSeadragon.Tile} best - The current \"best\" tile to draw.\n */function updateTile(tiledImage,haveDrawn,drawLevel,x,y,level,levelOpacity,levelVisibility,viewportCenter,numberOfTiles,currentTime,best){var tile=getTile(x,y,level,tiledImage,tiledImage.source,tiledImage.tilesMatrix,currentTime,numberOfTiles,tiledImage._worldWidthCurrent,tiledImage._worldHeightCurrent),drawTile=drawLevel;if(tiledImage.viewer){/**\n * - Needs documentation -\n *\n * @event update-tile\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.\n * @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.\n * @property {OpenSeadragon.Tile} tile\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */tiledImage.viewer.raiseEvent('update-tile',{tiledImage:tiledImage,tile:tile});}setCoverage(tiledImage.coverage,level,x,y,false);var loadingCoverage=tile.loaded||tile.loading||isCovered(tiledImage.loadingCoverage,level,x,y);setCoverage(tiledImage.loadingCoverage,level,x,y,loadingCoverage);if(!tile.exists){return best;}if(haveDrawn&&!drawTile){if(isCovered(tiledImage.coverage,level,x,y)){setCoverage(tiledImage.coverage,level,x,y,true);}else{drawTile=true;}}if(!drawTile){return best;}positionTile(tile,tiledImage.source.tileOverlap,tiledImage.viewport,viewportCenter,levelVisibility,tiledImage);if(!tile.loaded){if(tile.context2D){setTileLoaded(tiledImage,tile);}else{var imageRecord=tiledImage._tileCache.getImageRecord(tile.cacheKey);if(imageRecord){var image=imageRecord.getImage();setTileLoaded(tiledImage,tile,image);}}}if(tile.loaded){var needsDraw=blendTile(tiledImage,tile,x,y,level,levelOpacity,currentTime);if(needsDraw){tiledImage._needsDraw=true;}}else if(tile.loading){// the tile is already in the download queue\ntiledImage._tilesLoading++;}else if(!loadingCoverage){best=compareTiles(best,tile);}return best;}/**\n * @private\n * @inner\n * Obtains a tile at the given location.\n * @param {Number} x\n * @param {Number} y\n * @param {Number} level\n * @param {OpenSeadragon.TiledImage} tiledImage\n * @param {OpenSeadragon.TileSource} tileSource\n * @param {Object} tilesMatrix - A '3d' dictionary [level][x][y] --> Tile.\n * @param {Number} time\n * @param {Number} numTiles\n * @param {Number} worldWidth\n * @param {Number} worldHeight\n * @returns {OpenSeadragon.Tile}\n */function getTile(x,y,level,tiledImage,tileSource,tilesMatrix,time,numTiles,worldWidth,worldHeight){var xMod,yMod,bounds,sourceBounds,exists,url,ajaxHeaders,context2D,tile;if(!tilesMatrix[level]){tilesMatrix[level]={};}if(!tilesMatrix[level][x]){tilesMatrix[level][x]={};}if(!tilesMatrix[level][x][y]){xMod=(numTiles.x+x%numTiles.x)%numTiles.x;yMod=(numTiles.y+y%numTiles.y)%numTiles.y;bounds=tileSource.getTileBounds(level,xMod,yMod);sourceBounds=tileSource.getTileBounds(level,xMod,yMod,true);exists=tileSource.tileExists(level,xMod,yMod);url=tileSource.getTileUrl(level,xMod,yMod);// Headers are only applicable if loadTilesWithAjax is set\nif(tiledImage.loadTilesWithAjax){ajaxHeaders=tileSource.getTileAjaxHeaders(level,xMod,yMod);// Combine tile AJAX headers with tiled image AJAX headers (if applicable)\nif($.isPlainObject(tiledImage.ajaxHeaders)){ajaxHeaders=$.extend({},tiledImage.ajaxHeaders,ajaxHeaders);}}else{ajaxHeaders=null;}context2D=tileSource.getContext2D?tileSource.getContext2D(level,xMod,yMod):undefined;bounds.x+=(x-xMod)/numTiles.x;bounds.y+=worldHeight/worldWidth*((y-yMod)/numTiles.y);tile=new $.Tile(level,x,y,bounds,exists,url,context2D,tiledImage.loadTilesWithAjax,ajaxHeaders,sourceBounds);if(xMod===numTiles.x-1){tile.isRightMost=true;}if(yMod===numTiles.y-1){tile.isBottomMost=true;}tilesMatrix[level][x][y]=tile;}tile=tilesMatrix[level][x][y];tile.lastTouchTime=time;return tile;}/**\n * @private\n * @inner\n * Dispatch a job to the ImageLoader to load the Image for a Tile.\n * @param {OpenSeadragon.TiledImage} tiledImage\n * @param {OpenSeadragon.Tile} tile\n * @param {Number} time\n */function loadTile(tiledImage,tile,time){tile.loading=true;var customAjax;// Bind tiledImage if filtering Ajax\nif($.isFunction(tiledImage.makeAjaxRequest)){customAjax=tiledImage.makeAjaxRequest;}tiledImage._imageLoader.addJob({src:tile.url,makeAjaxRequest:customAjax,loadWithAjax:tile.loadWithAjax,ajaxHeaders:tile.ajaxHeaders,crossOriginPolicy:tiledImage.crossOriginPolicy,ajaxWithCredentials:tiledImage.ajaxWithCredentials,callback:function callback(image,errorMsg,tileRequest){onTileLoad(tiledImage,tile,time,image,errorMsg,tileRequest);},abort:function abort(){tile.loading=false;}});}/**\n * @private\n * @inner\n * Callback fired when a Tile's Image finished downloading.\n * @param {OpenSeadragon.TiledImage} tiledImage\n * @param {OpenSeadragon.Tile} tile\n * @param {Number} time\n * @param {Image} image\n * @param {String} errorMsg\n * @param {XMLHttpRequest} tileRequest\n */function onTileLoad(tiledImage,tile,time,image,errorMsg,tileRequest){if(!image){$.console.log(\"Tile %s failed to load: %s - error: %s\",tile,tile.url,errorMsg);/**\n * Triggered when a tile fails to load.\n *\n * @event tile-load-failed\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Tile} tile - The tile that failed to load.\n * @property {OpenSeadragon.TiledImage} tiledImage - The tiled image the tile belongs to.\n * @property {number} time - The time in milliseconds when the tile load began.\n * @property {string} message - The error message.\n * @property {XMLHttpRequest} tileRequest - The XMLHttpRequest used to load the tile if available.\n */tiledImage.viewer.raiseEvent(\"tile-load-failed\",{tile:tile,tiledImage:tiledImage,time:time,message:errorMsg,tileRequest:tileRequest});tile.loading=false;tile.exists=false;return;}if(time Boolean.\n * @param {Number} level - The resolution level of the tile.\n * @param {Number} x - The X position of the tile.\n * @param {Number} y - The Y position of the tile.\n * @returns {Boolean}\n */function providesCoverage(coverage,level,x,y){var rows,cols,i,j;if(!coverage[level]){return false;}if(x===undefined||y===undefined){rows=coverage[level];for(i in rows){if(rows.hasOwnProperty(i)){cols=rows[i];for(j in cols){if(cols.hasOwnProperty(j)&&!cols[j]){return false;}}}}return true;}return coverage[level][x]===undefined||coverage[level][x][y]===undefined||coverage[level][x][y]===true;}/**\n * @private\n * @inner\n * Returns true if the given tile is completely covered by higher-level\n * tiles of higher resolution representing the same content. If neither x\n * nor y is given, returns true if the entire visible level is covered.\n *\n * @param {Object} coverage - A '3d' dictionary [level][x][y] --> Boolean.\n * @param {Number} level - The resolution level of the tile.\n * @param {Number} x - The X position of the tile.\n * @param {Number} y - The Y position of the tile.\n * @returns {Boolean}\n */function isCovered(coverage,level,x,y){if(x===undefined||y===undefined){return providesCoverage(coverage,level+1);}else{return providesCoverage(coverage,level+1,2*x,2*y)&&providesCoverage(coverage,level+1,2*x,2*y+1)&&providesCoverage(coverage,level+1,2*x+1,2*y)&&providesCoverage(coverage,level+1,2*x+1,2*y+1);}}/**\n * @private\n * @inner\n * Sets whether the given tile provides coverage or not.\n *\n * @param {Object} coverage - A '3d' dictionary [level][x][y] --> Boolean.\n * @param {Number} level - The resolution level of the tile.\n * @param {Number} x - The X position of the tile.\n * @param {Number} y - The Y position of the tile.\n * @param {Boolean} covers - Whether the tile provides coverage.\n */function setCoverage(coverage,level,x,y,covers){if(!coverage[level]){$.console.warn(\"Setting coverage for a tile before its level's coverage has been reset: %s\",level);return;}if(!coverage[level][x]){coverage[level][x]={};}coverage[level][x][y]=covers;}/**\n * @private\n * @inner\n * Resets coverage information for the given level. This should be called\n * after every draw routine. Note that at the beginning of the next draw\n * routine, coverage for every visible tile should be explicitly set.\n *\n * @param {Object} coverage - A '3d' dictionary [level][x][y] --> Boolean.\n * @param {Number} level - The resolution level of tiles to completely reset.\n */function resetCoverage(coverage,level){coverage[level]={};}/**\n * @private\n * @inner\n * Determines whether the 'last best' tile for the area is better than the\n * tile in question.\n *\n * @param {OpenSeadragon.Tile} previousBest\n * @param {OpenSeadragon.Tile} tile\n * @returns {OpenSeadragon.Tile} The new best tile.\n */function compareTiles(previousBest,tile){if(!previousBest){return tile;}if(tile.visibility>previousBest.visibility){return tile;}else if(tile.visibility==previousBest.visibility){if(tile.squaredDistance1&&imageZoom>tiledImage.smoothTileEdgesMinZoom&&!tiledImage.iOSDevice&&tiledImage.getRotation(true)%360===0&&// TODO: support tile edge smoothing with tiled image rotation.\n$.supportsCanvas){// When zoomed in a lot (>100%) the tile edges are visible.\n// So we have to composite them at ~100% and scale them up together.\n// Note: Disabled on iOS devices per default as it causes a native crash\nuseSketch=true;sketchScale=tile.getScaleForEdgeSmoothing();sketchTranslate=tile.getTranslationForEdgeSmoothing(sketchScale,tiledImage._drawer.getCanvasSize(false),tiledImage._drawer.getCanvasSize(true));}var bounds;if(useSketch){if(!sketchScale){// Except when edge smoothing, we only clean the part of the\n// sketch canvas we are going to use for performance reasons.\nbounds=tiledImage.viewport.viewportToViewerElementRectangle(tiledImage.getClippedBounds(true)).getIntegerBoundingBox().times($.pixelDensityRatio);}tiledImage._drawer._clear(true,bounds);}// When scaling, we must rotate only when blending the sketch canvas to\n// avoid interpolation\nif(!sketchScale){if(tiledImage.viewport.degrees!==0){tiledImage._drawer._offsetForRotation({degrees:tiledImage.viewport.degrees,useSketch:useSketch});}else{if(tiledImage._drawer.viewer.viewport.flipped){tiledImage._drawer._flip({});}}if(tiledImage.getRotation(true)%360!==0){tiledImage._drawer._offsetForRotation({degrees:tiledImage.getRotation(true),point:tiledImage.viewport.pixelFromPointNoRotate(tiledImage._getRotationPoint(true),true),useSketch:useSketch});}}var usedClip=false;if(tiledImage._clip){tiledImage._drawer.saveContext(useSketch);var box=tiledImage.imageToViewportRectangle(tiledImage._clip,true);box=box.rotate(-tiledImage.getRotation(true),tiledImage._getRotationPoint(true));var clipRect=tiledImage._drawer.viewportToDrawerRectangle(box);if(sketchScale){clipRect=clipRect.times(sketchScale);}if(sketchTranslate){clipRect=clipRect.translate(sketchTranslate);}tiledImage._drawer.setClip(clipRect,useSketch);usedClip=true;}if(tiledImage.placeholderFillStyle&&tiledImage._hasOpaqueTile===false){var placeholderRect=tiledImage._drawer.viewportToDrawerRectangle(tiledImage.getBounds(true));if(sketchScale){placeholderRect=placeholderRect.times(sketchScale);}if(sketchTranslate){placeholderRect=placeholderRect.translate(sketchTranslate);}var fillStyle=null;if(typeof tiledImage.placeholderFillStyle===\"function\"){fillStyle=tiledImage.placeholderFillStyle(tiledImage,tiledImage._drawer.context);}else{fillStyle=tiledImage.placeholderFillStyle;}tiledImage._drawer.drawRectangle(placeholderRect,fillStyle,useSketch);}for(var i=lastDrawn.length-1;i>=0;i--){tile=lastDrawn[i];tiledImage._drawer.drawTile(tile,tiledImage._drawingHandler,useSketch,sketchScale,sketchTranslate);tile.beingDrawn=true;if(tiledImage.viewer){/**\n * - Needs documentation -\n *\n * @event tile-drawn\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.\n * @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.\n * @property {OpenSeadragon.Tile} tile\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */tiledImage.viewer.raiseEvent('tile-drawn',{tiledImage:tiledImage,tile:tile});}}if(usedClip){tiledImage._drawer.restoreContext(useSketch);}if(!sketchScale){if(tiledImage.getRotation(true)%360!==0){tiledImage._drawer._restoreRotationChanges(useSketch);}if(tiledImage.viewport.degrees!==0){tiledImage._drawer._restoreRotationChanges(useSketch);}else{if(tiledImage._drawer.viewer.viewport.flipped){tiledImage._drawer._flip({});}}}if(useSketch){if(sketchScale){if(tiledImage.viewport.degrees!==0){tiledImage._drawer._offsetForRotation({degrees:tiledImage.viewport.degrees,useSketch:false});}if(tiledImage.getRotation(true)%360!==0){tiledImage._drawer._offsetForRotation({degrees:tiledImage.getRotation(true),point:tiledImage.viewport.pixelFromPointNoRotate(tiledImage._getRotationPoint(true),true),useSketch:false});}}tiledImage._drawer.blendSketch({opacity:tiledImage.opacity,scale:sketchScale,translate:sketchTranslate,compositeOperation:tiledImage.compositeOperation,bounds:bounds});if(sketchScale){if(tiledImage.getRotation(true)%360!==0){tiledImage._drawer._restoreRotationChanges(false);}if(tiledImage.viewport.degrees!==0){tiledImage._drawer._restoreRotationChanges(false);}}}drawDebugInfo(tiledImage,lastDrawn);}/**\n * @private\n * @inner\n * Draws special debug information for a TiledImage if in debug mode.\n * @param {OpenSeadragon.TiledImage} tiledImage\n * @param {OpenSeadragon.Tile[]} lastDrawn - An unordered list of Tiles drawn last frame.\n */function drawDebugInfo(tiledImage,lastDrawn){if(tiledImage.debugMode){for(var i=lastDrawn.length-1;i>=0;i--){var tile=lastDrawn[i];try{tiledImage._drawer.drawDebugInfo(tile,lastDrawn.length,i,tiledImage);}catch(e){$.console.error(e);}}}}})(OpenSeadragon);/*\n * OpenSeadragon - TileCache\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){// private class\nvar TileRecord=function TileRecord(options){$.console.assert(options,\"[TileCache.cacheTile] options is required\");$.console.assert(options.tile,\"[TileCache.cacheTile] options.tile is required\");$.console.assert(options.tiledImage,\"[TileCache.cacheTile] options.tiledImage is required\");this.tile=options.tile;this.tiledImage=options.tiledImage;};// private class\nvar ImageRecord=function ImageRecord(options){$.console.assert(options,\"[ImageRecord] options is required\");$.console.assert(options.image,\"[ImageRecord] options.image is required\");this._image=options.image;this._tiles=[];};ImageRecord.prototype={destroy:function destroy(){this._image=null;this._renderedContext=null;this._tiles=null;},getImage:function getImage(){return this._image;},getRenderedContext:function getRenderedContext(){if(!this._renderedContext){var canvas=document.createElement('canvas');canvas.width=this._image.width;canvas.height=this._image.height;this._renderedContext=canvas.getContext('2d');this._renderedContext.drawImage(this._image,0,0);//since we are caching the prerendered image on a canvas\n//allow the image to not be held in memory\nthis._image=null;}return this._renderedContext;},setRenderedContext:function setRenderedContext(renderedContext){$.console.error(\"ImageRecord.setRenderedContext is deprecated. \"+\"The rendered context should be created by the ImageRecord \"+\"itself when calling ImageRecord.getRenderedContext.\");this._renderedContext=renderedContext;},addTile:function addTile(tile){$.console.assert(tile,'[ImageRecord.addTile] tile is required');this._tiles.push(tile);},removeTile:function removeTile(tile){for(var i=0;ithis._maxImageCacheCount){var worstTile=null;var worstTileIndex=-1;var worstTileRecord=null;var prevTile,worstTime,worstLevel,prevTime,prevLevel,prevTileRecord;for(var i=this._tilesLoaded.length-1;i>=0;i--){prevTileRecord=this._tilesLoaded[i];prevTile=prevTileRecord.tile;if(prevTile.level<=cutoff||prevTile.beingDrawn){continue;}else if(!worstTile){worstTile=prevTile;worstTileIndex=i;worstTileRecord=prevTileRecord;continue;}prevTime=prevTile.lastTouchTime;worstTime=worstTile.lastTouchTime;prevLevel=prevTile.level;worstLevel=worstTile.level;if(prevTimeworstLevel){worstTile=prevTile;worstTileIndex=i;worstTileRecord=prevTileRecord;}}if(worstTile&&worstTileIndex>=0){this._unloadTile(worstTileRecord);insertionIndex=worstTileIndex;}}this._tilesLoaded[insertionIndex]=new TileRecord({tile:options.tile,tiledImage:options.tiledImage});},/**\n * Clears all tiles associated with the specified tiledImage.\n * @param {OpenSeadragon.TiledImage} tiledImage\n */clearTilesFor:function clearTilesFor(tiledImage){$.console.assert(tiledImage,'[TileCache.clearTilesFor] tiledImage is required');var tileRecord;for(var i=0;i=this._items.length){throw new Error(\"Index bigger than number of layers.\");}if(index===oldIndex||oldIndex===-1){return;}this._items.splice(oldIndex,1);this._items.splice(index,0,item);this._needsDraw=true;/**\n * Raised when the order of the indexes has been changed.\n * @event item-index-change\n * @memberOf OpenSeadragon.World\n * @type {object}\n * @property {OpenSeadragon.World} eventSource - A reference to the World which raised the event.\n * @property {OpenSeadragon.TiledImage} item - The item whose index has\n * been changed\n * @property {Number} previousIndex - The previous index of the item\n * @property {Number} newIndex - The new index of the item\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('item-index-change',{item:item,previousIndex:oldIndex,newIndex:index});},/**\n * Remove an item.\n * @param {OpenSeadragon.TiledImage} item - The item to remove.\n * @fires OpenSeadragon.World.event:remove-item\n * @fires OpenSeadragon.World.event:metrics-change\n */removeItem:function removeItem(item){$.console.assert(item,\"[World.removeItem] item is required\");var index=$.indexOf(this._items,item);if(index===-1){return;}item.removeHandler('bounds-change',this._delegatedFigureSizes);item.removeHandler('clip-change',this._delegatedFigureSizes);item.destroy();this._items.splice(index,1);this._figureSizes();this._needsDraw=true;this._raiseRemoveItem(item);},/**\n * Remove all items.\n * @fires OpenSeadragon.World.event:remove-item\n * @fires OpenSeadragon.World.event:metrics-change\n */removeAll:function removeAll(){// We need to make sure any pending images are canceled so the world items don't get messed up\nthis.viewer._cancelPendingImages();var item;var i;for(i=0;ibox.height){width=tileSize;}else{width=tileSize*(box.width/box.height);}height=width*(box.height/box.width);position=new $.Point(x+(tileSize-width)/2,y+(tileSize-height)/2);item.setPosition(position,immediately);item.setWidth(width,immediately);if(layout==='horizontal'){x+=increment;}else{y+=increment;}}this.setAutoRefigureSizes(true);},// private\n_figureSizes:function _figureSizes(){var oldHomeBounds=this._homeBounds?this._homeBounds.clone():null;var oldContentSize=this._contentSize?this._contentSize.clone():null;var oldContentFactor=this._contentFactor||0;if(!this._items.length){this._homeBounds=new $.Rect(0,0,1,1);this._contentSize=new $.Point(1,1);this._contentFactor=1;}else{var item=this._items[0];var bounds=item.getBounds();this._contentFactor=item.getContentSize().x/bounds.width;var clippedBounds=item.getClippedBounds().getBoundingBox();var left=clippedBounds.x;var top=clippedBounds.y;var right=clippedBounds.x+clippedBounds.width;var bottom=clippedBounds.y+clippedBounds.height;for(var i=1;iDEPRECATED. A relative path to load a DZI file from the server.\n * Prefer the newer Options.tileSources.\n *\n * @property {String} [prefixUrl='/images/']\n * Prepends the prefixUrl to navImages paths, which is very useful\n * since the default paths are rarely useful for production\n * environments.\n *\n * @property {OpenSeadragon.NavImages} [navImages]\n * An object with a property for each button or other built-in navigation\n * control, eg the current 'zoomIn', 'zoomOut', 'home', and 'fullpage'.\n * Each of those in turn provides an image path for each state of the button\n * or navigation control, eg 'REST', 'GROUP', 'HOVER', 'PRESS'. Finally the\n * image paths, by default assume there is a folder on the servers root path\n * called '/images', eg '/images/zoomin_rest.png'. If you need to adjust\n * these paths, prefer setting the option.prefixUrl rather than overriding\n * every image path directly through this setting.\n *\n * @property {Boolean} [debugMode=false]\n * TODO: provide an in-screen panel providing event detail feedback.\n *\n * @property {String} [debugGridColor=['#437AB2', '#1B9E77', '#D95F02', '#7570B3', '#E7298A', '#66A61E', '#E6AB02', '#A6761D', '#666666']]\n * The colors of grids in debug mode. Each tiled image's grid uses a consecutive color.\n * If there are more tiled images than provided colors, the color vector is recycled.\n *\n * @property {Number} [blendTime=0]\n * Specifies the duration of animation as higher or lower level tiles are\n * replacing the existing tile.\n *\n * @property {Boolean} [alwaysBlend=false]\n * Forces the tile to always blend. By default the tiles skip blending\n * when the blendTime is surpassed and the current animation frame would\n * not complete the blend.\n *\n * @property {Boolean} [autoHideControls=true]\n * If the user stops interacting with the viewport, fade the navigation\n * controls. Useful for presentation since the controls are by default\n * floated on top of the image the user is viewing.\n *\n * @property {Boolean} [immediateRender=false]\n * Render the best closest level first, ignoring the lowering levels which\n * provide the effect of very blurry to sharp. It is recommended to change\n * setting to true for mobile devices.\n *\n * @property {Number} [defaultZoomLevel=0]\n * Zoom level to use when image is first opened or the home button is clicked.\n * If 0, adjusts to fit viewer.\n *\n * @property {Number} [opacity=1]\n * Default proportional opacity of the tiled images (1=opaque, 0=hidden)\n * Hidden images do not draw and only load when preloading is allowed.\n *\n * @property {Boolean} [preload=false]\n * Default switch for loading hidden images (true loads, false blocks)\n *\n * @property {String} [compositeOperation=null]\n * Valid values are 'source-over', 'source-atop', 'source-in', 'source-out',\n * 'destination-over', 'destination-atop', 'destination-in',\n * 'destination-out', 'lighter', 'copy' or 'xor'\n *\n * @property {String|CanvasGradient|CanvasPattern|Function} [placeholderFillStyle=null]\n * Draws a colored rectangle behind the tile if it is not loaded yet.\n * You can pass a CSS color value like \"#FF8800\".\n * When passing a function the tiledImage and canvas context are available as argument which is useful when you draw a gradient or pattern.\n *\n * @property {Number} [degrees=0]\n * Initial rotation.\n *\n * @property {Boolean} [flipped=false]\n * Initial flip state.\n *\n * @property {Number} [minZoomLevel=null]\n *\n * @property {Number} [maxZoomLevel=null]\n *\n * @property {Boolean} [homeFillsViewer=false]\n * Make the 'home' button fill the viewer and clip the image, instead\n * of fitting the image to the viewer and letterboxing.\n *\n * @property {Boolean} [panHorizontal=true]\n * Allow horizontal pan.\n *\n * @property {Boolean} [panVertical=true]\n * Allow vertical pan.\n *\n * @property {Boolean} [constrainDuringPan=false]\n *\n * @property {Boolean} [wrapHorizontal=false]\n * Set to true to force the image to wrap horizontally within the viewport.\n * Useful for maps or images representing the surface of a sphere or cylinder.\n *\n * @property {Boolean} [wrapVertical=false]\n * Set to true to force the image to wrap vertically within the viewport.\n * Useful for maps or images representing the surface of a sphere or cylinder.\n *\n * @property {Number} [minZoomImageRatio=0.9]\n * The minimum percentage ( expressed as a number between 0 and 1 ) of\n * the viewport height or width at which the zoom out will be constrained.\n * Setting it to 0, for example will allow you to zoom out infinity.\n *\n * @property {Number} [maxZoomPixelRatio=1.1]\n * The maximum ratio to allow a zoom-in to affect the highest level pixel\n * ratio. This can be set to Infinity to allow 'infinite' zooming into the\n * image though it is less effective visually if the HTML5 Canvas is not\n * availble on the viewing device.\n *\n * @property {Number} [smoothTileEdgesMinZoom=1.1]\n * A zoom percentage ( where 1 is 100% ) of the highest resolution level.\n * When zoomed in beyond this value alternative compositing will be used to\n * smooth out the edges between tiles. This will have a performance impact.\n * Can be set to Infinity to turn it off.\n * Note: This setting is ignored on iOS devices due to a known bug (See {@link https://github.com/openseadragon/openseadragon/issues/952})\n *\n * @property {Boolean} [iOSDevice=?]\n * True if running on an iOS device, false otherwise.\n * Used to disable certain features that behave differently on iOS devices.\n *\n * @property {Boolean} [autoResize=true]\n * Set to false to prevent polling for viewer size changes. Useful for providing custom resize behavior.\n *\n * @property {Boolean} [preserveImageSizeOnResize=false]\n * Set to true to have the image size preserved when the viewer is resized. This requires autoResize=true (default).\n *\n * @property {Number} [minScrollDeltaTime=50]\n * Number of milliseconds between canvas-scroll events. This value helps normalize the rate of canvas-scroll\n * events between different devices, causing the faster devices to slow down enough to make the zoom control\n * more manageable.\n *\n * @property {Number} [pixelsPerWheelLine=40]\n * For pixel-resolution scrolling devices, the number of pixels equal to one scroll line.\n *\n * @property {Number} [pixelsPerArrowPress=40]\n * The number of pixels viewport moves when an arrow key is pressed.\n *\n * @property {Number} [visibilityRatio=0.5]\n * The percentage ( as a number from 0 to 1 ) of the source image which\n * must be kept within the viewport. If the image is dragged beyond that\n * limit, it will 'bounce' back until the minimum visibility ratio is\n * achieved. Setting this to 0 and wrapHorizontal ( or wrapVertical ) to\n * true will provide the effect of an infinitely scrolling viewport.\n *\n * @property {Object} [viewportMargins={}]\n * Pushes the \"home\" region in from the sides by the specified amounts.\n * Possible subproperties (Numbers, in screen coordinates): left, top, right, bottom.\n *\n * @property {Number} [imageLoaderLimit=0]\n * The maximum number of image requests to make concurrently. By default\n * it is set to 0 allowing the browser to make the maximum number of\n * image requests in parallel as allowed by the browsers policy.\n *\n * @property {Number} [clickTimeThreshold=300]\n * The number of milliseconds within which a pointer down-up event combination\n * will be treated as a click gesture.\n *\n * @property {Number} [clickDistThreshold=5]\n * The maximum distance allowed between a pointer down event and a pointer up event\n * to be treated as a click gesture.\n *\n * @property {Number} [dblClickTimeThreshold=300]\n * The number of milliseconds within which two pointer down-up event combinations\n * will be treated as a double-click gesture.\n *\n * @property {Number} [dblClickDistThreshold=20]\n * The maximum distance allowed between two pointer click events\n * to be treated as a double-click gesture.\n *\n * @property {Number} [springStiffness=6.5]\n *\n * @property {Number} [animationTime=1.2]\n * Specifies the animation duration per each {@link OpenSeadragon.Spring}\n * which occur when the image is dragged or zoomed.\n *\n * @property {OpenSeadragon.GestureSettings} [gestureSettingsMouse]\n * Settings for gestures generated by a mouse pointer device. (See {@link OpenSeadragon.GestureSettings})\n * @property {Boolean} [gestureSettingsMouse.scrollToZoom=true] - Zoom on scroll gesture\n * @property {Boolean} [gestureSettingsMouse.clickToZoom=true] - Zoom on click gesture\n * @property {Boolean} [gestureSettingsMouse.dblClickToZoom=false] - Zoom on double-click gesture. Note: If set to true\n * then clickToZoom should be set to false to prevent multiple zooms.\n * @property {Boolean} [gestureSettingsMouse.pinchToZoom=false] - Zoom on pinch gesture\n * @property {Boolean} [gestureSettingsMouse.zoomToRefPoint=true] - If zoomToRefPoint is true, the zoom is centered at the pointer position. Otherwise,\n * the zoom is centered at the canvas center.\n * @property {Boolean} [gestureSettingsMouse.flickEnabled=false] - Enable flick gesture\n * @property {Number} [gestureSettingsMouse.flickMinSpeed=120] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)\n * @property {Number} [gestureSettingsMouse.flickMomentum=0.25] - If flickEnabled is true, the momentum factor for the flick gesture\n * @property {Boolean} [gestureSettingsMouse.pinchRotate=false] - If pinchRotate is true, the user will have the ability to rotate the image using their fingers.\n *\n * @property {OpenSeadragon.GestureSettings} [gestureSettingsTouch]\n * Settings for gestures generated by a touch pointer device. (See {@link OpenSeadragon.GestureSettings})\n * @property {Boolean} [gestureSettingsTouch.scrollToZoom=false] - Zoom on scroll gesture\n * @property {Boolean} [gestureSettingsTouch.clickToZoom=false] - Zoom on click gesture\n * @property {Boolean} [gestureSettingsTouch.dblClickToZoom=true] - Zoom on double-click gesture. Note: If set to true\n * then clickToZoom should be set to false to prevent multiple zooms.\n * @property {Boolean} [gestureSettingsTouch.pinchToZoom=true] - Zoom on pinch gesture\n * @property {Boolean} [gestureSettingsTouch.zoomToRefPoint=true] - If zoomToRefPoint is true, the zoom is centered at the pointer position. Otherwise,\n * the zoom is centered at the canvas center.\n * @property {Boolean} [gestureSettingsTouch.flickEnabled=true] - Enable flick gesture\n * @property {Number} [gestureSettingsTouch.flickMinSpeed=120] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)\n * @property {Number} [gestureSettingsTouch.flickMomentum=0.25] - If flickEnabled is true, the momentum factor for the flick gesture\n * @property {Boolean} [gestureSettingsTouch.pinchRotate=false] - If pinchRotate is true, the user will have the ability to rotate the image using their fingers.\n *\n * @property {OpenSeadragon.GestureSettings} [gestureSettingsPen]\n * Settings for gestures generated by a pen pointer device. (See {@link OpenSeadragon.GestureSettings})\n * @property {Boolean} [gestureSettingsPen.scrollToZoom=false] - Zoom on scroll gesture\n * @property {Boolean} [gestureSettingsPen.clickToZoom=true] - Zoom on click gesture\n * @property {Boolean} [gestureSettingsPen.dblClickToZoom=false] - Zoom on double-click gesture. Note: If set to true\n * then clickToZoom should be set to false to prevent multiple zooms.\n * @property {Boolean} [gestureSettingsPen.pinchToZoom=false] - Zoom on pinch gesture\n * @property {Boolean} [gestureSettingsPan.zoomToRefPoint=true] - If zoomToRefPoint is true, the zoom is centered at the pointer position. Otherwise,\n * the zoom is centered at the canvas center.\n * @property {Boolean} [gestureSettingsPen.flickEnabled=false] - Enable flick gesture\n * @property {Number} [gestureSettingsPen.flickMinSpeed=120] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)\n * @property {Number} [gestureSettingsPen.flickMomentum=0.25] - If flickEnabled is true, the momentum factor for the flick gesture\n * @property {Boolean} [gestureSettingsPen.pinchRotate=false] - If pinchRotate is true, the user will have the ability to rotate the image using their fingers.\n *\n * @property {OpenSeadragon.GestureSettings} [gestureSettingsUnknown]\n * Settings for gestures generated by unknown pointer devices. (See {@link OpenSeadragon.GestureSettings})\n * @property {Boolean} [gestureSettingsUnknown.scrollToZoom=true] - Zoom on scroll gesture\n * @property {Boolean} [gestureSettingsUnknown.clickToZoom=false] - Zoom on click gesture\n * @property {Boolean} [gestureSettingsUnknown.dblClickToZoom=true] - Zoom on double-click gesture. Note: If set to true\n * then clickToZoom should be set to false to prevent multiple zooms.\n * @property {Boolean} [gestureSettingsUnknown.pinchToZoom=true] - Zoom on pinch gesture\n * @property {Boolean} [gestureSettingsUnknown.zoomToRefPoint=true] - If zoomToRefPoint is true, the zoom is centered at the pointer position. Otherwise,\n * the zoom is centered at the canvas center.\n * @property {Boolean} [gestureSettingsUnknown.flickEnabled=true] - Enable flick gesture\n * @property {Number} [gestureSettingsUnknown.flickMinSpeed=120] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)\n * @property {Number} [gestureSettingsUnknown.flickMomentum=0.25] - If flickEnabled is true, the momentum factor for the flick gesture\n * @property {Boolean} [gestureSettingsUnknown.pinchRotate=false] - If pinchRotate is true, the user will have the ability to rotate the image using their fingers.\n *\n * @property {Number} [zoomPerClick=2.0]\n * The \"zoom distance\" per mouse click or touch tap. Note: Setting this to 1.0 effectively disables the click-to-zoom feature (also see gestureSettings[Mouse|Touch|Pen].clickToZoom/dblClickToZoom).\n *\n * @property {Number} [zoomPerScroll=1.2]\n * The \"zoom distance\" per mouse scroll or touch pinch. Note: Setting this to 1.0 effectively disables the mouse-wheel zoom feature (also see gestureSettings[Mouse|Touch|Pen].scrollToZoom}).\n *\n * @property {Number} [zoomPerSecond=1.0]\n * The number of seconds to animate a single zoom event over.\n *\n * @property {Boolean} [showNavigator=false]\n * Set to true to make the navigator minimap appear.\n *\n * @property {String} [navigatorId=navigator-GENERATED DATE]\n * The ID of a div to hold the navigator minimap.\n * If an ID is specified, the navigatorPosition, navigatorSizeRatio, navigatorMaintainSizeRatio, navigator[Top|Left|Height|Width] and navigatorAutoFade options will be ignored.\n * If an ID is not specified, a div element will be generated and placed on top of the main image.\n *\n * @property {String} [navigatorPosition='TOP_RIGHT']\n * Valid values are 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', 'BOTTOM_RIGHT', or 'ABSOLUTE'.
\n * If 'ABSOLUTE' is specified, then navigator[Top|Left|Height|Width] determines the size and position of the navigator minimap in the viewer, and navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.
\n * For 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', and 'BOTTOM_RIGHT', the navigatorSizeRatio or navigator[Height|Width] values determine the size of the navigator minimap.\n *\n * @property {Number} [navigatorSizeRatio=0.2]\n * Ratio of navigator size to viewer size. Ignored if navigator[Height|Width] are specified.\n *\n * @property {Boolean} [navigatorMaintainSizeRatio=false]\n * If true, the navigator minimap is resized (using navigatorSizeRatio) when the viewer size changes.\n *\n * @property {Number|String} [navigatorTop=null]\n * Specifies the location of the navigator minimap (see navigatorPosition).\n *\n * @property {Number|String} [navigatorLeft=null]\n * Specifies the location of the navigator minimap (see navigatorPosition).\n *\n * @property {Number|String} [navigatorHeight=null]\n * Specifies the size of the navigator minimap (see navigatorPosition).\n * If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.\n *\n * @property {Number|String} [navigatorWidth=null]\n * Specifies the size of the navigator minimap (see navigatorPosition).\n * If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.\n *\n * @property {Boolean} [navigatorAutoResize=true]\n * Set to false to prevent polling for navigator size changes. Useful for providing custom resize behavior.\n * Setting to false can also improve performance when the navigator is configured to a fixed size.\n *\n * @property {Boolean} [navigatorAutoFade=true]\n * If the user stops interacting with the viewport, fade the navigator minimap.\n * Setting to false will make the navigator minimap always visible.\n *\n * @property {Boolean} [navigatorRotate=true]\n * If true, the navigator will be rotated together with the viewer.\n *\n * @property {Number} [controlsFadeDelay=2000]\n * The number of milliseconds to wait once the user has stopped interacting\n * with the interface before begining to fade the controls. Assumes\n * showNavigationControl and autoHideControls are both true.\n *\n * @property {Number} [controlsFadeLength=1500]\n * The number of milliseconds to animate the controls fading out.\n *\n * @property {Number} [maxImageCacheCount=200]\n * The max number of images we should keep in memory (per drawer).\n *\n * @property {Number} [timeout=30000]\n * The max number of milliseconds that an image job may take to complete.\n *\n * @property {Boolean} [useCanvas=true]\n * Set to false to not use an HTML canvas element for image rendering even if canvas is supported.\n *\n * @property {Number} [minPixelRatio=0.5]\n * The higher the minPixelRatio, the lower the quality of the image that\n * is considered sufficient to stop rendering a given zoom level. For\n * example, if you are targeting mobile devices with less bandwith you may\n * try setting this to 1.5 or higher.\n *\n * @property {Boolean} [mouseNavEnabled=true]\n * Is the user able to interact with the image via mouse or touch. Default\n * interactions include draging the image in a plane, and zooming in toward\n * and away from the image.\n *\n * @property {Boolean} [showNavigationControl=true]\n * Set to false to prevent the appearance of the default navigation controls.
\n * Note that if set to false, the customs buttons set by the options\n * zoomInButton, zoomOutButton etc, are rendered inactive.\n *\n * @property {OpenSeadragon.ControlAnchor} [navigationControlAnchor=TOP_LEFT]\n * Placement of the default navigation controls.\n * To set the placement of the sequence controls, see the\n * sequenceControlAnchor option.\n *\n * @property {Boolean} [showZoomControl=true]\n * If true then + and - buttons to zoom in and out are displayed.
\n * Note: {@link OpenSeadragon.Options.showNavigationControl} is overriding\n * this setting when set to false.\n *\n * @property {Boolean} [showHomeControl=true]\n * If true then the 'Go home' button is displayed to go back to the original\n * zoom and pan.
\n * Note: {@link OpenSeadragon.Options.showNavigationControl} is overriding\n * this setting when set to false.\n *\n * @property {Boolean} [showFullPageControl=true]\n * If true then the 'Toggle full page' button is displayed to switch\n * between full page and normal mode.
\n * Note: {@link OpenSeadragon.Options.showNavigationControl} is overriding\n * this setting when set to false.\n *\n * @property {Boolean} [showRotationControl=false]\n * If true then the rotate left/right controls will be displayed as part of the\n * standard controls. This is also subject to the browser support for rotate\n * (e.g. viewer.drawer.canRotate()).
\n * Note: {@link OpenSeadragon.Options.showNavigationControl} is overriding\n * this setting when set to false.\n *\n * @property {Boolean} [showFlipControl=false]\n * If true then the flip controls will be displayed as part of the\n * standard controls.\n *\n * @property {Boolean} [showSequenceControl=true]\n * If sequenceMode is true, then provide buttons for navigating forward and\n * backward through the images.\n *\n * @property {OpenSeadragon.ControlAnchor} [sequenceControlAnchor=TOP_LEFT]\n * Placement of the default sequence controls.\n *\n * @property {Boolean} [navPrevNextWrap=false]\n * If true then the 'previous' button will wrap to the last image when\n * viewing the first image and the 'next' button will wrap to the first\n * image when viewing the last image.\n *\n * @property {String} zoomInButton\n * Set the id of the custom 'Zoom in' button to use.\n * This is useful to have a custom button anywhere in the web page.
\n * To only change the button images, consider using\n * {@link OpenSeadragon.Options.navImages}\n *\n * @property {String} zoomOutButton\n * Set the id of the custom 'Zoom out' button to use.\n * This is useful to have a custom button anywhere in the web page.
\n * To only change the button images, consider using\n * {@link OpenSeadragon.Options.navImages}\n *\n * @property {String} homeButton\n * Set the id of the custom 'Go home' button to use.\n * This is useful to have a custom button anywhere in the web page.
\n * To only change the button images, consider using\n * {@link OpenSeadragon.Options.navImages}\n *\n * @property {String} fullPageButton\n * Set the id of the custom 'Toggle full page' button to use.\n * This is useful to have a custom button anywhere in the web page.
\n * To only change the button images, consider using\n * {@link OpenSeadragon.Options.navImages}\n *\n * @property {String} rotateLeftButton\n * Set the id of the custom 'Rotate left' button to use.\n * This is useful to have a custom button anywhere in the web page.
\n * To only change the button images, consider using\n * {@link OpenSeadragon.Options.navImages}\n *\n * @property {String} rotateRightButton\n * Set the id of the custom 'Rotate right' button to use.\n * This is useful to have a custom button anywhere in the web page.
\n * To only change the button images, consider using\n * {@link OpenSeadragon.Options.navImages}\n *\n * @property {String} previousButton\n * Set the id of the custom 'Previous page' button to use.\n * This is useful to have a custom button anywhere in the web page.
\n * To only change the button images, consider using\n * {@link OpenSeadragon.Options.navImages}\n *\n * @property {String} nextButton\n * Set the id of the custom 'Next page' button to use.\n * This is useful to have a custom button anywhere in the web page.
\n * To only change the button images, consider using\n * {@link OpenSeadragon.Options.navImages}\n *\n * @property {Boolean} [sequenceMode=false]\n * Set to true to have the viewer treat your tilesources as a sequence of images to\n * be opened one at a time rather than all at once.\n *\n * @property {Number} [initialPage=0]\n * If sequenceMode is true, display this page initially.\n *\n * @property {Boolean} [preserveViewport=false]\n * If sequenceMode is true, then normally navigating through each image resets the\n * viewport to 'home' position. If preserveViewport is set to true, then the viewport\n * position is preserved when navigating between images in the sequence.\n *\n * @property {Boolean} [preserveOverlays=false]\n * If sequenceMode is true, then normally navigating through each image\n * resets the overlays.\n * If preserveOverlays is set to true, then the overlays added with {@link OpenSeadragon.Viewer#addOverlay}\n * are preserved when navigating between images in the sequence.\n * Note: setting preserveOverlays overrides any overlays specified in the global\n * \"overlays\" option for the Viewer. It's also not compatible with specifying\n * per-tileSource overlays via the options, as those overlays will persist\n * even after the tileSource is closed.\n *\n * @property {Boolean} [showReferenceStrip=false]\n * If sequenceMode is true, then display a scrolling strip of image thumbnails for\n * navigating through the images.\n *\n * @property {String} [referenceStripScroll='horizontal']\n *\n * @property {Element} [referenceStripElement=null]\n *\n * @property {Number} [referenceStripHeight=null]\n *\n * @property {Number} [referenceStripWidth=null]\n *\n * @property {String} [referenceStripPosition='BOTTOM_LEFT']\n *\n * @property {Number} [referenceStripSizeRatio=0.2]\n *\n * @property {Boolean} [collectionMode=false]\n * Set to true to have the viewer arrange your TiledImages in a grid or line.\n *\n * @property {Number} [collectionRows=3]\n * If collectionMode is true, specifies how many rows the grid should have. Use 1 to make a line.\n * If collectionLayout is 'vertical', specifies how many columns instead.\n *\n * @property {Number} [collectionColumns=0]\n * If collectionMode is true, specifies how many columns the grid should have. Use 1 to make a line.\n * If collectionLayout is 'vertical', specifies how many rows instead. Ignored if collectionRows is not set to a falsy value.\n *\n * @property {String} [collectionLayout='horizontal']\n * If collectionMode is true, specifies whether to arrange vertically or horizontally.\n *\n * @property {Number} [collectionTileSize=800]\n * If collectionMode is true, specifies the size, in viewport coordinates, for each TiledImage to fit into.\n * The TiledImage will be centered within a square of the specified size.\n *\n * @property {Number} [collectionTileMargin=80]\n * If collectionMode is true, specifies the margin, in viewport coordinates, between each TiledImage.\n *\n * @property {String|Boolean} [crossOriginPolicy=false]\n * Valid values are 'Anonymous', 'use-credentials', and false. If false, canvas requests will\n * not use CORS, and the canvas will be tainted.\n *\n * @property {Boolean} [ajaxWithCredentials=false]\n * Whether to set the withCredentials XHR flag for AJAX requests.\n * Note that this can be overridden at the {@link OpenSeadragon.TileSource} level.\n *\n * @property {Boolean} [loadTilesWithAjax=false]\n * Whether to load tile data using AJAX requests.\n * Note that this can be overridden at the {@link OpenSeadragon.TileSource} level.\n *\n * @property {Object} [ajaxHeaders={}]\n * A set of headers to include when making AJAX requests for tile sources or tiles.\n *\n */ /**\n * Settings for gestures generated by a pointer device.\n *\n * @typedef {Object} GestureSettings\n * @memberof OpenSeadragon\n *\n * @property {Boolean} scrollToZoom\n * Set to false to disable zooming on scroll gestures.\n *\n * @property {Boolean} clickToZoom\n * Set to false to disable zooming on click gestures.\n *\n * @property {Boolean} dblClickToZoom\n * Set to false to disable zooming on double-click gestures. Note: If set to true\n * then clickToZoom should be set to false to prevent multiple zooms.\n *\n * @property {Boolean} pinchToZoom\n * Set to false to disable zooming on pinch gestures.\n *\n * @property {Boolean} flickEnabled\n * Set to false to disable the kinetic panning effect (flick) at the end of a drag gesture.\n *\n * @property {Number} flickMinSpeed\n * 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.\n *\n * @property {Number} flickMomentum\n * 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.\n * A larger value will make the flick feel \"lighter\", while a smaller value will make the flick feel \"heavier\".\n * Note: springStiffness and animationTime also affect the \"spring\" used to stop the flick animation.\n *\n */ /**\n * The names for the image resources used for the image navigation buttons.\n *\n * @typedef {Object} NavImages\n * @memberof OpenSeadragon\n *\n * @property {Object} zoomIn - Images for the zoom-in button.\n * @property {String} zoomIn.REST\n * @property {String} zoomIn.GROUP\n * @property {String} zoomIn.HOVER\n * @property {String} zoomIn.DOWN\n *\n * @property {Object} zoomOut - Images for the zoom-out button.\n * @property {String} zoomOut.REST\n * @property {String} zoomOut.GROUP\n * @property {String} zoomOut.HOVER\n * @property {String} zoomOut.DOWN\n *\n * @property {Object} home - Images for the home button.\n * @property {String} home.REST\n * @property {String} home.GROUP\n * @property {String} home.HOVER\n * @property {String} home.DOWN\n *\n * @property {Object} fullpage - Images for the full-page button.\n * @property {String} fullpage.REST\n * @property {String} fullpage.GROUP\n * @property {String} fullpage.HOVER\n * @property {String} fullpage.DOWN\n *\n * @property {Object} rotateleft - Images for the rotate left button.\n * @property {String} rotateleft.REST\n * @property {String} rotateleft.GROUP\n * @property {String} rotateleft.HOVER\n * @property {String} rotateleft.DOWN\n *\n * @property {Object} rotateright - Images for the rotate right button.\n * @property {String} rotateright.REST\n * @property {String} rotateright.GROUP\n * @property {String} rotateright.HOVER\n * @property {String} rotateright.DOWN\n *\n * @property {Object} flip - Images for the flip button.\n * @property {String} flip.REST\n * @property {String} flip.GROUP\n * @property {String} flip.HOVER\n * @property {String} flip.DOWN\n *\n * @property {Object} previous - Images for the previous button.\n * @property {String} previous.REST\n * @property {String} previous.GROUP\n * @property {String} previous.HOVER\n * @property {String} previous.DOWN\n *\n * @property {Object} next - Images for the next button.\n * @property {String} next.REST\n * @property {String} next.GROUP\n * @property {String} next.HOVER\n * @property {String} next.DOWN\n *\n */function OpenSeadragon(options){return new OpenSeadragon.Viewer(options);}(function($){/**\n * The OpenSeadragon version.\n *\n * @member {Object} OpenSeadragon.version\n * @property {String} versionStr - The version number as a string ('major.minor.revision').\n * @property {Number} major - The major version number.\n * @property {Number} minor - The minor version number.\n * @property {Number} revision - The revision number.\n * @since 1.0.0\n */$.version={versionStr:'2.3.1',major:parseInt('2',10),minor:parseInt('3',10),revision:parseInt('1',10)};/**\n * Taken from jquery 1.6.1\n * [[Class]] -> type pairs\n * @private\n */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\ntoString=Object.prototype.toString,hasOwn=Object.prototype.hasOwnProperty;/**\n * Taken from jQuery 1.6.1\n * @function isFunction\n * @memberof OpenSeadragon\n * @see {@link http://www.jquery.com/ jQuery}\n */$.isFunction=function(obj){return $.type(obj)===\"function\";};/**\n * Taken from jQuery 1.6.1\n * @function isArray\n * @memberof OpenSeadragon\n * @see {@link http://www.jquery.com/ jQuery}\n */$.isArray=Array.isArray||function(obj){return $.type(obj)===\"array\";};/**\n * A crude way of determining if an object is a window.\n * Taken from jQuery 1.6.1\n * @function isWindow\n * @memberof OpenSeadragon\n * @see {@link http://www.jquery.com/ jQuery}\n */$.isWindow=function(obj){return obj&&_typeof(obj)===\"object\"&&\"setInterval\"in obj;};/**\n * Taken from jQuery 1.6.1\n * @function type\n * @memberof OpenSeadragon\n * @see {@link http://www.jquery.com/ jQuery}\n */$.type=function(obj){return obj===null||obj===undefined?String(obj):class2type[toString.call(obj)]||\"object\";};/**\n * Taken from jQuery 1.6.1\n * @function isPlainObject\n * @memberof OpenSeadragon\n * @see {@link http://www.jquery.com/ jQuery}\n */$.isPlainObject=function(obj){// Must be an Object.\n// Because of IE, we also have to check the presence of the constructor property.\n// Make sure that DOM nodes and window objects don't pass through, as well\nif(!obj||OpenSeadragon.type(obj)!==\"object\"||obj.nodeType||$.isWindow(obj)){return false;}// Not own constructor property must be Object\nif(obj.constructor&&!hasOwn.call(obj,\"constructor\")&&!hasOwn.call(obj.constructor.prototype,\"isPrototypeOf\")){return false;}// Own properties are enumerated firstly, so to speed up,\n// if last one is own, then all properties are own.\nvar lastKey;for(var key in obj){lastKey=key;}return lastKey===undefined||hasOwn.call(obj,lastKey);};/**\n * Taken from jQuery 1.6.1\n * @function isEmptyObject\n * @memberof OpenSeadragon\n * @see {@link http://www.jquery.com/ jQuery}\n */$.isEmptyObject=function(obj){for(var name in obj){return false;}return true;};/**\n * Shim around Object.freeze. Does nothing if Object.freeze is not supported.\n * @param {Object} obj The object to freeze.\n * @return {Object} obj The frozen object.\n */$.freezeObject=function(obj){if(Object.freeze){$.freezeObject=Object.freeze;}else{$.freezeObject=function(obj){return obj;};}return $.freezeObject(obj);};/**\n * True if the browser supports the HTML5 canvas element\n * @member {Boolean} supportsCanvas\n * @memberof OpenSeadragon\n */$.supportsCanvas=function(){var canvasElement=document.createElement('canvas');return!!($.isFunction(canvasElement.getContext)&&canvasElement.getContext('2d'));}();/**\n * Test whether the submitted canvas is tainted or not.\n * @argument {Canvas} canvas The canvas to test.\n * @returns {Boolean} True if the canvas is tainted.\n */$.isCanvasTainted=function(canvas){var isTainted=false;try{// We test if the canvas is tainted by retrieving data from it.\n// An exception will be raised if the canvas is tainted.\ncanvas.getContext('2d').getImageData(0,0,1,1);}catch(e){isTainted=true;}return isTainted;};/**\n * A ratio comparing the device screen's pixel density to the canvas's backing store pixel density,\n * clamped to a minimum of 1. Defaults to 1 if canvas isn't supported by the browser.\n * @member {Number} pixelDensityRatio\n * @memberof OpenSeadragon\n */$.pixelDensityRatio=function(){if($.supportsCanvas){var context=document.createElement('canvas').getContext('2d');var devicePixelRatio=window.devicePixelRatio||1;var backingStoreRatio=context.webkitBackingStorePixelRatio||context.mozBackingStorePixelRatio||context.msBackingStorePixelRatio||context.oBackingStorePixelRatio||context.backingStorePixelRatio||1;return Math.max(devicePixelRatio,1)/backingStoreRatio;}else{return 1;}}();})(OpenSeadragon);/**\n * This closure defines all static methods available to the OpenSeadragon\n * namespace. Many, if not most, are taked directly from jQuery for use\n * to simplify and reduce common programming patterns. More static methods\n * from jQuery may eventually make their way into this though we are\n * attempting to avoid an explicit dependency on jQuery only because\n * OpenSeadragon is a broadly useful code base and would be made less broad\n * by requiring jQuery fully.\n *\n * Some static methods have also been refactored from the original OpenSeadragon\n * project.\n */(function($){/**\n * Taken from jQuery 1.6.1\n * @function extend\n * @memberof OpenSeadragon\n * @see {@link http://www.jquery.com/ jQuery}\n */$.extend=function(){var options,name,src,copy,copyIsArray,clone,target=arguments[0]||{},length=arguments.length,deep=false,i=1;// Handle a deep copy situation\nif(typeof target===\"boolean\"){deep=target;target=arguments[1]||{};// skip the boolean and the target\ni=2;}// Handle case when target is a string or something (possible in deep copy)\nif(_typeof(target)!==\"object\"&&!OpenSeadragon.isFunction(target)){target={};}// extend jQuery itself if only one argument is passed\nif(length===i){target=this;--i;}for(;i how much of the viewer can be negative space\nminPixelRatio:0.5,//->closer to 0 draws tiles meant for a higher zoom at this zoom\ndefaultZoomLevel:0,minZoomLevel:null,maxZoomLevel:null,homeFillsViewer:false,//UI RESPONSIVENESS AND FEEL\nclickTimeThreshold:300,clickDistThreshold:5,dblClickTimeThreshold:300,dblClickDistThreshold:20,springStiffness:6.5,animationTime:1.2,gestureSettingsMouse:{scrollToZoom:true,clickToZoom:true,dblClickToZoom:false,pinchToZoom:false,zoomToRefPoint:true,flickEnabled:false,flickMinSpeed:120,flickMomentum:0.25,pinchRotate:false},gestureSettingsTouch:{scrollToZoom:false,clickToZoom:false,dblClickToZoom:true,pinchToZoom:true,zoomToRefPoint:true,flickEnabled:true,flickMinSpeed:120,flickMomentum:0.25,pinchRotate:false},gestureSettingsPen:{scrollToZoom:false,clickToZoom:true,dblClickToZoom:false,pinchToZoom:false,zoomToRefPoint:true,flickEnabled:false,flickMinSpeed:120,flickMomentum:0.25,pinchRotate:false},gestureSettingsUnknown:{scrollToZoom:false,clickToZoom:false,dblClickToZoom:true,pinchToZoom:true,zoomToRefPoint:true,flickEnabled:true,flickMinSpeed:120,flickMomentum:0.25,pinchRotate:false},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\nmaxZoomPixelRatio:1.1,//-> higher allows 'over zoom' into pixels\nsmoothTileEdgesMinZoom:1.1,//-> higher than maxZoomPixelRatio disables it\niOSDevice:isIOSDevice(),pixelsPerWheelLine:40,pixelsPerArrowPress:40,autoResize:true,preserveImageSizeOnResize:false,// requires autoResize=true\nminScrollDeltaTime:50,//DEFAULT CONTROL SETTINGS\nshowSequenceControl:true,//SEQUENCE\nsequenceControlAnchor:null,//SEQUENCE\npreserveViewport:false,//SEQUENCE\npreserveOverlays:false,//SEQUENCE\nnavPrevNextWrap:false,//SEQUENCE\nshowNavigationControl:true,//ZOOM/HOME/FULL/ROTATION\nnavigationControlAnchor:null,//ZOOM/HOME/FULL/ROTATION\nshowZoomControl:true,//ZOOM\nshowHomeControl:true,//HOME\nshowFullPageControl:true,//FULL\nshowRotationControl:false,//ROTATION\nshowFlipControl:false,//FLIP\ncontrolsFadeDelay:2000,//ZOOM/HOME/FULL/SEQUENCE\ncontrolsFadeLength:1500,//ZOOM/HOME/FULL/SEQUENCE\nmouseNavEnabled:true,//GENERAL MOUSE INTERACTIVITY\n//VIEWPORT NAVIGATOR SETTINGS\nshowNavigator:false,navigatorId:null,navigatorPosition:null,navigatorSizeRatio:0.2,navigatorMaintainSizeRatio:false,navigatorTop:null,navigatorLeft:null,navigatorHeight:null,navigatorWidth:null,navigatorAutoResize:true,navigatorAutoFade:true,navigatorRotate:true,// INITIAL ROTATION\ndegrees:0,// INITIAL FLIP STATE\nflipped:false,// APPEARANCE\nopacity:1,preload:false,compositeOperation:null,placeholderFillStyle:null,//REFERENCE STRIP SETTINGS\nshowReferenceStrip:false,referenceStripScroll:'horizontal',referenceStripElement:null,referenceStripHeight:null,referenceStripWidth:null,referenceStripPosition:'BOTTOM_LEFT',referenceStripSizeRatio:0.2,//COLLECTION VISUALIZATION SETTINGS\ncollectionRows:3,//or columns depending on layout\ncollectionColumns:0,//columns in horizontal layout, rows in vertical layout\ncollectionLayout:'horizontal',//vertical\ncollectionMode:false,collectionTileSize:800,collectionTileMargin:80,//PERFORMANCE SETTINGS\nimageLoaderLimit:0,maxImageCacheCount:200,timeout:30000,useCanvas:true,// Use canvas element for drawing if available\n//INTERFACE RESOURCE SETTINGS\nprefixUrl:\"/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'},flip:{// Flip icon designed by Yaroslav Samoylov from the Noun Project and modified by Nelson Campos ncampos@criteriamarathon.com, https://thenounproject.com/term/flip/136289/\nREST:'flip_rest.png',GROUP:'flip_grouphover.png',HOVER:'flip_hover.png',DOWN:'flip_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\ndebugMode:false,debugGridColor:['#437AB2','#1B9E77','#D95F02','#7570B3','#E7298A','#66A61E','#E6AB02','#A6761D','#666666']},/**\n * TODO: get rid of this. I can't see how it's required at all. Looks\n * like an early legacy code artifact.\n * @static\n * @ignore\n */SIGNAL:\"----seadragon----\",/**\n * Returns a function which invokes the method as if it were a method belonging to the object.\n * @function\n * @param {Object} object\n * @param {Function} method\n * @returns {Function}\n */delegate:function delegate(object,method){return function(){var args=arguments;if(args===undefined){args=[];}return method.apply(object,args);};},/**\n * An enumeration of Browser vendors.\n * @static\n * @type {Object}\n * @property {Number} UNKNOWN\n * @property {Number} IE\n * @property {Number} FIREFOX\n * @property {Number} SAFARI\n * @property {Number} CHROME\n * @property {Number} OPERA\n */BROWSERS:{UNKNOWN:0,IE:1,FIREFOX:2,SAFARI:3,CHROME:4,OPERA:5},/**\n * Returns a DOM Element for the given id or element.\n * @function\n * @param {String|Element} element Accepts an id or element.\n * @returns {Element} The element with the given id, null, or the element itself.\n */getElement:function getElement(element){if(typeof element==\"string\"){element=document.getElementById(element);}return element;},/**\n * Determines the position of the upper-left corner of the element.\n * @function\n * @param {Element|String} element - the elemenet we want the position for.\n * @returns {OpenSeadragon.Point} - the position of the upper left corner of the element.\n */getElementPosition:function getElementPosition(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;},/**\n * Determines the position of the upper-left corner of the element adjusted for current page and/or element scroll.\n * @function\n * @param {Element|String} element - the element we want the position for.\n * @returns {OpenSeadragon.Point} - the position of the upper left corner of the element adjusted for current page and/or element scroll.\n */getElementOffset:function getElementOffset(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)!==( true?\"undefined\":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));},/**\n * Determines the height and width of the given element.\n * @function\n * @param {Element|String} element\n * @returns {OpenSeadragon.Point}\n */getElementSize:function getElementSize(element){element=$.getElement(element);return new $.Point(element.clientWidth,element.clientHeight);},/**\n * Returns the CSSStyle object for the given element.\n * @function\n * @param {Element|String} element\n * @returns {CSSStyle}\n */getElementStyle:document.documentElement.currentStyle?function(element){element=$.getElement(element);return element.currentStyle;}:function(element){element=$.getElement(element);return window.getComputedStyle(element,\"\");},/**\n * Returns the property with the correct vendor prefix appended.\n * @param {String} property the property name\n * @returns {String} the property with the correct prefix or null if not\n * supported.\n */getCssPropertyWithVendorPrefix:function getCssPropertyWithVendorPrefix(property){var memo={};$.getCssPropertyWithVendorPrefix=function(property){if(memo[property]!==undefined){return memo[property];}var style=document.createElement('div').style;var result=null;if(style[property]!==undefined){result=property;}else{var prefixes=['Webkit','Moz','MS','O','webkit','moz','ms','o'];var suffix=$.capitalizeFirstLetter(property);for(var i=0;i=offset.x&&point.x=offset.y;},/**\n * Gets the latest event, really only useful internally since its\n * specific to IE behavior.\n * @function\n * @param {Event} [event]\n * @returns {Event}\n * @deprecated For internal use only\n * @private\n */getEvent:function getEvent(event){if(event){$.getEvent=function(event){return event;};}else{$.getEvent=function(){return window.event;};}return $.getEvent(event);},/**\n * Gets the position of the mouse on the screen for a given event.\n * @function\n * @param {Event} [event]\n * @returns {OpenSeadragon.Point}\n */getMousePosition:function getMousePosition(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);},/**\n * Determines the page's current scroll position.\n * @function\n * @returns {OpenSeadragon.Point}\n */getPageScroll:function getPageScroll(){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.\nreturn new $.Point(0,0);}return $.getPageScroll();},/**\n * Set the page scroll position.\n * @function\n * @returns {OpenSeadragon.Point}\n */setPageScroll:function setPageScroll(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\n// is no way to detect the correct method.\nreturn;}document.body.scrollLeft=scroll.x;document.body.scrollTop=scroll.y;var currentScroll=$.getPageScroll();if(currentScroll.x!==originalScroll.x&¤tScroll.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&¤tScroll.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.\n$.setPageScroll=function(scroll){};}return $.setPageScroll(scroll);},/**\n * Determines the size of the browsers window.\n * @function\n * @returns {OpenSeadragon.Point}\n */getWindowSize:function getWindowSize(){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();},/**\n * Wraps the given element in a nest of divs so that the element can\n * be easily centered using CSS tables\n * @function\n * @param {Element|String} element\n * @returns {Element} outermost wrapper element\n */makeCenteredNode:function makeCenteredNode(element){// Convert a possible ID to an actual HTMLElement\nelement=$.getElement(element);/*\n CSS tables require you to have a display:table/row/cell hierarchy so we need to create\n three nested wrapper divs:\n */var wrappers=[$.makeNeutralElement('div'),$.makeNeutralElement('div'),$.makeNeutralElement('div')];// It feels like we should be able to pass style dicts to makeNeutralElement:\n$.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];},/**\n * Creates an easily positionable element of the given type that therefor\n * serves as an excellent container element.\n * @function\n * @param {String} tagName\n * @returns {Element}\n */makeNeutralElement:function makeNeutralElement(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;},/**\n * Returns the current milliseconds, using Date.now() if available\n * @function\n */now:function now(){if(Date.now){$.now=Date.now;}else{$.now=function(){return new Date().getTime();};}return $.now();},/**\n * Ensures an image is loaded correctly to support alpha transparency.\n * Generally only IE has issues doing this correctly for formats like\n * png.\n * @function\n * @param {String} src\n * @returns {Element}\n */makeTransparentImage:function makeTransparentImage(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\n};img.src=src;element.style.filter=\"progid:DXImageTransform.Microsoft.AlphaImageLoader(src='\"+src+\"', sizingMethod='scale')\";return element;};}return $.makeTransparentImage(src);},/**\n * Sets the opacity of the specified element.\n * @function\n * @param {Element|String} element\n * @param {Number} opacity\n * @param {Boolean} [usesAlpha]\n */setElementOpacity:function setElementOpacity(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=\"\";}}},/**\n * Sets the specified element's touch-action style attribute to 'none'.\n * @function\n * @param {Element|String} element\n */setElementTouchActionNone:function setElementTouchActionNone(element){element=$.getElement(element);if(typeof element.style.touchAction!=='undefined'){element.style.touchAction='none';}else if(typeof element.style.msTouchAction!=='undefined'){element.style.msTouchAction='none';}},/**\n * Add the specified CSS class to the element if not present.\n * @function\n * @param {Element|String} element\n * @param {String} className\n */addClass:function addClass(element,className){element=$.getElement(element);if(!element.className){element.className=className;}else if((' '+element.className+' ').indexOf(' '+className+' ')===-1){element.className+=' '+className;}},/**\n * Find the first index at which an element is found in an array or -1\n * if not present.\n *\n * Code taken and adapted from\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Compatibility\n *\n * @function\n * @param {Array} array The array from which to find the element\n * @param {Object} searchElement The element to find\n * @param {Number} [fromIndex=0] Index to start research.\n * @returns {Number} The index of the element in the array.\n */indexOf:function indexOf(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=200&&request.status<300||request.status===0&&protocol!==\"http:\"&&protocol!==\"https:\"){onSuccess(request);}else{$.console.log(\"AJAX request returned %d: %s\",request.status,url);if($.isFunction(onError)){onError(request);}}}};try{request.open(\"GET\",url,true);if(responseType){request.responseType=responseType;}if(headers){for(var headerName in headers){if(headers.hasOwnProperty(headerName)&&headers[headerName]){request.setRequestHeader(headerName,headers[headerName]);}}}if(withCredentials){request.withCredentials=true;}request.send(null);}catch(e){var msg=e.message;/*\n IE < 10 does not support CORS and an XHR request to a different origin will fail as soon\n as send() is called. This is particularly easy to miss during development and appear in\n production if you use a CDN or domain sharding and the security policy is likely to break\n exception handlers since any attempt to access a property of the request object will\n raise an access denied TypeError inside the catch block.\n\n To be friendlier, we'll check for this specific error and add a documentation pointer\n to point developers in the right direction. We test the exception number because IE's\n error messages are localized.\n */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(window.XDomainRequest){// IE9 or IE8 might as well try to use XDomainRequest\nvar xdr=new XDomainRequest();if(xdr){xdr.onload=function(e){if($.isFunction(onSuccess)){onSuccess({// Faking an xhr object\nresponseText:xdr.responseText,status:200,// XDomainRequest doesn't support status codes, so we just fake one! :/\nstatusText:'OK'});}};xdr.onerror=function(e){if($.isFunction(onError)){onError({// Faking an xhr object\nresponseText:xdr.responseText,status:444,// 444 No Response\nstatusText:'An error happened. Due to an XDomainRequest deficiency we can not extract any information about this error. Upgrade your browser.'});}};try{xdr.open('GET',url);xdr.send();}catch(e2){if($.isFunction(onError)){onError(request,e);}}}}else{if($.isFunction(onError)){onError(request,e);}}}return request;},/**\n * Taken from jQuery 1.6.1\n * @function\n * @param {Object} options\n * @param {String} options.url\n * @param {Function} options.callback\n * @param {String} [options.param='callback'] The name of the url parameter\n * to request the jsonp provider with.\n * @param {String} [options.callbackName=] The name of the callback to\n * request the jsonp provider with.\n */jsonp:function jsonp(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\nurl+=(/\\?/.test(url)?\"&\":\"?\")+callbackParam+\"=\"+jsonpCallback;// Install callback\nwindow[jsonpCallback]=function(response){if(!previous){try{delete window[jsonpCallback];}catch(e){//swallow\n}}else{window[jsonpCallback]=previous;}if(callback&&$.isFunction(callback)){callback(response);}};script=document.createElement(\"script\");//TODO: having an issue with async info requests\nif(undefined!==options.async||false!==options.async){script.async=\"async\";}if(options.scriptCharset){script.charset=options.scriptCharset;}script.src=url;// Attach handlers for all browsers\nscript.onload=script.onreadystatechange=function(_,isAbort){if(isAbort||!script.readyState||/loaded|complete/.test(script.readyState)){// Handle memory leak in IE\nscript.onload=script.onreadystatechange=null;// Remove the script\nif(head&&script.parentNode){head.removeChild(script);}// Dereference the script\nscript=undefined;}};// Use insertBefore instead of appendChild to circumvent an IE6 bug.\n// This arises when a base node is used (#2709 and #4378).\nhead.insertBefore(script,head.firstChild);},/**\n * Fully deprecated. Will throw an error.\n * @function\n * @deprecated use {@link OpenSeadragon.Viewer#open}\n */createFromDZI:function createFromDZI(){throw\"OpenSeadragon.createFromDZI is deprecated, use Viewer.open.\";},/**\n * Parses an XML string into a DOM Document.\n * @function\n * @param {String} string\n * @returns {Document}\n */parseXml:function parseXml(string){if(window.DOMParser){$.parseXml=function(string){var xmlDoc=null,parser;parser=new DOMParser();xmlDoc=parser.parseFromString(string,\"text/xml\");return xmlDoc;};}else if(window.ActiveXObject){$.parseXml=function(string){var xmlDoc=null;xmlDoc=new ActiveXObject(\"Microsoft.XMLDOM\");xmlDoc.async=false;xmlDoc.loadXML(string);return xmlDoc;};}else{throw new Error(\"Browser doesn't support XML DOM.\");}return $.parseXml(string);},/**\n * Parses a JSON string into a Javascript object.\n * @function\n * @param {String} string\n * @returns {Object}\n */parseJSON:function parseJSON(string){if(window.JSON&&window.JSON.parse){$.parseJSON=window.JSON.parse;}else{// Should only be used by IE8 in non standards mode\n$.parseJSON=function(string){/*jshint evil:true*/ //eslint-disable-next-line no-eval\nreturn eval('('+string+')');};}return $.parseJSON(string);},/**\n * Reports whether the image format is supported for tiling in this\n * version.\n * @function\n * @param {String} [extension]\n * @returns {Boolean}\n */imageFormatSupported:function imageFormatSupported(extension){extension=extension?extension:\"\";// eslint-disable-next-line no-use-before-define\nreturn!!FILEFORMATS[extension.toLowerCase()];}});/**\n * The current browser vendor, version, and related information regarding detected features.\n * @member {Object} Browser\n * @memberof OpenSeadragon\n * @static\n * @type {Object}\n * @property {OpenSeadragon.BROWSERS} vendor - One of the {@link OpenSeadragon.BROWSERS} enumeration values.\n * @property {Number} version\n * @property {Boolean} alpha - Does the browser support image alpha transparency.\n */$.Browser={vendor:$.BROWSERS.UNKNOWN,version:0,alpha:true};var 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,\n//version and supporting feature sets.\nvar ver=navigator.appVersion,ua=navigator.userAgent,regex;//console.error( 'appName: ' + navigator.appName );\n//console.error( 'appVersion: ' + navigator.appVersion );\n//console.error( 'userAgent: ' + navigator.userAgent );\nswitch(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\")));}else{regex=new RegExp(\"Trident/.*rv:([0-9]{1,}[.0-9]{0,})\");if(regex.exec(ua)!==null){$.Browser.vendor=$.BROWSERS.IE;$.Browser.version=parseFloat(RegExp.$1);}}}break;case\"Opera\":$.Browser.vendor=$.BROWSERS.OPERA;$.Browser.version=parseFloat(ver);break;}// ignore '?' portion of query string\nvar query=window.location.search.substring(1),parts=query.split('&'),part,sep,i;for(i=0;i0){URLPARAMS[part.substring(0,sep)]=decodeURIComponent(part.substring(sep+1));}}//determine if this browser supports image alpha transparency\n$.Browser.alpha=!($.Browser.vendor==$.BROWSERS.IE&&$.Browser.version<9||$.Browser.vendor==$.BROWSERS.CHROME&&$.Browser.version<2);//determine if this browser supports element.style.opacity\n$.Browser.opacity=!($.Browser.vendor==$.BROWSERS.IE&&$.Browser.version<9);})();//TODO: $.console is often used inside a try/catch block which generally\n// prevents allowings errors to occur with detection until a debugger\n// is attached. Although I've been guilty of the same anti-pattern\n// I eventually was convinced that errors should naturally propogate in\n// all but the most special cases.\n/**\n * A convenient alias for console when available, and a simple null\n * function when console is unavailable.\n * @static\n * @private\n */var nullfunction=function nullfunction(msg){//document.location.hash = msg;\n};$.console=window.console||{log:nullfunction,debug:nullfunction,info:nullfunction,warn:nullfunction,error:nullfunction,assert:nullfunction};// Adding support for HTML5's requestAnimationFrame as suggested by acdha.\n// Implementation taken from matt synder's post here:\n// http://mattsnider.com/cross-browser-and-legacy-supported-requestframeanimation/\n(function(w){// most browsers have an implementation\nvar requestAnimationFrame=w.requestAnimationFrame||w.mozRequestAnimationFrame||w.webkitRequestAnimationFrame||w.msRequestAnimationFrame;var cancelAnimationFrame=w.cancelAnimationFrame||w.mozCancelAnimationFrame||w.webkitCancelAnimationFrame||w.msCancelAnimationFrame;// polyfill, when necessary\nif(requestAnimationFrame&&cancelAnimationFrame){// We can't assign these window methods directly to $ because they\n// expect their \"this\" to be \"window\", so we call them in wrappers.\n$.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\n$.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\n// requests, but none that get added during the\n// processing.\n// Swap the arrays so we don't have to create a new\n// array every frame.\nvar temp=processing;processing=aAnimQueue;aAnimQueue=temp;while(processing.length){processing.shift()[1](time);}}else{// don't continue the interval, if unnecessary\nclearInterval(iIntervalId);iIntervalId=undefined;}},1000/50);// estimating support for 50 frames per second\n}return iRequestId;};// create a mock cancelAnimationFrame function\n$.cancelAnimationFrame=function(requestId){// find the request ID and remove it\nvar i,j;for(i=0,j=aAnimQueue.length;i}\n */getActivePointersListsExceptType:function getActivePointersListsExceptType(type){var delegate=THIS[this.hash];var listArray=[];for(var i=0;iDeprecated. Use buttons instead.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */enterHandler:function enterHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Number} event.buttons\n * Current buttons pressed.\n * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @param {Number} event.pointers\n * Number of pointers (all types) active in the tracked element.\n * @param {Boolean} event.insideElementPressed\n * True if the left mouse button is currently being pressed and was\n * initiated inside the tracked element, otherwise false.\n * @param {Boolean} event.buttonDownAny\n * Was the button down anywhere in the screen during the event. Deprecated. Use buttons instead.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */exitHandler:function exitHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Number} event.buttons\n * Current buttons pressed.\n * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */pressHandler:function pressHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Number} event.button\n * Button which caused the event.\n * -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.\n * @param {Number} event.buttons\n * Current buttons pressed.\n * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */nonPrimaryPressHandler:function nonPrimaryPressHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Number} event.buttons\n * Current buttons pressed.\n * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @param {Boolean} event.insideElementPressed\n * True if the left mouse button is currently being pressed and was\n * initiated inside the tracked element, otherwise false.\n * @param {Boolean} event.insideElementReleased\n * True if the cursor inside the tracked element when the button was released.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */releaseHandler:function releaseHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Number} event.button\n * Button which caused the event.\n * -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.\n * @param {Number} event.buttons\n * Current buttons pressed.\n * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */nonPrimaryReleaseHandler:function nonPrimaryReleaseHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Number} event.buttons\n * Current buttons pressed.\n * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */moveHandler:function moveHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Number} event.scroll\n * The scroll delta for the event.\n * @param {Boolean} event.shift\n * True if the shift key was pressed during this event.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead. Touch devices no longer generate scroll event.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */scrollHandler:function scrollHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Boolean} event.quick\n * True only if the clickDistThreshold and clickTimeThreshold are both passed. Useful for ignoring drag events.\n * @param {Boolean} event.shift\n * True if the shift key was pressed during this event.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */clickHandler:function clickHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Boolean} event.shift\n * True if the shift key was pressed during this event.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */dblClickHandler:function dblClickHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Number} event.buttons\n * Current buttons pressed.\n * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @param {OpenSeadragon.Point} event.delta\n * The x,y components of the difference between the current position and the last drag event position. Useful for ignoring or weighting the events.\n * @param {Number} event.speed\n * Current computed speed, in pixels per second.\n * @param {Number} event.direction\n * Current computed direction, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.\n * @param {Boolean} event.shift\n * True if the shift key was pressed during this event.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */dragHandler:function dragHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Number} event.speed\n * Speed at the end of a drag gesture, in pixels per second.\n * @param {Number} event.direction\n * Direction at the end of a drag gesture, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.\n * @param {Boolean} event.shift\n * True if the shift key was pressed during this event.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */dragEndHandler:function dragEndHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {Array.} event.gesturePoints\n * Gesture points associated with the gesture. Velocity data can be found here.\n * @param {OpenSeadragon.Point} event.lastCenter\n * The previous center point of the two pinch contact points relative to the tracked element.\n * @param {OpenSeadragon.Point} event.center\n * The center point of the two pinch contact points relative to the tracked element.\n * @param {Number} event.lastDistance\n * The previous distance between the two pinch contact points in CSS pixels.\n * @param {Number} event.distance\n * The distance between the two pinch contact points in CSS pixels.\n * @param {Boolean} event.shift\n * True if the shift key was pressed during this event.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */pinchHandler:function pinchHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {String} event.pointerType\n * \"mouse\", \"touch\", \"pen\", etc.\n * @param {OpenSeadragon.Point} event.position\n * The position of the event relative to the tracked element.\n * @param {Number} event.buttons\n * Current buttons pressed.\n * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @param {Boolean} event.isTouchEvent\n * True if the original event is a touch event, otherwise false. Deprecated. Use pointerType and/or originalEvent instead.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */stopHandler:function stopHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {Number} event.keyCode\n * The key code that was pressed.\n * @param {Boolean} event.ctrl\n * True if the ctrl key was pressed during this event.\n * @param {Boolean} event.shift\n * True if the shift key was pressed during this event.\n * @param {Boolean} event.alt\n * True if the alt key was pressed during this event.\n * @param {Boolean} event.meta\n * True if the meta key was pressed during this event.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */keyDownHandler:function keyDownHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {Number} event.keyCode\n * The key code that was pressed.\n * @param {Boolean} event.ctrl\n * True if the ctrl key was pressed during this event.\n * @param {Boolean} event.shift\n * True if the shift key was pressed during this event.\n * @param {Boolean} event.alt\n * True if the alt key was pressed during this event.\n * @param {Boolean} event.meta\n * True if the meta key was pressed during this event.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */keyUpHandler:function keyUpHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {Number} event.keyCode\n * The key code that was pressed.\n * @param {Boolean} event.ctrl\n * True if the ctrl key was pressed during this event.\n * @param {Boolean} event.shift\n * True if the shift key was pressed during this event.\n * @param {Boolean} event.alt\n * True if the alt key was pressed during this event.\n * @param {Boolean} event.meta\n * True if the meta key was pressed during this event.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */keyHandler:function keyHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */focusHandler:function focusHandler(){},/**\n * Implement or assign implementation to these handlers during or after\n * calling the constructor.\n * @function\n * @param {Object} event\n * @param {OpenSeadragon.MouseTracker} event.eventSource\n * A reference to the tracker instance.\n * @param {Object} event.originalEvent\n * The original event object.\n * @param {Boolean} event.preventDefaultAction\n * Set to true to prevent the tracker subscriber from performing its default action (subscriber implementation dependent). Default: false.\n * @param {Object} event.userData\n * Arbitrary user-defined object.\n */blurHandler:function blurHandler(){}};/**\n * Resets all active mousetrakers. (Added to patch issue #697 \"Mouse up outside map will cause \"canvas-drag\" event to stick\")\n *\n * @private\n * @member resetAllMouseTrackers\n * @memberof OpenSeadragon.MouseTracker\n */$.MouseTracker.resetAllMouseTrackers=function(){for(var i=0;i8||'onwheel'in document.createElement('div')?'wheel':// Modern browsers support 'wheel'\ndocument.onmousewheel!==undefined?'mousewheel':// Webkit and IE support at least 'mousewheel'\n'DOMMouseScroll';// Assume old Firefox\n/**\n * Detect legacy mouse capture support.\n */$.MouseTracker.supportsMouseCapture=function(){var divElement=document.createElement('div');return $.isFunction(divElement.setCapture)&&$.isFunction(divElement.releaseCapture);}();/**\n * Detect browser pointer device event model(s) and build appropriate list of events to subscribe to.\n */$.MouseTracker.subscribeEvents=[\"click\",\"dblclick\",\"keydown\",\"keyup\",\"keypress\",\"focus\",\"blur\",$.MouseTracker.wheelEventName];if($.MouseTracker.wheelEventName==\"DOMMouseScroll\"){// Older Firefox\n$.MouseTracker.subscribeEvents.push(\"MozMousePixelScroll\");}// Note: window.navigator.pointerEnable is deprecated on IE 11 and not part of W3C spec.\nif(window.PointerEvent&&(window.navigator.pointerEnabled||$.Browser.vendor!==$.BROWSERS.IE)){// IE11 and other W3C Pointer Event implementations (see http://www.w3.org/TR/pointerevents)\n$.MouseTracker.havePointerEvents=true;$.MouseTracker.subscribeEvents.push(\"pointerover\",\"pointerout\",\"pointerdown\",\"pointerup\",\"pointermove\",\"pointercancel\");$.MouseTracker.unprefixedPointerEvents=true;if(navigator.maxTouchPoints){$.MouseTracker.maxTouchPoints=navigator.maxTouchPoints;}else{$.MouseTracker.maxTouchPoints=0;}$.MouseTracker.haveMouseEnter=false;}else if(window.MSPointerEvent&&window.navigator.msPointerEnabled){// IE10\n$.MouseTracker.havePointerEvents=true;$.MouseTracker.subscribeEvents.push(\"MSPointerOver\",\"MSPointerOut\",\"MSPointerDown\",\"MSPointerUp\",\"MSPointerMove\",\"MSPointerCancel\");$.MouseTracker.unprefixedPointerEvents=false;if(navigator.msMaxTouchPoints){$.MouseTracker.maxTouchPoints=navigator.msMaxTouchPoints;}else{$.MouseTracker.maxTouchPoints=0;}$.MouseTracker.haveMouseEnter=false;}else{// Legacy W3C mouse events\n$.MouseTracker.havePointerEvents=false;if($.Browser.vendor===$.BROWSERS.IE&&$.Browser.version<9){$.MouseTracker.subscribeEvents.push(\"mouseenter\",\"mouseleave\");$.MouseTracker.haveMouseEnter=true;}else{$.MouseTracker.subscribeEvents.push(\"mouseover\",\"mouseout\");$.MouseTracker.haveMouseEnter=false;}$.MouseTracker.subscribeEvents.push(\"mousedown\",\"mouseup\",\"mousemove\");if('ontouchstart'in window){// iOS, Android, and other W3c Touch Event implementations\n// (see http://www.w3.org/TR/touch-events/)\n// (see https://developer.apple.com/library/ios/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html)\n// (see https://developer.apple.com/library/safari/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html)\n$.MouseTracker.subscribeEvents.push(\"touchstart\",\"touchend\",\"touchmove\",\"touchcancel\");}if('ongesturestart'in window){// iOS (see https://developer.apple.com/library/ios/documentation/AppleApplications/Reference/SafariWebContent/HandlingEvents/HandlingEvents.html)\n// Subscribe to these to prevent default gesture handling\n$.MouseTracker.subscribeEvents.push(\"gesturestart\",\"gesturechange\");}$.MouseTracker.mousePointerId=\"legacy-mouse\";$.MouseTracker.maxTouchPoints=10;}///////////////////////////////////////////////////////////////////////////////\n// Classes and typedefs\n///////////////////////////////////////////////////////////////////////////////\n/**\n * Represents a point of contact on the screen made by a mouse cursor, pen, touch, or other pointer device.\n *\n * @typedef {Object} GesturePoint\n * @memberof OpenSeadragon.MouseTracker\n *\n * @property {Number} id\n * Identifier unique from all other active GesturePoints for a given pointer device.\n * @property {String} type\n * The pointer device type: \"mouse\", \"touch\", \"pen\", etc.\n * @property {Boolean} captured\n * True if events for the gesture point are captured to the tracked element.\n * @property {Boolean} isPrimary\n * True if the gesture point is a master pointer amongst the set of active pointers for each pointer type. True for mouse and primary (first) touch/pen pointers.\n * @property {Boolean} insideElementPressed\n * True if button pressed or contact point initiated inside the screen area of the tracked element.\n * @property {Boolean} insideElement\n * True if pointer or contact point is currently inside the bounds of the tracked element.\n * @property {Number} speed\n * Current computed speed, in pixels per second.\n * @property {Number} direction\n * Current computed direction, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.\n * @property {OpenSeadragon.Point} contactPos\n * The initial pointer contact position, relative to the page including any scrolling. Only valid if the pointer has contact (pressed, touch contact, pen contact).\n * @property {Number} contactTime\n * The initial pointer contact time, in milliseconds. Only valid if the pointer has contact (pressed, touch contact, pen contact).\n * @property {OpenSeadragon.Point} lastPos\n * The last pointer position, relative to the page including any scrolling.\n * @property {Number} lastTime\n * The last pointer contact time, in milliseconds.\n * @property {OpenSeadragon.Point} currentPos\n * The current pointer position, relative to the page including any scrolling.\n * @property {Number} currentTime\n * The current pointer contact time, in milliseconds.\n */ /**\n * @class GesturePointList\n * @classdesc Provides an abstraction for a set of active {@link OpenSeadragon.MouseTracker.GesturePoint|GesturePoint} objects for a given pointer device type.\n * Active pointers are any pointer being tracked for this element which are in the hit-test area\n * of the element (for hover-capable devices) and/or have contact or a button press initiated in the element.\n * @memberof OpenSeadragon.MouseTracker\n * @param {String} type - The pointer device type: \"mouse\", \"touch\", \"pen\", etc.\n */$.MouseTracker.GesturePointList=function(type){this._gPoints=[];/**\n * The pointer device type: \"mouse\", \"touch\", \"pen\", etc.\n * @member {String} type\n * @memberof OpenSeadragon.MouseTracker.GesturePointList#\n */this.type=type;/**\n * Current buttons pressed for the device.\n * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @member {Number} buttons\n * @memberof OpenSeadragon.MouseTracker.GesturePointList#\n */this.buttons=0;/**\n * Current number of contact points (touch points, mouse down, etc.) for the device.\n * @member {Number} contacts\n * @memberof OpenSeadragon.MouseTracker.GesturePointList#\n */this.contacts=0;/**\n * Current number of clicks for the device. Used for multiple click gesture tracking.\n * @member {Number} clicks\n * @memberof OpenSeadragon.MouseTracker.GesturePointList#\n */this.clicks=0;/**\n * Current number of captured pointers for the device.\n * @member {Number} captureCount\n * @memberof OpenSeadragon.MouseTracker.GesturePointList#\n */this.captureCount=0;};/** @lends OpenSeadragon.MouseTracker.GesturePointList.prototype */$.MouseTracker.GesturePointList.prototype={/**\n * @function\n * @returns {Number} Number of gesture points in the list.\n */getLength:function getLength(){return this._gPoints.length;},/**\n * @function\n * @returns {Array.} The list of gesture points in the list as an array (read-only).\n */asArray:function asArray(){return this._gPoints;},/**\n * @function\n * @param {OpenSeadragon.MouseTracker.GesturePoint} gesturePoint - A gesture point to add to the list.\n * @returns {Number} Number of gesture points in the list.\n */add:function add(gp){return this._gPoints.push(gp);},/**\n * @function\n * @param {Number} id - The id of the gesture point to remove from the list.\n * @returns {Number} Number of gesture points in the list.\n */removeById:function removeById(id){var i,len=this._gPoints.length;for(i=0;i1&&(this.type===\"mouse\"||this.type===\"pen\")){this.contacts=1;}},/**\n * Decrement this pointer's contact count.\n * It will make sure the count does not go below 0.\n * @function\n */removeContact:function removeContact(){--this.contacts;if(this.contacts<0){this.contacts=0;}}};///////////////////////////////////////////////////////////////////////////////\n// Utility functions\n///////////////////////////////////////////////////////////////////////////////\n/**\n * Removes all tracked pointers.\n * @private\n * @inner\n */function clearTrackedPointers(tracker){var delegate=THIS[tracker.hash],i,pointerListCount=delegate.activePointersLists.length;for(i=0;i0){$.removeEvent($.MouseTracker.captureElement,'mousemove',delegate.mousemovecaptured,true);$.removeEvent($.MouseTracker.captureElement,'mouseup',delegate.mouseupcaptured,true);$.removeEvent($.MouseTracker.captureElement,$.MouseTracker.unprefixedPointerEvents?'pointermove':'MSPointerMove',delegate.pointermovecaptured,true);$.removeEvent($.MouseTracker.captureElement,$.MouseTracker.unprefixedPointerEvents?'pointerup':'MSPointerUp',delegate.pointerupcaptured,true);$.removeEvent($.MouseTracker.captureElement,'touchmove',delegate.touchmovecaptured,true);$.removeEvent($.MouseTracker.captureElement,'touchend',delegate.touchendcaptured,true);delegate.activePointersLists[i].captureCount=0;}}for(i=0;i0){for(i=0;i0){// simulate touchend/mouseup\nupdatePointersUp(tracker,event,abortGPoints,0);// 0 means primary button press/release or touch contact\n// release pointer capture\npointsList.captureCount=1;releasePointer(tracker,pointsList.type);// simulate touchleave/mouseout\nupdatePointersExit(tracker,event,abortGPoints);}}}/**\n * @private\n * @inner\n */function onTouchStart(tracker,event){var time,i,j,touchCount=event.changedTouches.length,gPoints=[],parentGPoints,pointsList=tracker.getActivePointersListByType('touch');time=$.now();if(pointsList.getLength()>event.touches.length-touchCount){$.console.warn('Tracked touch contact count doesn\\'t match event.touches.length. Removing all tracked touch pointers.');abortContacts(tracker,event,pointsList);}for(i=0;i} gPoints\n * Gesture points associated with the event.\n */function updatePointersEnter(tracker,event,gPoints){var pointsList=tracker.getActivePointersListByType(gPoints[0].type),i,gPointCount=gPoints.length,curGPoint,updateGPoint,propagate;for(i=0;i} gPoints\n * Gesture points associated with the event.\n */function updatePointersExit(tracker,event,gPoints){var pointsList=tracker.getActivePointersListByType(gPoints[0].type),i,gPointCount=gPoints.length,curGPoint,updateGPoint,propagate;for(i=0;i} gPoints\n * Gesture points associated with the event.\n * @param {Number} buttonChanged\n * The button involved in the event: -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.\n * Note on chorded button presses (a button pressed when another button is already pressed): In the W3C Pointer Events model,\n * only one pointerdown/pointerup event combo is fired. Chorded button state changes instead fire pointermove events.\n *\n * @returns {Boolean} True if pointers should be captured to the tracked element, otherwise false.\n */function updatePointersDown(tracker,event,gPoints,buttonChanged){var delegate=THIS[tracker.hash],propagate,pointsList=tracker.getActivePointersListByType(gPoints[0].type),i,gPointCount=gPoints.length,curGPoint,updateGPoint;if(typeof event.buttons!=='undefined'){pointsList.buttons=event.buttons;}else{if($.Browser.vendor===$.BROWSERS.IE&&$.Browser.version<9){if(buttonChanged===0){// Primary\npointsList.buttons+=1;}else if(buttonChanged===1){// Aux\npointsList.buttons+=4;}else if(buttonChanged===2){// Secondary\npointsList.buttons+=2;}else if(buttonChanged===3){// X1 (Back)\npointsList.buttons+=8;}else if(buttonChanged===4){// X2 (Forward)\npointsList.buttons+=16;}else if(buttonChanged===5){// Pen Eraser\npointsList.buttons+=32;}}else{if(buttonChanged===0){// Primary\npointsList.buttons|=1;}else if(buttonChanged===1){// Aux\npointsList.buttons|=4;}else if(buttonChanged===2){// Secondary\npointsList.buttons|=2;}else if(buttonChanged===3){// X1 (Back)\npointsList.buttons|=8;}else if(buttonChanged===4){// X2 (Forward)\npointsList.buttons|=16;}else if(buttonChanged===5){// Pen Eraser\npointsList.buttons|=32;}}}// Some pointers may steal control from another pointer without firing the appropriate release events\n// e.g. Touching a screen while click-dragging with certain mice.\nvar otherPointsLists=tracker.getActivePointersListsExceptType(gPoints[0].type);for(i=0;i} gPoints\n * Gesture points associated with the event.\n * @param {Number} buttonChanged\n * The button involved in the event: -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.\n * Note on chorded button presses (a button pressed when another button is already pressed): In the W3C Pointer Events model,\n * only one pointerdown/pointerup event combo is fired. Chorded button state changes instead fire pointermove events.\n *\n * @returns {Boolean} True if pointer capture should be released from the tracked element, otherwise false.\n */function updatePointersUp(tracker,event,gPoints,buttonChanged){var delegate=THIS[tracker.hash],pointsList=tracker.getActivePointersListByType(gPoints[0].type),propagate,releasePoint,releaseTime,i,gPointCount=gPoints.length,curGPoint,updateGPoint,releaseCapture=false,wasCaptured=false,quick;if(typeof event.buttons!=='undefined'){pointsList.buttons=event.buttons;}else{if($.Browser.vendor===$.BROWSERS.IE&&$.Browser.version<9){if(buttonChanged===0){// Primary\npointsList.buttons-=1;}else if(buttonChanged===1){// Aux\npointsList.buttons-=4;}else if(buttonChanged===2){// Secondary\npointsList.buttons-=2;}else if(buttonChanged===3){// X1 (Back)\npointsList.buttons-=8;}else if(buttonChanged===4){// X2 (Forward)\npointsList.buttons-=16;}else if(buttonChanged===5){// Pen Eraser\npointsList.buttons-=32;}}else{if(buttonChanged===0){// Primary\npointsList.buttons^=~1;}else if(buttonChanged===1){// Aux\npointsList.buttons^=~4;}else if(buttonChanged===2){// Secondary\npointsList.buttons^=~2;}else if(buttonChanged===3){// X1 (Back)\npointsList.buttons^=~8;}else if(buttonChanged===4){// X2 (Forward)\npointsList.buttons^=~16;}else if(buttonChanged===5){// Pen Eraser\npointsList.buttons^=~32;}}}// Only capture and track primary button, pen, and touch contacts\nif(buttonChanged!==0){// Aux Release\nif(tracker.nonPrimaryReleaseHandler){propagate=tracker.nonPrimaryReleaseHandler({eventSource:tracker,pointerType:gPoints[0].type,position:getPointRelativeToAbsolute(gPoints[0].currentPos,tracker.element),button:buttonChanged,buttons:pointsList.buttons,isTouchEvent:gPoints[0].type==='touch',originalEvent:event,preventDefaultAction:false,userData:tracker.userData});if(propagate===false){$.cancelEvent(event);}}// A primary mouse button may have been released while the non-primary button was down\nvar otherPointsList=tracker.getActivePointersListByType(\"mouse\");// Stop tracking the mouse; see https://github.com/openseadragon/openseadragon/pull/1223\nabortContacts(tracker,event,otherPointsList);// No-op if no active pointer\nreturn false;}for(i=0;i} gPoints\n * Gesture points associated with the event.\n */function updatePointersMove(tracker,event,gPoints){var delegate=THIS[tracker.hash],pointsList=tracker.getActivePointersListByType(gPoints[0].type),i,gPointCount=gPoints.length,curGPoint,updateGPoint,gPointArray,delta,propagate;if(typeof event.buttons!=='undefined'){pointsList.buttons=event.buttons;}for(i=0;i} gPoints\n * Gesture points associated with the event.\n */function updatePointersCancel(tracker,event,gPoints){updatePointersUp(tracker,event,gPoints,0);updatePointersExit(tracker,event,gPoints);}/**\n * @private\n * @inner\n */function handlePointerStop(tracker,originalMoveEvent,pointerType){if(tracker.stopHandler){tracker.stopHandler({eventSource:tracker,pointerType:pointerType,position:getMouseRelative(originalMoveEvent,tracker.element),buttons:tracker.getActivePointersListByType(pointerType).buttons,isTouchEvent:pointerType==='touch',originalEvent:originalMoveEvent,preventDefaultAction:false,userData:tracker.userData});}}/**\n * True if inside an iframe, otherwise false.\n * @member {Boolean} isInIframe\n * @private\n * @inner\n */var isInIframe=function(){try{return window.self!==window.top;}catch(e){return true;}}();/**\n * @function\n * @private\n * @inner\n * @returns {Boolean} True if the target has access rights to events, otherwise false.\n */function canAccessEvents(target){try{return target.addEventListener&&target.removeEventListener;}catch(e){return false;}}})(OpenSeadragon);/*\n * OpenSeadragon - Control\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * An enumeration of supported locations where controls can be anchored.\n * The anchoring is always relative to the container.\n * @member ControlAnchor\n * @memberof OpenSeadragon\n * @static\n * @type {Object}\n * @property {Number} NONE\n * @property {Number} TOP_LEFT\n * @property {Number} TOP_RIGHT\n * @property {Number} BOTTOM_LEFT\n * @property {Number} BOTTOM_RIGHT\n * @property {Number} ABSOLUTE\n */$.ControlAnchor={NONE:0,TOP_LEFT:1,TOP_RIGHT:2,BOTTOM_RIGHT:3,BOTTOM_LEFT:4,ABSOLUTE:5};/**\n * @class Control\n * @classdesc A Control represents any interface element which is meant to allow the user\n * to interact with the zoomable interface. Any control can be anchored to any\n * element.\n *\n * @memberof OpenSeadragon\n * @param {Element} element - the control element to be anchored in the container.\n * @param {Object } options - All required and optional settings for configuring a control element.\n * @param {OpenSeadragon.ControlAnchor} [options.anchor=OpenSeadragon.ControlAnchor.NONE] - the position of the control\n * relative to the container.\n * @param {Boolean} [options.attachToViewer=true] - Whether the control should be added directly to the viewer, or\n * directly to the container\n * @param {Boolean} [options.autoFade=true] - Whether the control should have the autofade behavior\n * @param {Element} container - the element to control will be anchored too.\n */$.Control=function(element,options,container){var parent=element.parentNode;if(typeof options==='number'){$.console.error(\"Passing an anchor directly into the OpenSeadragon.Control constructor is deprecated; \"+\"please use an options object instead. \"+\"Support for this deprecated variant is scheduled for removal in December 2013\");options={anchor:options};}options.attachToViewer=typeof options.attachToViewer==='undefined'?true:options.attachToViewer;/**\n * True if the control should have autofade behavior.\n * @member {Boolean} autoFade\n * @memberof OpenSeadragon.Control#\n */this.autoFade=typeof options.autoFade==='undefined'?true:options.autoFade;/**\n * The element providing the user interface with some type of control (e.g. a zoom-in button).\n * @member {Element} element\n * @memberof OpenSeadragon.Control#\n */this.element=element;/**\n * The position of the Control relative to its container.\n * @member {OpenSeadragon.ControlAnchor} anchor\n * @memberof OpenSeadragon.Control#\n */this.anchor=options.anchor;/**\n * The Control's containing element.\n * @member {Element} container\n * @memberof OpenSeadragon.Control#\n */this.container=container;/**\n * A neutral element surrounding the control element.\n * @member {Element} wrapper\n * @memberof OpenSeadragon.Control#\n */if(this.anchor==$.ControlAnchor.ABSOLUTE){this.wrapper=$.makeNeutralElement(\"div\");this.wrapper.style.position=\"absolute\";this.wrapper.style.top=typeof options.top==\"number\"?options.top+'px':options.top;this.wrapper.style.left=typeof options.left==\"number\"?options.left+'px':options.left;this.wrapper.style.height=typeof options.height==\"number\"?options.height+'px':options.height;this.wrapper.style.width=typeof options.width==\"number\"?options.width+'px':options.width;this.wrapper.style.margin=\"0px\";this.wrapper.style.padding=\"0px\";this.element.style.position=\"relative\";this.element.style.top=\"0px\";this.element.style.left=\"0px\";this.element.style.height=\"100%\";this.element.style.width=\"100%\";}else{this.wrapper=$.makeNeutralElement(\"div\");this.wrapper.style.display=\"inline-block\";if(this.anchor==$.ControlAnchor.NONE){// IE6 fix\nthis.wrapper.style.width=this.wrapper.style.height=\"100%\";}}this.wrapper.appendChild(this.element);if(options.attachToViewer){if(this.anchor==$.ControlAnchor.TOP_RIGHT||this.anchor==$.ControlAnchor.BOTTOM_RIGHT){this.container.insertBefore(this.wrapper,this.container.firstChild);}else{this.container.appendChild(this.wrapper);}}else{parent.appendChild(this.wrapper);}};/** @lends OpenSeadragon.Control.prototype */$.Control.prototype={/**\n * Removes the control from the container.\n * @function\n */destroy:function destroy(){this.wrapper.removeChild(this.element);this.container.removeChild(this.wrapper);},/**\n * Determines if the control is currently visible.\n * @function\n * @return {Boolean} true if currenly visible, false otherwise.\n */isVisible:function isVisible(){return this.wrapper.style.display!=\"none\";},/**\n * Toggles the visibility of the control.\n * @function\n * @param {Boolean} visible - true to make visible, false to hide.\n */setVisible:function setVisible(visible){this.wrapper.style.display=visible?this.anchor==$.ControlAnchor.ABSOLUTE?'block':'inline-block':\"none\";},/**\n * Sets the opacity level for the control.\n * @function\n * @param {Number} opactiy - a value between 1 and 0 inclusively.\n */setOpacity:function setOpacity(opacity){if(this.element[$.SIGNAL]&&$.Browser.vendor==$.BROWSERS.IE){$.setElementOpacity(this.element,opacity,true);}else{$.setElementOpacity(this.wrapper,opacity,true);}}};})(OpenSeadragon);/*\n * OpenSeadragon - ControlDock\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * @class ControlDock\n * @classdesc Provides a container element (a <form> element) with support for the layout of control elements.\n *\n * @memberof OpenSeadragon\n */$.ControlDock=function(options){var layouts=['topleft','topright','bottomright','bottomleft'],layout,i;$.extend(true,this,{id:'controldock-'+$.now()+'-'+Math.floor(Math.random()*1000000),container:$.makeNeutralElement('div'),controls:[]},options);// Disable the form's submit; otherwise button clicks and return keys\n// can trigger it.\nthis.container.onsubmit=function(){return false;};if(this.element){this.element=$.getElement(this.element);this.element.appendChild(this.container);this.element.style.position='relative';this.container.style.width='100%';this.container.style.height='100%';}for(i=0;i=0){return;// they're trying to add a duplicate control\n}switch(controlOptions.anchor){case $.ControlAnchor.TOP_RIGHT:div=this.controls.topright;element.style.position=\"relative\";element.style.paddingRight=\"0px\";element.style.paddingTop=\"0px\";break;case $.ControlAnchor.BOTTOM_RIGHT:div=this.controls.bottomright;element.style.position=\"relative\";element.style.paddingRight=\"0px\";element.style.paddingBottom=\"0px\";break;case $.ControlAnchor.BOTTOM_LEFT:div=this.controls.bottomleft;element.style.position=\"relative\";element.style.paddingLeft=\"0px\";element.style.paddingBottom=\"0px\";break;case $.ControlAnchor.TOP_LEFT:div=this.controls.topleft;element.style.position=\"relative\";element.style.paddingLeft=\"0px\";element.style.paddingTop=\"0px\";break;case $.ControlAnchor.ABSOLUTE:div=this.container;element.style.margin=\"0px\";element.style.padding=\"0px\";break;default:case $.ControlAnchor.NONE:div=this.container;element.style.margin=\"0px\";element.style.padding=\"0px\";break;}this.controls.push(new $.Control(element,controlOptions,div));element.style.display=\"inline-block\";},/**\n * @function\n * @return {OpenSeadragon.ControlDock} Chainable.\n */removeControl:function removeControl(element){element=$.getElement(element);var i=getControlIndex(this,element);if(i>=0){this.controls[i].destroy();this.controls.splice(i,1);}return this;},/**\n * @function\n * @return {OpenSeadragon.ControlDock} Chainable.\n */clearControls:function clearControls(){while(this.controls.length>0){this.controls.pop().destroy();}return this;},/**\n * @function\n * @return {Boolean}\n */areControlsEnabled:function areControlsEnabled(){var i;for(i=this.controls.length-1;i>=0;i--){if(this.controls[i].isVisible()){return true;}}return false;},/**\n * @function\n * @return {OpenSeadragon.ControlDock} Chainable.\n */setControlsEnabled:function setControlsEnabled(enabled){var i;for(i=this.controls.length-1;i>=0;i--){this.controls[i].setVisible(enabled);}return this;}};///////////////////////////////////////////////////////////////////////////////\n// Utility methods\n///////////////////////////////////////////////////////////////////////////////\nfunction getControlIndex(dock,element){var controls=dock.controls,i;for(i=controls.length-1;i>=0;i--){if(controls[i].element==element){return i;}}return-1;}})(OpenSeadragon);/*\n * OpenSeadragon - Placement\n *\n * Copyright (C) 2010-2016 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * An enumeration of positions to anchor an element.\n * @member Placement\n * @memberOf OpenSeadragon\n * @static\n * @readonly\n * @property {OpenSeadragon.Placement} CENTER\n * @property {OpenSeadragon.Placement} TOP_LEFT\n * @property {OpenSeadragon.Placement} TOP\n * @property {OpenSeadragon.Placement} TOP_RIGHT\n * @property {OpenSeadragon.Placement} RIGHT\n * @property {OpenSeadragon.Placement} BOTTOM_RIGHT\n * @property {OpenSeadragon.Placement} BOTTOM\n * @property {OpenSeadragon.Placement} BOTTOM_LEFT\n * @property {OpenSeadragon.Placement} LEFT\n */$.Placement=$.freezeObject({CENTER:0,TOP_LEFT:1,TOP:2,TOP_RIGHT:3,RIGHT:4,BOTTOM_RIGHT:5,BOTTOM:6,BOTTOM_LEFT:7,LEFT:8,properties:{0:{isLeft:false,isHorizontallyCentered:true,isRight:false,isTop:false,isVerticallyCentered:true,isBottom:false},1:{isLeft:true,isHorizontallyCentered:false,isRight:false,isTop:true,isVerticallyCentered:false,isBottom:false},2:{isLeft:false,isHorizontallyCentered:true,isRight:false,isTop:true,isVerticallyCentered:false,isBottom:false},3:{isLeft:false,isHorizontallyCentered:false,isRight:true,isTop:true,isVerticallyCentered:false,isBottom:false},4:{isLeft:false,isHorizontallyCentered:false,isRight:true,isTop:false,isVerticallyCentered:true,isBottom:false},5:{isLeft:false,isHorizontallyCentered:false,isRight:true,isTop:false,isVerticallyCentered:false,isBottom:true},6:{isLeft:false,isHorizontallyCentered:true,isRight:false,isTop:false,isVerticallyCentered:false,isBottom:true},7:{isLeft:true,isHorizontallyCentered:false,isRight:false,isTop:false,isVerticallyCentered:false,isBottom:true},8:{isLeft:true,isHorizontallyCentered:false,isRight:false,isTop:false,isVerticallyCentered:true,isBottom:false}}});})(OpenSeadragon);/*\n * OpenSeadragon - Viewer\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){// dictionary from hash to private properties\nvar THIS={};var nextHash=1;/**\n *\n * The main point of entry into creating a zoomable image on the page.
\n *
\n * We have provided an idiomatic javascript constructor which takes\n * a single object, but still support the legacy positional arguments.
\n *
\n * The options below are given in order that they appeared in the constructor\n * as arguments and we translate a positional call into an idiomatic call.
\n *
\n * To create a viewer, you can use either of this methods:
\n * \n * var viewer = new OpenSeadragon.Viewer(options);
\n * var viewer = OpenSeadragon(options);
\n *
\n * @class Viewer\n * @classdesc The main OpenSeadragon viewer class.\n *\n * @memberof OpenSeadragon\n * @extends OpenSeadragon.EventSource\n * @extends OpenSeadragon.ControlDock\n * @param {OpenSeadragon.Options} options - Viewer options.\n *\n **/$.Viewer=function(options){var args=arguments,_this=this,i;//backward compatibility for positional args while prefering more\n//idiomatic javascript options object as the only argument\nif(!$.isPlainObject(options)){options={id:args[0],xmlPath:args.length>1?args[1]:undefined,prefixUrl:args.length>2?args[2]:undefined,controls:args.length>3?args[3]:undefined,overlays:args.length>4?args[4]:undefined};}//options.config and the general config argument are deprecated\n//in favor of the more direct specification of optional settings\n//being pass directly on the options object\nif(options.config){$.extend(true,options,options.config);delete options.config;}//Public properties\n//Allow the options object to override global defaults\n$.extend(true,this,{//internal state and dom identifiers\nid:options.id,hash:options.hash||nextHash++,/**\n * Index for page to be shown first next time open() is called (only used in sequenceMode).\n * @member {Number} initialPage\n * @memberof OpenSeadragon.Viewer#\n */initialPage:0,//dom nodes\n/**\n * The parent element of this Viewer instance, passed in when the Viewer was created.\n * @member {Element} element\n * @memberof OpenSeadragon.Viewer#\n */element:null,/**\n * A <div> element (provided by {@link OpenSeadragon.ControlDock}), the base element of this Viewer instance.
\n * Child element of {@link OpenSeadragon.Viewer#element}.\n * @member {Element} container\n * @memberof OpenSeadragon.Viewer#\n */container:null,/**\n * A <div> element, the element where user-input events are handled for panning and zooming.
\n * Child element of {@link OpenSeadragon.Viewer#container},\n * positioned on top of {@link OpenSeadragon.Viewer#keyboardCommandArea}.
\n * The parent of {@link OpenSeadragon.Drawer#canvas} instances.\n * @member {Element} canvas\n * @memberof OpenSeadragon.Viewer#\n */canvas:null,// Overlays list. An overlay allows to add html on top of the viewer.\noverlays:[],// Container inside the canvas where overlays are drawn.\noverlaysContainer:null,//private state properties\npreviousBody:[],//This was originally initialized in the constructor and so could never\n//have anything in it. now it can because we allow it to be specified\n//in the options and is only empty by default if not specified. Also\n//this array was returned from get_controls which I find confusing\n//since this object has a controls property which is treated in other\n//functions like clearControls. I'm removing the accessors.\ncustomControls:[],//These are originally not part options but declared as members\n//in initialize. It's still considered idiomatic to put them here\nsource:null,/**\n * Handles rendering of tiles in the viewer. Created for each TileSource opened.\n * @member {OpenSeadragon.Drawer} drawer\n * @memberof OpenSeadragon.Viewer#\n */drawer:null,world:null,/**\n * Handles coordinate-related functionality - zoom, pan, rotation, etc. Created for each TileSource opened.\n * @member {OpenSeadragon.Viewport} viewport\n * @memberof OpenSeadragon.Viewer#\n */viewport:null,/**\n * @member {OpenSeadragon.Navigator} navigator\n * @memberof OpenSeadragon.Viewer#\n */navigator:null,//A collection viewport is a separate viewport used to provide\n//simultaneous rendering of sets of tiles\ncollectionViewport:null,collectionDrawer:null,//UI image resources\n//TODO: rename navImages to uiImages\nnavImages:null,//interface button controls\nbuttons:null,//TODO: this is defunct so safely remove it\nprofiler:null},$.DEFAULT_SETTINGS,options);if(typeof this.hash===\"undefined\"){throw new Error(\"A hash must be defined, either by specifying options.id or options.hash.\");}if(typeof THIS[this.hash]!==\"undefined\"){// We don't want to throw an error here, as the user might have discarded\n// the previous viewer with the same hash and now want to recreate it.\n$.console.warn(\"Hash \"+this.hash+\" has already been used.\");}//Private state properties\nTHIS[this.hash]={\"fsBoundsDelta\":new $.Point(1,1),\"prevContainerSize\":null,\"animating\":false,\"forceRedraw\":false,\"mouseInside\":false,\"group\":null,// whether we should be continuously zooming\n\"zooming\":false,// how much we should be continuously zooming by\n\"zoomFactor\":null,\"lastZoomTime\":null,\"fullPage\":false,\"onfullscreenchange\":null};this._sequenceIndex=0;this._firstOpen=true;this._updateRequestId=null;this._loadQueue=[];this.currentOverlays=[];this._lastScrollTime=$.now();// variable used to help normalize the scroll event speed of different devices\n//Inherit some behaviors and properties\n$.EventSource.call(this);this.addHandler('open-failed',function(event){var msg=$.getString(\"Errors.OpenFailed\",event.eventSource,event.message);_this._showMessage(msg);});$.ControlDock.call(this,options);//Deal with tile sources\nif(this.xmlPath){//Deprecated option. Now it is preferred to use the tileSources option\nthis.tileSources=[this.xmlPath];}this.element=this.element||document.getElementById(this.id);this.canvas=$.makeNeutralElement(\"div\");this.canvas.className=\"openseadragon-canvas\";(function(style){style.width=\"100%\";style.height=\"100%\";style.overflow=\"hidden\";style.position=\"absolute\";style.top=\"0px\";style.left=\"0px\";})(this.canvas.style);$.setElementTouchActionNone(this.canvas);if(options.tabIndex!==\"\"){this.canvas.tabIndex=options.tabIndex===undefined?0:options.tabIndex;}//the container is created through applying the ControlDock constructor above\nthis.container.className=\"openseadragon-container\";(function(style){style.width=\"100%\";style.height=\"100%\";style.position=\"relative\";style.overflow=\"hidden\";style.left=\"0px\";style.top=\"0px\";style.textAlign=\"left\";// needed to protect against\n})(this.container.style);this.container.insertBefore(this.canvas,this.container.firstChild);this.element.appendChild(this.container);//Used for toggling between fullscreen and default container size\n//TODO: these can be closure private and shared across Viewer\n// instances.\nthis.bodyWidth=document.body.style.width;this.bodyHeight=document.body.style.height;this.bodyOverflow=document.body.style.overflow;this.docOverflow=document.documentElement.style.overflow;this.innerTracker=new $.MouseTracker({element:this.canvas,startDisabled:!this.mouseNavEnabled,clickTimeThreshold:this.clickTimeThreshold,clickDistThreshold:this.clickDistThreshold,dblClickTimeThreshold:this.dblClickTimeThreshold,dblClickDistThreshold:this.dblClickDistThreshold,keyDownHandler:$.delegate(this,onCanvasKeyDown),keyHandler:$.delegate(this,onCanvasKeyPress),clickHandler:$.delegate(this,onCanvasClick),dblClickHandler:$.delegate(this,onCanvasDblClick),dragHandler:$.delegate(this,onCanvasDrag),dragEndHandler:$.delegate(this,onCanvasDragEnd),enterHandler:$.delegate(this,onCanvasEnter),exitHandler:$.delegate(this,onCanvasExit),pressHandler:$.delegate(this,onCanvasPress),releaseHandler:$.delegate(this,onCanvasRelease),nonPrimaryPressHandler:$.delegate(this,onCanvasNonPrimaryPress),nonPrimaryReleaseHandler:$.delegate(this,onCanvasNonPrimaryRelease),scrollHandler:$.delegate(this,onCanvasScroll),pinchHandler:$.delegate(this,onCanvasPinch)});this.outerTracker=new $.MouseTracker({element:this.container,startDisabled:!this.mouseNavEnabled,clickTimeThreshold:this.clickTimeThreshold,clickDistThreshold:this.clickDistThreshold,dblClickTimeThreshold:this.dblClickTimeThreshold,dblClickDistThreshold:this.dblClickDistThreshold,enterHandler:$.delegate(this,onContainerEnter),exitHandler:$.delegate(this,onContainerExit)});if(this.toolbar){this.toolbar=new $.ControlDock({element:this.toolbar});}this.bindStandardControls();THIS[this.hash].prevContainerSize=_getSafeElemSize(this.container);// Create the world\nthis.world=new $.World({viewer:this});this.world.addHandler('add-item',function(event){// For backwards compatibility, we maintain the source property\n_this.source=_this.world.getItemAt(0).source;THIS[_this.hash].forceRedraw=true;if(!_this._updateRequestId){_this._updateRequestId=scheduleUpdate(_this,updateMulti);}});this.world.addHandler('remove-item',function(event){// For backwards compatibility, we maintain the source property\nif(_this.world.getItemCount()){_this.source=_this.world.getItemAt(0).source;}else{_this.source=null;}THIS[_this.hash].forceRedraw=true;});this.world.addHandler('metrics-change',function(event){if(_this.viewport){_this.viewport._setContentBounds(_this.world.getHomeBounds(),_this.world.getContentFactor());}});this.world.addHandler('item-index-change',function(event){// For backwards compatibility, we maintain the source property\n_this.source=_this.world.getItemAt(0).source;});// Create the viewport\nthis.viewport=new $.Viewport({containerSize:THIS[this.hash].prevContainerSize,springStiffness:this.springStiffness,animationTime:this.animationTime,minZoomImageRatio:this.minZoomImageRatio,maxZoomPixelRatio:this.maxZoomPixelRatio,visibilityRatio:this.visibilityRatio,wrapHorizontal:this.wrapHorizontal,wrapVertical:this.wrapVertical,defaultZoomLevel:this.defaultZoomLevel,minZoomLevel:this.minZoomLevel,maxZoomLevel:this.maxZoomLevel,viewer:this,degrees:this.degrees,flipped:this.flipped,navigatorRotate:this.navigatorRotate,homeFillsViewer:this.homeFillsViewer,margins:this.viewportMargins});this.viewport._setContentBounds(this.world.getHomeBounds(),this.world.getContentFactor());// Create the image loader\nthis.imageLoader=new $.ImageLoader({jobLimit:this.imageLoaderLimit,timeout:options.timeout});// Create the tile cache\nthis.tileCache=new $.TileCache({maxImageCacheCount:this.maxImageCacheCount});// Create the drawer\nthis.drawer=new $.Drawer({viewer:this,viewport:this.viewport,element:this.canvas,debugGridColor:this.debugGridColor});// Overlay container\nthis.overlaysContainer=$.makeNeutralElement(\"div\");this.canvas.appendChild(this.overlaysContainer);// Now that we have a drawer, see if it supports rotate. If not we need to remove the rotate buttons\nif(!this.drawer.canRotate()){// Disable/remove the rotate left/right buttons since they aren't supported\nif(this.rotateLeft){i=this.buttons.buttons.indexOf(this.rotateLeft);this.buttons.buttons.splice(i,1);this.buttons.element.removeChild(this.rotateLeft.element);}if(this.rotateRight){i=this.buttons.buttons.indexOf(this.rotateRight);this.buttons.buttons.splice(i,1);this.buttons.element.removeChild(this.rotateRight.element);}}//Instantiate a navigator if configured\nif(this.showNavigator){this.navigator=new $.Navigator({id:this.navigatorId,position:this.navigatorPosition,sizeRatio:this.navigatorSizeRatio,maintainSizeRatio:this.navigatorMaintainSizeRatio,top:this.navigatorTop,left:this.navigatorLeft,width:this.navigatorWidth,height:this.navigatorHeight,autoResize:this.navigatorAutoResize,autoFade:this.navigatorAutoFade,prefixUrl:this.prefixUrl,viewer:this,navigatorRotate:this.navigatorRotate,crossOriginPolicy:this.crossOriginPolicy});}// Sequence mode\nif(this.sequenceMode){this.bindSequenceControls();}// Open initial tilesources\nif(this.tileSources){this.open(this.tileSources);}// Add custom controls\nfor(i=0;i-1&&options.index=0&&page=0){// they're trying to add a duplicate overlay\nreturn this;}var overlay=getOverlayObject(this,options);this.currentOverlays.push(overlay);overlay.drawHTML(this.overlaysContainer,this.viewport);/**\n * Raised when an overlay is added to the viewer (see {@link OpenSeadragon.Viewer#addOverlay}).\n *\n * @event add-overlay\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.\n * @property {Element} element - The overlay element.\n * @property {OpenSeadragon.Point|OpenSeadragon.Rect} location\n * @property {OpenSeadragon.Placement} placement\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('add-overlay',{element:element,location:options.location,placement:options.placement});return this;},/**\n * Updates the overlay represented by the reference to the element or\n * element id moving it to the new location, relative to the new placement.\n * @method\n * @param {Element|String} element - A reference to an element or an id for\n * the element which is overlayed.\n * @param {OpenSeadragon.Point|OpenSeadragon.Rect} location - The point or\n * rectangle which will be overlayed. This is a viewport relative location.\n * @param {OpenSeadragon.Placement} placement - The position of the\n * viewport which the location coordinates will be treated as relative\n * to.\n * @return {OpenSeadragon.Viewer} Chainable.\n * @fires OpenSeadragon.Viewer.event:update-overlay\n */updateOverlay:function updateOverlay(element,location,placement){var i;element=$.getElement(element);i=getOverlayIndex(this.currentOverlays,element);if(i>=0){this.currentOverlays[i].update(location,placement);THIS[this.hash].forceRedraw=true;/**\n * Raised when an overlay's location or placement changes\n * (see {@link OpenSeadragon.Viewer#updateOverlay}).\n *\n * @event update-overlay\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the\n * Viewer which raised the event.\n * @property {Element} element\n * @property {OpenSeadragon.Point|OpenSeadragon.Rect} location\n * @property {OpenSeadragon.Placement} placement\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('update-overlay',{element:element,location:location,placement:placement});}return this;},/**\n * Removes an overlay identified by the reference element or element id\n * and schedules an update.\n * @method\n * @param {Element|String} element - A reference to the element or an\n * element id which represent the ovelay content to be removed.\n * @return {OpenSeadragon.Viewer} Chainable.\n * @fires OpenSeadragon.Viewer.event:remove-overlay\n */removeOverlay:function removeOverlay(element){var i;element=$.getElement(element);i=getOverlayIndex(this.currentOverlays,element);if(i>=0){this.currentOverlays[i].destroy();this.currentOverlays.splice(i,1);THIS[this.hash].forceRedraw=true;/**\n * Raised when an overlay is removed from the viewer\n * (see {@link OpenSeadragon.Viewer#removeOverlay}).\n *\n * @event remove-overlay\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the\n * Viewer which raised the event.\n * @property {Element} element - The overlay element.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('remove-overlay',{element:element});}return this;},/**\n * Removes all currently configured Overlays from this Viewer and schedules\n * an update.\n * @method\n * @return {OpenSeadragon.Viewer} Chainable.\n * @fires OpenSeadragon.Viewer.event:clear-overlay\n */clearOverlays:function clearOverlays(){while(this.currentOverlays.length>0){this.currentOverlays.pop().destroy();}THIS[this.hash].forceRedraw=true;/**\n * Raised when all overlays are removed from the viewer (see {@link OpenSeadragon.Drawer#clearOverlays}).\n *\n * @event clear-overlay\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('clear-overlay',{});return this;},/**\n * Finds an overlay identified by the reference element or element id\n * and returns it as an object, return null if not found.\n * @method\n * @param {Element|String} element - A reference to the element or an\n * element id which represents the overlay content.\n * @return {OpenSeadragon.Overlay} the matching overlay or null if none found.\n */getOverlayById:function getOverlayById(element){var i;element=$.getElement(element);i=getOverlayIndex(this.currentOverlays,element);if(i>=0){return this.currentOverlays[i];}else{return null;}},/**\n * Updates the sequence buttons.\n * @function OpenSeadragon.Viewer.prototype._updateSequenceButtons\n * @private\n * @param {Number} Sequence Value\n */_updateSequenceButtons:function _updateSequenceButtons(page){if(this.nextButton){if(!this.tileSources||this.tileSources.length-1===page){//Disable next button\nif(!this.navPrevNextWrap){this.nextButton.disable();}}else{this.nextButton.enable();}}if(this.previousButton){if(page>0){//Enable previous button\nthis.previousButton.enable();}else{if(!this.navPrevNextWrap){this.previousButton.disable();}}}},/**\n * Display a message in the viewport\n * @function OpenSeadragon.Viewer.prototype._showMessage\n * @private\n * @param {String} text message\n */_showMessage:function _showMessage(message){this._hideMessage();var div=$.makeNeutralElement(\"div\");div.appendChild(document.createTextNode(message));this.messageDiv=$.makeCenteredNode(div);$.addClass(this.messageDiv,\"openseadragon-message\");this.container.appendChild(this.messageDiv);},/**\n * Hide any currently displayed viewport message\n * @function OpenSeadragon.Viewer.prototype._hideMessage\n * @private\n */_hideMessage:function _hideMessage(){var div=this.messageDiv;if(div){div.parentNode.removeChild(div);delete this.messageDiv;}},/**\n * Gets this viewer's gesture settings for the given pointer device type.\n * @method\n * @param {String} type - The pointer device type to get the gesture settings for (\"mouse\", \"touch\", \"pen\", etc.).\n * @return {OpenSeadragon.GestureSettings}\n */gestureSettingsByDeviceType:function gestureSettingsByDeviceType(type){switch(type){case'mouse':return this.gestureSettingsMouse;case'touch':return this.gestureSettingsTouch;case'pen':return this.gestureSettingsPen;default:return this.gestureSettingsUnknown;}},// private\n_drawOverlays:function _drawOverlays(){var i,length=this.currentOverlays.length;for(i=0;i1){this.referenceStrip=new $.ReferenceStrip({id:this.referenceStripElement,position:this.referenceStripPosition,sizeRatio:this.referenceStripSizeRatio,scroll:this.referenceStripScroll,height:this.referenceStripHeight,width:this.referenceStripWidth,tileSources:this.tileSources,prefixUrl:this.prefixUrl,viewer:this});this.referenceStrip.setFocus(this._sequenceIndex);}}else{$.console.warn('Attempting to display a reference strip while \"sequenceMode\" is off.');}}});/**\n * _getSafeElemSize is like getElementSize(), but refuses to return 0 for x or y,\n * which was causing some calling operations to return NaN.\n * @returns {Point}\n * @private\n */function _getSafeElemSize(oElement){oElement=$.getElement(oElement);return new $.Point(oElement.clientWidth===0?1:oElement.clientWidth,oElement.clientHeight===0?1:oElement.clientHeight);}/**\n * @function\n * @private\n */function getTileSourceImplementation(viewer,tileSource,imgOptions,successCallback,failCallback){var _this=viewer;//allow plain xml strings or json strings to be parsed here\nif($.type(tileSource)=='string'){//xml should start with \"<\" and end with \">\"\nif(tileSource.match(/^\\s*<.*>\\s*$/)){tileSource=$.parseXml(tileSource);//json should start with \"{\" or \"[\" and end with \"}\" or \"]\"\n}else if(tileSource.match(/^\\s*[\\{\\[].*[\\}\\]]\\s*$/)){try{var tileSourceJ=$.parseJSON(tileSource);tileSource=tileSourceJ;}catch(e){//tileSource = tileSource;\n}}}function waitUntilReady(tileSource,originalTileSource){if(tileSource.ready){successCallback(tileSource);}else{tileSource.addHandler('ready',function(){successCallback(tileSource);});tileSource.addHandler('open-failed',function(event){failCallback({message:event.message,source:originalTileSource});});}}setTimeout(function(){if($.type(tileSource)=='string'){//If its still a string it means it must be a url at this point\ntileSource=new $.TileSource({url:tileSource,crossOriginPolicy:imgOptions.crossOriginPolicy!==undefined?imgOptions.crossOriginPolicy:viewer.crossOriginPolicy,ajaxWithCredentials:viewer.ajaxWithCredentials,ajaxHeaders:viewer.ajaxHeaders,useCanvas:viewer.useCanvas,success:function success(event){successCallback(event.tileSource);}});tileSource.addHandler('open-failed',function(event){failCallback(event);});}else if($.isPlainObject(tileSource)||tileSource.nodeType){if(tileSource.crossOriginPolicy===undefined&&(imgOptions.crossOriginPolicy!==undefined||viewer.crossOriginPolicy!==undefined)){tileSource.crossOriginPolicy=imgOptions.crossOriginPolicy!==undefined?imgOptions.crossOriginPolicy:viewer.crossOriginPolicy;}if(tileSource.ajaxWithCredentials===undefined){tileSource.ajaxWithCredentials=viewer.ajaxWithCredentials;}if(tileSource.useCanvas===undefined){tileSource.useCanvas=viewer.useCanvas;}if($.isFunction(tileSource.getTileUrl)){//Custom tile source\nvar customTileSource=new $.TileSource(tileSource);customTileSource.getTileUrl=tileSource.getTileUrl;successCallback(customTileSource);}else{//inline configuration\nvar $TileSource=$.TileSource.determineType(_this,tileSource);if(!$TileSource){failCallback({message:\"Unable to load TileSource\",source:tileSource});return;}var options=$TileSource.prototype.configure.apply(_this,[tileSource]);waitUntilReady(new $TileSource(options),tileSource);}}else{//can assume it's already a tile source implementation\nwaitUntilReady(tileSource,tileSource);}});}function getOverlayObject(viewer,overlay){if(overlay instanceof $.Overlay){return overlay;}var element=null;if(overlay.element){element=$.getElement(overlay.element);}else{var id=overlay.id?overlay.id:\"openseadragon-overlay-\"+Math.floor(Math.random()*10000000);element=$.getElement(overlay.id);if(!element){element=document.createElement(\"a\");element.href=\"#/overlay/\"+id;}element.id=id;$.addClass(element,overlay.className?overlay.className:\"openseadragon-overlay\");}var location=overlay.location;var width=overlay.width;var height=overlay.height;if(!location){var x=overlay.x;var y=overlay.y;if(overlay.px!==undefined){var rect=viewer.viewport.imageToViewportRectangle(new $.Rect(overlay.px,overlay.py,width||0,height||0));x=rect.x;y=rect.y;width=width!==undefined?rect.width:undefined;height=height!==undefined?rect.height:undefined;}location=new $.Point(x,y);}var placement=overlay.placement;if(placement&&$.type(placement)===\"string\"){placement=$.Placement[overlay.placement.toUpperCase()];}return new $.Overlay({element:element,location:location,placement:placement,onDraw:overlay.onDraw,checkResize:overlay.checkResize,width:width,height:height,rotationMode:overlay.rotationMode});}/**\n * @private\n * @inner\n * Determines the index of the given overlay in the given overlays array.\n */function getOverlayIndex(overlays,element){var i;for(i=overlays.length-1;i>=0;i--){if(overlays[i].element===element){return i;}}return-1;}///////////////////////////////////////////////////////////////////////////////\n// Schedulers provide the general engine for animation\n///////////////////////////////////////////////////////////////////////////////\nfunction scheduleUpdate(viewer,updateFunc){return $.requestAnimationFrame(function(){updateFunc(viewer);});}//provides a sequence in the fade animation\nfunction scheduleControlsFade(viewer){$.requestAnimationFrame(function(){updateControlsFade(viewer);});}//initiates an animation to hide the controls\nfunction beginControlsAutoHide(viewer){if(!viewer.autoHideControls){return;}viewer.controlsShouldFade=true;viewer.controlsFadeBeginTime=$.now()+viewer.controlsFadeDelay;window.setTimeout(function(){scheduleControlsFade(viewer);},viewer.controlsFadeDelay);}//determines if fade animation is done or continues the animation\nfunction updateControlsFade(viewer){var currentTime,deltaTime,opacity,i;if(viewer.controlsShouldFade){currentTime=$.now();deltaTime=currentTime-viewer.controlsFadeBeginTime;opacity=1.0-deltaTime/viewer.controlsFadeLength;opacity=Math.min(1.0,opacity);opacity=Math.max(0.0,opacity);for(i=viewer.controls.length-1;i>=0;i--){if(viewer.controls[i].autoFade){viewer.controls[i].setOpacity(opacity);}}if(opacity>0){// fade again\nscheduleControlsFade(viewer);}}}//stop the fade animation on the controls and show them\nfunction abortControlsAutoHide(viewer){var i;viewer.controlsShouldFade=false;for(i=viewer.controls.length-1;i>=0;i--){viewer.controls[i].setOpacity(1.0);}}///////////////////////////////////////////////////////////////////////////////\n// Default view event handlers.\n///////////////////////////////////////////////////////////////////////////////\nfunction onFocus(){abortControlsAutoHide(this);}function onBlur(){beginControlsAutoHide(this);}function onCanvasKeyDown(event){var canvasKeyDownEventArgs={originalEvent:event.originalEvent,preventDefaultAction:event.preventDefaultAction,preventVerticalPan:event.preventVerticalPan,preventHorizontalPan:event.preventHorizontalPan};/**\n * Raised when a keyboard key is pressed and the focus is on the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-key\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {Boolean} preventDefaultAction - Set to true to prevent default keyboard behaviour. Default: false.\n * @property {Boolean} preventVerticalPan - Set to true to prevent keyboard vertical panning. Default: false.\n * @property {Boolean} preventHorizontalPan - Set to true to prevent keyboard horizontal panning. Default: false.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-key',canvasKeyDownEventArgs);if(!canvasKeyDownEventArgs.preventDefaultAction&&!event.ctrl&&!event.alt&&!event.meta){switch(event.keyCode){case 38://up arrow\nif(!canvasKeyDownEventArgs.preventVerticalPan){if(event.shift){this.viewport.zoomBy(1.1);}else{this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(0,-this.pixelsPerArrowPress)));}this.viewport.applyConstraints();}return false;case 40://down arrow\nif(!canvasKeyDownEventArgs.preventVerticalPan){if(event.shift){this.viewport.zoomBy(0.9);}else{this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(0,this.pixelsPerArrowPress)));}this.viewport.applyConstraints();}return false;case 37://left arrow\nif(!canvasKeyDownEventArgs.preventHorizontalPan){this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(-this.pixelsPerArrowPress,0)));this.viewport.applyConstraints();}return false;case 39://right arrow\nif(!canvasKeyDownEventArgs.preventHorizontalPan){this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(this.pixelsPerArrowPress,0)));this.viewport.applyConstraints();}return false;default://console.log( 'navigator keycode %s', event.keyCode );\nreturn true;}}else{return true;}}function onCanvasKeyPress(event){var canvasKeyPressEventArgs={originalEvent:event.originalEvent,preventDefaultAction:event.preventDefaultAction,preventVerticalPan:event.preventVerticalPan,preventHorizontalPan:event.preventHorizontalPan};// This event is documented in onCanvasKeyDown\nthis.raiseEvent('canvas-key',canvasKeyPressEventArgs);if(!canvasKeyPressEventArgs.preventDefaultAction&&!event.ctrl&&!event.alt&&!event.meta){switch(event.keyCode){case 43://=|+\ncase 61://=|+\nthis.viewport.zoomBy(1.1);this.viewport.applyConstraints();return false;case 45://-|_\nthis.viewport.zoomBy(0.9);this.viewport.applyConstraints();return false;case 48://0|)\nthis.viewport.goHome();this.viewport.applyConstraints();return false;case 119://w\ncase 87://W\nif(!canvasKeyPressEventArgs.preventVerticalPan){if(event.shift){this.viewport.zoomBy(1.1);}else{this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(0,-40)));}this.viewport.applyConstraints();}return false;case 115://s\ncase 83://S\nif(!canvasKeyPressEventArgs.preventVerticalPan){if(event.shift){this.viewport.zoomBy(0.9);}else{this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(0,40)));}this.viewport.applyConstraints();}return false;case 97://a\nif(!canvasKeyPressEventArgs.preventHorizontalPan){this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(-40,0)));this.viewport.applyConstraints();}return false;case 100://d\nif(!canvasKeyPressEventArgs.preventHorizontalPan){this.viewport.panBy(this.viewport.deltaPointsFromPixels(new $.Point(40,0)));this.viewport.applyConstraints();}return false;case 114://r - 90 degrees clockwise rotation\nif(this.viewport.flipped){this.viewport.setRotation(this.viewport.degrees-90);}else{this.viewport.setRotation(this.viewport.degrees+90);}this.viewport.applyConstraints();return false;case 82://R - 90 degrees counterclockwise rotation\nif(this.viewport.flipped){this.viewport.setRotation(this.viewport.degrees+90);}else{this.viewport.setRotation(this.viewport.degrees-90);}this.viewport.applyConstraints();return false;case 102://f\nthis.viewport.toggleFlip();return false;default:// console.log( 'navigator keycode %s', event.keyCode );\nreturn true;}}else{return true;}}function onCanvasClick(event){var gestureSettings;var haveKeyboardFocus=document.activeElement==this.canvas;// If we don't have keyboard focus, request it.\nif(!haveKeyboardFocus){this.canvas.focus();}if(this.viewport.flipped){event.position.x=this.viewport.getContainerSize().x-event.position.x;}var canvasClickEventArgs={tracker:event.eventSource,position:event.position,quick:event.quick,shift:event.shift,originalEvent:event.originalEvent,preventDefaultAction:event.preventDefaultAction};/**\n * Raised when a mouse press/release or touch/remove occurs on the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-click\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {Boolean} quick - True only if the clickDistThreshold and clickTimeThreshold are both passed. Useful for differentiating between clicks and drags.\n * @property {Boolean} shift - True if the shift key was pressed during this event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {Boolean} preventDefaultAction - Set to true to prevent default click to zoom behaviour. Default: false.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-click',canvasClickEventArgs);if(!canvasClickEventArgs.preventDefaultAction&&this.viewport&&event.quick){gestureSettings=this.gestureSettingsByDeviceType(event.pointerType);if(gestureSettings.clickToZoom){this.viewport.zoomBy(event.shift?1.0/this.zoomPerClick:this.zoomPerClick,gestureSettings.zoomToRefPoint?this.viewport.pointFromPixel(event.position,true):null);this.viewport.applyConstraints();}}}function onCanvasDblClick(event){var gestureSettings;var canvasDblClickEventArgs={tracker:event.eventSource,position:event.position,shift:event.shift,originalEvent:event.originalEvent,preventDefaultAction:event.preventDefaultAction};/**\n * Raised when a double mouse press/release or touch/remove occurs on the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-double-click\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {Boolean} shift - True if the shift key was pressed during this event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {Boolean} preventDefaultAction - Set to true to prevent default double tap to zoom behaviour. Default: false.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-double-click',canvasDblClickEventArgs);if(!canvasDblClickEventArgs.preventDefaultAction&&this.viewport){gestureSettings=this.gestureSettingsByDeviceType(event.pointerType);if(gestureSettings.dblClickToZoom){this.viewport.zoomBy(event.shift?1.0/this.zoomPerClick:this.zoomPerClick,gestureSettings.zoomToRefPoint?this.viewport.pointFromPixel(event.position,true):null);this.viewport.applyConstraints();}}}function onCanvasDrag(event){var gestureSettings;var canvasDragEventArgs={tracker:event.eventSource,position:event.position,delta:event.delta,speed:event.speed,direction:event.direction,shift:event.shift,originalEvent:event.originalEvent,preventDefaultAction:event.preventDefaultAction};/**\n * Raised when a mouse or touch drag operation occurs on the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-drag\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {OpenSeadragon.Point} delta - The x,y components of the difference between start drag and end drag.\n * @property {Number} speed - Current computed speed, in pixels per second.\n * @property {Number} direction - Current computed direction, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.\n * @property {Boolean} shift - True if the shift key was pressed during this event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {Boolean} preventDefaultAction - Set to true to prevent default drag behaviour. Default: false.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-drag',canvasDragEventArgs);if(!canvasDragEventArgs.preventDefaultAction&&this.viewport){gestureSettings=this.gestureSettingsByDeviceType(event.pointerType);if(!this.panHorizontal){event.delta.x=0;}if(!this.panVertical){event.delta.y=0;}if(this.viewport.flipped){event.delta.x=-event.delta.x;}if(this.constrainDuringPan){var delta=this.viewport.deltaPointsFromPixels(event.delta.negate());this.viewport.centerSpringX.target.value+=delta.x;this.viewport.centerSpringY.target.value+=delta.y;var bounds=this.viewport.getBounds();var constrainedBounds=this.viewport.getConstrainedBounds();this.viewport.centerSpringX.target.value-=delta.x;this.viewport.centerSpringY.target.value-=delta.y;if(bounds.x!=constrainedBounds.x){event.delta.x=0;}if(bounds.y!=constrainedBounds.y){event.delta.y=0;}}this.viewport.panBy(this.viewport.deltaPointsFromPixels(event.delta.negate()),gestureSettings.flickEnabled&&!this.constrainDuringPan);}}function onCanvasDragEnd(event){if(!event.preventDefaultAction&&this.viewport){var gestureSettings=this.gestureSettingsByDeviceType(event.pointerType);if(gestureSettings.flickEnabled&&event.speed>=gestureSettings.flickMinSpeed){var amplitudeX=0;if(this.panHorizontal){amplitudeX=gestureSettings.flickMomentum*event.speed*Math.cos(event.direction);}var amplitudeY=0;if(this.panVertical){amplitudeY=gestureSettings.flickMomentum*event.speed*Math.sin(event.direction);}var center=this.viewport.pixelFromPoint(this.viewport.getCenter(true));var target=this.viewport.pointFromPixel(new $.Point(center.x-amplitudeX,center.y-amplitudeY));this.viewport.panTo(target,false);}this.viewport.applyConstraints();}/**\n * Raised when a mouse or touch drag operation ends on the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-drag-end\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {Number} speed - Speed at the end of a drag gesture, in pixels per second.\n * @property {Number} direction - Direction at the end of a drag gesture, expressed as an angle counterclockwise relative to the positive X axis (-pi to pi, in radians). Only valid if speed > 0.\n * @property {Boolean} shift - True if the shift key was pressed during this event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-drag-end',{tracker:event.eventSource,position:event.position,speed:event.speed,direction:event.direction,shift:event.shift,originalEvent:event.originalEvent});}function onCanvasEnter(event){/**\n * Raised when a pointer enters the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-enter\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {String} pointerType - \"mouse\", \"touch\", \"pen\", etc.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {Number} buttons - Current buttons pressed. A combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @property {Number} pointers - Number of pointers (all types) active in the tracked element.\n * @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.\n * @property {Boolean} buttonDownAny - Was the button down anywhere in the screen during the event. Deprecated. Use buttons instead.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-enter',{tracker:event.eventSource,pointerType:event.pointerType,position:event.position,buttons:event.buttons,pointers:event.pointers,insideElementPressed:event.insideElementPressed,buttonDownAny:event.buttonDownAny,originalEvent:event.originalEvent});}function onCanvasExit(event){if(window.location!=window.parent.location){$.MouseTracker.resetAllMouseTrackers();}/**\n * Raised when a pointer leaves the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-exit\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {String} pointerType - \"mouse\", \"touch\", \"pen\", etc.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {Number} buttons - Current buttons pressed. A combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @property {Number} pointers - Number of pointers (all types) active in the tracked element.\n * @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.\n * @property {Boolean} buttonDownAny - Was the button down anywhere in the screen during the event. Deprecated. Use buttons instead.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-exit',{tracker:event.eventSource,pointerType:event.pointerType,position:event.position,buttons:event.buttons,pointers:event.pointers,insideElementPressed:event.insideElementPressed,buttonDownAny:event.buttonDownAny,originalEvent:event.originalEvent});}function onCanvasPress(event){/**\n * Raised when the primary mouse button is pressed or touch starts on the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-press\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {String} pointerType - \"mouse\", \"touch\", \"pen\", etc.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.\n * @property {Boolean} insideElementReleased - True if the cursor still inside the tracked element when the button was released.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-press',{tracker:event.eventSource,pointerType:event.pointerType,position:event.position,insideElementPressed:event.insideElementPressed,insideElementReleased:event.insideElementReleased,originalEvent:event.originalEvent});}function onCanvasRelease(event){/**\n * Raised when the primary mouse button is released or touch ends on the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-release\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {String} pointerType - \"mouse\", \"touch\", \"pen\", etc.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.\n * @property {Boolean} insideElementReleased - True if the cursor still inside the tracked element when the button was released.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-release',{tracker:event.eventSource,pointerType:event.pointerType,position:event.position,insideElementPressed:event.insideElementPressed,insideElementReleased:event.insideElementReleased,originalEvent:event.originalEvent});}function onCanvasNonPrimaryPress(event){/**\n * Raised when any non-primary pointer button is pressed on the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-nonprimary-press\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {String} pointerType - \"mouse\", \"touch\", \"pen\", etc.\n * @property {Number} button - Button which caused the event.\n * -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.\n * @property {Number} buttons - Current buttons pressed.\n * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-nonprimary-press',{tracker:event.eventSource,position:event.position,pointerType:event.pointerType,button:event.button,buttons:event.buttons,originalEvent:event.originalEvent});}function onCanvasNonPrimaryRelease(event){/**\n * Raised when any non-primary pointer button is released on the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-nonprimary-release\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {String} pointerType - \"mouse\", \"touch\", \"pen\", etc.\n * @property {Number} button - Button which caused the event.\n * -1: none, 0: primary/left, 1: aux/middle, 2: secondary/right, 3: X1/back, 4: X2/forward, 5: pen eraser.\n * @property {Number} buttons - Current buttons pressed.\n * Combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-nonprimary-release',{tracker:event.eventSource,position:event.position,pointerType:event.pointerType,button:event.button,buttons:event.buttons,originalEvent:event.originalEvent});}function onCanvasPinch(event){var gestureSettings,centerPt,lastCenterPt,panByPt;if(!event.preventDefaultAction&&this.viewport){gestureSettings=this.gestureSettingsByDeviceType(event.pointerType);if(gestureSettings.pinchToZoom){centerPt=this.viewport.pointFromPixel(event.center,true);lastCenterPt=this.viewport.pointFromPixel(event.lastCenter,true);panByPt=lastCenterPt.minus(centerPt);if(!this.panHorizontal){panByPt.x=0;}if(!this.panVertical){panByPt.y=0;}this.viewport.zoomBy(event.distance/event.lastDistance,centerPt,true);if(gestureSettings.zoomToRefPoint){this.viewport.panBy(panByPt,true);}this.viewport.applyConstraints();}if(gestureSettings.pinchRotate){// Pinch rotate\nvar angle1=Math.atan2(event.gesturePoints[0].currentPos.y-event.gesturePoints[1].currentPos.y,event.gesturePoints[0].currentPos.x-event.gesturePoints[1].currentPos.x);var angle2=Math.atan2(event.gesturePoints[0].lastPos.y-event.gesturePoints[1].lastPos.y,event.gesturePoints[0].lastPos.x-event.gesturePoints[1].lastPos.x);this.viewport.setRotation(this.viewport.getRotation()+(angle1-angle2)*(180/Math.PI));}}/**\n * Raised when a pinch event occurs on the {@link OpenSeadragon.Viewer#canvas} element.\n *\n * @event canvas-pinch\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {Array.} gesturePoints - Gesture points associated with the gesture. Velocity data can be found here.\n * @property {OpenSeadragon.Point} lastCenter - The previous center point of the two pinch contact points relative to the tracked element.\n * @property {OpenSeadragon.Point} center - The center point of the two pinch contact points relative to the tracked element.\n * @property {Number} lastDistance - The previous distance between the two pinch contact points in CSS pixels.\n * @property {Number} distance - The distance between the two pinch contact points in CSS pixels.\n * @property {Boolean} shift - True if the shift key was pressed during this event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-pinch',{tracker:event.eventSource,gesturePoints:event.gesturePoints,lastCenter:event.lastCenter,center:event.center,lastDistance:event.lastDistance,distance:event.distance,shift:event.shift,originalEvent:event.originalEvent});//cancels event\nreturn false;}function onCanvasScroll(event){var gestureSettings,factor,thisScrollTime,deltaScrollTime;/* Certain scroll devices fire the scroll event way too fast so we are injecting a simple adjustment to keep things\n * partially normalized. If we have already fired an event within the last 'minScrollDelta' milliseconds we skip\n * this one and wait for the next event. */thisScrollTime=$.now();deltaScrollTime=thisScrollTime-this._lastScrollTime;if(deltaScrollTime>this.minScrollDeltaTime){this._lastScrollTime=thisScrollTime;if(this.viewport.flipped){event.position.x=this.viewport.getContainerSize().x-event.position.x;}if(!event.preventDefaultAction&&this.viewport){gestureSettings=this.gestureSettingsByDeviceType(event.pointerType);if(gestureSettings.scrollToZoom){factor=Math.pow(this.zoomPerScroll,event.scroll);this.viewport.zoomBy(factor,gestureSettings.zoomToRefPoint?this.viewport.pointFromPixel(event.position,true):null);this.viewport.applyConstraints();}}/**\n * Raised when a scroll event occurs on the {@link OpenSeadragon.Viewer#canvas} element (mouse wheel).\n *\n * @event canvas-scroll\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {Number} scroll - The scroll delta for the event.\n * @property {Boolean} shift - True if the shift key was pressed during this event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('canvas-scroll',{tracker:event.eventSource,position:event.position,scroll:event.scroll,shift:event.shift,originalEvent:event.originalEvent});if(gestureSettings&&gestureSettings.scrollToZoom){//cancels event\nreturn false;}}else{gestureSettings=this.gestureSettingsByDeviceType(event.pointerType);if(gestureSettings&&gestureSettings.scrollToZoom){return false;// We are swallowing this event\n}}}function onContainerEnter(event){THIS[this.hash].mouseInside=true;abortControlsAutoHide(this);/**\n * Raised when the cursor enters the {@link OpenSeadragon.Viewer#container} element.\n *\n * @event container-enter\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {Number} buttons - Current buttons pressed. A combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @property {Number} pointers - Number of pointers (all types) active in the tracked element.\n * @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.\n * @property {Boolean} buttonDownAny - Was the button down anywhere in the screen during the event. Deprecated. Use buttons instead.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('container-enter',{tracker:event.eventSource,position:event.position,buttons:event.buttons,pointers:event.pointers,insideElementPressed:event.insideElementPressed,buttonDownAny:event.buttonDownAny,originalEvent:event.originalEvent});}function onContainerExit(event){if(event.pointers<1){THIS[this.hash].mouseInside=false;if(!THIS[this.hash].animating){beginControlsAutoHide(this);}}/**\n * Raised when the cursor leaves the {@link OpenSeadragon.Viewer#container} element.\n *\n * @event container-exit\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.MouseTracker} tracker - A reference to the MouseTracker which originated this event.\n * @property {OpenSeadragon.Point} position - The position of the event relative to the tracked element.\n * @property {Number} buttons - Current buttons pressed. A combination of bit flags 0: none, 1: primary (or touch contact), 2: secondary, 4: aux (often middle), 8: X1 (often back), 16: X2 (often forward), 32: pen eraser.\n * @property {Number} pointers - Number of pointers (all types) active in the tracked element.\n * @property {Boolean} insideElementPressed - True if the left mouse button is currently being pressed and was initiated inside the tracked element, otherwise false.\n * @property {Boolean} buttonDownAny - Was the button down anywhere in the screen during the event. Deprecated. Use buttons instead.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('container-exit',{tracker:event.eventSource,position:event.position,buttons:event.buttons,pointers:event.pointers,insideElementPressed:event.insideElementPressed,buttonDownAny:event.buttonDownAny,originalEvent:event.originalEvent});}///////////////////////////////////////////////////////////////////////////////\n// Page update routines ( aka Views - for future reference )\n///////////////////////////////////////////////////////////////////////////////\nfunction updateMulti(viewer){updateOnce(viewer);// Request the next frame, unless we've been closed\nif(viewer.isOpen()){viewer._updateRequestId=scheduleUpdate(viewer,updateMulti);}else{viewer._updateRequestId=false;}}function updateOnce(viewer){//viewer.profiler.beginUpdate();\nif(viewer._opening){return;}if(viewer.autoResize){var containerSize=_getSafeElemSize(viewer.container);var prevContainerSize=THIS[viewer.hash].prevContainerSize;if(!containerSize.equals(prevContainerSize)){var viewport=viewer.viewport;if(viewer.preserveImageSizeOnResize){var resizeRatio=prevContainerSize.x/containerSize.x;var zoom=viewport.getZoom()*resizeRatio;var center=viewport.getCenter();viewport.resize(containerSize,false);viewport.zoomTo(zoom,null,true);viewport.panTo(center,true);}else{// maintain image position\nvar oldBounds=viewport.getBounds();viewport.resize(containerSize,true);viewport.fitBoundsWithConstraints(oldBounds,true);}THIS[viewer.hash].prevContainerSize=containerSize;THIS[viewer.hash].forceRedraw=true;}}var viewportChange=viewer.viewport.update();var animated=viewer.world.update()||viewportChange;if(viewportChange){/**\n * Raised when any spring animation update occurs (zoom, pan, etc.),\n * before the viewer has drawn the new location.\n *\n * @event viewport-change\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */viewer.raiseEvent('viewport-change');}if(viewer.referenceStrip){animated=viewer.referenceStrip.update(viewer.viewport)||animated;}if(!THIS[viewer.hash].animating&&animated){/**\n * Raised when any spring animation starts (zoom, pan, etc.).\n *\n * @event animation-start\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */viewer.raiseEvent(\"animation-start\");abortControlsAutoHide(viewer);}if(animated||THIS[viewer.hash].forceRedraw||viewer.world.needsDraw()){drawWorld(viewer);viewer._drawOverlays();if(viewer.navigator){viewer.navigator.update(viewer.viewport);}THIS[viewer.hash].forceRedraw=false;if(animated){/**\n * Raised when any spring animation update occurs (zoom, pan, etc.),\n * after the viewer has drawn the new location.\n *\n * @event animation\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */viewer.raiseEvent(\"animation\");}}if(THIS[viewer.hash].animating&&!animated){/**\n * Raised when any spring animation ends (zoom, pan, etc.).\n *\n * @event animation-finish\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */viewer.raiseEvent(\"animation-finish\");if(!THIS[viewer.hash].mouseInside){beginControlsAutoHide(viewer);}}THIS[viewer.hash].animating=animated;//viewer.profiler.endUpdate();\n}function drawWorld(viewer){viewer.imageLoader.clear();viewer.drawer.clear();viewer.world.draw();/**\n * - Needs documentation -\n *\n * @event update-viewport\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */viewer.raiseEvent('update-viewport',{});}///////////////////////////////////////////////////////////////////////////////\n// Navigation Controls\n///////////////////////////////////////////////////////////////////////////////\nfunction resolveUrl(prefix,url){return prefix?prefix+url:url;}function beginZoomingIn(){THIS[this.hash].lastZoomTime=$.now();THIS[this.hash].zoomFactor=this.zoomPerSecond;THIS[this.hash].zooming=true;scheduleZoom(this);}function beginZoomingOut(){THIS[this.hash].lastZoomTime=$.now();THIS[this.hash].zoomFactor=1.0/this.zoomPerSecond;THIS[this.hash].zooming=true;scheduleZoom(this);}function endZooming(){THIS[this.hash].zooming=false;}function scheduleZoom(viewer){$.requestAnimationFrame($.delegate(viewer,doZoom));}function doZoom(){var currentTime,deltaTime,adjustedFactor;if(THIS[this.hash].zooming&&this.viewport){currentTime=$.now();deltaTime=currentTime-THIS[this.hash].lastZoomTime;adjustedFactor=Math.pow(THIS[this.hash].zoomFactor,deltaTime/1000);this.viewport.zoomBy(adjustedFactor);this.viewport.applyConstraints();THIS[this.hash].lastZoomTime=currentTime;scheduleZoom(this);}}function doSingleZoomIn(){if(this.viewport){THIS[this.hash].zooming=false;this.viewport.zoomBy(this.zoomPerClick/1.0);this.viewport.applyConstraints();}}function doSingleZoomOut(){if(this.viewport){THIS[this.hash].zooming=false;this.viewport.zoomBy(1.0/this.zoomPerClick);this.viewport.applyConstraints();}}function lightUp(){this.buttons.emulateEnter();this.buttons.emulateExit();}function onHome(){if(this.viewport){this.viewport.goHome();}}function onFullScreen(){if(this.isFullPage()&&!$.isFullScreen()){// Is fullPage but not fullScreen\nthis.setFullPage(false);}else{this.setFullScreen(!this.isFullPage());}// correct for no mouseout event on change\nif(this.buttons){this.buttons.emulateExit();}this.fullPageButton.element.focus();if(this.viewport){this.viewport.applyConstraints();}}/**\n * Note: The current rotation feature is limited to 90 degree turns.\n */function onRotateLeft(){if(this.viewport){var currRotation=this.viewport.getRotation();if(this.viewport.flipped){currRotation=$.positiveModulo(currRotation+90,360);}else{currRotation=$.positiveModulo(currRotation-90,360);}this.viewport.setRotation(currRotation);}}/**\n * Note: The current rotation feature is limited to 90 degree turns.\n */function onRotateRight(){if(this.viewport){var currRotation=this.viewport.getRotation();if(this.viewport.flipped){currRotation=$.positiveModulo(currRotation-90,360);}else{currRotation=$.positiveModulo(currRotation+90,360);}this.viewport.setRotation(currRotation);}}/**\n * Note: When pressed flip control button\n */function onFlip(){this.viewport.toggleFlip();}function onPrevious(){var previous=this._sequenceIndex-1;if(this.navPrevNextWrap&&previous<0){previous+=this.tileSources.length;}this.goToPage(previous);}function onNext(){var next=this._sequenceIndex+1;if(this.navPrevNextWrap&&next>=this.tileSources.length){next=0;}this.goToPage(next);}})(OpenSeadragon);/*\n * OpenSeadragon - Navigator\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * @class Navigator\n * @classdesc The Navigator provides a small view of the current image as fixed\n * while representing the viewport as a moving box serving as a frame\n * of reference in the larger viewport as to which portion of the image\n * is currently being examined. The navigator's viewport can be interacted\n * with using the keyboard or the mouse.\n *\n * @memberof OpenSeadragon\n * @extends OpenSeadragon.Viewer\n * @extends OpenSeadragon.EventSource\n * @param {Object} options\n */$.Navigator=function(options){var viewer=options.viewer,_this=this,viewerSize,navigatorSize;//We may need to create a new element and id if they did not\n//provide the id for the existing element\nif(!options.id){options.id='navigator-'+$.now();this.element=$.makeNeutralElement(\"div\");options.controlOptions={anchor:$.ControlAnchor.TOP_RIGHT,attachToViewer:true,autoFade:options.autoFade};if(options.position){if('BOTTOM_RIGHT'==options.position){options.controlOptions.anchor=$.ControlAnchor.BOTTOM_RIGHT;}else if('BOTTOM_LEFT'==options.position){options.controlOptions.anchor=$.ControlAnchor.BOTTOM_LEFT;}else if('TOP_RIGHT'==options.position){options.controlOptions.anchor=$.ControlAnchor.TOP_RIGHT;}else if('TOP_LEFT'==options.position){options.controlOptions.anchor=$.ControlAnchor.TOP_LEFT;}else if('ABSOLUTE'==options.position){options.controlOptions.anchor=$.ControlAnchor.ABSOLUTE;options.controlOptions.top=options.top;options.controlOptions.left=options.left;options.controlOptions.height=options.height;options.controlOptions.width=options.width;}}}else{this.element=document.getElementById(options.id);options.controlOptions={anchor:$.ControlAnchor.NONE,attachToViewer:false,autoFade:false};}this.element.id=options.id;this.element.className+=' navigator';options=$.extend(true,{sizeRatio:$.DEFAULT_SETTINGS.navigatorSizeRatio},options,{element:this.element,tabIndex:-1,// No keyboard navigation, omit from tab order\n//These need to be overridden to prevent recursion since\n//the navigator is a viewer and a viewer has a navigator\nshowNavigator:false,mouseNavEnabled:false,showNavigationControl:false,showSequenceControl:false,immediateRender:true,blendTime:0,animationTime:0,autoResize:options.autoResize,// prevent resizing the navigator from adding unwanted space around the image\nminZoomImageRatio:1.0});options.minPixelRatio=this.minPixelRatio=viewer.minPixelRatio;$.setElementTouchActionNone(this.element);this.borderWidth=2;//At some browser magnification levels the display regions lines up correctly, but at some there appears to\n//be a one pixel gap.\nthis.fudge=new $.Point(1,1);this.totalBorderWidths=new $.Point(this.borderWidth*2,this.borderWidth*2).minus(this.fudge);if(options.controlOptions.anchor!=$.ControlAnchor.NONE){(function(style,borderWidth){style.margin='0px';style.border=borderWidth+'px solid #555';style.padding='0px';style.background='#000';style.opacity=0.8;style.overflow='hidden';})(this.element.style,this.borderWidth);}this.displayRegion=$.makeNeutralElement(\"div\");this.displayRegion.id=this.element.id+'-displayregion';this.displayRegion.className='displayregion';(function(style,borderWidth){style.position='relative';style.top='0px';style.left='0px';style.fontSize='0px';style.overflow='hidden';style.border=borderWidth+'px solid #900';style.margin='0px';style.padding='0px';//TODO: IE doesnt like this property being set\n//try{ style.outline = '2px auto #909'; }catch(e){/*ignore*/}\nstyle.background='transparent';// We use square bracket notation on the statement below, because float is a keyword.\n// This is important for the Google Closure compiler, if nothing else.\n/*jshint sub:true */style['float']='left';//Webkit\nstyle.cssFloat='left';//Firefox\nstyle.styleFloat='left';//IE\nstyle.zIndex=999999999;style.cursor='default';})(this.displayRegion.style,this.borderWidth);this.displayRegionContainer=$.makeNeutralElement(\"div\");this.displayRegionContainer.id=this.element.id+'-displayregioncontainer';this.displayRegionContainer.className=\"displayregioncontainer\";this.displayRegionContainer.style.width=\"100%\";this.displayRegionContainer.style.height=\"100%\";viewer.addControl(this.element,options.controlOptions);this._resizeWithViewer=options.controlOptions.anchor!=$.ControlAnchor.ABSOLUTE&&options.controlOptions.anchor!=$.ControlAnchor.NONE;if(this._resizeWithViewer){if(options.width&&options.height){this.element.style.height=typeof options.height==\"number\"?options.height+'px':options.height;this.element.style.width=typeof options.width==\"number\"?options.width+'px':options.width;}else{viewerSize=$.getElementSize(viewer.element);this.element.style.height=Math.round(viewerSize.y*options.sizeRatio)+'px';this.element.style.width=Math.round(viewerSize.x*options.sizeRatio)+'px';this.oldViewerSize=viewerSize;}navigatorSize=$.getElementSize(this.element);this.elementArea=navigatorSize.x*navigatorSize.y;}this.oldContainerSize=new $.Point(0,0);$.Viewer.apply(this,[options]);this.displayRegionContainer.appendChild(this.displayRegion);this.element.getElementsByTagName('div')[0].appendChild(this.displayRegionContainer);function rotate(degrees){_setTransformRotate(_this.displayRegionContainer,degrees);_setTransformRotate(_this.displayRegion,-degrees);_this.viewport.setRotation(degrees);}if(options.navigatorRotate){var degrees=options.viewer.viewport?options.viewer.viewport.getRotation():options.viewer.degrees||0;rotate(degrees);options.viewer.addHandler(\"rotate\",function(args){rotate(args.degrees);});}// Remove the base class' (Viewer's) innerTracker and replace it with our own\nthis.innerTracker.destroy();this.innerTracker=new $.MouseTracker({element:this.element,dragHandler:$.delegate(this,onCanvasDrag),clickHandler:$.delegate(this,onCanvasClick),releaseHandler:$.delegate(this,onCanvasRelease),scrollHandler:$.delegate(this,onCanvasScroll)});this.addHandler(\"reset-size\",function(){if(_this.viewport){_this.viewport.goHome(true);}});viewer.world.addHandler(\"item-index-change\",function(event){window.setTimeout(function(){var item=_this.world.getItemAt(event.previousIndex);_this.world.setItemIndex(item,event.newIndex);},1);});viewer.world.addHandler(\"remove-item\",function(event){var theirItem=event.item;var myItem=_this._getMatchingItem(theirItem);if(myItem){_this.world.removeItem(myItem);}});this.update(viewer.viewport);};$.extend($.Navigator.prototype,$.EventSource.prototype,$.Viewer.prototype,/** @lends OpenSeadragon.Navigator.prototype */{/**\n * Used to notify the navigator when its size has changed.\n * Especially useful when {@link OpenSeadragon.Options}.navigatorAutoResize is set to false and the navigator is resizable.\n * @function\n */updateSize:function updateSize(){if(this.viewport){var containerSize=new $.Point(this.container.clientWidth===0?1:this.container.clientWidth,this.container.clientHeight===0?1:this.container.clientHeight);if(!containerSize.equals(this.oldContainerSize)){this.viewport.resize(containerSize,true);this.viewport.goHome(true);this.oldContainerSize=containerSize;this.drawer.clear();this.world.draw();}}},/**\n /* Flip navigator element\n * @param {Boolean} state - Flip state to set.\n */setFlip:function setFlip(state){this.viewport.setFlip(state);this.setDisplayTransform(this.viewer.viewport.getFlip()?\"scale(-1,1)\":\"scale(1,1)\");return this;},setDisplayTransform:function setDisplayTransform(rule){setElementTransform(this.displayRegion,rule);setElementTransform(this.canvas,rule);setElementTransform(this.element,rule);},/**\n * Used to update the navigator minimap's viewport rectangle when a change in the viewer's viewport occurs.\n * @function\n * @param {OpenSeadragon.Viewport} The viewport this navigator is tracking.\n */update:function update(viewport){var viewerSize,newWidth,newHeight,bounds,topleft,bottomright;viewerSize=$.getElementSize(this.viewer.element);if(this._resizeWithViewer&&viewerSize.x&&viewerSize.y&&!viewerSize.equals(this.oldViewerSize)){this.oldViewerSize=viewerSize;if(this.maintainSizeRatio||!this.elementArea){newWidth=viewerSize.x*this.sizeRatio;newHeight=viewerSize.y*this.sizeRatio;}else{newWidth=Math.sqrt(this.elementArea*(viewerSize.x/viewerSize.y));newHeight=this.elementArea/newWidth;}this.element.style.width=Math.round(newWidth)+'px';this.element.style.height=Math.round(newHeight)+'px';if(!this.elementArea){this.elementArea=newWidth*newHeight;}this.updateSize();}if(viewport&&this.viewport){bounds=viewport.getBoundsNoRotate(true);topleft=this.viewport.pixelFromPointNoRotate(bounds.getTopLeft(),false);bottomright=this.viewport.pixelFromPointNoRotate(bounds.getBottomRight(),false).minus(this.totalBorderWidths);//update style for navigator-box\nvar style=this.displayRegion.style;style.display=this.world.getItemCount()?'block':'none';style.top=Math.round(topleft.y)+'px';style.left=Math.round(topleft.x)+'px';var width=Math.abs(topleft.x-bottomright.x);var height=Math.abs(topleft.y-bottomright.y);// make sure width and height are non-negative so IE doesn't throw\nstyle.width=Math.round(Math.max(width,0))+'px';style.height=Math.round(Math.max(height,0))+'px';}},// overrides Viewer.addTiledImage\naddTiledImage:function addTiledImage(options){var _this=this;var original=options.originalTiledImage;delete options.original;var optionsClone=$.extend({},options,{success:function success(event){var myItem=event.item;myItem._originalForNavigator=original;_this._matchBounds(myItem,original,true);function matchBounds(){_this._matchBounds(myItem,original);}function matchOpacity(){_this._matchOpacity(myItem,original);}function matchCompositeOperation(){_this._matchCompositeOperation(myItem,original);}original.addHandler('bounds-change',matchBounds);original.addHandler('clip-change',matchBounds);original.addHandler('opacity-change',matchOpacity);original.addHandler('composite-operation-change',matchCompositeOperation);}});return $.Viewer.prototype.addTiledImage.apply(this,[optionsClone]);},// private\n_getMatchingItem:function _getMatchingItem(theirItem){var count=this.world.getItemCount();var item;for(var i=0;i\n * By default the image pyramid is split into N layers where the image's longest\n * side in M (in pixels), where N is the smallest integer which satisfies\n * 2^(N+1) >= M.\n *\n * @memberof OpenSeadragon\n * @extends OpenSeadragon.EventSource\n * @param {Object} options\n * You can either specify a URL, or literally define the TileSource (by specifying\n * width, height, tileSize, tileOverlap, minLevel, and maxLevel). For the former,\n * the extending class is expected to implement 'getImageInfo' and 'configure'.\n * For the latter, the construction is assumed to occur through\n * the extending classes implementation of 'configure'.\n * @param {String} [options.url]\n * The URL for the data necessary for this TileSource.\n * @param {String} [options.referenceStripThumbnailUrl]\n * The URL for a thumbnail image to be used by the reference strip\n * @param {Function} [options.success]\n * A function to be called upon successful creation.\n * @param {Boolean} [options.ajaxWithCredentials]\n * If this TileSource needs to make an AJAX call, this specifies whether to set\n * the XHR's withCredentials (for accessing secure data).\n * @param {Object} [options.ajaxHeaders]\n * A set of headers to include in AJAX requests.\n * @param {Number} [options.width]\n * Width of the source image at max resolution in pixels.\n * @param {Number} [options.height]\n * Height of the source image at max resolution in pixels.\n * @param {Number} [options.tileSize]\n * The size of the tiles to assumed to make up each pyramid layer in pixels.\n * Tile size determines the point at which the image pyramid must be\n * divided into a matrix of smaller images.\n * Use options.tileWidth and options.tileHeight to support non-square tiles.\n * @param {Number} [options.tileWidth]\n * The width of the tiles to assumed to make up each pyramid layer in pixels.\n * @param {Number} [options.tileHeight]\n * The height of the tiles to assumed to make up each pyramid layer in pixels.\n * @param {Number} [options.tileOverlap]\n * The number of pixels each tile is expected to overlap touching tiles.\n * @param {Number} [options.minLevel]\n * The minimum level to attempt to load.\n * @param {Number} [options.maxLevel]\n * The maximum level to attempt to load.\n */$.TileSource=function(width,height,tileSize,tileOverlap,minLevel,maxLevel){var _this=this;var args=arguments,options,i;if($.isPlainObject(width)){options=width;}else{options={width:args[0],height:args[1],tileSize:args[2],tileOverlap:args[3],minLevel:args[4],maxLevel:args[5]};}//Tile sources supply some events, namely 'ready' when they must be configured\n//by asynchronously fetching their configuration data.\n$.EventSource.call(this);//we allow options to override anything we dont treat as\n//required via idiomatic options or which is functionally\n//set depending on the state of the readiness of this tile\n//source\n$.extend(true,this,options);if(!this.success){//Any functions that are passed as arguments are bound to the ready callback\nfor(i=0;i1||tiles.y>1){break;}}return i-1;},/**\n * @function\n * @param {Number} level\n * @param {OpenSeadragon.Point} point\n */getTileAtPoint:function getTileAtPoint(level,point){var validPoint=point.x>=0&&point.x<=1&&point.y>=0&&point.y<=1/this.aspectRatio;$.console.assert(validPoint,\"[TileSource.getTileAtPoint] must be called with a valid point.\");var widthScaled=this.dimensions.x*this.getLevelScale(level);var pixelX=point.x*widthScaled;var pixelY=point.y*widthScaled;var x=Math.floor(pixelX/this.getTileWidth(level));var y=Math.floor(pixelY/this.getTileHeight(level));// When point.x == 1 or point.y == 1 / this.aspectRatio we want to\n// return the last tile of the row/column\nif(point.x>=1){x=this.getNumTiles(level).x-1;}var EPSILON=1e-15;if(point.y>=1/this.aspectRatio-EPSILON){y=this.getNumTiles(level).y-1;}return new $.Point(x,y);},/**\n * @function\n * @param {Number} level\n * @param {Number} x\n * @param {Number} y\n * @param {Boolean} [isSource=false] Whether to return the source bounds of the tile.\n * @returns {OpenSeadragon.Rect} Either where this tile fits (in normalized coordinates) or the\n * portion of the tile to use as the source of the drawing operation (in pixels), depending on\n * the isSource parameter.\n */getTileBounds:function getTileBounds(level,x,y,isSource){var dimensionsScaled=this.dimensions.times(this.getLevelScale(level)),tileWidth=this.getTileWidth(level),tileHeight=this.getTileHeight(level),px=x===0?0:tileWidth*x-this.tileOverlap,py=y===0?0:tileHeight*y-this.tileOverlap,sx=tileWidth+(x===0?1:2)*this.tileOverlap,sy=tileHeight+(y===0?1:2)*this.tileOverlap,scale=1.0/dimensionsScaled.x;sx=Math.min(sx,dimensionsScaled.x-px);sy=Math.min(sy,dimensionsScaled.y-py);if(isSource){return new $.Rect(0,0,sx,sy);}return new $.Rect(px*scale,py*scale,sx*scale,sy*scale);},/**\n * Responsible for retrieving, and caching the\n * image metadata pertinent to this TileSources implementation.\n * @function\n * @param {String} url\n * @throws {Error}\n */getImageInfo:function getImageInfo(url){var _this=this,callbackName,callback,readySource,options,urlParts,filename,lastDot;if(url){urlParts=url.split('/');filename=urlParts[urlParts.length-1];lastDot=filename.lastIndexOf('.');if(lastDot>-1){urlParts[urlParts.length-1]=filename.slice(0,lastDot);}}callback=function callback(data){if(typeof data===\"string\"){data=$.parseXml(data);}var $TileSource=$.TileSource.determineType(_this,data,url);if(!$TileSource){/**\n * Raised when an error occurs loading a TileSource.\n *\n * @event open-failed\n * @memberof OpenSeadragon.TileSource\n * @type {object}\n * @property {OpenSeadragon.TileSource} eventSource - A reference to the TileSource which raised the event.\n * @property {String} message\n * @property {String} source\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent('open-failed',{message:\"Unable to load TileSource\",source:url});return;}options=$TileSource.prototype.configure.apply(_this,[data,url]);if(options.ajaxWithCredentials===undefined){options.ajaxWithCredentials=_this.ajaxWithCredentials;}readySource=new $TileSource(options);_this.ready=true;/**\n * Raised when a TileSource is opened and initialized.\n *\n * @event ready\n * @memberof OpenSeadragon.TileSource\n * @type {object}\n * @property {OpenSeadragon.TileSource} eventSource - A reference to the TileSource which raised the event.\n * @property {Object} tileSource\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent('ready',{tileSource:readySource});};if(url.match(/\\.js$/)){//TODO: Its not very flexible to require tile sources to end jsonp\n// request for info with a url that ends with '.js' but for\n// now it's the only way I see to distinguish uniformly.\ncallbackName=url.split('/').pop().replace('.js','');$.jsonp({url:url,async:false,callbackName:callbackName,callback:callback});}else{// request info via xhr asynchronously.\n$.makeAjaxRequest({url:url,withCredentials:this.ajaxWithCredentials,headers:this.ajaxHeaders,success:function success(xhr){var data=processResponse(xhr);callback(data);},error:function error(xhr,exc){var msg;/*\n IE < 10 will block XHR requests to different origins. Any property access on the request\n object will raise an exception which we'll attempt to handle by formatting the original\n exception rather than the second one raised when we try to access xhr.status\n */try{msg=\"HTTP \"+xhr.status+\" attempting to load TileSource\";}catch(e){var formattedExc;if(typeof exc==\"undefined\"||!exc.toString){formattedExc=\"Unknown error\";}else{formattedExc=exc.toString();}msg=formattedExc+\" attempting to load TileSource\";}/***\n * Raised when an error occurs loading a TileSource.\n *\n * @event open-failed\n * @memberof OpenSeadragon.TileSource\n * @type {object}\n * @property {OpenSeadragon.TileSource} eventSource - A reference to the TileSource which raised the event.\n * @property {String} message\n * @property {String} source\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent('open-failed',{message:msg,source:url});}});}},/**\n * Responsible determining if a the particular TileSource supports the\n * data format ( and allowed to apply logic against the url the data was\n * loaded from, if any ). Overriding implementations are expected to do\n * something smart with data and / or url to determine support. Also\n * understand that iteration order of TileSources is not guarunteed so\n * please make sure your data or url is expressive enough to ensure a simple\n * and sufficient mechanisim for clear determination.\n * @function\n * @param {String|Object|Array|Document} data\n * @param {String} url - the url the data was loaded\n * from if any.\n * @return {Boolean}\n */supports:function supports(data,url){return false;},/**\n * Responsible for parsing and configuring the\n * image metadata pertinent to this TileSources implementation.\n * This method is not implemented by this class other than to throw an Error\n * announcing you have to implement it. Because of the variety of tile\n * server technologies, and various specifications for building image\n * pyramids, this method is here to allow easy integration.\n * @function\n * @param {String|Object|Array|Document} data\n * @param {String} url - the url the data was loaded\n * from if any.\n * @return {Object} options - A dictionary of keyword arguments sufficient\n * to configure this tile sources constructor.\n * @throws {Error}\n */configure:function configure(data,url){throw new Error(\"Method not implemented.\");},/**\n * Responsible for retrieving the url which will return an image for the\n * region specified by the given x, y, and level components.\n * This method is not implemented by this class other than to throw an Error\n * announcing you have to implement it. Because of the variety of tile\n * server technologies, and various specifications for building image\n * pyramids, this method is here to allow easy integration.\n * @function\n * @param {Number} level\n * @param {Number} x\n * @param {Number} y\n * @throws {Error}\n */getTileUrl:function getTileUrl(level,x,y){throw new Error(\"Method not implemented.\");},/**\n * Responsible for retrieving the headers which will be attached to the image request for the\n * region specified by the given x, y, and level components.\n * This option is only relevant if {@link OpenSeadragon.Options}.loadTilesWithAjax is set to true.\n * The headers returned here will override headers specified at the Viewer or TiledImage level.\n * Specifying a falsy value for a header will clear its existing value set at the Viewer or\n * TiledImage level (if any).\n * @function\n * @param {Number} level\n * @param {Number} x\n * @param {Number} y\n * @returns {Object}\n */getTileAjaxHeaders:function getTileAjaxHeaders(level,x,y){return{};},/**\n * @function\n * @param {Number} level\n * @param {Number} x\n * @param {Number} y\n */tileExists:function tileExists(level,x,y){var numTiles=this.getNumTiles(level);return level>=this.minLevel&&level<=this.maxLevel&&x>=0&&y>=0&&x=0;i--){rect=this.displayRects[i];for(level=rect.minLevel;level<=rect.maxLevel;level++){if(!this._levelRects[level]){this._levelRects[level]=[];}this._levelRects[level].push(rect);}}}$.TileSource.apply(this,[options]);};$.extend($.DziTileSource.prototype,$.TileSource.prototype,/** @lends OpenSeadragon.DziTileSource.prototype */{/**\n * Determine if the data and/or url imply the image service is supported by\n * this tile source.\n * @function\n * @param {Object|Array} data\n * @param {String} optional - url\n */supports:function supports(data,url){var ns;if(data.Image){ns=data.Image.xmlns;}else if(data.documentElement){if(\"Image\"==data.documentElement.localName||\"Image\"==data.documentElement.tagName){ns=data.documentElement.namespaceURI;}}ns=(ns||'').toLowerCase();return ns.indexOf('schemas.microsoft.com/deepzoom/2008')!==-1||ns.indexOf('schemas.microsoft.com/deepzoom/2009')!==-1;},/**\n *\n * @function\n * @param {Object|XMLDocument} data - the raw configuration\n * @param {String} url - the url the data was retreived from if any.\n * @return {Object} options - A dictionary of keyword arguments sufficient\n * to configure this tile sources constructor.\n */configure:function configure(data,url){var options;if(!$.isPlainObject(data)){options=configureFromXML(this,data);}else{options=configureFromObject(this,data);}if(url&&!options.tilesUrl){options.tilesUrl=url.replace(/([^\\/]+?)(\\.(dzi|xml|js)?(\\?[^\\/]*)?)?\\/?$/,'$1_files/');if(url.search(/\\.(dzi|xml|js)\\?/)!=-1){options.queryParams=url.match(/\\?.*/);}else{options.queryParams='';}}return options;},/**\n * @function\n * @param {Number} level\n * @param {Number} x\n * @param {Number} y\n */getTileUrl:function getTileUrl(level,x,y){return[this.tilesUrl,level,'/',x,'_',y,'.',this.fileFormat,this.queryParams].join('');},/**\n * @function\n * @param {Number} level\n * @param {Number} x\n * @param {Number} y\n */tileExists:function tileExists(level,x,y){var rects=this._levelRects[level],rect,scale,xMin,yMin,xMax,yMax,i;if(!rects||!rects.length){return true;}for(i=rects.length-1;i>=0;i--){rect=rects[i];if(levelrect.maxLevel){continue;}scale=this.getLevelScale(level);xMin=rect.x*scale;yMin=rect.y*scale;xMax=xMin+rect.width*scale;yMax=yMin+rect.height*scale;xMin=Math.floor(xMin/this._tileWidth);yMin=Math.floor(yMin/this._tileWidth);// DZI tiles are square, so we just use _tileWidth\nxMax=Math.ceil(xMax/this._tileWidth);yMax=Math.ceil(yMax/this._tileWidth);if(xMin<=x&&x0){options.tileSize=Math.max.apply(null,smallerTiles);}else{// If we're smaller than 256, just use the short side.\noptions.tileSize=shortDim;}}else if(this.sizes&&this.sizes.length>0){// This info.json can't be tiled, but we can still construct a legacy pyramid from the sizes array.\n// In this mode, IIIFTileSource will call functions from the abstract baseTileSource or the\n// LegacyTileSource instead of performing IIIF tiling.\nthis.emulateLegacyImagePyramid=true;options.levels=constructLevels(this);// use the largest available size to define tiles\n$.extend(true,options,{width:options.levels[options.levels.length-1].width,height:options.levels[options.levels.length-1].height,tileSize:Math.max(options.height,options.width),tileOverlap:0,minLevel:0,maxLevel:options.levels.length-1});this.levels=options.levels;}else{$.console.error(\"Nothing in the info.json to construct image pyramids from\");}if(!options.maxLevel&&!this.emulateLegacyImagePyramid){if(!this.scale_factors){options.maxLevel=Number(Math.ceil(Math.log(Math.max(this.width,this.height),2)));}else{var maxScaleFactor=Math.max.apply(null,this.scale_factors);options.maxLevel=Math.round(Math.log(maxScaleFactor)*Math.LOG2E);}}$.TileSource.apply(this,[options]);};$.extend($.IIIFTileSource.prototype,$.TileSource.prototype,/** @lends OpenSeadragon.IIIFTileSource.prototype */{/**\n * Determine if the data and/or url imply the image service is supported by\n * this tile source.\n * @function\n * @param {Object|Array} data\n * @param {String} optional - url\n */supports:function supports(data,url){// Version 2.0 and forwards\nif(data.protocol&&data.protocol=='http://iiif.io/api/image'){return true;// Version 1.1\n}else if(data['@context']&&(data['@context']==\"http://library.stanford.edu/iiif/image-api/1.1/context.json\"||data['@context']==\"http://iiif.io/api/image/1/context.json\")){// N.B. the iiif.io context is wrong, but where the representation lives so likely to be used\nreturn true;// Version 1.0\n}else if(data.profile&&data.profile.indexOf(\"http://library.stanford.edu/iiif/image-api/compliance.html\")===0){return true;}else if(data.identifier&&data.width&&data.height){return true;}else if(data.documentElement&&\"info\"==data.documentElement.tagName&&\"http://library.stanford.edu/iiif/image-api/ns/\"==data.documentElement.namespaceURI){return true;// Not IIIF\n}else{return false;}},/**\n *\n * @function\n * @param {Object} data - the raw configuration\n * @example IIIF 1.1 Info Looks like this\n * {\n * \"@context\" : \"http://library.stanford.edu/iiif/image-api/1.1/context.json\",\n * \"@id\" : \"http://iiif.example.com/prefix/1E34750D-38DB-4825-A38A-B60A345E591C\",\n * \"width\" : 6000,\n * \"height\" : 4000,\n * \"scale_factors\" : [ 1, 2, 4 ],\n * \"tile_width\" : 1024,\n * \"tile_height\" : 1024,\n * \"formats\" : [ \"jpg\", \"png\" ],\n * \"qualities\" : [ \"native\", \"grey\" ],\n * \"profile\" : \"http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level0\"\n * }\n */configure:function configure(data,url){// Try to deduce our version and fake it upwards if needed\nif(!$.isPlainObject(data)){var options=configureFromXml10(data);options['@context']=\"http://iiif.io/api/image/1.0/context.json\";options['@id']=url.replace('/info.xml','');return options;}else if(!data['@context']){data['@context']='http://iiif.io/api/image/1.0/context.json';data['@id']=url.replace('/info.json','');return data;}else{return data;}},/**\n * Return the tileWidth for the given level.\n * @function\n * @param {Number} level\n */getTileWidth:function getTileWidth(level){if(this.emulateLegacyImagePyramid){return $.TileSource.prototype.getTileWidth.call(this,level);}var scaleFactor=Math.pow(2,this.maxLevel-level);if(this.tileSizePerScaleFactor&&this.tileSizePerScaleFactor[scaleFactor]){return this.tileSizePerScaleFactor[scaleFactor].width;}return this._tileWidth;},/**\n * Return the tileHeight for the given level.\n * @function\n * @param {Number} level\n */getTileHeight:function getTileHeight(level){if(this.emulateLegacyImagePyramid){return $.TileSource.prototype.getTileHeight.call(this,level);}var scaleFactor=Math.pow(2,this.maxLevel-level);if(this.tileSizePerScaleFactor&&this.tileSizePerScaleFactor[scaleFactor]){return this.tileSizePerScaleFactor[scaleFactor].height;}return this._tileHeight;},/**\n * @function\n * @param {Number} level\n */getLevelScale:function getLevelScale(level){if(this.emulateLegacyImagePyramid){var levelScale=NaN;if(this.levels.length>0&&level>=this.minLevel&&level<=this.maxLevel){levelScale=this.levels[level].width/this.levels[this.maxLevel].width;}return levelScale;}return $.TileSource.prototype.getLevelScale.call(this,level);},/**\n * @function\n * @param {Number} level\n */getNumTiles:function getNumTiles(level){if(this.emulateLegacyImagePyramid){var scale=this.getLevelScale(level);if(scale){return new $.Point(1,1);}else{return new $.Point(0,0);}}return $.TileSource.prototype.getNumTiles.call(this,level);},/**\n * @function\n * @param {Number} level\n * @param {OpenSeadragon.Point} point\n */getTileAtPoint:function getTileAtPoint(level,point){if(this.emulateLegacyImagePyramid){return new $.Point(0,0);}return $.TileSource.prototype.getTileAtPoint.call(this,level,point);},/**\n * Responsible for retrieving the url which will return an image for the\n * region specified by the given x, y, and level components.\n * @function\n * @param {Number} level - z index\n * @param {Number} x\n * @param {Number} y\n * @throws {Error}\n */getTileUrl:function getTileUrl(level,x,y){if(this.emulateLegacyImagePyramid){var url=null;if(this.levels.length>0&&level>=this.minLevel&&level<=this.maxLevel){url=this.levels[level].url;}return url;}//# constants\nvar IIIF_ROTATION='0',//## get the scale (level as a decimal)\nscale=Math.pow(0.5,this.maxLevel-level),//# image dimensions at this level\nlevelWidth=Math.ceil(this.width*scale),levelHeight=Math.ceil(this.height*scale),//## iiif region\ntileWidth,tileHeight,iiifTileSizeWidth,iiifTileSizeHeight,iiifRegion,iiifTileX,iiifTileY,iiifTileW,iiifTileH,iiifSize,iiifQuality,uri;tileWidth=this.getTileWidth(level);tileHeight=this.getTileHeight(level);iiifTileSizeWidth=Math.ceil(tileWidth/scale);iiifTileSizeHeight=Math.ceil(tileHeight/scale);if(this['@context'].indexOf('/1.0/context.json')>-1||this['@context'].indexOf('/1.1/context.json')>-1||this['@context'].indexOf('/1/context.json')>-1){iiifQuality=\"native.jpg\";}else{iiifQuality=\"default.jpg\";}if(levelWidth. Rainer Simon has contributed\n * the included code to the OpenSeadragon project under the New BSD license;\n * see .\n */(function($){/**\n * @class OsmTileSource\n * @classdesc A tilesource implementation for OpenStreetMap.
\n *\n * Note 1. Zoomlevels. Deep Zoom and OSM define zoom levels differently. In Deep\n * Zoom, level 0 equals an image of 1x1 pixels. In OSM, level 0 equals an image of\n * 256x256 levels (see http://gasi.ch/blog/inside-deep-zoom-2). I.e. there is a\n * difference of log2(256)=8 levels.
\n *\n * Note 2. Image dimension. According to the OSM Wiki\n * (http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Zoom_levels)\n * the highest Mapnik zoom level has 256.144x256.144 tiles, with a 256x256\n * pixel size. I.e. the Deep Zoom image dimension is 65.572.864x65.572.864\n * pixels.\n *\n * @memberof OpenSeadragon\n * @extends OpenSeadragon.TileSource\n * @param {Number|Object} width - the pixel width of the image or the idiomatic\n * options object which is used instead of positional arguments.\n * @param {Number} height\n * @param {Number} tileSize\n * @param {Number} tileOverlap\n * @param {String} tilesUrl\n */$.OsmTileSource=function(width,height,tileSize,tileOverlap,tilesUrl){var options;if($.isPlainObject(width)){options=width;}else{options={width:arguments[0],height:arguments[1],tileSize:arguments[2],tileOverlap:arguments[3],tilesUrl:arguments[4]};}//apply default setting for standard public OpenStreatMaps service\n//but allow them to be specified so fliks can host there own instance\n//or apply against other services supportting the same standard\nif(!options.width||!options.height){options.width=65572864;options.height=65572864;}if(!options.tileSize){options.tileSize=256;options.tileOverlap=0;}if(!options.tilesUrl){options.tilesUrl=\"http://tile.openstreetmap.org/\";}options.minLevel=8;$.TileSource.apply(this,[options]);};$.extend($.OsmTileSource.prototype,$.TileSource.prototype,/** @lends OpenSeadragon.OsmTileSource.prototype */{/**\n * Determine if the data and/or url imply the image service is supported by\n * this tile source.\n * @function\n * @param {Object|Array} data\n * @param {String} optional - url\n */supports:function supports(data,url){return data.type&&\"openstreetmaps\"==data.type;},/**\n *\n * @function\n * @param {Object} data - the raw configuration\n * @param {String} url - the url the data was retreived from if any.\n * @return {Object} options - A dictionary of keyword arguments sufficient\n * to configure this tile sources constructor.\n */configure:function configure(data,url){return data;},/**\n * @function\n * @param {Number} level\n * @param {Number} x\n * @param {Number} y\n */getTileUrl:function getTileUrl(level,x,y){return this.tilesUrl+(level-8)+\"/\"+x+\"/\"+y+\".png\";}});})(OpenSeadragon);/*\n * OpenSeadragon - TmsTileSource\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */ /*\n * Derived from the TMS tile source in Rainer Simon's seajax-utils project\n * . Rainer Simon has contributed\n * the included code to the OpenSeadragon project under the New BSD license;\n * see .\n */(function($){/**\n * @class TmsTileSource\n * @classdesc A tilesource implementation for Tiled Map Services (TMS).\n * TMS tile scheme ( [ as supported by OpenLayers ] is described here\n * ( http://openlayers.org/dev/examples/tms.html ).\n *\n * @memberof OpenSeadragon\n * @extends OpenSeadragon.TileSource\n * @param {Number|Object} width - the pixel width of the image or the idiomatic\n * options object which is used instead of positional arguments.\n * @param {Number} height\n * @param {Number} tileSize\n * @param {Number} tileOverlap\n * @param {String} tilesUrl\n */$.TmsTileSource=function(width,height,tileSize,tileOverlap,tilesUrl){var options;if($.isPlainObject(width)){options=width;}else{options={width:arguments[0],height:arguments[1],tileSize:arguments[2],tileOverlap:arguments[3],tilesUrl:arguments[4]};}// TMS has integer multiples of 256 for width/height and adds buffer\n// if necessary -> account for this!\nvar bufferedWidth=Math.ceil(options.width/256)*256,bufferedHeight=Math.ceil(options.height/256)*256,max;// Compute number of zoomlevels in this tileset\nif(bufferedWidth>bufferedHeight){max=bufferedWidth/256;}else{max=bufferedHeight/256;}options.maxLevel=Math.ceil(Math.log(max)/Math.log(2))-1;options.tileSize=256;options.width=bufferedWidth;options.height=bufferedHeight;$.TileSource.apply(this,[options]);};$.extend($.TmsTileSource.prototype,$.TileSource.prototype,/** @lends OpenSeadragon.TmsTileSource.prototype */{/**\n * Determine if the data and/or url imply the image service is supported by\n * this tile source.\n * @function\n * @param {Object|Array} data\n * @param {String} optional - url\n */supports:function supports(data,url){return data.type&&\"tiledmapservice\"==data.type;},/**\n *\n * @function\n * @param {Object} data - the raw configuration\n * @param {String} url - the url the data was retreived from if any.\n * @return {Object} options - A dictionary of keyword arguments sufficient\n * to configure this tile sources constructor.\n */configure:function configure(data,url){return data;},/**\n * @function\n * @param {Number} level\n * @param {Number} x\n * @param {Number} y\n */getTileUrl:function getTileUrl(level,x,y){// Convert from Deep Zoom definition to TMS zoom definition\nvar yTiles=this.getNumTiles(level).y-1;return this.tilesUrl+level+\"/\"+x+\"/\"+(yTiles-y)+\".png\";}});})(OpenSeadragon);(function($){/**\n * @class ZoomifyTileSource\n * @classdesc A tilesource implementation for the zoomify format.\n *\n * A description of the format can be found here:\n * https://ecommons.cornell.edu/bitstream/handle/1813/5410/Introducing_Zoomify_Image.pdf\n *\n * There are two ways of creating a zoomify tilesource for openseadragon\n *\n * 1) Supplying all necessary information in the tilesource object. A minimal example object for this method looks like this:\n *\n * {\n * type: \"zoomifytileservice\",\n * width: 1000,\n * height: 1000,\n * tilesUrl: \"/test/data/zoomify/\"\n * }\n *\n * The tileSize is currently hardcoded to 256 (the usual Zoomify default). The tileUrl must the path to the image _directory_.\n *\n * 2) Loading image metadata from xml file: (CURRENTLY NOT SUPPORTED)\n *\n * When creating zoomify formatted images one \"xml\" like file with name ImageProperties.xml\n * will be created as well. Here is an example of such a file:\n *\n * \n *\n * To use this xml file as metadata source you must supply the path to the ImageProperties.xml file and leave out all other parameters:\n * As stated above, this method of loading a zoomify tilesource is currently not supported\n *\n * {\n * type: \"zoomifytileservice\",\n * tilesUrl: \"/test/data/zoomify/ImageProperties.xml\"\n * }\n\n *\n * @memberof OpenSeadragon\n * @extends OpenSeadragon.TileSource\n * @param {Number} width - the pixel width of the image.\n * @param {Number} height\n * @param {Number} tileSize\n * @param {String} tilesUrl\n */$.ZoomifyTileSource=function(options){options.tileSize=256;var currentImageSize={x:options.width,y:options.height};options.imageSizes=[{x:options.width,y:options.height}];options.gridSize=[this._getGridSize(options.width,options.height,options.tileSize)];while(parseInt(currentImageSize.x,10)>options.tileSize||parseInt(currentImageSize.y,10)>options.tileSize){currentImageSize.x=Math.floor(currentImageSize.x/2);currentImageSize.y=Math.floor(currentImageSize.y/2);options.imageSizes.push({x:currentImageSize.x,y:currentImageSize.y});options.gridSize.push(this._getGridSize(currentImageSize.x,currentImageSize.y,options.tileSize));}options.imageSizes.reverse();options.gridSize.reverse();options.minLevel=0;options.maxLevel=options.gridSize.length-1;OpenSeadragon.TileSource.apply(this,[options]);};$.extend($.ZoomifyTileSource.prototype,$.TileSource.prototype,/** @lends OpenSeadragon.ZoomifyTileSource.prototype */{//private\n_getGridSize:function _getGridSize(width,height,tileSize){return{x:Math.ceil(width/tileSize),y:Math.ceil(height/tileSize)};},//private\n_calculateAbsoluteTileNumber:function _calculateAbsoluteTileNumber(level,x,y){var num=0;var size={};//Sum up all tiles below the level we want the number of tiles\nfor(var z=0;z0){width=options.levels[options.levels.length-1].width;height=options.levels[options.levels.length-1].height;}else{width=0;height=0;$.console.error(\"No supported image formats found\");}$.extend(true,options,{width:width,height:height,tileSize:Math.max(height,width),tileOverlap:0,minLevel:0,maxLevel:options.levels.length>0?options.levels.length-1:0});$.TileSource.apply(this,[options]);this.levels=options.levels;};$.extend($.LegacyTileSource.prototype,$.TileSource.prototype,/** @lends OpenSeadragon.LegacyTileSource.prototype */{/**\n * Determine if the data and/or url imply the image service is supported by\n * this tile source.\n * @function\n * @param {Object|Array} data\n * @param {String} optional - url\n */supports:function supports(data,url){return data.type&&\"legacy-image-pyramid\"==data.type||data.documentElement&&\"legacy-image-pyramid\"==data.documentElement.getAttribute('type');},/**\n *\n * @function\n * @param {Object|XMLDocument} configuration - the raw configuration\n * @param {String} dataUrl - the url the data was retreived from if any.\n * @return {Object} options - A dictionary of keyword arguments sufficient\n * to configure this tile sources constructor.\n */configure:function configure(configuration,dataUrl){var options;if(!$.isPlainObject(configuration)){options=configureFromXML(this,configuration);}else{options=configureFromObject(this,configuration);}return options;},/**\n * @function\n * @param {Number} level\n */getLevelScale:function getLevelScale(level){var levelScale=NaN;if(this.levels.length>0&&level>=this.minLevel&&level<=this.maxLevel){levelScale=this.levels[level].width/this.levels[this.maxLevel].width;}return levelScale;},/**\n * @function\n * @param {Number} level\n */getNumTiles:function getNumTiles(level){var scale=this.getLevelScale(level);if(scale){return new $.Point(1,1);}else{return new $.Point(0,0);}},/**\n * This method is not implemented by this class other than to throw an Error\n * announcing you have to implement it. Because of the variety of tile\n * server technologies, and various specifications for building image\n * pyramids, this method is here to allow easy integration.\n * @function\n * @param {Number} level\n * @param {Number} x\n * @param {Number} y\n * @throws {Error}\n */getTileUrl:function getTileUrl(level,x,y){var url=null;if(this.levels.length>0&&level>=this.minLevel&&level<=this.maxLevel){url=this.levels[level].url;}return url;}});/**\n * This method removes any files from the Array which dont conform to our\n * basic requirements for a 'level' in the LegacyTileSource.\n * @private\n * @inner\n * @function\n */function filterFiles(files){var filtered=[],file,i;for(i=0;i');}}return filtered.sort(function(a,b){return a.height-b.height;});}/**\n * @private\n * @inner\n * @function\n */function configureFromXML(tileSource,xmlDoc){if(!xmlDoc||!xmlDoc.documentElement){throw new Error($.getString(\"Errors.Xml\"));}var root=xmlDoc.documentElement,rootName=root.tagName,conf=null,levels=[],level,i;if(rootName==\"image\"){try{conf={type:root.getAttribute(\"type\"),levels:[]};levels=root.getElementsByTagName(\"level\");for(i=0;i=this.minLevel&&level<=this.maxLevel){levelScale=this.levels[level].width/this.levels[this.maxLevel].width;}return levelScale;},/**\n * @function\n * @param {Number} level\n */getNumTiles:function getNumTiles(level){var scale=this.getLevelScale(level);if(scale){return new $.Point(1,1);}else{return new $.Point(0,0);}},/**\n * Retrieves a tile url\n * @function\n * @param {Number} level Level of the tile\n * @param {Number} x x coordinate of the tile\n * @param {Number} y y coordinate of the tile\n */getTileUrl:function getTileUrl(level,x,y){var url=null;if(level>=this.minLevel&&level<=this.maxLevel){url=this.levels[level].url;}return url;},/**\n * Retrieves a tile context 2D\n * @function\n * @param {Number} level Level of the tile\n * @param {Number} x x coordinate of the tile\n * @param {Number} y y coordinate of the tile\n */getContext2D:function getContext2D(level,x,y){var context=null;if(level>=this.minLevel&&level<=this.maxLevel){context=this.levels[level].context2D;}return context;},// private\n//\n// Builds the differents levels of the pyramid if possible\n// (i.e. if canvas API enabled and no canvas tainting issue).\n_buildLevels:function _buildLevels(){var levels=[{url:this._image.src,/* IE8 fix since it has no naturalWidth and naturalHeight */width:Object.prototype.hasOwnProperty.call(this._image,'naturalWidth')?this._image.naturalWidth:this._image.width,height:Object.prototype.hasOwnProperty.call(this._image,'naturalHeight')?this._image.naturalHeight:this._image.height}];if(!this.buildPyramid||!$.supportsCanvas||!this.useCanvas){// We don't need the image anymore. Allows it to be GC.\ndelete this._image;return levels;}/* IE8 fix since it has no naturalWidth and naturalHeight */var currentWidth=Object.prototype.hasOwnProperty.call(this._image,'naturalWidth')?this._image.naturalWidth:this._image.width;var currentHeight=Object.prototype.hasOwnProperty.call(this._image,'naturalHeight')?this._image.naturalHeight:this._image.height;var bigCanvas=document.createElement(\"canvas\");var bigContext=bigCanvas.getContext(\"2d\");bigCanvas.width=currentWidth;bigCanvas.height=currentHeight;bigContext.drawImage(this._image,0,0,currentWidth,currentHeight);// We cache the context of the highest level because the browser\n// is a lot faster at downsampling something it already has\n// downsampled before.\nlevels[0].context2D=bigContext;// We don't need the image anymore. Allows it to be GC.\ndelete this._image;if($.isCanvasTainted(bigCanvas)){// If the canvas is tainted, we can't compute the pyramid.\nreturn levels;}// We build smaller levels until either width or height becomes\n// 1 pixel wide.\nwhile(currentWidth>=2&¤tHeight>=2){currentWidth=Math.floor(currentWidth/2);currentHeight=Math.floor(currentHeight/2);var smallCanvas=document.createElement(\"canvas\");var smallContext=smallCanvas.getContext(\"2d\");smallCanvas.width=currentWidth;smallCanvas.height=currentHeight;smallContext.drawImage(bigCanvas,0,0,currentWidth,currentHeight);levels.splice(0,0,{context2D:smallContext,width:currentWidth,height:currentHeight});bigCanvas=smallCanvas;bigContext=smallContext;}return levels;}});})(OpenSeadragon);/*\n * OpenSeadragon - TileSourceCollection\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){// deprecated\n$.TileSourceCollection=function(tileSize,tileSources,rows,layout){$.console.error('TileSourceCollection is deprecated; use World instead');};})(OpenSeadragon);/*\n * OpenSeadragon - Button\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * An enumeration of button states\n * @member ButtonState\n * @memberof OpenSeadragon\n * @static\n * @type {Object}\n * @property {Number} REST\n * @property {Number} GROUP\n * @property {Number} HOVER\n * @property {Number} DOWN\n */$.ButtonState={REST:0,GROUP:1,HOVER:2,DOWN:3};/**\n * @class Button\n * @classdesc Manages events, hover states for individual buttons, tool-tips, as well\n * as fading the buttons out when the user has not interacted with them\n * for a specified period.\n *\n * @memberof OpenSeadragon\n * @extends OpenSeadragon.EventSource\n * @param {Object} options\n * @param {Element} [options.element=null] Element to use as the button. If not specified, an HTML <div> element is created.\n * @param {String} [options.tooltip=null] Provides context help for the button when the\n * user hovers over it.\n * @param {String} [options.srcRest=null] URL of image to use in 'rest' state.\n * @param {String} [options.srcGroup=null] URL of image to use in 'up' state.\n * @param {String} [options.srcHover=null] URL of image to use in 'hover' state.\n * @param {String} [options.srcDown=null] URL of image to use in 'down' state.\n * @param {Number} [options.fadeDelay=0] How long to wait before fading.\n * @param {Number} [options.fadeLength=2000] How long should it take to fade the button.\n * @param {OpenSeadragon.EventHandler} [options.onPress=null] Event handler callback for {@link OpenSeadragon.Button.event:press}.\n * @param {OpenSeadragon.EventHandler} [options.onRelease=null] Event handler callback for {@link OpenSeadragon.Button.event:release}.\n * @param {OpenSeadragon.EventHandler} [options.onClick=null] Event handler callback for {@link OpenSeadragon.Button.event:click}.\n * @param {OpenSeadragon.EventHandler} [options.onEnter=null] Event handler callback for {@link OpenSeadragon.Button.event:enter}.\n * @param {OpenSeadragon.EventHandler} [options.onExit=null] Event handler callback for {@link OpenSeadragon.Button.event:exit}.\n * @param {OpenSeadragon.EventHandler} [options.onFocus=null] Event handler callback for {@link OpenSeadragon.Button.event:focus}.\n * @param {OpenSeadragon.EventHandler} [options.onBlur=null] Event handler callback for {@link OpenSeadragon.Button.event:blur}.\n */$.Button=function(options){var _this=this;$.EventSource.call(this);$.extend(true,this,{tooltip:null,srcRest:null,srcGroup:null,srcHover:null,srcDown:null,clickTimeThreshold:$.DEFAULT_SETTINGS.clickTimeThreshold,clickDistThreshold:$.DEFAULT_SETTINGS.clickDistThreshold,/**\n * How long to wait before fading.\n * @member {Number} fadeDelay\n * @memberof OpenSeadragon.Button#\n */fadeDelay:0,/**\n * How long should it take to fade the button.\n * @member {Number} fadeLength\n * @memberof OpenSeadragon.Button#\n */fadeLength:2000,onPress:null,onRelease:null,onClick:null,onEnter:null,onExit:null,onFocus:null,onBlur:null},options);/**\n * The button element.\n * @member {Element} element\n * @memberof OpenSeadragon.Button#\n */this.element=options.element||$.makeNeutralElement(\"div\");//if the user has specified the element to bind the control to explicitly\n//then do not add the default control images\nif(!options.element){this.imgRest=$.makeTransparentImage(this.srcRest);this.imgGroup=$.makeTransparentImage(this.srcGroup);this.imgHover=$.makeTransparentImage(this.srcHover);this.imgDown=$.makeTransparentImage(this.srcDown);this.imgRest.alt=this.imgGroup.alt=this.imgHover.alt=this.imgDown.alt=this.tooltip;this.element.style.position=\"relative\";$.setElementTouchActionNone(this.element);this.imgGroup.style.position=this.imgHover.style.position=this.imgDown.style.position=\"absolute\";this.imgGroup.style.top=this.imgHover.style.top=this.imgDown.style.top=\"0px\";this.imgGroup.style.left=this.imgHover.style.left=this.imgDown.style.left=\"0px\";this.imgHover.style.visibility=this.imgDown.style.visibility=\"hidden\";if($.Browser.vendor==$.BROWSERS.FIREFOX&&$.Browser.version<3){this.imgGroup.style.top=this.imgHover.style.top=this.imgDown.style.top=\"\";}this.element.appendChild(this.imgRest);this.element.appendChild(this.imgGroup);this.element.appendChild(this.imgHover);this.element.appendChild(this.imgDown);}this.addHandler(\"press\",this.onPress);this.addHandler(\"release\",this.onRelease);this.addHandler(\"click\",this.onClick);this.addHandler(\"enter\",this.onEnter);this.addHandler(\"exit\",this.onExit);this.addHandler(\"focus\",this.onFocus);this.addHandler(\"blur\",this.onBlur);/**\n * The button's current state.\n * @member {OpenSeadragon.ButtonState} currentState\n * @memberof OpenSeadragon.Button#\n */this.currentState=$.ButtonState.GROUP;// When the button last began to fade.\nthis.fadeBeginTime=null;// Whether this button should fade after user stops interacting with the viewport.\nthis.shouldFade=false;this.element.style.display=\"inline-block\";this.element.style.position=\"relative\";this.element.title=this.tooltip;/**\n * Tracks mouse/touch/key events on the button.\n * @member {OpenSeadragon.MouseTracker} tracker\n * @memberof OpenSeadragon.Button#\n */this.tracker=new $.MouseTracker({element:this.element,clickTimeThreshold:this.clickTimeThreshold,clickDistThreshold:this.clickDistThreshold,enterHandler:function enterHandler(event){if(event.insideElementPressed){inTo(_this,$.ButtonState.DOWN);/**\n * Raised when the cursor enters the Button element.\n *\n * @event enter\n * @memberof OpenSeadragon.Button\n * @type {object}\n * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent(\"enter\",{originalEvent:event.originalEvent});}else if(!event.buttonDownAny){inTo(_this,$.ButtonState.HOVER);}},focusHandler:function focusHandler(event){this.enterHandler(event);/**\n * Raised when the Button element receives focus.\n *\n * @event focus\n * @memberof OpenSeadragon.Button\n * @type {object}\n * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent(\"focus\",{originalEvent:event.originalEvent});},exitHandler:function exitHandler(event){outTo(_this,$.ButtonState.GROUP);if(event.insideElementPressed){/**\n * Raised when the cursor leaves the Button element.\n *\n * @event exit\n * @memberof OpenSeadragon.Button\n * @type {object}\n * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent(\"exit\",{originalEvent:event.originalEvent});}},blurHandler:function blurHandler(event){this.exitHandler(event);/**\n * Raised when the Button element loses focus.\n *\n * @event blur\n * @memberof OpenSeadragon.Button\n * @type {object}\n * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent(\"blur\",{originalEvent:event.originalEvent});},pressHandler:function pressHandler(event){inTo(_this,$.ButtonState.DOWN);/**\n * Raised when a mouse button is pressed or touch occurs in the Button element.\n *\n * @event press\n * @memberof OpenSeadragon.Button\n * @type {object}\n * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent(\"press\",{originalEvent:event.originalEvent});},releaseHandler:function releaseHandler(event){if(event.insideElementPressed&&event.insideElementReleased){outTo(_this,$.ButtonState.HOVER);/**\n * Raised when the mouse button is released or touch ends in the Button element.\n *\n * @event release\n * @memberof OpenSeadragon.Button\n * @type {object}\n * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent(\"release\",{originalEvent:event.originalEvent});}else if(event.insideElementPressed){outTo(_this,$.ButtonState.GROUP);}else{inTo(_this,$.ButtonState.HOVER);}},clickHandler:function clickHandler(event){if(event.quick){/**\n * Raised when a mouse button is pressed and released or touch is initiated and ended in the Button element within the time and distance threshold.\n *\n * @event click\n * @memberof OpenSeadragon.Button\n * @type {object}\n * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent(\"click\",{originalEvent:event.originalEvent});}},keyHandler:function keyHandler(event){//console.log( \"%s : handling key %s!\", _this.tooltip, event.keyCode);\nif(13===event.keyCode){/***\n * Raised when a mouse button is pressed and released or touch is initiated and ended in the Button element within the time and distance threshold.\n *\n * @event click\n * @memberof OpenSeadragon.Button\n * @type {object}\n * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent(\"click\",{originalEvent:event.originalEvent});/***\n * Raised when the mouse button is released or touch ends in the Button element.\n *\n * @event release\n * @memberof OpenSeadragon.Button\n * @type {object}\n * @property {OpenSeadragon.Button} eventSource - A reference to the Button which raised the event.\n * @property {Object} originalEvent - The original DOM event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.raiseEvent(\"release\",{originalEvent:event.originalEvent});return false;}return true;}});outTo(this,$.ButtonState.REST);};$.extend($.Button.prototype,$.EventSource.prototype,/** @lends OpenSeadragon.Button.prototype */{/**\n * TODO: Determine what this function is intended to do and if it's actually\n * useful as an API point.\n * @function\n */notifyGroupEnter:function notifyGroupEnter(){inTo(this,$.ButtonState.GROUP);},/**\n * TODO: Determine what this function is intended to do and if it's actually\n * useful as an API point.\n * @function\n */notifyGroupExit:function notifyGroupExit(){outTo(this,$.ButtonState.REST);},/**\n * @function\n */disable:function disable(){this.notifyGroupExit();this.element.disabled=true;$.setElementOpacity(this.element,0.2,true);},/**\n * @function\n */enable:function enable(){this.element.disabled=false;$.setElementOpacity(this.element,1.0,true);this.notifyGroupEnter();}});function scheduleFade(button){$.requestAnimationFrame(function(){updateFade(button);});}function updateFade(button){var currentTime,deltaTime,opacity;if(button.shouldFade){currentTime=$.now();deltaTime=currentTime-button.fadeBeginTime;opacity=1.0-deltaTime/button.fadeLength;opacity=Math.min(1.0,opacity);opacity=Math.max(0.0,opacity);if(button.imgGroup){$.setElementOpacity(button.imgGroup,opacity,true);}if(opacity>0){// fade again\nscheduleFade(button);}}}function beginFading(button){button.shouldFade=true;button.fadeBeginTime=$.now()+button.fadeDelay;window.setTimeout(function(){scheduleFade(button);},button.fadeDelay);}function stopFading(button){button.shouldFade=false;if(button.imgGroup){$.setElementOpacity(button.imgGroup,1.0,true);}}function inTo(button,newState){if(button.element.disabled){return;}if(newState>=$.ButtonState.GROUP&&button.currentState==$.ButtonState.REST){stopFading(button);button.currentState=$.ButtonState.GROUP;}if(newState>=$.ButtonState.HOVER&&button.currentState==$.ButtonState.GROUP){if(button.imgHover){button.imgHover.style.visibility=\"\";}button.currentState=$.ButtonState.HOVER;}if(newState>=$.ButtonState.DOWN&&button.currentState==$.ButtonState.HOVER){if(button.imgDown){button.imgDown.style.visibility=\"\";}button.currentState=$.ButtonState.DOWN;}}function outTo(button,newState){if(button.element.disabled){return;}if(newState<=$.ButtonState.HOVER&&button.currentState==$.ButtonState.DOWN){if(button.imgDown){button.imgDown.style.visibility=\"hidden\";}button.currentState=$.ButtonState.HOVER;}if(newState<=$.ButtonState.GROUP&&button.currentState==$.ButtonState.HOVER){if(button.imgHover){button.imgHover.style.visibility=\"hidden\";}button.currentState=$.ButtonState.GROUP;}if(newState<=$.ButtonState.REST&&button.currentState==$.ButtonState.GROUP){beginFading(button);button.currentState=$.ButtonState.REST;}}})(OpenSeadragon);/*\n * OpenSeadragon - ButtonGroup\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * @class ButtonGroup\n * @classdesc Manages events on groups of buttons.\n *\n * @memberof OpenSeadragon\n * @param {Object} options - A dictionary of settings applied against the entire group of buttons.\n * @param {Array} options.buttons Array of buttons\n * @param {Element} [options.element] Element to use as the container\n **/$.ButtonGroup=function(options){$.extend(true,this,{/**\n * An array containing the buttons themselves.\n * @member {Array} buttons\n * @memberof OpenSeadragon.ButtonGroup#\n */buttons:[],clickTimeThreshold:$.DEFAULT_SETTINGS.clickTimeThreshold,clickDistThreshold:$.DEFAULT_SETTINGS.clickDistThreshold,labelText:\"\"},options);// copy the button elements TODO: Why?\nvar buttons=this.buttons.concat([]),_this=this,i;/**\n * The shared container for the buttons.\n * @member {Element} element\n * @memberof OpenSeadragon.ButtonGroup#\n */this.element=options.element||$.makeNeutralElement(\"div\");// TODO What if there IS an options.group specified?\nif(!options.group){this.label=$.makeNeutralElement(\"label\");//TODO: support labels for ButtonGroups\n//this.label.innerHTML = this.labelText;\nthis.element.style.display=\"inline-block\";this.element.appendChild(this.label);for(i=0;i=270){newTopLeft=this.getTopRight();this.x=newTopLeft.x;this.y=newTopLeft.y;newWidth=this.height;this.height=this.width;this.width=newWidth;this.degrees-=270;}else if(this.degrees>=180){newTopLeft=this.getBottomRight();this.x=newTopLeft.x;this.y=newTopLeft.y;this.degrees-=180;}else if(this.degrees>=90){newTopLeft=this.getBottomLeft();this.x=newTopLeft.x;this.y=newTopLeft.y;newWidth=this.height;this.height=this.width;this.width=newWidth;this.degrees-=90;}};/**\n * Builds a rectangle having the 3 specified points as summits.\n * @static\n * @memberof OpenSeadragon.Rect\n * @param {OpenSeadragon.Point} topLeft\n * @param {OpenSeadragon.Point} topRight\n * @param {OpenSeadragon.Point} bottomLeft\n * @returns {OpenSeadragon.Rect}\n */$.Rect.fromSummits=function(topLeft,topRight,bottomLeft){var width=topLeft.distanceTo(topRight);var height=topLeft.distanceTo(bottomLeft);var diff=topRight.minus(topLeft);var radians=Math.atan(diff.y/diff.x);if(diff.x<0){radians+=Math.PI;}else if(diff.y<0){radians+=2*Math.PI;}return new $.Rect(topLeft.x,topLeft.y,width,height,radians/Math.PI*180);};/** @lends OpenSeadragon.Rect.prototype */$.Rect.prototype={/**\n * @function\n * @returns {OpenSeadragon.Rect} a duplicate of this Rect\n */clone:function clone(){return new $.Rect(this.x,this.y,this.width,this.height,this.degrees);},/**\n * The aspect ratio is simply the ratio of width to height.\n * @function\n * @returns {Number} The ratio of width to height.\n */getAspectRatio:function getAspectRatio(){return this.width/this.height;},/**\n * Provides the coordinates of the upper-left corner of the rectangle as a\n * point.\n * @function\n * @returns {OpenSeadragon.Point} The coordinate of the upper-left corner of\n * the rectangle.\n */getTopLeft:function getTopLeft(){return new $.Point(this.x,this.y);},/**\n * Provides the coordinates of the bottom-right corner of the rectangle as a\n * point.\n * @function\n * @returns {OpenSeadragon.Point} The coordinate of the bottom-right corner of\n * the rectangle.\n */getBottomRight:function getBottomRight(){return new $.Point(this.x+this.width,this.y+this.height).rotate(this.degrees,this.getTopLeft());},/**\n * Provides the coordinates of the top-right corner of the rectangle as a\n * point.\n * @function\n * @returns {OpenSeadragon.Point} The coordinate of the top-right corner of\n * the rectangle.\n */getTopRight:function getTopRight(){return new $.Point(this.x+this.width,this.y).rotate(this.degrees,this.getTopLeft());},/**\n * Provides the coordinates of the bottom-left corner of the rectangle as a\n * point.\n * @function\n * @returns {OpenSeadragon.Point} The coordinate of the bottom-left corner of\n * the rectangle.\n */getBottomLeft:function getBottomLeft(){return new $.Point(this.x,this.y+this.height).rotate(this.degrees,this.getTopLeft());},/**\n * Computes the center of the rectangle.\n * @function\n * @returns {OpenSeadragon.Point} The center of the rectangle as represented\n * as represented by a 2-dimensional vector (x,y)\n */getCenter:function getCenter(){return new $.Point(this.x+this.width/2.0,this.y+this.height/2.0).rotate(this.degrees,this.getTopLeft());},/**\n * Returns the width and height component as a vector OpenSeadragon.Point\n * @function\n * @returns {OpenSeadragon.Point} The 2 dimensional vector representing the\n * the width and height of the rectangle.\n */getSize:function getSize(){return new $.Point(this.width,this.height);},/**\n * Determines if two Rectangles have equivalent components.\n * @function\n * @param {OpenSeadragon.Rect} rectangle The Rectangle to compare to.\n * @return {Boolean} 'true' if all components are equal, otherwise 'false'.\n */equals:function equals(other){return other instanceof $.Rect&&this.x===other.x&&this.y===other.y&&this.width===other.width&&this.height===other.height&&this.degrees===other.degrees;},/**\n * Multiply all dimensions (except degrees) in this Rect by a factor and\n * return a new Rect.\n * @function\n * @param {Number} factor The factor to multiply vector components.\n * @returns {OpenSeadragon.Rect} A new rect representing the multiplication\n * of the vector components by the factor\n */times:function times(factor){return new $.Rect(this.x*factor,this.y*factor,this.width*factor,this.height*factor,this.degrees);},/**\n * Translate/move this Rect by a vector and return new Rect.\n * @function\n * @param {OpenSeadragon.Point} delta The translation vector.\n * @returns {OpenSeadragon.Rect} A new rect with altered position\n */translate:function translate(delta){return new $.Rect(this.x+delta.x,this.y+delta.y,this.width,this.height,this.degrees);},/**\n * Returns the smallest rectangle that will contain this and the given\n * rectangle bounding boxes.\n * @param {OpenSeadragon.Rect} rect\n * @return {OpenSeadragon.Rect} The new rectangle.\n */union:function union(rect){var thisBoundingBox=this.getBoundingBox();var otherBoundingBox=rect.getBoundingBox();var left=Math.min(thisBoundingBox.x,otherBoundingBox.x);var top=Math.min(thisBoundingBox.y,otherBoundingBox.y);var right=Math.max(thisBoundingBox.x+thisBoundingBox.width,otherBoundingBox.x+otherBoundingBox.width);var bottom=Math.max(thisBoundingBox.y+thisBoundingBox.height,otherBoundingBox.y+otherBoundingBox.height);return new $.Rect(left,top,right-left,bottom-top);},/**\n * Returns the bounding box of the intersection of this rectangle with the\n * given rectangle.\n * @param {OpenSeadragon.Rect} rect\n * @return {OpenSeadragon.Rect} the bounding box of the intersection\n * or null if the rectangles don't intersect.\n */intersection:function intersection(rect){// Simplified version of Weiler Atherton clipping algorithm\n// https://en.wikipedia.org/wiki/Weiler%E2%80%93Atherton_clipping_algorithm\n// Because we just want the bounding box of the intersection,\n// we can just compute the bounding box of:\n// 1. all the summits of this which are inside rect\n// 2. all the summits of rect which are inside this\n// 3. all the intersections of rect and this\nvar EPSILON=0.0000000001;var intersectionPoints=[];var thisTopLeft=this.getTopLeft();if(rect.containsPoint(thisTopLeft,EPSILON)){intersectionPoints.push(thisTopLeft);}var thisTopRight=this.getTopRight();if(rect.containsPoint(thisTopRight,EPSILON)){intersectionPoints.push(thisTopRight);}var thisBottomLeft=this.getBottomLeft();if(rect.containsPoint(thisBottomLeft,EPSILON)){intersectionPoints.push(thisBottomLeft);}var thisBottomRight=this.getBottomRight();if(rect.containsPoint(thisBottomRight,EPSILON)){intersectionPoints.push(thisBottomRight);}var rectTopLeft=rect.getTopLeft();if(this.containsPoint(rectTopLeft,EPSILON)){intersectionPoints.push(rectTopLeft);}var rectTopRight=rect.getTopRight();if(this.containsPoint(rectTopRight,EPSILON)){intersectionPoints.push(rectTopRight);}var rectBottomLeft=rect.getBottomLeft();if(this.containsPoint(rectBottomLeft,EPSILON)){intersectionPoints.push(rectBottomLeft);}var rectBottomRight=rect.getBottomRight();if(this.containsPoint(rectBottomRight,EPSILON)){intersectionPoints.push(rectBottomRight);}var thisSegments=this._getSegments();var rectSegments=rect._getSegments();for(var i=0;imaxX){maxX=point.x;}if(point.ymaxY){maxY=point.y;}}return new $.Rect(minX,minY,maxX-minX,maxY-minY);},// private\n_getSegments:function _getSegments(){var topLeft=this.getTopLeft();var topRight=this.getTopRight();var bottomLeft=this.getBottomLeft();var bottomRight=this.getBottomRight();return[[topLeft,topRight],[topRight,bottomRight],[bottomRight,bottomLeft],[bottomLeft,topLeft]];},/**\n * Rotates a rectangle around a point.\n * @function\n * @param {Number} degrees The angle in degrees to rotate.\n * @param {OpenSeadragon.Point} [pivot] The point about which to rotate.\n * Defaults to the center of the rectangle.\n * @return {OpenSeadragon.Rect}\n */rotate:function rotate(degrees,pivot){degrees=$.positiveModulo(degrees,360);if(degrees===0){return this.clone();}pivot=pivot||this.getCenter();var newTopLeft=this.getTopLeft().rotate(degrees,pivot);var newTopRight=this.getTopRight().rotate(degrees,pivot);var diff=newTopRight.minus(newTopLeft);// Handle floating point error\ndiff=diff.apply(function(x){var EPSILON=1e-15;return Math.abs(x)=-epsilon&&(point.x-topRight.x)*topDiff.x+(point.y-topRight.y)*topDiff.y<=epsilon&&(point.x-topLeft.x)*leftDiff.x+(point.y-topLeft.y)*leftDiff.y>=-epsilon&&(point.x-bottomLeft.x)*leftDiff.x+(point.y-bottomLeft.y)*leftDiff.y<=epsilon;},/**\n * Provides a string representation of the rectangle which is useful for\n * debugging.\n * @function\n * @returns {String} A string representation of the rectangle.\n */toString:function toString(){return\"[\"+Math.round(this.x*100)/100+\", \"+Math.round(this.y*100)/100+\", \"+Math.round(this.width*100)/100+\"x\"+Math.round(this.height*100)/100+\", \"+Math.round(this.degrees*100)/100+\"deg\"+\"]\";}};})(OpenSeadragon);/*\n * OpenSeadragon - ReferenceStrip\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){// dictionary from id to private properties\nvar THIS={};/**\n * The CollectionDrawer is a reimplementation if the Drawer API that\n * focuses on allowing a viewport to be redefined as a collection\n * of smaller viewports, defined by a clear number of rows and / or\n * columns of which each item in the matrix of viewports has its own\n * source.\n *\n * This idea is a reexpression of the idea of dzi collections\n * which allows a clearer algorithm to reuse the tile sources already\n * supported by OpenSeadragon, in heterogenious or homogenious\n * sequences just like mixed groups already supported by the viewer\n * for the purpose of image sequnces.\n *\n * TODO: The difficult part of this feature is figuring out how to express\n * this functionality as a combination of the functionality already\n * provided by Drawer, Viewport, TileSource, and Navigator. It may\n * require better abstraction at those points in order to effeciently\n * reuse those paradigms.\n */ /**\n * @class ReferenceStrip\n * @memberof OpenSeadragon\n * @param {Object} options\n */$.ReferenceStrip=function(options){var _this=this,viewer=options.viewer,viewerSize=$.getElementSize(viewer.element),element,style,i;//We may need to create a new element and id if they did not\n//provide the id for the existing element\nif(!options.id){options.id='referencestrip-'+$.now();this.element=$.makeNeutralElement(\"div\");this.element.id=options.id;this.element.className='referencestrip';}options=$.extend(true,{sizeRatio:$.DEFAULT_SETTINGS.referenceStripSizeRatio,position:$.DEFAULT_SETTINGS.referenceStripPosition,scroll:$.DEFAULT_SETTINGS.referenceStripScroll,clickTimeThreshold:$.DEFAULT_SETTINGS.clickTimeThreshold},options,{//required overrides\nelement:this.element,//These need to be overridden to prevent recursion since\n//the navigator is a viewer and a viewer has a navigator\nshowNavigator:false,mouseNavEnabled:false,showNavigationControl:false,showSequenceControl:false});$.extend(this,options);//Private state properties\nTHIS[this.id]={\"animating\":false};this.minPixelRatio=this.viewer.minPixelRatio;style=this.element.style;style.marginTop='0px';style.marginRight='0px';style.marginBottom='0px';style.marginLeft='0px';style.left='0px';style.bottom='0px';style.border='0px';style.background='#000';style.position='relative';$.setElementTouchActionNone(this.element);$.setElementOpacity(this.element,0.8);this.viewer=viewer;this.innerTracker=new $.MouseTracker({element:this.element,dragHandler:$.delegate(this,onStripDrag),scrollHandler:$.delegate(this,onStripScroll),enterHandler:$.delegate(this,onStripEnter),exitHandler:$.delegate(this,onStripExit),keyDownHandler:$.delegate(this,onKeyDown),keyHandler:$.delegate(this,onKeyPress)});//Controls the position and orientation of the reference strip and sets the\n//appropriate width and height\nif(options.width&&options.height){this.element.style.width=options.width+'px';this.element.style.height=options.height+'px';viewer.addControl(this.element,{anchor:$.ControlAnchor.BOTTOM_LEFT});}else{if(\"horizontal\"==options.scroll){this.element.style.width=viewerSize.x*options.sizeRatio*viewer.tileSources.length+12*viewer.tileSources.length+'px';this.element.style.height=viewerSize.y*options.sizeRatio+'px';viewer.addControl(this.element,{anchor:$.ControlAnchor.BOTTOM_LEFT});}else{this.element.style.height=viewerSize.y*options.sizeRatio*viewer.tileSources.length+12*viewer.tileSources.length+'px';this.element.style.width=viewerSize.x*options.sizeRatio+'px';viewer.addControl(this.element,{anchor:$.ControlAnchor.TOP_LEFT});}}this.panelWidth=viewerSize.x*this.sizeRatio+8;this.panelHeight=viewerSize.y*this.sizeRatio+8;this.panels=[];this.miniViewers={};/*jshint loopfunc:true*/for(i=0;ioffsetLeft+viewerSize.x-this.panelWidth){offset=Math.min(offset,scrollWidth-viewerSize.x);this.element.style.marginLeft=-offset+'px';loadPanels(this,viewerSize.x,-offset);}else if(offsetoffsetTop+viewerSize.y-this.panelHeight){offset=Math.min(offset,scrollHeight-viewerSize.y);this.element.style.marginTop=-offset+'px';loadPanels(this,viewerSize.y,-offset);}else if(offset0){//forward\nif(offsetLeft>-(scrollWidth-viewerSize.x)){this.element.style.marginLeft=offsetLeft+event.delta.x*2+'px';loadPanels(this,viewerSize.x,offsetLeft+event.delta.x*2);}}else if(-event.delta.x<0){//reverse\nif(offsetLeft<0){this.element.style.marginLeft=offsetLeft+event.delta.x*2+'px';loadPanels(this,viewerSize.x,offsetLeft+event.delta.x*2);}}}else{if(-event.delta.y>0){//forward\nif(offsetTop>-(scrollHeight-viewerSize.y)){this.element.style.marginTop=offsetTop+event.delta.y*2+'px';loadPanels(this,viewerSize.y,offsetTop+event.delta.y*2);}}else if(-event.delta.y<0){//reverse\nif(offsetTop<0){this.element.style.marginTop=offsetTop+event.delta.y*2+'px';loadPanels(this,viewerSize.y,offsetTop+event.delta.y*2);}}}}return false;}/**\n * @private\n * @inner\n * @function\n */function onStripScroll(event){var offsetLeft=Number(this.element.style.marginLeft.replace('px','')),offsetTop=Number(this.element.style.marginTop.replace('px','')),scrollWidth=Number(this.element.style.width.replace('px','')),scrollHeight=Number(this.element.style.height.replace('px','')),viewerSize=$.getElementSize(this.viewer.canvas);if(this.element){if('horizontal'==this.scroll){if(event.scroll>0){//forward\nif(offsetLeft>-(scrollWidth-viewerSize.x)){this.element.style.marginLeft=offsetLeft-event.scroll*60+'px';loadPanels(this,viewerSize.x,offsetLeft-event.scroll*60);}}else if(event.scroll<0){//reverse\nif(offsetLeft<0){this.element.style.marginLeft=offsetLeft-event.scroll*60+'px';loadPanels(this,viewerSize.x,offsetLeft-event.scroll*60);}}}else{if(event.scroll<0){//scroll up\nif(offsetTop>viewerSize.y-scrollHeight){this.element.style.marginTop=offsetTop+event.scroll*60+'px';loadPanels(this,viewerSize.y,offsetTop+event.scroll*60);}}else if(event.scroll>0){//scroll dowm\nif(offsetTop<0){this.element.style.marginTop=offsetTop+event.scroll*60+'px';loadPanels(this,viewerSize.y,offsetTop+event.scroll*60);}}}}//cancels event\nreturn false;}function loadPanels(strip,viewerSize,scroll){var panelSize,activePanelsStart,activePanelsEnd,miniViewer,style,i,element;if('horizontal'==strip.scroll){panelSize=strip.panelWidth;}else{panelSize=strip.panelHeight;}activePanelsStart=Math.ceil(viewerSize/panelSize)+5;activePanelsEnd=Math.ceil((Math.abs(scroll)+viewerSize)/panelSize)+1;activePanelsStart=activePanelsEnd-activePanelsStart;activePanelsStart=activePanelsStart<0?0:activePanelsStart;for(i=activePanelsStart;i1?args[1].springStiffness:5.0,/**\n * Animation duration per spring.\n * @member {Number} animationTime\n * @memberof OpenSeadragon.Spring#\n */animationTime:args.length>1?args[1].animationTime:1.5};}$.console.assert(typeof options.springStiffness===\"number\"&&options.springStiffness!==0,\"[OpenSeadragon.Spring] options.springStiffness must be a non-zero number\");$.console.assert(typeof options.animationTime===\"number\"&&options.animationTime>=0,\"[OpenSeadragon.Spring] options.animationTime must be a number greater than or equal to 0\");if(options.exponential){this._exponential=true;delete options.exponential;}$.extend(true,this,options);/**\n * @member {Object} current\n * @memberof OpenSeadragon.Spring#\n * @property {Number} value\n * @property {Number} time\n */this.current={value:typeof this.initial==\"number\"?this.initial:this._exponential?0:1,time:$.now()// always work in milliseconds\n};$.console.assert(!this._exponential||this.current.value!==0,\"[OpenSeadragon.Spring] value must be non-zero for exponential springs\");/**\n * @member {Object} start\n * @memberof OpenSeadragon.Spring#\n * @property {Number} value\n * @property {Number} time\n */this.start={value:this.current.value,time:this.current.time};/**\n * @member {Object} target\n * @memberof OpenSeadragon.Spring#\n * @property {Number} value\n * @property {Number} time\n */this.target={value:this.current.value,time:this.current.time};if(this._exponential){this.start._logValue=Math.log(this.start.value);this.target._logValue=Math.log(this.target.value);this.current._logValue=Math.log(this.current.value);}};/** @lends OpenSeadragon.Spring.prototype */$.Spring.prototype={/**\n * @function\n * @param {Number} target\n */resetTo:function resetTo(target){$.console.assert(!this._exponential||target!==0,\"[OpenSeadragon.Spring.resetTo] target must be non-zero for exponential springs\");this.start.value=this.target.value=this.current.value=target;this.start.time=this.target.time=this.current.time=$.now();if(this._exponential){this.start._logValue=Math.log(this.start.value);this.target._logValue=Math.log(this.target.value);this.current._logValue=Math.log(this.current.value);}},/**\n * @function\n * @param {Number} target\n */springTo:function springTo(target){$.console.assert(!this._exponential||target!==0,\"[OpenSeadragon.Spring.springTo] target must be non-zero for exponential springs\");this.start.value=this.current.value;this.start.time=this.current.time;this.target.value=target;this.target.time=this.start.time+1000*this.animationTime;if(this._exponential){this.start._logValue=Math.log(this.start.value);this.target._logValue=Math.log(this.target.value);}},/**\n * @function\n * @param {Number} delta\n */shiftBy:function shiftBy(delta){this.start.value+=delta;this.target.value+=delta;if(this._exponential){$.console.assert(this.target.value!==0&&this.start.value!==0,\"[OpenSeadragon.Spring.shiftBy] spring value must be non-zero for exponential springs\");this.start._logValue=Math.log(this.start.value);this.target._logValue=Math.log(this.target.value);}},setExponential:function setExponential(value){this._exponential=value;if(this._exponential){$.console.assert(this.current.value!==0&&this.target.value!==0&&this.start.value!==0,\"[OpenSeadragon.Spring.setExponential] spring value must be non-zero for exponential springs\");this.start._logValue=Math.log(this.start.value);this.target._logValue=Math.log(this.target.value);this.current._logValue=Math.log(this.current.value);}},/**\n * @function\n * @returns true if the value got updated, false otherwise\n */update:function update(){this.current.time=$.now();var startValue,targetValue;if(this._exponential){startValue=this.start._logValue;targetValue=this.target._logValue;}else{startValue=this.start.value;targetValue=this.target.value;}var currentValue=this.current.time>=this.target.time?targetValue:startValue+(targetValue-startValue)*transform(this.springStiffness,(this.current.time-this.start.time)/(this.target.time-this.start.time));var oldValue=this.current.value;if(this._exponential){this.current.value=Math.exp(currentValue);}else{this.current.value=currentValue;}return oldValue!=this.current.value;},/**\n * Returns whether the spring is at the target value\n * @function\n * @returns {Boolean} True if at target value, false otherwise\n */isAtTargetValue:function isAtTargetValue(){return this.current.value===this.target.value;}};/**\n * @private\n */function transform(stiffness,x){return(1.0-Math.exp(stiffness*-x))/(1.0-Math.exp(-stiffness));}})(OpenSeadragon);/*\n * OpenSeadragon - ImageLoader\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * @private\n * @class ImageJob\n * @classdesc Handles downloading of a single image.\n * @param {Object} options - Options for this ImageJob.\n * @param {String} [options.src] - URL of image to download.\n * @param {String} [options.loadWithAjax] - Whether to load this image with AJAX.\n * @param {String} [options.ajaxHeaders] - Headers to add to the image request if using AJAX.\n * @param {String} [options.crossOriginPolicy] - CORS policy to use for downloads\n * @param {Function} [options.callback] - Called once image has been downloaded.\n * @param {Function} [options.abort] - Called when this image job is aborted.\n * @param {Number} [options.timeout] - The max number of milliseconds that this image job may take to complete.\n */function ImageJob(options){$.extend(true,this,{timeout:$.DEFAULT_SETTINGS.timeout,makeAjaxRequest:$.makeAjaxRequest,jobId:null},options);/**\n * Image object which will contain downloaded image.\n * @member {Image} image\n * @memberof OpenSeadragon.ImageJob#\n */this.image=null;}ImageJob.prototype={errorMsg:null,/**\n * Starts the image job.\n * @method\n */start:function start(){var self=this;var selfAbort=this.abort;this.image=new Image();this.image.onload=function(){self.finish(true);};this.image.onabort=this.image.onerror=function(){self.errorMsg=\"Image load aborted\";self.finish(false);};this.jobId=window.setTimeout(function(){self.errorMsg=\"Image load exceeded timeout\";self.finish(false);},this.timeout);// Load the tile with an AJAX request if the loadWithAjax option is\n// set. Otherwise load the image by setting the source proprety of the image object.\nif(this.loadWithAjax){this.request=self.makeAjaxRequest({url:this.src,withCredentials:this.ajaxWithCredentials,headers:this.ajaxHeaders,responseType:\"arraybuffer\",success:function success(request){var blb;// Make the raw data into a blob.\n// BlobBuilder fallback adapted from\n// http://stackoverflow.com/questions/15293694/blob-constructor-browser-compatibility\ntry{// Store the original response\nself.image._array=request.response;blb=new window.Blob([request.response]);}catch(e){var BlobBuilder=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder||window.MSBlobBuilder;if(e.name==='TypeError'&&BlobBuilder){var bb=new BlobBuilder();bb.append(request.response);blb=bb.getBlob();}}// If the blob is empty for some reason consider the image load a failure.\nif(blb===undefined||blb.size===0){self.errorMsg=\"Empty image response.\";self.finish(false);}// Create a URL for the blob data and make it the source of the image object.\n// This will still trigger Image.onload to indicate a successful tile load.\nvar url=(window.URL||window.webkitURL).createObjectURL(blb);self.image.src=url;},error:function error(request){self.errorMsg=\"Image load aborted - XHR error\";self.finish(false);}});// Provide a function to properly abort the request.\nthis.abort=function(){self.request.abort();// Call the existing abort function if available\nif(typeof selfAbort===\"function\"){selfAbort();}};}else{if(this.crossOriginPolicy!==false){this.image.crossOrigin=this.crossOriginPolicy;}this.image.src=this.src;}},finish:function finish(successful){this.image.onload=this.image.onerror=this.image.onabort=null;if(!successful){this.image=null;}if(this.jobId){window.clearTimeout(this.jobId);}this.callback(this);}};/**\n * @class ImageLoader\n * @memberof OpenSeadragon\n * @classdesc Handles downloading of a set of images using asynchronous queue pattern.\n * You generally won't have to interact with the ImageLoader directly.\n * @param {Object} options - Options for this ImageLoader.\n * @param {Number} [options.jobLimit] - The number of concurrent image requests. See imageLoaderLimit in {@link OpenSeadragon.Options} for details.\n * @param {Number} [options.timeout] - The max number of milliseconds that an image job may take to complete.\n */$.ImageLoader=function(options){$.extend(true,this,{jobLimit:$.DEFAULT_SETTINGS.imageLoaderLimit,timeout:$.DEFAULT_SETTINGS.timeout,jobQueue:[],jobsInProgress:0},options);};/** @lends OpenSeadragon.ImageLoader.prototype */$.ImageLoader.prototype={/**\n * Add an unloaded image to the loader queue.\n * @method\n * @param {Object} options - Options for this job.\n * @param {String} [options.src] - URL of image to download.\n * @param {String} [options.loadWithAjax] - Whether to load this image with AJAX.\n * @param {String} [options.ajaxHeaders] - Headers to add to the image request if using AJAX.\n * @param {String|Boolean} [options.crossOriginPolicy] - CORS policy to use for downloads\n * @param {Boolean} [options.ajaxWithCredentials] - Whether to set withCredentials on AJAX\n * requests.\n * @param {Function} [options.callback] - Called once image has been downloaded.\n * @param {Function} [options.abort] - Called when this image job is aborted.\n */addJob:function addJob(options){var _this=this,complete=function complete(job){completeJob(_this,job,options.callback);},jobOptions={src:options.src,loadWithAjax:options.loadWithAjax,ajaxHeaders:options.loadWithAjax?options.ajaxHeaders:null,crossOriginPolicy:options.crossOriginPolicy,ajaxWithCredentials:options.ajaxWithCredentials,makeAjaxRequest:options.makeAjaxRequest,callback:complete,abort:options.abort,timeout:this.timeout},newJob=new ImageJob(jobOptions);if(!this.jobLimit||this.jobsInProgress0){nextJob=loader.jobQueue.shift();nextJob.start();loader.jobsInProgress++;}callback(job.image,job.errorMsg,job.request);}})(OpenSeadragon);/*\n * OpenSeadragon - Tile\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * @class Tile\n * @memberof OpenSeadragon\n * @param {Number} level The zoom level this tile belongs to.\n * @param {Number} x The vector component 'x'.\n * @param {Number} y The vector component 'y'.\n * @param {OpenSeadragon.Rect} bounds Where this tile fits, in normalized\n * coordinates.\n * @param {Boolean} exists Is this tile a part of a sparse image? ( Also has\n * this tile failed to load? )\n * @param {String} url The URL of this tile's image.\n * @param {CanvasRenderingContext2D} context2D The context2D of this tile if it\n * is provided directly by the tile source.\n * @param {Boolean} loadWithAjax Whether this tile image should be loaded with an AJAX request .\n * @param {Object} ajaxHeaders The headers to send with this tile's AJAX request (if applicable).\n * @param {OpenSeadragon.Rect} sourceBounds The portion of the tile to use as the source of the\n * drawing operation, in pixels. Note that this only works when drawing with canvas; when drawing\n * with HTML the entire tile is always used.\n */$.Tile=function(level,x,y,bounds,exists,url,context2D,loadWithAjax,ajaxHeaders,sourceBounds){/**\n * The zoom level this tile belongs to.\n * @member {Number} level\n * @memberof OpenSeadragon.Tile#\n */this.level=level;/**\n * The vector component 'x'.\n * @member {Number} x\n * @memberof OpenSeadragon.Tile#\n */this.x=x;/**\n * The vector component 'y'.\n * @member {Number} y\n * @memberof OpenSeadragon.Tile#\n */this.y=y;/**\n * Where this tile fits, in normalized coordinates\n * @member {OpenSeadragon.Rect} bounds\n * @memberof OpenSeadragon.Tile#\n */this.bounds=bounds;/**\n * The portion of the tile to use as the source of the drawing operation, in pixels. Note that\n * this only works when drawing with canvas; when drawing with HTML the entire tile is always used.\n * @member {OpenSeadragon.Rect} sourceBounds\n * @memberof OpenSeadragon.Tile#\n */this.sourceBounds=sourceBounds;/**\n * Is this tile a part of a sparse image? Also has this tile failed to load?\n * @member {Boolean} exists\n * @memberof OpenSeadragon.Tile#\n */this.exists=exists;/**\n * The URL of this tile's image.\n * @member {String} url\n * @memberof OpenSeadragon.Tile#\n */this.url=url;/**\n * The context2D of this tile if it is provided directly by the tile source.\n * @member {CanvasRenderingContext2D} context2D\n * @memberOf OpenSeadragon.Tile#\n */this.context2D=context2D;/**\n * Whether to load this tile's image with an AJAX request.\n * @member {Boolean} loadWithAjax\n * @memberof OpenSeadragon.Tile#\n */this.loadWithAjax=loadWithAjax;/**\n * The headers to be used in requesting this tile's image.\n * Only used if loadWithAjax is set to true.\n * @member {Object} ajaxHeaders\n * @memberof OpenSeadragon.Tile#\n */this.ajaxHeaders=ajaxHeaders;/**\n * The unique cache key for this tile.\n * @member {String} cacheKey\n * @memberof OpenSeadragon.Tile#\n */if(this.ajaxHeaders){this.cacheKey=this.url+\"+\"+JSON.stringify(this.ajaxHeaders);}else{this.cacheKey=this.url;}/**\n * Is this tile loaded?\n * @member {Boolean} loaded\n * @memberof OpenSeadragon.Tile#\n */this.loaded=false;/**\n * Is this tile loading?\n * @member {Boolean} loading\n * @memberof OpenSeadragon.Tile#\n */this.loading=false;/**\n * The HTML div element for this tile\n * @member {Element} element\n * @memberof OpenSeadragon.Tile#\n */this.element=null;/**\n * The HTML img element for this tile.\n * @member {Element} imgElement\n * @memberof OpenSeadragon.Tile#\n */this.imgElement=null;/**\n * The Image object for this tile.\n * @member {Object} image\n * @memberof OpenSeadragon.Tile#\n */this.image=null;/**\n * The alias of this.element.style.\n * @member {String} style\n * @memberof OpenSeadragon.Tile#\n */this.style=null;/**\n * This tile's position on screen, in pixels.\n * @member {OpenSeadragon.Point} position\n * @memberof OpenSeadragon.Tile#\n */this.position=null;/**\n * This tile's size on screen, in pixels.\n * @member {OpenSeadragon.Point} size\n * @memberof OpenSeadragon.Tile#\n */this.size=null;/**\n * The start time of this tile's blending.\n * @member {Number} blendStart\n * @memberof OpenSeadragon.Tile#\n */this.blendStart=null;/**\n * The current opacity this tile should be.\n * @member {Number} opacity\n * @memberof OpenSeadragon.Tile#\n */this.opacity=null;/**\n * The squared distance of this tile to the viewport center.\n * Use for comparing tiles.\n * @private\n * @member {Number} squaredDistance\n * @memberof OpenSeadragon.Tile#\n */this.squaredDistance=null;/**\n * The visibility score of this tile.\n * @member {Number} visibility\n * @memberof OpenSeadragon.Tile#\n */this.visibility=null;/**\n * Whether this tile is currently being drawn.\n * @member {Boolean} beingDrawn\n * @memberof OpenSeadragon.Tile#\n */this.beingDrawn=false;/**\n * Timestamp the tile was last touched.\n * @member {Number} lastTouchTime\n * @memberof OpenSeadragon.Tile#\n */this.lastTouchTime=0;/**\n * Whether this tile is in the right-most column for its level.\n * @member {Boolean} isRightMost\n * @memberof OpenSeadragon.Tile#\n */this.isRightMost=false;/**\n * Whether this tile is in the bottom-most row for its level.\n * @member {Boolean} isBottomMost\n * @memberof OpenSeadragon.Tile#\n */this.isBottomMost=false;};/** @lends OpenSeadragon.Tile.prototype */$.Tile.prototype={/**\n * Provides a string representation of this tiles level and (x,y)\n * components.\n * @function\n * @returns {String}\n */toString:function toString(){return this.level+\"/\"+this.x+\"_\"+this.y;},// private\n_hasTransparencyChannel:function _hasTransparencyChannel(){return!!this.context2D||this.url.match('.png');},/**\n * Renders the tile in an html container.\n * @function\n * @param {Element} container\n */drawHTML:function drawHTML(container){if(!this.cacheImageRecord){$.console.warn('[Tile.drawHTML] attempting to draw tile %s when it\\'s not cached',this.toString());return;}if(!this.loaded){$.console.warn(\"Attempting to draw tile %s when it's not yet loaded.\",this.toString());return;}//EXPERIMENTAL - trying to figure out how to scale the container\n// content during animation of the container size.\nif(!this.element){this.element=$.makeNeutralElement(\"div\");this.imgElement=this.cacheImageRecord.getImage().cloneNode();this.imgElement.style.msInterpolationMode=\"nearest-neighbor\";this.imgElement.style.width=\"100%\";this.imgElement.style.height=\"100%\";this.style=this.element.style;this.style.position=\"absolute\";}if(this.element.parentNode!=container){container.appendChild(this.element);}if(this.imgElement.parentNode!=this.element){this.element.appendChild(this.imgElement);}this.style.top=this.position.y+\"px\";this.style.left=this.position.x+\"px\";this.style.height=this.size.y+\"px\";this.style.width=this.size.x+\"px\";$.setElementOpacity(this.element,this.opacity);},/**\n * Renders the tile in a canvas-based context.\n * @function\n * @param {Canvas} context\n * @param {Function} drawingHandler - Method for firing the drawing event.\n * drawingHandler({context, tile, rendered})\n * where rendered
is the context with the pre-drawn image.\n * @param {Number} [scale=1] - Apply a scale to position and size\n * @param {OpenSeadragon.Point} [translate] - A translation vector\n */drawCanvas:function drawCanvas(context,drawingHandler,scale,translate){var position=this.position.times($.pixelDensityRatio),size=this.size.times($.pixelDensityRatio),rendered;if(!this.context2D&&!this.cacheImageRecord){$.console.warn('[Tile.drawCanvas] attempting to draw tile %s when it\\'s not cached',this.toString());return;}rendered=this.context2D||this.cacheImageRecord.getRenderedContext();if(!this.loaded||!rendered){$.console.warn(\"Attempting to draw tile %s when it's not yet loaded.\",this.toString());return;}context.save();context.globalAlpha=this.opacity;if(typeof scale==='number'&&scale!==1){// draw tile at a different scale\nposition=position.times(scale);size=size.times(scale);}if(translate instanceof $.Point){// shift tile position slightly\nposition=position.plus(translate);}//if we are supposed to be rendering fully opaque rectangle,\n//ie its done fading or fading is turned off, and if we are drawing\n//an image with an alpha channel, then the only way\n//to avoid seeing the tile underneath is to clear the rectangle\nif(context.globalAlpha===1&&this._hasTransparencyChannel()){//clearing only the inside of the rectangle occupied\n//by the png prevents edge flikering\ncontext.clearRect(position.x,position.y,size.x,size.y);}// This gives the application a chance to make image manipulation\n// changes as we are rendering the image\ndrawingHandler({context:context,tile:this,rendered:rendered});var sourceWidth,sourceHeight;if(this.sourceBounds){sourceWidth=Math.min(this.sourceBounds.width,rendered.canvas.width);sourceHeight=Math.min(this.sourceBounds.height,rendered.canvas.height);}else{sourceWidth=rendered.canvas.width;sourceHeight=rendered.canvas.height;}context.drawImage(rendered.canvas,0,0,sourceWidth,sourceHeight,position.x,position.y,size.x,size.y);context.restore();},/**\n * Get the ratio between current and original size.\n * @function\n * @return {Float}\n */getScaleForEdgeSmoothing:function getScaleForEdgeSmoothing(){var context;if(this.cacheImageRecord){context=this.cacheImageRecord.getRenderedContext();}else if(this.context2D){context=this.context2D;}else{$.console.warn('[Tile.drawCanvas] attempting to get tile scale %s when tile\\'s not cached',this.toString());return 1;}return context.canvas.width/(this.size.x*$.pixelDensityRatio);},/**\n * Get a translation vector that when applied to the tile position produces integer coordinates.\n * Needed to avoid swimming and twitching.\n * @function\n * @param {Number} [scale=1] - Scale to be applied to position.\n * @return {OpenSeadragon.Point}\n */getTranslationForEdgeSmoothing:function getTranslationForEdgeSmoothing(scale,canvasSize,sketchCanvasSize){// The translation vector must have positive values, otherwise the image goes a bit off\n// the sketch canvas to the top and left and we must use negative coordinates to repaint it\n// to the main canvas. In that case, some browsers throw:\n// INDEX_SIZE_ERR: DOM Exception 1: Index or size was negative, or greater than the allowed value.\nvar x=Math.max(1,Math.ceil((sketchCanvasSize.x-canvasSize.x)/2));var y=Math.max(1,Math.ceil((sketchCanvasSize.y-canvasSize.y)/2));return new $.Point(x,y).minus(this.position.times($.pixelDensityRatio).times(scale||1).apply(function(x){return x%1;}));},/**\n * Removes tile from its container.\n * @function\n */unload:function unload(){if(this.imgElement&&this.imgElement.parentNode){this.imgElement.parentNode.removeChild(this.imgElement);}if(this.element&&this.element.parentNode){this.element.parentNode.removeChild(this.element);}this.element=null;this.imgElement=null;this.loaded=false;this.loading=false;}};})(OpenSeadragon);/*\n * OpenSeadragon - Overlay\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * An enumeration of positions that an overlay may be assigned relative to\n * the viewport.\n * It is identical to OpenSeadragon.Placement but is kept for backward\n * compatibility.\n * @member OverlayPlacement\n * @memberof OpenSeadragon\n * @see OpenSeadragon.Placement\n * @static\n * @readonly\n * @type {Object}\n * @property {Number} CENTER\n * @property {Number} TOP_LEFT\n * @property {Number} TOP\n * @property {Number} TOP_RIGHT\n * @property {Number} RIGHT\n * @property {Number} BOTTOM_RIGHT\n * @property {Number} BOTTOM\n * @property {Number} BOTTOM_LEFT\n * @property {Number} LEFT\n */$.OverlayPlacement=$.Placement;/**\n * An enumeration of possible ways to handle overlays rotation\n * @member OverlayRotationMode\n * @memberOf OpenSeadragon\n * @static\n * @readonly\n * @property {Number} NO_ROTATION The overlay ignore the viewport rotation.\n * @property {Number} EXACT The overlay use CSS 3 transforms to rotate with\n * the viewport. If the overlay contains text, it will get rotated as well.\n * @property {Number} BOUNDING_BOX The overlay adjusts for rotation by\n * taking the size of the bounding box of the rotated bounds.\n * Only valid for overlays with Rect location and scalable in both directions.\n */$.OverlayRotationMode=$.freezeObject({NO_ROTATION:1,EXACT:2,BOUNDING_BOX:3});/**\n * @class Overlay\n * @classdesc Provides a way to float an HTML element on top of the viewer element.\n *\n * @memberof OpenSeadragon\n * @param {Object} options\n * @param {Element} options.element\n * @param {OpenSeadragon.Point|OpenSeadragon.Rect} options.location - The\n * location of the overlay on the image. If a {@link OpenSeadragon.Point}\n * is specified, the overlay will be located at this location with respect\n * to the placement option. If a {@link OpenSeadragon.Rect} is specified,\n * the overlay will be placed at this location with the corresponding width\n * and height and placement TOP_LEFT.\n * @param {OpenSeadragon.Placement} [options.placement=OpenSeadragon.Placement.TOP_LEFT]\n * Defines what part of the overlay should be at the specified options.location\n * @param {OpenSeadragon.Overlay.OnDrawCallback} [options.onDraw]\n * @param {Boolean} [options.checkResize=true] Set to false to avoid to\n * check the size of the overlay everytime it is drawn in the directions\n * which are not scaled. It will improve performances but will cause a\n * misalignment if the overlay size changes.\n * @param {Number} [options.width] The width of the overlay in viewport\n * coordinates. If specified, the width of the overlay will be adjusted when\n * the zoom changes.\n * @param {Number} [options.height] The height of the overlay in viewport\n * coordinates. If specified, the height of the overlay will be adjusted when\n * the zoom changes.\n * @param {Boolean} [options.rotationMode=OpenSeadragon.OverlayRotationMode.EXACT]\n * How to handle the rotation of the viewport.\n */$.Overlay=function(element,location,placement){/**\n * onDraw callback signature used by {@link OpenSeadragon.Overlay}.\n *\n * @callback OnDrawCallback\n * @memberof OpenSeadragon.Overlay\n * @param {OpenSeadragon.Point} position\n * @param {OpenSeadragon.Point} size\n * @param {Element} element\n */var options;if($.isPlainObject(element)){options=element;}else{options={element:element,location:location,placement:placement};}this.element=options.element;this.style=options.element.style;this._init(options);};/** @lends OpenSeadragon.Overlay.prototype */$.Overlay.prototype={// private\n_init:function _init(options){this.location=options.location;this.placement=options.placement===undefined?$.Placement.TOP_LEFT:options.placement;this.onDraw=options.onDraw;this.checkResize=options.checkResize===undefined?true:options.checkResize;// When this.width is not null, the overlay get scaled horizontally\nthis.width=options.width===undefined?null:options.width;// When this.height is not null, the overlay get scaled vertically\nthis.height=options.height===undefined?null:options.height;this.rotationMode=options.rotationMode||$.OverlayRotationMode.EXACT;// Having a rect as location is a syntactic sugar\nif(this.location instanceof $.Rect){this.width=this.location.width;this.height=this.location.height;this.location=this.location.getTopLeft();this.placement=$.Placement.TOP_LEFT;}// Deprecated properties kept for backward compatibility.\nthis.scales=this.width!==null&&this.height!==null;this.bounds=new $.Rect(this.location.x,this.location.y,this.width,this.height);this.position=this.location;},/**\n * Internal function to adjust the position of an overlay\n * depending on it size and placement.\n * @function\n * @param {OpenSeadragon.Point} position\n * @param {OpenSeadragon.Point} size\n */adjust:function adjust(position,size){var properties=$.Placement.properties[this.placement];if(!properties){return;}if(properties.isHorizontallyCentered){position.x-=size.x/2;}else if(properties.isRight){position.x-=size.x;}if(properties.isVerticallyCentered){position.y-=size.y/2;}else if(properties.isBottom){position.y-=size.y;}},/**\n * @function\n */destroy:function destroy(){var element=this.element;var style=this.style;if(element.parentNode){element.parentNode.removeChild(element);//this should allow us to preserve overlays when required between\n//pages\nif(element.prevElementParent){style.display='none';//element.prevElementParent.insertBefore(\n// element,\n// element.prevNextSibling\n//);\ndocument.body.appendChild(element);}}// clear the onDraw callback\nthis.onDraw=null;style.top=\"\";style.left=\"\";style.position=\"\";if(this.width!==null){style.width=\"\";}if(this.height!==null){style.height=\"\";}var transformOriginProp=$.getCssPropertyWithVendorPrefix('transformOrigin');var transformProp=$.getCssPropertyWithVendorPrefix('transform');if(transformOriginProp&&transformProp){style[transformOriginProp]=\"\";style[transformProp]=\"\";}},/**\n * @function\n * @param {Element} container\n */drawHTML:function drawHTML(container,viewport){var element=this.element;if(element.parentNode!==container){//save the source parent for later if we need it\nelement.prevElementParent=element.parentNode;element.prevNextSibling=element.nextSibling;container.appendChild(element);// have to set position before calculating size, fix #1116\nthis.style.position=\"absolute\";// this.size is used by overlays which don't get scaled in at\n// least one direction when this.checkResize is set to false.\nthis.size=$.getElementSize(element);}var positionAndSize=this._getOverlayPositionAndSize(viewport);var position=positionAndSize.position;var size=this.size=positionAndSize.size;var rotate=positionAndSize.rotate;// call the onDraw callback if it exists to allow one to overwrite\n// the drawing/positioning/sizing of the overlay\nif(this.onDraw){this.onDraw(position,size,this.element);}else{var style=this.style;style.left=position.x+\"px\";style.top=position.y+\"px\";if(this.width!==null){style.width=size.x+\"px\";}if(this.height!==null){style.height=size.y+\"px\";}var transformOriginProp=$.getCssPropertyWithVendorPrefix('transformOrigin');var transformProp=$.getCssPropertyWithVendorPrefix('transform');if(transformOriginProp&&transformProp){if(rotate){style[transformOriginProp]=this._getTransformOrigin();style[transformProp]=\"rotate(\"+rotate+\"deg)\";}else{style[transformOriginProp]=\"\";style[transformProp]=\"\";}}if(style.display!=='none'){style.display='block';}}},// private\n_getOverlayPositionAndSize:function _getOverlayPositionAndSize(viewport){var position=viewport.pixelFromPoint(this.location,true);var size=this._getSizeInPixels(viewport);this.adjust(position,size);var rotate=0;if(viewport.degrees&&this.rotationMode!==$.OverlayRotationMode.NO_ROTATION){// BOUNDING_BOX is only valid if both directions get scaled.\n// Get replaced by EXACT otherwise.\nif(this.rotationMode===$.OverlayRotationMode.BOUNDING_BOX&&this.width!==null&&this.height!==null){var rect=new $.Rect(position.x,position.y,size.x,size.y);var boundingBox=this._getBoundingBox(rect,viewport.degrees);position=boundingBox.getTopLeft();size=boundingBox.getSize();}else{rotate=viewport.degrees;}}return{position:position,size:size,rotate:rotate};},// private\n_getSizeInPixels:function _getSizeInPixels(viewport){var width=this.size.x;var height=this.size.y;if(this.width!==null||this.height!==null){var scaledSize=viewport.deltaPixelsFromPointsNoRotate(new $.Point(this.width||0,this.height||0),true);if(this.width!==null){width=scaledSize.x;}if(this.height!==null){height=scaledSize.y;}}if(this.checkResize&&(this.width===null||this.height===null)){var eltSize=this.size=$.getElementSize(this.element);if(this.width===null){width=eltSize.x;}if(this.height===null){height=eltSize.y;}}return new $.Point(width,height);},// private\n_getBoundingBox:function _getBoundingBox(rect,degrees){var refPoint=this._getPlacementPoint(rect);return rect.rotate(degrees,refPoint).getBoundingBox();},// private\n_getPlacementPoint:function _getPlacementPoint(rect){var result=new $.Point(rect.x,rect.y);var properties=$.Placement.properties[this.placement];if(properties){if(properties.isHorizontallyCentered){result.x+=rect.width/2;}else if(properties.isRight){result.x+=rect.width;}if(properties.isVerticallyCentered){result.y+=rect.height/2;}else if(properties.isBottom){result.y+=rect.height;}}return result;},// private\n_getTransformOrigin:function _getTransformOrigin(){var result=\"\";var properties=$.Placement.properties[this.placement];if(!properties){return result;}if(properties.isLeft){result=\"left\";}else if(properties.isRight){result=\"right\";}if(properties.isTop){result+=\" top\";}else if(properties.isBottom){result+=\" bottom\";}return result;},/**\n * Changes the overlay settings.\n * @function\n * @param {OpenSeadragon.Point|OpenSeadragon.Rect|Object} location\n * If an object is specified, the options are the same than the constructor\n * except for the element which can not be changed.\n * @param {OpenSeadragon.Placement} placement\n */update:function update(location,placement){var options=$.isPlainObject(location)?location:{location:location,placement:placement};this._init({location:options.location||this.location,placement:options.placement!==undefined?options.placement:this.placement,onDraw:options.onDraw||this.onDraw,checkResize:options.checkResize||this.checkResize,width:options.width!==undefined?options.width:this.width,height:options.height!==undefined?options.height:this.height,rotationMode:options.rotationMode||this.rotationMode});},/**\n * Returns the current bounds of the overlay in viewport coordinates\n * @function\n * @param {OpenSeadragon.Viewport} viewport the viewport\n * @returns {OpenSeadragon.Rect} overlay bounds\n */getBounds:function getBounds(viewport){$.console.assert(viewport,'A viewport must now be passed to Overlay.getBounds.');var width=this.width;var height=this.height;if(width===null||height===null){var size=viewport.deltaPointsFromPixelsNoRotate(this.size,true);if(width===null){width=size.x;}if(height===null){height=size.y;}}var location=this.location.clone();this.adjust(location,new $.Point(width,height));return this._adjustBoundsForRotation(viewport,new $.Rect(location.x,location.y,width,height));},// private\n_adjustBoundsForRotation:function _adjustBoundsForRotation(viewport,bounds){if(!viewport||viewport.degrees===0||this.rotationMode===$.OverlayRotationMode.EXACT){return bounds;}if(this.rotationMode===$.OverlayRotationMode.BOUNDING_BOX){// If overlay not fully scalable, BOUNDING_BOX falls back to EXACT\nif(this.width===null||this.height===null){return bounds;}// It is easier to just compute the position and size and\n// convert to viewport coordinates.\nvar positionAndSize=this._getOverlayPositionAndSize(viewport);return viewport.viewerElementToViewportRectangle(new $.Rect(positionAndSize.position.x,positionAndSize.position.y,positionAndSize.size.x,positionAndSize.size.y));}// NO_ROTATION case\nreturn bounds.rotate(-viewport.degrees,this._getPlacementPoint(bounds));}};})(OpenSeadragon);/*\n * OpenSeadragon - Drawer\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * @class Drawer\n * @memberof OpenSeadragon\n * @classdesc Handles rendering of tiles for an {@link OpenSeadragon.Viewer}.\n * @param {Object} options - Options for this Drawer.\n * @param {OpenSeadragon.Viewer} options.viewer - The Viewer that owns this Drawer.\n * @param {OpenSeadragon.Viewport} options.viewport - Reference to Viewer viewport.\n * @param {Element} options.element - Parent element.\n * @param {Number} [options.debugGridColor] - See debugGridColor in {@link OpenSeadragon.Options} for details.\n */$.Drawer=function(options){$.console.assert(options.viewer,\"[Drawer] options.viewer is required\");//backward compatibility for positional args while prefering more\n//idiomatic javascript options object as the only argument\nvar args=arguments;if(!$.isPlainObject(options)){options={source:args[0],// Reference to Viewer tile source.\nviewport:args[1],// Reference to Viewer viewport.\nelement:args[2]// Parent element.\n};}$.console.assert(options.viewport,\"[Drawer] options.viewport is required\");$.console.assert(options.element,\"[Drawer] options.element is required\");if(options.source){$.console.error(\"[Drawer] options.source is no longer accepted; use TiledImage instead\");}this.viewer=options.viewer;this.viewport=options.viewport;this.debugGridColor=typeof options.debugGridColor==='string'?[options.debugGridColor]:options.debugGridColor||$.DEFAULT_SETTINGS.debugGridColor;if(options.opacity){$.console.error(\"[Drawer] options.opacity is no longer accepted; set the opacity on the TiledImage instead\");}this.useCanvas=$.supportsCanvas&&(this.viewer?this.viewer.useCanvas:true);/**\n * The parent element of this Drawer instance, passed in when the Drawer was created.\n * The parent of {@link OpenSeadragon.Drawer#canvas}.\n * @member {Element} container\n * @memberof OpenSeadragon.Drawer#\n */this.container=$.getElement(options.element);/**\n * A <canvas> element if the browser supports them, otherwise a <div> element.\n * Child element of {@link OpenSeadragon.Drawer#container}.\n * @member {Element} canvas\n * @memberof OpenSeadragon.Drawer#\n */this.canvas=$.makeNeutralElement(this.useCanvas?\"canvas\":\"div\");/**\n * 2d drawing context for {@link OpenSeadragon.Drawer#canvas} if it's a <canvas> element, otherwise null.\n * @member {Object} context\n * @memberof OpenSeadragon.Drawer#\n */this.context=this.useCanvas?this.canvas.getContext(\"2d\"):null;/**\n * Sketch canvas used to temporarily draw tiles which cannot be drawn directly\n * to the main canvas due to opacity. Lazily initialized.\n */this.sketchCanvas=null;this.sketchContext=null;/**\n * @member {Element} element\n * @memberof OpenSeadragon.Drawer#\n * @deprecated Alias for {@link OpenSeadragon.Drawer#container}.\n */this.element=this.container;// We force our container to ltr because our drawing math doesn't work in rtl.\n// This issue only affects our canvas renderer, but we do it always for consistency.\n// Note that this means overlays you want to be rtl need to be explicitly set to rtl.\nthis.container.dir='ltr';// check canvas available width and height, set canvas width and height such that the canvas backing store is set to the proper pixel density\nif(this.useCanvas){var viewportSize=this._calculateCanvasSize();this.canvas.width=viewportSize.x;this.canvas.height=viewportSize.y;}this.canvas.style.width=\"100%\";this.canvas.style.height=\"100%\";this.canvas.style.position=\"absolute\";$.setElementOpacity(this.canvas,this.opacity,true);// explicit left-align\nthis.container.style.textAlign=\"left\";this.container.appendChild(this.canvas);};/** @lends OpenSeadragon.Drawer.prototype */$.Drawer.prototype={// deprecated\naddOverlay:function addOverlay(element,location,placement,onDraw){$.console.error(\"drawer.addOverlay is deprecated. Use viewer.addOverlay instead.\");this.viewer.addOverlay(element,location,placement,onDraw);return this;},// deprecated\nupdateOverlay:function updateOverlay(element,location,placement){$.console.error(\"drawer.updateOverlay is deprecated. Use viewer.updateOverlay instead.\");this.viewer.updateOverlay(element,location,placement);return this;},// deprecated\nremoveOverlay:function removeOverlay(element){$.console.error(\"drawer.removeOverlay is deprecated. Use viewer.removeOverlay instead.\");this.viewer.removeOverlay(element);return this;},// deprecated\nclearOverlays:function clearOverlays(){$.console.error(\"drawer.clearOverlays is deprecated. Use viewer.clearOverlays instead.\");this.viewer.clearOverlays();return this;},/**\n * Set the opacity of the drawer.\n * @param {Number} opacity\n * @return {OpenSeadragon.Drawer} Chainable.\n */setOpacity:function setOpacity(opacity){$.console.error(\"drawer.setOpacity is deprecated. Use tiledImage.setOpacity instead.\");var world=this.viewer.world;for(var i=0;imaxOpacity){maxOpacity=opacity;}}return maxOpacity;},// deprecated\nneedsUpdate:function needsUpdate(){$.console.error(\"[Drawer.needsUpdate] this function is deprecated. Use World.needsDraw instead.\");return this.viewer.world.needsDraw();},// deprecated\nnumTilesLoaded:function numTilesLoaded(){$.console.error(\"[Drawer.numTilesLoaded] this function is deprecated. Use TileCache.numTilesLoaded instead.\");return this.viewer.tileCache.numTilesLoaded();},// deprecated\nreset:function reset(){$.console.error(\"[Drawer.reset] this function is deprecated. Use World.resetItems instead.\");this.viewer.world.resetItems();return this;},// deprecated\nupdate:function update(){$.console.error(\"[Drawer.update] this function is deprecated. Use Drawer.clear and World.draw instead.\");this.clear();this.viewer.world.draw();return this;},/**\n * @return {Boolean} True if rotation is supported.\n */canRotate:function canRotate(){return this.useCanvas;},/**\n * Destroy the drawer (unload current loaded tiles)\n */destroy:function destroy(){//force unloading of current canvas (1x1 will be gc later, trick not necessarily needed)\nthis.canvas.width=1;this.canvas.height=1;this.sketchCanvas=null;this.sketchContext=null;},/**\n * Clears the Drawer so it's ready to draw another frame.\n */clear:function clear(){this.canvas.innerHTML=\"\";if(this.useCanvas){var viewportSize=this._calculateCanvasSize();if(this.canvas.width!=viewportSize.x||this.canvas.height!=viewportSize.y){this.canvas.width=viewportSize.x;this.canvas.height=viewportSize.y;if(this.sketchCanvas!==null){var sketchCanvasSize=this._calculateSketchCanvasSize();this.sketchCanvas.width=sketchCanvasSize.x;this.sketchCanvas.height=sketchCanvasSize.y;}}this._clear();}},_clear:function _clear(useSketch,bounds){if(!this.useCanvas){return;}var context=this._getContext(useSketch);if(bounds){context.clearRect(bounds.x,bounds.y,bounds.width,bounds.height);}else{var canvas=context.canvas;context.clearRect(0,0,canvas.width,canvas.height);}},/**\n * Scale from OpenSeadragon viewer rectangle to drawer rectangle\n * (ignoring rotation)\n * @param {OpenSeadragon.Rect} rectangle - The rectangle in viewport coordinate system.\n * @return {OpenSeadragon.Rect} Rectangle in drawer coordinate system.\n */viewportToDrawerRectangle:function viewportToDrawerRectangle(rectangle){var topLeft=this.viewport.pixelFromPointNoRotate(rectangle.getTopLeft(),true);var size=this.viewport.deltaPixelsFromPointsNoRotate(rectangle.getSize(),true);return new $.Rect(topLeft.x*$.pixelDensityRatio,topLeft.y*$.pixelDensityRatio,size.x*$.pixelDensityRatio,size.y*$.pixelDensityRatio);},/**\n * Draws the given tile.\n * @param {OpenSeadragon.Tile} tile - The tile to draw.\n * @param {Function} drawingHandler - Method for firing the drawing event if using canvas.\n * drawingHandler({context, tile, rendered})\n * @param {Boolean} useSketch - Whether to use the sketch canvas or not.\n * where rendered
is the context with the pre-drawn image.\n * @param {Float} [scale=1] - Apply a scale to tile position and size. Defaults to 1.\n * @param {OpenSeadragon.Point} [translate] A translation vector to offset tile position\n */drawTile:function drawTile(tile,drawingHandler,useSketch,scale,translate){$.console.assert(tile,'[Drawer.drawTile] tile is required');$.console.assert(drawingHandler,'[Drawer.drawTile] drawingHandler is required');if(this.useCanvas){var context=this._getContext(useSketch);scale=scale||1;tile.drawCanvas(context,drawingHandler,scale,translate);}else{tile.drawHTML(this.canvas);}},_getContext:function _getContext(useSketch){var context=this.context;if(useSketch){if(this.sketchCanvas===null){this.sketchCanvas=document.createElement(\"canvas\");var sketchCanvasSize=this._calculateSketchCanvasSize();this.sketchCanvas.width=sketchCanvasSize.x;this.sketchCanvas.height=sketchCanvasSize.y;this.sketchContext=this.sketchCanvas.getContext(\"2d\");// If the viewport is not currently rotated, the sketchCanvas\n// will have the same size as the main canvas. However, if\n// the viewport get rotated later on, we will need to resize it.\nif(this.viewport.getRotation()===0){var self=this;this.viewer.addHandler('rotate',function resizeSketchCanvas(){if(self.viewport.getRotation()===0){return;}self.viewer.removeHandler('rotate',resizeSketchCanvas);var sketchCanvasSize=self._calculateSketchCanvasSize();self.sketchCanvas.width=sketchCanvasSize.x;self.sketchCanvas.height=sketchCanvasSize.y;});}}context=this.sketchContext;}return context;},// private\nsaveContext:function saveContext(useSketch){if(!this.useCanvas){return;}this._getContext(useSketch).save();},// private\nrestoreContext:function restoreContext(useSketch){if(!this.useCanvas){return;}this._getContext(useSketch).restore();},// private\nsetClip:function setClip(rect,useSketch){if(!this.useCanvas){return;}var context=this._getContext(useSketch);context.beginPath();context.rect(rect.x,rect.y,rect.width,rect.height);context.clip();},// private\ndrawRectangle:function drawRectangle(rect,fillStyle,useSketch){if(!this.useCanvas){return;}var context=this._getContext(useSketch);context.save();context.fillStyle=fillStyle;context.fillRect(rect.x,rect.y,rect.width,rect.height);context.restore();},/**\n * Blends the sketch canvas in the main canvas.\n * @param {Object} options The options\n * @param {Float} options.opacity The opacity of the blending.\n * @param {Float} [options.scale=1] The scale at which tiles were drawn on\n * the sketch. Default is 1.\n * Use scale to draw at a lower scale and then enlarge onto the main canvas.\n * @param {OpenSeadragon.Point} [options.translate] A translation vector\n * that was used to draw the tiles\n * @param {String} [options.compositeOperation] - How the image is\n * composited onto other images; see compositeOperation in\n * {@link OpenSeadragon.Options} for possible values.\n * @param {OpenSeadragon.Rect} [options.bounds] The part of the sketch\n * canvas to blend in the main canvas. If specified, options.scale and\n * options.translate get ignored.\n */blendSketch:function blendSketch(opacity,scale,translate,compositeOperation){var options=opacity;if(!$.isPlainObject(options)){options={opacity:opacity,scale:scale,translate:translate,compositeOperation:compositeOperation};}if(!this.useCanvas||!this.sketchCanvas){return;}opacity=options.opacity;compositeOperation=options.compositeOperation;var bounds=options.bounds;this.context.save();this.context.globalAlpha=opacity;if(compositeOperation){this.context.globalCompositeOperation=compositeOperation;}if(bounds){// Internet Explorer, Microsoft Edge, and Safari have problems\n// when you call context.drawImage with negative x or y\n// or x + width or y + height greater than the canvas width or height respectively.\nif(bounds.x<0){bounds.width+=bounds.x;bounds.x=0;}if(bounds.x+bounds.width>this.canvas.width){bounds.width=this.canvas.width-bounds.x;}if(bounds.y<0){bounds.height+=bounds.y;bounds.y=0;}if(bounds.y+bounds.height>this.canvas.height){bounds.height=this.canvas.height-bounds.y;}this.context.drawImage(this.sketchCanvas,bounds.x,bounds.y,bounds.width,bounds.height,bounds.x,bounds.y,bounds.width,bounds.height);}else{scale=options.scale||1;translate=options.translate;var position=translate instanceof $.Point?translate:new $.Point(0,0);var widthExt=0;var heightExt=0;if(translate){var widthDiff=this.sketchCanvas.width-this.canvas.width;var heightDiff=this.sketchCanvas.height-this.canvas.height;widthExt=Math.round(widthDiff/2);heightExt=Math.round(heightDiff/2);}this.context.drawImage(this.sketchCanvas,position.x-widthExt*scale,position.y-heightExt*scale,(this.canvas.width+2*widthExt)*scale,(this.canvas.height+2*heightExt)*scale,-widthExt,-heightExt,this.canvas.width+2*widthExt,this.canvas.height+2*heightExt);}this.context.restore();},// private\ndrawDebugInfo:function drawDebugInfo(tile,count,i,tiledImage){if(!this.useCanvas){return;}var colorIndex=this.viewer.world.getIndexOfItem(tiledImage)%this.debugGridColor.length;var context=this.context;context.save();context.lineWidth=2*$.pixelDensityRatio;context.font='small-caps bold '+13*$.pixelDensityRatio+'px arial';context.strokeStyle=this.debugGridColor[colorIndex];context.fillStyle=this.debugGridColor[colorIndex];if(this.viewport.degrees!==0){this._offsetForRotation({degrees:this.viewport.degrees});}else{if(this.viewer.viewport.flipped){this._flip();}}if(tiledImage.getRotation(true)%360!==0){this._offsetForRotation({degrees:tiledImage.getRotation(true),point:tiledImage.viewport.pixelFromPointNoRotate(tiledImage._getRotationPoint(true),true)});}context.strokeRect(tile.position.x*$.pixelDensityRatio,tile.position.y*$.pixelDensityRatio,tile.size.x*$.pixelDensityRatio,tile.size.y*$.pixelDensityRatio);var tileCenterX=(tile.position.x+tile.size.x/2)*$.pixelDensityRatio;var tileCenterY=(tile.position.y+tile.size.y/2)*$.pixelDensityRatio;// Rotate the text the right way around.\ncontext.translate(tileCenterX,tileCenterY);context.rotate(Math.PI/180*-this.viewport.degrees);context.translate(-tileCenterX,-tileCenterY);if(tile.x===0&&tile.y===0){context.fillText(\"Zoom: \"+this.viewport.getZoom(),tile.position.x*$.pixelDensityRatio,(tile.position.y-30)*$.pixelDensityRatio);context.fillText(\"Pan: \"+this.viewport.getBounds().toString(),tile.position.x*$.pixelDensityRatio,(tile.position.y-20)*$.pixelDensityRatio);}context.fillText(\"Level: \"+tile.level,(tile.position.x+10)*$.pixelDensityRatio,(tile.position.y+20)*$.pixelDensityRatio);context.fillText(\"Column: \"+tile.x,(tile.position.x+10)*$.pixelDensityRatio,(tile.position.y+30)*$.pixelDensityRatio);context.fillText(\"Row: \"+tile.y,(tile.position.x+10)*$.pixelDensityRatio,(tile.position.y+40)*$.pixelDensityRatio);context.fillText(\"Order: \"+i+\" of \"+count,(tile.position.x+10)*$.pixelDensityRatio,(tile.position.y+50)*$.pixelDensityRatio);context.fillText(\"Size: \"+tile.size.toString(),(tile.position.x+10)*$.pixelDensityRatio,(tile.position.y+60)*$.pixelDensityRatio);context.fillText(\"Position: \"+tile.position.toString(),(tile.position.x+10)*$.pixelDensityRatio,(tile.position.y+70)*$.pixelDensityRatio);if(this.viewport.degrees!==0){this._restoreRotationChanges();}if(tiledImage.getRotation(true)%360!==0){this._restoreRotationChanges();}context.restore();},// private\ndebugRect:function debugRect(rect){if(this.useCanvas){var context=this.context;context.save();context.lineWidth=2*$.pixelDensityRatio;context.strokeStyle=this.debugGridColor[0];context.fillStyle=this.debugGridColor[0];context.strokeRect(rect.x*$.pixelDensityRatio,rect.y*$.pixelDensityRatio,rect.width*$.pixelDensityRatio,rect.height*$.pixelDensityRatio);context.restore();}},/**\n * Get the canvas size\n * @param {Boolean} sketch If set to true return the size of the sketch canvas\n * @returns {OpenSeadragon.Point} The size of the canvas\n */getCanvasSize:function getCanvasSize(sketch){var canvas=this._getContext(sketch).canvas;return new $.Point(canvas.width,canvas.height);},getCanvasCenter:function getCanvasCenter(){return new $.Point(this.canvas.width/2,this.canvas.height/2);},// private\n_offsetForRotation:function _offsetForRotation(options){var point=options.point?options.point.times($.pixelDensityRatio):this.getCanvasCenter();var context=this._getContext(options.useSketch);context.save();context.translate(point.x,point.y);if(this.viewer.viewport.flipped){context.rotate(Math.PI/180*-options.degrees);context.scale(-1,1);}else{context.rotate(Math.PI/180*options.degrees);}context.translate(-point.x,-point.y);},// private\n_flip:function _flip(options){options=options||{};var point=options.point?options.point.times($.pixelDensityRatio):this.getCanvasCenter();var context=this._getContext(options.useSketch);context.translate(point.x,0);context.scale(-1,1);context.translate(-point.x,0);},// private\n_restoreRotationChanges:function _restoreRotationChanges(useSketch){var context=this._getContext(useSketch);context.restore();},// private\n_calculateCanvasSize:function _calculateCanvasSize(){var pixelDensityRatio=$.pixelDensityRatio;var viewportSize=this.viewport.getContainerSize();return{x:viewportSize.x*pixelDensityRatio,y:viewportSize.y*pixelDensityRatio};},// private\n_calculateSketchCanvasSize:function _calculateSketchCanvasSize(){var canvasSize=this._calculateCanvasSize();if(this.viewport.getRotation()===0){return canvasSize;}// If the viewport is rotated, we need a larger sketch canvas in order\n// to support edge smoothing.\nvar sketchCanvasSize=Math.ceil(Math.sqrt(canvasSize.x*canvasSize.x+canvasSize.y*canvasSize.y));return{x:sketchCanvasSize,y:sketchCanvasSize};}};})(OpenSeadragon);/*\n * OpenSeadragon - Viewport\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * @class Viewport\n * @memberof OpenSeadragon\n * @classdesc Handles coordinate-related functionality (zoom, pan, rotation, etc.)\n * for an {@link OpenSeadragon.Viewer}.\n * @param {Object} options - Options for this Viewport.\n * @param {Object} [options.margins] - See viewportMargins in {@link OpenSeadragon.Options}.\n * @param {Number} [options.springStiffness] - See springStiffness in {@link OpenSeadragon.Options}.\n * @param {Number} [options.animationTime] - See animationTime in {@link OpenSeadragon.Options}.\n * @param {Number} [options.minZoomImageRatio] - See minZoomImageRatio in {@link OpenSeadragon.Options}.\n * @param {Number} [options.maxZoomPixelRatio] - See maxZoomPixelRatio in {@link OpenSeadragon.Options}.\n * @param {Number} [options.visibilityRatio] - See visibilityRatio in {@link OpenSeadragon.Options}.\n * @param {Boolean} [options.wrapHorizontal] - See wrapHorizontal in {@link OpenSeadragon.Options}.\n * @param {Boolean} [options.wrapVertical] - See wrapVertical in {@link OpenSeadragon.Options}.\n * @param {Number} [options.defaultZoomLevel] - See defaultZoomLevel in {@link OpenSeadragon.Options}.\n * @param {Number} [options.minZoomLevel] - See minZoomLevel in {@link OpenSeadragon.Options}.\n * @param {Number} [options.maxZoomLevel] - See maxZoomLevel in {@link OpenSeadragon.Options}.\n * @param {Number} [options.degrees] - See degrees in {@link OpenSeadragon.Options}.\n * @param {Boolean} [options.homeFillsViewer] - See homeFillsViewer in {@link OpenSeadragon.Options}.\n */$.Viewport=function(options){//backward compatibility for positional args while prefering more\n//idiomatic javascript options object as the only argument\nvar args=arguments;if(args.length&&args[0]instanceof $.Point){options={containerSize:args[0],contentSize:args[1],config:args[2]};}//options.config and the general config argument are deprecated\n//in favor of the more direct specification of optional settings\n//being passed directly on the options object\nif(options.config){$.extend(true,options,options.config);delete options.config;}this._margins=$.extend({left:0,top:0,right:0,bottom:0},options.margins||{});delete options.margins;$.extend(true,this,{//required settings\ncontainerSize:null,contentSize:null,//internal state properties\nzoomPoint:null,viewer:null,//configurable options\nspringStiffness:$.DEFAULT_SETTINGS.springStiffness,animationTime:$.DEFAULT_SETTINGS.animationTime,minZoomImageRatio:$.DEFAULT_SETTINGS.minZoomImageRatio,maxZoomPixelRatio:$.DEFAULT_SETTINGS.maxZoomPixelRatio,visibilityRatio:$.DEFAULT_SETTINGS.visibilityRatio,wrapHorizontal:$.DEFAULT_SETTINGS.wrapHorizontal,wrapVertical:$.DEFAULT_SETTINGS.wrapVertical,defaultZoomLevel:$.DEFAULT_SETTINGS.defaultZoomLevel,minZoomLevel:$.DEFAULT_SETTINGS.minZoomLevel,maxZoomLevel:$.DEFAULT_SETTINGS.maxZoomLevel,degrees:$.DEFAULT_SETTINGS.degrees,flipped:$.DEFAULT_SETTINGS.flipped,homeFillsViewer:$.DEFAULT_SETTINGS.homeFillsViewer},options);this._updateContainerInnerSize();this.centerSpringX=new $.Spring({initial:0,springStiffness:this.springStiffness,animationTime:this.animationTime});this.centerSpringY=new $.Spring({initial:0,springStiffness:this.springStiffness,animationTime:this.animationTime});this.zoomSpring=new $.Spring({exponential:true,initial:1,springStiffness:this.springStiffness,animationTime:this.animationTime});this._oldCenterX=this.centerSpringX.current.value;this._oldCenterY=this.centerSpringY.current.value;this._oldZoom=this.zoomSpring.current.value;this._setContentBounds(new $.Rect(0,0,1,1),1);this.goHome(true);this.update();};/** @lends OpenSeadragon.Viewport.prototype */$.Viewport.prototype={/**\n * Updates the viewport's home bounds and constraints for the given content size.\n * @function\n * @param {OpenSeadragon.Point} contentSize - size of the content in content units\n * @return {OpenSeadragon.Viewport} Chainable.\n * @fires OpenSeadragon.Viewer.event:reset-size\n */resetContentSize:function resetContentSize(contentSize){$.console.assert(contentSize,\"[Viewport.resetContentSize] contentSize is required\");$.console.assert(contentSize instanceof $.Point,\"[Viewport.resetContentSize] contentSize must be an OpenSeadragon.Point\");$.console.assert(contentSize.x>0,\"[Viewport.resetContentSize] contentSize.x must be greater than 0\");$.console.assert(contentSize.y>0,\"[Viewport.resetContentSize] contentSize.y must be greater than 0\");this._setContentBounds(new $.Rect(0,0,1,contentSize.y/contentSize.x),contentSize.x);return this;},// deprecated\nsetHomeBounds:function setHomeBounds(bounds,contentFactor){$.console.error(\"[Viewport.setHomeBounds] this function is deprecated; The content bounds should not be set manually.\");this._setContentBounds(bounds,contentFactor);},// Set the viewport's content bounds\n// @param {OpenSeadragon.Rect} bounds - the new bounds in viewport coordinates\n// without rotation\n// @param {Number} contentFactor - how many content units per viewport unit\n// @fires OpenSeadragon.Viewer.event:reset-size\n// @private\n_setContentBounds:function _setContentBounds(bounds,contentFactor){$.console.assert(bounds,\"[Viewport._setContentBounds] bounds is required\");$.console.assert(bounds instanceof $.Rect,\"[Viewport._setContentBounds] bounds must be an OpenSeadragon.Rect\");$.console.assert(bounds.width>0,\"[Viewport._setContentBounds] bounds.width must be greater than 0\");$.console.assert(bounds.height>0,\"[Viewport._setContentBounds] bounds.height must be greater than 0\");this._contentBoundsNoRotate=bounds.clone();this._contentSizeNoRotate=this._contentBoundsNoRotate.getSize().times(contentFactor);this._contentBounds=bounds.rotate(this.degrees).getBoundingBox();this._contentSize=this._contentBounds.getSize().times(contentFactor);this._contentAspectRatio=this._contentSize.x/this._contentSize.y;if(this.viewer){/**\n * Raised when the viewer's content size or home bounds are reset\n * (see {@link OpenSeadragon.Viewport#resetContentSize}).\n *\n * @event reset-size\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.Point} contentSize\n * @property {OpenSeadragon.Rect} contentBounds - Content bounds.\n * @property {OpenSeadragon.Rect} homeBounds - Content bounds.\n * Deprecated use contentBounds instead.\n * @property {Number} contentFactor\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.viewer.raiseEvent('reset-size',{contentSize:this._contentSizeNoRotate.clone(),contentFactor:contentFactor,homeBounds:this._contentBoundsNoRotate.clone(),contentBounds:this._contentBounds.clone()});}},/**\n * Returns the home zoom in \"viewport zoom\" value.\n * @function\n * @returns {Number} The home zoom in \"viewport zoom\".\n */getHomeZoom:function getHomeZoom(){if(this.defaultZoomLevel){return this.defaultZoomLevel;}var aspectFactor=this._contentAspectRatio/this.getAspectRatio();var output;if(this.homeFillsViewer){// fill the viewer and clip the image\noutput=aspectFactor>=1?aspectFactor:1;}else{output=aspectFactor>=1?1:aspectFactor;}return output/this._contentBounds.width;},/**\n * Returns the home bounds in viewport coordinates.\n * @function\n * @returns {OpenSeadragon.Rect} The home bounds in vewport coordinates.\n */getHomeBounds:function getHomeBounds(){return this.getHomeBoundsNoRotate().rotate(-this.getRotation());},/**\n * Returns the home bounds in viewport coordinates.\n * This method ignores the viewport rotation. Use\n * {@link OpenSeadragon.Viewport#getHomeBounds} to take it into account.\n * @function\n * @returns {OpenSeadragon.Rect} The home bounds in vewport coordinates.\n */getHomeBoundsNoRotate:function getHomeBoundsNoRotate(){var center=this._contentBounds.getCenter();var width=1.0/this.getHomeZoom();var height=width/this.getAspectRatio();return new $.Rect(center.x-width/2.0,center.y-height/2.0,width,height);},/**\n * @function\n * @param {Boolean} immediately\n * @fires OpenSeadragon.Viewer.event:home\n */goHome:function goHome(immediately){if(this.viewer){/**\n * Raised when the \"home\" operation occurs (see {@link OpenSeadragon.Viewport#goHome}).\n *\n * @event home\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {Boolean} immediately\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.viewer.raiseEvent('home',{immediately:immediately});}return this.fitBounds(this.getHomeBounds(),immediately);},/**\n * @function\n */getMinZoom:function getMinZoom(){var homeZoom=this.getHomeZoom(),zoom=this.minZoomLevel?this.minZoomLevel:this.minZoomImageRatio*homeZoom;return zoom;},/**\n * @function\n */getMaxZoom:function getMaxZoom(){var zoom=this.maxZoomLevel;if(!zoom){zoom=this._contentSize.x*this.maxZoomPixelRatio/this._containerInnerSize.x;zoom/=this._contentBounds.width;}return Math.max(zoom,this.getHomeZoom());},/**\n * @function\n */getAspectRatio:function getAspectRatio(){return this._containerInnerSize.x/this._containerInnerSize.y;},/**\n * @function\n * @returns {OpenSeadragon.Point} The size of the container, in screen coordinates.\n */getContainerSize:function getContainerSize(){return new $.Point(this.containerSize.x,this.containerSize.y);},/**\n * The margins push the \"home\" region in from the sides by the specified amounts.\n * @function\n * @returns {Object} Properties (Numbers, in screen coordinates): left, top, right, bottom.\n */getMargins:function getMargins(){return $.extend({},this._margins);// Make a copy so we are not returning our original\n},/**\n * The margins push the \"home\" region in from the sides by the specified amounts.\n * @function\n * @param {Object} margins - Properties (Numbers, in screen coordinates): left, top, right, bottom.\n */setMargins:function setMargins(margins){$.console.assert($.type(margins)==='object','[Viewport.setMargins] margins must be an object');this._margins=$.extend({left:0,top:0,right:0,bottom:0},margins);this._updateContainerInnerSize();if(this.viewer){this.viewer.forceRedraw();}},/**\n * Returns the bounds of the visible area in viewport coordinates.\n * @function\n * @param {Boolean} current - Pass true for the current location; defaults to false (target location).\n * @returns {OpenSeadragon.Rect} The location you are zoomed/panned to, in viewport coordinates.\n */getBounds:function getBounds(current){return this.getBoundsNoRotate(current).rotate(-this.getRotation());},/**\n * Returns the bounds of the visible area in viewport coordinates.\n * This method ignores the viewport rotation. Use\n * {@link OpenSeadragon.Viewport#getBounds} to take it into account.\n * @function\n * @param {Boolean} current - Pass true for the current location; defaults to false (target location).\n * @returns {OpenSeadragon.Rect} The location you are zoomed/panned to, in viewport coordinates.\n */getBoundsNoRotate:function getBoundsNoRotate(current){var center=this.getCenter(current);var width=1.0/this.getZoom(current);var height=width/this.getAspectRatio();return new $.Rect(center.x-width/2.0,center.y-height/2.0,width,height);},/**\n * @function\n * @param {Boolean} current - Pass true for the current location; defaults to false (target location).\n * @returns {OpenSeadragon.Rect} The location you are zoomed/panned to,\n * including the space taken by margins, in viewport coordinates.\n */getBoundsWithMargins:function getBoundsWithMargins(current){return this.getBoundsNoRotateWithMargins(current).rotate(-this.getRotation(),this.getCenter(current));},/**\n * @function\n * @param {Boolean} current - Pass true for the current location; defaults to false (target location).\n * @returns {OpenSeadragon.Rect} The location you are zoomed/panned to,\n * including the space taken by margins, in viewport coordinates.\n */getBoundsNoRotateWithMargins:function getBoundsNoRotateWithMargins(current){var bounds=this.getBoundsNoRotate(current);var factor=this._containerInnerSize.x*this.getZoom(current);bounds.x-=this._margins.left/factor;bounds.y-=this._margins.top/factor;bounds.width+=(this._margins.left+this._margins.right)/factor;bounds.height+=(this._margins.top+this._margins.bottom)/factor;return bounds;},/**\n * @function\n * @param {Boolean} current - Pass true for the current location; defaults to false (target location).\n */getCenter:function getCenter(current){var centerCurrent=new $.Point(this.centerSpringX.current.value,this.centerSpringY.current.value),centerTarget=new $.Point(this.centerSpringX.target.value,this.centerSpringY.target.value),oldZoomPixel,zoom,width,height,bounds,newZoomPixel,deltaZoomPixels,deltaZoomPoints;if(current){return centerCurrent;}else if(!this.zoomPoint){return centerTarget;}oldZoomPixel=this.pixelFromPoint(this.zoomPoint,true);zoom=this.getZoom();width=1.0/zoom;height=width/this.getAspectRatio();bounds=new $.Rect(centerCurrent.x-width/2.0,centerCurrent.y-height/2.0,width,height);newZoomPixel=this._pixelFromPoint(this.zoomPoint,bounds);deltaZoomPixels=newZoomPixel.minus(oldZoomPixel);deltaZoomPoints=deltaZoomPixels.divide(this._containerInnerSize.x*zoom);return centerTarget.plus(deltaZoomPoints);},/**\n * @function\n * @param {Boolean} current - Pass true for the current location; defaults to false (target location).\n */getZoom:function getZoom(current){if(current){return this.zoomSpring.current.value;}else{return this.zoomSpring.target.value;}},// private\n_applyZoomConstraints:function _applyZoomConstraints(zoom){return Math.max(Math.min(zoom,this.getMaxZoom()),this.getMinZoom());},/**\n * @function\n * @private\n * @param {OpenSeadragon.Rect} bounds\n * @return {OpenSeadragon.Rect} constrained bounds.\n */_applyBoundaryConstraints:function _applyBoundaryConstraints(bounds){var newBounds=new $.Rect(bounds.x,bounds.y,bounds.width,bounds.height);if(this.wrapHorizontal){//do nothing\n}else{var horizontalThreshold=this.visibilityRatio*newBounds.width;var boundsRight=newBounds.x+newBounds.width;var contentRight=this._contentBoundsNoRotate.x+this._contentBoundsNoRotate.width;var leftDx=this._contentBoundsNoRotate.x-boundsRight+horizontalThreshold;var rightDx=contentRight-newBounds.x-horizontalThreshold;if(horizontalThreshold>this._contentBoundsNoRotate.width){newBounds.x+=(leftDx+rightDx)/2;}else if(rightDx<0){newBounds.x+=rightDx;}else if(leftDx>0){newBounds.x+=leftDx;}}if(this.wrapVertical){//do nothing\n}else{var verticalThreshold=this.visibilityRatio*newBounds.height;var boundsBottom=newBounds.y+newBounds.height;var contentBottom=this._contentBoundsNoRotate.y+this._contentBoundsNoRotate.height;var topDy=this._contentBoundsNoRotate.y-boundsBottom+verticalThreshold;var bottomDy=contentBottom-newBounds.y-verticalThreshold;if(verticalThreshold>this._contentBoundsNoRotate.height){newBounds.y+=(topDy+bottomDy)/2;}else if(bottomDy<0){newBounds.y+=bottomDy;}else if(topDy>0){newBounds.y+=topDy;}}return newBounds;},/**\n * @function\n * @private\n * @param {Boolean} [immediately=false] - whether the function that triggered this event was\n * called with the \"immediately\" flag\n */_raiseConstraintsEvent:function _raiseConstraintsEvent(immediately){if(this.viewer){/**\n * Raised when the viewport constraints are applied (see {@link OpenSeadragon.Viewport#applyConstraints}).\n *\n * @event constrain\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {Boolean} immediately - whether the function that triggered this event was\n * called with the \"immediately\" flag\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.viewer.raiseEvent('constrain',{immediately:immediately});}},/**\n * Enforces the minZoom, maxZoom and visibilityRatio constraints by\n * zooming and panning to the closest acceptable zoom and location.\n * @function\n * @param {Boolean} [immediately=false]\n * @return {OpenSeadragon.Viewport} Chainable.\n * @fires OpenSeadragon.Viewer.event:constrain\n */applyConstraints:function applyConstraints(immediately){var actualZoom=this.getZoom();var constrainedZoom=this._applyZoomConstraints(actualZoom);if(actualZoom!==constrainedZoom){this.zoomTo(constrainedZoom,this.zoomPoint,immediately);}var bounds=this.getBoundsNoRotate();var constrainedBounds=this._applyBoundaryConstraints(bounds);this._raiseConstraintsEvent(immediately);if(bounds.x!==constrainedBounds.x||bounds.y!==constrainedBounds.y||immediately){this.fitBounds(constrainedBounds.rotate(-this.getRotation()),immediately);}return this;},/**\n * Equivalent to {@link OpenSeadragon.Viewport#applyConstraints}\n * @function\n * @param {Boolean} [immediately=false]\n * @return {OpenSeadragon.Viewport} Chainable.\n * @fires OpenSeadragon.Viewer.event:constrain\n */ensureVisible:function ensureVisible(immediately){return this.applyConstraints(immediately);},/**\n * @function\n * @private\n * @param {OpenSeadragon.Rect} bounds\n * @param {Object} options (immediately=false, constraints=false)\n * @return {OpenSeadragon.Viewport} Chainable.\n */_fitBounds:function _fitBounds(bounds,options){options=options||{};var immediately=options.immediately||false;var constraints=options.constraints||false;var aspect=this.getAspectRatio();var center=bounds.getCenter();// Compute width and height of bounding box.\nvar newBounds=new $.Rect(bounds.x,bounds.y,bounds.width,bounds.height,bounds.degrees+this.getRotation()).getBoundingBox();if(newBounds.getAspectRatio()>=aspect){newBounds.height=newBounds.width/aspect;}else{newBounds.width=newBounds.height*aspect;}// Compute x and y from width, height and center position\nnewBounds.x=center.x-newBounds.width/2;newBounds.y=center.y-newBounds.height/2;var newZoom=1.0/newBounds.width;if(constraints){var newBoundsAspectRatio=newBounds.getAspectRatio();var newConstrainedZoom=this._applyZoomConstraints(newZoom);if(newZoom!==newConstrainedZoom){newZoom=newConstrainedZoom;newBounds.width=1.0/newZoom;newBounds.x=center.x-newBounds.width/2;newBounds.height=newBounds.width/newBoundsAspectRatio;newBounds.y=center.y-newBounds.height/2;}newBounds=this._applyBoundaryConstraints(newBounds);center=newBounds.getCenter();this._raiseConstraintsEvent(immediately);}if(immediately){this.panTo(center,true);return this.zoomTo(newZoom,null,true);}this.panTo(this.getCenter(true),true);this.zoomTo(this.getZoom(true),null,true);var oldBounds=this.getBounds();var oldZoom=this.getZoom();if(oldZoom===0||Math.abs(newZoom/oldZoom-1)<0.00000001){this.zoomTo(newZoom,true);return this.panTo(center,immediately);}newBounds=newBounds.rotate(-this.getRotation());var referencePoint=newBounds.getTopLeft().times(newZoom).minus(oldBounds.getTopLeft().times(oldZoom)).divide(newZoom-oldZoom);return this.zoomTo(newZoom,referencePoint,immediately);},/**\n * Makes the viewport zoom and pan so that the specified bounds take\n * as much space as possible in the viewport.\n * Note: this method ignores the constraints (minZoom, maxZoom and\n * visibilityRatio).\n * Use {@link OpenSeadragon.Viewport#fitBoundsWithConstraints} to enforce\n * them.\n * @function\n * @param {OpenSeadragon.Rect} bounds\n * @param {Boolean} [immediately=false]\n * @return {OpenSeadragon.Viewport} Chainable.\n */fitBounds:function fitBounds(bounds,immediately){return this._fitBounds(bounds,{immediately:immediately,constraints:false});},/**\n * Makes the viewport zoom and pan so that the specified bounds take\n * as much space as possible in the viewport while enforcing the constraints\n * (minZoom, maxZoom and visibilityRatio).\n * Note: because this method enforces the constraints, part of the\n * provided bounds may end up outside of the viewport.\n * Use {@link OpenSeadragon.Viewport#fitBounds} to ignore them.\n * @function\n * @param {OpenSeadragon.Rect} bounds\n * @param {Boolean} [immediately=false]\n * @return {OpenSeadragon.Viewport} Chainable.\n */fitBoundsWithConstraints:function fitBoundsWithConstraints(bounds,immediately){return this._fitBounds(bounds,{immediately:immediately,constraints:true});},/**\n * Zooms so the image just fills the viewer vertically.\n * @param {Boolean} immediately\n * @return {OpenSeadragon.Viewport} Chainable.\n */fitVertically:function fitVertically(immediately){var box=new $.Rect(this._contentBounds.x+this._contentBounds.width/2,this._contentBounds.y,0,this._contentBounds.height);return this.fitBounds(box,immediately);},/**\n * Zooms so the image just fills the viewer horizontally.\n * @param {Boolean} immediately\n * @return {OpenSeadragon.Viewport} Chainable.\n */fitHorizontally:function fitHorizontally(immediately){var box=new $.Rect(this._contentBounds.x,this._contentBounds.y+this._contentBounds.height/2,this._contentBounds.width,0);return this.fitBounds(box,immediately);},/**\n * Returns bounds taking constraints into account\n * Added to improve constrained panning\n * @param {Boolean} current - Pass true for the current location; defaults to false (target location).\n * @return {OpenSeadragon.Viewport} Chainable.\n */getConstrainedBounds:function getConstrainedBounds(current){var bounds,constrainedBounds;bounds=this.getBounds(current);constrainedBounds=this._applyBoundaryConstraints(bounds);return constrainedBounds;},/**\n * @function\n * @param {OpenSeadragon.Point} delta\n * @param {Boolean} immediately\n * @return {OpenSeadragon.Viewport} Chainable.\n * @fires OpenSeadragon.Viewer.event:pan\n */panBy:function panBy(delta,immediately){var center=new $.Point(this.centerSpringX.target.value,this.centerSpringY.target.value);return this.panTo(center.plus(delta),immediately);},/**\n * @function\n * @param {OpenSeadragon.Point} center\n * @param {Boolean} immediately\n * @return {OpenSeadragon.Viewport} Chainable.\n * @fires OpenSeadragon.Viewer.event:pan\n */panTo:function panTo(center,immediately){if(immediately){this.centerSpringX.resetTo(center.x);this.centerSpringY.resetTo(center.y);}else{this.centerSpringX.springTo(center.x);this.centerSpringY.springTo(center.y);}if(this.viewer){/**\n * Raised when the viewport is panned (see {@link OpenSeadragon.Viewport#panBy} and {@link OpenSeadragon.Viewport#panTo}).\n *\n * @event pan\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.Point} center\n * @property {Boolean} immediately\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.viewer.raiseEvent('pan',{center:center,immediately:immediately});}return this;},/**\n * @function\n * @return {OpenSeadragon.Viewport} Chainable.\n * @fires OpenSeadragon.Viewer.event:zoom\n */zoomBy:function zoomBy(factor,refPoint,immediately){return this.zoomTo(this.zoomSpring.target.value*factor,refPoint,immediately);},/**\n * Zooms to the specified zoom level\n * @function\n * @param {Number} zoom The zoom level to zoom to.\n * @param {OpenSeadragon.Point} [refPoint] The point which will stay at\n * the same screen location. Defaults to the viewport center.\n * @param {Boolean} [immediately=false]\n * @return {OpenSeadragon.Viewport} Chainable.\n * @fires OpenSeadragon.Viewer.event:zoom\n */zoomTo:function zoomTo(zoom,refPoint,immediately){var _this=this;this.zoomPoint=refPoint instanceof $.Point&&!isNaN(refPoint.x)&&!isNaN(refPoint.y)?refPoint:null;if(immediately){this._adjustCenterSpringsForZoomPoint(function(){_this.zoomSpring.resetTo(zoom);});}else{this.zoomSpring.springTo(zoom);}if(this.viewer){/**\n * Raised when the viewport zoom level changes (see {@link OpenSeadragon.Viewport#zoomBy} and {@link OpenSeadragon.Viewport#zoomTo}).\n *\n * @event zoom\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {Number} zoom\n * @property {OpenSeadragon.Point} refPoint\n * @property {Boolean} immediately\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.viewer.raiseEvent('zoom',{zoom:zoom,refPoint:refPoint,immediately:immediately});}return this;},/**\n * Rotates this viewport to the angle specified.\n * @function\n * @return {OpenSeadragon.Viewport} Chainable.\n */setRotation:function setRotation(degrees){if(!this.viewer||!this.viewer.drawer.canRotate()){return this;}this.degrees=$.positiveModulo(degrees,360);this._setContentBounds(this.viewer.world.getHomeBounds(),this.viewer.world.getContentFactor());this.viewer.forceRedraw();/**\n * Raised when rotation has been changed.\n *\n * @event rotate\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.\n * @property {Number} degrees - The number of degrees the rotation was set to.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.viewer.raiseEvent('rotate',{\"degrees\":degrees});return this;},/**\n * Gets the current rotation in degrees.\n * @function\n * @return {Number} The current rotation in degrees.\n */getRotation:function getRotation(){return this.degrees;},/**\n * @function\n * @return {OpenSeadragon.Viewport} Chainable.\n * @fires OpenSeadragon.Viewer.event:resize\n */resize:function resize(newContainerSize,maintain){var oldBounds=this.getBoundsNoRotate(),newBounds=oldBounds,widthDeltaFactor;this.containerSize.x=newContainerSize.x;this.containerSize.y=newContainerSize.y;this._updateContainerInnerSize();if(maintain){// TODO: widthDeltaFactor will always be 1; probably not what's intended\nwidthDeltaFactor=newContainerSize.x/this.containerSize.x;newBounds.width=oldBounds.width*widthDeltaFactor;newBounds.height=newBounds.width/this.getAspectRatio();}if(this.viewer){/**\n * Raised when the viewer is resized (see {@link OpenSeadragon.Viewport#resize}).\n *\n * @event resize\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised this event.\n * @property {OpenSeadragon.Point} newContainerSize\n * @property {Boolean} maintain\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.viewer.raiseEvent('resize',{newContainerSize:newContainerSize,maintain:maintain});}return this.fitBounds(newBounds,true);},// private\n_updateContainerInnerSize:function _updateContainerInnerSize(){this._containerInnerSize=new $.Point(Math.max(1,this.containerSize.x-(this._margins.left+this._margins.right)),Math.max(1,this.containerSize.y-(this._margins.top+this._margins.bottom)));},/**\n * Update the zoom and center (X and Y) springs.\n * @function\n * @returns {Boolean} True if any change has been made, false otherwise.\n */update:function update(){var _this=this;this._adjustCenterSpringsForZoomPoint(function(){_this.zoomSpring.update();});this.centerSpringX.update();this.centerSpringY.update();var changed=this.centerSpringX.current.value!==this._oldCenterX||this.centerSpringY.current.value!==this._oldCenterY||this.zoomSpring.current.value!==this._oldZoom;this._oldCenterX=this.centerSpringX.current.value;this._oldCenterY=this.centerSpringY.current.value;this._oldZoom=this.zoomSpring.current.value;return changed;},_adjustCenterSpringsForZoomPoint:function _adjustCenterSpringsForZoomPoint(zoomSpringHandler){if(this.zoomPoint){var oldZoomPixel=this.pixelFromPoint(this.zoomPoint,true);zoomSpringHandler();var newZoomPixel=this.pixelFromPoint(this.zoomPoint,true);var deltaZoomPixels=newZoomPixel.minus(oldZoomPixel);var deltaZoomPoints=this.deltaPointsFromPixels(deltaZoomPixels,true);this.centerSpringX.shiftBy(deltaZoomPoints.x);this.centerSpringY.shiftBy(deltaZoomPoints.y);if(this.zoomSpring.isAtTargetValue()){this.zoomPoint=null;}}else{zoomSpringHandler();}},/**\n * Convert a delta (translation vector) from viewport coordinates to pixels\n * coordinates. This method does not take rotation into account.\n * Consider using deltaPixelsFromPoints if you need to account for rotation.\n * @param {OpenSeadragon.Point} deltaPoints - The translation vector to convert.\n * @param {Boolean} [current=false] - Pass true for the current location;\n * defaults to false (target location).\n * @returns {OpenSeadragon.Point}\n */deltaPixelsFromPointsNoRotate:function deltaPixelsFromPointsNoRotate(deltaPoints,current){return deltaPoints.times(this._containerInnerSize.x*this.getZoom(current));},/**\n * Convert a delta (translation vector) from viewport coordinates to pixels\n * coordinates.\n * @param {OpenSeadragon.Point} deltaPoints - The translation vector to convert.\n * @param {Boolean} [current=false] - Pass true for the current location;\n * defaults to false (target location).\n * @returns {OpenSeadragon.Point}\n */deltaPixelsFromPoints:function deltaPixelsFromPoints(deltaPoints,current){return this.deltaPixelsFromPointsNoRotate(deltaPoints.rotate(this.getRotation()),current);},/**\n * Convert a delta (translation vector) from pixels coordinates to viewport\n * coordinates. This method does not take rotation into account.\n * Consider using deltaPointsFromPixels if you need to account for rotation.\n * @param {OpenSeadragon.Point} deltaPixels - The translation vector to convert.\n * @param {Boolean} [current=false] - Pass true for the current location;\n * defaults to false (target location).\n * @returns {OpenSeadragon.Point}\n */deltaPointsFromPixelsNoRotate:function deltaPointsFromPixelsNoRotate(deltaPixels,current){return deltaPixels.divide(this._containerInnerSize.x*this.getZoom(current));},/**\n * Convert a delta (translation vector) from pixels coordinates to viewport\n * coordinates.\n * @param {OpenSeadragon.Point} deltaPixels - The translation vector to convert.\n * @param {Boolean} [current=false] - Pass true for the current location;\n * defaults to false (target location).\n * @returns {OpenSeadragon.Point}\n */deltaPointsFromPixels:function deltaPointsFromPixels(deltaPixels,current){return this.deltaPointsFromPixelsNoRotate(deltaPixels,current).rotate(-this.getRotation());},/**\n * Convert viewport coordinates to pixels coordinates.\n * This method does not take rotation into account.\n * Consider using pixelFromPoint if you need to account for rotation.\n * @param {OpenSeadragon.Point} point the viewport coordinates\n * @param {Boolean} [current=false] - Pass true for the current location;\n * defaults to false (target location).\n * @returns {OpenSeadragon.Point}\n */pixelFromPointNoRotate:function pixelFromPointNoRotate(point,current){return this._pixelFromPointNoRotate(point,this.getBoundsNoRotate(current));},/**\n * Convert viewport coordinates to pixel coordinates.\n * @param {OpenSeadragon.Point} point the viewport coordinates\n * @param {Boolean} [current=false] - Pass true for the current location;\n * defaults to false (target location).\n * @returns {OpenSeadragon.Point}\n */pixelFromPoint:function pixelFromPoint(point,current){return this._pixelFromPoint(point,this.getBoundsNoRotate(current));},// private\n_pixelFromPointNoRotate:function _pixelFromPointNoRotate(point,bounds){return point.minus(bounds.getTopLeft()).times(this._containerInnerSize.x/bounds.width).plus(new $.Point(this._margins.left,this._margins.top));},// private\n_pixelFromPoint:function _pixelFromPoint(point,bounds){return this._pixelFromPointNoRotate(point.rotate(this.getRotation(),this.getCenter(true)),bounds);},/**\n * Convert pixel coordinates to viewport coordinates.\n * This method does not take rotation into account.\n * Consider using pointFromPixel if you need to account for rotation.\n * @param {OpenSeadragon.Point} pixel Pixel coordinates\n * @param {Boolean} [current=false] - Pass true for the current location;\n * defaults to false (target location).\n * @returns {OpenSeadragon.Point}\n */pointFromPixelNoRotate:function pointFromPixelNoRotate(pixel,current){var bounds=this.getBoundsNoRotate(current);return pixel.minus(new $.Point(this._margins.left,this._margins.top)).divide(this._containerInnerSize.x/bounds.width).plus(bounds.getTopLeft());},/**\n * Convert pixel coordinates to viewport coordinates.\n * @param {OpenSeadragon.Point} pixel Pixel coordinates\n * @param {Boolean} [current=false] - Pass true for the current location;\n * defaults to false (target location).\n * @returns {OpenSeadragon.Point}\n */pointFromPixel:function pointFromPixel(pixel,current){return this.pointFromPixelNoRotate(pixel,current).rotate(-this.getRotation(),this.getCenter(true));},// private\n_viewportToImageDelta:function _viewportToImageDelta(viewerX,viewerY){var scale=this._contentBoundsNoRotate.width;return new $.Point(viewerX*this._contentSizeNoRotate.x/scale,viewerY*this._contentSizeNoRotate.x/scale);},/**\n * Translates from OpenSeadragon viewer coordinate system to image coordinate system.\n * This method can be called either by passing X,Y coordinates or an\n * OpenSeadragon.Point\n * Note: not accurate with multi-image; use TiledImage.viewportToImageCoordinates instead.\n * @function\n * @param {(OpenSeadragon.Point|Number)} viewerX either a point or the X\n * coordinate in viewport coordinate system.\n * @param {Number} [viewerY] Y coordinate in viewport coordinate system.\n * @return {OpenSeadragon.Point} a point representing the coordinates in the image.\n */viewportToImageCoordinates:function viewportToImageCoordinates(viewerX,viewerY){if(viewerX instanceof $.Point){//they passed a point instead of individual components\nreturn this.viewportToImageCoordinates(viewerX.x,viewerX.y);}if(this.viewer){var count=this.viewer.world.getItemCount();if(count>1){$.console.error('[Viewport.viewportToImageCoordinates] is not accurate '+'with multi-image; use TiledImage.viewportToImageCoordinates instead.');}else if(count===1){// It is better to use TiledImage.viewportToImageCoordinates\n// because this._contentBoundsNoRotate can not be relied on\n// with clipping.\nvar item=this.viewer.world.getItemAt(0);return item.viewportToImageCoordinates(viewerX,viewerY,true);}}return this._viewportToImageDelta(viewerX-this._contentBoundsNoRotate.x,viewerY-this._contentBoundsNoRotate.y);},// private\n_imageToViewportDelta:function _imageToViewportDelta(imageX,imageY){var scale=this._contentBoundsNoRotate.width;return new $.Point(imageX/this._contentSizeNoRotate.x*scale,imageY/this._contentSizeNoRotate.x*scale);},/**\n * Translates from image coordinate system to OpenSeadragon viewer coordinate system\n * This method can be called either by passing X,Y coordinates or an\n * OpenSeadragon.Point\n * Note: not accurate with multi-image; use TiledImage.imageToViewportCoordinates instead.\n * @function\n * @param {(OpenSeadragon.Point | Number)} imageX the point or the\n * X coordinate in image coordinate system.\n * @param {Number} [imageY] Y coordinate in image coordinate system.\n * @return {OpenSeadragon.Point} a point representing the coordinates in the viewport.\n */imageToViewportCoordinates:function imageToViewportCoordinates(imageX,imageY){if(imageX instanceof $.Point){//they passed a point instead of individual components\nreturn this.imageToViewportCoordinates(imageX.x,imageX.y);}if(this.viewer){var count=this.viewer.world.getItemCount();if(count>1){$.console.error('[Viewport.imageToViewportCoordinates] is not accurate '+'with multi-image; use TiledImage.imageToViewportCoordinates instead.');}else if(count===1){// It is better to use TiledImage.viewportToImageCoordinates\n// because this._contentBoundsNoRotate can not be relied on\n// with clipping.\nvar item=this.viewer.world.getItemAt(0);return item.imageToViewportCoordinates(imageX,imageY,true);}}var point=this._imageToViewportDelta(imageX,imageY);point.x+=this._contentBoundsNoRotate.x;point.y+=this._contentBoundsNoRotate.y;return point;},/**\n * Translates from a rectangle which describes a portion of the image in\n * pixel coordinates to OpenSeadragon viewport rectangle coordinates.\n * This method can be called either by passing X,Y,width,height or an\n * OpenSeadragon.Rect\n * Note: not accurate with multi-image; use TiledImage.imageToViewportRectangle instead.\n * @function\n * @param {(OpenSeadragon.Rect | Number)} imageX the rectangle or the X\n * coordinate of the top left corner of the rectangle in image coordinate system.\n * @param {Number} [imageY] the Y coordinate of the top left corner of the rectangle\n * in image coordinate system.\n * @param {Number} [pixelWidth] the width in pixel of the rectangle.\n * @param {Number} [pixelHeight] the height in pixel of the rectangle.\n * @returns {OpenSeadragon.Rect} This image's bounds in viewport coordinates\n */imageToViewportRectangle:function imageToViewportRectangle(imageX,imageY,pixelWidth,pixelHeight){var rect=imageX;if(!(rect instanceof $.Rect)){//they passed individual components instead of a rectangle\nrect=new $.Rect(imageX,imageY,pixelWidth,pixelHeight);}if(this.viewer){var count=this.viewer.world.getItemCount();if(count>1){$.console.error('[Viewport.imageToViewportRectangle] is not accurate '+'with multi-image; use TiledImage.imageToViewportRectangle instead.');}else if(count===1){// It is better to use TiledImage.imageToViewportRectangle\n// because this._contentBoundsNoRotate can not be relied on\n// with clipping.\nvar item=this.viewer.world.getItemAt(0);return item.imageToViewportRectangle(imageX,imageY,pixelWidth,pixelHeight,true);}}var coordA=this.imageToViewportCoordinates(rect.x,rect.y);var coordB=this._imageToViewportDelta(rect.width,rect.height);return new $.Rect(coordA.x,coordA.y,coordB.x,coordB.y,rect.degrees);},/**\n * Translates from a rectangle which describes a portion of\n * the viewport in point coordinates to image rectangle coordinates.\n * This method can be called either by passing X,Y,width,height or an\n * OpenSeadragon.Rect\n * Note: not accurate with multi-image; use TiledImage.viewportToImageRectangle instead.\n * @function\n * @param {(OpenSeadragon.Rect | Number)} viewerX either a rectangle or\n * the X coordinate of the top left corner of the rectangle in viewport\n * coordinate system.\n * @param {Number} [viewerY] the Y coordinate of the top left corner of the rectangle\n * in viewport coordinate system.\n * @param {Number} [pointWidth] the width of the rectangle in viewport coordinate system.\n * @param {Number} [pointHeight] the height of the rectangle in viewport coordinate system.\n */viewportToImageRectangle:function viewportToImageRectangle(viewerX,viewerY,pointWidth,pointHeight){var rect=viewerX;if(!(rect instanceof $.Rect)){//they passed individual components instead of a rectangle\nrect=new $.Rect(viewerX,viewerY,pointWidth,pointHeight);}if(this.viewer){var count=this.viewer.world.getItemCount();if(count>1){$.console.error('[Viewport.viewportToImageRectangle] is not accurate '+'with multi-image; use TiledImage.viewportToImageRectangle instead.');}else if(count===1){// It is better to use TiledImage.viewportToImageCoordinates\n// because this._contentBoundsNoRotate can not be relied on\n// with clipping.\nvar item=this.viewer.world.getItemAt(0);return item.viewportToImageRectangle(viewerX,viewerY,pointWidth,pointHeight,true);}}var coordA=this.viewportToImageCoordinates(rect.x,rect.y);var coordB=this._viewportToImageDelta(rect.width,rect.height);return new $.Rect(coordA.x,coordA.y,coordB.x,coordB.y,rect.degrees);},/**\n * Convert pixel coordinates relative to the viewer element to image\n * coordinates.\n * Note: not accurate with multi-image.\n * @param {OpenSeadragon.Point} pixel\n * @returns {OpenSeadragon.Point}\n */viewerElementToImageCoordinates:function viewerElementToImageCoordinates(pixel){var point=this.pointFromPixel(pixel,true);return this.viewportToImageCoordinates(point);},/**\n * Convert pixel coordinates relative to the image to\n * viewer element coordinates.\n * Note: not accurate with multi-image.\n * @param {OpenSeadragon.Point} pixel\n * @returns {OpenSeadragon.Point}\n */imageToViewerElementCoordinates:function imageToViewerElementCoordinates(pixel){var point=this.imageToViewportCoordinates(pixel);return this.pixelFromPoint(point,true);},/**\n * Convert pixel coordinates relative to the window to image coordinates.\n * Note: not accurate with multi-image.\n * @param {OpenSeadragon.Point} pixel\n * @returns {OpenSeadragon.Point}\n */windowToImageCoordinates:function windowToImageCoordinates(pixel){$.console.assert(this.viewer,\"[Viewport.windowToImageCoordinates] the viewport must have a viewer.\");var viewerCoordinates=pixel.minus($.getElementPosition(this.viewer.element));return this.viewerElementToImageCoordinates(viewerCoordinates);},/**\n * Convert image coordinates to pixel coordinates relative to the window.\n * Note: not accurate with multi-image.\n * @param {OpenSeadragon.Point} pixel\n * @returns {OpenSeadragon.Point}\n */imageToWindowCoordinates:function imageToWindowCoordinates(pixel){$.console.assert(this.viewer,\"[Viewport.imageToWindowCoordinates] the viewport must have a viewer.\");var viewerCoordinates=this.imageToViewerElementCoordinates(pixel);return viewerCoordinates.plus($.getElementPosition(this.viewer.element));},/**\n * Convert pixel coordinates relative to the viewer element to viewport\n * coordinates.\n * @param {OpenSeadragon.Point} pixel\n * @returns {OpenSeadragon.Point}\n */viewerElementToViewportCoordinates:function viewerElementToViewportCoordinates(pixel){return this.pointFromPixel(pixel,true);},/**\n * Convert viewport coordinates to pixel coordinates relative to the\n * viewer element.\n * @param {OpenSeadragon.Point} point\n * @returns {OpenSeadragon.Point}\n */viewportToViewerElementCoordinates:function viewportToViewerElementCoordinates(point){return this.pixelFromPoint(point,true);},/**\n * Convert a rectangle in pixel coordinates relative to the viewer element\n * to viewport coordinates.\n * @param {OpenSeadragon.Rect} rectangle the rectangle to convert\n * @returns {OpenSeadragon.Rect} the converted rectangle\n */viewerElementToViewportRectangle:function viewerElementToViewportRectangle(rectangle){return $.Rect.fromSummits(this.pointFromPixel(rectangle.getTopLeft(),true),this.pointFromPixel(rectangle.getTopRight(),true),this.pointFromPixel(rectangle.getBottomLeft(),true));},/**\n * Convert a rectangle in viewport coordinates to pixel coordinates relative\n * to the viewer element.\n * @param {OpenSeadragon.Rect} rectangle the rectangle to convert\n * @returns {OpenSeadragon.Rect} the converted rectangle\n */viewportToViewerElementRectangle:function viewportToViewerElementRectangle(rectangle){return $.Rect.fromSummits(this.pixelFromPoint(rectangle.getTopLeft(),true),this.pixelFromPoint(rectangle.getTopRight(),true),this.pixelFromPoint(rectangle.getBottomLeft(),true));},/**\n * Convert pixel coordinates relative to the window to viewport coordinates.\n * @param {OpenSeadragon.Point} pixel\n * @returns {OpenSeadragon.Point}\n */windowToViewportCoordinates:function windowToViewportCoordinates(pixel){$.console.assert(this.viewer,\"[Viewport.windowToViewportCoordinates] the viewport must have a viewer.\");var viewerCoordinates=pixel.minus($.getElementPosition(this.viewer.element));return this.viewerElementToViewportCoordinates(viewerCoordinates);},/**\n * Convert viewport coordinates to pixel coordinates relative to the window.\n * @param {OpenSeadragon.Point} point\n * @returns {OpenSeadragon.Point}\n */viewportToWindowCoordinates:function viewportToWindowCoordinates(point){$.console.assert(this.viewer,\"[Viewport.viewportToWindowCoordinates] the viewport must have a viewer.\");var viewerCoordinates=this.viewportToViewerElementCoordinates(point);return viewerCoordinates.plus($.getElementPosition(this.viewer.element));},/**\n * Convert a viewport zoom to an image zoom.\n * Image zoom: ratio of the original image size to displayed image size.\n * 1 means original image size, 0.5 half size...\n * Viewport zoom: ratio of the displayed image's width to viewport's width.\n * 1 means identical width, 2 means image's width is twice the viewport's width...\n * Note: not accurate with multi-image.\n * @function\n * @param {Number} viewportZoom The viewport zoom\n * target zoom.\n * @returns {Number} imageZoom The image zoom\n */viewportToImageZoom:function viewportToImageZoom(viewportZoom){if(this.viewer){var count=this.viewer.world.getItemCount();if(count>1){$.console.error('[Viewport.viewportToImageZoom] is not '+'accurate with multi-image.');}else if(count===1){// It is better to use TiledImage.viewportToImageZoom\n// because this._contentBoundsNoRotate can not be relied on\n// with clipping.\nvar item=this.viewer.world.getItemAt(0);return item.viewportToImageZoom(viewportZoom);}}var imageWidth=this._contentSizeNoRotate.x;var containerWidth=this._containerInnerSize.x;var scale=this._contentBoundsNoRotate.width;var viewportToImageZoomRatio=containerWidth/imageWidth*scale;return viewportZoom*viewportToImageZoomRatio;},/**\n * Convert an image zoom to a viewport zoom.\n * Image zoom: ratio of the original image size to displayed image size.\n * 1 means original image size, 0.5 half size...\n * Viewport zoom: ratio of the displayed image's width to viewport's width.\n * 1 means identical width, 2 means image's width is twice the viewport's width...\n * Note: not accurate with multi-image.\n * @function\n * @param {Number} imageZoom The image zoom\n * target zoom.\n * @returns {Number} viewportZoom The viewport zoom\n */imageToViewportZoom:function imageToViewportZoom(imageZoom){if(this.viewer){var count=this.viewer.world.getItemCount();if(count>1){$.console.error('[Viewport.imageToViewportZoom] is not accurate '+'with multi-image.');}else if(count===1){// It is better to use TiledImage.imageToViewportZoom\n// because this._contentBoundsNoRotate can not be relied on\n// with clipping.\nvar item=this.viewer.world.getItemAt(0);return item.imageToViewportZoom(imageZoom);}}var imageWidth=this._contentSizeNoRotate.x;var containerWidth=this._containerInnerSize.x;var scale=this._contentBoundsNoRotate.width;var viewportToImageZoomRatio=imageWidth/containerWidth/scale;return imageZoom*viewportToImageZoomRatio;},/**\n * Toggles flip state and demands a new drawing on navigator and viewer objects.\n * @function\n * @return {OpenSeadragon.Viewport} Chainable.\n */toggleFlip:function toggleFlip(){this.setFlip(!this.getFlip());return this;},/**\n * Gets flip state stored on viewport.\n * @function\n * @return {Boolean} Flip state.\n */getFlip:function getFlip(){return this.flipped;},/**\n * Sets flip state according to the state input argument.\n * @function\n * @param {Boolean} state - Flip state to set.\n * @return {OpenSeadragon.Viewport} Chainable.\n */setFlip:function setFlip(state){if(this.flipped===state){return this;}this.flipped=state;if(this.viewer.navigator){this.viewer.navigator.setFlip(this.getFlip());}this.viewer.forceRedraw();/**\n * Raised when flip state has been changed.\n *\n * @event flip\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.\n * @property {Number} flipped - The flip state after this change.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.viewer.raiseEvent('flip',{\"flipped\":state});return this;}};})(OpenSeadragon);/*\n * OpenSeadragon - TiledImage\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){/**\n * You shouldn't have to create a TiledImage instance directly; get it asynchronously by\n * using {@link OpenSeadragon.Viewer#open} or {@link OpenSeadragon.Viewer#addTiledImage} instead.\n * @class TiledImage\n * @memberof OpenSeadragon\n * @extends OpenSeadragon.EventSource\n * @classdesc Handles rendering of tiles for an {@link OpenSeadragon.Viewer}.\n * A new instance is created for each TileSource opened.\n * @param {Object} options - Configuration for this TiledImage.\n * @param {OpenSeadragon.TileSource} options.source - The TileSource that defines this TiledImage.\n * @param {OpenSeadragon.Viewer} options.viewer - The Viewer that owns this TiledImage.\n * @param {OpenSeadragon.TileCache} options.tileCache - The TileCache for this TiledImage to use.\n * @param {OpenSeadragon.Drawer} options.drawer - The Drawer for this TiledImage to draw onto.\n * @param {OpenSeadragon.ImageLoader} options.imageLoader - The ImageLoader for this TiledImage to use.\n * @param {Number} [options.x=0] - Left position, in viewport coordinates.\n * @param {Number} [options.y=0] - Top position, in viewport coordinates.\n * @param {Number} [options.width=1] - Width, in viewport coordinates.\n * @param {Number} [options.height] - Height, in viewport coordinates.\n * @param {OpenSeadragon.Rect} [options.fitBounds] The bounds in viewport coordinates\n * to fit the image into. If specified, x, y, width and height get ignored.\n * @param {OpenSeadragon.Placement} [options.fitBoundsPlacement=OpenSeadragon.Placement.CENTER]\n * How to anchor the image in the bounds if options.fitBounds is set.\n * @param {OpenSeadragon.Rect} [options.clip] - An area, in image pixels, to clip to\n * (portions of the image outside of this area will not be visible). Only works on\n * browsers that support the HTML5 canvas.\n * @param {Number} [options.springStiffness] - See {@link OpenSeadragon.Options}.\n * @param {Boolean} [options.animationTime] - See {@link OpenSeadragon.Options}.\n * @param {Number} [options.minZoomImageRatio] - See {@link OpenSeadragon.Options}.\n * @param {Boolean} [options.wrapHorizontal] - See {@link OpenSeadragon.Options}.\n * @param {Boolean} [options.wrapVertical] - See {@link OpenSeadragon.Options}.\n * @param {Boolean} [options.immediateRender] - See {@link OpenSeadragon.Options}.\n * @param {Number} [options.blendTime] - See {@link OpenSeadragon.Options}.\n * @param {Boolean} [options.alwaysBlend] - See {@link OpenSeadragon.Options}.\n * @param {Number} [options.minPixelRatio] - See {@link OpenSeadragon.Options}.\n * @param {Number} [options.smoothTileEdgesMinZoom] - See {@link OpenSeadragon.Options}.\n * @param {Boolean} [options.iOSDevice] - See {@link OpenSeadragon.Options}.\n * @param {Number} [options.opacity=1] - Set to draw at proportional opacity. If zero, images will not draw.\n * @param {Boolean} [options.preload=false] - Set true to load even when the image is hidden by zero opacity.\n * @param {String} [options.compositeOperation] - How the image is composited onto other images; see compositeOperation in {@link OpenSeadragon.Options} for possible values.\n * @param {Boolean} [options.debugMode] - See {@link OpenSeadragon.Options}.\n * @param {String|CanvasGradient|CanvasPattern|Function} [options.placeholderFillStyle] - See {@link OpenSeadragon.Options}.\n * @param {String|Boolean} [options.crossOriginPolicy] - See {@link OpenSeadragon.Options}.\n * @param {Boolean} [options.ajaxWithCredentials] - See {@link OpenSeadragon.Options}.\n * @param {Boolean} [options.loadTilesWithAjax]\n * Whether to load tile data using AJAX requests.\n * Defaults to the setting in {@link OpenSeadragon.Options}.\n * @param {Object} [options.ajaxHeaders={}]\n * A set of headers to include when making tile AJAX requests.\n */$.TiledImage=function(options){var _this=this;$.console.assert(options.tileCache,\"[TiledImage] options.tileCache is required\");$.console.assert(options.drawer,\"[TiledImage] options.drawer is required\");$.console.assert(options.viewer,\"[TiledImage] options.viewer is required\");$.console.assert(options.imageLoader,\"[TiledImage] options.imageLoader is required\");$.console.assert(options.source,\"[TiledImage] options.source is required\");$.console.assert(!options.clip||options.clip instanceof $.Rect,\"[TiledImage] options.clip must be an OpenSeadragon.Rect if present\");$.EventSource.call(this);this._tileCache=options.tileCache;delete options.tileCache;this._drawer=options.drawer;delete options.drawer;this._imageLoader=options.imageLoader;delete options.imageLoader;if(options.clip instanceof $.Rect){this._clip=options.clip.clone();}delete options.clip;var x=options.x||0;delete options.x;var y=options.y||0;delete options.y;// Ratio of zoomable image height to width.\nthis.normHeight=options.source.dimensions.y/options.source.dimensions.x;this.contentAspectX=options.source.dimensions.x/options.source.dimensions.y;var scale=1;if(options.width){scale=options.width;delete options.width;if(options.height){$.console.error(\"specifying both width and height to a tiledImage is not supported\");delete options.height;}}else if(options.height){scale=options.height/this.normHeight;delete options.height;}var fitBounds=options.fitBounds;delete options.fitBounds;var fitBoundsPlacement=options.fitBoundsPlacement||OpenSeadragon.Placement.CENTER;delete options.fitBoundsPlacement;var degrees=options.degrees||0;delete options.degrees;$.extend(true,this,{//internal state properties\nviewer:null,tilesMatrix:{},// A '3d' dictionary [level][x][y] --> Tile.\ncoverage:{},// A '3d' dictionary [level][x][y] --> Boolean; shows what areas have been drawn.\nloadingCoverage:{},// A '3d' dictionary [level][x][y] --> Boolean; shows what areas are loaded or are being loaded/blended.\nlastDrawn:[],// An unordered list of Tiles drawn last frame.\nlastResetTime:0,// Last time for which the tiledImage was reset.\n_midDraw:false,// Is the tiledImage currently updating the viewport?\n_needsDraw:true,// Does the tiledImage need to update the viewport again?\n_hasOpaqueTile:false,// Do we have even one fully opaque tile?\n_tilesLoading:0,// The number of pending tile requests.\n//configurable settings\nspringStiffness:$.DEFAULT_SETTINGS.springStiffness,animationTime:$.DEFAULT_SETTINGS.animationTime,minZoomImageRatio:$.DEFAULT_SETTINGS.minZoomImageRatio,wrapHorizontal:$.DEFAULT_SETTINGS.wrapHorizontal,wrapVertical:$.DEFAULT_SETTINGS.wrapVertical,immediateRender:$.DEFAULT_SETTINGS.immediateRender,blendTime:$.DEFAULT_SETTINGS.blendTime,alwaysBlend:$.DEFAULT_SETTINGS.alwaysBlend,minPixelRatio:$.DEFAULT_SETTINGS.minPixelRatio,smoothTileEdgesMinZoom:$.DEFAULT_SETTINGS.smoothTileEdgesMinZoom,iOSDevice:$.DEFAULT_SETTINGS.iOSDevice,debugMode:$.DEFAULT_SETTINGS.debugMode,crossOriginPolicy:$.DEFAULT_SETTINGS.crossOriginPolicy,ajaxWithCredentials:$.DEFAULT_SETTINGS.ajaxWithCredentials,placeholderFillStyle:$.DEFAULT_SETTINGS.placeholderFillStyle,opacity:$.DEFAULT_SETTINGS.opacity,preload:$.DEFAULT_SETTINGS.preload,compositeOperation:$.DEFAULT_SETTINGS.compositeOperation},options);this._preload=this.preload;delete this.preload;this._fullyLoaded=false;this._xSpring=new $.Spring({initial:x,springStiffness:this.springStiffness,animationTime:this.animationTime});this._ySpring=new $.Spring({initial:y,springStiffness:this.springStiffness,animationTime:this.animationTime});this._scaleSpring=new $.Spring({initial:scale,springStiffness:this.springStiffness,animationTime:this.animationTime});this._degreesSpring=new $.Spring({initial:degrees,springStiffness:this.springStiffness,animationTime:this.animationTime});this._updateForScale();if(fitBounds){this.fitBounds(fitBounds,fitBoundsPlacement,true);}// We need a callback to give image manipulation a chance to happen\nthis._drawingHandler=function(args){/**\n * This event is fired just before the tile is drawn giving the application a chance to alter the image.\n *\n * NOTE: This event is only fired when the drawer is using a <canvas>.\n *\n * @event tile-drawing\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.\n * @property {OpenSeadragon.Tile} tile - The Tile being drawn.\n * @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.\n * @property {OpenSeadragon.Tile} context - The HTML canvas context being drawn into.\n * @property {OpenSeadragon.Tile} rendered - The HTML canvas context containing the tile imagery.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */_this.viewer.raiseEvent('tile-drawing',$.extend({tiledImage:_this},args));};};$.extend($.TiledImage.prototype,$.EventSource.prototype,/** @lends OpenSeadragon.TiledImage.prototype */{/**\n * @returns {Boolean} Whether the TiledImage needs to be drawn.\n */needsDraw:function needsDraw(){return this._needsDraw;},/**\n * @returns {Boolean} Whether all tiles necessary for this TiledImage to draw at the current view have been loaded.\n */getFullyLoaded:function getFullyLoaded(){return this._fullyLoaded;},// private\n_setFullyLoaded:function _setFullyLoaded(flag){if(flag===this._fullyLoaded){return;}this._fullyLoaded=flag;/**\n * Fired when the TiledImage's \"fully loaded\" flag (whether all tiles necessary for this TiledImage\n * to draw at the current view have been loaded) changes.\n *\n * @event fully-loaded-change\n * @memberof OpenSeadragon.TiledImage\n * @type {object}\n * @property {Boolean} fullyLoaded - The new \"fully loaded\" value.\n * @property {OpenSeadragon.TiledImage} eventSource - A reference to the TiledImage which raised the event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('fully-loaded-change',{fullyLoaded:this._fullyLoaded});},/**\n * Clears all tiles and triggers an update on the next call to\n * {@link OpenSeadragon.TiledImage#update}.\n */reset:function reset(){this._tileCache.clearTilesFor(this);this.lastResetTime=$.now();this._needsDraw=true;},/**\n * Updates the TiledImage's bounds, animating if needed.\n * @returns {Boolean} Whether the TiledImage animated.\n */update:function update(){var xUpdated=this._xSpring.update();var yUpdated=this._ySpring.update();var scaleUpdated=this._scaleSpring.update();var degreesUpdated=this._degreesSpring.update();if(xUpdated||yUpdated||scaleUpdated||degreesUpdated){this._updateForScale();this._needsDraw=true;return true;}return false;},/**\n * Draws the TiledImage to its Drawer.\n */draw:function draw(){if(this.opacity!==0||this._preload){this._midDraw=true;this._updateViewport();this._midDraw=false;}// Images with opacity 0 should not need to be drawn in future. this._needsDraw = false is set in this._updateViewport() for other images.\nelse{this._needsDraw=false;}},/**\n * Destroy the TiledImage (unload current loaded tiles).\n */destroy:function destroy(){this.reset();},/**\n * Get this TiledImage's bounds in viewport coordinates.\n * @param {Boolean} [current=false] - Pass true for the current location;\n * false for target location.\n * @returns {OpenSeadragon.Rect} This TiledImage's bounds in viewport coordinates.\n */getBounds:function getBounds(current){return this.getBoundsNoRotate(current).rotate(this.getRotation(current),this._getRotationPoint(current));},/**\n * Get this TiledImage's bounds in viewport coordinates without taking\n * rotation into account.\n * @param {Boolean} [current=false] - Pass true for the current location;\n * false for target location.\n * @returns {OpenSeadragon.Rect} This TiledImage's bounds in viewport coordinates.\n */getBoundsNoRotate:function getBoundsNoRotate(current){return current?new $.Rect(this._xSpring.current.value,this._ySpring.current.value,this._worldWidthCurrent,this._worldHeightCurrent):new $.Rect(this._xSpring.target.value,this._ySpring.target.value,this._worldWidthTarget,this._worldHeightTarget);},// deprecated\ngetWorldBounds:function getWorldBounds(){$.console.error('[TiledImage.getWorldBounds] is deprecated; use TiledImage.getBounds instead');return this.getBounds();},/**\n * Get the bounds of the displayed part of the tiled image.\n * @param {Boolean} [current=false] Pass true for the current location,\n * false for the target location.\n * @returns {$.Rect} The clipped bounds in viewport coordinates.\n */getClippedBounds:function getClippedBounds(current){var bounds=this.getBoundsNoRotate(current);if(this._clip){var worldWidth=current?this._worldWidthCurrent:this._worldWidthTarget;var ratio=worldWidth/this.source.dimensions.x;var clip=this._clip.times(ratio);bounds=new $.Rect(bounds.x+clip.x,bounds.y+clip.y,clip.width,clip.height);}return bounds.rotate(this.getRotation(current),this._getRotationPoint(current));},/**\n * @returns {OpenSeadragon.Point} This TiledImage's content size, in original pixels.\n */getContentSize:function getContentSize(){return new $.Point(this.source.dimensions.x,this.source.dimensions.y);},// private\n_viewportToImageDelta:function _viewportToImageDelta(viewerX,viewerY,current){var scale=current?this._scaleSpring.current.value:this._scaleSpring.target.value;return new $.Point(viewerX*(this.source.dimensions.x/scale),viewerY*(this.source.dimensions.y*this.contentAspectX/scale));},/**\n * Translates from OpenSeadragon viewer coordinate system to image coordinate system.\n * This method can be called either by passing X,Y coordinates or an {@link OpenSeadragon.Point}.\n * @param {Number|OpenSeadragon.Point} viewerX - The X coordinate or point in viewport coordinate system.\n * @param {Number} [viewerY] - The Y coordinate in viewport coordinate system.\n * @param {Boolean} [current=false] - Pass true to use the current location; false for target location.\n * @return {OpenSeadragon.Point} A point representing the coordinates in the image.\n */viewportToImageCoordinates:function viewportToImageCoordinates(viewerX,viewerY,current){var point;if(viewerX instanceof $.Point){//they passed a point instead of individual components\ncurrent=viewerY;point=viewerX;}else{point=new $.Point(viewerX,viewerY);}point=point.rotate(-this.getRotation(current),this._getRotationPoint(current));return current?this._viewportToImageDelta(point.x-this._xSpring.current.value,point.y-this._ySpring.current.value):this._viewportToImageDelta(point.x-this._xSpring.target.value,point.y-this._ySpring.target.value);},// private\n_imageToViewportDelta:function _imageToViewportDelta(imageX,imageY,current){var scale=current?this._scaleSpring.current.value:this._scaleSpring.target.value;return new $.Point(imageX/this.source.dimensions.x*scale,imageY/this.source.dimensions.y/this.contentAspectX*scale);},/**\n * Translates from image coordinate system to OpenSeadragon viewer coordinate system\n * This method can be called either by passing X,Y coordinates or an {@link OpenSeadragon.Point}.\n * @param {Number|OpenSeadragon.Point} imageX - The X coordinate or point in image coordinate system.\n * @param {Number} [imageY] - The Y coordinate in image coordinate system.\n * @param {Boolean} [current=false] - Pass true to use the current location; false for target location.\n * @return {OpenSeadragon.Point} A point representing the coordinates in the viewport.\n */imageToViewportCoordinates:function imageToViewportCoordinates(imageX,imageY,current){if(imageX instanceof $.Point){//they passed a point instead of individual components\ncurrent=imageY;imageY=imageX.y;imageX=imageX.x;}var point=this._imageToViewportDelta(imageX,imageY);if(current){point.x+=this._xSpring.current.value;point.y+=this._ySpring.current.value;}else{point.x+=this._xSpring.target.value;point.y+=this._ySpring.target.value;}return point.rotate(this.getRotation(current),this._getRotationPoint(current));},/**\n * Translates from a rectangle which describes a portion of the image in\n * pixel coordinates to OpenSeadragon viewport rectangle coordinates.\n * This method can be called either by passing X,Y,width,height or an {@link OpenSeadragon.Rect}.\n * @param {Number|OpenSeadragon.Rect} imageX - The left coordinate or rectangle in image coordinate system.\n * @param {Number} [imageY] - The top coordinate in image coordinate system.\n * @param {Number} [pixelWidth] - The width in pixel of the rectangle.\n * @param {Number} [pixelHeight] - The height in pixel of the rectangle.\n * @param {Boolean} [current=false] - Pass true to use the current location; false for target location.\n * @return {OpenSeadragon.Rect} A rect representing the coordinates in the viewport.\n */imageToViewportRectangle:function imageToViewportRectangle(imageX,imageY,pixelWidth,pixelHeight,current){var rect=imageX;if(rect instanceof $.Rect){//they passed a rect instead of individual components\ncurrent=imageY;}else{rect=new $.Rect(imageX,imageY,pixelWidth,pixelHeight);}var coordA=this.imageToViewportCoordinates(rect.getTopLeft(),current);var coordB=this._imageToViewportDelta(rect.width,rect.height,current);return new $.Rect(coordA.x,coordA.y,coordB.x,coordB.y,rect.degrees+this.getRotation(current));},/**\n * Translates from a rectangle which describes a portion of\n * the viewport in point coordinates to image rectangle coordinates.\n * This method can be called either by passing X,Y,width,height or an {@link OpenSeadragon.Rect}.\n * @param {Number|OpenSeadragon.Rect} viewerX - The left coordinate or rectangle in viewport coordinate system.\n * @param {Number} [viewerY] - The top coordinate in viewport coordinate system.\n * @param {Number} [pointWidth] - The width in viewport coordinate system.\n * @param {Number} [pointHeight] - The height in viewport coordinate system.\n * @param {Boolean} [current=false] - Pass true to use the current location; false for target location.\n * @return {OpenSeadragon.Rect} A rect representing the coordinates in the image.\n */viewportToImageRectangle:function viewportToImageRectangle(viewerX,viewerY,pointWidth,pointHeight,current){var rect=viewerX;if(viewerX instanceof $.Rect){//they passed a rect instead of individual components\ncurrent=viewerY;}else{rect=new $.Rect(viewerX,viewerY,pointWidth,pointHeight);}var coordA=this.viewportToImageCoordinates(rect.getTopLeft(),current);var coordB=this._viewportToImageDelta(rect.width,rect.height,current);return new $.Rect(coordA.x,coordA.y,coordB.x,coordB.y,rect.degrees-this.getRotation(current));},/**\n * Convert pixel coordinates relative to the viewer element to image\n * coordinates.\n * @param {OpenSeadragon.Point} pixel\n * @returns {OpenSeadragon.Point}\n */viewerElementToImageCoordinates:function viewerElementToImageCoordinates(pixel){var point=this.viewport.pointFromPixel(pixel,true);return this.viewportToImageCoordinates(point);},/**\n * Convert pixel coordinates relative to the image to\n * viewer element coordinates.\n * @param {OpenSeadragon.Point} pixel\n * @returns {OpenSeadragon.Point}\n */imageToViewerElementCoordinates:function imageToViewerElementCoordinates(pixel){var point=this.imageToViewportCoordinates(pixel);return this.viewport.pixelFromPoint(point,true);},/**\n * Convert pixel coordinates relative to the window to image coordinates.\n * @param {OpenSeadragon.Point} pixel\n * @returns {OpenSeadragon.Point}\n */windowToImageCoordinates:function windowToImageCoordinates(pixel){var viewerCoordinates=pixel.minus(OpenSeadragon.getElementPosition(this.viewer.element));return this.viewerElementToImageCoordinates(viewerCoordinates);},/**\n * Convert image coordinates to pixel coordinates relative to the window.\n * @param {OpenSeadragon.Point} pixel\n * @returns {OpenSeadragon.Point}\n */imageToWindowCoordinates:function imageToWindowCoordinates(pixel){var viewerCoordinates=this.imageToViewerElementCoordinates(pixel);return viewerCoordinates.plus(OpenSeadragon.getElementPosition(this.viewer.element));},// private\n// Convert rectangle in viewport coordinates to this tiled image point\n// coordinates (x in [0, 1] and y in [0, aspectRatio])\n_viewportToTiledImageRectangle:function _viewportToTiledImageRectangle(rect){var scale=this._scaleSpring.current.value;rect=rect.rotate(-this.getRotation(true),this._getRotationPoint(true));return new $.Rect((rect.x-this._xSpring.current.value)/scale,(rect.y-this._ySpring.current.value)/scale,rect.width/scale,rect.height/scale,rect.degrees);},/**\n * Convert a viewport zoom to an image zoom.\n * Image zoom: ratio of the original image size to displayed image size.\n * 1 means original image size, 0.5 half size...\n * Viewport zoom: ratio of the displayed image's width to viewport's width.\n * 1 means identical width, 2 means image's width is twice the viewport's width...\n * @function\n * @param {Number} viewportZoom The viewport zoom\n * @returns {Number} imageZoom The image zoom\n */viewportToImageZoom:function viewportToImageZoom(viewportZoom){var ratio=this._scaleSpring.current.value*this.viewport._containerInnerSize.x/this.source.dimensions.x;return ratio*viewportZoom;},/**\n * Convert an image zoom to a viewport zoom.\n * Image zoom: ratio of the original image size to displayed image size.\n * 1 means original image size, 0.5 half size...\n * Viewport zoom: ratio of the displayed image's width to viewport's width.\n * 1 means identical width, 2 means image's width is twice the viewport's width...\n * Note: not accurate with multi-image.\n * @function\n * @param {Number} imageZoom The image zoom\n * @returns {Number} viewportZoom The viewport zoom\n */imageToViewportZoom:function imageToViewportZoom(imageZoom){var ratio=this._scaleSpring.current.value*this.viewport._containerInnerSize.x/this.source.dimensions.x;return imageZoom/ratio;},/**\n * Sets the TiledImage's position in the world.\n * @param {OpenSeadragon.Point} position - The new position, in viewport coordinates.\n * @param {Boolean} [immediately=false] - Whether to animate to the new position or snap immediately.\n * @fires OpenSeadragon.TiledImage.event:bounds-change\n */setPosition:function setPosition(position,immediately){var sameTarget=this._xSpring.target.value===position.x&&this._ySpring.target.value===position.y;if(immediately){if(sameTarget&&this._xSpring.current.value===position.x&&this._ySpring.current.value===position.y){return;}this._xSpring.resetTo(position.x);this._ySpring.resetTo(position.y);this._needsDraw=true;}else{if(sameTarget){return;}this._xSpring.springTo(position.x);this._ySpring.springTo(position.y);this._needsDraw=true;}if(!sameTarget){this._raiseBoundsChange();}},/**\n * Sets the TiledImage's width in the world, adjusting the height to match based on aspect ratio.\n * @param {Number} width - The new width, in viewport coordinates.\n * @param {Boolean} [immediately=false] - Whether to animate to the new size or snap immediately.\n * @fires OpenSeadragon.TiledImage.event:bounds-change\n */setWidth:function setWidth(width,immediately){this._setScale(width,immediately);},/**\n * Sets the TiledImage's height in the world, adjusting the width to match based on aspect ratio.\n * @param {Number} height - The new height, in viewport coordinates.\n * @param {Boolean} [immediately=false] - Whether to animate to the new size or snap immediately.\n * @fires OpenSeadragon.TiledImage.event:bounds-change\n */setHeight:function setHeight(height,immediately){this._setScale(height/this.normHeight,immediately);},/**\n * Positions and scales the TiledImage to fit in the specified bounds.\n * Note: this method fires OpenSeadragon.TiledImage.event:bounds-change\n * twice\n * @param {OpenSeadragon.Rect} bounds The bounds to fit the image into.\n * @param {OpenSeadragon.Placement} [anchor=OpenSeadragon.Placement.CENTER]\n * How to anchor the image in the bounds.\n * @param {Boolean} [immediately=false] Whether to animate to the new size\n * or snap immediately.\n * @fires OpenSeadragon.TiledImage.event:bounds-change\n */fitBounds:function fitBounds(bounds,anchor,immediately){anchor=anchor||$.Placement.CENTER;var anchorProperties=$.Placement.properties[anchor];var aspectRatio=this.contentAspectX;var xOffset=0;var yOffset=0;var displayedWidthRatio=1;var displayedHeightRatio=1;if(this._clip){aspectRatio=this._clip.getAspectRatio();displayedWidthRatio=this._clip.width/this.source.dimensions.x;displayedHeightRatio=this._clip.height/this.source.dimensions.y;if(bounds.getAspectRatio()>aspectRatio){xOffset=this._clip.x/this._clip.height*bounds.height;yOffset=this._clip.y/this._clip.height*bounds.height;}else{xOffset=this._clip.x/this._clip.width*bounds.width;yOffset=this._clip.y/this._clip.width*bounds.width;}}if(bounds.getAspectRatio()>aspectRatio){// We will have margins on the X axis\nvar height=bounds.height/displayedHeightRatio;var marginLeft=0;if(anchorProperties.isHorizontallyCentered){marginLeft=(bounds.width-bounds.height*aspectRatio)/2;}else if(anchorProperties.isRight){marginLeft=bounds.width-bounds.height*aspectRatio;}this.setPosition(new $.Point(bounds.x-xOffset+marginLeft,bounds.y-yOffset),immediately);this.setHeight(height,immediately);}else{// We will have margins on the Y axis\nvar width=bounds.width/displayedWidthRatio;var marginTop=0;if(anchorProperties.isVerticallyCentered){marginTop=(bounds.height-bounds.width/aspectRatio)/2;}else if(anchorProperties.isBottom){marginTop=bounds.height-bounds.width/aspectRatio;}this.setPosition(new $.Point(bounds.x-xOffset,bounds.y-yOffset+marginTop),immediately);this.setWidth(width,immediately);}},/**\n * @returns {OpenSeadragon.Rect|null} The TiledImage's current clip rectangle,\n * in image pixels, or null if none.\n */getClip:function getClip(){if(this._clip){return this._clip.clone();}return null;},/**\n * @param {OpenSeadragon.Rect|null} newClip - An area, in image pixels, to clip to\n * (portions of the image outside of this area will not be visible). Only works on\n * browsers that support the HTML5 canvas.\n * @fires OpenSeadragon.TiledImage.event:clip-change\n */setClip:function setClip(newClip){$.console.assert(!newClip||newClip instanceof $.Rect,\"[TiledImage.setClip] newClip must be an OpenSeadragon.Rect or null\");if(newClip instanceof $.Rect){this._clip=newClip.clone();}else{this._clip=null;}this._needsDraw=true;/**\n * Raised when the TiledImage's clip is changed.\n * @event clip-change\n * @memberOf OpenSeadragon.TiledImage\n * @type {object}\n * @property {OpenSeadragon.TiledImage} eventSource - A reference to the\n * TiledImage which raised the event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('clip-change');},/**\n * @returns {Number} The TiledImage's current opacity.\n */getOpacity:function getOpacity(){return this.opacity;},/**\n * @param {Number} opacity Opacity the tiled image should be drawn at.\n * @fires OpenSeadragon.TiledImage.event:opacity-change\n */setOpacity:function setOpacity(opacity){if(opacity===this.opacity){return;}this.opacity=opacity;this._needsDraw=true;/**\n * Raised when the TiledImage's opacity is changed.\n * @event opacity-change\n * @memberOf OpenSeadragon.TiledImage\n * @type {object}\n * @property {Number} opacity - The new opacity value.\n * @property {OpenSeadragon.TiledImage} eventSource - A reference to the\n * TiledImage which raised the event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('opacity-change',{opacity:this.opacity});},/**\n * @returns {Boolean} whether the tiledImage can load its tiles even when it has zero opacity.\n */getPreload:function getPreload(){return this._preload;},/**\n * Set true to load even when hidden. Set false to block loading when hidden.\n */setPreload:function setPreload(preload){this._preload=!!preload;this._needsDraw=true;},/**\n * Get the rotation of this tiled image in degrees.\n * @param {Boolean} [current=false] True for current rotation, false for target.\n * @returns {Number} the rotation of this tiled image in degrees.\n */getRotation:function getRotation(current){return current?this._degreesSpring.current.value:this._degreesSpring.target.value;},/**\n * Set the current rotation of this tiled image in degrees.\n * @param {Number} degrees the rotation in degrees.\n * @param {Boolean} [immediately=false] Whether to animate to the new angle\n * or rotate immediately.\n * @fires OpenSeadragon.TiledImage.event:bounds-change\n */setRotation:function setRotation(degrees,immediately){if(this._degreesSpring.target.value===degrees&&this._degreesSpring.isAtTargetValue()){return;}if(immediately){this._degreesSpring.resetTo(degrees);}else{this._degreesSpring.springTo(degrees);}this._needsDraw=true;this._raiseBoundsChange();},/**\n * Get the point around which this tiled image is rotated\n * @private\n * @param {Boolean} current True for current rotation point, false for target.\n * @returns {OpenSeadragon.Point}\n */_getRotationPoint:function _getRotationPoint(current){return this.getBoundsNoRotate(current).getCenter();},/**\n * @returns {String} The TiledImage's current compositeOperation.\n */getCompositeOperation:function getCompositeOperation(){return this.compositeOperation;},/**\n * @param {String} compositeOperation the tiled image should be drawn with this globalCompositeOperation.\n * @fires OpenSeadragon.TiledImage.event:composite-operation-change\n */setCompositeOperation:function setCompositeOperation(compositeOperation){if(compositeOperation===this.compositeOperation){return;}this.compositeOperation=compositeOperation;this._needsDraw=true;/**\n * Raised when the TiledImage's opacity is changed.\n * @event composite-operation-change\n * @memberOf OpenSeadragon.TiledImage\n * @type {object}\n * @property {String} compositeOperation - The new compositeOperation value.\n * @property {OpenSeadragon.TiledImage} eventSource - A reference to the\n * TiledImage which raised the event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('composite-operation-change',{compositeOperation:this.compositeOperation});},// private\n_setScale:function _setScale(scale,immediately){var sameTarget=this._scaleSpring.target.value===scale;if(immediately){if(sameTarget&&this._scaleSpring.current.value===scale){return;}this._scaleSpring.resetTo(scale);this._updateForScale();this._needsDraw=true;}else{if(sameTarget){return;}this._scaleSpring.springTo(scale);this._updateForScale();this._needsDraw=true;}if(!sameTarget){this._raiseBoundsChange();}},// private\n_updateForScale:function _updateForScale(){this._worldWidthTarget=this._scaleSpring.target.value;this._worldHeightTarget=this.normHeight*this._scaleSpring.target.value;this._worldWidthCurrent=this._scaleSpring.current.value;this._worldHeightCurrent=this.normHeight*this._scaleSpring.current.value;},// private\n_raiseBoundsChange:function _raiseBoundsChange(){/**\n * Raised when the TiledImage's bounds are changed.\n * Note that this event is triggered only when the animation target is changed;\n * not for every frame of animation.\n * @event bounds-change\n * @memberOf OpenSeadragon.TiledImage\n * @type {object}\n * @property {OpenSeadragon.TiledImage} eventSource - A reference to the\n * TiledImage which raised the event.\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('bounds-change');},// private\n_isBottomItem:function _isBottomItem(){return this.viewer.world.getItemAt(0)===this;},// private\n_getLevelsInterval:function _getLevelsInterval(){var lowestLevel=Math.max(this.source.minLevel,Math.floor(Math.log(this.minZoomImageRatio)/Math.log(2)));var currentZeroRatio=this.viewport.deltaPixelsFromPointsNoRotate(this.source.getPixelRatio(0),true).x*this._scaleSpring.current.value;var highestLevel=Math.min(Math.abs(this.source.maxLevel),Math.abs(Math.floor(Math.log(currentZeroRatio/this.minPixelRatio)/Math.log(2))));// Calculations for the interval of levels to draw\n// can return invalid intervals; fix that here if necessary\nlowestLevel=Math.min(lowestLevel,highestLevel);return{lowestLevel:lowestLevel,highestLevel:highestLevel};},/**\n * @private\n * @inner\n * Pretty much every other line in this needs to be documented so it's clear\n * how each piece of this routine contributes to the drawing process. That's\n * why there are so many TODO's inside this function.\n */_updateViewport:function _updateViewport(){this._needsDraw=false;this._tilesLoading=0;this.loadingCoverage={};// Reset tile's internal drawn state\nwhile(this.lastDrawn.length>0){var tile=this.lastDrawn.pop();tile.beingDrawn=false;}var viewport=this.viewport;var drawArea=this._viewportToTiledImageRectangle(viewport.getBoundsWithMargins(true));if(!this.wrapHorizontal&&!this.wrapVertical){var tiledImageBounds=this._viewportToTiledImageRectangle(this.getClippedBounds(true));drawArea=drawArea.intersection(tiledImageBounds);if(drawArea===null){return;}}var levelsInterval=this._getLevelsInterval();var lowestLevel=levelsInterval.lowestLevel;var highestLevel=levelsInterval.highestLevel;var bestTile=null;var haveDrawn=false;var currentTime=$.now();// Update any level that will be drawn\nfor(var level=highestLevel;level>=lowestLevel;level--){var drawLevel=false;//Avoid calculations for draw if we have already drawn this\nvar currentRenderPixelRatio=viewport.deltaPixelsFromPointsNoRotate(this.source.getPixelRatio(level),true).x*this._scaleSpring.current.value;if(level===lowestLevel||!haveDrawn&¤tRenderPixelRatio>=this.minPixelRatio){drawLevel=true;haveDrawn=true;}else if(!haveDrawn){continue;}//Perform calculations for draw if we haven't drawn this\nvar targetRenderPixelRatio=viewport.deltaPixelsFromPointsNoRotate(this.source.getPixelRatio(level),false).x*this._scaleSpring.current.value;var targetZeroRatio=viewport.deltaPixelsFromPointsNoRotate(this.source.getPixelRatio(Math.max(this.source.getClosestLevel(),0)),false).x*this._scaleSpring.current.value;var optimalRatio=this.immediateRender?1:targetZeroRatio;var levelOpacity=Math.min(1,(currentRenderPixelRatio-0.5)/0.5);var levelVisibility=optimalRatio/Math.abs(optimalRatio-targetRenderPixelRatio);// Update the level and keep track of 'best' tile to load\nbestTile=updateLevel(this,haveDrawn,drawLevel,level,levelOpacity,levelVisibility,drawArea,currentTime,bestTile);// Stop the loop if lower-res tiles would all be covered by\n// already drawn tiles\nif(providesCoverage(this.coverage,level)){break;}}// Perform the actual drawing\ndrawTiles(this,this.lastDrawn);// Load the new 'best' tile\nif(bestTile&&!bestTile.context2D){loadTile(this,bestTile,currentTime);this._needsDraw=true;this._setFullyLoaded(false);}else{this._setFullyLoaded(this._tilesLoading===0);}},// private\n_getCornerTiles:function _getCornerTiles(level,topLeftBound,bottomRightBound){var leftX;var rightX;if(this.wrapHorizontal){leftX=$.positiveModulo(topLeftBound.x,1);rightX=$.positiveModulo(bottomRightBound.x,1);}else{leftX=Math.max(0,topLeftBound.x);rightX=Math.min(1,bottomRightBound.x);}var topY;var bottomY;var aspectRatio=1/this.source.aspectRatio;if(this.wrapVertical){topY=$.positiveModulo(topLeftBound.y,aspectRatio);bottomY=$.positiveModulo(bottomRightBound.y,aspectRatio);}else{topY=Math.max(0,topLeftBound.y);bottomY=Math.min(aspectRatio,bottomRightBound.y);}var topLeftTile=this.source.getTileAtPoint(level,new $.Point(leftX,topY));var bottomRightTile=this.source.getTileAtPoint(level,new $.Point(rightX,bottomY));var numTiles=this.source.getNumTiles(level);if(this.wrapHorizontal){topLeftTile.x+=numTiles.x*Math.floor(topLeftBound.x);bottomRightTile.x+=numTiles.x*Math.floor(bottomRightBound.x);}if(this.wrapVertical){topLeftTile.y+=numTiles.y*Math.floor(topLeftBound.y/aspectRatio);bottomRightTile.y+=numTiles.y*Math.floor(bottomRightBound.y/aspectRatio);}return{topLeft:topLeftTile,bottomRight:bottomRightTile};}});/**\n * @private\n * @inner\n * Updates all tiles at a given resolution level.\n * @param {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.\n * @param {Boolean} haveDrawn\n * @param {Boolean} drawLevel\n * @param {Number} level\n * @param {Number} levelOpacity\n * @param {Number} levelVisibility\n * @param {OpenSeadragon.Point} viewportTL - The index of the most top-left visible tile.\n * @param {OpenSeadragon.Point} viewportBR - The index of the most bottom-right visible tile.\n * @param {Number} currentTime\n * @param {OpenSeadragon.Tile} best - The current \"best\" tile to draw.\n */function updateLevel(tiledImage,haveDrawn,drawLevel,level,levelOpacity,levelVisibility,drawArea,currentTime,best){var topLeftBound=drawArea.getBoundingBox().getTopLeft();var bottomRightBound=drawArea.getBoundingBox().getBottomRight();if(tiledImage.viewer){/**\n * - Needs documentation -\n *\n * @event update-level\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.\n * @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.\n * @property {Object} havedrawn\n * @property {Object} level\n * @property {Object} opacity\n * @property {Object} visibility\n * @property {OpenSeadragon.Rect} drawArea\n * @property {Object} topleft deprecated, use drawArea instead\n * @property {Object} bottomright deprecated, use drawArea instead\n * @property {Object} currenttime\n * @property {Object} best\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */tiledImage.viewer.raiseEvent('update-level',{tiledImage:tiledImage,havedrawn:haveDrawn,level:level,opacity:levelOpacity,visibility:levelVisibility,drawArea:drawArea,topleft:topLeftBound,bottomright:bottomRightBound,currenttime:currentTime,best:best});}resetCoverage(tiledImage.coverage,level);resetCoverage(tiledImage.loadingCoverage,level);//OK, a new drawing so do your calculations\nvar cornerTiles=tiledImage._getCornerTiles(level,topLeftBound,bottomRightBound);var topLeftTile=cornerTiles.topLeft;var bottomRightTile=cornerTiles.bottomRight;var numberOfTiles=tiledImage.source.getNumTiles(level);var viewportCenter=tiledImage.viewport.pixelFromPoint(tiledImage.viewport.getCenter());for(var x=topLeftTile.x;x<=bottomRightTile.x;x++){for(var y=topLeftTile.y;y<=bottomRightTile.y;y++){// Optimisation disabled with wrapping because getTileBounds does not\n// work correctly with x and y outside of the number of tiles\nif(!tiledImage.wrapHorizontal&&!tiledImage.wrapVertical){var tileBounds=tiledImage.source.getTileBounds(level,x,y);if(drawArea.intersection(tileBounds)===null){// This tile is outside of the viewport, no need to draw it\ncontinue;}}best=updateTile(tiledImage,drawLevel,haveDrawn,x,y,level,levelOpacity,levelVisibility,viewportCenter,numberOfTiles,currentTime,best);}}return best;}/**\n * @private\n * @inner\n * Update a single tile at a particular resolution level.\n * @param {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.\n * @param {Boolean} haveDrawn\n * @param {Boolean} drawLevel\n * @param {Number} x\n * @param {Number} y\n * @param {Number} level\n * @param {Number} levelOpacity\n * @param {Number} levelVisibility\n * @param {OpenSeadragon.Point} viewportCenter\n * @param {Number} numberOfTiles\n * @param {Number} currentTime\n * @param {OpenSeadragon.Tile} best - The current \"best\" tile to draw.\n */function updateTile(tiledImage,haveDrawn,drawLevel,x,y,level,levelOpacity,levelVisibility,viewportCenter,numberOfTiles,currentTime,best){var tile=getTile(x,y,level,tiledImage,tiledImage.source,tiledImage.tilesMatrix,currentTime,numberOfTiles,tiledImage._worldWidthCurrent,tiledImage._worldHeightCurrent),drawTile=drawLevel;if(tiledImage.viewer){/**\n * - Needs documentation -\n *\n * @event update-tile\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.\n * @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.\n * @property {OpenSeadragon.Tile} tile\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */tiledImage.viewer.raiseEvent('update-tile',{tiledImage:tiledImage,tile:tile});}setCoverage(tiledImage.coverage,level,x,y,false);var loadingCoverage=tile.loaded||tile.loading||isCovered(tiledImage.loadingCoverage,level,x,y);setCoverage(tiledImage.loadingCoverage,level,x,y,loadingCoverage);if(!tile.exists){return best;}if(haveDrawn&&!drawTile){if(isCovered(tiledImage.coverage,level,x,y)){setCoverage(tiledImage.coverage,level,x,y,true);}else{drawTile=true;}}if(!drawTile){return best;}positionTile(tile,tiledImage.source.tileOverlap,tiledImage.viewport,viewportCenter,levelVisibility,tiledImage);if(!tile.loaded){if(tile.context2D){setTileLoaded(tiledImage,tile);}else{var imageRecord=tiledImage._tileCache.getImageRecord(tile.cacheKey);if(imageRecord){var image=imageRecord.getImage();setTileLoaded(tiledImage,tile,image);}}}if(tile.loaded){var needsDraw=blendTile(tiledImage,tile,x,y,level,levelOpacity,currentTime);if(needsDraw){tiledImage._needsDraw=true;}}else if(tile.loading){// the tile is already in the download queue\ntiledImage._tilesLoading++;}else if(!loadingCoverage){best=compareTiles(best,tile);}return best;}/**\n * @private\n * @inner\n * Obtains a tile at the given location.\n * @param {Number} x\n * @param {Number} y\n * @param {Number} level\n * @param {OpenSeadragon.TiledImage} tiledImage\n * @param {OpenSeadragon.TileSource} tileSource\n * @param {Object} tilesMatrix - A '3d' dictionary [level][x][y] --> Tile.\n * @param {Number} time\n * @param {Number} numTiles\n * @param {Number} worldWidth\n * @param {Number} worldHeight\n * @returns {OpenSeadragon.Tile}\n */function getTile(x,y,level,tiledImage,tileSource,tilesMatrix,time,numTiles,worldWidth,worldHeight){var xMod,yMod,bounds,sourceBounds,exists,url,ajaxHeaders,context2D,tile;if(!tilesMatrix[level]){tilesMatrix[level]={};}if(!tilesMatrix[level][x]){tilesMatrix[level][x]={};}if(!tilesMatrix[level][x][y]){xMod=(numTiles.x+x%numTiles.x)%numTiles.x;yMod=(numTiles.y+y%numTiles.y)%numTiles.y;bounds=tileSource.getTileBounds(level,xMod,yMod);sourceBounds=tileSource.getTileBounds(level,xMod,yMod,true);exists=tileSource.tileExists(level,xMod,yMod);url=tileSource.getTileUrl(level,xMod,yMod);// Headers are only applicable if loadTilesWithAjax is set\nif(tiledImage.loadTilesWithAjax){ajaxHeaders=tileSource.getTileAjaxHeaders(level,xMod,yMod);// Combine tile AJAX headers with tiled image AJAX headers (if applicable)\nif($.isPlainObject(tiledImage.ajaxHeaders)){ajaxHeaders=$.extend({},tiledImage.ajaxHeaders,ajaxHeaders);}}else{ajaxHeaders=null;}context2D=tileSource.getContext2D?tileSource.getContext2D(level,xMod,yMod):undefined;bounds.x+=(x-xMod)/numTiles.x;bounds.y+=worldHeight/worldWidth*((y-yMod)/numTiles.y);tile=new $.Tile(level,x,y,bounds,exists,url,context2D,tiledImage.loadTilesWithAjax,ajaxHeaders,sourceBounds);if(xMod===numTiles.x-1){tile.isRightMost=true;}if(yMod===numTiles.y-1){tile.isBottomMost=true;}tilesMatrix[level][x][y]=tile;}tile=tilesMatrix[level][x][y];tile.lastTouchTime=time;return tile;}/**\n * @private\n * @inner\n * Dispatch a job to the ImageLoader to load the Image for a Tile.\n * @param {OpenSeadragon.TiledImage} tiledImage\n * @param {OpenSeadragon.Tile} tile\n * @param {Number} time\n */function loadTile(tiledImage,tile,time){tile.loading=true;var customAjax;// Bind tiledImage if filtering Ajax\nif($.isFunction(tiledImage.makeAjaxRequest)){customAjax=tiledImage.makeAjaxRequest;}tiledImage._imageLoader.addJob({src:tile.url,makeAjaxRequest:customAjax,loadWithAjax:tile.loadWithAjax,ajaxHeaders:tile.ajaxHeaders,crossOriginPolicy:tiledImage.crossOriginPolicy,ajaxWithCredentials:tiledImage.ajaxWithCredentials,callback:function callback(image,errorMsg,tileRequest){onTileLoad(tiledImage,tile,time,image,errorMsg,tileRequest);},abort:function abort(){tile.loading=false;}});}/**\n * @private\n * @inner\n * Callback fired when a Tile's Image finished downloading.\n * @param {OpenSeadragon.TiledImage} tiledImage\n * @param {OpenSeadragon.Tile} tile\n * @param {Number} time\n * @param {Image} image\n * @param {String} errorMsg\n * @param {XMLHttpRequest} tileRequest\n */function onTileLoad(tiledImage,tile,time,image,errorMsg,tileRequest){if(!image){$.console.log(\"Tile %s failed to load: %s - error: %s\",tile,tile.url,errorMsg);/**\n * Triggered when a tile fails to load.\n *\n * @event tile-load-failed\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Tile} tile - The tile that failed to load.\n * @property {OpenSeadragon.TiledImage} tiledImage - The tiled image the tile belongs to.\n * @property {number} time - The time in milliseconds when the tile load began.\n * @property {string} message - The error message.\n * @property {XMLHttpRequest} tileRequest - The XMLHttpRequest used to load the tile if available.\n */tiledImage.viewer.raiseEvent(\"tile-load-failed\",{tile:tile,tiledImage:tiledImage,time:time,message:errorMsg,tileRequest:tileRequest});tile.loading=false;tile.exists=false;return;}if(time Boolean.\n * @param {Number} level - The resolution level of the tile.\n * @param {Number} x - The X position of the tile.\n * @param {Number} y - The Y position of the tile.\n * @returns {Boolean}\n */function providesCoverage(coverage,level,x,y){var rows,cols,i,j;if(!coverage[level]){return false;}if(x===undefined||y===undefined){rows=coverage[level];for(i in rows){if(rows.hasOwnProperty(i)){cols=rows[i];for(j in cols){if(cols.hasOwnProperty(j)&&!cols[j]){return false;}}}}return true;}return coverage[level][x]===undefined||coverage[level][x][y]===undefined||coverage[level][x][y]===true;}/**\n * @private\n * @inner\n * Returns true if the given tile is completely covered by higher-level\n * tiles of higher resolution representing the same content. If neither x\n * nor y is given, returns true if the entire visible level is covered.\n *\n * @param {Object} coverage - A '3d' dictionary [level][x][y] --> Boolean.\n * @param {Number} level - The resolution level of the tile.\n * @param {Number} x - The X position of the tile.\n * @param {Number} y - The Y position of the tile.\n * @returns {Boolean}\n */function isCovered(coverage,level,x,y){if(x===undefined||y===undefined){return providesCoverage(coverage,level+1);}else{return providesCoverage(coverage,level+1,2*x,2*y)&&providesCoverage(coverage,level+1,2*x,2*y+1)&&providesCoverage(coverage,level+1,2*x+1,2*y)&&providesCoverage(coverage,level+1,2*x+1,2*y+1);}}/**\n * @private\n * @inner\n * Sets whether the given tile provides coverage or not.\n *\n * @param {Object} coverage - A '3d' dictionary [level][x][y] --> Boolean.\n * @param {Number} level - The resolution level of the tile.\n * @param {Number} x - The X position of the tile.\n * @param {Number} y - The Y position of the tile.\n * @param {Boolean} covers - Whether the tile provides coverage.\n */function setCoverage(coverage,level,x,y,covers){if(!coverage[level]){$.console.warn(\"Setting coverage for a tile before its level's coverage has been reset: %s\",level);return;}if(!coverage[level][x]){coverage[level][x]={};}coverage[level][x][y]=covers;}/**\n * @private\n * @inner\n * Resets coverage information for the given level. This should be called\n * after every draw routine. Note that at the beginning of the next draw\n * routine, coverage for every visible tile should be explicitly set.\n *\n * @param {Object} coverage - A '3d' dictionary [level][x][y] --> Boolean.\n * @param {Number} level - The resolution level of tiles to completely reset.\n */function resetCoverage(coverage,level){coverage[level]={};}/**\n * @private\n * @inner\n * Determines whether the 'last best' tile for the area is better than the\n * tile in question.\n *\n * @param {OpenSeadragon.Tile} previousBest\n * @param {OpenSeadragon.Tile} tile\n * @returns {OpenSeadragon.Tile} The new best tile.\n */function compareTiles(previousBest,tile){if(!previousBest){return tile;}if(tile.visibility>previousBest.visibility){return tile;}else if(tile.visibility==previousBest.visibility){if(tile.squaredDistance1&&imageZoom>tiledImage.smoothTileEdgesMinZoom&&!tiledImage.iOSDevice&&tiledImage.getRotation(true)%360===0&&// TODO: support tile edge smoothing with tiled image rotation.\n$.supportsCanvas){// When zoomed in a lot (>100%) the tile edges are visible.\n// So we have to composite them at ~100% and scale them up together.\n// Note: Disabled on iOS devices per default as it causes a native crash\nuseSketch=true;sketchScale=tile.getScaleForEdgeSmoothing();sketchTranslate=tile.getTranslationForEdgeSmoothing(sketchScale,tiledImage._drawer.getCanvasSize(false),tiledImage._drawer.getCanvasSize(true));}var bounds;if(useSketch){if(!sketchScale){// Except when edge smoothing, we only clean the part of the\n// sketch canvas we are going to use for performance reasons.\nbounds=tiledImage.viewport.viewportToViewerElementRectangle(tiledImage.getClippedBounds(true)).getIntegerBoundingBox().times($.pixelDensityRatio);}tiledImage._drawer._clear(true,bounds);}// When scaling, we must rotate only when blending the sketch canvas to\n// avoid interpolation\nif(!sketchScale){if(tiledImage.viewport.degrees!==0){tiledImage._drawer._offsetForRotation({degrees:tiledImage.viewport.degrees,useSketch:useSketch});}else{if(tiledImage._drawer.viewer.viewport.flipped){tiledImage._drawer._flip({});}}if(tiledImage.getRotation(true)%360!==0){tiledImage._drawer._offsetForRotation({degrees:tiledImage.getRotation(true),point:tiledImage.viewport.pixelFromPointNoRotate(tiledImage._getRotationPoint(true),true),useSketch:useSketch});}}var usedClip=false;if(tiledImage._clip){tiledImage._drawer.saveContext(useSketch);var box=tiledImage.imageToViewportRectangle(tiledImage._clip,true);box=box.rotate(-tiledImage.getRotation(true),tiledImage._getRotationPoint(true));var clipRect=tiledImage._drawer.viewportToDrawerRectangle(box);if(sketchScale){clipRect=clipRect.times(sketchScale);}if(sketchTranslate){clipRect=clipRect.translate(sketchTranslate);}tiledImage._drawer.setClip(clipRect,useSketch);usedClip=true;}if(tiledImage.placeholderFillStyle&&tiledImage._hasOpaqueTile===false){var placeholderRect=tiledImage._drawer.viewportToDrawerRectangle(tiledImage.getBounds(true));if(sketchScale){placeholderRect=placeholderRect.times(sketchScale);}if(sketchTranslate){placeholderRect=placeholderRect.translate(sketchTranslate);}var fillStyle=null;if(typeof tiledImage.placeholderFillStyle===\"function\"){fillStyle=tiledImage.placeholderFillStyle(tiledImage,tiledImage._drawer.context);}else{fillStyle=tiledImage.placeholderFillStyle;}tiledImage._drawer.drawRectangle(placeholderRect,fillStyle,useSketch);}for(var i=lastDrawn.length-1;i>=0;i--){tile=lastDrawn[i];tiledImage._drawer.drawTile(tile,tiledImage._drawingHandler,useSketch,sketchScale,sketchTranslate);tile.beingDrawn=true;if(tiledImage.viewer){/**\n * - Needs documentation -\n *\n * @event tile-drawn\n * @memberof OpenSeadragon.Viewer\n * @type {object}\n * @property {OpenSeadragon.Viewer} eventSource - A reference to the Viewer which raised the event.\n * @property {OpenSeadragon.TiledImage} tiledImage - Which TiledImage is being drawn.\n * @property {OpenSeadragon.Tile} tile\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */tiledImage.viewer.raiseEvent('tile-drawn',{tiledImage:tiledImage,tile:tile});}}if(usedClip){tiledImage._drawer.restoreContext(useSketch);}if(!sketchScale){if(tiledImage.getRotation(true)%360!==0){tiledImage._drawer._restoreRotationChanges(useSketch);}if(tiledImage.viewport.degrees!==0){tiledImage._drawer._restoreRotationChanges(useSketch);}else{if(tiledImage._drawer.viewer.viewport.flipped){tiledImage._drawer._flip({});}}}if(useSketch){if(sketchScale){if(tiledImage.viewport.degrees!==0){tiledImage._drawer._offsetForRotation({degrees:tiledImage.viewport.degrees,useSketch:false});}if(tiledImage.getRotation(true)%360!==0){tiledImage._drawer._offsetForRotation({degrees:tiledImage.getRotation(true),point:tiledImage.viewport.pixelFromPointNoRotate(tiledImage._getRotationPoint(true),true),useSketch:false});}}tiledImage._drawer.blendSketch({opacity:tiledImage.opacity,scale:sketchScale,translate:sketchTranslate,compositeOperation:tiledImage.compositeOperation,bounds:bounds});if(sketchScale){if(tiledImage.getRotation(true)%360!==0){tiledImage._drawer._restoreRotationChanges(false);}if(tiledImage.viewport.degrees!==0){tiledImage._drawer._restoreRotationChanges(false);}}}drawDebugInfo(tiledImage,lastDrawn);}/**\n * @private\n * @inner\n * Draws special debug information for a TiledImage if in debug mode.\n * @param {OpenSeadragon.TiledImage} tiledImage\n * @param {OpenSeadragon.Tile[]} lastDrawn - An unordered list of Tiles drawn last frame.\n */function drawDebugInfo(tiledImage,lastDrawn){if(tiledImage.debugMode){for(var i=lastDrawn.length-1;i>=0;i--){var tile=lastDrawn[i];try{tiledImage._drawer.drawDebugInfo(tile,lastDrawn.length,i,tiledImage);}catch(e){$.console.error(e);}}}}})(OpenSeadragon);/*\n * OpenSeadragon - TileCache\n *\n * Copyright (C) 2009 CodePlex Foundation\n * Copyright (C) 2010-2013 OpenSeadragon contributors\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n * - Redistributions of source code must retain the above copyright notice,\n * this list of conditions and the following disclaimer.\n *\n * - Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * - Neither the name of CodePlex Foundation nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */(function($){// private class\nvar TileRecord=function TileRecord(options){$.console.assert(options,\"[TileCache.cacheTile] options is required\");$.console.assert(options.tile,\"[TileCache.cacheTile] options.tile is required\");$.console.assert(options.tiledImage,\"[TileCache.cacheTile] options.tiledImage is required\");this.tile=options.tile;this.tiledImage=options.tiledImage;};// private class\nvar ImageRecord=function ImageRecord(options){$.console.assert(options,\"[ImageRecord] options is required\");$.console.assert(options.image,\"[ImageRecord] options.image is required\");this._image=options.image;this._tiles=[];};ImageRecord.prototype={destroy:function destroy(){this._image=null;this._renderedContext=null;this._tiles=null;},getImage:function getImage(){return this._image;},getRenderedContext:function getRenderedContext(){if(!this._renderedContext){var canvas=document.createElement('canvas');canvas.width=this._image.width;canvas.height=this._image.height;this._renderedContext=canvas.getContext('2d');this._renderedContext.drawImage(this._image,0,0);//since we are caching the prerendered image on a canvas\n//allow the image to not be held in memory\nthis._image=null;}return this._renderedContext;},setRenderedContext:function setRenderedContext(renderedContext){$.console.error(\"ImageRecord.setRenderedContext is deprecated. \"+\"The rendered context should be created by the ImageRecord \"+\"itself when calling ImageRecord.getRenderedContext.\");this._renderedContext=renderedContext;},addTile:function addTile(tile){$.console.assert(tile,'[ImageRecord.addTile] tile is required');this._tiles.push(tile);},removeTile:function removeTile(tile){for(var i=0;ithis._maxImageCacheCount){var worstTile=null;var worstTileIndex=-1;var worstTileRecord=null;var prevTile,worstTime,worstLevel,prevTime,prevLevel,prevTileRecord;for(var i=this._tilesLoaded.length-1;i>=0;i--){prevTileRecord=this._tilesLoaded[i];prevTile=prevTileRecord.tile;if(prevTile.level<=cutoff||prevTile.beingDrawn){continue;}else if(!worstTile){worstTile=prevTile;worstTileIndex=i;worstTileRecord=prevTileRecord;continue;}prevTime=prevTile.lastTouchTime;worstTime=worstTile.lastTouchTime;prevLevel=prevTile.level;worstLevel=worstTile.level;if(prevTimeworstLevel){worstTile=prevTile;worstTileIndex=i;worstTileRecord=prevTileRecord;}}if(worstTile&&worstTileIndex>=0){this._unloadTile(worstTileRecord);insertionIndex=worstTileIndex;}}this._tilesLoaded[insertionIndex]=new TileRecord({tile:options.tile,tiledImage:options.tiledImage});},/**\n * Clears all tiles associated with the specified tiledImage.\n * @param {OpenSeadragon.TiledImage} tiledImage\n */clearTilesFor:function clearTilesFor(tiledImage){$.console.assert(tiledImage,'[TileCache.clearTilesFor] tiledImage is required');var tileRecord;for(var i=0;i=this._items.length){throw new Error(\"Index bigger than number of layers.\");}if(index===oldIndex||oldIndex===-1){return;}this._items.splice(oldIndex,1);this._items.splice(index,0,item);this._needsDraw=true;/**\n * Raised when the order of the indexes has been changed.\n * @event item-index-change\n * @memberOf OpenSeadragon.World\n * @type {object}\n * @property {OpenSeadragon.World} eventSource - A reference to the World which raised the event.\n * @property {OpenSeadragon.TiledImage} item - The item whose index has\n * been changed\n * @property {Number} previousIndex - The previous index of the item\n * @property {Number} newIndex - The new index of the item\n * @property {?Object} userData - Arbitrary subscriber-defined object.\n */this.raiseEvent('item-index-change',{item:item,previousIndex:oldIndex,newIndex:index});},/**\n * Remove an item.\n * @param {OpenSeadragon.TiledImage} item - The item to remove.\n * @fires OpenSeadragon.World.event:remove-item\n * @fires OpenSeadragon.World.event:metrics-change\n */removeItem:function removeItem(item){$.console.assert(item,\"[World.removeItem] item is required\");var index=$.indexOf(this._items,item);if(index===-1){return;}item.removeHandler('bounds-change',this._delegatedFigureSizes);item.removeHandler('clip-change',this._delegatedFigureSizes);item.destroy();this._items.splice(index,1);this._figureSizes();this._needsDraw=true;this._raiseRemoveItem(item);},/**\n * Remove all items.\n * @fires OpenSeadragon.World.event:remove-item\n * @fires OpenSeadragon.World.event:metrics-change\n */removeAll:function removeAll(){// We need to make sure any pending images are canceled so the world items don't get messed up\nthis.viewer._cancelPendingImages();var item;var i;for(i=0;ibox.height){width=tileSize;}else{width=tileSize*(box.width/box.height);}height=width*(box.height/box.width);position=new $.Point(x+(tileSize-width)/2,y+(tileSize-height)/2);item.setPosition(position,immediately);item.setWidth(width,immediately);if(layout==='horizontal'){x+=increment;}else{y+=increment;}}this.setAutoRefigureSizes(true);},// private\n_figureSizes:function _figureSizes(){var oldHomeBounds=this._homeBounds?this._homeBounds.clone():null;var oldContentSize=this._contentSize?this._contentSize.clone():null;var oldContentFactor=this._contentFactor||0;if(!this._items.length){this._homeBounds=new $.Rect(0,0,1,1);this._contentSize=new $.Point(1,1);this._contentFactor=1;}else{var item=this._items[0];var bounds=item.getBounds();this._contentFactor=item.getContentSize().x/bounds.width;var clippedBounds=item.getClippedBounds().getBoundingBox();var left=clippedBounds.x;var top=clippedBounds.y;var right=clippedBounds.x+clippedBounds.width;var bottom=clippedBounds.y+clippedBounds.height;for(var i=1;i