PageRenderTime 269ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

/src/openseadragon.js

https://github.com/manmeet90/openseadragon
JavaScript | 2472 lines | 1106 code | 272 blank | 1094 comment | 219 complexity | 53c050f174221960935c175fb6a8684c MD5 | raw file
Possible License(s): BSD-3-Clause
  1. /*
  2. * OpenSeadragon
  3. *
  4. * Copyright (C) 2009 CodePlex Foundation
  5. * Copyright (C) 2010-2013 OpenSeadragon contributors
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions are
  9. * met:
  10. *
  11. * - Redistributions of source code must retain the above copyright notice,
  12. * this list of conditions and the following disclaimer.
  13. *
  14. * - Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in the
  16. * documentation and/or other materials provided with the distribution.
  17. *
  18. * - Neither the name of CodePlex Foundation nor the names of its
  19. * contributors may be used to endorse or promote products derived from
  20. * this software without specific prior written permission.
  21. *
  22. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  23. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  24. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  25. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  26. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  27. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
  28. * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  29. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  30. * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  31. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  32. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  33. */
  34. /*
  35. * Portions of this source file taken from jQuery:
  36. *
  37. * Copyright 2011 John Resig
  38. *
  39. * Permission is hereby granted, free of charge, to any person obtaining
  40. * a copy of this software and associated documentation files (the
  41. * "Software"), to deal in the Software without restriction, including
  42. * without limitation the rights to use, copy, modify, merge, publish,
  43. * distribute, sublicense, and/or sell copies of the Software, and to
  44. * permit persons to whom the Software is furnished to do so, subject to
  45. * the following conditions:
  46. *
  47. * The above copyright notice and this permission notice shall be
  48. * included in all copies or substantial portions of the Software.
  49. *
  50. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  51. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  52. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  53. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  54. * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  55. * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  56. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  57. */
  58. /*
  59. * Portions of this source file taken from mattsnider.com:
  60. *
  61. * Copyright (c) 2006-2013 Matt Snider
  62. *
  63. * Permission is hereby granted, free of charge, to any person obtaining a
  64. * copy of this software and associated documentation files (the "Software"),
  65. * to deal in the Software without restriction, including without limitation
  66. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  67. * and/or sell copies of the Software, and to permit persons to whom the
  68. * Software is furnished to do so, subject to the following conditions:
  69. *
  70. * The above copyright notice and this permission notice shall be included
  71. * in all copies or substantial portions of the Software.
  72. *
  73. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  74. * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  75. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  76. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  77. * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
  78. * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
  79. * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  80. */
  81. /**
  82. * @version <%= pkg.name %> <%= pkg.version %>
  83. *
  84. * @file
  85. * <h2><strong>OpenSeadragon - Javascript Deep Zooming</strong></h2>
  86. * <p>
  87. * OpenSeadragon provides an html interface for creating
  88. * deep zoom user interfaces. The simplest examples include deep
  89. * zoom for large resolution images, and complex examples include
  90. * zoomable map interfaces driven by SVG files.
  91. * </p>
  92. *
  93. */
  94. /**
  95. * @module OpenSeadragon
  96. *
  97. */
  98. /**
  99. * @namespace OpenSeadragon
  100. *
  101. * @classdesc The root namespace for OpenSeadragon. All utility methods
  102. * and classes are defined on or below this namespace.
  103. *
  104. */
  105. // Typedefs
  106. /**
  107. * All required and optional settings for instantiating a new instance of an OpenSeadragon image viewer.
  108. *
  109. * @typedef {Object} Options
  110. * @memberof OpenSeadragon
  111. *
  112. * @property {String} id
  113. * Id of the element to append the viewer's container element to. If not provided, the 'element' property must be provided.
  114. * If both the element and id properties are specified, the viewer is appended to the element provided in the element property.
  115. *
  116. * @property {Element} element
  117. * The element to append the viewer's container element to. If not provided, the 'id' property must be provided.
  118. * If both the element and id properties are specified, the viewer is appended to the element provided in the element property.
  119. *
  120. * @property {Array|String|Function|Object[]|Array[]|String[]|Function[]} [tileSources=null]
  121. * As an Array, the tileSource can hold either Objects or mixed
  122. * types of Arrays of Objects, Strings, or Functions. When a value is a String,
  123. * the tileSource is used to create a {@link OpenSeadragon.DziTileSource}.
  124. * When a value is a Function, the function is used to create a new
  125. * {@link OpenSeadragon.TileSource} whose abstract method
  126. * getUrl( level, x, y ) is implemented by the function. Finally, when it
  127. * is an Array of objects, it is used to create a
  128. * {@link OpenSeadragon.LegacyTileSource}.
  129. *
  130. * @property {Array} overlays Array of objects defining permanent overlays of
  131. * the viewer. The overlays added via this option and later removed with
  132. * {@link OpenSeadragon.Viewer#removeOverlay} will be added back when a new
  133. * image is opened.
  134. * To add overlays which can be definitively removed, one must use
  135. * {@link OpenSeadragon.Viewer#addOverlay}
  136. * If displaying a sequence of images, the overlays can be associated
  137. * with a specific page by passing the overlays array to the page's
  138. * tile source configuration.
  139. * Expected properties:
  140. * * x, y, (or px, py for pixel coordinates) to define the location.
  141. * * width, height in point if using x,y or in pixels if using px,py. If width
  142. * and height are specified, the overlay size is adjusted when zooming,
  143. * otherwise the size stays the size of the content (or the size defined by CSS).
  144. * * className to associate a class to the overlay
  145. * * id to set the overlay element. If an element with this id already exists,
  146. * it is reused, otherwise it is created. If not specified, a new element is
  147. * created.
  148. * * placement a string to define the relative position to the viewport.
  149. * Only used if no width and height are specified. Default: 'TOP_LEFT'.
  150. * See {@link OpenSeadragon.OverlayPlacement} for possible values.
  151. *
  152. * @property {String} [xmlPath=null]
  153. * <strong>DEPRECATED</strong>. A relative path to load a DZI file from the server.
  154. * Prefer the newer Options.tileSources.
  155. *
  156. * @property {String} [prefixUrl='/images/']
  157. * Prepends the prefixUrl to navImages paths, which is very useful
  158. * since the default paths are rarely useful for production
  159. * environments.
  160. *
  161. * @property {OpenSeadragon.NavImages} [navImages]
  162. * An object with a property for each button or other built-in navigation
  163. * control, eg the current 'zoomIn', 'zoomOut', 'home', and 'fullpage'.
  164. * Each of those in turn provides an image path for each state of the button
  165. * or navigation control, eg 'REST', 'GROUP', 'HOVER', 'PRESS'. Finally the
  166. * image paths, by default assume there is a folder on the servers root path
  167. * called '/images', eg '/images/zoomin_rest.png'. If you need to adjust
  168. * these paths, prefer setting the option.prefixUrl rather than overriding
  169. * every image path directly through this setting.
  170. *
  171. * @property {Object} [tileHost=null]
  172. * TODO: Implement this. Currently not used.
  173. *
  174. * @property {Boolean} [debugMode=false]
  175. * TODO: provide an in-screen panel providing event detail feedback.
  176. *
  177. * @property {String} [debugGridColor='#437AB2']
  178. *
  179. * @property {Number} [blendTime=0]
  180. * Specifies the duration of animation as higher or lower level tiles are
  181. * replacing the existing tile.
  182. *
  183. * @property {Boolean} [alwaysBlend=false]
  184. * Forces the tile to always blend. By default the tiles skip blending
  185. * when the blendTime is surpassed and the current animation frame would
  186. * not complete the blend.
  187. *
  188. * @property {Boolean} [autoHideControls=true]
  189. * If the user stops interacting with the viewport, fade the navigation
  190. * controls. Useful for presentation since the controls are by default
  191. * floated on top of the image the user is viewing.
  192. *
  193. * @property {Boolean} [immediateRender=false]
  194. * Render the best closest level first, ignoring the lowering levels which
  195. * provide the effect of very blurry to sharp. It is recommended to change
  196. * setting to true for mobile devices.
  197. *
  198. * @property {Number} [defaultZoomLevel=0]
  199. * Zoom level to use when image is first opened or the home button is clicked.
  200. * If 0, adjusts to fit viewer.
  201. *
  202. * @property {Number} [opacity=1]
  203. * Opacity of the drawer (1=opaque, 0=transparent)
  204. *
  205. * @property {Number} [layersAspectRatioEpsilon=0.0001]
  206. * Maximum aspectRatio mismatch between 2 layers.
  207. *
  208. * @property {Number} [degrees=0]
  209. * Initial rotation.
  210. *
  211. * @property {Number} [minZoomLevel=null]
  212. *
  213. * @property {Number} [maxZoomLevel=null]
  214. *
  215. * @property {Boolean} [panHorizontal=true]
  216. * Allow horizontal pan.
  217. *
  218. * @property {Boolean} [panVertical=true]
  219. * Allow vertical pan.
  220. *
  221. * @property {Boolean} [constrainDuringPan=false]
  222. *
  223. * @property {Boolean} [wrapHorizontal=false]
  224. * Set to true to force the image to wrap horizontally within the viewport.
  225. * Useful for maps or images representing the surface of a sphere or cylinder.
  226. *
  227. * @property {Boolean} [wrapVertical=false]
  228. * Set to true to force the image to wrap vertically within the viewport.
  229. * Useful for maps or images representing the surface of a sphere or cylinder.
  230. *
  231. * @property {Number} [minZoomImageRatio=0.9]
  232. * The minimum percentage ( expressed as a number between 0 and 1 ) of
  233. * the viewport height or width at which the zoom out will be constrained.
  234. * Setting it to 0, for example will allow you to zoom out infinitly.
  235. *
  236. * @property {Number} [maxZoomPixelRatio=1.1]
  237. * The maximum ratio to allow a zoom-in to affect the highest level pixel
  238. * ratio. This can be set to Infinity to allow 'infinite' zooming into the
  239. * image though it is less effective visually if the HTML5 Canvas is not
  240. * availble on the viewing device.
  241. *
  242. * @property {Boolean} [autoResize=true]
  243. * Set to false to prevent polling for viewer size changes. Useful for providing custom resize behavior.
  244. *
  245. * @property {Number} [pixelsPerWheelLine=40]
  246. * For pixel-resolution scrolling devices, the number of pixels equal to one scroll line.
  247. *
  248. * @property {Number} [visibilityRatio=0.5]
  249. * The percentage ( as a number from 0 to 1 ) of the source image which
  250. * must be kept within the viewport. If the image is dragged beyond that
  251. * limit, it will 'bounce' back until the minimum visibility ration is
  252. * achieved. Setting this to 0 and wrapHorizontal ( or wrapVertical ) to
  253. * true will provide the effect of an infinitely scrolling viewport.
  254. *
  255. * @property {Number} [imageLoaderLimit=0]
  256. * The maximum number of image requests to make concurrently. By default
  257. * it is set to 0 allowing the browser to make the maximum number of
  258. * image requests in parallel as allowed by the browsers policy.
  259. *
  260. * @property {Number} [clickTimeThreshold=300]
  261. * If multiple mouse clicks occurs within less than this number of
  262. * milliseconds, treat them as a single click.
  263. *
  264. * @property {Number} [clickDistThreshold=5]
  265. * If a mouse or touch drag occurs and the distance to the starting drag
  266. * point is less than this many pixels, ignore the drag event.
  267. *
  268. * @property {Number} [springStiffness=5.0]
  269. *
  270. * @property {Number} [animationTime=1.2]
  271. * Specifies the animation duration per each {@link OpenSeadragon.Spring}
  272. * which occur when the image is dragged or zoomed.
  273. *
  274. * @property {OpenSeadragon.GestureSettings} [gestureSettingsMouse]
  275. * Settings for gestures generated by a mouse pointer device. (See {@link OpenSeadragon.GestureSettings})
  276. * @property {Boolean} [gestureSettingsMouse.scrollToZoom=true] - Zoom on scroll gesture
  277. * @property {Boolean} [gestureSettingsMouse.clickToZoom=true] - Zoom on click gesture
  278. * @property {Boolean} [gestureSettingsMouse.pinchToZoom=false] - Zoom on pinch gesture
  279. * @property {Boolean} [gestureSettingsMouse.flickEnabled=false] - Enable flick gesture
  280. * @property {Number} [gestureSettingsMouse.flickMinSpeed=20] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)
  281. * @property {Number} [gestureSettingsMouse.flickMomentum=0.40] - If flickEnabled is true, the momentum factor for the flick gesture
  282. *
  283. * @property {OpenSeadragon.GestureSettings} [gestureSettingsTouch]
  284. * Settings for gestures generated by a touch pointer device. (See {@link OpenSeadragon.GestureSettings})
  285. * @property {Boolean} [gestureSettingsTouch.scrollToZoom=false] - Zoom on scroll gesture
  286. * @property {Boolean} [gestureSettingsTouch.clickToZoom=false] - Zoom on click gesture
  287. * @property {Boolean} [gestureSettingsTouch.pinchToZoom=true] - Zoom on pinch gesture
  288. * @property {Boolean} [gestureSettingsTouch.flickEnabled=true] - Enable flick gesture
  289. * @property {Number} [gestureSettingsTouch.flickMinSpeed=20] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)
  290. * @property {Number} [gestureSettingsTouch.flickMomentum=0.40] - If flickEnabled is true, the momentum factor for the flick gesture
  291. *
  292. * @property {OpenSeadragon.GestureSettings} [gestureSettingsPen]
  293. * Settings for gestures generated by a pen pointer device. (See {@link OpenSeadragon.GestureSettings})
  294. * @property {Boolean} [gestureSettingsPen.scrollToZoom=false] - Zoom on scroll gesture
  295. * @property {Boolean} [gestureSettingsPen.clickToZoom=true] - Zoom on click gesture
  296. * @property {Boolean} [gestureSettingsPen.pinchToZoom=false] - Zoom on pinch gesture
  297. * @property {Boolean} [gestureSettingsPen.flickEnabled=false] - Enable flick gesture
  298. * @property {Number} [gestureSettingsPen.flickMinSpeed=20] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)
  299. * @property {Number} [gestureSettingsPen.flickMomentum=0.40] - If flickEnabled is true, the momentum factor for the flick gesture
  300. *
  301. * @property {OpenSeadragon.GestureSettings} [gestureSettingsUnknown]
  302. * Settings for gestures generated by unknown pointer devices. (See {@link OpenSeadragon.GestureSettings})
  303. * @property {Boolean} [gestureSettingsUnknown.scrollToZoom=true] - Zoom on scroll gesture
  304. * @property {Boolean} [gestureSettingsUnknown.clickToZoom=false] - Zoom on click gesture
  305. * @property {Boolean} [gestureSettingsUnknown.pinchToZoom=true] - Zoom on pinch gesture
  306. * @property {Boolean} [gestureSettingsUnknown.flickEnabled=true] - Enable flick gesture
  307. * @property {Number} [gestureSettingsUnknown.flickMinSpeed=20] - If flickEnabled is true, the minimum speed to initiate a flick gesture (pixels-per-second)
  308. * @property {Number} [gestureSettingsUnknown.flickMomentum=0.40] - If flickEnabled is true, the momentum factor for the flick gesture
  309. *
  310. * @property {Number} [zoomPerClick=2.0]
  311. * The "zoom distance" per mouse click or touch tap. <em><strong>Note:</strong> Setting this to 1.0 effectively disables the click-to-zoom feature (also see gestureSettings[Mouse|Touch|Pen].clickToZoom).</em>
  312. *
  313. * @property {Number} [zoomPerScroll=1.2]
  314. * The "zoom distance" per mouse scroll or touch pinch. <em><strong>Note:</strong> Setting this to 1.0 effectively disables the mouse-wheel zoom feature (also see gestureSettings[Mouse|Touch|Pen].scrollToZoom}).</em>
  315. *
  316. * @property {Number} [zoomPerSecond=1.0]
  317. * The number of seconds to animate a single zoom event over.
  318. *
  319. * @property {Boolean} [showNavigator=false]
  320. * Set to true to make the navigator minimap appear.
  321. *
  322. * @property {Boolean} [navigatorId=navigator-GENERATED DATE]
  323. * The ID of a div to hold the navigator minimap.
  324. * If an ID is specified, the navigatorPosition, navigatorSizeRatio, navigatorMaintainSizeRatio, and navigatorTop|Left|Height|Width options will be ignored.
  325. * If an ID is not specified, a div element will be generated and placed on top of the main image.
  326. *
  327. * @property {String} [navigatorPosition='TOP_RIGHT']
  328. * Valid values are 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', 'BOTTOM_RIGHT', or 'ABSOLUTE'.<br>
  329. * If 'ABSOLUTE' is specified, then navigatorTop|Left|Height|Width determines the size and position of the navigator minimap in the viewer, and navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.<br>
  330. * For 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM_LEFT', and 'BOTTOM_RIGHT', the navigatorSizeRatio or navigatorHeight|Width values determine the size of the navigator minimap.
  331. *
  332. * @property {Number} [navigatorSizeRatio=0.2]
  333. * Ratio of navigator size to viewer size. Ignored if navigatorHeight|Width are specified.
  334. *
  335. * @property {Boolean} [navigatorMaintainSizeRatio=false]
  336. * If true, the navigator minimap is resized (using navigatorSizeRatio) when the viewer size changes.
  337. *
  338. * @property {Number|String} [navigatorTop=null]
  339. * Specifies the location of the navigator minimap (see navigatorPosition).
  340. *
  341. * @property {Number|String} [navigatorLeft=null]
  342. * Specifies the location of the navigator minimap (see navigatorPosition).
  343. *
  344. * @property {Number|String} [navigatorHeight=null]
  345. * Specifies the size of the navigator minimap (see navigatorPosition).
  346. * If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.
  347. *
  348. * @property {Number|String} [navigatorWidth=null]
  349. * Specifies the size of the navigator minimap (see navigatorPosition).
  350. * If specified, navigatorSizeRatio and navigatorMaintainSizeRatio are ignored.
  351. *
  352. * @property {Boolean} [navigatorAutoResize=true]
  353. * Set to false to prevent polling for navigator size changes. Useful for providing custom resize behavior.
  354. * Setting to false can also improve performance when the navigator is configured to a fixed size.
  355. *
  356. * @property {Number} [controlsFadeDelay=2000]
  357. * The number of milliseconds to wait once the user has stopped interacting
  358. * with the interface before begining to fade the controls. Assumes
  359. * showNavigationControl and autoHideControls are both true.
  360. *
  361. * @property {Number} [controlsFadeLength=1500]
  362. * The number of milliseconds to animate the controls fading out.
  363. *
  364. * @property {Number} [maxImageCacheCount=200]
  365. * The max number of images we should keep in memory (per drawer).
  366. *
  367. * @property {Number} [timeout=30000]
  368. *
  369. * @property {Boolean} [useCanvas=true]
  370. * Set to false to not use an HTML canvas element for image rendering even if canvas is supported.
  371. *
  372. * @property {Number} [minPixelRatio=0.5]
  373. * The higher the minPixelRatio, the lower the quality of the image that
  374. * is considered sufficient to stop rendering a given zoom level. For
  375. * example, if you are targeting mobile devices with less bandwith you may
  376. * try setting this to 1.5 or higher.
  377. *
  378. * @property {Boolean} [mouseNavEnabled=true]
  379. * Is the user able to interact with the image via mouse or touch. Default
  380. * interactions include draging the image in a plane, and zooming in toward
  381. * and away from the image.
  382. *
  383. * @property {Boolean} [showNavigationControl=true]
  384. * Set to false to prevent the appearance of the default navigation controls.<br>
  385. * Note that if set to false, the customs buttons set by the options
  386. * zoomInButton, zoomOutButton etc, are rendered inactive.
  387. *
  388. * @property {OpenSeadragon.ControlAnchor} [navigationControlAnchor=TOP_LEFT]
  389. * Placement of the default navigation controls.
  390. * To set the placement of the sequence controls, see the
  391. * sequenceControlAnchor option.
  392. *
  393. * @property {Boolean} [showZoomControl=true]
  394. * If true then + and - buttons to zoom in and out are displayed.<br>
  395. * Note: {@link OpenSeadragon.Options.showNavigationControl} is overriding
  396. * this setting when set to false.
  397. *
  398. * @property {Boolean} [showHomeControl=true]
  399. * If true then the 'Go home' button is displayed to go back to the original
  400. * zoom and pan.<br>
  401. * Note: {@link OpenSeadragon.Options.showNavigationControl} is overriding
  402. * this setting when set to false.
  403. *
  404. * @property {Boolean} [showFullPageControl=true]
  405. * If true then the 'Toggle full page' button is displayed to switch
  406. * between full page and normal mode.<br>
  407. * Note: {@link OpenSeadragon.Options.showNavigationControl} is overriding
  408. * this setting when set to false.
  409. *
  410. * @property {Boolean} [showRotationControl=false]
  411. * If true then the rotate left/right controls will be displayed as part of the
  412. * standard controls. This is also subject to the browser support for rotate
  413. * (e.g. viewer.drawer.canRotate()).<br>
  414. * Note: {@link OpenSeadragon.Options.showNavigationControl} is overriding
  415. * this setting when set to false.
  416. *
  417. * @property {Boolean} [showSequenceControl=true]
  418. * If the viewer has been configured with a sequence of tile sources, then
  419. * provide buttons for navigating forward and backward through the images.
  420. *
  421. * @property {OpenSeadragon.ControlAnchor} [sequenceControlAnchor=TOP_LEFT]
  422. * Placement of the default sequence controls.
  423. *
  424. * @property {Boolean} [navPrevNextWrap=false]
  425. * If true then the 'previous' button will wrap to the last image when
  426. * viewing the first image and the 'next' button will wrap to the first
  427. * image when viewing the last image.
  428. *
  429. * @property {String} zoomInButton
  430. * Set the id of the custom 'Zoom in' button to use.
  431. * This is useful to have a custom button anywhere in the web page.<br>
  432. * To only change the button images, consider using
  433. * {@link OpenSeadragon.Options.navImages}
  434. *
  435. * @property {String} zoomOutButton
  436. * Set the id of the custom 'Zoom out' button to use.
  437. * This is useful to have a custom button anywhere in the web page.<br>
  438. * To only change the button images, consider using
  439. * {@link OpenSeadragon.Options.navImages}
  440. *
  441. * @property {String} homeButton
  442. * Set the id of the custom 'Go home' button to use.
  443. * This is useful to have a custom button anywhere in the web page.<br>
  444. * To only change the button images, consider using
  445. * {@link OpenSeadragon.Options.navImages}
  446. *
  447. * @property {String} fullPageButton
  448. * Set the id of the custom 'Toggle full page' button to use.
  449. * This is useful to have a custom button anywhere in the web page.<br>
  450. * To only change the button images, consider using
  451. * {@link OpenSeadragon.Options.navImages}
  452. *
  453. * @property {String} rotateLeftButton
  454. * Set the id of the custom 'Rotate left' button to use.
  455. * This is useful to have a custom button anywhere in the web page.<br>
  456. * To only change the button images, consider using
  457. * {@link OpenSeadragon.Options.navImages}
  458. *
  459. * @property {String} rotateRightButton
  460. * Set the id of the custom 'Rotate right' button to use.
  461. * This is useful to have a custom button anywhere in the web page.<br>
  462. * To only change the button images, consider using
  463. * {@link OpenSeadragon.Options.navImages}
  464. *
  465. * @property {String} previousButton
  466. * Set the id of the custom 'Previous page' button to use.
  467. * This is useful to have a custom button anywhere in the web page.<br>
  468. * To only change the button images, consider using
  469. * {@link OpenSeadragon.Options.navImages}
  470. *
  471. * @property {String} nextButton
  472. * Set the id of the custom 'Next page' button to use.
  473. * This is useful to have a custom button anywhere in the web page.<br>
  474. * To only change the button images, consider using
  475. * {@link OpenSeadragon.Options.navImages}
  476. *
  477. * @property {Number} [initialPage=0]
  478. * If the viewer has been configured with a sequence of tile sources, display this page initially.
  479. *
  480. * @property {Boolean} [preserveViewport=false]
  481. * If the viewer has been configured with a sequence of tile sources, then
  482. * normally navigating to through each image resets the viewport to 'home'
  483. * position. If preserveViewport is set to true, then the viewport position
  484. * is preserved when navigating between images in the sequence.
  485. *
  486. * @property {Boolean} [showReferenceStrip=false]
  487. * If the viewer has been configured with a sequence of tile sources, then
  488. * display a scrolling strip of image thumbnails for navigating through the images.
  489. *
  490. * @property {String} [referenceStripScroll='horizontal']
  491. *
  492. * @property {Element} [referenceStripElement=null]
  493. *
  494. * @property {Number} [referenceStripHeight=null]
  495. *
  496. * @property {Number} [referenceStripWidth=null]
  497. *
  498. * @property {String} [referenceStripPosition='BOTTOM_LEFT']
  499. *
  500. * @property {Number} [referenceStripSizeRatio=0.2]
  501. *
  502. * @property {Boolean} [collectionMode=false]
  503. *
  504. * @property {Number} [collectionRows=3]
  505. *
  506. * @property {String} [collectionLayout='horizontal']
  507. *
  508. * @property {Number} [collectionTileSize=800]
  509. *
  510. * @property {String|Boolean} [crossOriginPolicy=false]
  511. * Valid values are 'Anonymous', 'use-credentials', and false. If false, canvas requests will
  512. * not use CORS, and the canvas will be tainted.
  513. *
  514. */
  515. /**
  516. * Settings for gestures generated by a pointer device.
  517. *
  518. * @typedef {Object} GestureSettings
  519. * @memberof OpenSeadragon
  520. *
  521. * @property {Boolean} scrollToZoom
  522. * Set to false to disable zooming on scroll gestures.
  523. *
  524. * @property {Boolean} clickToZoom
  525. * Set to false to disable zooming on click gestures.
  526. *
  527. * @property {Boolean} pinchToZoom
  528. * Set to false to disable zooming on pinch gestures.
  529. *
  530. * @property {Boolean} flickEnabled
  531. * Set to false to disable the kinetic panning effect (flick) at the end of a drag gesture.
  532. *
  533. * @property {Number} flickMinSpeed
  534. * 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.
  535. *
  536. * @property {Number} flickMomentum
  537. * 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.
  538. * A larger value will make the flick feel "lighter", while a smaller value will make the flick feel "heavier".
  539. * Note: springStiffness and animationTime also affect the "spring" used to stop the flick animation.
  540. *
  541. */
  542. /**
  543. * The names for the image resources used for the image navigation buttons.
  544. *
  545. * @typedef {Object} NavImages
  546. * @memberof OpenSeadragon
  547. *
  548. * @property {Object} zoomIn - Images for the zoom-in button.
  549. * @property {String} zoomIn.REST
  550. * @property {String} zoomIn.GROUP
  551. * @property {String} zoomIn.HOVER
  552. * @property {String} zoomIn.DOWN
  553. *
  554. * @property {Object} zoomOut - Images for the zoom-out button.
  555. * @property {String} zoomOut.REST
  556. * @property {String} zoomOut.GROUP
  557. * @property {String} zoomOut.HOVER
  558. * @property {String} zoomOut.DOWN
  559. *
  560. * @property {Object} home - Images for the home button.
  561. * @property {String} home.REST
  562. * @property {String} home.GROUP
  563. * @property {String} home.HOVER
  564. * @property {String} home.DOWN
  565. *
  566. * @property {Object} fullpage - Images for the full-page button.
  567. * @property {String} fullpage.REST
  568. * @property {String} fullpage.GROUP
  569. * @property {String} fullpage.HOVER
  570. * @property {String} fullpage.DOWN
  571. *
  572. * @property {Object} rotateleft - Images for the rotate left button.
  573. * @property {String} rotateleft.REST
  574. * @property {String} rotateleft.GROUP
  575. * @property {String} rotateleft.HOVER
  576. * @property {String} rotateleft.DOWN
  577. *
  578. * @property {Object} rotateright - Images for the rotate right button.
  579. * @property {String} rotateright.REST
  580. * @property {String} rotateright.GROUP
  581. * @property {String} rotateright.HOVER
  582. * @property {String} rotateright.DOWN
  583. *
  584. * @property {Object} previous - Images for the previous button.
  585. * @property {String} previous.REST
  586. * @property {String} previous.GROUP
  587. * @property {String} previous.HOVER
  588. * @property {String} previous.DOWN
  589. *
  590. * @property {Object} next - Images for the next button.
  591. * @property {String} next.REST
  592. * @property {String} next.GROUP
  593. * @property {String} next.HOVER
  594. * @property {String} next.DOWN
  595. *
  596. */
  597. /**
  598. * This function serves as a single point of instantiation for an {@link OpenSeadragon.Viewer}, including all
  599. * combinations of out-of-the-box configurable features.
  600. *
  601. * @function OpenSeadragon
  602. * @memberof module:OpenSeadragon
  603. * @param {OpenSeadragon.Options} options - Viewer options.
  604. * @returns {OpenSeadragon.Viewer}
  605. */
  606. window.OpenSeadragon = window.OpenSeadragon || function( options ){
  607. return new OpenSeadragon.Viewer( options );
  608. };
  609. (function( $ ){
  610. /**
  611. * The OpenSeadragon version.
  612. *
  613. * @member {Object} OpenSeadragon.version
  614. * @property {String} versionStr - The version number as a string ('major.minor.revision').
  615. * @property {Number} major - The major version number.
  616. * @property {Number} minor - The minor version number.
  617. * @property {Number} revision - The revision number.
  618. * @since 1.0.0
  619. */
  620. /* jshint ignore:start */
  621. $.version = {
  622. versionStr: '<%= osdVersion.versionStr %>',
  623. major: <%= osdVersion.major %>,
  624. minor: <%= osdVersion.minor %>,
  625. revision: <%= osdVersion.revision %>
  626. };
  627. /* jshint ignore:end */
  628. /**
  629. * Taken from jquery 1.6.1
  630. * [[Class]] -> type pairs
  631. * @private
  632. */
  633. var class2type = {
  634. '[object Boolean]': 'boolean',
  635. '[object Number]': 'number',
  636. '[object String]': 'string',
  637. '[object Function]': 'function',
  638. '[object Array]': 'array',
  639. '[object Date]': 'date',
  640. '[object RegExp]': 'regexp',
  641. '[object Object]': 'object'
  642. },
  643. // Save a reference to some core methods
  644. toString = Object.prototype.toString,
  645. hasOwn = Object.prototype.hasOwnProperty;
  646. /**
  647. * Taken from jQuery 1.6.1
  648. * @function isFunction
  649. * @memberof OpenSeadragon
  650. * @see {@link http://www.jquery.com/ jQuery}
  651. */
  652. $.isFunction = function( obj ) {
  653. return $.type(obj) === "function";
  654. };
  655. /**
  656. * Taken from jQuery 1.6.1
  657. * @function isArray
  658. * @memberof OpenSeadragon
  659. * @see {@link http://www.jquery.com/ jQuery}
  660. */
  661. $.isArray = Array.isArray || function( obj ) {
  662. return $.type(obj) === "array";
  663. };
  664. /**
  665. * A crude way of determining if an object is a window.
  666. * Taken from jQuery 1.6.1
  667. * @function isWindow
  668. * @memberof OpenSeadragon
  669. * @see {@link http://www.jquery.com/ jQuery}
  670. */
  671. $.isWindow = function( obj ) {
  672. return obj && typeof obj === "object" && "setInterval" in obj;
  673. };
  674. /**
  675. * Taken from jQuery 1.6.1
  676. * @function type
  677. * @memberof OpenSeadragon
  678. * @see {@link http://www.jquery.com/ jQuery}
  679. */
  680. $.type = function( obj ) {
  681. return ( obj === null ) || ( obj === undefined ) ?
  682. String( obj ) :
  683. class2type[ toString.call(obj) ] || "object";
  684. };
  685. /**
  686. * Taken from jQuery 1.6.1
  687. * @function isPlainObject
  688. * @memberof OpenSeadragon
  689. * @see {@link http://www.jquery.com/ jQuery}
  690. */
  691. $.isPlainObject = function( obj ) {
  692. // Must be an Object.
  693. // Because of IE, we also have to check the presence of the constructor property.
  694. // Make sure that DOM nodes and window objects don't pass through, as well
  695. if ( !obj || OpenSeadragon.type(obj) !== "object" || obj.nodeType || $.isWindow( obj ) ) {
  696. return false;
  697. }
  698. // Not own constructor property must be Object
  699. if ( obj.constructor &&
  700. !hasOwn.call(obj, "constructor") &&
  701. !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
  702. return false;
  703. }
  704. // Own properties are enumerated firstly, so to speed up,
  705. // if last one is own, then all properties are own.
  706. var key;
  707. for ( key in obj ) {}
  708. return key === undefined || hasOwn.call( obj, key );
  709. };
  710. /**
  711. * Taken from jQuery 1.6.1
  712. * @function isEmptyObject
  713. * @memberof OpenSeadragon
  714. * @see {@link http://www.jquery.com/ jQuery}
  715. */
  716. $.isEmptyObject = function( obj ) {
  717. for ( var name in obj ) {
  718. return false;
  719. }
  720. return true;
  721. };
  722. /**
  723. * True if the browser supports the HTML5 canvas element
  724. * @member {Boolean} supportsCanvas
  725. * @memberof OpenSeadragon
  726. */
  727. $.supportsCanvas = (function () {
  728. var canvasElement = document.createElement( 'canvas' );
  729. return !!( $.isFunction( canvasElement.getContext ) &&
  730. canvasElement.getContext( '2d' ) );
  731. }());
  732. }( OpenSeadragon ));
  733. /**
  734. * This closure defines all static methods available to the OpenSeadragon
  735. * namespace. Many, if not most, are taked directly from jQuery for use
  736. * to simplify and reduce common programming patterns. More static methods
  737. * from jQuery may eventually make their way into this though we are
  738. * attempting to avoid an explicit dependency on jQuery only because
  739. * OpenSeadragon is a broadly useful code base and would be made less broad
  740. * by requiring jQuery fully.
  741. *
  742. * Some static methods have also been refactored from the original OpenSeadragon
  743. * project.
  744. */
  745. (function( $ ){
  746. /**
  747. * Taken from jQuery 1.6.1
  748. * @function extend
  749. * @memberof OpenSeadragon
  750. * @see {@link http://www.jquery.com/ jQuery}
  751. */
  752. $.extend = function() {
  753. var options,
  754. name,
  755. src,
  756. copy,
  757. copyIsArray,
  758. clone,
  759. target = arguments[ 0 ] || {},
  760. length = arguments.length,
  761. deep = false,
  762. i = 1;
  763. // Handle a deep copy situation
  764. if ( typeof target === "boolean" ) {
  765. deep = target;
  766. target = arguments[ 1 ] || {};
  767. // skip the boolean and the target
  768. i = 2;
  769. }
  770. // Handle case when target is a string or something (possible in deep copy)
  771. if ( typeof target !== "object" && !OpenSeadragon.isFunction( target ) ) {
  772. target = {};
  773. }
  774. // extend jQuery itself if only one argument is passed
  775. if ( length === i ) {
  776. target = this;
  777. --i;
  778. }
  779. for ( ; i < length; i++ ) {
  780. // Only deal with non-null/undefined values
  781. options = arguments[ i ];
  782. if ( options !== null || options !== undefined ) {
  783. // Extend the base object
  784. for ( name in options ) {
  785. src = target[ name ];
  786. copy = options[ name ];
  787. // Prevent never-ending loop
  788. if ( target === copy ) {
  789. continue;
  790. }
  791. // Recurse if we're merging plain objects or arrays
  792. if ( deep && copy && ( OpenSeadragon.isPlainObject( copy ) || ( copyIsArray = OpenSeadragon.isArray( copy ) ) ) ) {
  793. if ( copyIsArray ) {
  794. copyIsArray = false;
  795. clone = src && OpenSeadragon.isArray( src ) ? src : [];
  796. } else {
  797. clone = src && OpenSeadragon.isPlainObject( src ) ? src : {};
  798. }
  799. // Never move original objects, clone them
  800. target[ name ] = OpenSeadragon.extend( deep, clone, copy );
  801. // Don't bring in undefined values
  802. } else if ( copy !== undefined ) {
  803. target[ name ] = copy;
  804. }
  805. }
  806. }
  807. }
  808. // Return the modified object
  809. return target;
  810. };
  811. $.extend( $, /** @lends OpenSeadragon */{
  812. /**
  813. * The default values for the optional settings documented at {@link OpenSeadragon.Options}.
  814. * @static
  815. * @type {Object}
  816. */
  817. DEFAULT_SETTINGS: {
  818. //DATA SOURCE DETAILS
  819. xmlPath: null,
  820. tileSources: null,
  821. tileHost: null,
  822. initialPage: 0,
  823. crossOriginPolicy: false,
  824. //PAN AND ZOOM SETTINGS AND CONSTRAINTS
  825. panHorizontal: true,
  826. panVertical: true,
  827. constrainDuringPan: false,
  828. wrapHorizontal: false,
  829. wrapVertical: false,
  830. visibilityRatio: 0.5, //-> how much of the viewer can be negative space
  831. minPixelRatio: 0.5, //->closer to 0 draws tiles meant for a higher zoom at this zoom
  832. defaultZoomLevel: 0,
  833. minZoomLevel: null,
  834. maxZoomLevel: null,
  835. //UI RESPONSIVENESS AND FEEL
  836. clickTimeThreshold: 300,
  837. clickDistThreshold: 5,
  838. springStiffness: 5.0,
  839. animationTime: 1.2,
  840. gestureSettingsMouse: { scrollToZoom: true, clickToZoom: true, pinchToZoom: false, flickEnabled: false, flickMinSpeed: 20, flickMomentum: 0.40 },
  841. gestureSettingsTouch: { scrollToZoom: false, clickToZoom: false, pinchToZoom: true, flickEnabled: true, flickMinSpeed: 20, flickMomentum: 0.40 },
  842. gestureSettingsPen: { scrollToZoom: false, clickToZoom: true, pinchToZoom: false, flickEnabled: false, flickMinSpeed: 20, flickMomentum: 0.40 },
  843. gestureSettingsUnknown: { scrollToZoom: false, clickToZoom: false, pinchToZoom: true, flickEnabled: true, flickMinSpeed: 20, flickMomentum: 0.40 },
  844. zoomPerClick: 2,
  845. zoomPerScroll: 1.2,
  846. zoomPerSecond: 1.0,
  847. blendTime: 0,
  848. alwaysBlend: false,
  849. autoHideControls: true,
  850. immediateRender: false,
  851. minZoomImageRatio: 0.9, //-> closer to 0 allows zoom out to infinity
  852. maxZoomPixelRatio: 1.1, //-> higher allows 'over zoom' into pixels
  853. pixelsPerWheelLine: 40,
  854. autoResize: true,
  855. //DEFAULT CONTROL SETTINGS
  856. showSequenceControl: true, //SEQUENCE
  857. sequenceControlAnchor: null, //SEQUENCE
  858. preserveViewport: false, //SEQUENCE
  859. navPrevNextWrap: false, //SEQUENCE
  860. showNavigationControl: true, //ZOOM/HOME/FULL/ROTATION
  861. navigationControlAnchor: null, //ZOOM/HOME/FULL/ROTATION
  862. showZoomControl: true, //ZOOM
  863. showHomeControl: true, //HOME
  864. showFullPageControl: true, //FULL
  865. showRotationControl: false, //ROTATION
  866. controlsFadeDelay: 2000, //ZOOM/HOME/FULL/SEQUENCE
  867. controlsFadeLength: 1500, //ZOOM/HOME/FULL/SEQUENCE
  868. mouseNavEnabled: true, //GENERAL MOUSE INTERACTIVITY
  869. //VIEWPORT NAVIGATOR SETTINGS
  870. showNavigator: false,
  871. navigatorId: null,
  872. navigatorPosition: null,
  873. navigatorSizeRatio: 0.2,
  874. navigatorMaintainSizeRatio: false,
  875. navigatorTop: null,
  876. navigatorLeft: null,
  877. navigatorHeight: null,
  878. navigatorWidth: null,
  879. navigatorAutoResize: true,
  880. // INITIAL ROTATION
  881. degrees: 0,
  882. // APPEARANCE
  883. opacity: 1,
  884. // LAYERS SETTINGS
  885. layersAspectRatioEpsilon: 0.0001,
  886. //REFERENCE STRIP SETTINGS
  887. showReferenceStrip: false,
  888. referenceStripScroll: 'horizontal',
  889. referenceStripElement: null,
  890. referenceStripHeight: null,
  891. referenceStripWidth: null,
  892. referenceStripPosition: 'BOTTOM_LEFT',
  893. referenceStripSizeRatio: 0.2,
  894. //COLLECTION VISUALIZATION SETTINGS
  895. collectionRows: 3, //or columns depending on layout
  896. collectionLayout: 'horizontal', //vertical
  897. collectionMode: false,
  898. collectionTileSize: 800,
  899. //PERFORMANCE SETTINGS
  900. imageLoaderLimit: 0,
  901. maxImageCacheCount: 200,
  902. timeout: 30000,
  903. useCanvas: true, // Use canvas element for drawing if available
  904. //INTERFACE RESOURCE SETTINGS
  905. prefixUrl: "/images/",
  906. navImages: {
  907. zoomIn: {
  908. REST: 'zoomin_rest.png',
  909. GROUP: 'zoomin_grouphover.png',
  910. HOVER: 'zoomin_hover.png',
  911. DOWN: 'zoomin_pressed.png'
  912. },
  913. zoomOut: {
  914. REST: 'zoomout_rest.png',
  915. GROUP: 'zoomout_grouphover.png',
  916. HOVER: 'zoomout_hover.png',
  917. DOWN: 'zoomout_pressed.png'
  918. },
  919. home: {
  920. REST: 'home_rest.png',
  921. GROUP: 'home_grouphover.png',
  922. HOVER: 'home_hover.png',
  923. DOWN: 'home_pressed.png'
  924. },
  925. fullpage: {
  926. REST: 'fullpage_rest.png',
  927. GROUP: 'fullpage_grouphover.png',
  928. HOVER: 'fullpage_hover.png',
  929. DOWN: 'fullpage_pressed.png'
  930. },
  931. rotateleft: {
  932. REST: 'rotateleft_rest.png',
  933. GROUP: 'rotateleft_grouphover.png',
  934. HOVER: 'rotateleft_hover.png',
  935. DOWN: 'rotateleft_pressed.png'
  936. },
  937. rotateright: {
  938. REST: 'rotateright_rest.png',
  939. GROUP: 'rotateright_grouphover.png',
  940. HOVER: 'rotateright_hover.png',
  941. DOWN: 'rotateright_pressed.png'
  942. },
  943. previous: {
  944. REST: 'previous_rest.png',
  945. GROUP: 'previous_grouphover.png',
  946. HOVER: 'previous_hover.png',
  947. DOWN: 'previous_pressed.png'
  948. },
  949. next: {
  950. REST: 'next_rest.png',
  951. GROUP: 'next_grouphover.png',
  952. HOVER: 'next_hover.png',
  953. DOWN: 'next_pressed.png'
  954. }
  955. },
  956. //DEVELOPER SETTINGS
  957. debugMode: false,
  958. debugGridColor: '#437AB2'
  959. },
  960. /**
  961. * TODO: get rid of this. I can't see how it's required at all. Looks
  962. * like an early legacy code artifact.
  963. * @static
  964. * @ignore
  965. */
  966. SIGNAL: "----seadragon----",
  967. /**
  968. * Returns a function which invokes the method as if it were a method belonging to the object.
  969. * @function
  970. * @param {Object} object
  971. * @param {Function} method
  972. * @returns {Function}
  973. */
  974. delegate: function( object, method ) {
  975. return function(){
  976. var args = arguments;
  977. if ( args === undefined ){
  978. args = [];
  979. }
  980. return method.apply( object, args );
  981. };
  982. },
  983. /**
  984. * An enumeration of Browser vendors.
  985. * @static
  986. * @type {Object}
  987. * @property {Number} UNKNOWN
  988. * @property {Number} IE
  989. * @property {Number} FIREFOX
  990. * @property {Number} SAFARI
  991. * @property {Number} CHROME
  992. * @property {Number} OPERA
  993. */
  994. BROWSERS: {
  995. UNKNOWN: 0,
  996. IE: 1,
  997. FIREFOX: 2,
  998. SAFARI: 3,
  999. CHROME: 4,
  1000. OPERA: 5
  1001. },
  1002. /**
  1003. * Returns a DOM Element for the given id or element.
  1004. * @function
  1005. * @param {String|Element} element Accepts an id or element.
  1006. * @returns {Element} The element with the given id, null, or the element itself.
  1007. */
  1008. getElement: function( element ) {
  1009. if ( typeof ( element ) == "string" ) {
  1010. element = document.getElementById( element );
  1011. }
  1012. return element;
  1013. },
  1014. /**
  1015. * Determines the position of the upper-left corner of the element.
  1016. * @function
  1017. * @param {Element|String} element - the elemenet we want the position for.
  1018. * @returns {OpenSeadragon.Point} - the position of the upper left corner of the element.
  1019. */
  1020. getElementPosition: function( element ) {
  1021. var result = new $.Point(),
  1022. isFixed,
  1023. offsetParent;
  1024. element = $.getElement( element );
  1025. isFixed = $.getElementStyle( element ).position == "fixed";
  1026. offsetParent = getOffsetParent( element, isFixed );
  1027. while ( offsetParent ) {
  1028. result.x += element.offsetLeft;
  1029. result.y += element.offsetTop;
  1030. if ( isFixed ) {
  1031. result = result.plus( $.getPageScroll() );
  1032. }
  1033. element = offsetParent;
  1034. isFixed = $.getElementStyle( element ).position == "fixed";
  1035. offsetParent = getOffsetParent( element, isFixed );
  1036. }
  1037. return result;
  1038. },
  1039. /**
  1040. * Determines the position of the upper-left corner of the element adjusted for current page and/or element scroll.
  1041. * @function
  1042. * @param {Element|String} element - the element we want the position for.
  1043. * @returns {OpenSeadragon.Point} - the position of the upper left corner of the element adjusted for current page and/or element scroll.
  1044. */
  1045. getElementOffset: function( element ) {
  1046. element = $.getElement( element );
  1047. var doc = element && element.ownerDocument,
  1048. docElement,
  1049. win,
  1050. boundingRect = { top: 0, left: 0 };
  1051. if ( !doc ) {
  1052. return new $.Point();
  1053. }
  1054. docElement = doc.documentElement;
  1055. if ( typeof element.getBoundingClientRect !== typeof undefined ) {
  1056. boundingRect = element.getBoundingClientRect();
  1057. }
  1058. win = ( doc == doc.window ) ?
  1059. doc :
  1060. ( doc.nodeType === 9 ) ?
  1061. doc.defaultView || doc.parentWindow :
  1062. false;
  1063. return new $.Point(
  1064. boundingRect.left + ( win.pageXOffset || docElement.scrollLeft ) - ( docElement.clientLeft || 0 ),
  1065. boundingRect.top + ( win.pageYOffset || docElement.scrollTop ) - ( docElement.clientTop || 0 )
  1066. );
  1067. },
  1068. /**
  1069. * Determines the height and width of the given element.
  1070. * @function
  1071. * @param {Element|String} element
  1072. * @returns {OpenSeadragon.Point}
  1073. */
  1074. getElementSize: function( element ) {
  1075. element = $.getElement( element );
  1076. return new $.Point(
  1077. element.clientWidth,
  1078. element.clientHeight
  1079. );
  1080. },
  1081. /**
  1082. * Returns the CSSStyle object for the given element.
  1083. * @function
  1084. * @param {Element|String} element
  1085. * @returns {CSSStyle}
  1086. */
  1087. getElementStyle:
  1088. document.documentElement.currentStyle ?
  1089. function( element ) {
  1090. element = $.getElement( element );
  1091. return element.currentStyle;
  1092. } :
  1093. function( element ) {
  1094. element = $.getElement( element );
  1095. return window.getComputedStyle( element, "" );
  1096. },
  1097. /**
  1098. * Determines if a point is within the bounding rectangle of the given element (hit-test).
  1099. * @function
  1100. * @param {Element|String} element
  1101. * @param {OpenSeadragon.Point} point
  1102. * @returns {Boolean}
  1103. */
  1104. pointInElement: function( element, point ) {
  1105. element = $.getElement( element );
  1106. var offset = $.getElementOffset( element ),
  1107. size = $.getElementSize( element );
  1108. return point.x >= offset.x && point.x < offset.x + size.x && point.y < offset.y + size.y && point.y >= offset.y;
  1109. },
  1110. /**
  1111. * Gets the latest event, really only useful internally since its
  1112. * specific to IE behavior.
  1113. * @function
  1114. * @param {Event} [event]
  1115. * @returns {Event}
  1116. * @deprecated For internal use only
  1117. * @private
  1118. */
  1119. getEvent: function( event ) {
  1120. if( event ){
  1121. $.getEvent = function( event ) {
  1122. return event;
  1123. };
  1124. } else {
  1125. $.getEvent = function() {
  1126. return window.event;
  1127. };
  1128. }
  1129. return $.getEvent( event );
  1130. },
  1131. /**
  1132. * Gets the position of the mouse on the screen for a given event.
  1133. * @function
  1134. * @param {Event} [event]
  1135. * @returns {OpenSeadragon.Point}
  1136. */
  1137. getMousePosition: function( event ) {
  1138. if ( typeof( event.pageX ) == "number" ) {
  1139. $.getMousePosition = function( event ){
  1140. var result = new $.Point();
  1141. event = $.getEvent( event );
  1142. result.x = event.pageX;
  1143. result.y = event.pageY;
  1144. return result;
  1145. };
  1146. } else if ( typeof( event.clientX ) == "number" ) {
  1147. $.getMousePosition = function( event ){
  1148. var result = new $.Point();
  1149. event = $.getEvent( event );
  1150. result.x =
  1151. event.clientX +
  1152. document.body.scrollLeft +
  1153. document.documentElement.scrollLeft;
  1154. result.y =
  1155. event.clientY +
  1156. document.body.scrollTop +
  1157. document.documentElement.scrollTop;
  1158. return result;
  1159. };
  1160. } else {
  1161. throw new Error(
  1162. "Unknown event mouse position, no known technique."
  1163. );
  1164. }
  1165. return $.getMousePosition( event );
  1166. },
  1167. /**
  1168. * Determines the page's current scroll position.
  1169. * @function
  1170. * @returns {OpenSeadragon.Point}
  1171. */
  1172. getPageScroll: function() {
  1173. var docElement = document.documentElement || {},
  1174. body = document.body || {};
  1175. if ( typeof( window.pageXOffset ) == "number" ) {
  1176. $.getPageScroll = function(){
  1177. return new $.Point(
  1178. window.pageXOffset,
  1179. window.pageYOffset
  1180. );
  1181. };
  1182. } else if ( body.scrollLeft || body.scrollTop ) {
  1183. $.getPageScroll = function(){
  1184. return new $.Point(
  1185. document.body.scrollLeft,
  1186. document.body.scrollTop
  1187. );
  1188. };
  1189. } else if ( docElement.scrollLeft || docElement.scrollTop ) {
  1190. $.getPageScroll = function(){
  1191. return new $.Point(
  1192. document.documentElement.scrollLeft,
  1193. document.documentElement.scrollTop
  1194. );
  1195. };
  1196. } else {
  1197. // We can't reassign the function yet, as there was no scroll.
  1198. return new $.Point(0,0);
  1199. }
  1200. return $.getPageScroll();
  1201. },
  1202. /**
  1203. * Set the page scroll position.
  1204. * @function
  1205. * @returns {OpenSeadragon.Point}
  1206. */
  1207. setPageScroll: function( scroll ) {
  1208. if ( typeof ( window.scrollTo ) !== "undefined" ) {
  1209. $.setPageScroll = function( scroll ) {
  1210. window.scrollTo( scroll.x, scroll.y );
  1211. };
  1212. } else {
  1213. var originalScroll = $.getPageScroll();
  1214. if ( originalScroll.x === scroll.x &&
  1215. originalScroll.y === scroll.y ) {
  1216. // We are already correctly positioned and there
  1217. // is no way to detect the correct method.
  1218. return;
  1219. }
  1220. document.body.scrollLeft = scroll.x;
  1221. document.body.scrollTop = scroll.y;
  1222. var currentScroll = $.getPageScroll();
  1223. if ( currentScroll.x !== originalScroll.x &&
  1224. currentScroll.y !== originalScroll.y ) {
  1225. $.setPageScroll = function( scroll ) {
  1226. document.body.scrollLeft = scroll.x;
  1227. document.body.scrollTop = scroll.y;
  1228. };
  1229. return;
  1230. }
  1231. document.documentElement.scrollLeft = scroll.x;
  1232. document.documentElement.scrollTop = scroll.y;
  1233. currentScroll = $.getPageScroll();
  1234. if ( currentScroll.x !== originalScroll.x &&
  1235. currentScroll.y !== originalScroll.y ) {
  1236. $.setPageScroll = function( scroll ) {
  1237. document.documentElement.scrollLeft = scroll.x;
  1238. document.documentElement.scrollTop = scroll.y;
  1239. };
  1240. return;
  1241. }
  1242. // We can't find anything working, so we do nothing.
  1243. $.setPageScroll = function( scroll ) {
  1244. };
  1245. }
  1246. return $.setPageScroll( scroll );
  1247. },
  1248. /**
  1249. * Determines the size of the browsers window.
  1250. * @function
  1251. * @returns {OpenSeadragon.Point}
  1252. */
  1253. getWindowSize: function() {
  1254. var docElement = document.documentElement || {},
  1255. body = document.body || {};
  1256. if ( typeof( window.innerWidth ) == 'number' ) {
  1257. $.getWindowSize = function(){
  1258. return new $.Point(
  1259. window.innerWidth,
  1260. window.innerHeight
  1261. );
  1262. };
  1263. } else if ( docElement.clientWidth || docElement.clientHeight ) {
  1264. $.getWindowSize = function(){
  1265. return new $.Point(
  1266. document.documentElement.clientWidth,
  1267. document.documentElement.clientHeight
  1268. );
  1269. };
  1270. } else if ( body.clientWidth || body.clientHeight ) {
  1271. $.getWindowSize = function(){
  1272. return new $.Point(
  1273. document.body.clientWidth,
  1274. document.body.clientHeight
  1275. );
  1276. };
  1277. } else {
  1278. throw new Error("Unknown window size, no known technique.");
  1279. }
  1280. return $.getWindowSize();
  1281. },
  1282. /**
  1283. * Wraps the given element in a nest of divs so that the element can
  1284. * be easily centered using CSS tables
  1285. * @function
  1286. * @param {Element|String} element
  1287. * @returns {Element} outermost wrapper element
  1288. */
  1289. makeCenteredNode: function( element ) {
  1290. // Convert a possible ID to an actual HTMLElement
  1291. element = $.getElement( element );
  1292. /*
  1293. CSS tables require you to have a display:table/row/cell hierarchy so we need to create
  1294. three nested wrapper divs:
  1295. */
  1296. var wrappers = [
  1297. $.makeNeutralElement( 'div' ),
  1298. $.makeNeutralElement( 'div' ),
  1299. $.makeNeutralElement( 'div' )
  1300. ];
  1301. // It feels like we should be able to pass style dicts to makeNeutralElement:
  1302. $.extend(wrappers[0].style, {
  1303. display: "table",
  1304. height: "100%",
  1305. width: "100%"
  1306. });
  1307. $.extend(wrappers[1].style, {
  1308. display: "table-row"
  1309. });
  1310. $.extend(wrappers[2].style, {
  1311. display: "table-cell",
  1312. verticalAlign: "middle",
  1313. textAlign: "center"
  1314. });
  1315. wrappers[0].appendChild(wrappers[1]);
  1316. wrappers[1].appendChild(wrappers[2]);
  1317. wrappers[2].appendChild(element);
  1318. return wrappers[0];
  1319. },
  1320. /**
  1321. * Creates an easily positionable element of the given type that therefor
  1322. * serves as an excellent container element.
  1323. * @function
  1324. * @param {String} tagName
  1325. * @returns {Element}
  1326. */
  1327. makeNeutralElement: function( tagName ) {
  1328. var element = document.createElement( tagName ),
  1329. style = element.style;
  1330. style.background = "transparent none";
  1331. style.border = "none";
  1332. style.margin = "0px";
  1333. style.padding = "0px";
  1334. style.position = "static";
  1335. return element;
  1336. },
  1337. /**
  1338. * Returns the current milliseconds, using Date.now() if available
  1339. * @function
  1340. */
  1341. now: function( ) {
  1342. if (Date.now) {
  1343. $.now = Date.now;
  1344. } else {
  1345. $.now = function() { return new Date().getTime(); };
  1346. }
  1347. return $.now();
  1348. },
  1349. /**
  1350. * Ensures an image is loaded correctly to support alpha transparency.
  1351. * Generally only IE has issues doing this correctly for formats like
  1352. * png.
  1353. * @function
  1354. * @param {String} src
  1355. * @returns {Element}
  1356. */
  1357. makeTransparentImage: function( src ) {
  1358. $.makeTransparentImage = function( src ){
  1359. var img = $.makeNeutralElement( "img" );
  1360. img.src = src;
  1361. return img;
  1362. };
  1363. if ( $.Browser.vendor == $.BROWSERS.IE && $.Browser.version < 7 ) {
  1364. $.makeTransparentImage = function( src ){
  1365. var img = $.makeNeutralElement( "img" ),
  1366. element = null;
  1367. element = $.makeNeutralElement("span");
  1368. element.style.display = "inline-block";
  1369. img.onload = function() {
  1370. element.style.width = element.style.width || img.width + "px";
  1371. element.style.height = element.style.height || img.height + "px";
  1372. img.onload = null;
  1373. img = null; // to prevent memory leaks in IE
  1374. };
  1375. img.src = src;
  1376. element.style.filter =
  1377. "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" +
  1378. src +
  1379. "', sizingMethod='scale')";
  1380. return element;
  1381. };
  1382. }
  1383. return $.makeTransparentImage( src );
  1384. },
  1385. /**
  1386. * Sets the opacity of the specified element.
  1387. * @function
  1388. * @param {Element|String} element
  1389. * @param {Number} opacity
  1390. * @param {Boolean} [usesAlpha]
  1391. */
  1392. setElementOpacity: function( element, opacity, usesAlpha ) {
  1393. var ieOpacity,
  1394. ieFilter;
  1395. element = $.getElement( element );
  1396. if ( usesAlpha && !$.Browser.alpha ) {
  1397. opacity = Math.round( opacity );
  1398. }
  1399. if ( $.Browser.opacity ) {
  1400. element.style.opacity = opacity < 1 ? opacity : "";
  1401. } else {
  1402. if ( opacity < 1 ) {
  1403. ieOpacity = Math.round( 100 * opacity );
  1404. ieFilter = "alpha(opacity=" + ieOpacity + ")";
  1405. element.style.filter = ieFilter;
  1406. } else {
  1407. element.style.filter = "";
  1408. }
  1409. }
  1410. },
  1411. /**
  1412. * Add the specified CSS class to the element if not present.
  1413. * @function
  1414. * @param {Element|String} element
  1415. * @param {String} className
  1416. */
  1417. addClass: function( element, className ) {
  1418. element = $.getElement( element );
  1419. if ( ! element.className ) {
  1420. element.className = className;
  1421. } else if ( ( ' ' + element.className + ' ' ).
  1422. indexOf( ' ' + className + ' ' ) === -1 ) {
  1423. element.className += ' ' + className;
  1424. }
  1425. },
  1426. /**
  1427. * Find the first index at which an element is found in an array or -1
  1428. * if not present.
  1429. *
  1430. * Code taken and adapted from
  1431. * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Compatibility
  1432. *
  1433. * @function
  1434. * @param {Array} array The array from which to find the element
  1435. * @param {Object} searchElement The element to find
  1436. * @param {Number} [fromIndex=0] Index to start research.
  1437. * @returns {Number} The index of the element in the array.
  1438. */
  1439. indexOf: function( array, searchElement, fromIndex ) {
  1440. if ( Array.prototype.indexOf ) {
  1441. this.indexOf = function( array, searchElement, fromIndex ) {
  1442. return array.indexOf( searchElement, fromIndex );
  1443. };
  1444. } else {
  1445. this.indexOf = function( array, searchElement, fromIndex ) {
  1446. var i,
  1447. pivot = ( fromIndex ) ? fromIndex : 0,
  1448. length;
  1449. if ( !array ) {
  1450. throw new TypeError( );
  1451. }
  1452. length = array.length;
  1453. if ( length === 0 || pivot >= length ) {
  1454. return -1;
  1455. }
  1456. if ( pivot < 0 ) {
  1457. pivot = length - Math.abs( pivot );
  1458. }
  1459. for ( i = pivot; i < length; i++ ) {
  1460. if ( array[i] === searchElement ) {
  1461. return i;
  1462. }
  1463. }
  1464. return -1;
  1465. };
  1466. }
  1467. return this.indexOf( array, searchElement, fromIndex );
  1468. },
  1469. /**
  1470. * Remove the specified CSS class from the element.
  1471. * @function
  1472. * @param {Element|String} element
  1473. * @param {String} className
  1474. */
  1475. removeClass: function( element, className ) {
  1476. var oldClasses,
  1477. newClasses = [],
  1478. i;
  1479. element = $.getElement( element );
  1480. oldClasses = element.className.split( /\s+/ );
  1481. for ( i = 0; i < oldClasses.length; i++ ) {
  1482. if ( oldClasses[ i ] && oldClasses[ i ] !== className ) {
  1483. newClasses.push( oldClasses[ i ] );
  1484. }
  1485. }
  1486. element.className = newClasses.join(' ');
  1487. },
  1488. /**
  1489. * Adds an event listener for the given element, eventName and handler.
  1490. * @function
  1491. * @param {Element|String} element
  1492. * @param {String} eventName
  1493. * @param {Function} handler
  1494. * @param {Boolean} [useCapture]
  1495. */
  1496. addEvent: (function () {
  1497. if ( window.addEventListener ) {
  1498. return function ( element, eventName, handler, useCapture ) {
  1499. element = $.getElement( element );
  1500. element.addEventListener( eventName, handler, useCapture );
  1501. };
  1502. } else if ( window.attachEvent ) {
  1503. return function ( element, eventName, handler, useCapture ) {
  1504. element = $.getElement( element );
  1505. element.attachEvent( 'on' + eventName, handler );
  1506. };
  1507. } else {
  1508. throw new Error( "No known event model." );
  1509. }
  1510. }()),
  1511. /**
  1512. * Remove a given event listener for the given element, event type and
  1513. * handler.
  1514. * @function
  1515. * @param {Element|String} element
  1516. * @param {String} eventName
  1517. * @param {Function} handler
  1518. * @param {Boolean} [useCapture]
  1519. */
  1520. removeEvent: (function () {
  1521. if ( window.removeEventListener ) {
  1522. return function ( element, eventName, handler, useCapture ) {
  1523. element = $.getElement( element );
  1524. element.removeEventListener( eventName, handler, useCapture );
  1525. };
  1526. } else if ( window.detachEvent ) {
  1527. return function( element, eventName, handler, useCapture ) {
  1528. element = $.getElement( element );
  1529. element.detachEvent( 'on' + eventName, handler );
  1530. };
  1531. } else {
  1532. throw new Error( "No known event model." );
  1533. }
  1534. }()),
  1535. /**
  1536. * Cancels the default browser behavior had the event propagated all
  1537. * the way up the DOM to the window object.
  1538. * @function
  1539. * @param {Event} [event]
  1540. */
  1541. cancelEvent: function( event ) {
  1542. event = $.getEvent( event );
  1543. if ( event.preventDefault ) {
  1544. $.cancelEvent = function( event ){
  1545. // W3C for preventing default
  1546. event.preventDefault();
  1547. };
  1548. } else {
  1549. $.cancelEvent = function( event ){
  1550. event = $.getEvent( event );
  1551. // legacy for preventing default
  1552. event.cancel = true;
  1553. // IE for preventing default
  1554. event.returnValue = false;
  1555. };
  1556. }
  1557. $.cancelEvent( event );
  1558. },
  1559. /**
  1560. * Stops the propagation of the event up the DOM.
  1561. * @function
  1562. * @param {Event} [event]
  1563. */
  1564. stopEvent: function( event ) {
  1565. event = $.getEvent( event );
  1566. if ( event.stopPropagation ) {
  1567. // W3C for stopping propagation
  1568. $.stopEvent = function( event ){
  1569. event.stopPropagation();
  1570. };
  1571. } else {
  1572. // IE for stopping propagation
  1573. $.stopEvent = function( event ){
  1574. event = $.getEvent( event );
  1575. event.cancelBubble = true;
  1576. };
  1577. }
  1578. $.stopEvent( event );
  1579. },
  1580. /**
  1581. * Similar to OpenSeadragon.delegate, but it does not immediately call
  1582. * the method on the object, returning a function which can be called
  1583. * repeatedly to delegate the method. It also allows additonal arguments
  1584. * to be passed during construction which will be added during each
  1585. * invocation, and each invocation can add additional arguments as well.
  1586. *
  1587. * @function
  1588. * @param {Object} object
  1589. * @param {Function} method
  1590. * @param [args] any additional arguments are passed as arguments to the
  1591. * created callback
  1592. * @returns {Function}
  1593. */
  1594. createCallback: function( object, method ) {
  1595. //TODO: This pattern is painful to use and debug. It's much cleaner
  1596. // to use pinning plus anonymous functions. Get rid of this
  1597. // pattern!
  1598. var initialArgs = [],
  1599. i;
  1600. for ( i = 2; i < arguments.length; i++ ) {
  1601. initialArgs.push( arguments[ i ] );
  1602. }
  1603. return function() {
  1604. var args = initialArgs.concat( [] ),
  1605. i;
  1606. for ( i = 0; i < arguments.length; i++ ) {
  1607. args.push( arguments[ i ] );
  1608. }
  1609. return method.apply( object, args );
  1610. };
  1611. },
  1612. /**
  1613. * Retreives the value of a url parameter from the window.location string.
  1614. * @function
  1615. * @param {String} key
  1616. * @returns {String} The value of the url parameter or null if no param matches.
  1617. */
  1618. getUrlParameter: function( key ) {
  1619. var value = URLPARAMS[ key ];
  1620. return value ? value : null;
  1621. },
  1622. createAjaxRequest: function(){
  1623. var request;
  1624. if ( window.XMLHttpRequest ) {
  1625. $.createAjaxRequest = function( ){
  1626. return new XMLHttpRequest();
  1627. };
  1628. request = new XMLHttpRequest();
  1629. } else if ( window.ActiveXObject ) {
  1630. /*jshint loopfunc:true*/
  1631. /* global ActiveXObject:true */
  1632. for ( var i = 0; i < ACTIVEX.length; i++ ) {
  1633. try {
  1634. request = new ActiveXObject( ACTIVEX[ i ] );
  1635. $.createAjaxRequest = function( ){
  1636. return new ActiveXObject( ACTIVEX[ i ] );
  1637. };
  1638. break;
  1639. } catch (e) {
  1640. continue;
  1641. }
  1642. }
  1643. }
  1644. if ( !request ) {
  1645. throw new Error( "Browser doesn't support XMLHttpRequest." );
  1646. }
  1647. return request;
  1648. },
  1649. /**
  1650. * Makes an AJAX request.
  1651. * @function
  1652. * @param {String} url - the url to request
  1653. * @param {Function} onSuccess - a function to call on a successful response
  1654. * @param {Function} onError - a function to call on when an error occurs
  1655. * @throws {Error}
  1656. */
  1657. makeAjaxRequest: function( url, onSuccess, onError ) {
  1658. var request = $.createAjaxRequest();
  1659. if ( !$.isFunction( onSuccess ) ) {
  1660. throw new Error( "makeAjaxRequest requires a success callback" );
  1661. }
  1662. request.onreadystatechange = function() {
  1663. // 4 = DONE (https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#Properties)
  1664. if ( request.readyState == 4 ) {
  1665. request.onreadystatechange = function(){};
  1666. if ( request.status == 200 ) {
  1667. onSuccess( request );
  1668. } else {
  1669. $.console.log( "AJAX request returned %s: %s", request.status, url );
  1670. if ( $.isFunction( onError ) ) {
  1671. onError( request );
  1672. }
  1673. }
  1674. }
  1675. };
  1676. try {
  1677. request.open( "GET", url, true );
  1678. request.send( null );
  1679. } catch (e) {
  1680. var msg = e.message;
  1681. /*
  1682. IE < 10 does not support CORS and an XHR request to a different origin will fail as soon
  1683. as send() is called. This is particularly easy to miss during development and appear in
  1684. production if you use a CDN or domain sharding and the security policy is likely to break
  1685. exception handlers since any attempt to access a property of the request object will
  1686. raise an access denied TypeError inside the catch block.
  1687. To be friendlier, we'll check for this specific error and add a documentation pointer
  1688. to point developers in the right direction. We test the exception number because IE's
  1689. error messages are localized.
  1690. */
  1691. var oldIE = $.Browser.vendor == $.BROWSERS.IE && $.Browser.version < 10;
  1692. if ( oldIE && typeof( e.number ) != "undefined" && e.number == -2147024891 ) {
  1693. msg += "\nSee http://msdn.microsoft.com/en-us/library/ms537505(v=vs.85).aspx#xdomain";
  1694. }
  1695. $.console.log( "%s while making AJAX request: %s", e.name, msg );
  1696. request.onreadystatechange = function(){};
  1697. if ( $.isFunction( onError ) ) {
  1698. onError( request, e );
  1699. }
  1700. }
  1701. },
  1702. /**
  1703. * Taken from jQuery 1.6.1
  1704. * @function
  1705. * @param {Object} options
  1706. * @param {String} options.url
  1707. * @param {Function} options.callback
  1708. * @param {String} [options.param='callback'] The name of the url parameter
  1709. * to request the jsonp provider with.
  1710. * @param {String} [options.callbackName=] The name of the callback to
  1711. * request the jsonp provider with.
  1712. */
  1713. jsonp: function( options ){
  1714. var script,
  1715. url = options.url,
  1716. head = document.head ||
  1717. document.getElementsByTagName( "head" )[ 0 ] ||
  1718. document.documentElement,
  1719. jsonpCallback = options.callbackName || 'openseadragon' + $.now(),
  1720. previous = window[ jsonpCallback ],
  1721. replace = "$1" + jsonpCallback + "$2",
  1722. callbackParam = options.param || 'callback',
  1723. callback = options.callback;
  1724. url = url.replace( /(\=)\?(&|$)|\?\?/i, replace );
  1725. // Add callback manually
  1726. url += (/\?/.test( url ) ? "&" : "?") + callbackParam + "=" + jsonpCallback;
  1727. // Install callback
  1728. window[ jsonpCallback ] = function( response ) {
  1729. if ( !previous ){
  1730. try{
  1731. delete window[ jsonpCallback ];
  1732. }catch(e){
  1733. //swallow
  1734. }
  1735. } else {
  1736. window[ jsonpCallback ] = previous;
  1737. }
  1738. if( callback && $.isFunction( callback ) ){
  1739. callback( response );
  1740. }
  1741. };
  1742. script = document.createElement( "script" );
  1743. //TODO: having an issue with async info requests
  1744. if( undefined !== options.async || false !== options.async ){
  1745. script.async = "async";
  1746. }
  1747. if ( options.scriptCharset ) {
  1748. script.charset = options.scriptCharset;
  1749. }
  1750. script.src = url;
  1751. // Attach handlers for all browsers
  1752. script.onload = script.onreadystatechange = function( _, isAbort ) {
  1753. if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
  1754. // Handle memory leak in IE
  1755. script.onload = script.onreadystatechange = null;
  1756. // Remove the script
  1757. if ( head && script.parentNode ) {
  1758. head.removeChild( script );
  1759. }
  1760. // Dereference the script
  1761. script = undefined;
  1762. }
  1763. };
  1764. // Use insertBefore instead of appendChild to circumvent an IE6 bug.
  1765. // This arises when a base node is used (#2709 and #4378).
  1766. head.insertBefore( script, head.firstChild );
  1767. },
  1768. /**
  1769. * Fully deprecated. Will throw an error.
  1770. * @function
  1771. * @deprecated use {@link OpenSeadragon.Viewer#open}
  1772. */
  1773. createFromDZI: function() {
  1774. throw "OpenSeadragon.createFromDZI is deprecated, use Viewer.open.";
  1775. },
  1776. /**
  1777. * Parses an XML string into a DOM Document.
  1778. * @function
  1779. * @param {String} string
  1780. * @returns {Document}
  1781. */
  1782. parseXml: function( string ) {
  1783. //TODO: yet another example where we can determine the correct
  1784. // implementation once at start-up instead of everytime we use
  1785. // the function. DONE.
  1786. if ( window.ActiveXObject ) {
  1787. $.parseXml = function( string ){
  1788. var xmlDoc = null;
  1789. xmlDoc = new ActiveXObject( "Microsoft.XMLDOM" );
  1790. xmlDoc.async = false;
  1791. xmlDoc.loadXML( string );
  1792. return xmlDoc;
  1793. };
  1794. } else if ( window.DOMParser ) {
  1795. $.parseXml = function( string ){
  1796. var xmlDoc = null,
  1797. parser;
  1798. parser = new DOMParser();
  1799. xmlDoc = parser.parseFromString( string, "text/xml" );
  1800. return xmlDoc;
  1801. };
  1802. } else {
  1803. throw new Error( "Browser doesn't support XML DOM." );
  1804. }
  1805. return $.parseXml( string );
  1806. },
  1807. /**
  1808. * Reports whether the image format is supported for tiling in this
  1809. * version.
  1810. * @function
  1811. * @param {String} [extension]
  1812. * @returns {Boolean}
  1813. */
  1814. imageFormatSupported: function( extension ) {
  1815. extension = extension ? extension : "";
  1816. return !!FILEFORMATS[ extension.toLowerCase() ];
  1817. }
  1818. });
  1819. /**
  1820. * The current browser vendor, version, and related information regarding detected features.
  1821. * @member {Object} Browser
  1822. * @memberof OpenSeadragon
  1823. * @static
  1824. * @type {Object}
  1825. * @property {OpenSeadragon.BROWSERS} vendor - One of the {@link OpenSeadragon.BROWSERS} enumeration values.
  1826. * @property {Number} version
  1827. * @property {Boolean} alpha - Does the browser support image alpha transparency.
  1828. */
  1829. $.Browser = {
  1830. vendor: $.BROWSERS.UNKNOWN,
  1831. version: 0,
  1832. alpha: true
  1833. };
  1834. var ACTIVEX = [
  1835. "Msxml2.XMLHTTP",
  1836. "Msxml3.XMLHTTP",
  1837. "Microsoft.XMLHTTP"
  1838. ],
  1839. FILEFORMATS = {
  1840. "bmp": false,
  1841. "jpeg": true,
  1842. "jpg": true,
  1843. "png": true,
  1844. "tif": false,
  1845. "wdp": false
  1846. },
  1847. URLPARAMS = {};
  1848. (function() {
  1849. //A small auto-executing routine to determine the browser vendor,
  1850. //version and supporting feature sets.
  1851. var app = navigator.appName,
  1852. ver = navigator.appVersion,
  1853. ua = navigator.userAgent;
  1854. //console.error( 'appName: ' + navigator.appName );
  1855. //console.error( 'appVersion: ' + navigator.appVersion );
  1856. //console.error( 'userAgent: ' + navigator.userAgent );
  1857. switch( navigator.appName ){
  1858. case "Microsoft Internet Explorer":
  1859. if( !!window.attachEvent &&
  1860. !!window.ActiveXObject ) {
  1861. $.Browser.vendor = $.BROWSERS.IE;
  1862. $.Browser.version = parseFloat(
  1863. ua.substring(
  1864. ua.indexOf( "MSIE" ) + 5,
  1865. ua.indexOf( ";", ua.indexOf( "MSIE" ) ) )
  1866. );
  1867. }
  1868. break;
  1869. case "Netscape":
  1870. if( !!window.addEventListener ){
  1871. if ( ua.indexOf( "Firefox" ) >= 0 ) {
  1872. $.Browser.vendor = $.BROWSERS.FIREFOX;
  1873. $.Browser.version = parseFloat(
  1874. ua.substring( ua.indexOf( "Firefox" ) + 8 )
  1875. );
  1876. } else if ( ua.indexOf( "Safari" ) >= 0 ) {
  1877. $.Browser.vendor = ua.indexOf( "Chrome" ) >= 0 ?
  1878. $.BROWSERS.CHROME :
  1879. $.BROWSERS.SAFARI;
  1880. $.Browser.version = parseFloat(
  1881. ua.substring(
  1882. ua.substring( 0, ua.indexOf( "Safari" ) ).lastIndexOf( "/" ) + 1,
  1883. ua.indexOf( "Safari" )
  1884. )
  1885. );
  1886. }
  1887. }
  1888. break;
  1889. case "Opera":
  1890. $.Browser.vendor = $.BROWSERS.OPERA;
  1891. $.Browser.version = parseFloat( ver );
  1892. break;
  1893. }
  1894. // ignore '?' portion of query string
  1895. var query = window.location.search.substring( 1 ),
  1896. parts = query.split('&'),
  1897. part,
  1898. sep,
  1899. i;
  1900. for ( i = 0; i < parts.length; i++ ) {
  1901. part = parts[ i ];
  1902. sep = part.indexOf( '=' );
  1903. if ( sep > 0 ) {
  1904. URLPARAMS[ part.substring( 0, sep ) ] =
  1905. decodeURIComponent( part.substring( sep + 1 ) );
  1906. }
  1907. }
  1908. //determine if this browser supports image alpha transparency
  1909. $.Browser.alpha = !(
  1910. (
  1911. $.Browser.vendor == $.BROWSERS.IE &&
  1912. $.Browser.version < 9
  1913. ) || (
  1914. $.Browser.vendor == $.BROWSERS.CHROME &&
  1915. $.Browser.version < 2
  1916. )
  1917. );
  1918. //determine if this browser supports element.style.opacity
  1919. $.Browser.opacity = !(
  1920. $.Browser.vendor == $.BROWSERS.IE &&
  1921. $.Browser.version < 9
  1922. );
  1923. })();
  1924. //TODO: $.console is often used inside a try/catch block which generally
  1925. // prevents allowings errors to occur with detection until a debugger
  1926. // is attached. Although I've been guilty of the same anti-pattern
  1927. // I eventually was convinced that errors should naturally propogate in
  1928. // all but the most special cases.
  1929. /**
  1930. * A convenient alias for console when available, and a simple null
  1931. * function when console is unavailable.
  1932. * @static
  1933. * @private
  1934. */
  1935. var nullfunction = function( msg ){
  1936. //document.location.hash = msg;
  1937. };
  1938. $.console = window.console || {
  1939. log: nullfunction,
  1940. debug: nullfunction,
  1941. info: nullfunction,
  1942. warn: nullfunction,
  1943. error: nullfunction
  1944. };
  1945. // Adding support for HTML5's requestAnimationFrame as suggested by acdha.
  1946. // Implementation taken from matt synder's post here:
  1947. // http://mattsnider.com/cross-browser-and-legacy-supported-requestframeanimation/
  1948. (function( w ) {
  1949. // most browsers have an implementation
  1950. var requestAnimationFrame = w.requestAnimationFrame ||
  1951. w.mozRequestAnimationFrame ||
  1952. w.webkitRequestAnimationFrame ||
  1953. w.msRequestAnimationFrame;
  1954. var cancelAnimationFrame = w.cancelAnimationFrame ||
  1955. w.mozCancelAnimationFrame ||
  1956. w.webkitCancelAnimationFrame ||
  1957. w.msCancelAnimationFrame;
  1958. // polyfill, when necessary
  1959. if ( requestAnimationFrame && cancelAnimationFrame ) {
  1960. // We can't assign these window methods directly to $ because they
  1961. // expect their "this" to be "window", so we call them in wrappers.
  1962. $.requestAnimationFrame = function(){
  1963. return requestAnimationFrame.apply( w, arguments );
  1964. };
  1965. $.cancelAnimationFrame = function(){
  1966. return cancelAnimationFrame.apply( w, arguments );
  1967. };
  1968. } else {
  1969. var aAnimQueue = [],
  1970. processing = [],
  1971. iRequestId = 0,
  1972. iIntervalId;
  1973. // create a mock requestAnimationFrame function
  1974. $.requestAnimationFrame = function( callback ) {
  1975. aAnimQueue.push( [ ++iRequestId, callback ] );
  1976. if ( !iIntervalId ) {
  1977. iIntervalId = setInterval( function() {
  1978. if ( aAnimQueue.length ) {
  1979. var time = $.now();
  1980. // Process all of the currently outstanding frame
  1981. // requests, but none that get added during the
  1982. // processing.
  1983. // Swap the arrays so we don't have to create a new
  1984. // array every frame.
  1985. var temp = processing;
  1986. processing = aAnimQueue;
  1987. aAnimQueue = temp;
  1988. while ( processing.length ) {
  1989. processing.shift()[ 1 ]( time );
  1990. }
  1991. } else {
  1992. // don't continue the interval, if unnecessary
  1993. clearInterval( iIntervalId );
  1994. iIntervalId = undefined;
  1995. }
  1996. }, 1000 / 50); // estimating support for 50 frames per second
  1997. }
  1998. return iRequestId;
  1999. };
  2000. // create a mock cancelAnimationFrame function
  2001. $.cancelAnimationFrame = function( requestId ) {
  2002. // find the request ID and remove it
  2003. var i, j;
  2004. for ( i = 0, j = aAnimQueue.length; i < j; i += 1 ) {
  2005. if ( aAnimQueue[ i ][ 0 ] === requestId ) {
  2006. aAnimQueue.splice( i, 1 );
  2007. return;
  2008. }
  2009. }
  2010. // If it's not in the queue, it may be in the set we're currently
  2011. // processing (if cancelAnimationFrame is called from within a
  2012. // requestAnimationFrame callback).
  2013. for ( i = 0, j = processing.length; i < j; i += 1 ) {
  2014. if ( processing[ i ][ 0 ] === requestId ) {
  2015. processing.splice( i, 1 );
  2016. return;
  2017. }
  2018. }
  2019. };
  2020. }
  2021. })( window );
  2022. /**
  2023. * @private
  2024. * @inner
  2025. * @function
  2026. * @param {Element} element
  2027. * @param {Boolean} [isFixed]
  2028. * @returns {Element}
  2029. */
  2030. function getOffsetParent( element, isFixed ) {
  2031. if ( isFixed && element != document.body ) {
  2032. return document.body;
  2033. } else {
  2034. return element.offsetParent;
  2035. }
  2036. }
  2037. /**
  2038. * @private
  2039. * @inner
  2040. * @function
  2041. * @param {XMLHttpRequest} xhr
  2042. * @param {String} tilesUrl
  2043. * @deprecated
  2044. */
  2045. function processDZIResponse( xhr, tilesUrl ) {
  2046. var status,
  2047. statusText,
  2048. doc = null;
  2049. if ( !xhr ) {
  2050. throw new Error( $.getString( "Errors.Security" ) );
  2051. } else if ( xhr.status !== 200 && xhr.status !== 0 ) {
  2052. status = xhr.status;
  2053. statusText = ( status == 404 ) ?
  2054. "Not Found" :
  2055. xhr.statusText;
  2056. throw new Error( $.getString( "Errors.Status", status, statusText ) );
  2057. }
  2058. if ( xhr.responseXML && xhr.responseXML.documentElement ) {
  2059. doc = xhr.responseXML;
  2060. } else if ( xhr.responseText ) {
  2061. doc = $.parseXml( xhr.responseText );
  2062. }
  2063. return processDZIXml( doc, tilesUrl );
  2064. }
  2065. /**
  2066. * @private
  2067. * @inner
  2068. * @function
  2069. * @param {Document} xmlDoc
  2070. * @param {String} tilesUrl
  2071. * @deprecated
  2072. */
  2073. function processDZIXml( xmlDoc, tilesUrl ) {
  2074. if ( !xmlDoc || !xmlDoc.documentElement ) {
  2075. throw new Error( $.getString( "Errors.Xml" ) );
  2076. }
  2077. var root = xmlDoc.documentElement,
  2078. rootName = root.tagName;
  2079. if ( rootName == "Image" ) {
  2080. try {
  2081. return processDZI( root, tilesUrl );
  2082. } catch ( e ) {
  2083. throw (e instanceof Error) ?
  2084. e :
  2085. new Error( $.getString("Errors.Dzi") );
  2086. }
  2087. } else if ( rootName == "Collection" ) {
  2088. throw new Error( $.getString( "Errors.Dzc" ) );
  2089. } else if ( rootName == "Error" ) {
  2090. return $._processDZIError( root );
  2091. }
  2092. throw new Error( $.getString( "Errors.Dzi" ) );
  2093. }
  2094. /**
  2095. * @private
  2096. * @inner
  2097. * @function
  2098. * @param {Element} imageNode
  2099. * @param {String} tilesUrl
  2100. * @deprecated
  2101. */
  2102. function processDZI( imageNode, tilesUrl ) {
  2103. var fileFormat = imageNode.getAttribute( "Format" ),
  2104. sizeNode = imageNode.getElementsByTagName( "Size" )[ 0 ],
  2105. dispRectNodes = imageNode.getElementsByTagName( "DisplayRect" ),
  2106. width = parseInt( sizeNode.getAttribute( "Width" ), 10 ),
  2107. height = parseInt( sizeNode.getAttribute( "Height" ), 10 ),
  2108. tileSize = parseInt( imageNode.getAttribute( "TileSize" ), 10 ),
  2109. tileOverlap = parseInt( imageNode.getAttribute( "Overlap" ), 10 ),
  2110. dispRects = [],
  2111. dispRectNode,
  2112. rectNode,
  2113. i;
  2114. if ( !$.imageFormatSupported( fileFormat ) ) {
  2115. throw new Error(
  2116. $.getString( "Errors.ImageFormat", fileFormat.toUpperCase() )
  2117. );
  2118. }
  2119. for ( i = 0; i < dispRectNodes.length; i++ ) {
  2120. dispRectNode = dispRectNodes[ i ];
  2121. rectNode = dispRectNode.getElementsByTagName( "Rect" )[ 0 ];
  2122. dispRects.push( new $.DisplayRect(
  2123. parseInt( rectNode.getAttribute( "X" ), 10 ),
  2124. parseInt( rectNode.getAttribute( "Y" ), 10 ),
  2125. parseInt( rectNode.getAttribute( "Width" ), 10 ),
  2126. parseInt( rectNode.getAttribute( "Height" ), 10 ),
  2127. 0, // ignore MinLevel attribute, bug in Deep Zoom Composer
  2128. parseInt( dispRectNode.getAttribute( "MaxLevel" ), 10 )
  2129. ));
  2130. }
  2131. return new $.DziTileSource(
  2132. width,
  2133. height,
  2134. tileSize,
  2135. tileOverlap,
  2136. tilesUrl,
  2137. fileFormat,
  2138. dispRects
  2139. );
  2140. }
  2141. /**
  2142. * @private
  2143. * @inner
  2144. * @function
  2145. * @param {Element} imageNode
  2146. * @param {String} tilesUrl
  2147. * @deprecated
  2148. */
  2149. function processDZIJSON( imageData, tilesUrl ) {
  2150. var fileFormat = imageData.Format,
  2151. sizeData = imageData.Size,
  2152. dispRectData = imageData.DisplayRect || [],
  2153. width = parseInt( sizeData.Width, 10 ),
  2154. height = parseInt( sizeData.Height, 10 ),
  2155. tileSize = parseInt( imageData.TileSize, 10 ),
  2156. tileOverlap = parseInt( imageData.Overlap, 10 ),
  2157. dispRects = [],
  2158. rectData,
  2159. i;
  2160. if ( !$.imageFormatSupported( fileFormat ) ) {
  2161. throw new Error(
  2162. $.getString( "Errors.ImageFormat", fileFormat.toUpperCase() )
  2163. );
  2164. }
  2165. for ( i = 0; i < dispRectData.length; i++ ) {
  2166. rectData = dispRectData[ i ].Rect;
  2167. dispRects.push( new $.DisplayRect(
  2168. parseInt( rectData.X, 10 ),
  2169. parseInt( rectData.Y, 10 ),
  2170. parseInt( rectData.Width, 10 ),
  2171. parseInt( rectData.Height, 10 ),
  2172. 0, // ignore MinLevel attribute, bug in Deep Zoom Composer
  2173. parseInt( rectData.MaxLevel, 10 )
  2174. ));
  2175. }
  2176. return new $.DziTileSource(
  2177. width,
  2178. height,
  2179. tileSize,
  2180. tileOverlap,
  2181. tilesUrl,
  2182. fileFormat,
  2183. dispRects
  2184. );
  2185. }
  2186. /**
  2187. * @private
  2188. * @inner
  2189. * @function
  2190. * @param {Document} errorNode
  2191. * @throws {Error}
  2192. * @deprecated
  2193. */
  2194. $._processDZIError = function ( errorNode ) {
  2195. var messageNode = errorNode.getElementsByTagName( "Message" )[ 0 ],
  2196. message = messageNode.firstChild.nodeValue;
  2197. throw new Error(message);
  2198. };
  2199. }( OpenSeadragon ));