diff --git a/build/js/diva.js b/build/js/diva.js deleted file mode 100644 index e1694b6e..00000000 --- a/build/js/diva.js +++ /dev/null @@ -1,13555 +0,0 @@ -(function webpackUniversalModuleDefinition(root, factory) { - if(typeof exports === 'object' && typeof module === 'object') - module.exports = factory(require("jquery")); - else if(typeof define === 'function' && define.amd) - define(["jquery"], factory); - else if(typeof exports === 'object') - exports["diva"] = factory(require("jquery")); - else - root["diva"] = factory(root["jQuery"]); -})(this, function(__WEBPACK_EXTERNAL_MODULE_3__) { -return /******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; - -/******/ // The require function -/******/ function __webpack_require__(moduleId) { - -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) -/******/ return installedModules[moduleId].exports; - -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ exports: {}, -/******/ id: moduleId, -/******/ loaded: false -/******/ }; - -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); - -/******/ // Flag the module as loaded -/******/ module.loaded = true; - -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } - - -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; - -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; - -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; - -/******/ // Load entry module and return exports -/******/ return __webpack_require__(0); -/******/ }) -/************************************************************************/ -/******/ ([ -/* 0 */ -/***/ (function(module, exports, __webpack_require__) { - - __webpack_require__(1); - module.exports = __webpack_require__(7); - - -/***/ }), -/* 1 */ -/***/ (function(module, exports, __webpack_require__) { - - var diva = __webpack_require__(2); - - diva.registerPlugin(__webpack_require__(6)); - diva.registerPlugin(__webpack_require__(43)); - diva.registerPlugin(__webpack_require__(44)); - diva.registerPlugin(__webpack_require__(45)); - diva.registerPlugin(__webpack_require__(46)); - diva.registerPlugin(__webpack_require__(47)); - - -/***/ }), -/* 2 */ -/***/ (function(module, exports, __webpack_require__) { - - var $ = __webpack_require__(3); - - var Events = __webpack_require__(4); - var PluginRegistry = __webpack_require__(5); - - var diva = module.exports = { - Events: new Events(), - - registerPlugin: function (plugin) - { - PluginRegistry.register(plugin); - }, - - /** - * Create a new Diva instance at the given element - * - * @param element {Element} - * @param options {Object} - * @returns {Diva} - */ - create: function (element, options) - { - if (diva.find(element)) - throw new Error('Diva is already initialized on ' + reprElem(element)); - - var $elem = $(element); - $elem.diva(options); - - return $elem.data('diva'); - }, - - /** - * Return the Diva instance attached to the - * element, if any. - * - * @param element - * @returns {Diva|null} - */ - find: function (element) - { - return $(element).data('diva') || null; - } - }; - - function reprElem(elem) - { - var id = elem.id ? '#' + elem.id : elem.id; - var classes = elem.className ? '.' + elem.className.split(/\s+/g).join('.') : ''; - - return (id ? id : elem.tagName.toLowerCase()) + classes; - } - - -/***/ }), -/* 3 */ -/***/ (function(module, exports) { - - module.exports = __WEBPACK_EXTERNAL_MODULE_3__; - -/***/ }), -/* 4 */ -/***/ (function(module, exports) { - - module.exports = Events; - - /** - * Events. Pub/Sub system for Loosely Coupled logic. - * Based on Peter Higgins' port from Dojo to jQuery - * https://github.com/phiggins42/bloody-jquery-plugins/blob/master/pubsub.js - * - * Re-adapted to vanilla Javascript - * - * @class Events - */ - function Events() - { - this._cache = {}; - } - - /** - * diva.Events.publish - * e.g.: diva.Events.publish("PageDidLoad", [pageIndex, filename, pageSelector], this); - * - * @class Events - * @method publish - * @param topic {String} - * @param args {Array} - * @param scope {Object=} Optional - Subscribed functions will be executed with the supplied object as `this`. - * It is necessary to supply this argument with the self variable when within a Diva instance. - * The scope argument is matched with the instance ID of subscribers to determine whether they - * should be executed. (See instanceID argument of subscribe.) - */ - Events.prototype.publish = function (topic, args, scope) - { - if (this._cache[topic]) - { - var thisTopic = this._cache[topic]; - - if (typeof thisTopic.global !== 'undefined') - { - var thisTopicGlobal = thisTopic.global; - var globalCount = thisTopicGlobal.length; - - for (var i=0; i < globalCount; i++) - { - thisTopicGlobal[i].apply(scope || null, args || []); - } - } - - if (scope && typeof scope.getInstanceId !== 'undefined') - { - // get publisher instance ID from scope arg, compare, and execute if match - var instanceID = scope.getInstanceId(); - - if (this._cache[topic][instanceID]) - { - var thisTopicInstance = this._cache[topic][instanceID]; - var scopedCount = thisTopicInstance.length; - - for (var j=0; j < scopedCount; j++) - { - thisTopicInstance[j].apply(scope, args || []); - } - } - } - } - }; - - /** - * diva.Events.subscribe - * e.g.: diva.Events.subscribe("PageDidLoad", highlight, settings.ID) - * - * @class Events - * @method subscribe - * @param topic {String} - * @param callback {Function} - * @param instanceID {String=} Optional - String representing the ID of a Diva instance; if provided, - * callback only fires for events published from that instance. - * @return Event handler {Array} - */ - Events.prototype.subscribe = function (topic, callback, instanceID) - { - if (!this._cache[topic]) - { - this._cache[topic] = {}; - } - - if (typeof instanceID === 'string') - { - if (!this._cache[topic][instanceID]) - { - this._cache[topic][instanceID] = []; - } - - this._cache[topic][instanceID].push(callback); - } - else - { - if (!this._cache[topic].global) - { - this._cache[topic].global = []; - } - - this._cache[topic].global.push(callback); - } - - var handle = instanceID ? [topic, callback, instanceID] : [topic, callback]; - - return handle; - }; - - /** - * diva.Events.unsubscribe - * e.g.: var handle = Events.subscribe("PageDidLoad", highlight); - * Events.unsubscribe(handle); - * - * @class Events - * @method unsubscribe - * @param handle {Array} - * @param completely {Boolean=} - Unsubscribe all events for a given topic. - * @return success {Boolean} - */ - Events.prototype.unsubscribe = function (handle, completely) - { - var t = handle[0]; - - if (this._cache[t]) - { - var topicArray; - var instanceID = handle.length === 3 ? handle[2] : 'global'; - - topicArray = this._cache[t][instanceID]; - - if (!topicArray) - { - return false; - } - - if (completely) - { - delete this._cache[t][instanceID]; - return topicArray.length > 0; - } - - var i = topicArray.length; - while (i--) - { - if (topicArray[i] === handle[1]) - { - this._cache[t][instanceID].splice(i, 1); - return true; - } - } - } - - return false; - }; - - /** - * diva.Events.unsubscribeAll - * e.g.: diva.Events.unsubscribeAll('global'); - * - * @class Events - * @param instanceID {String=} Optional - instance ID to remove subscribers from or 'global' (if omitted, - * subscribers in all scopes removed) - * @method unsubscribeAll - */ - Events.prototype.unsubscribeAll = function (instanceID) - { - if (instanceID) - { - var topics = Object.keys(this._cache); - var i = topics.length; - var topic; - - while (i--) - { - topic = topics[i]; - - if (typeof this._cache[topic][instanceID] !== 'undefined') - { - delete this._cache[topic][instanceID]; - } - } - } - else - { - this._cache = {}; - } - }; - - -/***/ }), -/* 5 */ -/***/ (function(module, exports) { - - /** - * @module - * @private - * The global plugin registry. - */ - - var plugins = []; - - module.exports = { - register: function (plugin) - { - plugins.push(plugin); - }, - getAll: function () - { - return plugins; - } - }; - - -/***/ }), -/* 6 */ -/***/ (function(module, exports, __webpack_require__) { - - /* - Diva.JS autoscroll plugin - Author: Andrew Horwitz - - Lets Diva scroll in the primary direction (as determined by - settings.verticallyOriented) automatically at a given/changeable rate. - - Relevant settings: - -scrollSpeed: pixels per second (defaults to 10) - -disableManualScroll: disables manual scroll while automatic scroll is on (defaults to false) - -currentlyAutoScrolling: whether or not autoscroll is currently on - -autoScrollRefresh: ms between scrolling actions - -disableAutoscrollPrefs: disables the autoscroll preferences panel - - Relevant methods: - -startScrolling, stopScrolling, toggleScrolling - -changeRefresh, changeScrollSpeed (setters for respective options) - -disableManualScroll, enableManualScroll - */ - - var jQuery = __webpack_require__(3); - var diva = __webpack_require__(7); - - (function ($) - { - module.exports = (function() - { - var settings = {}; - var retval = - { - init: function(divaSettings, divaInstance) - { - var pixelsPerScroll; - var disableManualScroll; - var autoScrollRefresh; - var defaultAutoRefresh; - var scrollSpeed; - - function log10(x) - { - return Math.log(x) / Math.log(10); - } - - divaInstance.startScrolling = function() - { - if (divaSettings.currentlyAutoScrolling) - { - console.warn("You are trying to start autoscrolling, but it is already scrolling."); - return; - } - - $("#" + divaSettings.ID + "autoscroll-toggle").text("Turn off"); - if (disableManualScroll) - { - divaInstance.disableScrollable(); - } - - divaSettings.currentlyAutoScrolling = true; - restartScrollingInterval(); - }; - - var restartScrollingInterval = function() - { - clearInterval(divaSettings.autoScrollInterval); - if (divaSettings.verticallyOriented) - { - divaSettings.autoScrollInterval = setInterval(function(){ - divaSettings.viewportObject.scrollTop(divaSettings.viewportObject.scrollTop() + pixelsPerScroll); - }, autoScrollRefresh); - } - else - { - divaSettings.autoScrollInterval = setInterval(function(){ - divaSettings.viewportObject.scrollLeft(divaSettings.viewportObject.scrollLeft() + pixelsPerScroll); - }, autoScrollRefresh); - } - }; - - divaInstance.stopScrolling = function() - { - if (!divaSettings.currentlyAutoScrolling) - { - console.warn("You are trying to stop autoscrolling, but it is not currently active."); - return; - } - - $("#" + divaSettings.ID + "autoscroll-toggle").text("Turn on"); - if (disableManualScroll) - { - divaInstance.enableScrollable(); - } - - divaSettings.currentlyAutoScrolling = false; - clearInterval(divaSettings.autoScrollInterval); - }; - - divaInstance.toggleScrolling = function() - { - if (divaSettings.currentlyAutoScrolling) - divaInstance.stopScrolling(); - else - divaInstance.startScrolling(); - }; - - divaInstance.changeRefresh = function(newRefresh) - { - autoScrollRefresh = newRefresh; - updatePixelsPerScroll(); - }; - - divaInstance.changeScrollSpeed = function(newSpeed) - { - scrollSpeed = newSpeed; - updatePixelsPerScroll(); - - $("#" + divaSettings.ID + "autoscroll-pps").val(log10(scrollSpeed)); - if (divaSettings.currentlyAutoScrolling) - { - restartScrollingInterval(); - } - }; - - var updatePixelsPerScroll = function() - { - autoScrollRefresh = defaultAutoRefresh; - pixelsPerScroll = scrollSpeed / (1000 / autoScrollRefresh); - - //should be minimum of one otherwise it won't change the actual value - //user can change autoscrollrefresh or scrollspeed; this may overwrite autoScrollRefresh - if (pixelsPerScroll < 1) - { - autoScrollRefresh = autoScrollRefresh * (1 / pixelsPerScroll); - pixelsPerScroll = scrollSpeed / (1000 / autoScrollRefresh); - } - }; - - divaInstance.disableManualScroll = function() - { - disableManualScroll = true; - if (divaSettings.currentlyAutoScrolling) - { - divaInstance.disableScrollable(); - } - }; - - divaInstance.enableManualScroll = function() - { - disableManualScroll = false; - if (divaSettings.currentlyAutoScrolling) - { - divaInstance.enableScrollable(); - } - }; - - divaSettings.currentlyAutoScrolling = false; - divaSettings.autoScrollInterval = ""; - - disableManualScroll = divaSettings.disableManualScroll || false; - autoScrollRefresh = divaSettings.autoScrollRefresh || 50; - defaultAutoRefresh = autoScrollRefresh; - - divaInstance.changeScrollSpeed((divaSettings.scrollSpeed || 10)); - - $(window).on('keyup', function(e) - { - if (e.shiftKey && e.keyCode === 32) - { - divaInstance.toggleScrolling(); - } - }); - - if (!divaSettings.disableAutoscrollPrefs) - { - var setPosition = function(isFullscreen) - { - if (divaSettings.inFullscreen) - { - var fullscreenTools = $(divaSettings.selector + 'tools'); - var toolsMargin = fullscreenTools.css('right'); - settings.jqObj.css({ - 'right': toolsMargin, - 'margin-right': 0, - 'top': fullscreenTools.offset().top + fullscreenTools.outerHeight() + 15 - }); - } - else - { - settings.jqObj.css({ - 'right': $(window).width() - (divaSettings.viewportObject.offset().left + divaSettings.viewportObject.outerWidth()) + divaSettings.scrollbarWidth, - 'margin-right': '.6em' - }); - settings.jqObj.offset({'top': divaSettings.viewportObject.offset().top + 1}); - } - }; - - diva.Events.subscribe('ModeDidSwitch', setPosition, divaSettings.ID); - - diva.Events.subscribe('ViewerDidLoad', function(s) - { - var autoscrollPrefsString = - "
" + - "Autoscrolling options:
" + - "Speed:" + - "
" + - "Allow manual scroll:" + - "
" + - "" + - "
"; - $("#" + divaSettings.ID + "page-nav").before("
"); - $("body").prepend(autoscrollPrefsString); - - $("#" + divaSettings.ID + "autoscroll-pps").on('change', function(e) - { - divaInstance.changeScrollSpeed(Math.pow(10, e.target.value)); - }); - - $("#" + divaSettings.ID + "autoscroll-manual").on('change', function(e) - { - if (e.target.checked) - divaInstance.enableManualScroll(); - else - divaInstance.disableManualScroll(); - }); - - $("#" + divaSettings.ID + "autoscroll-toggle").on('click', divaInstance.toggleScrolling); - - $("#" + divaSettings.ID + "autoscroll-icon").on('click', function(e) - { - settings.jqObj = $("#" + divaSettings.ID + "autoscroll-prefs"); - - if (settings.jqObj.css('display') === 'none') - { - settings.jqObj.css({'display': 'block'}); - - setPosition(divaSettings.inFullscreen); - - } - else - { - settings.jqObj.css('display', 'none'); - } - }); - }, divaSettings.ID); - } - }, - pluginName: 'autoscroll', - titleText: 'Automatically scrolls page along primary axis' - }; - return retval; - })(); - })(jQuery); - - -/***/ }), -/* 7 */ -/***/ (function(module, exports, __webpack_require__) { - - /* - Copyright (C) 2011-2016 by Wendy Liu, Evan Magoni, Andrew Hankinson, Andrew Horwitz, Laurent Pugin - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - */ - __webpack_require__(8); - - var jQuery = __webpack_require__(3); - - var elt = __webpack_require__(9); - var HashParams = __webpack_require__(10); - - var ActiveDivaController = __webpack_require__(11); - var diva = __webpack_require__(2); - var ImageManifest = __webpack_require__(12); - var createToolbar = __webpack_require__(14); - var ViewerCore = __webpack_require__(15); - - // Start the active Diva tracker - var activeDiva = new ActiveDivaController(); // jshint ignore: line - - module.exports = diva; - - // this pattern was taken from http://www.virgentech.com/blog/2009/10/building-object-oriented-jquery-plugin.html - (function ($) - { - var Diva = function (element, options) - { - // Global instance variables (set way down in `init`) - var settings, viewerState, divaState; - var self = this; - - // These are elements that can be overridden upon instantiation - // See https://github.com/DDMAL/diva.js/wiki/Settings for more details - options = $.extend({ - adaptivePadding: 0.05, // The ratio of padding to the page dimension - arrowScrollAmount: 40, // The amount (in pixels) to scroll by when using arrow keys - blockMobileMove: false, // Prevent moving or scrolling the page on mobile devices - objectData: '', // A IIIF Manifest or a JSON file generated by process.py that provides the object dimension data, or a URL pointing to such data - *REQUIRED* - enableAutoTitle: true, // Shows the title within a div of id diva-title - enableFilename: true, // Uses filenames and not page numbers for links (i=bm_001.tif, not p=1) - enableFullscreen: true, // Enable or disable fullscreen icon (mode still available) - enableGotoPage: true, // A "go to page" jump box - enableGotoSuggestions: true, // Controls whether suggestions are shown under the input field when the user is typing in the 'go to page' form - enableGridIcon: true, // A grid view of all the pages - enableGridControls: 'buttons', // Specify control of pages per grid row in Grid view. Possible values: 'buttons' (+/-), 'slider'. Any other value disables the controls. - enableImageTitles: true, // Adds "Page {n}" title to page images if true - enableKeyScroll: true, // Captures scrolling using the arrow and page up/down keys regardless of page focus. When off, defers to default browser scrolling behavior. - enableLinkIcon: true, // Controls the visibility of the link icon - enableNonPagedVisibilityIcon: true, // Controls the visibility of the icon to toggle the visibility of non-paged pages. (Automatically hidden if no 'non-paged' pages). - enableSpaceScroll: false, // Scrolling down by pressing the space key - enableToolbar: true, // Enables the toolbar. Note that disabling this means you have to handle all controls yourself. - enableZoomControls: 'buttons', // Specify controls for zooming in and out. Possible values: 'buttons' (+/-), 'slider'. Any other value disables the controls. - fillParentHeight: true, // Use a flexbox layout to allow Diva to fill its parent's height - fixedPadding: 10, // Fallback if adaptive padding is set to 0 - fixedHeightGrid: true, // So each page in grid view has the same height (only widths differ) - goDirectlyTo: 0, // Default initial page to show (0-indexed) - hashParamSuffix: null, // Used when there are multiple document viewers on a page - iipServerURL: '', // The URL to the IIPImage installation, including the `?FIF=` - *REQUIRED*, unless using IIIF - inFullscreen: false, // Set to true to load fullscreen mode initially - inBookLayout: false, // Set to true to view the document with facing pages in document mode - inGrid: false, // Set to true to load grid view initially - imageDir: '', // Image directory, either absolute path or relative to IIP's FILESYSTEM_PREFIX - *REQUIRED*, unless using IIIF - maxPagesPerRow: 8, // Maximum number of pages per row in grid view - maxZoomLevel: -1, // Optional; defaults to the max zoom returned in the JSON response - minPagesPerRow: 2, // Minimum pages per row in grid view. Recommended default. - minZoomLevel: 0, // Defaults to 0 (the minimum zoom) - onGotoSubmit: null, // When set to a function that takes a string and returns a page index, this will override the default behaviour of the 'go to page' form submission - pageAliases: {}, // An object mapping specific page indices to aliases (has priority over 'pageAliasFunction' - pageAliasFunction: function(){return false;}, // A function mapping page indices to an alias. If false is returned, default page number is displayed - pageLoadTimeout: 200, // Number of milliseconds to wait before loading pages - pagesPerRow: 5, // The default number of pages per row in grid view - showNonPagedPages: false, // Whether pages tagged as 'non-paged' (in IIIF manifests only) should be visible after initial load - throbberTimeout: 100, // Number of milliseconds to wait before showing throbber - tileHeight: 256, // The height of each tile, in pixels; usually 256 - tileWidth: 256, // The width of each tile, in pixels; usually 256 - toolbarParentObject: null, // The toolbar parent object. - verticallyOriented: true, // Determines vertical vs. horizontal orientation - viewportMargin: 200, // Pretend tiles +/- 200px away from viewport are in - zoomLevel: 2 // The initial zoom level (used to store the current zoom level) - }, options); - - // Returns the page index associated with the given filename; must called after setting settings.manifest - var getPageIndex = function (filename) - { - return getPageIndexForManifest(settings.manifest, filename); - }; - - var NotAnIIIFManifestException = function (message) - { - return message; - }; - - var getPageIndexForManifest = function (manifest, filename) - { - var i, - np = manifest.pages.length; - - for (i = 0; i < np; i++) - { - if (!manifest.pages[i]) - { - return -1; - } - - if (manifest.pages[i].f === filename) - { - return i; - } - } - - return -1; - }; - - // Check if a page index is valid - var isPageValid = function (pageIndex) - { - return settings.manifest.isPageValid(pageIndex, settings.showNonPagedPages); - }; - - var reloadViewer = function (newOptions) - { - return divaState.viewerCore.reload(newOptions); - }; - - // Called when the change view icon is clicked - var changeView = function (destinationView) - { - switch (destinationView) - { - case 'document': - return reloadViewer({ - inGrid: false, - inBookLayout: false - }); - - case 'book': - return reloadViewer({ - inGrid: false, - inBookLayout: true - }); - - case 'grid': - return reloadViewer({ - inGrid: true - }); - - default: - return false; - } - }; - - //toggles between orientations - var toggleOrientation = function () - { - var verticallyOriented = !settings.verticallyOriented; - - //if in grid, switch out of grid - reloadViewer({ - inGrid: false, - verticallyOriented: verticallyOriented, - goDirectlyTo: settings.currentPageIndex, - verticalOffset: divaState.viewerCore.getYOffset(), - horizontalOffset: divaState.viewerCore.getXOffset() - }); - - return verticallyOriented; - }; - - // Called when the fullscreen icon is clicked - var toggleFullscreen = function () - { - reloadViewer({ - inFullscreen: !settings.inFullscreen - }); - }; - - var getState = function () - { - var view; - - if (settings.inGrid) - { - view = 'g'; - } - else if (settings.inBookLayout) - { - view = 'b'; - } - else - { - view = 'd'; - } - - var layout = divaState.viewerCore.getCurrentLayout(); - var pageOffset = layout.getPageToViewportCenterOffset(settings.currentPageIndex, viewerState.viewport); - - var state = { - 'f': settings.inFullscreen, - 'v': view, - 'z': settings.zoomLevel, - 'n': settings.pagesPerRow, - 'i': settings.enableFilename ? settings.manifest.pages[settings.currentPageIndex].f : false, - 'p': settings.enableFilename ? false : settings.currentPageIndex + 1, - 'y': pageOffset ? pageOffset.y : false, - 'x': pageOffset ? pageOffset.x : false - }; - - return state; - }; - - var getLoadOptionsForState = function (state, manifest) - { - manifest = manifest || settings.manifest; - - var options = ('v' in state) ? getViewState(state.v) : {}; - - if ('f' in state) - options.inFullscreen = state.f; - - if ('z' in state) - options.zoomLevel = state.z; - - if ('n' in state) - options.pagesPerRow = state.n; - - // Only change specify the page if state.i or state.p is valid - var pageIndex = getPageIndexForManifest(manifest, state.i); - - if (!(pageIndex >= 0 && pageIndex < manifest.pages.length)) - { - pageIndex = state.p - 1; - - // Possibly NaN - if (!(pageIndex >= 0 && pageIndex < manifest.pages.length)) - pageIndex = null; - } - - if (pageIndex !== null) - { - var horizontalOffset = parseInt(state.x, 10); - var verticalOffset = parseInt(state.y, 10); - - options.goDirectlyTo = pageIndex; - options.horizontalOffset = horizontalOffset; - options.verticalOffset = verticalOffset; - } - - return options; - }; - - var getURLHash = function () - { - var hashParams = getState(); - var hashStringBuilder = []; - var param; - - for (param in hashParams) - { - if (hashParams[param] !== false) - hashStringBuilder.push(param + settings.hashParamSuffix + '=' + encodeURIComponent(hashParams[param])); - } - - return hashStringBuilder.join('&'); - }; - - // Returns the URL to the current state of the document viewer (so it should be an exact replica) - var getCurrentURL = function () - { - return location.protocol + '//' + location.host + location.pathname + location.search + '#' + getURLHash(); - }; - - var getViewState = function(view) - { - switch (view) - { - case 'd': - return { - inGrid: false, - inBookLayout: false - }; - - case 'b': - return { - inGrid: false, - inBookLayout: true - }; - - case 'g': - return { - inGrid: true, - inBookLayout: false - }; - - default: - return null; - } - }; - - var showError = function(message) - { - divaState.viewerCore.showError(message); - }; - - var ajaxError = function(jqxhr, status, error) - { - // Show a basic error message within the document viewer pane - - var errorMessage = ['Invalid objectData setting. Error code: ' + jqxhr.status + ' ' + error]; - - // Detect and handle CORS errors - var dataHasAbsolutePath = settings.objectData.lastIndexOf('http', 0) === 0; - - if (dataHasAbsolutePath && error === '') - { - var jsonHost = settings.objectData.replace(/https?:\/\//i, "").split(/[/?#]/)[0]; - - if (location.hostname !== jsonHost) - { - errorMessage.push( - elt('p', 'Attempted to access cross-origin data without CORS.'), - elt('p', - 'You may need to update your server configuration to support CORS. For help, see the ', - elt('a', { - href: 'https://github.com/DDMAL/diva.js/wiki/Installation#a-note-about-cross-site-requests', - target: '_blank' - }, 'cross-site request documentation.') - ) - ); - } - } - - showError(errorMessage); - }; - - var loadObjectData = function (responseData, hashState) - { - var manifest; - - // TODO improve IIIF detection method - if (!responseData.hasOwnProperty('@context') && (responseData['@context'].indexOf('iiif') === -1 || responseData['@context'].indexOf('shared-canvas') === -1)) - { - throw new NotAnIIIFManifestException('This does not appear to be a IIIF Manifest.'); - } - - // trigger ManifestDidLoad event - // FIXME: Why is this triggered before the manifest is parsed? See https://github.com/DDMAL/diva.js/issues/357 - diva.Events.publish('ManifestDidLoad', [responseData], self); - - manifest = ImageManifest.fromIIIF(responseData); - var loadOptions = hashState ? getLoadOptionsForState(hashState, manifest) : {}; - - divaState.viewerCore.setManifest(manifest, loadOptions); - }; - - /** Parse the hash parameters into the format used by getState and setState */ - var getHashParamState = function () - { - var state = {}; - - ['f', 'v', 'z', 'n', 'i', 'p', 'y', 'x'].forEach(function (param) - { - var value = HashParams.get(param + settings.hashParamSuffix); - - // `false` is returned if the value is missing - if (value !== false) - state[param] = value; - }); - - // Do some awkward special-casing, since this format is kind of weird. - - // For inFullscreen (f), true and false strings should be interpreted - // as booleans. - if (state.f === 'true') - state.f = true; - else if (state.f === 'false') - state.f = false; - - // Convert numerical values to integers, if provided - ['z', 'n', 'p', 'x', 'y'].forEach(function (param) - { - if (param in state) - state[param] = parseInt(state[param], 10); - }); - - return state; - }; - - var checkLoaded = function() - { - if (!viewerState.loaded) - { - console.warn("The viewer is not completely initialized. This is likely because it is still downloading data. To fix this, only call this function if the isReady() method returns true."); - return false; - } - return true; - }; - - var init = function () - { - // In order to fill the height, use a wrapper div displayed using a flexbox layout - var wrapperElement = elt('div', { - class: "diva-wrapper" + (options.fillParentHeight ? " diva-wrapper-flexbox" : "") - }); - element.appendChild(wrapperElement); - options.toolbarParentObject = options.toolbarParentObject || $(wrapperElement); - - var viewerCore = new ViewerCore(wrapperElement, options, self); - - viewerState = viewerCore.getInternalState(); - settings = viewerCore.getSettings(); - - // Add the ID to the wrapper element now that the ID has been generated by the viewer core - wrapperElement.id = settings.ID + 'wrapper'; - - divaState = { - viewerCore: viewerCore, - toolbar: settings.enableToolbar ? createToolbar(self) : null - }; - - var hashState = getHashParamState(); - - if (typeof settings.objectData === 'object') - { - // Defer execution until initialization has completed - setTimeout(function () - { - loadObjectData(settings.objectData, hashState); - }, 0); - } - else - { - var pendingManifestRequest = $.ajax({ - url: settings.objectData, - cache: true, - dataType: 'json', - error: ajaxError, - success: function (responseData) - { - loadObjectData(responseData, hashState); - } - }); - - // Store the pending request so that it can be cancelled in the event that Diva needs to be destroyed - viewerCore.setPendingManifestRequest(pendingManifestRequest); - } - }; - - /* PUBLIC FUNCTIONS - =============================================== - */ - - // Returns the title of the document, based on the directory name - this.getItemTitle = function () - { - return settings.manifest.itemTitle; - }; - - // Go to a particular page by its page number (with indexing starting at 1) - //xAnchor may either be "left", "right", or default "center"; the (xAnchor) side of the page will be anchored to the (xAnchor) side of the diva-outer element - //yAnchor may either be "top", "bottom", or default "center"; same process as xAnchor. - // returns True if the page number passed is valid; false if it is not. - this.gotoPageByNumber = function (pageNumber, xAnchor, yAnchor) - { - console.warn("This method is deprecated. Consider using gotoPageByIndex(pageIndex, xAnchor, yAnchor) instead."); - var pageIndex = parseInt(pageNumber, 10) - 1; - return this.gotoPageByIndex(pageIndex, xAnchor, yAnchor); - }; - - // Go to a particular page (with indexing starting at 0) - //xAnchor may either be "left", "right", or default "center"; the (xAnchor) side of the page will be anchored to the (xAnchor) side of the diva-outer element - //yAnchor may either be "top", "bottom", or default "center"; same process as xAnchor. - // returns True if the page index is valid; false if it is not. - this.gotoPageByIndex = function (pageIndex, xAnchor, yAnchor) - { - pageIndex = parseInt(pageIndex, 10); - - if (isPageValid(pageIndex)) - { - var xOffset = divaState.viewerCore.getXOffset(pageIndex, xAnchor); - var yOffset = divaState.viewerCore.getYOffset(pageIndex, yAnchor); - - viewerState.renderer.goto(pageIndex, yOffset, xOffset); - return true; - } - - return false; - }; - - this.getNumberOfPages = function () - { - if (!checkLoaded()) - return false; - - return settings.numPages; - }; - - // Get page dimensions in the current view and zoom level - this.getPageDimensions = function (pageIndex) - { - if (!checkLoaded()) - return null; - - return divaState.viewerCore.getCurrentLayout().getPageDimensions(pageIndex); - }; - - // Returns the dimensions of a given page index at a given zoom level - this.getPageDimensionsAtZoomLevel = function (pageIdx, zoomLevel) - { - if (!checkLoaded()) - return false; - - if (zoomLevel > settings.maxZoomLevel) - zoomLevel = settings.maxZoomLevel; - - var pg = settings.manifest.pages[parseInt(pageIdx, 10)]; - var pgAtZoom = pg.d[parseInt(zoomLevel, 10)]; - return {'width': pgAtZoom.w, 'height': pgAtZoom.h}; - }; - - // Returns the dimensions of a given page at the current zoom level - // The current page index will be used if no pageIndex is specified - // Also works in Grid view - this.getPageDimensionsAtCurrentZoomLevel = function(pageIndex) - { - pageIndex = isPageValid(pageIndex) ? pageIndex : settings.currentPageIndex; - - if (!isPageValid(pageIndex)) - throw new Error('Invalid Page Index'); - - return divaState.viewerCore.getCurrentLayout().getPageDimensions(pageIndex); - }; - - // Returns the dimensions of the current page at the current zoom level - // Also works in Grid view - this.getCurrentPageDimensionsAtCurrentZoomLevel = function () - { - return this.getPageDimensionsAtCurrentZoomLevel(settings.currentPageIndex); - }; - - this.isReady = function () - { - return viewerState.loaded; - }; - - this.getCurrentPageIndex = function () - { - return settings.currentPageIndex; - }; - - this.getCurrentPageFilename = function () - { - return settings.manifest.pages[settings.currentPageIndex].f; - }; - - this.getCurrentCanvas = function (settings) - { - return settings.manifest.pages[settings.currentPageIndex].canvas; - }; - - this.getCurrentPageNumber = function () - { - console.warn("This method is deprecated. Consider using getCurrentPageIndex() instead."); - return settings.currentPageIndex + 1; - }; - - // Returns an array of all filenames in the document - this.getFilenames = function () - { - var filenames = []; - - for (var i = 0; i < settings.numPages; i++) - { - filenames[i] = settings.manifest.pages[i].f; - } - - return filenames; - }; - - // Returns the current zoom level - this.getZoomLevel = function () - { - return settings.zoomLevel; - }; - - // gets the maximum zoom level for the entire document - this.getMaxZoomLevel = function () - { - return settings.maxZoomLevel; - }; - - // gets the max zoom level for a given page - this.getMaxZoomLevelForPage = function (pageIdx) - { - if (!checkLoaded) - return false; - - return settings.manifest.pages[pageIdx].m; - }; - - this.getMinZoomLevel = function () - { - return settings.minZoomLevel; - }; - - // Use the provided zoom level (will check for validity first) - // Returns false if the zoom level is invalid, true otherwise - this.setZoomLevel = function (zoomLevel) - { - if (settings.inGrid) - { - reloadViewer({ - inGrid: false - }); - } - - return divaState.viewerCore.zoom(zoomLevel); - }; - - this.getGridPagesPerRow = function () - { - // TODO(wabain): Add test case - return this.pagesPerRow; - }; - - this.setGridPagesPerRow = function (newValue) - { - // TODO(wabain): Add test case - if (!divaState.viewerCore.isValidOption('pagesPerRow', newValue)) - return false; - - return reloadViewer({ - inGrid: true, - pagesPerRow: newValue - }); - }; - - // Zoom in. Will return false if it's at the maximum zoom - this.zoomIn = function () - { - return this.setZoomLevel(settings.zoomLevel + 1); - }; - - // Zoom out. Will return false if it's at the minimum zoom - this.zoomOut = function () - { - return this.setZoomLevel(settings.zoomLevel - 1); - }; - - // Check if something (e.g. a highlight box on a particular page) is visible - this.isRegionInViewport = function (pageIndex, leftOffset, topOffset, width, height) - { - var layout = divaState.viewerCore.getCurrentLayout(); - - if (!layout) - return false; - - var offset = layout.getPageOffset(pageIndex); - - var top = offset.top + topOffset; - var left = offset.left + leftOffset; - - return viewerState.viewport.intersectsRegion({ - top: top, - bottom: top + height, - left: left, - right: left + width - }); - }; - - //Public wrapper for isPageVisible - //Determines if a page is currently in the viewport - this.isPageInViewport = function (pageIndex) - { - return viewerState.renderer.isPageVisible(pageIndex); - }; - - //Public wrapper for isPageLoaded - //Determines if a page is currently in the DOM - this.isPageLoaded = function (pageIndex) - { - console.warn("This method is deprecated. Consider using isPageInViewport(pageIndex) instead."); - return this.isPageInViewport(pageIndex); - }; - - // Toggle fullscreen mode - this.toggleFullscreenMode = function () - { - toggleFullscreen(); - }; - - // Show/Hide non-paged pages - this.toggleNonPagedPagesVisibility = function () - { - reloadViewer({ showNonPagedPages: !settings.showNonPagedPages }); - }; - - // Show non-paged pages - this.showNonPagedPages = function () - { - reloadViewer({ showNonPagedPages: true }); - }; - - // Hide non-paged pages - this.hideNonPagedPages = function () - { - reloadViewer({ showNonPagedPages: false }); - }; - - // Close toolbar popups - this.closePopups = function () - { - divaState.toolbar.closePopups(); - }; - - // Enter fullscreen mode if currently not in fullscreen mode - // Returns false if in fullscreen mode initially, true otherwise - // This function will work even if enableFullscreen is set to false - this.enterFullscreenMode = function () - { - if (!settings.inFullscreen) - { - toggleFullscreen(); - return true; - } - - return false; - }; - - // Leave fullscreen mode if currently in fullscreen mode - // Returns true if in fullscreen mode intitially, false otherwise - this.leaveFullscreenMode = function () - { - if (settings.inFullscreen) - { - toggleFullscreen(); - return true; - } - - return false; - }; - - this.isInFullscreen = function () - { - return settings.inFullscreen; - }; - - // Change views. Takes 'document', 'book', or 'grid' to specify which view to switch into - this.changeView = function(destinationView) - { - return changeView(destinationView); - }; - - // Enter grid view if currently not in grid view - // Returns false if in grid view initially, true otherwise - this.enterGridView = function () - { - if (!settings.inGrid) - { - changeView('grid'); - return true; - } - - return false; - }; - - // Leave grid view if currently in grid view - // Returns true if in grid view initially, false otherwise - this.leaveGridView = function () - { - if (settings.inGrid) - { - reloadViewer({ inGrid: false }); - return true; - } - - return false; - }; - - // Jump to a page based on its filename - // Returns true if successful and false if the filename is invalid - this.gotoPageByName = function (filename, xAnchor, yAnchor) - { - var pageIndex = getPageIndex(filename); - return this.gotoPageByIndex(pageIndex, xAnchor, yAnchor); - }; - - this.gotoPageByLabel = function (label, xAnchor, yAnchor) - { - var pages = settings.manifest.pages; - for (var i = 0, len = pages.length; i < len; i++) - { - if (pages[i].l.toLowerCase().indexOf(label.toLowerCase()) > -1) - return this.gotoPageByIndex(i, xAnchor, yAnchor); - } - - // If no label was found, try to parse a page number - var pageIndex = parseInt(label, 10) - 1; - return this.gotoPageByIndex(pageIndex, xAnchor, yAnchor); - }; - - // Get the page index (0-based) corresponding to a given filename - // If the page index doesn't exist, this will return -1 - this.getPageIndex = function (filename) - { - return getPageIndex(filename); - }; - - // Get the current URL (exposes the private method) - this.getCurrentURL = function () - { - return getCurrentURL(); - }; - - // Check if a page index is within the range of the document - this.isPageIndexValid = function (pageIndex) - { - return isPageValid(pageIndex); - }; - - // Get the hash part only of the current URL (without the leading #) - this.getURLHash = function () - { - return getURLHash(); - }; - - // Get an object representing the state of this diva instance (for setState) - this.getState = function () - { - return getState(); - }; - - // Align this diva instance with a state object (as returned by getState) - this.setState = function (state) - { - reloadViewer(getLoadOptionsForState(state)); - }; - - // Get the instance selector for this instance, since it's auto-generated. - this.getInstanceSelector = function () - { - return settings.selector; - }; - - // Get the instance ID -- essentially the selector without the leading '#'. - this.getInstanceId = function () - { - return settings.ID; - }; - - this.getSettings = function () - { - return settings; - }; - - /* - Translates a measurement from the zoom level on the largest size - to one on the current zoom level. Takes a single number from the - max zoom level and translates that to a number scaled to the current - zoom level. - - For example, a point 1000 on an image that is on zoom level 2 of 5 - translates to a position of 111.111... (1000 / (5 - 2)^2). - - Works for a single pixel co-ordinate or a dimension (e.g., translates a box - that is 1000 pixels wide on the original to one that is 111.111 pixels wide - on the current zoom level). - */ - this.translateFromMaxZoomLevel = function (position) - { - var zoomDifference = settings.maxZoomLevel - settings.zoomLevel; - return position / Math.pow(2, zoomDifference); - }; - - /* - Translates a measurement from the current zoom level to the position on the - largest zoom level. Takes a single number and returns that number's value on the - image at the max zoom level. - - Works for a single pixel co-ordinate or a dimension (e.g., translates a box - that is 111.111 pixels wide on the current image to one that is 1000 pixels wide - on the current zoom level). - */ - this.translateToMaxZoomLevel = function (position) - { - var zoomDifference = settings.maxZoomLevel - settings.zoomLevel; - - // if there is no difference, it's a number on the max zoom level and - // we can just return the position. - if (zoomDifference === 0) - return position; - - return position * Math.pow(2, zoomDifference); - }; - - // Re-enables document dragging, scrolling (by keyboard if set), and zooming by double-clicking - this.enableScrollable = function() - { - divaState.viewerCore.enableScrollable(); - }; - - // Disables document dragging, scrolling (by keyboard if set), and zooming by double-clicking - this.disableScrollable = function () - { - divaState.viewerCore.disableScrollable(); - }; - - //Changes between horizontal layout and vertical layout. Returns true if document is now vertically oriented, false otherwise. - this.toggleOrientation = function () - { - return toggleOrientation(); - }; - - //Returns distance between the northwest corners of diva-inner and page index - this.getPageOffset = function(pageIndex, options) - { - var region = divaState.viewerCore.getPageRegion(pageIndex, options); - - return { - top: region.top, - left: region.left - }; - }; - - //shortcut to getPageOffset for current page - this.getCurrentPageOffset = function() - { - return this.getPageOffset(settings.currentPageIndex); - }; - - //Returns the page dimensions of given page at the current zoom level - this.getPageDimensionsAtCurrentGridLevel = function(pageIndex) - { - console.warn("This method is deprecated. Consider using getPageDimensionsAtCurrentZoomLevel(pageIndex) instead."); - return this.getPageDimensionsAtCurrentZoomLevel(pageIndex); - }; - - /* - Given a pageX and pageY value (as could be retreived from a jQuery event object), - returns either the page visible at that (x,y) position or -1 if no page is. - */ - this.getPageIndexForPageXYValues = function(pageX, pageY) - { - //get the four edges of the outer element - var outerOffset = viewerState.outerElement.getBoundingClientRect(); - var outerTop = outerOffset.top; - var outerLeft = outerOffset.left; - var outerBottom = outerOffset.bottom; - var outerRight = outerOffset.right; - - //if the clicked position was outside the diva-outer object, it was not on a visible portion of a page - if (pageX < outerLeft || pageX > outerRight) - return -1; - - if (pageY < outerTop || pageY > outerBottom) - return -1; - - //navigate through all diva page objects - var pages = document.getElementsByClassName('diva-page'); - var curPageIdx = pages.length; - while (curPageIdx--) - { - //get the offset for each page - var curPage = pages[curPageIdx]; - var curOffset = curPage.getBoundingClientRect(); - - //if this point is outside the horizontal boundaries of the page, continue - if (pageX < curOffset.left || pageX > curOffset.right) - continue; - - //same with vertical boundaries - if (pageY < curOffset.top || pageY > curOffset.bottom) - continue; - - //if we made it through the above two, we found the page we're looking for - return curPage.getAttribute('data-index'); - } - - //if we made it through that entire while loop, we didn't click on a page - return -1; - }; - - /* - * Given a set of clientX, clientY co-ordinates, returns an object - * - **/ - this.getPageCoordinatesHit = function(clientX, clientY) - { - if (viewerState.renderer) - { - return viewerState.renderer.getPageHit(clientX, clientY); - } - - return null; - }; - - /** - * Returns a URL for the image of the page at the given index. The - * optional size parameter supports setting the image width or height - * (default is full-sized). - */ - this.getPageImageURL = function (pageIndex, size) - { - return settings.manifest.getPageImageURL(pageIndex, size); - }; - - //Pretty self-explanatory. - this.isVerticallyOriented = function() - { - return settings.verticallyOriented; - }; - - this.changeObject = function(objectData) - { - viewerState.loaded = false; - divaState.viewerCore.clear(); - - if (viewerState.renderer) - viewerState.renderer.destroy(); - - viewerState.options.objectData = objectData; - - if (typeof objectData === 'object') - { - setTimeout(function () - { - loadObjectData(objectData); - }); - - return; - } - - viewerState.throbberTimeoutID = setTimeout(function () - { - $(settings.selector + 'throbber').show(); - }, settings.throbberTimeout); - - $.ajax({ - url: settings.objectData, - cache: true, - dataType: 'json', - error: ajaxError, - success: function (responseData) - { - loadObjectData(responseData); - } - }); - }; - - this.activate = function () - { - viewerState.isActiveDiva = true; - }; - - this.deactivate = function () - { - viewerState.isActiveDiva = false; - }; - - // Destroys this instance, tells plugins to do the same (for testing) - this.destroy = function () - { - divaState.viewerCore.destroy(); - }; - - // "Secretly" expose the page overlay API for the highlight plugin - this.__addPageOverlay = function (overlay) - { - divaState.viewerCore.addPageOverlay(overlay); - }; - - this.__removePageOverlay = function (overlay) - { - divaState.viewerCore.removePageOverlay(overlay); - }; - - /**** Page Alias Functions ****/ - /* - Main function. Will return the first of these three that - resolves to boolean true: - -Explicit alias as defined in pageAliases - -Result of pageAliasFunction - -originalPageIndex + 1 (to simulate the original mapping) - - Else the function will return false. - */ - this.getAliasForPageIndex = function(originalPageIndex) - { - var pageIndex = parseInt(originalPageIndex, 10); - return settings.pageAliases[pageIndex] || settings.pageAliasFunction(pageIndex) || pageIndex + 1; - }; - - /* - Returns the first page index found for a given aliased number or false if not found. - This may cause issues if a specific alias is found for multiple page indices; use getPageIndicesForAlias and reimplement functions as necessary if this is the case. - */ - this.getPageIndexForAlias = function(aliasedNumber) - { - for(var idx = 0; idx < settings.numPages; idx++) - { - if(this.getAliasForPageIndex(idx) === aliasedNumber) - { - return idx; - } - } - return false; - }; - - //Returns array of page indices for a given aliased number. Returns an empty array if none are found. - this.getPageIndicesForAlias = function(aliasedNumber) - { - var indexArr = []; - for(var idx = 0; idx < settings.numPages; idx++) - { - if(this.getAliasForPageIndex(idx) === aliasedNumber) - { - indexArr.push(idx); - } - } - return indexArr; - }; - - - //Maps the current page index to getAliasForPageIndex - this.getCurrentAliasedPageIndex = function() - { - return this.getAliasForPageIndex(settings.currentPageIndex); - }; - - //Wrapper for gotoPageByIndex, keeping the aliased numbers in mind - this.gotoPageByAliasedNumber = function(aliasedNumber, xAnchor, yAnchor) - { - return this.gotoPageByIndex(this.getPageIndexForAlias(aliasedNumber), xAnchor, yAnchor); - }; - - // Call the init function when this object is created. - init(); - }; - - $.fn.diva = function (options) - { - return this.each(function () - { - var divaParent = $(this); - - // Return early if this element already has a plugin instance - if (divaParent.data('diva')) - return; - - // Throw an error if the element is not in the DOM, since it causes some problems - if (!document.body.contains(this)) - throw new Error('Diva could not be initialized because this element is not attached to the DOM'); - - // Otherwise, instantiate the document viewer - var diva = new Diva(this, options); - divaParent.data('diva', diva); - }); - }; - })(jQuery); - - -/***/ }), -/* 8 */ -/***/ (function(module, exports) { - - (function () { - if (Array.prototype.fill) return; - - var fill = function (value) { - // Steps 1-2. - if (this == null) { - throw new TypeError("this is null or not defined"); - } - - var O = Object(this); - - // Steps 3-5. - var len = O.length >>> 0; - - // Steps 6-7. - var start = arguments[1]; - var relativeStart = start >> 0; - - // Step 8. - var k = relativeStart < 0 ? - Math.max(len + relativeStart, 0) : - Math.min(relativeStart, len); - - // Steps 9-10. - var end = arguments[2]; - var relativeEnd = end === undefined ? - len : end >> 0; - - // Step 11. - var last = relativeEnd < 0 ? - Math.max(len + relativeEnd, 0) : - Math.min(relativeEnd, len); - - // Step 12. - while (k < last) { - O[k] = value; - k++; - } - - // Step 13. - return O; - }; - - if (Object.defineProperty) { - try { - Object.defineProperty(Array.prototype, 'fill', { - value: fill, - configurable: true, - enumerable: false, - writable: true - }); - } catch(e) {} - } - - if (!Array.prototype.fill) { - Array.prototype.fill = fill; - } - })(this); - - -/***/ }), -/* 9 */ -/***/ (function(module, exports) { - - module.exports = elt; - module.exports.setAttributes = setDOMAttributes; - - /** - * Convenience function to create a DOM element, set attributes on it, and - * append children. All arguments which are not of primitive type, are not - * arrays, and are not DOM nodes are treated as attribute hashes and are - * handled as described for setDOMAttributes. Children can either be a DOM - * node or a primitive value, which is converted to a text node. Arrays are - * handled recursively. Null and undefined values are ignored. - * - * Inspired by the ProseMirror helper of the same name. - */ - function elt(tag) - { - var el = document.createElement(tag); - var args = Array.prototype.slice.call(arguments, 1); - - while (args.length) - { - var arg = args.shift(); - handleEltConstructorArg(el, arg); - } - - return el; - } - - function handleEltConstructorArg(el, arg) - { - if (arg == null) - return; - - if (typeof arg !== 'object' && typeof arg !== 'function') - { - // Coerce to string - el.appendChild(document.createTextNode(arg)); - } - else if (arg instanceof window.Node) - { - el.appendChild(arg); - } - else if (arg instanceof Array) - { - var childCount = arg.length; - for (var i = 0; i < childCount; i++) - handleEltConstructorArg(el, arg[i]); - } - else - { - setDOMAttributes(el, arg); - } - } - - /** - * Set attributes of a DOM element. The `style` property is special-cased to - * accept either a string or an object whose own attributes are assigned to - * el.style. - */ - function setDOMAttributes(el, attributes) - { - for (var prop in attributes) - { - if (!attributes.hasOwnProperty(prop)) - continue; - - if (prop === 'style') - { - setStyle(el, attributes.style); - } - else - { - el.setAttribute(prop, attributes[prop]); - } - } - } - - function setStyle(el, style) - { - if (!style) - return; - - if (typeof style !== 'object') - { - el.style.cssText = style; - return; - } - - for (var cssProp in style) - { - if (!style.hasOwnProperty(cssProp)) - continue; - - el.style[cssProp] = style[cssProp]; - } - } - - -/***/ }), -/* 10 */ -/***/ (function(module, exports) { - - module.exports.get = getHashParam; - module.exports.update = updateHashParam; - - // For getting the #key values from the URL. For specifying a page and zoom level - // Look into caching, because we only need to get this during the initial load - // Although for the tests I guess we would need to override caching somehow - function getHashParam(key) { - var hash = window.location.hash; - if (hash !== '') { - // Check if there is something that looks like either &key= or #key= - var startIndex = (hash.indexOf('&' + key + '=') > 0) ? hash.indexOf('&' + key + '=') : hash.indexOf('#' + key + '='); - - // If startIndex is still -1, it means it can't find either - if (startIndex >= 0) { - // Add the length of the key plus the & and = - startIndex += key.length + 2; - - // Either to the next ampersand or to the end of the string - var endIndex = hash.indexOf('&', startIndex); - if (endIndex > startIndex) { - return decodeURIComponent(hash.substring(startIndex, endIndex)); - } else if (endIndex < 0) { - // This means this hash param is the last one - return decodeURIComponent(hash.substring(startIndex)); - } - // If the key doesn't have a value I think - return ''; - } else { - // If it can't find the key - return false; - } - } else { - // If there are no hash params just return false - return false; - } - } - - function updateHashParam(key, value) { - // First make sure that we have to do any work at all - var originalValue = getHashParam(key); - var hash = window.location.hash; - if (originalValue !== value) { - // Is the key already in the URL? - if (typeof originalValue == 'string') { - // Already in the URL. Just get rid of the original value - var startIndex = (hash.indexOf('&' + key + '=') > 0) ? hash.indexOf('&' + key + '=') : hash.indexOf('#' + key + '='); - var endIndex = startIndex + key.length + 2 + originalValue.length; - // # if it's the first, & otherwise - var startThing = (startIndex === 0) ? '#' : '&'; - window.location.replace(hash.substring(0, startIndex) + startThing + key + '=' + value + hash.substring(endIndex)); - } else { - // It's not present - add it - if (hash.length === 0) { - window.location.replace('#' + key + '=' + value); - } else { - // Append it - window.location.replace(hash + '&' + key + '=' + value); - } - } - } - } - - -/***/ }), -/* 11 */ -/***/ (function(module, exports, __webpack_require__) { - - var jQuery = __webpack_require__(3); - - //Used to keep track of whether Diva was last clicked or which Diva was last clicked when there are multiple - var ActiveDivaController = (function ($) - { - return function () - { - var active; - - //global click listener - $(document).on('click', function(e) - { - updateActive($(e.target)); - }); - - //parameter should already be a jQuery selector - var updateActive = function (target) - { - var nearestOuter; - - //these will find 0 or 1 objects, never more - var findOuter = target.find('.diva-outer'); - var closestOuter = target.closest('.diva-outer'); - var outers = document.getElementsByClassName('diva-outer'); - var outerLen = outers.length; - var idx; - - //clicked on something that was not either a parent or sibling of a diva-outer - if (findOuter.length > 0) - { - nearestOuter = findOuter; - } - //clicked on something that was a child of a diva-outer - else if (closestOuter.length > 0) - { - nearestOuter = closestOuter; - } - //clicked on something that was not in any Diva tree - else - { - //deactivate everything and return - for (idx = 0; idx < outerLen; idx++) - { - $(outers[idx].parentElement.parentElement).data('diva').deactivate(); - } - return; - } - - //if we found one, activate it... - nearestOuter.parent().parent().data('diva').activate(); - active = nearestOuter.parent(); - - //...and deactivate all the others - outers = document.getElementsByClassName('diva-outer'); - for(idx = 0; idx < outerLen; idx++) - { - //getAttribute to attr - comparing DOM element to jQuery element - if (outers[idx].getAttribute('id') != nearestOuter.attr('id')) - $(outers[idx].parentElement.parentElement).data('diva').deactivate(); - } - }; - - //public accessor in case. Will return a jQuery selector. - this.getActive = function() - { - return active; - }; - }; - })(jQuery); - - module.exports = ActiveDivaController; - - -/***/ }), -/* 12 */ -/***/ (function(module, exports, __webpack_require__) { - - /* jshint unused: true */ - - var parseIIIFManifest = __webpack_require__(13); - - module.exports = ImageManifest; - - function ImageManifest(data, urlAdapter) - { - // Save all the data we need - this.pages = data.pgs; - this.maxZoom = data.max_zoom; - this.maxRatio = data.dims.max_ratio; - this.minRatio = data.dims.min_ratio; - this.itemTitle = data.item_title; - - // Only given for IIIF manifests - this.paged = !!data.paged; - - // These are arrays, the index corresponding to the zoom level - this._maxWidths = data.dims.max_w; - this._maxHeights = data.dims.max_h; - this._averageWidths = data.dims.a_wid; - this._averageHeights = data.dims.a_hei; - this._totalHeights = data.dims.t_hei; - this._totalWidths = data.dims.t_wid; - - this._urlAdapter = urlAdapter; - } - - ImageManifest.fromIIIF = function (iiifManifest) - { - var data = parseIIIFManifest(iiifManifest); - return new ImageManifest(data, new IIIFSourceAdapter()); - }; - - ImageManifest.fromLegacyManifest = function (data, config) - { - // For IIP manifests, use the page number (indexed starting from 1) as a label for each page - for (var i = 0, len = data.pgs.length; i < len; i++) - data.pgs[i].l = (i + 1).toString(); - - return new ImageManifest(data, new LegacyManifestSourceAdapter(config)); - }; - - ImageManifest.prototype.isPageValid = function (pageIndex, showNonPagedPages) - { - if (!showNonPagedPages && this.paged && !this.pages[pageIndex].paged) - return false; - - return pageIndex >= 0 && pageIndex < this.pages.length; - }; - - ImageManifest.prototype.getMaxPageDimensions = function (pageIndex) - { - var maxDims = this.pages[pageIndex].d[this.maxZoom]; - - return { - height: maxDims.h, - width: maxDims.w - }; - }; - - ImageManifest.prototype.getPageDimensionsAtZoomLevel = function (pageIndex, zoomLevel) - { - var maxDims = this.pages[pageIndex].d[this.maxZoom]; - - var scaleRatio = getScaleRatio(this.maxZoom, zoomLevel); - - return { - height: maxDims.h * scaleRatio, - width: maxDims.w * scaleRatio - }; - }; - - /** - * Returns a URL for the image of the given page. The optional size - * parameter supports setting the image width or height (default is - * full-sized). - */ - ImageManifest.prototype.getPageImageURL = function (pageIndex, size) - { - return this._urlAdapter.getPageImageURL(this, pageIndex, size); - }; - - /** - * Return an array of tile objects for the specified page and integer zoom level - */ - ImageManifest.prototype.getPageImageTiles = function (pageIndex, zoomLevel, tileDimensions) - { - var page = this.pages[pageIndex]; - - if (!isFinite(zoomLevel) || zoomLevel % 1 !== 0) - throw new TypeError('Zoom level must be an integer: ' + zoomLevel); - - var rows = Math.ceil(page.d[zoomLevel].h / tileDimensions.height); - var cols = Math.ceil(page.d[zoomLevel].w / tileDimensions.width); - - var tiles = []; - - var row, col, url; - - for (row = 0; row < rows; row++) - { - for (col = 0; col < cols; col++) - { - url = this._urlAdapter.getTileImageURL(this, pageIndex, { - row: row, - col: col, - rowCount: rows, - colCount: cols, - zoomLevel: zoomLevel, - tileDimensions: tileDimensions - }); - - // FIXME: Dimensions should account for partial tiles (e.g. the - // last row and column in a tiled image) - tiles.push({ - row: row, - col: col, - zoomLevel: zoomLevel, - dimensions: { - height: tileDimensions.height, - width: tileDimensions.width - }, - offset: { - top: row * tileDimensions.height, - left: col * tileDimensions.width - }, - url: url - }); - } - } - - return { - zoomLevel: zoomLevel, - rows: rows, - cols: cols, - tiles: tiles - }; - }; - - ImageManifest.prototype.getMaxWidth = zoomedPropertyGetter('_maxWidths'); - ImageManifest.prototype.getMaxHeight = zoomedPropertyGetter('_maxHeights'); - ImageManifest.prototype.getAverageWidth = zoomedPropertyGetter('_averageWidths'); - ImageManifest.prototype.getAverageHeight = zoomedPropertyGetter('_averageHeights'); - ImageManifest.prototype.getTotalWidth = zoomedPropertyGetter('_totalWidths'); - ImageManifest.prototype.getTotalHeight = zoomedPropertyGetter('_totalHeights'); - - function zoomedPropertyGetter(privateName) - { - return function (zoomLevel) - { - return this[privateName][zoomLevel]; - }; - } - - function getScaleRatio(sourceZoomLevel, targetZoomLevel) - { - return 1 / Math.pow(2, sourceZoomLevel - targetZoomLevel); - } - - function IIIFSourceAdapter() - { - // No-op - } - - IIIFSourceAdapter.prototype.getPageImageURL = function (manifest, pageIndex, size) - { - var dimens; - - if (!size || (size.width == null && size.height == null)) - dimens = 'full'; - else - dimens = (size.width == null ? '' : size.width) + ',' + (size.height == null ? '' : size.height); - - var page = manifest.pages[pageIndex]; - var quality = (page.api > 1.1) ? 'default' : 'native'; - - return encodeURI(page.url + 'full/' + dimens + '/0/' + quality + '.jpg'); - }; - - IIIFSourceAdapter.prototype.getTileImageURL = function (manifest, pageIndex, params) - { - var page = manifest.pages[pageIndex]; - - var height, width; - - if (params.row === params.rowCount - 1) - height = page.d[params.zoomLevel].h - (params.rowCount - 1) * params.tileDimensions.height; - else - height = params.tileDimensions.height; - - if (params.col === params.colCount - 1) - width = page.d[params.zoomLevel].w - (params.colCount - 1) * params.tileDimensions.width; - else - width = params.tileDimensions.width; - - var zoomDifference = Math.pow(2, manifest.maxZoom - params.zoomLevel); - - var x = params.col * params.tileDimensions.width * zoomDifference; - var y = params.row * params.tileDimensions.height * zoomDifference; - - if (page.hasOwnProperty('xoffset')) - { - x += page.xoffset; - y += page.yoffset; - } - - var region = [x, y, width * zoomDifference, height * zoomDifference].join(','); - - var quality = (page.api > 1.1) ? 'default' : 'native'; - - return encodeURI(page.url + region + '/' + width + ',' + height + '/0/' + quality + '.jpg'); - }; - - function LegacyManifestSourceAdapter(config) - { - this._config = config; - } - - LegacyManifestSourceAdapter.prototype.getPageImageURL = function (manifest, pageIndex, size) - { - // Without width or height specified, IIPImage defaults to full-size - var dimens = ''; - - if (size) - { - if (size.width != null) - dimens += '&WID=' + size.width; - - if (size.height != null) - dimens += '&HEI=' + size.height; - } - - var filename = manifest.pages[pageIndex].f; - - return this._config.iipServerURL + "?FIF=" + this._config.imageDir + '/' + filename + dimens + '&CVT=JPEG'; - }; - - LegacyManifestSourceAdapter.prototype.getTileImageURL = function (manifest, pageIndex, params) - { - var page = manifest.pages[pageIndex]; - var requestedZoomLevel = params.zoomLevel + page.m - manifest.maxZoom; - var index = (params.row * params.colCount) + params.col; - var jtl = requestedZoomLevel + ',' + index; - - return encodeURI(this._config.iipServerURL + "?FIF=" + this._config.imageDir + '/' + page.f + '&JTL=' + jtl + '&CVT=JPEG'); - }; - - -/***/ }), -/* 13 */ -/***/ (function(module, exports) { - - /* jshint unused: true */ - - module.exports = parseIIIFManifest; - - var getMaxZoomLevel = function (width, height) - { - var largestDimension = Math.max(width, height); - return Math.ceil(Math.log((largestDimension + 1) / (256 + 1)) / Math.log(2)); - }; - - var incorporateZoom = function (imageDimension, zoomDifference) - { - return imageDimension / (Math.pow(2, zoomDifference)); - }; - - var getOtherImageData = function(otherImages, lowestMaxZoom, canvasWidth, canvasHeight) - { - return otherImages.map( - function (itm) - { - var w = itm.width || canvasWidth; - var h = itm.height || canvasHeight; - - var dims = new Array(lowestMaxZoom + 1); - for (var j = 0; j < lowestMaxZoom + 1; j++) - { - dims[j] = { - h: Math.floor(incorporateZoom(h, lowestMaxZoom - j)), - w: Math.floor(incorporateZoom(w, lowestMaxZoom - j)) - }; - } - return { - label: itm.label || "", - dims: dims - }; - } - ); - }; - - /** - * Parses a IIIF Presentation API Manifest and converts it into a Diva.js-format object - * (See https://github.com/DDMAL/diva.js/wiki/Development-notes#data-received-through-ajax-request) - * (This is a client-side re-implementation of generate_json.py) - * - * @param {Object} manifest - an object that represents a valid IIIF manifest - * @returns {Object} divaServiceBlock - the data needed by Diva to show a view of a single document - */ - function parseIIIFManifest(manifest) - { - var sequence = manifest.sequences[0]; - var canvases = sequence.canvases; - var numCanvases = canvases.length; - - var pages = new Array(canvases.length); - - var thisCanvas, thisResource, thisImage, otherImages, context, url, info, imageAPIVersion, - width, height, maxZoom, canvas, label, imageLabel, zoomDimensions, widthAtCurrentZoomLevel, - heightAtCurrentZoomLevel; - - var lowestMaxZoom = 100; - var maxRatio = 0; - var minRatio = 100; - - // quickly determine the lowest possible max zoom level (i.e., the upper bound for images) across all canvases. - // while we're here, compute the global ratios as well. - for (var z = 0; z < numCanvases; z++) - { - var c = canvases[z]; - var w = c.width; - var h = c.height; - var mz = getMaxZoomLevel(w, h); - var ratio = h / w; - maxRatio = Math.max(ratio, maxRatio); - minRatio = Math.min(ratio, minRatio); - - lowestMaxZoom = Math.min(lowestMaxZoom, mz); - } - - // Uint8Arrays are pre-initialized with zeroes. These ones need to be - // pre-initialized since we will do arithmetic and value checking on them - var totalWidths = new Array(lowestMaxZoom + 1).fill(0); - var totalHeights = new Array(lowestMaxZoom + 1).fill(0); - var maxWidths = new Array(lowestMaxZoom + 1).fill(0); - var maxHeights = new Array(lowestMaxZoom + 1).fill(0); - - for (var i = 0; i < numCanvases; i++) - { - thisCanvas = canvases[i]; - canvas = thisCanvas['@id']; - label = thisCanvas.label; - thisResource = thisCanvas.images[0].resource; - - /* - * If a canvas has multiple images it will be encoded - * with a resource type of "oa:Choice". The primary image will be available - * on the 'default' key, with other images available under 'item.' - * */ - if (thisResource['@type'] === "oa:Choice") - { - thisImage = thisResource.default; - } - else - { - thisImage = thisResource; - } - - // Prioritize the canvas height / width first, since images may not have h/w - width = thisCanvas.width || thisImage.width; - height = thisCanvas.height || thisImage.height; - if (width <= 0 || height <= 0) - { - console.warn('Invalid width or height for canvas ' + label + '. Skipping'); - continue; - } - - maxZoom = getMaxZoomLevel(width, height); - - if (thisResource.item) - { - otherImages = getOtherImageData(thisResource.item, lowestMaxZoom, width, height); - } - - imageLabel = thisImage.label || null; - - info = parseImageInfo(thisImage); - url = info.url.slice(-1) !== '/' ? info.url + '/' : info.url; // append trailing slash to url if it's not there. - - context = thisImage.service['@context']; - - if (context === 'http://iiif.io/api/image/2/context.json') - { - imageAPIVersion = 2; - } - else if (context === 'http://library.stanford.edu/iiif/image-api/1.1/context.json') - { - imageAPIVersion = 1.1; - } - else - { - imageAPIVersion = 1.0; - } - - zoomDimensions = new Array(lowestMaxZoom + 1); - - for (var k = 0; k < lowestMaxZoom + 1; k++) - { - widthAtCurrentZoomLevel = Math.floor(incorporateZoom(width, lowestMaxZoom - k)); - heightAtCurrentZoomLevel = Math.floor(incorporateZoom(height, lowestMaxZoom - k)); - zoomDimensions[k] = { - h: heightAtCurrentZoomLevel, - w: widthAtCurrentZoomLevel - }; - - totalWidths[k] += widthAtCurrentZoomLevel; - totalHeights[k] += heightAtCurrentZoomLevel; - maxWidths[k] = Math.max(widthAtCurrentZoomLevel, maxWidths[k]); - maxHeights[k] = Math.max(heightAtCurrentZoomLevel, maxHeights[k]); - } - - pages[i] = { - d: zoomDimensions, - m: maxZoom, - l: label, // canvas label ('page 1, page 2', etc.) - il: imageLabel, // default image label ('primary image', 'UV light', etc.) - f: info.url, - url: url, - api: imageAPIVersion, - paged: thisCanvas.viewingHint !== 'non-paged', - facingPages: thisCanvas.viewingHint === 'facing-pages', - canvas: canvas, - otherImages: otherImages, - xoffset: info.x || null, - yoffset: info.y || null - }; - } - - var averageWidths = new Array(lowestMaxZoom + 1); - var averageHeights = new Array(lowestMaxZoom + 1); - - for (var a = 0; a < lowestMaxZoom + 1; a++) - { - averageWidths[a] = totalWidths[a] / numCanvases; - averageHeights[a] = totalHeights[a] / numCanvases; - } - - var dims = { - a_wid: averageWidths, - a_hei: averageHeights, - max_w: maxWidths, - max_h: maxHeights, - max_ratio: maxRatio, - min_ratio: minRatio, - t_hei: totalHeights, - t_wid: totalWidths - }; - - return { - item_title: manifest.label, - dims: dims, - max_zoom: lowestMaxZoom, - pgs: pages, - paged: manifest.viewingHint === 'paged' || sequence.viewingHint === 'paged' - }; - } - - /** - * Takes in a resource block from a canvas and outputs the following information associated with that resource: - * - Image URL - * - Image region to be displayed - * - * @param {Object} resource - an object representing the resource block of a canvas section in a IIIF manifest - * @returns {Object} imageInfo - an object containing image URL and region - */ - function parseImageInfo(resource) - { - var url = resource['@id']; - var fragmentRegex = /#xywh=([0-9]+,[0-9]+,[0-9]+,[0-9]+)/; - var xywh = ''; - var stripURL = true; - - if (/\/([0-9]+,[0-9]+,[0-9]+,[0-9]+)\//.test(url)) - { - // if resource in image API format, extract region x,y,w,h from URL (after 4th slash from last) - // matches coordinates in URLs of the form http://www.example.org/iiif/book1-page1/40,50,1200,1800/full/0/default.jpg - var urlArray = url.split('/'); - xywh = urlArray[urlArray.length - 4]; - } - else if (fragmentRegex.test(url)) - { - // matches coordinates of the style http://www.example.org/iiif/book1/canvas/p1#xywh=50,50,320,240 - var result = fragmentRegex.exec(url); - xywh = result[1]; - } - else if (resource.service && resource.service['@id']) - { - // assume canvas size based on image size - url = resource.service['@id']; - // this URL excludes region parameters so we don't need to remove them - stripURL = false; - } - - if (stripURL) - { - // extract URL up to identifier (we eliminate the last 5 parameters: /region/size/rotation/quality.format) - url = url.split('/').slice(0, -4).join('/'); - } - - var imageInfo = { - url: url - }; - - if (xywh.length) - { - // parse into separate components - var dimensions = xywh.split(','); - imageInfo.x = parseInt(dimensions[0], 10); - imageInfo.y = parseInt(dimensions[1], 10); - imageInfo.w = parseInt(dimensions[2], 10); - imageInfo.h = parseInt(dimensions[3], 10); - } - - return imageInfo; - } - - -/***/ }), -/* 14 */ -/***/ (function(module, exports, __webpack_require__) { - - var $ = __webpack_require__(3); - - var diva = __webpack_require__(2); - var elt = __webpack_require__(9); - - module.exports = createToolbar; - - function createToolbar(viewer) - { - var settings = viewer.getSettings(); - - // FIXME(wabain): Temporarily copied from within Diva - var elemAttrs = function (ident, base) - { - var attrs = { - id: settings.ID + ident, - class: 'diva-' + ident - }; - - if (base) - return $.extend(attrs, base); - else - return attrs; - }; - - /** Convenience function to subscribe to a Diva event */ - var subscribe = function (event, callback) - { - diva.Events.subscribe(event, callback, settings.ID); - }; - - // Creates a toolbar button - var createButtonElement = function(name, label, callback) - { - var button = elt('button', { - type: 'button', - id: settings.ID + name, - class: 'diva-' + name + ' diva-button', - title: label - }); - - if (callback) - button.addEventListener('click', callback, false); - - return button; - }; - - // Higher-level function for creators of zoom and grid controls - var getResolutionControlCreator = function (config) - { - return function () - { - var controls; - - switch (settings[config.controllerSetting]) - { - case 'slider': - controls = config.createSlider(); - break; - - case 'buttons': - controls = config.createButtons(); - break; - - default: - // Don't display anything - return null; - } - - var wrapper = elt('span', - controls, - config.createLabel() - ); - - var updateWrapper = function () - { - if (settings.inGrid === config.showInGrid) - wrapper.style.display = 'inline'; - else - wrapper.style.display = 'none'; - }; - - subscribe('ViewDidSwitch', updateWrapper); - subscribe('ObjectDidLoad', updateWrapper); - - // Set initial value - updateWrapper(); - - return wrapper; - }; - }; - - // Zoom controls - var createZoomControls = getResolutionControlCreator({ - controllerSetting: 'enableZoomControls', - showInGrid: false, - - createSlider: function () - { - var elem = createSlider('zoom-slider', { - step: 0.1, - value: settings.zoomLevel, - min: settings.minZoomLevel, - max: settings.maxZoomLevel - }); - var $elem = $(elem); - - $elem.on('input', function() - { - var floatValue = parseFloat(this.value); - viewer.setZoomLevel(floatValue); - }); - - $elem.on('change', function () - { - var floatValue = parseFloat(this.value); - if (floatValue !== settings.zoomLevel) - viewer.setZoomLevel(floatValue); - }); - - var updateSlider = function () - { - if (settings.zoomLevel !== $elem.val()) - $elem.val(settings.zoomLevel); - }; - - subscribe('ZoomLevelDidChange', updateSlider); - subscribe('ViewerDidLoad', function () - { - elt.setAttributes(elem, { - min: settings.minZoomLevel, - max: settings.maxZoomLevel - }); - - updateSlider(); - }); - - return elem; - }, - - createButtons: function () - { - return elt('span', - createButtonElement('zoom-out-button', 'Zoom Out', function () - { - viewer.setZoomLevel(settings.zoomLevel - 1); - }), - createButtonElement('zoom-in-button', 'Zoom In', function () - { - viewer.setZoomLevel(settings.zoomLevel + 1); - }) - ); - }, - - createLabel: function () - { - var elem = createLabel('diva-zoom-label', 'zoom-label', 'Zoom level: ', 'zoom-level', settings.zoomLevel); - var textSpan = $(elem).find(settings.selector + 'zoom-level')[0]; - - var updateText = function () - { - textSpan.textContent = settings.zoomLevel.toFixed(2); - }; - - subscribe('ZoomLevelDidChange', updateText); - subscribe('ViewerDidLoad', updateText); - - return elem; - } - }); - - // Grid controls - var createGridControls = getResolutionControlCreator({ - controllerSetting: 'enableGridControls', - showInGrid: true, - - createSlider: function () - { - var elem = createSlider('grid-slider', { - value: settings.pagesPerRow, - min: settings.minPagesPerRow, - max: settings.maxPagesPerRow - }); - var $elem = $(elem); - - $elem.on('input', function() - { - var intValue = parseInt(elem.value, 10); - viewer.setGridPagesPerRow(intValue); - }); - - $elem.on('change', function () - { - var intValue = parseInt(elem.value, 10); - if (intValue !== settings.pagesPerRow) - viewer.setGridPagesPerRow(intValue); - }); - - subscribe('GridRowNumberDidChange', function () - { - // Update the position of the handle within the slider - if (settings.pagesPerRow !== $elem.val()) - $elem.val(settings.pagesPerRow); - }); - - return elem; - }, - - createButtons: function () - { - return elt('span', - createButtonElement('grid-out-button', 'Zoom Out', function () - { - viewer.setGridPagesPerRow(settings.pagesPerRow - 1); - }), - createButtonElement('grid-in-button', 'Zoom In', function () - { - viewer.setGridPagesPerRow(settings.pagesPerRow + 1); - }) - ); - }, - - createLabel: function () - { - var elem = createLabel('diva-grid-label', 'grid-label', 'Pages per row: ', 'pages-per-row', settings.pagesPerRow); - var textSpan = $(elem).find(settings.selector + 'pages-per-row')[0]; - - subscribe('GridRowNumberDidChange', function () - { - textSpan.textContent = settings.pagesPerRow; - }); - - return elem; - } - }); - - var createViewMenu = function() - { - var viewOptionsList = elt('div', elemAttrs('view-options')); - - var changeViewButton = createButtonElement('view-icon', 'Change view', function () - { - $(viewOptionsList).toggle(); - }); - - $(document).mouseup(function (event) - { - var container = $(viewOptionsList); - - if (!container.is(event.target) && container.has(event.target).length === 0 && event.target.id !== settings.ID + 'view-icon') - { - container.hide(); - } - }); - - var selectView = function (view) - { - viewer.changeView(view); - - //hide view menu - $(viewOptionsList).hide(); - }; - - var updateViewMenu = function() - { - var viewIconClasses = ' diva-view-icon diva-button'; - - // display the icon of the mode we're currently in (?) - if (settings.inGrid) - { - changeViewButton.className = 'diva-grid-icon' + viewIconClasses; - } - else if (settings.inBookLayout) - { - changeViewButton.className = 'diva-book-icon' + viewIconClasses; - } - else - { - changeViewButton.className = 'diva-document-icon' + viewIconClasses; - } - - var viewOptions = document.createDocumentFragment(); - - // then display document, book, and grid buttons in that order, excluding the current view - if (settings.inGrid || settings.inBookLayout) - viewOptions.appendChild(createButtonElement('document-icon', 'Document View', selectView.bind(null, 'document'))); - - if (settings.inGrid || !settings.inBookLayout) - viewOptions.appendChild(createButtonElement('book-icon', 'Book View', selectView.bind(null, 'book'))); - - if (!settings.inGrid) - viewOptions.appendChild(createButtonElement('grid-icon', 'Grid View', selectView.bind(null, 'grid'))); - - // remove old menu - while (viewOptionsList.firstChild) - { - viewOptionsList.removeChild(viewOptionsList.firstChild); - } - - // insert new menu - viewOptionsList.appendChild(viewOptions); - }; - - subscribe('ViewDidSwitch', updateViewMenu); - subscribe('ObjectDidLoad', updateViewMenu); - - return elt('div', elemAttrs('view-menu'), - changeViewButton, - viewOptionsList - ); - }; - - var createSlider = function(name, options) - { - return elt('input', options, { - id: settings.ID + name, - class: 'diva-' + name + ' diva-slider', - type: 'range' - }); - }; - - var createLabel = function(name, id, label, innerName, innerValue) - { - return elt('div', { - id: settings.ID + id, - class: name + ' diva-label' - }, - [ - label, - elt('span', { - id: settings.ID + innerName - }, innerValue) - ]); - }; - - var createPageNavigationControls = function () - { - // Go to page form - var gotoForm = settings.enableGotoPage ? createGotoPageForm() : null; - - return elt('span', elemAttrs('page-nav'), - createPageLabel(), // 'Page x of y' label - gotoForm - ); - }; - - var createGotoPageForm = function () - { - var gotoPageInput = elt('input', { - id: settings.ID + 'goto-page-input', - class: 'diva-input diva-goto-page-input', - autocomplete: 'off', - type: 'text' - }); - - var gotoPageSubmit = elt('input', { - id: settings.ID + 'goto-page-submit', - class: 'diva-button diva-button-text', - type: 'submit', - value: 'Go' - }); - - var inputSuggestions = elt('div', { - id: settings.ID + 'input-suggestions', - class: 'diva-input-suggestions' - } - ); - - var gotoForm = elt('form', { - id: settings.ID + 'goto-page', - class: 'diva-goto-form' - }, - gotoPageInput, - gotoPageSubmit, - inputSuggestions - ); - - $(gotoForm).on('submit', function () - { - var desiredPageLabel = gotoPageInput.value; - - if (settings.onGotoSubmit && typeof settings.onGotoSubmit === "function") - { - var pageIndex = settings.onGotoSubmit(desiredPageLabel); - if (!viewer.gotoPageByIndex(pageIndex)) - alert("No page could be found with that label or page number"); - - } - else // Default if no function is specified in the settings - { - if (!viewer.gotoPageByLabel(desiredPageLabel)) - alert("No page could be found with that label or page number"); - } - - // Hide the suggestions - inputSuggestions.style.display = 'none'; - - // Prevent the default action of reloading the page - return false; - }); - - $(gotoPageInput).on('input focus', function () - { - inputSuggestions.innerHTML = ''; // Remove all previous suggestions - - var value = gotoPageInput.value; - var numSuggestions = 0; - if (settings.enableGotoSuggestions && value) - { - var pages = settings.manifest.pages; - for (var i = 0, len = pages.length; i < len && numSuggestions < 10; i++) - { - if (pages[i].l.toLowerCase().indexOf(value.toLowerCase()) > -1) - { - var newInputSuggestion = elt('div', { - class: 'diva-input-suggestion' - }, - pages[i].l - ); - - inputSuggestions.appendChild(newInputSuggestion); - - numSuggestions++; - } - } - - // Show label suggestions - if (numSuggestions > 0) - inputSuggestions.style.display = 'block'; - } - else - inputSuggestions.style.display = 'none'; - }); - - $(gotoPageInput).on('keydown', function (e) - { - var el; - if (e.keyCode === 13) // 'Enter' key - { - var active = $('.active', inputSuggestions); - if (active.length) - gotoPageInput.value = active.text(); - - } - if (e.keyCode === 38) // Up arrow key - { - el = $('.active', inputSuggestions); - var prevEl = el.prev(); - if (prevEl.length) - { - el.removeClass('active'); - prevEl.addClass('active'); - } - else - { - el.removeClass('active'); - $('.diva-input-suggestion:last', inputSuggestions).addClass('active'); - } - } - else if (e.keyCode === 40) // Down arrow key - { - el = $('.active', inputSuggestions); - var nextEl = el.next(); - if (nextEl.length) - { - el.removeClass('active'); - nextEl.addClass('active'); - } - else - { - el.removeClass('active'); - $('.diva-input-suggestion:first', inputSuggestions).addClass('active'); - } - } - }); - - $(inputSuggestions).on('mousedown', '.diva-input-suggestion', function() - { - gotoPageInput.value = this.textContent; - inputSuggestions.style.display = 'none'; - $(gotoPageInput).trigger('submit'); - }); - - $(gotoPageInput).on('blur', function () - { - // Hide label suggestions - inputSuggestions.style.display = 'none'; - }); - - return gotoForm; - }; - - var createPageLabel = function() - { - // Current page - var currentPage = elt('span', { - id: settings.ID + 'current-page' - }); - - var updateCurrentPage = function () - { - currentPage.textContent = viewer.getCurrentAliasedPageIndex(); - }; - - subscribe('VisiblePageDidChange', updateCurrentPage); - subscribe('ViewerDidLoad', updateCurrentPage); - - // Number of pages - var numPages = elt('span', { - id: settings.ID + 'num-pages' - }); - - var updateNumPages = function () - { - numPages.textContent = settings.numPages; - }; - - subscribe('NumberOfPagesDidChange', updateNumPages); - subscribe('ObjectDidLoad', updateNumPages); - - return elt('span', { - class: 'diva-page-label diva-label' - }, - 'Page ', currentPage, ' of ', numPages - ); - }; - - var createToolbarButtonGroup = function () - { - var buttons = [createViewMenu()]; - - if (settings.enableLinkIcon) - buttons.push(createLinkIcon()); - - if (settings.enableNonPagedVisibilityIcon) - buttons.push(createToggleNonPagedButton()); - - if (settings.enableFullscreen) - buttons.push(createFullscreenButton()); - - return elt('span', elemAttrs('toolbar-button-group'), buttons); - }; - - var createLinkIcon = function () - { - var elem = createButtonElement('link-icon', 'Link to this page'); - var linkIcon = $(elem); - - linkIcon.on('click', function () - { - $('body').prepend( - elt('div', { - id: settings.ID + 'link-popup', - class: 'diva-popup diva-link-popup' - }, [ - elt('input', { - id: settings.ID + 'link-popup-input', - class: 'diva-input', - type: 'text', - value: viewer.getCurrentURL() - }) - ]) - ); - - if (settings.inFullscreen) - { - $(settings.selector + 'link-popup').addClass('in-fullscreen'); - } - else - { - // Calculate the left and top offsets - var leftOffset = linkIcon.offset().left - 222 + linkIcon.outerWidth(); - var topOffset = linkIcon.offset().top + linkIcon.outerHeight() - 1; - - $(settings.selector + 'link-popup').css({ - 'top': topOffset + 'px', - 'left': leftOffset + 'px' - }); - } - - // Catch onmouseup events outside of this div - $('body').mouseup(function (event) - { - var targetID = event.target.id; - - if (targetID !== settings.ID + 'link-popup' && targetID !== settings.ID + 'link-popup-input') - $(settings.selector + 'link-popup').remove(); - }); - - // Also delete it upon scroll and page up/down key events - // FIXME(wabain): This is aggressive - settings.viewportObject.scroll(function () - { - $(settings.selector + 'link-popup').remove(); - }); - $(settings.selector + 'link-popup input').click(function () - { - $(this).focus().select(); - }); - - return false; - }); - - return elem; - }; - - var createFullscreenButton = function () - { - return createButtonElement('fullscreen-icon', 'Toggle fullscreen mode', function () - { - viewer.toggleFullscreenMode(); - }); - }; - - var createToggleNonPagedButton = function () - { - var getClassName = function() - { - return 'toggle-nonpaged-icon' + (viewer.getSettings().showNonPagedPages ? '-active' : ''); - }; - - var toggleNonPagedButton = createButtonElement(getClassName(), 'Toggle visibility of non-paged pages', function() - { - viewer.toggleNonPagedPagesVisibility(); - var newClassName = 'diva-' + getClassName(); - this.className = this.className.replace(/diva-toggle-nonpaged-icon(-active)?/, newClassName); - }); - - var updateNonPagedButtonVisibility = function () - { - var pages = settings.manifest.pages; - for (var i = 0; i < pages.length; i++) - { - if (settings.manifest.paged && !pages[i].paged) - { - // Show the button, there is at least one non-paged page - toggleNonPagedButton.style.display = 'inline-block'; - return; - } - } - - // No non-paged pages were found, hide the button - toggleNonPagedButton.style.display = 'none'; - }; - subscribe('ObjectDidLoad', updateNonPagedButtonVisibility); - - return toggleNonPagedButton; - }; - - // Handles all status updating etc (both fullscreen and not) - var init = function () - { - var leftTools = [createZoomControls(), createGridControls()]; - var rightTools = [createPageNavigationControls(), createToolbarButtonGroup()]; - - var tools = elt('div', elemAttrs('tools'), - elt('div', elemAttrs('tools-left'), leftTools), - elt('div', elemAttrs('tools-right'), rightTools) - ); - - settings.toolbarParentObject.prepend(tools); - - // Handle entry to and exit from fullscreen mode - var switchMode = function () - { - var toolsRightElement = document.getElementById(settings.ID + 'tools-right'); - var pageNavElement = document.getElementById(settings.ID + 'page-nav'); - - if (!settings.inFullscreen) - { - // Leaving fullscreen - $(tools).removeClass('diva-fullscreen-tools'); - - //move ID-page-nav to beginning of tools right - toolsRightElement.removeChild(pageNavElement); - toolsRightElement.insertBefore(pageNavElement, toolsRightElement.firstChild); - } - else - { - // Entering fullscreen - $(tools).addClass('diva-fullscreen-tools'); - - //move ID-page-nav to end of tools right - toolsRightElement.removeChild(pageNavElement); - toolsRightElement.appendChild(pageNavElement); - } - }; - - subscribe('ModeDidSwitch', switchMode); - subscribe('ViewerDidLoad', switchMode); - - var toolbar = { - element: tools, - closePopups: function () - { - $('.diva-popup').css('display', 'none'); - } - }; - - return toolbar; - }; - - return init(); - } - - -/***/ }), -/* 15 */ -/***/ (function(module, exports, __webpack_require__) { - - var $ = __webpack_require__(3); - - __webpack_require__(16); - - var elt = __webpack_require__(9); - var getScrollbarWidth = __webpack_require__(17); - - var gestureEvents = __webpack_require__(18); - var diva = __webpack_require__(2); - var DocumentHandler = __webpack_require__(19); - var GridHandler = __webpack_require__(23); - var PageOverlayManager = __webpack_require__(24); - var PluginRegistry = __webpack_require__(5); - var Renderer = __webpack_require__(25); - var getPageLayouts = __webpack_require__(35); - var createSettingsView = __webpack_require__(40); - var ValidationRunner = __webpack_require__(41); - var Viewport = __webpack_require__(42); - - var debug = __webpack_require__(26)('diva:ViewerCore'); - - module.exports = ViewerCore; - - // Define validations - var optionsValidations = [ - { - key: 'goDirectlyTo', - validate: function (value, settings) - { - if (value < 0 || value >= settings.manifest.pages.length) - return 0; - } - }, - { - key: 'minPagesPerRow', - validate: function (value) - { - return Math.max(2, value); - } - }, - { - key: 'maxPagesPerRow', - validate: function (value, settings) - { - return Math.max(value, settings.minPagesPerRow); - } - }, - { - key: 'pagesPerRow', - validate: function (value, settings) - { - // Default to the maximum - if (value < settings.minPagesPerRow || value > settings.maxPagesPerRow) - return settings.maxPagesPerRow; - } - }, - { - key: 'maxZoomLevel', - validate: function (value, settings, config) - { - // Changing this value isn't really an error, it just depends on the - // source manifest - config.suppressWarning(); - - if (value < 0 || value > settings.manifest.maxZoom) - return settings.manifest.maxZoom; - } - }, - { - key: 'minZoomLevel', - validate: function (value, settings, config) - { - // Changes based on the manifest value shouldn't trigger a - // warning - if (value > settings.manifest.maxZoom) - { - config.suppressWarning(); - return 0; - } - - if (value < 0 || value > settings.maxZoomLevel) - return 0; - } - }, - { - key: 'zoomLevel', - validate: function (value, settings, config) - { - if (value > settings.manifest.maxZoom) - { - config.suppressWarning(); - return 0; - } - - if (value < settings.minZoomLevel || value > settings.maxZoomLevel) - return settings.minZoomLevel; - } - } - ]; - - function ViewerCore(element, options, publicInstance) - { - var self = this; - var parentObject = $(element); - - // Things that cannot be changed because of the way they are used by the script - // Many of these are declared with arbitrary values that are changed later on - var viewerState = { - currentPageIndex: 0, // The current page in the viewport (center-most page) - horizontalOffset: 0, // Distance from the center of the diva element to the top of the current page - horizontalPadding: 0, // Either the fixed padding or adaptive padding - ID: null, // The prefix of the IDs of the elements (usually 1-diva-) - initialKeyScroll: false, // Holds the initial state of enableKeyScroll - initialSpaceScroll: false, // Holds the initial state of enableSpaceScroll - innerElement: null, // The native .diva-outer DOM object - innerObject: {}, // $(settings.ID + 'inner'), for selecting the .diva-inner element - isActiveDiva: true, // In the case that multiple diva panes exist on the same page, this should have events funneled to it. - isScrollable: true, // Used in enable/disableScrollable public methods - isZooming: false, // Flag to keep track of whether zooming is still in progress, for handleZoom - loaded: false, // A flag for when everything is loaded and ready to go. - manifest: null, - mobileWebkit: false, // Checks if the user is on a touch device (iPad/iPod/iPhone/Android) - numPages: 0, // Number of pages in the array - oldZoomLevel: -1, // Holds the previous zoom level after zooming in or out - options: options, - outerElement: null, // The native .diva-outer DOM object - outerObject: {}, // $(settings.ID + 'outer'), for selecting the .diva-outer element - pageOverlays: new PageOverlayManager(), - pageTools: [], // The plugins which are enabled as page tools - parentObject: parentObject, // JQuery object referencing the parent element - pendingManifestRequest: null, // Reference to the xhr request retrieving the manifest. Used to cancel the request on destroy() - plugins: [], // Filled with the enabled plugins from the registry - renderer: null, - resizeTimer: -1, // Holds the ID of the timeout used when resizing the window (for clearing) - scrollbarWidth: 0, // Set to the actual scrollbar width in init() - selector: '', // Uses the generated ID prefix to easily select elements - throbberTimeoutID: -1, // Holds the ID of the throbber loading timeout - toolbar: null, // Holds an object with some toolbar-related functions - verticalOffset: 0, // Distance from the center of the diva element to the left side of the current page - verticalPadding: 0, // Either the fixed padding or adaptive padding - viewHandler: null, - viewport: null, // Object caching the viewport dimensions - viewportElement: null, - viewportObject: null - }; - - var settings = createSettingsView([options, viewerState]); - - // Aliases for compatibilty - Object.defineProperties(settings, { - // Height of the document viewer pane - panelHeight: { - get: function () - { - return viewerState.viewport.height; - } - }, - // Width of the document viewer pane - panelWidth: { - get: function () - { - return viewerState.viewport.width; - } - } - }); - - var optionsValidator = new ValidationRunner({ - additionalProperties: [ - { - key: 'manifest', - get: function () - { - return viewerState.manifest; - } - } - ], - - validations: optionsValidations - }); - - var isValidOption = function (key, value) - { - return optionsValidator.isValid(key, value, viewerState.options); - }; - - var elemAttrs = function (ident, base) - { - var attrs = { - id: settings.ID + ident, - class: 'diva-' + ident - }; - - if (base) - return $.extend(attrs, base); - else - return attrs; - }; - - var getPageData = function (pageIndex, attribute) - { - return settings.manifest.pages[pageIndex].d[settings.zoomLevel][attribute]; - }; - - // Reset some settings and empty the viewport - var clearViewer = function () - { - viewerState.viewport.top = 0; - - // Clear all the timeouts to prevent undesired pages from loading - clearTimeout(viewerState.resizeTimer); - }; - - /** - * Update settings to match the specified options. Load the viewer, - * fire appropriate events for changed options. - */ - var reloadViewer = function (newOptions) - { - var queuedEvents = []; - - newOptions = optionsValidator.getValidatedOptions(settings, newOptions); - - // Set the zoom level if valid and fire a ZoomLevelDidChange event - if (hasChangedOption(newOptions, 'zoomLevel')) - { - viewerState.oldZoomLevel = settings.zoomLevel; - viewerState.options.zoomLevel = newOptions.zoomLevel; - queuedEvents.push(["ZoomLevelDidChange", newOptions.zoomLevel]); - } - - // Set the pages per row if valid and fire an event - if (hasChangedOption(newOptions, 'pagesPerRow')) - { - viewerState.options.pagesPerRow = newOptions.pagesPerRow; - queuedEvents.push(["GridRowNumberDidChange", newOptions.pagesPerRow]); - } - - // Update verticallyOriented (no event fired) - if (hasChangedOption(newOptions, 'verticallyOriented')) - viewerState.options.verticallyOriented = newOptions.verticallyOriented; - - // Show/Hide non-paged pages - if (hasChangedOption(newOptions, 'showNonPagedPages')) - { - viewerState.options.showNonPagedPages = newOptions.showNonPagedPages; - } - - // Update page position (no event fired here) - if ('goDirectlyTo' in newOptions) - { - viewerState.options.goDirectlyTo = newOptions.goDirectlyTo; - - if ('verticalOffset' in newOptions) - viewerState.verticalOffset = newOptions.verticalOffset; - - if ('horizontalOffset' in newOptions) - viewerState.horizontalOffset = newOptions.horizontalOffset; - } - else - { - // Otherwise the default is to remain on the current page - viewerState.options.goDirectlyTo = settings.currentPageIndex; - } - - if (hasChangedOption(newOptions, 'inGrid') || hasChangedOption(newOptions, 'inBookLayout')) - { - if ('inGrid' in newOptions) - viewerState.options.inGrid = newOptions.inGrid; - - if ('inBookLayout' in newOptions) - viewerState.options.inBookLayout = newOptions.inBookLayout; - - queuedEvents.push(["ViewDidSwitch", settings.inGrid]); - } - - // Note: prepareModeChange() depends on inGrid and the vertical/horizontalOffset (for now) - if (hasChangedOption(newOptions, 'inFullscreen')) - { - viewerState.options.inFullscreen = newOptions.inFullscreen; - prepareModeChange(newOptions); - queuedEvents.push(["ModeDidSwitch", settings.inFullscreen]); - } - - clearViewer(); - updateViewHandlerAndRendering(); - - if (viewerState.renderer) - { - // TODO: The usage of padding variables is still really - // messy and inconsistent - var rendererConfig = { - pageLayouts: getPageLayouts(settings), - padding: getPadding(), - maxZoomLevel: settings.inGrid ? null : viewerState.manifest.maxZoom, - verticallyOriented: settings.verticallyOriented || settings.inGrid, - }; - - var viewportPosition = { - zoomLevel: settings.inGrid ? null : settings.zoomLevel, - anchorPage: settings.goDirectlyTo, - verticalOffset: viewerState.verticalOffset, - horizontalOffset: viewerState.horizontalOffset - }; - - var sourceProvider = getCurrentSourceProvider(); - - if (debug.enabled) - { - var serialized = Object.keys(rendererConfig) - .filter(function (key) - { - // Too long - return key !== 'pageLayouts' && key !== 'padding'; - }) - .map(function (key) - { - var value = rendererConfig[key]; - return key + ': ' + JSON.stringify(value); - }) - .join(', '); - - debug('reload with %s', serialized); - } - - viewerState.renderer.load(rendererConfig, viewportPosition, sourceProvider); - } - - queuedEvents.forEach(function (params) - { - publish.apply(null, params); - }); - - return true; - }; - - var hasChangedOption = function (options, key) - { - return key in options && options[key] !== settings[key]; - }; - - // Handles switching in and out of fullscreen mode - var prepareModeChange = function (options) - { - // Toggle the classes - var changeClass = options.inFullscreen ? 'addClass' : 'removeClass'; - viewerState.outerObject[changeClass]('diva-fullscreen'); - $('body')[changeClass]('diva-hide-scrollbar'); - settings.parentObject[changeClass]('diva-full-width'); - - // Adjust Diva's internal panel size, keeping the old values - var storedHeight = settings.panelHeight; - var storedWidth = settings.panelWidth; - viewerState.viewport.invalidate(); - - // If this isn't the original load, the offsets matter, and the position isn't being changed... - if (!viewerState.loaded && !settings.inGrid && !('verticalOffset' in options)) - { - //get the updated panel size - var newHeight = settings.panelHeight; - var newWidth = settings.panelWidth; - - //and re-center the new panel on the same point - viewerState.verticalOffset += ((storedHeight - newHeight) / 2); - viewerState.horizontalOffset += ((storedWidth - newWidth) / 2); - } - - //turn on/off escape key listener - if (options.inFullscreen) - $(document).on('keyup', escapeListener); - else - $(document).off('keyup', escapeListener); - }; - - // Update the view handler and the view rendering for the current view - var updateViewHandlerAndRendering = function () - { - var Handler = settings.inGrid ? GridHandler : DocumentHandler; - - if (viewerState.viewHandler && !(viewerState.viewHandler instanceof Handler)) - { - viewerState.viewHandler.destroy(); - viewerState.viewHandler = null; - } - - if (!viewerState.viewHandler) - viewerState.viewHandler = new Handler(self); - - if (!viewerState.renderer) - initializeRenderer(); - }; - - // TODO: This could probably be done upon ViewerCore initialization - var initializeRenderer = function () - { - var compatErrors = Renderer.getCompatibilityErrors(); - - if (compatErrors) - { - showError(compatErrors); - } - else - { - var options = { - viewport: viewerState.viewport, - outerElement: viewerState.outerElement, - innerElement: viewerState.innerElement - }; - - var hooks = { - onViewWillLoad: function () - { - viewerState.viewHandler.onViewWillLoad(); - }, - onViewDidLoad: function () - { - updatePageOverlays(); - viewerState.viewHandler.onViewDidLoad(); - }, - onViewDidUpdate: function (pages, targetPage) - { - updatePageOverlays(); - viewerState.viewHandler.onViewDidUpdate(pages, targetPage); - }, - onViewDidTransition: function () - { - updatePageOverlays(); - }, - onPageWillLoad: function (pageIndex) - { - publish('PageWillLoad', pageIndex); - } - }; - - viewerState.renderer = new Renderer(options, hooks); - } - }; - - var getCurrentSourceProvider = function () - { - if (settings.inGrid) - { - var gridSourceProvider = { - getAllZoomLevelsForPage: function (page) - { - return [gridSourceProvider.getBestZoomLevelForPage(page)]; - }, - getBestZoomLevelForPage: function (page) - { - var url = settings.manifest.getPageImageURL(page.index, { - width: page.dimensions.width - }); - - return { - zoomLevel: 1, // FIXME - rows: 1, - cols: 1, - tiles: [{ - url: url, - zoomLevel: 1, // FIXME - row: 0, - col: 0, - dimensions: page.dimensions, - offset: { - top: 0, - left: 0 - } - }] - }; - } - }; - - return gridSourceProvider; - } - - var tileDimens = { - width: settings.tileWidth, - height: settings.tileHeight - }; - - return { - getBestZoomLevelForPage: function (page) - { - return settings.manifest.getPageImageTiles(page.index, Math.ceil(settings.zoomLevel), tileDimens); - }, - getAllZoomLevelsForPage: function (page) - { - var levels = []; - - var levelCount = viewerState.manifest.maxZoom; - for (var level=0; level <= levelCount; level++) - { - levels.push(settings.manifest.getPageImageTiles(page.index, level, tileDimens)); - } - - levels.reverse(); - - return levels; - } - }; - }; - - var getPadding = function () - { - var topPadding, leftPadding; - var docVPadding, docHPadding; - - if (settings.inGrid) - { - docVPadding = settings.fixedPadding; - topPadding = leftPadding = docHPadding = 0; - } - else - { - topPadding = settings.verticallyOriented ? viewerState.verticalPadding : 0; - leftPadding = settings.verticallyOriented ? 0 : viewerState.horizontalPadding; - - docVPadding = settings.verticallyOriented ? 0 : viewerState.verticalPadding; - docHPadding = settings.verticallyOriented ? viewerState.horizontalPadding : 0; - } - - return { - document: { - top: docVPadding, - bottom: docVPadding, - left: docHPadding, - right: docHPadding - }, - page: { - top: topPadding, - bottom: 0, - left: leftPadding, - right: 0 - } - }; - }; - - var updatePageOverlays = function () - { - viewerState.pageOverlays.updateOverlays(viewerState.renderer.getRenderedPages()); - }; - - //Shortcut for closing fullscreen with the escape key - var escapeListener = function (e) - { - if (e.keyCode == 27) - { - reloadViewer({ - inFullscreen: !settings.inFullscreen - }); - } - }; - - // Called to handle any zoom level - var handleZoom = function (newZoomLevel, focalPoint) - { - // If the zoom level provided is invalid, return false - if (!isValidOption('zoomLevel', newZoomLevel)) - return false; - - // If no focal point was given, zoom on the center of the viewport - if (focalPoint == null) - { - var viewport = viewerState.viewport; - var currentRegion = viewerState.renderer.layout.getPageRegion(settings.currentPageIndex); - - focalPoint = { - anchorPage: settings.currentPageIndex, - offset: { - left: (viewport.width / 2) - (currentRegion.left - viewport.left), - top: (viewport.height / 2) - (currentRegion.top - viewport.top) - } - }; - } - - var pageRegion = viewerState.renderer.layout.getPageRegion(focalPoint.anchorPage); - - // calculate distance from cursor coordinates to center of viewport - var focalXToCenter = (pageRegion.left + focalPoint.offset.left) - - (settings.viewport.left + (settings.viewport.width / 2)); - var focalYToCenter = (pageRegion.top + focalPoint.offset.top) - - (settings.viewport.top + (settings.viewport.height / 2)); - - function getPositionForZoomLevel(zoomLevel) - { - var zoomRatio = Math.pow(2, zoomLevel - initialZoomLevel); - - //TODO(jeromepl): Calculate position from page top left to viewport top left - // calculate horizontal/verticalOffset: distance from viewport center to page upper left corner - var horizontalOffset = (focalPoint.offset.left * zoomRatio) - focalXToCenter; - var verticalOffset = (focalPoint.offset.top * zoomRatio) - focalYToCenter; - - return { - zoomLevel: zoomLevel, - anchorPage: focalPoint.anchorPage, - verticalOffset: verticalOffset, - horizontalOffset: horizontalOffset - }; - } - - var initialZoomLevel = viewerState.oldZoomLevel = settings.zoomLevel; - viewerState.options.zoomLevel = newZoomLevel; - - var endPosition = getPositionForZoomLevel(newZoomLevel); - viewerState.options.goDirectlyTo = endPosition.anchorPage; - viewerState.verticalOffset = endPosition.verticalOffset; - viewerState.horizontalOffset = endPosition.horizontalOffset; - - viewerState.renderer.transitionViewportPosition({ - duration: 300, - parameters: { - zoomLevel: { - from: initialZoomLevel, - to: newZoomLevel - } - }, - getPosition: function (parameters) - { - return getPositionForZoomLevel(parameters.zoomLevel); - }, - onEnd: function (info) - { - viewerState.viewportObject.scroll(scrollFunction); - - if (info.interrupted) - viewerState.oldZoomLevel = newZoomLevel; - } - }); - - // Update the slider - publish("ZoomLevelDidChange", newZoomLevel); - - // While zooming, don't update scroll offsets based on the scaled version of diva-inner - viewerState.viewportObject.off('scroll'); - - return true; - }; - - /* - Gets the Y-offset for a specific point on a specific page - Acceptable values for "anchor": - "top" (default) - will anchor top of the page to the top of the diva-outer element - "bottom" - top, s/top/bottom - "center" - will center the page on the diva element - Returned value will be the distance from the center of the diva-outer element to the top of the current page for the specified anchor - */ - var getYOffset = function (pageIndex, anchor) - { - pageIndex = (typeof(pageIndex) === "undefined" ? settings.currentPageIndex : pageIndex); - - if (anchor === "center" || anchor === "centre") //how you can tell an American coded this - { - return parseInt(getPageData(pageIndex, "h") / 2, 10); - } - else if (anchor === "bottom") - { - return parseInt(getPageData(pageIndex, "h") - settings.panelHeight / 2, 10); - } - else - { - return parseInt(settings.panelHeight / 2, 10); - } - }; - - //Same as getYOffset with "left" and "right" as acceptable values instead of "top" and "bottom" - var getXOffset = function (pageIndex, anchor) - { - pageIndex = (typeof(pageIndex) === "undefined" ? settings.currentPageIndex : pageIndex); - - if (anchor === "left") - { - return parseInt(settings.panelWidth / 2, 10); - } - else if (anchor === "right") - { - return parseInt(getPageData(pageIndex, "w") - settings.panelWidth / 2, 10); - } - else - { - return parseInt(getPageData(pageIndex, "w") / 2, 10); - } - }; - - // updates panelHeight/panelWidth on resize - var updatePanelSize = function () - { - viewerState.viewport.invalidate(); - - // FIXME(wabain): This should really only be called after initial load - if (viewerState.renderer) - { - updateOffsets(); - viewerState.renderer.goto(settings.currentPageIndex, viewerState.verticalOffset, viewerState.horizontalOffset); - } - - return true; - }; - - var updateOffsets = function () - { - var pageOffset = viewerState.renderer.layout.getPageToViewportCenterOffset(settings.currentPageIndex, viewerState.viewport); - - if (pageOffset) - { - viewerState.horizontalOffset = pageOffset.x; - viewerState.verticalOffset = pageOffset.y; - } - }; - - // Bind mouse events (drag to scroll, double-click) - var bindMouseEvents = function() - { - // Set drag scroll on first descendant of class dragger on both selected elements - viewerState.viewportObject.dragscrollable({dragSelector: '.diva-dragger', acceptPropagatedEvent: true}); - viewerState.innerObject.dragscrollable({dragSelector: '.diva-dragger', acceptPropagatedEvent: true}); - - gestureEvents.onDoubleClick(viewerState.viewportObject, function (event, coords) - { - debug('Double click at %s, %s', coords.left, coords.top); - viewerState.viewHandler.onDoubleClick(event, coords); - }); - }; - - var onResize = function() - { - updatePanelSize(); - // Cancel any previously-set resize timeouts - clearTimeout(viewerState.resizeTimer); - - viewerState.resizeTimer = setTimeout(function () - { - var pageOffset = viewerState.renderer.layout.getPageToViewportCenterOffset(settings.currentPageIndex, viewerState.viewport); - - if (pageOffset) - { - reloadViewer({ - goDirectlyTo: settings.currentPageIndex, - verticalOffset: pageOffset.y, - horizontalOffset: pageOffset.x - }); - } - else - { - reloadViewer({ - goDirectlyTo: settings.currentPageIndex - }); - } - }, 200); - }; - - // Bind touch and orientation change events - var bindTouchEvents = function() - { - // Block the user from moving the window only if it's not integrated - if (settings.blockMobileMove) - { - $('body').bind('touchmove', function (event) - { - var e = event.originalEvent; - e.preventDefault(); - - return false; - }); - } - - // Touch events for swiping in the viewport to scroll pages - viewerState.viewportObject.kinetic({ - triggerHardware: true - }); - - gestureEvents.onPinch(viewerState.viewportObject, function (event, coords, start, end) - { - debug('Pinch %s at %s, %s', end - start, coords.left, coords.top); - viewerState.viewHandler.onPinch(event, coords, start, end); - }); - - gestureEvents.onDoubleTap(viewerState.viewportObject, function (event, coords) - { - debug('Double tap at %s, %s', coords.left, coords.top); - viewerState.viewHandler.onDoubleClick(event, coords); - }); - }; - - // Handle the scroll - var scrollFunction = function () - { - var previousTopScroll = viewerState.viewport.top; - var previousLeftScroll = viewerState.viewport.left; - - var direction; - - viewerState.viewport.invalidate(); - - var newScrollTop = viewerState.viewport.top; - var newScrollLeft = viewerState.viewport.left; - - if (settings.verticallyOriented || settings.inGrid) - direction = newScrollTop - previousTopScroll; - else - direction = newScrollLeft - previousLeftScroll; - - //give adjust the direction we care about - viewerState.renderer.adjust(direction); - - var primaryScroll = (settings.verticallyOriented || settings.inGrid) ? newScrollTop : newScrollLeft; - - publish("ViewerDidScroll", primaryScroll); - - if (direction > 0) - { - publish("ViewerDidScrollDown", primaryScroll); - } - else if (direction < 0) - { - publish("ViewerDidScrollUp", primaryScroll); - } - - updateOffsets(); - }; - - // Binds most of the event handlers (some more in createToolbar) - var handleEvents = function () - { - // Change the cursor for dragging - viewerState.innerObject.mousedown(function () - { - viewerState.innerObject.addClass('diva-grabbing'); - }); - - viewerState.innerObject.mouseup(function () - { - viewerState.innerObject.removeClass('diva-grabbing'); - }); - - bindMouseEvents(); - - viewerState.viewportObject.scroll(scrollFunction); - - var upArrowKey = 38, - downArrowKey = 40, - leftArrowKey = 37, - rightArrowKey = 39, - spaceKey = 32, - pageUpKey = 33, - pageDownKey = 34, - homeKey = 36, - endKey = 35; - - // Catch the key presses in document - $(document).on('keydown.diva', function (event) - { - if (!viewerState.isActiveDiva) - return true; - - // Space or page down - go to the next page - if ((settings.enableSpaceScroll && !event.shiftKey && event.keyCode === spaceKey) || (settings.enableKeyScroll && event.keyCode === pageDownKey)) - { - viewerState.viewport.top += settings.panelHeight; - return false; - } - else if (!settings.enableSpaceScroll && event.keyCode === spaceKey) - { - event.preventDefault(); - } - - if (settings.enableKeyScroll) - { - // Don't steal keyboard shortcuts (metaKey = command [OS X], super [Win/Linux]) - if (event.shiftKey || event.ctrlKey || event.metaKey) - return true; - - switch (event.keyCode) - { - case pageUpKey: - // Page up - go to the previous page - viewerState.viewport.top -= settings.panelHeight; - return false; - - case upArrowKey: - // Up arrow - scroll up - viewerState.viewport.top -= settings.arrowScrollAmount; - return false; - - case downArrowKey: - // Down arrow - scroll down - viewerState.viewport.top += settings.arrowScrollAmount; - return false; - - case leftArrowKey: - // Left arrow - scroll left - viewerState.viewport.left -= settings.arrowScrollAmount; - return false; - - case rightArrowKey: - // Right arrow - scroll right - viewerState.viewport.left += settings.arrowScrollAmount; - return false; - - case homeKey: - // Home key - go to the beginning of the document - viewerState.viewport.top = 0; - return false; - - case endKey: - // End key - go to the end of the document - // Count on the viewport coordinate value being normalized - if (settings.verticallyOriented) - viewerState.viewport.top = Infinity; - else - viewerState.viewport.left = Infinity; - - return false; - - default: - return true; - } - } - return true; - }); - - diva.Events.subscribe('ViewerDidTerminate', function() - { - $(document).off('keydown.diva'); - }, settings.ID); - - bindTouchEvents(); - - // Handle window resizing events - window.addEventListener('resize', onResize, false); - - diva.Events.subscribe('ViewerDidTerminate', function() - { - window.removeEventListener('resize', onResize, false); - }, settings.ID); - - // Handle orientation change separately - if ('onorientationchange' in window) - { - window.addEventListener('orientationchange', onResize, false); - - diva.Events.subscribe('ViewerDidTerminate', function() - { - window.removeEventListener('orientationchange', onResize, false); - }, settings.ID); - } - - diva.Events.subscribe('PanelSizeDidChange', updatePanelSize, settings.ID); - - // Clear page and resize timeouts when the viewer is destroyed - diva.Events.subscribe('ViewerDidTerminate', function () - { - if (viewerState.renderer) - viewerState.renderer.destroy(); - - clearTimeout(viewerState.resizeTimer); - }, settings.ID); - }; - - var initPlugins = function () - { - // Add all the plugins that have not been explicitly disabled to - // settings.plugins - PluginRegistry.getAll().forEach(function (plugin) - { - var pluginProperName = plugin.pluginName[0].toUpperCase() + plugin.pluginName.substring(1); - - if (settings['enable' + pluginProperName]) - { - // Call the init function and check return value - var enablePlugin = plugin.init(settings, publicInstance); - - // If int returns false, consider the plugin disabled - if (!enablePlugin) - return; - - // Create the pageTools bar if handleClick is set to a function - if (typeof plugin.handleClick === 'function') - { - viewerState.pageTools.push(plugin); - } - - // Add it to settings.plugins so it can be used later - settings.plugins.push(plugin); - } - }); - }; - - var showThrobber = function () - { - hideThrobber(); - - viewerState.throbberTimeoutID = setTimeout(function () - { - $(settings.selector + 'throbber').show(); - }, settings.throbberTimeout); - }; - - var hideThrobber = function () - { - // Clear the timeout, if it hasn't executed yet - clearTimeout(viewerState.throbberTimeoutID); - - // Hide the throbber if it has already executed - $(settings.selector + 'throbber').hide(); - }; - - var showError = function(message) - { - var errorElement = elt('div', elemAttrs('error'), [ - elt('button', elemAttrs('error-close', {'aria-label': 'Close dialog'})), - elt('p', - elt('strong', 'Error') - ), - elt('div', message) - ]); - - viewerState.outerObject.append(errorElement); - - //bind dialog close button - $(settings.selector + 'error-close').on('click', function() - { - errorElement.parentNode.removeChild(errorElement); - }); - }; - - var setManifest = function (manifest, loadOptions) - { - viewerState.manifest = manifest; - - hideThrobber(); - - // Convenience value - viewerState.numPages = settings.manifest.pages.length; - - optionsValidator.validate(viewerState.options); - - publish('NumberOfPagesDidChange', settings.numPages); - - if (settings.enableAutoTitle) - { - if ($(settings.selector + 'title').length) - $(settings.selector + 'title').html(settings.manifest.itemTitle); - else - settings.parentObject.prepend(elt('div', elemAttrs('title'), [settings.manifest.itemTitle])); - } - - // Calculate the horizontal and vertical inter-page padding based on the dimensions of the average zoom level - if (settings.adaptivePadding > 0) - { - var z = Math.floor((settings.minZoomLevel + settings.maxZoomLevel) / 2); - viewerState.horizontalPadding = parseInt(settings.manifest.getAverageWidth(z) * settings.adaptivePadding, 10); - viewerState.verticalPadding = parseInt(settings.manifest.getAverageHeight(z) * settings.adaptivePadding, 10); - } - else - { - // It's less than or equal to 0; use fixedPadding instead - viewerState.horizontalPadding = settings.fixedPadding; - viewerState.verticalPadding = settings.fixedPadding; - } - - // Make sure the vertical padding is at least 40, if plugin icons are enabled - if (viewerState.pageTools.length) - { - viewerState.verticalPadding = Math.max(40, viewerState.verticalPadding); - } - - // If we detect a viewingHint of 'paged' in the manifest or sequence, enable book view by default - if (settings.manifest.paged) - { - viewerState.options.inBookLayout = true; - } - - // Plugin setup hooks should be bound to the ObjectDidLoad event - publish('ObjectDidLoad', settings); - - // Adjust the document panel dimensions - updatePanelSize(); - - var needsXCoord, needsYCoord; - - var anchoredVertically = false; - var anchoredHorizontally = false; - - // NB: `==` here will check both null and undefined - if (loadOptions.goDirectlyTo == null) - { - loadOptions.goDirectlyTo = settings.goDirectlyTo; - needsXCoord = needsYCoord = true; - } - else - { - needsXCoord = loadOptions.horizontalOffset == null || isNaN(loadOptions.horizontalOffset); - needsYCoord = loadOptions.verticalOffset == null || isNaN(loadOptions.verticalOffset); - } - - // Set default values for the horizontal and vertical offsets - if (needsXCoord) - { - // FIXME: What if inBookLayout/verticallyOriented is changed by loadOptions? - if (loadOptions.goDirectlyTo === 0 && settings.inBookLayout && settings.verticallyOriented) - { - // if in book layout, center the first opening by default - loadOptions.horizontalOffset = viewerState.horizontalPadding; - } - else - { - anchoredHorizontally = true; - loadOptions.horizontalOffset = getXOffset(loadOptions.goDirectlyTo, "center"); - } - } - - if (needsYCoord) - { - anchoredVertically = true; - loadOptions.verticalOffset = getYOffset(loadOptions.goDirectlyTo, "top"); - } - - reloadViewer(loadOptions); - - //prep dimensions one last time now that pages have loaded - updatePanelSize(); - - // FIXME: This is a hack to ensure that the outerElement scrollbars are taken into account - if (settings.verticallyOriented) - viewerState.innerElement.style.minWidth = settings.panelWidth + 'px'; - else - viewerState.innerElement.style.minHeight = settings.panelHeight + 'px'; - - // FIXME: If the page was supposed to be positioned relative to the viewport we need to - // recalculate it to take into account the scrollbars - if (anchoredVertically || anchoredHorizontally) - { - if (anchoredVertically) - viewerState.verticalOffset = getYOffset(settings.currentPageIndex, "top"); - - if (anchoredHorizontally) - viewerState.horizontalOffset = getXOffset(settings.currentPageIndex, "center"); - - viewerState.renderer.goto(settings.currentPageIndex, viewerState.verticalOffset, viewerState.horizontalOffset); - } - - // signal that everything should be set up and ready to go. - viewerState.loaded = true; - - publish("ViewerDidLoad", settings); - }; - - var publish = function (event) - { - var args = Array.prototype.slice.call(arguments, 1); - diva.Events.publish(event, args, publicInstance); - }; - - var init = function () - { - // First figure out the width of the scrollbar in this browser - // TODO(wabain): Cache this somewhere else - // Only some of the plugins rely on this now - viewerState.scrollbarWidth = getScrollbarWidth(); - - // If window.orientation is defined, then it's probably mobileWebkit - viewerState.mobileWebkit = window.orientation !== undefined; - - // Generate an ID that can be used as a prefix for all the other IDs - var idNumber = generateId(); - viewerState.ID = 'diva-' + idNumber + '-'; - viewerState.selector = '#' + settings.ID; - - if (options.hashParamSuffix === null) - { - // Omit the suffix from the first instance - if (idNumber === 1) - options.hashParamSuffix = ''; - else - options.hashParamSuffix = idNumber + ''; - } - - // Create the inner and outer panels - var innerElem = elt('div', elemAttrs('inner', { class: 'diva-inner diva-dragger' })); - var viewportElem = elt('div', elemAttrs('viewport'), innerElem); - var outerElem = elt('div', elemAttrs('outer'), - viewportElem, - elt('div', elemAttrs('throbber'))); - - viewerState.innerElement = innerElem; - viewerState.viewportElement = viewportElem; - viewerState.outerElement = outerElem; - - viewerState.innerObject = $(innerElem); - viewerState.viewportObject = $(viewportElem); - viewerState.outerObject = $(outerElem); - - settings.parentObject.append(outerElem); - - viewerState.viewport = new Viewport(viewerState.viewportElement, { - intersectionTolerance: settings.viewportMargin - }); - - // Do all the plugin initialisation - initPlugins(); - - handleEvents(); - - // Show the throbber while waiting for the manifest to load - showThrobber(); - }; - - this.getSettings = function () - { - return settings; - }; - - // Temporary accessor for the state of the viewer core - // TODO: Replace this with a more restricted view of whatever needs - // be exposed through settings for backwards compat - this.getInternalState = function () - { - return viewerState; - }; - - this.getPublicInstance = function () - { - return publicInstance; - }; - - this.getPageTools = function () - { - return viewerState.pageTools; - }; - - this.getCurrentLayout = function () - { - return viewerState.renderer ? viewerState.renderer.layout : null; - }; - - /** Get a copy of the current viewport dimensions */ - this.getViewport = function () - { - var viewport = viewerState.viewport; - - return { - top: viewport.top, - left: viewport.left, - bottom: viewport.bottom, - right: viewport.right, - - width: viewport.width, - height: viewport.height - }; - }; - - this.addPageOverlay = function (overlay) - { - viewerState.pageOverlays.addOverlay(overlay); - }; - - this.removePageOverlay = function (overlay) - { - viewerState.pageOverlays.removeOverlay(overlay); - }; - - this.getPageRegion = function (pageIndex, options) - { - var layout = viewerState.renderer.layout; - var region = layout.getPageRegion(pageIndex, options); - - if (options && options.incorporateViewport) - { - var secondaryDim = settings.verticallyOriented ? 'width' : 'height'; - - if (viewerState.viewport[secondaryDim] > layout.dimensions[secondaryDim]) - { - var docOffset = (viewerState.viewport[secondaryDim] - layout.dimensions[secondaryDim]) / 2; - - if (settings.verticallyOriented) - { - return { - top: region.top, - bottom: region.bottom, - - left: region.left + docOffset, - right: region.right + docOffset - }; - } - else - { - return { - top: region.top + docOffset, - bottom: region.bottom + docOffset, - - left: region.left, - right: region.right - }; - } - } - } - - return region; - }; - - this.getPagePositionAtViewportOffset = function (coords) - { - var docCoords = { - left: coords.left + viewerState.viewport.left, - top: coords.top + viewerState.viewport.top - }; - - var renderedPages = viewerState.renderer.getRenderedPages(); - var pageCount = renderedPages.length; - - // Find the page on which the coords occur - for (var i=0; i < pageCount; i++) - { - var pageIndex = renderedPages[i]; - var region = viewerState.renderer.layout.getPageRegion(pageIndex); - - if (region.left <= docCoords.left && region.right >= docCoords.left && - region.top <= docCoords.top && region.bottom >= docCoords.top) - { - return { - anchorPage: pageIndex, - offset: { - left: docCoords.left - region.left, - top: docCoords.top - region.top - } - }; - } - } - - // Fall back to current page - // FIXME: Would be better to use the closest page or something - var currentRegion = viewerState.renderer.layout.getPageRegion(settings.currentPageIndex); - - return { - anchorPage: settings.currentPageIndex, - offset: { - left: docCoords.left - currentRegion.left, - top: docCoords.top - currentRegion.top - } - }; - }; - - this.setManifest = function (manifest, loadOptions) - { - setManifest(manifest, loadOptions || {}); - }; - - /** - * Set the current page to the given index, firing VisiblePageDidChange - * - * @param pageIndex - */ - this.setCurrentPage = function (pageIndex) - { - if (viewerState.currentPageIndex !== pageIndex) - { - viewerState.currentPageIndex = pageIndex; - publish("VisiblePageDidChange", pageIndex, this.getPageName(pageIndex)); - } - }; - - this.getPageName = function (pageIndex) - { - return viewerState.manifest.pages[pageIndex].f; - }; - - this.reload = function (newOptions) - { - reloadViewer(newOptions); - }; - - this.zoom = function (zoomLevel, focalPoint) - { - return handleZoom(zoomLevel, focalPoint); - }; - - this.enableScrollable = function () - { - if (!viewerState.isScrollable) - { - bindMouseEvents(); - viewerState.options.enableKeyScroll = viewerState.initialKeyScroll; - viewerState.options.enableSpaceScroll = viewerState.initialSpaceScroll; - viewerState.viewportElement.style.overflow = 'auto'; - viewerState.isScrollable = true; - } - }; - - this.disableScrollable = function () - { - if (viewerState.isScrollable) - { - // block dragging/double-click zooming - if (viewerState.innerObject.hasClass('diva-dragger')) - viewerState.innerObject.unbind('mousedown'); - viewerState.outerObject.unbind('dblclick'); - viewerState.outerObject.unbind('contextmenu'); - - // disable all other scrolling actions - viewerState.viewportElement.style.overflow = 'hidden'; - - // block scrolling keys behavior, respecting initial scroll settings - viewerState.initialKeyScroll = settings.enableKeyScroll; - viewerState.initialSpaceScroll = settings.enableSpaceScroll; - viewerState.options.enableKeyScroll = false; - viewerState.options.enableSpaceScroll = false; - - viewerState.isScrollable = false; - } - }; - - this.isValidOption = function (key, value) - { - return isValidOption(key, value); - }; - - this.showError = function (message) - { - // FIXME: Not totally sure it makes sense to always do that here - hideThrobber(); - - var errorElement = elt('div', elemAttrs('error'), [ - elt('button', elemAttrs('error-close', {'aria-label': 'Close dialog'})), - elt('p', - elt('strong', 'Error') - ), - elt('div', message) - ]); - - viewerState.outerObject.append(errorElement); - - //bind dialog close button - $(settings.selector + 'error-close').on('click', function() - { - errorElement.parentNode.removeChild(errorElement); - }); - }; - - this.getXOffset = function (pageIndex, xAnchor) - { - return getXOffset(pageIndex, xAnchor); - }; - - this.getYOffset = function (pageIndex, yAnchor) - { - return getYOffset(pageIndex, yAnchor); - }; - - this.publish = publish; - - this.clear = function () - { - clearViewer(); - }; - - this.setPendingManifestRequest = function (pendingManifestRequest) - { - viewerState.pendingManifestRequest = pendingManifestRequest; - }; - - // Destroys this instance, tells plugins to do the same (for testing) - this.destroy = function () - { - // Useful event to access elements in diva before they get destroyed. Used by the highlight plugin. - publish('ViewerWillTerminate', settings); - - // Cancel any pending request retrieving a manifest - if (settings.pendingManifestRequest) - settings.pendingManifestRequest.abort(); - - // Removes the hide-scrollbar class from the body - $('body').removeClass('diva-hide-scrollbar'); - - // Empty the parent container and remove any diva-related data - settings.parentObject.parent().empty().removeData('diva'); - - // Remove any additional styling on the parent element - settings.parentObject.parent().removeAttr('style').removeAttr('class'); - - publish('ViewerDidTerminate', settings); - - // Clear the Events cache - diva.Events.unsubscribeAll(settings.ID); - }; - - // Call the init function when this object is created. - init(); - } - - generateId.counter = 1; - - function generateId() { - return generateId.counter++; - } - - -/***/ }), -/* 16 */ -/***/ (function(module, exports, __webpack_require__) { - - /* jshint unused: false */ - - var jQuery = __webpack_require__(3); - - /* istanbul ignore next This is a vendored dependency */ - /* - * jQuery dragscrollable Plugin - * version: 1.0 (25-Jun-2009) - * Copyright (c) 2009 Miquel Herrera - * http://plugins.jquery.com/project/Dragscrollable - * - * Dual licensed under the MIT and GPL licenses: - * http://www.opensource.org/licenses/mit-license.php - * http://www.gnu.org/licenses/gpl.html - * - */ - (function ($) { // secure $ jQuery alias - - /** - * Adds the ability to manage elements scroll by dragging - * one or more of its descendant elements. Options parameter - * allow to specifically select which inner elements will - * respond to the drag events. - * - * options properties: - * ------------------------------------------------------------------------ - * dragSelector | jquery selector to apply to each wrapped element - * | to find which will be the dragging elements. - * | Defaults to '>:first' which is the first child of - * | scrollable element - * ------------------------------------------------------------------------ - * acceptPropagatedEvent| Will the dragging element accept propagated - * | events? default is yes, a propagated mouse event - * | on a inner element will be accepted and processed. - * | If set to false, only events originated on the - * | draggable elements will be processed. - * ------------------------------------------------------------------------ - * preventDefault | Prevents the event to propagate further effectivey - * | dissabling other default actions. Defaults to true - * ------------------------------------------------------------------------ - * - * usage examples: - * - * To add the scroll by drag to the element id=viewport when dragging its - * first child accepting any propagated events - * $('#viewport').dragscrollable(); - * - * To add the scroll by drag ability to any element div of class viewport - * when dragging its first descendant of class dragMe responding only to - * evcents originated on the '.dragMe' elements. - * $('div.viewport').dragscrollable({dragSelector:'.dragMe:first', - * acceptPropagatedEvent: false}); - * - * Notice that some 'viewports' could be nested within others but events - * would not interfere as acceptPropagatedEvent is set to false. - * - */ - $.fn.dragscrollable = function( options ){ - - var settings = $.extend( - { - dragSelector:'>:first', - acceptPropagatedEvent: true, - preventDefault: true - },options || {}); - - - var dragscroll= { - mouseDownHandler : function(event) { - // mousedown, left click, check propagation - if (event.which!=1 || - (!event.data.acceptPropagatedEvent && event.target != this)){ - return false; - } - - // Initial coordinates will be the last when dragging - event.data.lastCoord = {left: event.clientX, top: event.clientY}; - - $.event.add( document, "mouseup", - dragscroll.mouseUpHandler, event.data ); - $.event.add( document, "mousemove", - dragscroll.mouseMoveHandler, event.data ); - if (event.data.preventDefault) { - event.preventDefault(); - return false; - } - }, - mouseMoveHandler : function(event) { // User is dragging - // How much did the mouse move? - var delta = {left: (event.clientX - event.data.lastCoord.left), - top: (event.clientY - event.data.lastCoord.top)}; - - // Set the scroll position relative to what ever the scroll is now - event.data.scrollable.scrollLeft( - event.data.scrollable.scrollLeft() - delta.left); - event.data.scrollable.scrollTop( - event.data.scrollable.scrollTop() - delta.top); - - // Save where the cursor is - event.data.lastCoord={left: event.clientX, top: event.clientY}; - if (event.data.preventDefault) { - event.preventDefault(); - return false; - } - - }, - mouseUpHandler : function(event) { // Stop scrolling - $.event.remove( document, "mousemove", dragscroll.mouseMoveHandler); - $.event.remove( document, "mouseup", dragscroll.mouseUpHandler); - if (event.data.preventDefault) { - event.preventDefault(); - return false; - } - } - }; - - // set up the initial events - this.each(function() { - // closure object data for each scrollable element - var data = {scrollable : $(this), - acceptPropagatedEvent : settings.acceptPropagatedEvent, - preventDefault : settings.preventDefault }; - // Set mouse initiating event on the desired descendant - $(this).find(settings.dragSelector). - bind('mousedown', data, dragscroll.mouseDownHandler); - }); - }; //end plugin dragscrollable - - })( jQuery ); // confine scope - - /* istanbul ignore next This is a vendored dependency */ - /** - jQuery.kinetic v2.2.1 - Dave Taylor http://davetayls.me - - @license The MIT License (MIT) - @preserve Copyright (c) 2012 Dave Taylor http://davetayls.me - */ - (function ($){ - 'use strict'; - - var ACTIVE_CLASS = 'kinetic-active'; - - /** - * Provides requestAnimationFrame in a cross browser way. - * http://paulirish.com/2011/requestanimationframe-for-smart-animating/ - */ - if (!window.requestAnimationFrame){ - - window.requestAnimationFrame = ( function (){ - - return window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function (/* function FrameRequestCallback */ callback, /* DOMElement Element */ element){ - window.setTimeout(callback, 1000 / 60); - }; - - }()); - - } - - // add touch checker to jQuery.support - $.support = $.support || {}; - $.extend($.support, { - touch: 'ontouchend' in document - }); - - - // KINETIC CLASS DEFINITION - // ====================== - - var Kinetic = function (element, settings) { - this.settings = settings; - this.el = element; - this.$el = $(element); - - this._initElements(); - - return this; - }; - - Kinetic.DATA_KEY = 'kinetic'; - Kinetic.DEFAULTS = { - cursor: 'move', - decelerate: true, - triggerHardware: false, - threshold: 0, - y: true, - x: true, - slowdown: 0.9, - maxvelocity: 40, - throttleFPS: 60, - invert: false, - movingClass: { - up: 'kinetic-moving-up', - down: 'kinetic-moving-down', - left: 'kinetic-moving-left', - right: 'kinetic-moving-right' - }, - deceleratingClass: { - up: 'kinetic-decelerating-up', - down: 'kinetic-decelerating-down', - left: 'kinetic-decelerating-left', - right: 'kinetic-decelerating-right' - } - }; - - - // Public functions - - Kinetic.prototype.start = function (options){ - this.settings = $.extend(this.settings, options); - this.velocity = options.velocity || this.velocity; - this.velocityY = options.velocityY || this.velocityY; - this.settings.decelerate = false; - this._move(); - }; - - Kinetic.prototype.end = function (){ - this.settings.decelerate = true; - }; - - Kinetic.prototype.stop = function (){ - this.velocity = 0; - this.velocityY = 0; - this.settings.decelerate = true; - if ($.isFunction(this.settings.stopped)){ - this.settings.stopped.call(this); - } - }; - - Kinetic.prototype.detach = function (){ - this._detachListeners(); - this.$el - .removeClass(ACTIVE_CLASS) - .css('cursor', ''); - }; - - Kinetic.prototype.attach = function (){ - if (this.$el.hasClass(ACTIVE_CLASS)) { - return; - } - this._attachListeners(this.$el); - this.$el - .addClass(ACTIVE_CLASS) - .css('cursor', this.settings.cursor); - }; - - - // Internal functions - - Kinetic.prototype._initElements = function (){ - this.$el.addClass(ACTIVE_CLASS); - - $.extend(this, { - xpos: null, - prevXPos: false, - ypos: null, - prevYPos: false, - mouseDown: false, - throttleTimeout: 1000 / this.settings.throttleFPS, - lastMove: null, - elementFocused: null - }); - - this.velocity = 0; - this.velocityY = 0; - - // make sure we reset everything when mouse up - $(document) - .mouseup($.proxy(this._resetMouse, this)) - .click($.proxy(this._resetMouse, this)); - - this._initEvents(); - - this.$el.css('cursor', this.settings.cursor); - - if (this.settings.triggerHardware){ - this.$el.css({ - '-webkit-transform': 'translate3d(0,0,0)', - '-webkit-perspective': '1000', - '-webkit-backface-visibility': 'hidden' - }); - } - }; - - Kinetic.prototype._initEvents = function(){ - var self = this; - this.settings.events = { - touchStart: function (e){ - var touch; - if (self._useTarget(e.target, e)){ - touch = e.originalEvent.touches[0]; - self.threshold = self._threshold(e.target, e); - self._start(touch.clientX, touch.clientY); - e.stopPropagation(); - } - }, - touchMove: function (e){ - var touch; - if (self.mouseDown){ - touch = e.originalEvent.touches[0]; - self._inputmove(touch.clientX, touch.clientY); - if (e.preventDefault){ - e.preventDefault(); - } - } - }, - inputDown: function (e){ - if (self._useTarget(e.target, e)){ - self.threshold = self._threshold(e.target, e); - self._start(e.clientX, e.clientY); - self.elementFocused = e.target; - if (e.target.nodeName === 'IMG'){ - e.preventDefault(); - } - e.stopPropagation(); - } - }, - inputEnd: function (e){ - if (self._useTarget(e.target, e)){ - self._end(); - self.elementFocused = null; - if (e.preventDefault){ - e.preventDefault(); - } - } - }, - inputMove: function (e){ - if (self.mouseDown){ - self._inputmove(e.clientX, e.clientY); - if (e.preventDefault){ - e.preventDefault(); - } - } - }, - scroll: function (e){ - if ($.isFunction(self.settings.moved)){ - self.settings.moved.call(self, self.settings); - } - if (e.preventDefault){ - e.preventDefault(); - } - }, - inputClick: function (e){ - if (Math.abs(self.velocity) > 0){ - e.preventDefault(); - return false; - } - }, - // prevent drag and drop images in ie - dragStart: function (e){ - if (self._useTarget(e.target, e) && self.elementFocused){ - return false; - } - }, - // prevent selection when dragging - selectStart: function (e){ - if ($.isFunction(self.settings.selectStart)){ - return self.settings.selectStart.apply(self, arguments); - } else if (self._useTarget(e.target, e)) { - return false; - } - } - }; - - this._attachListeners(this.$el, this.settings); - - }; - - Kinetic.prototype._inputmove = function (clientX, clientY){ - var $this = this.$el; - var el = this.el; - - if (!this.lastMove || new Date() > new Date(this.lastMove.getTime() + this.throttleTimeout)){ - this.lastMove = new Date(); - - if (this.mouseDown && (this.xpos || this.ypos)){ - var movedX = (clientX - this.xpos); - var movedY = (clientY - this.ypos); - if (this.settings.invert) { - movedX *= -1; - movedY *= -1; - } - if(this.threshold > 0){ - var moved = Math.sqrt(movedX * movedX + movedY * movedY); - if(this.threshold > moved){ - return; - } else { - this.threshold = 0; - } - } - if (this.elementFocused){ - $(this.elementFocused).blur(); - this.elementFocused = null; - $this.focus(); - } - - this.settings.decelerate = false; - this.velocity = this.velocityY = 0; - - var scrollLeft = this.scrollLeft(); - var scrollTop = this.scrollTop(); - - this.scrollLeft(this.settings.x ? scrollLeft - movedX : scrollLeft); - this.scrollTop(this.settings.y ? scrollTop - movedY : scrollTop); - - this.prevXPos = this.xpos; - this.prevYPos = this.ypos; - this.xpos = clientX; - this.ypos = clientY; - - this._calculateVelocities(); - this._setMoveClasses(this.settings.movingClass); - - if ($.isFunction(this.settings.moved)){ - this.settings.moved.call(this, this.settings); - } - } - } - }; - - Kinetic.prototype._calculateVelocities = function (){ - this.velocity = this._capVelocity(this.prevXPos - this.xpos, this.settings.maxvelocity); - this.velocityY = this._capVelocity(this.prevYPos - this.ypos, this.settings.maxvelocity); - if (this.settings.invert) { - this.velocity *= -1; - this.velocityY *= -1; - } - }; - - Kinetic.prototype._end = function (){ - if (this.xpos && this.prevXPos && this.settings.decelerate === false){ - this.settings.decelerate = true; - this._calculateVelocities(); - this.xpos = this.prevXPos = this.mouseDown = false; - this._move(); - } - }; - - Kinetic.prototype._useTarget = function (target, event){ - if ($.isFunction(this.settings.filterTarget)){ - return this.settings.filterTarget.call(this, target, event) !== false; - } - return true; - }; - - Kinetic.prototype._threshold = function (target, event){ - if ($.isFunction(this.settings.threshold)){ - return this.settings.threshold.call(this, target, event); - } - return this.settings.threshold; - }; - - Kinetic.prototype._start = function (clientX, clientY){ - this.mouseDown = true; - this.velocity = this.prevXPos = 0; - this.velocityY = this.prevYPos = 0; - this.xpos = clientX; - this.ypos = clientY; - }; - - Kinetic.prototype._resetMouse = function (){ - this.xpos = false; - this.ypos = false; - this.mouseDown = false; - }; - - Kinetic.prototype._decelerateVelocity = function (velocity, slowdown){ - return Math.floor(Math.abs(velocity)) === 0 ? 0 // is velocity less than 1? - : velocity * slowdown; // reduce slowdown - }; - - Kinetic.prototype._capVelocity = function (velocity, max){ - var newVelocity = velocity; - if (velocity > 0){ - if (velocity > max){ - newVelocity = max; - } - } else { - if (velocity < (0 - max)){ - newVelocity = (0 - max); - } - } - return newVelocity; - }; - - Kinetic.prototype._setMoveClasses = function (classes){ - // FIXME: consider if we want to apply PL #44, this should not remove - // classes we have not defined on the element! - var settings = this.settings; - var $this = this.$el; - - $this.removeClass(settings.movingClass.up) - .removeClass(settings.movingClass.down) - .removeClass(settings.movingClass.left) - .removeClass(settings.movingClass.right) - .removeClass(settings.deceleratingClass.up) - .removeClass(settings.deceleratingClass.down) - .removeClass(settings.deceleratingClass.left) - .removeClass(settings.deceleratingClass.right); - - if (this.velocity > 0){ - $this.addClass(classes.right); - } - if (this.velocity < 0){ - $this.addClass(classes.left); - } - if (this.velocityY > 0){ - $this.addClass(classes.down); - } - if (this.velocityY < 0){ - $this.addClass(classes.up); - } - - }; - - - // do the actual kinetic movement - Kinetic.prototype._move = function (){ - var $scroller = this._getScroller(); - var scroller = $scroller[0]; - var self = this; - var settings = self.settings; - - // set scrollLeft - if (settings.x && scroller.scrollWidth > 0){ - this.scrollLeft(this.scrollLeft() + this.velocity); - if (Math.abs(this.velocity) > 0){ - this.velocity = settings.decelerate ? - self._decelerateVelocity(this.velocity, settings.slowdown) : this.velocity; - } - } else { - this.velocity = 0; - } - - // set scrollTop - if (settings.y && scroller.scrollHeight > 0){ - this.scrollTop(this.scrollTop() + this.velocityY); - if (Math.abs(this.velocityY) > 0){ - this.velocityY = settings.decelerate ? - self._decelerateVelocity(this.velocityY, settings.slowdown) : this.velocityY; - } - } else { - this.velocityY = 0; - } - - self._setMoveClasses(settings.deceleratingClass); - - if ($.isFunction(settings.moved)){ - settings.moved.call(this, settings); - } - - if (Math.abs(this.velocity) > 0 || Math.abs(this.velocityY) > 0){ - if (!this.moving) { - this.moving = true; - // tick for next movement - window.requestAnimationFrame(function (){ - self.moving = false; - self._move(); - }); - } - } else { - self.stop(); - } - }; - - // get current scroller to apply positioning to - Kinetic.prototype._getScroller = function(){ - var $scroller = this.$el; - if (this.$el.is('body') || this.$el.is('html')){ - $scroller = $(window); - } - return $scroller; - }; - - // set the scroll position - Kinetic.prototype.scrollLeft = function(left){ - var $scroller = this._getScroller(); - if (typeof left === 'number'){ - $scroller.scrollLeft(left); - this.settings.scrollLeft = left; - } else { - return $scroller.scrollLeft(); - } - }; - Kinetic.prototype.scrollTop = function(top){ - var $scroller = this._getScroller(); - if (typeof top === 'number'){ - $scroller.scrollTop(top); - this.settings.scrollTop = top; - } else { - return $scroller.scrollTop(); - } - }; - - Kinetic.prototype._attachListeners = function (){ - var $this = this.$el; - var settings = this.settings; - - if ($.support.touch){ - $this - .bind('touchstart', settings.events.touchStart) - .bind('touchend', settings.events.inputEnd) - .bind('touchmove', settings.events.touchMove); - } - - $this - .mousedown(settings.events.inputDown) - .mouseup(settings.events.inputEnd) - .mousemove(settings.events.inputMove); - - $this - .click(settings.events.inputClick) - .scroll(settings.events.scroll) - .bind('selectstart', settings.events.selectStart) - .bind('dragstart', settings.events.dragStart); - }; - - Kinetic.prototype._detachListeners = function (){ - var $this = this.$el; - var settings = this.settings; - if ($.support.touch){ - $this - .unbind('touchstart', settings.events.touchStart) - .unbind('touchend', settings.events.inputEnd) - .unbind('touchmove', settings.events.touchMove); - } - - $this - .unbind('mousedown', settings.events.inputDown) - .unbind('mouseup', settings.events.inputEnd) - .unbind('mousemove', settings.events.inputMove); - - $this - .unbind('click', settings.events.inputClick) - .unbind('scroll', settings.events.scroll) - .unbind('selectstart', settings.events.selectStart) - .unbind('dragstart', settings.events.dragStart); - }; - - - // EXPOSE KINETIC CONSTRUCTOR - // ========================== - $.Kinetic = Kinetic; - - // KINETIC PLUGIN DEFINITION - // ======================= - - $.fn.kinetic = function (option, callOptions) { - return this.each(function () { - var $this = $(this); - var instance = $this.data(Kinetic.DATA_KEY); - var options = $.extend({}, Kinetic.DEFAULTS, $this.data(), typeof option === 'object' && option); - - if (!instance) { - $this.data(Kinetic.DATA_KEY, (instance = new Kinetic(this, options))); - } - - if (typeof option === 'string') { - instance[option](callOptions); - } - - }); - }; - - }(jQuery)); - - /* istanbul ignore next - We should maybe be testing this, but realistically that would mean maintaining a real fork */ - - // jQuery.kinetic core modifications for diva.js (compatible with jQuery.kinetic 2.2.1) - // use jQuery.kinetic for touch handlers only since we are using dragscrollable for mouse handlers - // - (kinetic provides inertial scrolling [ease into stopped state on release] for touch events and dragscrollable - // allows non-inertial scrolling which we like for mice) - - (function($) - { - $.Kinetic.prototype._attachListeners = function() - { - // attach only touch listeners - var $this = this.$el; - var settings = this.settings; - - if ($.support.touch) - { - $this - .bind('touchstart', settings.events.touchStart) - .bind('touchend', settings.events.inputEnd) - .bind('touchmove', settings.events.touchMove); - } - - $this - .click(settings.events.inputClick) - .scroll(settings.events.scroll) - .bind('selectstart', settings.events.selectStart) - .bind('dragstart', settings.events.dragStart); - }; - - $.Kinetic.prototype._detachListeners = function() - { - // detach only touch listeners - var $this = this.$el; - var settings = this.settings; - - if ($.support.touch) - { - $this - .unbind('touchstart', settings.events.touchStart) - .unbind('touchend', settings.events.inputEnd) - .unbind('touchmove', settings.events.touchMove); - } - - $this - .unbind('click', settings.events.inputClick) - .unbind('scroll', settings.events.scroll) - .unbind('selectstart', settings.events.selectStart) - .unbind('dragstart', settings.events.dragStart); - }; - })(jQuery); - - -/***/ }), -/* 17 */ -/***/ (function(module, exports) { - - // From http://www.alexandre-gomes.com/?p=115, modified slightly - module.exports = function getScrollbarWidth() { - var inner = document.createElement('p'); - inner.style.width = '100%'; - inner.style.height = '200px'; - - var outer = document.createElement('div'); - outer.style.position = 'absolute'; - outer.style.top = '0px'; - outer.style.left = '0px'; - outer.style.visibility = 'hidden'; - outer.style.width = '200px'; - outer.style.height = '150px'; - outer.style.overflow = 'hidden'; - outer.appendChild(inner); - - document.body.appendChild(outer); - - var w1 = inner.offsetWidth; - outer.style.overflow = 'scroll'; - var w2 = inner.offsetWidth; - if (w1 == w2) { - w2 = outer.clientWidth; // for IE i think - } - - document.body.removeChild(outer); - return w1 - w2; - }; - - -/***/ }), -/* 18 */ -/***/ (function(module, exports) { - - module.exports = { - onDoubleClick: onDoubleClick, - onPinch: onPinch, - onDoubleTap: onDoubleTap - }; - - var DOUBLE_CLICK_TIMEOUT = 500; - - var DOUBLE_TAP_DISTANCE_THRESHOLD = 50; - var DOUBLE_TAP_TIMEOUT = 250; - - function onDoubleClick(elem, callback) - { - elem.on('dblclick', function (event) - { - if (!event.ctrlKey) - { - callback(event, getRelativeOffset(event.currentTarget, event)); - } - }); - - // Handle the control key for macs (in conjunction with double-clicking) - // FIXME: Does a click get handled with ctrl pressed on non-Macs? - var tracker = createDoubleEventTracker(DOUBLE_CLICK_TIMEOUT); - - elem.on('contextmenu', function (event) - { - event.preventDefault(); - - if (event.ctrlKey) - { - if (tracker.isTriggered()) - { - tracker.reset(); - callback(event, getRelativeOffset(event.currentTarget, event)); - } - else - { - tracker.trigger(); - } - } - }); - } - - function onPinch(elem, callback) - { - var startDistance = 0; - - elem.on('touchstart', function(event) - { - // Prevent mouse event from firing - event.preventDefault(); - - if (event.originalEvent.touches.length === 2) - { - startDistance = distance( - event.originalEvent.touches[0].clientX, - event.originalEvent.touches[0].clientY, - event.originalEvent.touches[1].clientX, - event.originalEvent.touches[1].clientY - ); - } - }); - - elem.on('touchmove', function(event) - { - // Prevent mouse event from firing - event.preventDefault(); - - if (event.originalEvent.touches.length === 2) - { - var touches = event.originalEvent.touches; - - var moveDistance = distance( - touches[0].clientX, - touches[0].clientY, - touches[1].clientX, - touches[1].clientY - ); - - var zoomDelta = moveDistance - startDistance; - - if (Math.abs(zoomDelta) > 0) - { - var touchCenter = { - pageX: (touches[0].clientX + touches[1].clientX) / 2, - pageY: (touches[0].clientY + touches[1].clientY) / 2 - }; - - callback(event, getRelativeOffset(event.currentTarget, touchCenter), startDistance, moveDistance); - } - } - }); - } - - function onDoubleTap(elem, callback) - { - var tracker = createDoubleEventTracker(DOUBLE_TAP_TIMEOUT); - var firstTap = null; - - elem.on('touchend', function (event) - { - // Prevent mouse event from firing - event.preventDefault(); - - if (tracker.isTriggered()) - { - tracker.reset(); - - // Doubletap has occurred - var secondTap = { - pageX: event.originalEvent.changedTouches[0].clientX, - pageY: event.originalEvent.changedTouches[0].clientY - }; - - // If first tap is close to second tap (prevents interference with scale event) - var tapDistance = distance(firstTap.pageX, firstTap.pageY, secondTap.pageX, secondTap.pageY); - - // TODO: Could give something higher-level than secondTap to callback - if (tapDistance < DOUBLE_TAP_DISTANCE_THRESHOLD) - callback(event, getRelativeOffset(event.currentTarget, secondTap)); - - firstTap = null; - } - else - { - firstTap = { - pageX: event.originalEvent.changedTouches[0].clientX, - pageY: event.originalEvent.changedTouches[0].clientY - }; - - tracker.trigger(); - } - }); - } - - // Pythagorean theorem to get the distance between two points (used for - // calculating finger distance for double-tap and pinch-zoom) - function distance(x1, y1, x2, y2) - { - return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); - } - - // Utility to keep track of whether an event has been triggered twice - // during a a given duration - function createDoubleEventTracker(timeoutDuration) - { - var triggered = false; - var timeoutId = null; - - return { - trigger: function () - { - triggered = true; - resetTimeout(); - timeoutId = setTimeout(function () - { - triggered = false; - timeoutId = null; - }, timeoutDuration); - }, - isTriggered: function () - { - return triggered; - }, - reset: function () - { - triggered = false; - resetTimeout(); - } - }; - - function resetTimeout() - { - if (timeoutId !== null) - { - clearTimeout(timeoutId); - timeoutId = null; - } - } - } - - function getRelativeOffset(elem, pageCoords) - { - var bounds = elem.getBoundingClientRect(); - - return { - left: pageCoords.pageX - bounds.left, - top: pageCoords.pageY - bounds.top - }; - } - - -/***/ }), -/* 19 */ -/***/ (function(module, exports, __webpack_require__) { - - var maxBy = __webpack_require__(20); - var PageToolsOverlay = __webpack_require__(22); - - module.exports = DocumentHandler; - - function DocumentHandler(viewerCore) - { - this._viewerCore = viewerCore; - this._viewerState = viewerCore.getInternalState(); - this._overlays = []; - - if (viewerCore.getPageTools().length) - { - var numPages = viewerCore.getSettings().numPages; - - for (var i=0; i < numPages; i++) - { - var overlay = new PageToolsOverlay(i, viewerCore); - this._overlays.push(overlay); - viewerCore.addPageOverlay(overlay); - } - } - } - - // USER EVENTS - DocumentHandler.prototype.onDoubleClick = function (event, coords) - { - var settings = this._viewerCore.getSettings(); - var newZoomLevel = event.ctrlKey ? settings.zoomLevel - 1 : settings.zoomLevel + 1; - - var position = this._viewerCore.getPagePositionAtViewportOffset(coords); - - this._viewerCore.zoom(newZoomLevel, position); - }; - - DocumentHandler.prototype.onPinch = function (event, coords, startDistance, endDistance) - { - // FIXME: Do this check in a way which is less spaghetti code-y - var viewerState = this._viewerCore.getInternalState(); - var settings = this._viewerCore.getSettings(); - - var newZoomLevel = Math.log(Math.pow(2, settings.zoomLevel) * endDistance / (startDistance * Math.log(2))) / Math.log(2); - newZoomLevel = Math.max(settings.minZoomLevel, newZoomLevel); - newZoomLevel = Math.min(settings.maxZoomLevel, newZoomLevel); - - if (newZoomLevel === settings.zoomLevel) - return; - - var position = this._viewerCore.getPagePositionAtViewportOffset(coords); - - var layout = this._viewerCore.getCurrentLayout(); - var centerOffset = layout.getPageToViewportCenterOffset(position.anchorPage, viewerState.viewport); - var scaleRatio = 1 / Math.pow(2, settings.zoomLevel - newZoomLevel); - - this._viewerCore.reload({ - zoomLevel: newZoomLevel, - goDirectlyTo: position.anchorPage, - horizontalOffset: (centerOffset.x - position.offset.left) + position.offset.left * scaleRatio, - verticalOffset: (centerOffset.y - position.offset.top) + position.offset.top * scaleRatio - }); - }; - - // VIEW EVENTS - DocumentHandler.prototype.onViewWillLoad = function () - { - this._viewerCore.publish('DocumentWillLoad', this._viewerCore.getSettings()); - }; - - DocumentHandler.prototype.onViewDidLoad = function () - { - // TODO: Should only be necessary to handle changes on view update, not - // initial load - this._handleZoomLevelChange(); - - var currentPageIndex = this._viewerCore.getSettings().currentPageIndex; - var fileName = this._viewerCore.getPageName(currentPageIndex); - this._viewerCore.publish("DocumentDidLoad", currentPageIndex, fileName); - }; - - DocumentHandler.prototype.onViewDidUpdate = function (renderedPages, targetPage) - { - var currentPage = (targetPage !== null) ? - targetPage : - getCentermostPage(renderedPages, this._viewerCore.getCurrentLayout(), this._viewerCore.getViewport()); - - // Don't change the current page if there is no page in the viewport - // FIXME: Would be better to fall back to the page closest to the viewport - if (currentPage !== null) - this._viewerCore.setCurrentPage(currentPage); - - if (targetPage !== null) - this._viewerCore.publish("ViewerDidJump", targetPage); - - this._handleZoomLevelChange(); - }; - - DocumentHandler.prototype._handleZoomLevelChange = function () - { - var viewerState = this._viewerState; - var zoomLevel = viewerState.options.zoomLevel; - - // If this is not the initial load, trigger the zoom events - if (viewerState.oldZoomLevel !== zoomLevel && viewerState.oldZoomLevel >= 0) - { - if (viewerState.oldZoomLevel < zoomLevel) - { - this._viewerCore.publish("ViewerDidZoomIn", zoomLevel); - } - else - { - this._viewerCore.publish("ViewerDidZoomOut", zoomLevel); - } - - this._viewerCore.publish("ViewerDidZoom", zoomLevel); - } - - viewerState.oldZoomLevel = zoomLevel; - }; - - DocumentHandler.prototype.destroy = function () - { - this._overlays.forEach(function (overlay) - { - this._viewerCore.removePageOverlay(overlay); - }, this); - }; - - function getCentermostPage(renderedPages, layout, viewport) - { - var centerY = viewport.top + (viewport.height / 2); - var centerX = viewport.left + (viewport.width / 2); - - // Find the minimum distance from the viewport center to a page. - // Compute minus the squared distance from viewport center to the page's border. - // http://gamedev.stackexchange.com/questions/44483/how-do-i-calculate-distance-between-a-point-and-an-axis-aligned-rectangle - var centerPage = maxBy(renderedPages, function (pageIndex) - { - var dims = layout.getPageDimensions(pageIndex); - var imageOffset = layout.getPageOffset(pageIndex, {excludePadding: false}); - - var midX = imageOffset.left + (dims.width / 2); - var midY = imageOffset.top + (dims.height / 2); - - var dx = Math.max(Math.abs(centerX - midX) - (dims.width / 2), 0); - var dy = Math.max(Math.abs(centerY - midY) - (dims.height / 2), 0); - - return -(dx * dx + dy * dy); - }); - - return centerPage != null ? centerPage : null; - } - - -/***/ }), -/* 20 */ -/***/ (function(module, exports, __webpack_require__) { - - /* WEBPACK VAR INJECTION */(function(global, module) {/** - * lodash (Custom Build) - * Build: `lodash modularize exports="npm" -o ./` - * Copyright jQuery Foundation and other contributors - * Released under MIT license - * Based on Underscore.js 1.8.3 - * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors - */ - - /** Used as the size to enable large array optimizations. */ - var LARGE_ARRAY_SIZE = 200; - - /** Used as the `TypeError` message for "Functions" methods. */ - var FUNC_ERROR_TEXT = 'Expected a function'; - - /** Used to stand-in for `undefined` hash values. */ - var HASH_UNDEFINED = '__lodash_hash_undefined__'; - - /** Used to compose bitmasks for comparison styles. */ - var UNORDERED_COMPARE_FLAG = 1, - PARTIAL_COMPARE_FLAG = 2; - - /** Used as references for various `Number` constants. */ - var INFINITY = 1 / 0, - MAX_SAFE_INTEGER = 9007199254740991; - - /** `Object#toString` result references. */ - var argsTag = '[object Arguments]', - arrayTag = '[object Array]', - boolTag = '[object Boolean]', - dateTag = '[object Date]', - errorTag = '[object Error]', - funcTag = '[object Function]', - genTag = '[object GeneratorFunction]', - mapTag = '[object Map]', - numberTag = '[object Number]', - objectTag = '[object Object]', - promiseTag = '[object Promise]', - regexpTag = '[object RegExp]', - setTag = '[object Set]', - stringTag = '[object String]', - symbolTag = '[object Symbol]', - weakMapTag = '[object WeakMap]'; - - var arrayBufferTag = '[object ArrayBuffer]', - dataViewTag = '[object DataView]', - float32Tag = '[object Float32Array]', - float64Tag = '[object Float64Array]', - int8Tag = '[object Int8Array]', - int16Tag = '[object Int16Array]', - int32Tag = '[object Int32Array]', - uint8Tag = '[object Uint8Array]', - uint8ClampedTag = '[object Uint8ClampedArray]', - uint16Tag = '[object Uint16Array]', - uint32Tag = '[object Uint32Array]'; - - /** Used to match property names within property paths. */ - var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/, - reIsPlainProp = /^\w*$/, - reLeadingDot = /^\./, - rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g; - - /** - * Used to match `RegExp` - * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). - */ - var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; - - /** Used to match backslashes in property paths. */ - var reEscapeChar = /\\(\\)?/g; - - /** Used to detect host constructors (Safari). */ - var reIsHostCtor = /^\[object .+?Constructor\]$/; - - /** Used to detect unsigned integer values. */ - var reIsUint = /^(?:0|[1-9]\d*)$/; - - /** Used to identify `toStringTag` values of typed arrays. */ - var typedArrayTags = {}; - typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = - typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = - typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = - typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = - typedArrayTags[uint32Tag] = true; - typedArrayTags[argsTag] = typedArrayTags[arrayTag] = - typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = - typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = - typedArrayTags[errorTag] = typedArrayTags[funcTag] = - typedArrayTags[mapTag] = typedArrayTags[numberTag] = - typedArrayTags[objectTag] = typedArrayTags[regexpTag] = - typedArrayTags[setTag] = typedArrayTags[stringTag] = - typedArrayTags[weakMapTag] = false; - - /** Detect free variable `global` from Node.js. */ - var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; - - /** Detect free variable `self`. */ - var freeSelf = typeof self == 'object' && self && self.Object === Object && self; - - /** Used as a reference to the global object. */ - var root = freeGlobal || freeSelf || Function('return this')(); - - /** Detect free variable `exports`. */ - var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports; - - /** Detect free variable `module`. */ - var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module; - - /** Detect the popular CommonJS extension `module.exports`. */ - var moduleExports = freeModule && freeModule.exports === freeExports; - - /** Detect free variable `process` from Node.js. */ - var freeProcess = moduleExports && freeGlobal.process; - - /** Used to access faster Node.js helpers. */ - var nodeUtil = (function() { - try { - return freeProcess && freeProcess.binding('util'); - } catch (e) {} - }()); - - /* Node.js helper references. */ - var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray; - - /** - * A specialized version of `_.some` for arrays without support for iteratee - * shorthands. - * - * @private - * @param {Array} [array] The array to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {boolean} Returns `true` if any element passes the predicate check, - * else `false`. - */ - function arraySome(array, predicate) { - var index = -1, - length = array ? array.length : 0; - - while (++index < length) { - if (predicate(array[index], index, array)) { - return true; - } - } - return false; - } - - /** - * The base implementation of `_.property` without support for deep paths. - * - * @private - * @param {string} key The key of the property to get. - * @returns {Function} Returns the new accessor function. - */ - function baseProperty(key) { - return function(object) { - return object == null ? undefined : object[key]; - }; - } - - /** - * The base implementation of `_.times` without support for iteratee shorthands - * or max array length checks. - * - * @private - * @param {number} n The number of times to invoke `iteratee`. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns the array of results. - */ - function baseTimes(n, iteratee) { - var index = -1, - result = Array(n); - - while (++index < n) { - result[index] = iteratee(index); - } - return result; - } - - /** - * The base implementation of `_.unary` without support for storing metadata. - * - * @private - * @param {Function} func The function to cap arguments for. - * @returns {Function} Returns the new capped function. - */ - function baseUnary(func) { - return function(value) { - return func(value); - }; - } - - /** - * Gets the value at `key` of `object`. - * - * @private - * @param {Object} [object] The object to query. - * @param {string} key The key of the property to get. - * @returns {*} Returns the property value. - */ - function getValue(object, key) { - return object == null ? undefined : object[key]; - } - - /** - * Checks if `value` is a host object in IE < 9. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a host object, else `false`. - */ - function isHostObject(value) { - // Many host objects are `Object` objects that can coerce to strings - // despite having improperly defined `toString` methods. - var result = false; - if (value != null && typeof value.toString != 'function') { - try { - result = !!(value + ''); - } catch (e) {} - } - return result; - } - - /** - * Converts `map` to its key-value pairs. - * - * @private - * @param {Object} map The map to convert. - * @returns {Array} Returns the key-value pairs. - */ - function mapToArray(map) { - var index = -1, - result = Array(map.size); - - map.forEach(function(value, key) { - result[++index] = [key, value]; - }); - return result; - } - - /** - * Creates a unary function that invokes `func` with its argument transformed. - * - * @private - * @param {Function} func The function to wrap. - * @param {Function} transform The argument transform. - * @returns {Function} Returns the new function. - */ - function overArg(func, transform) { - return function(arg) { - return func(transform(arg)); - }; - } - - /** - * Converts `set` to an array of its values. - * - * @private - * @param {Object} set The set to convert. - * @returns {Array} Returns the values. - */ - function setToArray(set) { - var index = -1, - result = Array(set.size); - - set.forEach(function(value) { - result[++index] = value; - }); - return result; - } - - /** Used for built-in method references. */ - var arrayProto = Array.prototype, - funcProto = Function.prototype, - objectProto = Object.prototype; - - /** Used to detect overreaching core-js shims. */ - var coreJsData = root['__core-js_shared__']; - - /** Used to detect methods masquerading as native. */ - var maskSrcKey = (function() { - var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); - return uid ? ('Symbol(src)_1.' + uid) : ''; - }()); - - /** Used to resolve the decompiled source of functions. */ - var funcToString = funcProto.toString; - - /** Used to check objects for own properties. */ - var hasOwnProperty = objectProto.hasOwnProperty; - - /** - * Used to resolve the - * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) - * of values. - */ - var objectToString = objectProto.toString; - - /** Used to detect if a method is native. */ - var reIsNative = RegExp('^' + - funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&') - .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' - ); - - /** Built-in value references. */ - var Symbol = root.Symbol, - Uint8Array = root.Uint8Array, - propertyIsEnumerable = objectProto.propertyIsEnumerable, - splice = arrayProto.splice; - - /* Built-in method references for those with the same name as other `lodash` methods. */ - var nativeKeys = overArg(Object.keys, Object); - - /* Built-in method references that are verified to be native. */ - var DataView = getNative(root, 'DataView'), - Map = getNative(root, 'Map'), - Promise = getNative(root, 'Promise'), - Set = getNative(root, 'Set'), - WeakMap = getNative(root, 'WeakMap'), - nativeCreate = getNative(Object, 'create'); - - /** Used to detect maps, sets, and weakmaps. */ - var dataViewCtorString = toSource(DataView), - mapCtorString = toSource(Map), - promiseCtorString = toSource(Promise), - setCtorString = toSource(Set), - weakMapCtorString = toSource(WeakMap); - - /** Used to convert symbols to primitives and strings. */ - var symbolProto = Symbol ? Symbol.prototype : undefined, - symbolValueOf = symbolProto ? symbolProto.valueOf : undefined, - symbolToString = symbolProto ? symbolProto.toString : undefined; - - /** - * Creates a hash object. - * - * @private - * @constructor - * @param {Array} [entries] The key-value pairs to cache. - */ - function Hash(entries) { - var index = -1, - length = entries ? entries.length : 0; - - this.clear(); - while (++index < length) { - var entry = entries[index]; - this.set(entry[0], entry[1]); - } - } - - /** - * Removes all key-value entries from the hash. - * - * @private - * @name clear - * @memberOf Hash - */ - function hashClear() { - this.__data__ = nativeCreate ? nativeCreate(null) : {}; - } - - /** - * Removes `key` and its value from the hash. - * - * @private - * @name delete - * @memberOf Hash - * @param {Object} hash The hash to modify. - * @param {string} key The key of the value to remove. - * @returns {boolean} Returns `true` if the entry was removed, else `false`. - */ - function hashDelete(key) { - return this.has(key) && delete this.__data__[key]; - } - - /** - * Gets the hash value for `key`. - * - * @private - * @name get - * @memberOf Hash - * @param {string} key The key of the value to get. - * @returns {*} Returns the entry value. - */ - function hashGet(key) { - var data = this.__data__; - if (nativeCreate) { - var result = data[key]; - return result === HASH_UNDEFINED ? undefined : result; - } - return hasOwnProperty.call(data, key) ? data[key] : undefined; - } - - /** - * Checks if a hash value for `key` exists. - * - * @private - * @name has - * @memberOf Hash - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ - function hashHas(key) { - var data = this.__data__; - return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key); - } - - /** - * Sets the hash `key` to `value`. - * - * @private - * @name set - * @memberOf Hash - * @param {string} key The key of the value to set. - * @param {*} value The value to set. - * @returns {Object} Returns the hash instance. - */ - function hashSet(key, value) { - var data = this.__data__; - data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value; - return this; - } - - // Add methods to `Hash`. - Hash.prototype.clear = hashClear; - Hash.prototype['delete'] = hashDelete; - Hash.prototype.get = hashGet; - Hash.prototype.has = hashHas; - Hash.prototype.set = hashSet; - - /** - * Creates an list cache object. - * - * @private - * @constructor - * @param {Array} [entries] The key-value pairs to cache. - */ - function ListCache(entries) { - var index = -1, - length = entries ? entries.length : 0; - - this.clear(); - while (++index < length) { - var entry = entries[index]; - this.set(entry[0], entry[1]); - } - } - - /** - * Removes all key-value entries from the list cache. - * - * @private - * @name clear - * @memberOf ListCache - */ - function listCacheClear() { - this.__data__ = []; - } - - /** - * Removes `key` and its value from the list cache. - * - * @private - * @name delete - * @memberOf ListCache - * @param {string} key The key of the value to remove. - * @returns {boolean} Returns `true` if the entry was removed, else `false`. - */ - function listCacheDelete(key) { - var data = this.__data__, - index = assocIndexOf(data, key); - - if (index < 0) { - return false; - } - var lastIndex = data.length - 1; - if (index == lastIndex) { - data.pop(); - } else { - splice.call(data, index, 1); - } - return true; - } - - /** - * Gets the list cache value for `key`. - * - * @private - * @name get - * @memberOf ListCache - * @param {string} key The key of the value to get. - * @returns {*} Returns the entry value. - */ - function listCacheGet(key) { - var data = this.__data__, - index = assocIndexOf(data, key); - - return index < 0 ? undefined : data[index][1]; - } - - /** - * Checks if a list cache value for `key` exists. - * - * @private - * @name has - * @memberOf ListCache - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ - function listCacheHas(key) { - return assocIndexOf(this.__data__, key) > -1; - } - - /** - * Sets the list cache `key` to `value`. - * - * @private - * @name set - * @memberOf ListCache - * @param {string} key The key of the value to set. - * @param {*} value The value to set. - * @returns {Object} Returns the list cache instance. - */ - function listCacheSet(key, value) { - var data = this.__data__, - index = assocIndexOf(data, key); - - if (index < 0) { - data.push([key, value]); - } else { - data[index][1] = value; - } - return this; - } - - // Add methods to `ListCache`. - ListCache.prototype.clear = listCacheClear; - ListCache.prototype['delete'] = listCacheDelete; - ListCache.prototype.get = listCacheGet; - ListCache.prototype.has = listCacheHas; - ListCache.prototype.set = listCacheSet; - - /** - * Creates a map cache object to store key-value pairs. - * - * @private - * @constructor - * @param {Array} [entries] The key-value pairs to cache. - */ - function MapCache(entries) { - var index = -1, - length = entries ? entries.length : 0; - - this.clear(); - while (++index < length) { - var entry = entries[index]; - this.set(entry[0], entry[1]); - } - } - - /** - * Removes all key-value entries from the map. - * - * @private - * @name clear - * @memberOf MapCache - */ - function mapCacheClear() { - this.__data__ = { - 'hash': new Hash, - 'map': new (Map || ListCache), - 'string': new Hash - }; - } - - /** - * Removes `key` and its value from the map. - * - * @private - * @name delete - * @memberOf MapCache - * @param {string} key The key of the value to remove. - * @returns {boolean} Returns `true` if the entry was removed, else `false`. - */ - function mapCacheDelete(key) { - return getMapData(this, key)['delete'](key); - } - - /** - * Gets the map value for `key`. - * - * @private - * @name get - * @memberOf MapCache - * @param {string} key The key of the value to get. - * @returns {*} Returns the entry value. - */ - function mapCacheGet(key) { - return getMapData(this, key).get(key); - } - - /** - * Checks if a map value for `key` exists. - * - * @private - * @name has - * @memberOf MapCache - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ - function mapCacheHas(key) { - return getMapData(this, key).has(key); - } - - /** - * Sets the map `key` to `value`. - * - * @private - * @name set - * @memberOf MapCache - * @param {string} key The key of the value to set. - * @param {*} value The value to set. - * @returns {Object} Returns the map cache instance. - */ - function mapCacheSet(key, value) { - getMapData(this, key).set(key, value); - return this; - } - - // Add methods to `MapCache`. - MapCache.prototype.clear = mapCacheClear; - MapCache.prototype['delete'] = mapCacheDelete; - MapCache.prototype.get = mapCacheGet; - MapCache.prototype.has = mapCacheHas; - MapCache.prototype.set = mapCacheSet; - - /** - * - * Creates an array cache object to store unique values. - * - * @private - * @constructor - * @param {Array} [values] The values to cache. - */ - function SetCache(values) { - var index = -1, - length = values ? values.length : 0; - - this.__data__ = new MapCache; - while (++index < length) { - this.add(values[index]); - } - } - - /** - * Adds `value` to the array cache. - * - * @private - * @name add - * @memberOf SetCache - * @alias push - * @param {*} value The value to cache. - * @returns {Object} Returns the cache instance. - */ - function setCacheAdd(value) { - this.__data__.set(value, HASH_UNDEFINED); - return this; - } - - /** - * Checks if `value` is in the array cache. - * - * @private - * @name has - * @memberOf SetCache - * @param {*} value The value to search for. - * @returns {number} Returns `true` if `value` is found, else `false`. - */ - function setCacheHas(value) { - return this.__data__.has(value); - } - - // Add methods to `SetCache`. - SetCache.prototype.add = SetCache.prototype.push = setCacheAdd; - SetCache.prototype.has = setCacheHas; - - /** - * Creates a stack cache object to store key-value pairs. - * - * @private - * @constructor - * @param {Array} [entries] The key-value pairs to cache. - */ - function Stack(entries) { - this.__data__ = new ListCache(entries); - } - - /** - * Removes all key-value entries from the stack. - * - * @private - * @name clear - * @memberOf Stack - */ - function stackClear() { - this.__data__ = new ListCache; - } - - /** - * Removes `key` and its value from the stack. - * - * @private - * @name delete - * @memberOf Stack - * @param {string} key The key of the value to remove. - * @returns {boolean} Returns `true` if the entry was removed, else `false`. - */ - function stackDelete(key) { - return this.__data__['delete'](key); - } - - /** - * Gets the stack value for `key`. - * - * @private - * @name get - * @memberOf Stack - * @param {string} key The key of the value to get. - * @returns {*} Returns the entry value. - */ - function stackGet(key) { - return this.__data__.get(key); - } - - /** - * Checks if a stack value for `key` exists. - * - * @private - * @name has - * @memberOf Stack - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ - function stackHas(key) { - return this.__data__.has(key); - } - - /** - * Sets the stack `key` to `value`. - * - * @private - * @name set - * @memberOf Stack - * @param {string} key The key of the value to set. - * @param {*} value The value to set. - * @returns {Object} Returns the stack cache instance. - */ - function stackSet(key, value) { - var cache = this.__data__; - if (cache instanceof ListCache) { - var pairs = cache.__data__; - if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) { - pairs.push([key, value]); - return this; - } - cache = this.__data__ = new MapCache(pairs); - } - cache.set(key, value); - return this; - } - - // Add methods to `Stack`. - Stack.prototype.clear = stackClear; - Stack.prototype['delete'] = stackDelete; - Stack.prototype.get = stackGet; - Stack.prototype.has = stackHas; - Stack.prototype.set = stackSet; - - /** - * Creates an array of the enumerable property names of the array-like `value`. - * - * @private - * @param {*} value The value to query. - * @param {boolean} inherited Specify returning inherited property names. - * @returns {Array} Returns the array of property names. - */ - function arrayLikeKeys(value, inherited) { - // Safari 8.1 makes `arguments.callee` enumerable in strict mode. - // Safari 9 makes `arguments.length` enumerable in strict mode. - var result = (isArray(value) || isArguments(value)) - ? baseTimes(value.length, String) - : []; - - var length = result.length, - skipIndexes = !!length; - - for (var key in value) { - if ((inherited || hasOwnProperty.call(value, key)) && - !(skipIndexes && (key == 'length' || isIndex(key, length)))) { - result.push(key); - } - } - return result; - } - - /** - * Gets the index at which the `key` is found in `array` of key-value pairs. - * - * @private - * @param {Array} array The array to inspect. - * @param {*} key The key to search for. - * @returns {number} Returns the index of the matched value, else `-1`. - */ - function assocIndexOf(array, key) { - var length = array.length; - while (length--) { - if (eq(array[length][0], key)) { - return length; - } - } - return -1; - } - - /** - * The base implementation of methods like `_.max` and `_.min` which accepts a - * `comparator` to determine the extremum value. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The iteratee invoked per iteration. - * @param {Function} comparator The comparator used to compare values. - * @returns {*} Returns the extremum value. - */ - function baseExtremum(array, iteratee, comparator) { - var index = -1, - length = array.length; - - while (++index < length) { - var value = array[index], - current = iteratee(value); - - if (current != null && (computed === undefined - ? (current === current && !isSymbol(current)) - : comparator(current, computed) - )) { - var computed = current, - result = value; - } - } - return result; - } - - /** - * The base implementation of `_.get` without support for default values. - * - * @private - * @param {Object} object The object to query. - * @param {Array|string} path The path of the property to get. - * @returns {*} Returns the resolved value. - */ - function baseGet(object, path) { - path = isKey(path, object) ? [path] : castPath(path); - - var index = 0, - length = path.length; - - while (object != null && index < length) { - object = object[toKey(path[index++])]; - } - return (index && index == length) ? object : undefined; - } - - /** - * The base implementation of `getTag`. - * - * @private - * @param {*} value The value to query. - * @returns {string} Returns the `toStringTag`. - */ - function baseGetTag(value) { - return objectToString.call(value); - } - - /** - * The base implementation of `_.gt` which doesn't coerce arguments. - * - * @private - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is greater than `other`, - * else `false`. - */ - function baseGt(value, other) { - return value > other; - } - - /** - * The base implementation of `_.hasIn` without support for deep paths. - * - * @private - * @param {Object} [object] The object to query. - * @param {Array|string} key The key to check. - * @returns {boolean} Returns `true` if `key` exists, else `false`. - */ - function baseHasIn(object, key) { - return object != null && key in Object(object); - } - - /** - * The base implementation of `_.isEqual` which supports partial comparisons - * and tracks traversed objects. - * - * @private - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @param {Function} [customizer] The function to customize comparisons. - * @param {boolean} [bitmask] The bitmask of comparison flags. - * The bitmask may be composed of the following flags: - * 1 - Unordered comparison - * 2 - Partial comparison - * @param {Object} [stack] Tracks traversed `value` and `other` objects. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - */ - function baseIsEqual(value, other, customizer, bitmask, stack) { - if (value === other) { - return true; - } - if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) { - return value !== value && other !== other; - } - return baseIsEqualDeep(value, other, baseIsEqual, customizer, bitmask, stack); - } - - /** - * A specialized version of `baseIsEqual` for arrays and objects which performs - * deep comparisons and tracks traversed objects enabling objects with circular - * references to be compared. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Function} [customizer] The function to customize comparisons. - * @param {number} [bitmask] The bitmask of comparison flags. See `baseIsEqual` - * for more details. - * @param {Object} [stack] Tracks traversed `object` and `other` objects. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ - function baseIsEqualDeep(object, other, equalFunc, customizer, bitmask, stack) { - var objIsArr = isArray(object), - othIsArr = isArray(other), - objTag = arrayTag, - othTag = arrayTag; - - if (!objIsArr) { - objTag = getTag(object); - objTag = objTag == argsTag ? objectTag : objTag; - } - if (!othIsArr) { - othTag = getTag(other); - othTag = othTag == argsTag ? objectTag : othTag; - } - var objIsObj = objTag == objectTag && !isHostObject(object), - othIsObj = othTag == objectTag && !isHostObject(other), - isSameTag = objTag == othTag; - - if (isSameTag && !objIsObj) { - stack || (stack = new Stack); - return (objIsArr || isTypedArray(object)) - ? equalArrays(object, other, equalFunc, customizer, bitmask, stack) - : equalByTag(object, other, objTag, equalFunc, customizer, bitmask, stack); - } - if (!(bitmask & PARTIAL_COMPARE_FLAG)) { - var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), - othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); - - if (objIsWrapped || othIsWrapped) { - var objUnwrapped = objIsWrapped ? object.value() : object, - othUnwrapped = othIsWrapped ? other.value() : other; - - stack || (stack = new Stack); - return equalFunc(objUnwrapped, othUnwrapped, customizer, bitmask, stack); - } - } - if (!isSameTag) { - return false; - } - stack || (stack = new Stack); - return equalObjects(object, other, equalFunc, customizer, bitmask, stack); - } - - /** - * The base implementation of `_.isMatch` without support for iteratee shorthands. - * - * @private - * @param {Object} object The object to inspect. - * @param {Object} source The object of property values to match. - * @param {Array} matchData The property names, values, and compare flags to match. - * @param {Function} [customizer] The function to customize comparisons. - * @returns {boolean} Returns `true` if `object` is a match, else `false`. - */ - function baseIsMatch(object, source, matchData, customizer) { - var index = matchData.length, - length = index, - noCustomizer = !customizer; - - if (object == null) { - return !length; - } - object = Object(object); - while (index--) { - var data = matchData[index]; - if ((noCustomizer && data[2]) - ? data[1] !== object[data[0]] - : !(data[0] in object) - ) { - return false; - } - } - while (++index < length) { - data = matchData[index]; - var key = data[0], - objValue = object[key], - srcValue = data[1]; - - if (noCustomizer && data[2]) { - if (objValue === undefined && !(key in object)) { - return false; - } - } else { - var stack = new Stack; - if (customizer) { - var result = customizer(objValue, srcValue, key, object, source, stack); - } - if (!(result === undefined - ? baseIsEqual(srcValue, objValue, customizer, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG, stack) - : result - )) { - return false; - } - } - } - return true; - } - - /** - * The base implementation of `_.isNative` without bad shim checks. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a native function, - * else `false`. - */ - function baseIsNative(value) { - if (!isObject(value) || isMasked(value)) { - return false; - } - var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor; - return pattern.test(toSource(value)); - } - - /** - * The base implementation of `_.isTypedArray` without Node.js optimizations. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. - */ - function baseIsTypedArray(value) { - return isObjectLike(value) && - isLength(value.length) && !!typedArrayTags[objectToString.call(value)]; - } - - /** - * The base implementation of `_.iteratee`. - * - * @private - * @param {*} [value=_.identity] The value to convert to an iteratee. - * @returns {Function} Returns the iteratee. - */ - function baseIteratee(value) { - // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9. - // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details. - if (typeof value == 'function') { - return value; - } - if (value == null) { - return identity; - } - if (typeof value == 'object') { - return isArray(value) - ? baseMatchesProperty(value[0], value[1]) - : baseMatches(value); - } - return property(value); - } - - /** - * The base implementation of `_.keys` which doesn't treat sparse arrays as dense. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - */ - function baseKeys(object) { - if (!isPrototype(object)) { - return nativeKeys(object); - } - var result = []; - for (var key in Object(object)) { - if (hasOwnProperty.call(object, key) && key != 'constructor') { - result.push(key); - } - } - return result; - } - - /** - * The base implementation of `_.matches` which doesn't clone `source`. - * - * @private - * @param {Object} source The object of property values to match. - * @returns {Function} Returns the new spec function. - */ - function baseMatches(source) { - var matchData = getMatchData(source); - if (matchData.length == 1 && matchData[0][2]) { - return matchesStrictComparable(matchData[0][0], matchData[0][1]); - } - return function(object) { - return object === source || baseIsMatch(object, source, matchData); - }; - } - - /** - * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`. - * - * @private - * @param {string} path The path of the property to get. - * @param {*} srcValue The value to match. - * @returns {Function} Returns the new spec function. - */ - function baseMatchesProperty(path, srcValue) { - if (isKey(path) && isStrictComparable(srcValue)) { - return matchesStrictComparable(toKey(path), srcValue); - } - return function(object) { - var objValue = get(object, path); - return (objValue === undefined && objValue === srcValue) - ? hasIn(object, path) - : baseIsEqual(srcValue, objValue, undefined, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG); - }; - } - - /** - * A specialized version of `baseProperty` which supports deep paths. - * - * @private - * @param {Array|string} path The path of the property to get. - * @returns {Function} Returns the new accessor function. - */ - function basePropertyDeep(path) { - return function(object) { - return baseGet(object, path); - }; - } - - /** - * The base implementation of `_.toString` which doesn't convert nullish - * values to empty strings. - * - * @private - * @param {*} value The value to process. - * @returns {string} Returns the string. - */ - function baseToString(value) { - // Exit early for strings to avoid a performance hit in some environments. - if (typeof value == 'string') { - return value; - } - if (isSymbol(value)) { - return symbolToString ? symbolToString.call(value) : ''; - } - var result = (value + ''); - return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; - } - - /** - * Casts `value` to a path array if it's not one. - * - * @private - * @param {*} value The value to inspect. - * @returns {Array} Returns the cast property path array. - */ - function castPath(value) { - return isArray(value) ? value : stringToPath(value); - } - - /** - * A specialized version of `baseIsEqualDeep` for arrays with support for - * partial deep comparisons. - * - * @private - * @param {Array} array The array to compare. - * @param {Array} other The other array to compare. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Function} customizer The function to customize comparisons. - * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual` - * for more details. - * @param {Object} stack Tracks traversed `array` and `other` objects. - * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. - */ - function equalArrays(array, other, equalFunc, customizer, bitmask, stack) { - var isPartial = bitmask & PARTIAL_COMPARE_FLAG, - arrLength = array.length, - othLength = other.length; - - if (arrLength != othLength && !(isPartial && othLength > arrLength)) { - return false; - } - // Assume cyclic values are equal. - var stacked = stack.get(array); - if (stacked && stack.get(other)) { - return stacked == other; - } - var index = -1, - result = true, - seen = (bitmask & UNORDERED_COMPARE_FLAG) ? new SetCache : undefined; - - stack.set(array, other); - stack.set(other, array); - - // Ignore non-index properties. - while (++index < arrLength) { - var arrValue = array[index], - othValue = other[index]; - - if (customizer) { - var compared = isPartial - ? customizer(othValue, arrValue, index, other, array, stack) - : customizer(arrValue, othValue, index, array, other, stack); - } - if (compared !== undefined) { - if (compared) { - continue; - } - result = false; - break; - } - // Recursively compare arrays (susceptible to call stack limits). - if (seen) { - if (!arraySome(other, function(othValue, othIndex) { - if (!seen.has(othIndex) && - (arrValue === othValue || equalFunc(arrValue, othValue, customizer, bitmask, stack))) { - return seen.add(othIndex); - } - })) { - result = false; - break; - } - } else if (!( - arrValue === othValue || - equalFunc(arrValue, othValue, customizer, bitmask, stack) - )) { - result = false; - break; - } - } - stack['delete'](array); - stack['delete'](other); - return result; - } - - /** - * A specialized version of `baseIsEqualDeep` for comparing objects of - * the same `toStringTag`. - * - * **Note:** This function only supports comparing values with tags of - * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {string} tag The `toStringTag` of the objects to compare. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Function} customizer The function to customize comparisons. - * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual` - * for more details. - * @param {Object} stack Tracks traversed `object` and `other` objects. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ - function equalByTag(object, other, tag, equalFunc, customizer, bitmask, stack) { - switch (tag) { - case dataViewTag: - if ((object.byteLength != other.byteLength) || - (object.byteOffset != other.byteOffset)) { - return false; - } - object = object.buffer; - other = other.buffer; - - case arrayBufferTag: - if ((object.byteLength != other.byteLength) || - !equalFunc(new Uint8Array(object), new Uint8Array(other))) { - return false; - } - return true; - - case boolTag: - case dateTag: - case numberTag: - // Coerce booleans to `1` or `0` and dates to milliseconds. - // Invalid dates are coerced to `NaN`. - return eq(+object, +other); - - case errorTag: - return object.name == other.name && object.message == other.message; - - case regexpTag: - case stringTag: - // Coerce regexes to strings and treat strings, primitives and objects, - // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring - // for more details. - return object == (other + ''); - - case mapTag: - var convert = mapToArray; - - case setTag: - var isPartial = bitmask & PARTIAL_COMPARE_FLAG; - convert || (convert = setToArray); - - if (object.size != other.size && !isPartial) { - return false; - } - // Assume cyclic values are equal. - var stacked = stack.get(object); - if (stacked) { - return stacked == other; - } - bitmask |= UNORDERED_COMPARE_FLAG; - - // Recursively compare objects (susceptible to call stack limits). - stack.set(object, other); - var result = equalArrays(convert(object), convert(other), equalFunc, customizer, bitmask, stack); - stack['delete'](object); - return result; - - case symbolTag: - if (symbolValueOf) { - return symbolValueOf.call(object) == symbolValueOf.call(other); - } - } - return false; - } - - /** - * A specialized version of `baseIsEqualDeep` for objects with support for - * partial deep comparisons. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Function} customizer The function to customize comparisons. - * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual` - * for more details. - * @param {Object} stack Tracks traversed `object` and `other` objects. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ - function equalObjects(object, other, equalFunc, customizer, bitmask, stack) { - var isPartial = bitmask & PARTIAL_COMPARE_FLAG, - objProps = keys(object), - objLength = objProps.length, - othProps = keys(other), - othLength = othProps.length; - - if (objLength != othLength && !isPartial) { - return false; - } - var index = objLength; - while (index--) { - var key = objProps[index]; - if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) { - return false; - } - } - // Assume cyclic values are equal. - var stacked = stack.get(object); - if (stacked && stack.get(other)) { - return stacked == other; - } - var result = true; - stack.set(object, other); - stack.set(other, object); - - var skipCtor = isPartial; - while (++index < objLength) { - key = objProps[index]; - var objValue = object[key], - othValue = other[key]; - - if (customizer) { - var compared = isPartial - ? customizer(othValue, objValue, key, other, object, stack) - : customizer(objValue, othValue, key, object, other, stack); - } - // Recursively compare objects (susceptible to call stack limits). - if (!(compared === undefined - ? (objValue === othValue || equalFunc(objValue, othValue, customizer, bitmask, stack)) - : compared - )) { - result = false; - break; - } - skipCtor || (skipCtor = key == 'constructor'); - } - if (result && !skipCtor) { - var objCtor = object.constructor, - othCtor = other.constructor; - - // Non `Object` object instances with different constructors are not equal. - if (objCtor != othCtor && - ('constructor' in object && 'constructor' in other) && - !(typeof objCtor == 'function' && objCtor instanceof objCtor && - typeof othCtor == 'function' && othCtor instanceof othCtor)) { - result = false; - } - } - stack['delete'](object); - stack['delete'](other); - return result; - } - - /** - * Gets the data for `map`. - * - * @private - * @param {Object} map The map to query. - * @param {string} key The reference key. - * @returns {*} Returns the map data. - */ - function getMapData(map, key) { - var data = map.__data__; - return isKeyable(key) - ? data[typeof key == 'string' ? 'string' : 'hash'] - : data.map; - } - - /** - * Gets the property names, values, and compare flags of `object`. - * - * @private - * @param {Object} object The object to query. - * @returns {Array} Returns the match data of `object`. - */ - function getMatchData(object) { - var result = keys(object), - length = result.length; - - while (length--) { - var key = result[length], - value = object[key]; - - result[length] = [key, value, isStrictComparable(value)]; - } - return result; - } - - /** - * Gets the native function at `key` of `object`. - * - * @private - * @param {Object} object The object to query. - * @param {string} key The key of the method to get. - * @returns {*} Returns the function if it's native, else `undefined`. - */ - function getNative(object, key) { - var value = getValue(object, key); - return baseIsNative(value) ? value : undefined; - } - - /** - * Gets the `toStringTag` of `value`. - * - * @private - * @param {*} value The value to query. - * @returns {string} Returns the `toStringTag`. - */ - var getTag = baseGetTag; - - // Fallback for data views, maps, sets, and weak maps in IE 11, - // for data views in Edge < 14, and promises in Node.js. - if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) || - (Map && getTag(new Map) != mapTag) || - (Promise && getTag(Promise.resolve()) != promiseTag) || - (Set && getTag(new Set) != setTag) || - (WeakMap && getTag(new WeakMap) != weakMapTag)) { - getTag = function(value) { - var result = objectToString.call(value), - Ctor = result == objectTag ? value.constructor : undefined, - ctorString = Ctor ? toSource(Ctor) : undefined; - - if (ctorString) { - switch (ctorString) { - case dataViewCtorString: return dataViewTag; - case mapCtorString: return mapTag; - case promiseCtorString: return promiseTag; - case setCtorString: return setTag; - case weakMapCtorString: return weakMapTag; - } - } - return result; - }; - } - - /** - * Checks if `path` exists on `object`. - * - * @private - * @param {Object} object The object to query. - * @param {Array|string} path The path to check. - * @param {Function} hasFunc The function to check properties. - * @returns {boolean} Returns `true` if `path` exists, else `false`. - */ - function hasPath(object, path, hasFunc) { - path = isKey(path, object) ? [path] : castPath(path); - - var result, - index = -1, - length = path.length; - - while (++index < length) { - var key = toKey(path[index]); - if (!(result = object != null && hasFunc(object, key))) { - break; - } - object = object[key]; - } - if (result) { - return result; - } - var length = object ? object.length : 0; - return !!length && isLength(length) && isIndex(key, length) && - (isArray(object) || isArguments(object)); - } - - /** - * Checks if `value` is a valid array-like index. - * - * @private - * @param {*} value The value to check. - * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. - * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. - */ - function isIndex(value, length) { - length = length == null ? MAX_SAFE_INTEGER : length; - return !!length && - (typeof value == 'number' || reIsUint.test(value)) && - (value > -1 && value % 1 == 0 && value < length); - } - - /** - * Checks if `value` is a property name and not a property path. - * - * @private - * @param {*} value The value to check. - * @param {Object} [object] The object to query keys on. - * @returns {boolean} Returns `true` if `value` is a property name, else `false`. - */ - function isKey(value, object) { - if (isArray(value)) { - return false; - } - var type = typeof value; - if (type == 'number' || type == 'symbol' || type == 'boolean' || - value == null || isSymbol(value)) { - return true; - } - return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || - (object != null && value in Object(object)); - } - - /** - * Checks if `value` is suitable for use as unique object key. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is suitable, else `false`. - */ - function isKeyable(value) { - var type = typeof value; - return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean') - ? (value !== '__proto__') - : (value === null); - } - - /** - * Checks if `func` has its source masked. - * - * @private - * @param {Function} func The function to check. - * @returns {boolean} Returns `true` if `func` is masked, else `false`. - */ - function isMasked(func) { - return !!maskSrcKey && (maskSrcKey in func); - } - - /** - * Checks if `value` is likely a prototype object. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. - */ - function isPrototype(value) { - var Ctor = value && value.constructor, - proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; - - return value === proto; - } - - /** - * Checks if `value` is suitable for strict equality comparisons, i.e. `===`. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` if suitable for strict - * equality comparisons, else `false`. - */ - function isStrictComparable(value) { - return value === value && !isObject(value); - } - - /** - * A specialized version of `matchesProperty` for source values suitable - * for strict equality comparisons, i.e. `===`. - * - * @private - * @param {string} key The key of the property to get. - * @param {*} srcValue The value to match. - * @returns {Function} Returns the new spec function. - */ - function matchesStrictComparable(key, srcValue) { - return function(object) { - if (object == null) { - return false; - } - return object[key] === srcValue && - (srcValue !== undefined || (key in Object(object))); - }; - } - - /** - * Converts `string` to a property path array. - * - * @private - * @param {string} string The string to convert. - * @returns {Array} Returns the property path array. - */ - var stringToPath = memoize(function(string) { - string = toString(string); - - var result = []; - if (reLeadingDot.test(string)) { - result.push(''); - } - string.replace(rePropName, function(match, number, quote, string) { - result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match)); - }); - return result; - }); - - /** - * Converts `value` to a string key if it's not a string or symbol. - * - * @private - * @param {*} value The value to inspect. - * @returns {string|symbol} Returns the key. - */ - function toKey(value) { - if (typeof value == 'string' || isSymbol(value)) { - return value; - } - var result = (value + ''); - return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; - } - - /** - * Converts `func` to its source code. - * - * @private - * @param {Function} func The function to process. - * @returns {string} Returns the source code. - */ - function toSource(func) { - if (func != null) { - try { - return funcToString.call(func); - } catch (e) {} - try { - return (func + ''); - } catch (e) {} - } - return ''; - } - - /** - * Creates a function that memoizes the result of `func`. If `resolver` is - * provided, it determines the cache key for storing the result based on the - * arguments provided to the memoized function. By default, the first argument - * provided to the memoized function is used as the map cache key. The `func` - * is invoked with the `this` binding of the memoized function. - * - * **Note:** The cache is exposed as the `cache` property on the memoized - * function. Its creation may be customized by replacing the `_.memoize.Cache` - * constructor with one whose instances implement the - * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object) - * method interface of `delete`, `get`, `has`, and `set`. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Function - * @param {Function} func The function to have its output memoized. - * @param {Function} [resolver] The function to resolve the cache key. - * @returns {Function} Returns the new memoized function. - * @example - * - * var object = { 'a': 1, 'b': 2 }; - * var other = { 'c': 3, 'd': 4 }; - * - * var values = _.memoize(_.values); - * values(object); - * // => [1, 2] - * - * values(other); - * // => [3, 4] - * - * object.a = 2; - * values(object); - * // => [1, 2] - * - * // Modify the result cache. - * values.cache.set(object, ['a', 'b']); - * values(object); - * // => ['a', 'b'] - * - * // Replace `_.memoize.Cache`. - * _.memoize.Cache = WeakMap; - */ - function memoize(func, resolver) { - if (typeof func != 'function' || (resolver && typeof resolver != 'function')) { - throw new TypeError(FUNC_ERROR_TEXT); - } - var memoized = function() { - var args = arguments, - key = resolver ? resolver.apply(this, args) : args[0], - cache = memoized.cache; - - if (cache.has(key)) { - return cache.get(key); - } - var result = func.apply(this, args); - memoized.cache = cache.set(key, result); - return result; - }; - memoized.cache = new (memoize.Cache || MapCache); - return memoized; - } - - // Assign cache to `_.memoize`. - memoize.Cache = MapCache; - - /** - * Performs a - * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * comparison between two values to determine if they are equivalent. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - * @example - * - * var object = { 'a': 1 }; - * var other = { 'a': 1 }; - * - * _.eq(object, object); - * // => true - * - * _.eq(object, other); - * // => false - * - * _.eq('a', 'a'); - * // => true - * - * _.eq('a', Object('a')); - * // => false - * - * _.eq(NaN, NaN); - * // => true - */ - function eq(value, other) { - return value === other || (value !== value && other !== other); - } - - /** - * Checks if `value` is likely an `arguments` object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an `arguments` object, - * else `false`. - * @example - * - * _.isArguments(function() { return arguments; }()); - * // => true - * - * _.isArguments([1, 2, 3]); - * // => false - */ - function isArguments(value) { - // Safari 8.1 makes `arguments.callee` enumerable in strict mode. - return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') && - (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag); - } - - /** - * Checks if `value` is classified as an `Array` object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an array, else `false`. - * @example - * - * _.isArray([1, 2, 3]); - * // => true - * - * _.isArray(document.body.children); - * // => false - * - * _.isArray('abc'); - * // => false - * - * _.isArray(_.noop); - * // => false - */ - var isArray = Array.isArray; - - /** - * Checks if `value` is array-like. A value is considered array-like if it's - * not a function and has a `value.length` that's an integer greater than or - * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is array-like, else `false`. - * @example - * - * _.isArrayLike([1, 2, 3]); - * // => true - * - * _.isArrayLike(document.body.children); - * // => true - * - * _.isArrayLike('abc'); - * // => true - * - * _.isArrayLike(_.noop); - * // => false - */ - function isArrayLike(value) { - return value != null && isLength(value.length) && !isFunction(value); - } - - /** - * This method is like `_.isArrayLike` except that it also checks if `value` - * is an object. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an array-like object, - * else `false`. - * @example - * - * _.isArrayLikeObject([1, 2, 3]); - * // => true - * - * _.isArrayLikeObject(document.body.children); - * // => true - * - * _.isArrayLikeObject('abc'); - * // => false - * - * _.isArrayLikeObject(_.noop); - * // => false - */ - function isArrayLikeObject(value) { - return isObjectLike(value) && isArrayLike(value); - } - - /** - * Checks if `value` is classified as a `Function` object. - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a function, else `false`. - * @example - * - * _.isFunction(_); - * // => true - * - * _.isFunction(/abc/); - * // => false - */ - function isFunction(value) { - // The use of `Object#toString` avoids issues with the `typeof` operator - // in Safari 8-9 which returns 'object' for typed array and other constructors. - var tag = isObject(value) ? objectToString.call(value) : ''; - return tag == funcTag || tag == genTag; - } - - /** - * Checks if `value` is a valid array-like length. - * - * **Note:** This method is loosely based on - * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. - * @example - * - * _.isLength(3); - * // => true - * - * _.isLength(Number.MIN_VALUE); - * // => false - * - * _.isLength(Infinity); - * // => false - * - * _.isLength('3'); - * // => false - */ - function isLength(value) { - return typeof value == 'number' && - value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; - } - - /** - * Checks if `value` is the - * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) - * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) - * - * @static - * @memberOf _ - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an object, else `false`. - * @example - * - * _.isObject({}); - * // => true - * - * _.isObject([1, 2, 3]); - * // => true - * - * _.isObject(_.noop); - * // => true - * - * _.isObject(null); - * // => false - */ - function isObject(value) { - var type = typeof value; - return !!value && (type == 'object' || type == 'function'); - } - - /** - * Checks if `value` is object-like. A value is object-like if it's not `null` - * and has a `typeof` result of "object". - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is object-like, else `false`. - * @example - * - * _.isObjectLike({}); - * // => true - * - * _.isObjectLike([1, 2, 3]); - * // => true - * - * _.isObjectLike(_.noop); - * // => false - * - * _.isObjectLike(null); - * // => false - */ - function isObjectLike(value) { - return !!value && typeof value == 'object'; - } - - /** - * Checks if `value` is classified as a `Symbol` primitive or object. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. - * @example - * - * _.isSymbol(Symbol.iterator); - * // => true - * - * _.isSymbol('abc'); - * // => false - */ - function isSymbol(value) { - return typeof value == 'symbol' || - (isObjectLike(value) && objectToString.call(value) == symbolTag); - } - - /** - * Checks if `value` is classified as a typed array. - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. - * @example - * - * _.isTypedArray(new Uint8Array); - * // => true - * - * _.isTypedArray([]); - * // => false - */ - var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray; - - /** - * Converts `value` to a string. An empty string is returned for `null` - * and `undefined` values. The sign of `-0` is preserved. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Lang - * @param {*} value The value to process. - * @returns {string} Returns the string. - * @example - * - * _.toString(null); - * // => '' - * - * _.toString(-0); - * // => '-0' - * - * _.toString([1, 2, 3]); - * // => '1,2,3' - */ - function toString(value) { - return value == null ? '' : baseToString(value); - } - - /** - * Gets the value at `path` of `object`. If the resolved value is - * `undefined`, the `defaultValue` is returned in its place. - * - * @static - * @memberOf _ - * @since 3.7.0 - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path of the property to get. - * @param {*} [defaultValue] The value returned for `undefined` resolved values. - * @returns {*} Returns the resolved value. - * @example - * - * var object = { 'a': [{ 'b': { 'c': 3 } }] }; - * - * _.get(object, 'a[0].b.c'); - * // => 3 - * - * _.get(object, ['a', '0', 'b', 'c']); - * // => 3 - * - * _.get(object, 'a.b.c', 'default'); - * // => 'default' - */ - function get(object, path, defaultValue) { - var result = object == null ? undefined : baseGet(object, path); - return result === undefined ? defaultValue : result; - } - - /** - * Checks if `path` is a direct or inherited property of `object`. - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path to check. - * @returns {boolean} Returns `true` if `path` exists, else `false`. - * @example - * - * var object = _.create({ 'a': _.create({ 'b': 2 }) }); - * - * _.hasIn(object, 'a'); - * // => true - * - * _.hasIn(object, 'a.b'); - * // => true - * - * _.hasIn(object, ['a', 'b']); - * // => true - * - * _.hasIn(object, 'b'); - * // => false - */ - function hasIn(object, path) { - return object != null && hasPath(object, path, baseHasIn); - } - - /** - * Creates an array of the own enumerable property names of `object`. - * - * **Note:** Non-object values are coerced to objects. See the - * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) - * for more details. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.keys(new Foo); - * // => ['a', 'b'] (iteration order is not guaranteed) - * - * _.keys('hi'); - * // => ['0', '1'] - */ - function keys(object) { - return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); - } - - /** - * This method returns the first argument it receives. - * - * @static - * @since 0.1.0 - * @memberOf _ - * @category Util - * @param {*} value Any value. - * @returns {*} Returns `value`. - * @example - * - * var object = { 'a': 1 }; - * - * console.log(_.identity(object) === object); - * // => true - */ - function identity(value) { - return value; - } - - /** - * Creates a function that returns the value at `path` of a given object. - * - * @static - * @memberOf _ - * @since 2.4.0 - * @category Util - * @param {Array|string} path The path of the property to get. - * @returns {Function} Returns the new accessor function. - * @example - * - * var objects = [ - * { 'a': { 'b': 2 } }, - * { 'a': { 'b': 1 } } - * ]; - * - * _.map(objects, _.property('a.b')); - * // => [2, 1] - * - * _.map(_.sortBy(objects, _.property(['a', 'b'])), 'a.b'); - * // => [1, 2] - */ - function property(path) { - return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path); - } - - /** - * This method is like `_.max` except that it accepts `iteratee` which is - * invoked for each element in `array` to generate the criterion by which - * the value is ranked. The iteratee is invoked with one argument: (value). - * - * @static - * @memberOf _ - * @since 4.0.0 - * @category Math - * @param {Array} array The array to iterate over. - * @param {Function} [iteratee=_.identity] The iteratee invoked per element. - * @returns {*} Returns the maximum value. - * @example - * - * var objects = [{ 'n': 1 }, { 'n': 2 }]; - * - * _.maxBy(objects, function(o) { return o.n; }); - * // => { 'n': 2 } - * - * // The `_.property` iteratee shorthand. - * _.maxBy(objects, 'n'); - * // => { 'n': 2 } - */ - function maxBy(array, iteratee) { - return (array && array.length) - ? baseExtremum(array, baseIteratee(iteratee, 2), baseGt) - : undefined; - } - - module.exports = maxBy; - - /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()), __webpack_require__(21)(module))) - -/***/ }), -/* 21 */ -/***/ (function(module, exports) { - - module.exports = function(module) { - if(!module.webpackPolyfill) { - module.deprecate = function() {}; - module.paths = []; - // module.parent = undefined by default - module.children = []; - module.webpackPolyfill = 1; - } - return module; - } - - -/***/ }), -/* 22 */ -/***/ (function(module, exports, __webpack_require__) { - - var elt = __webpack_require__(9); - - module.exports = PageToolsOverlay; - - function PageToolsOverlay(pageIndex, viewerCore) - { - this.page = pageIndex; - - this._viewerCore = viewerCore; - - this._innerElement = viewerCore.getSettings().innerElement; - this._pageToolsElem = null; - } - - PageToolsOverlay.prototype.mount = function () - { - if (this._pageToolsElem === null) - { - var buttons = this._initializePageToolButtons(); - - this._pageToolsElem = elt('div', {class: 'diva-page-tools-wrapper'}, - elt('div', {class: 'diva-page-tools'}, buttons) - ); - } - - this.refresh(); - this._innerElement.appendChild(this._pageToolsElem); - }; - - PageToolsOverlay.prototype._initializePageToolButtons = function () - { - // Callback parameters - var settings = this._viewerCore.getSettings(); - var publicInstance = this._viewerCore.getPublicInstance(); - var pageIndex = this.page; - - return this._viewerCore.getPageTools().map(function (plugin) - { - // If the title text is undefined, use the name of the plugin - var titleText = plugin.titleText || plugin.pluginName[0].toUpperCase() + plugin.pluginName.substring(1) + " plugin"; - - var button = elt('div', { - class: 'diva-' + plugin.pluginName + '-icon', - title: titleText - }); - - button.addEventListener('click', function (event) - { - plugin.handleClick.call(this, event, settings, publicInstance, pageIndex); - }, false); - - button.addEventListener('touchend', function (event) - { - // Prevent firing of emulated mouse events - event.preventDefault(); - - plugin.handleClick.call(this, event, settings, publicInstance, pageIndex); - }, false); - - return button; - }, this); - }; - - PageToolsOverlay.prototype.unmount = function () - { - this._innerElement.removeChild(this._pageToolsElem); - }; - - PageToolsOverlay.prototype.refresh = function () - { - var pos = this._viewerCore.getPageRegion(this.page, { - excludePadding: true, - incorporateViewport: true - }); - - this._pageToolsElem.style.top = pos.top + 'px'; - this._pageToolsElem.style.left = pos.left + 'px'; - }; - - -/***/ }), -/* 23 */ -/***/ (function(module, exports, __webpack_require__) { - - var maxBy = __webpack_require__(20); - - module.exports = GridHandler; - - function GridHandler(viewerCore) - { - this._viewerCore = viewerCore; - } - - // USER EVENTS - GridHandler.prototype.onDoubleClick = function (event, coords) - { - var position = this._viewerCore.getPagePositionAtViewportOffset(coords); - - var layout = this._viewerCore.getCurrentLayout(); - var viewport = this._viewerCore.getViewport(); - var pageToViewportCenterOffset = layout.getPageToViewportCenterOffset(position.anchorPage, viewport); - - this._viewerCore.reload({ - inGrid: false, - goDirectlyTo: position.anchorPage, - horizontalOffset: pageToViewportCenterOffset.x + position.offset.left, - verticalOffset: pageToViewportCenterOffset.y + position.offset.top - }); - }; - - GridHandler.prototype.onPinch = function () - { - this._viewerCore.reload({ inGrid: false }); - }; - - // VIEW EVENTS - GridHandler.prototype.onViewWillLoad = function () - { - // FIXME(wabain): Should something happen here? - /* No-op */ - }; - - GridHandler.prototype.onViewDidLoad = function () - { - // FIXME(wabain): Should something happen here? - /* No-op */ - }; - - GridHandler.prototype.onViewDidUpdate = function (renderedPages, targetPage) - { - // return early if there are no rendered pages in view. - if (renderedPages.length === 0) return; - - if (targetPage !== null) - { - this._viewerCore.setCurrentPage(targetPage); - return; - } - - // Select the current page from the first row if it is fully visible, or from - // the second row if it is fully visible, or from the centermost row otherwise. - // If the current page is in that group then don't change it. Otherwise, set - // the current page to the group's first page. - - var layout = this._viewerCore.getCurrentLayout(); - var groups = []; - - renderedPages.forEach(function (pageIndex) - { - var group = layout.getPageInfo(pageIndex).group; - if (groups.length === 0 || group !== groups[groups.length - 1]) - groups.push(group); - }); - - var viewport = this._viewerCore.getViewport(); - var chosenGroup; - - if (groups.length === 1 || groups[0].region.top >= viewport.top) - chosenGroup = groups[0]; - else if (groups[1].region.bottom <= viewport.bottom) - chosenGroup = groups[1]; - else - chosenGroup = getCentermostGroup(groups, viewport); - - var currentPage = this._viewerCore.getSettings().currentPageIndex; - - var hasCurrentPage = chosenGroup.pages.some(function (page) - { - return page.index === currentPage; - }); - - if (!hasCurrentPage) - this._viewerCore.setCurrentPage(chosenGroup.pages[0].index); - }; - - GridHandler.prototype.destroy = function () - { - // No-op - }; - - function getCentermostGroup(groups, viewport) - { - var viewportMiddle = viewport.top + viewport.height / 2; - - return maxBy(groups, function (group) - { - var groupMiddle = group.region.top + group.dimensions.height / 2; - return -Math.abs(viewportMiddle - groupMiddle); - }); - } - - -/***/ }), -/* 24 */ -/***/ (function(module, exports) { - - module.exports = PageOverlayManager; - - /** - * Manages a collection of page overlays, which implement a low-level - * API for synchronizing HTML pages to the canvas. Each overlay needs - * to implement the following protocol: - * - * mount(): Called when a page is first rendered - * refresh(): Called when a page is moved - * unmount(): Called when a previously rendered page has stopped being rendered - * - * @class - */ - - function PageOverlayManager() - { - this._pages = {}; - this._renderedPages = []; - this._renderedPageMap = {}; - } - - PageOverlayManager.prototype.addOverlay = function (overlay) - { - var overlaysByPage = this._pages[overlay.page] || (this._pages[overlay.page] = []); - - overlaysByPage.push(overlay); - - if (this._renderedPageMap[overlay.page]) - overlay.mount(); - }; - - PageOverlayManager.prototype.removeOverlay = function (overlay) - { - var page = overlay.page; - var overlaysByPage = this._pages[page]; - - if (!overlaysByPage) - return; - - var overlayIndex = overlaysByPage.indexOf(overlay); - - if (overlayIndex === -1) - return; - - if (this._renderedPageMap[page]) - overlaysByPage[overlayIndex].unmount(); - - overlaysByPage.splice(overlayIndex, 1); - - if (overlaysByPage.length === 0) - delete this._pages[page]; - }; - - PageOverlayManager.prototype.updateOverlays = function (renderedPages) - { - var previouslyRendered = this._renderedPages; - var newRenderedMap = {}; - - renderedPages.forEach(function (pageIndex) - { - newRenderedMap[pageIndex] = true; - - if (!this._renderedPageMap[pageIndex]) - { - this._renderedPageMap[pageIndex] = true; - - this._invokeOnOverlays(pageIndex, function (overlay) - { - overlay.mount(); - }); - } - }, this); - - previouslyRendered.forEach(function (pageIndex) - { - if (newRenderedMap[pageIndex]) - { - this._invokeOnOverlays(pageIndex, function (overlay) - { - overlay.refresh(); - }); - } - else - { - delete this._renderedPageMap[pageIndex]; - - this._invokeOnOverlays(pageIndex, function (overlay) - { - overlay.unmount(); - }); - } - }, this); - - this._renderedPages = renderedPages; - }; - - PageOverlayManager.prototype._invokeOnOverlays = function (pageIndex, func) - { - var overlays = this._pages[pageIndex]; - if (overlays) - overlays.forEach(func, this); - }; - - -/***/ }), -/* 25 */ -/***/ (function(module, exports, __webpack_require__) { - - 'use strict'; - - var debug = __webpack_require__(26)('diva:Renderer'); - var debugPaints = __webpack_require__(26)('diva:Renderer:paints'); - - var elt = __webpack_require__(9); - - var CompositeImage = __webpack_require__(30); - var DocumentLayout = __webpack_require__(31); - var ImageCache = __webpack_require__(32); - var ImageRequestHandler = __webpack_require__(33); - var InterpolateAnimation = __webpack_require__(34); - - var REQUEST_DEBOUNCE_INTERVAL = 250; - - - module.exports = Renderer; - - function Renderer(options, hooks) - { - this._viewport = options.viewport; - this._outerElement = options.outerElement; - this._documentElement = options.innerElement; - - this._hooks = hooks || {}; - - this._canvas = elt('canvas', { class: 'diva-viewer-canvas', tabindex: '1' }); - this._ctx = this._canvas.getContext('2d'); - - this.layout = null; - - this._sourceResolver = null; - this._renderedPages = null; - this._config = null; - this._zoomLevel = null; - this._compositeImages = null; - this._renderedTiles = null; - this._animation = null; - - // FIXME(wabain): What level should this be maintained at? - // Diva global? - this._cache = new ImageCache(); - this._pendingRequests = {}; - } - - Renderer.getCompatibilityErrors = function () - { - if (typeof HTMLCanvasElement !== 'undefined') - return null; - - return [ - 'Your browser lacks support for the ', elt('pre', 'canvas'), - ' element. Please upgrade your browser.' - ]; - }; - - /* - * Method supplied by Joseph Jezerinac, https://github.com/jezerinac - * - * https://github.com/DDMAL/diva.js/pull/370 - * - * Given a pair of x,y co-ordinates, translate that to a position on a rendered - * page. Returns an object containing the page index, a percentage of the w/h of - * the image, and the absolute value of the hit point on the image, e.g., a click in - * the top left corner of the image would return 0,0. - * - **/ - Renderer.prototype.getPageHit = function (clientX, clientY) - { - var bounds = this._outerElement.getBoundingClientRect(); - if (clientX < bounds.left || clientY < bounds.top || - clientX > bounds.left + bounds.width || clientY > bounds.top + bounds.height) - { - return null; - } - - clientX -= bounds.left; - clientY -= bounds.top; - - var numRenderedPages = this._renderedPages.length; - - for (var i = 0; i < numRenderedPages; i++) - { - var pageIndex = this._renderedPages[i]; - var pageInfo = this.layout.getPageInfo(pageIndex); - var pageOffset = this._getImageOffset(pageIndex); - - var viewportPaddingX = Math.max(0, (this._viewport.width - this.layout.dimensions.width) / 2); - var viewportPaddingY = Math.max(0, (this._viewport.height - this.layout.dimensions.height) / 2); - - var viewportOffsetX = pageOffset.left - this._viewport.left + viewportPaddingX; - var viewportOffsetY = pageOffset.top - this._viewport.top + viewportPaddingY; - - var destXOffset = viewportOffsetX < 0 ? -viewportOffsetX : 0; - var destYOffset = viewportOffsetY < 0 ? -viewportOffsetY : 0; - - var canvasX = Math.max(0, viewportOffsetX); - var canvasY = Math.max(0, viewportOffsetY); - - var destWidth = pageInfo.dimensions.width - destXOffset; - var destHeight = pageInfo.dimensions.height - destYOffset; - - if (clientX >= canvasX && clientX <= canvasX + destWidth && clientY >= canvasY && clientY <= canvasY + destHeight) - { - var xhp = ((clientX + destXOffset) - canvasX); - var yhp = ((clientY + destYOffset) - canvasY); - /* To get the percentage x and y you need to adjust the by the scroll offset and the canvas position - Also returns the absolute x and y at a given zoom level; these can be adjusted by clients later - using the translateFromMaxZoomLevel / translateToMaxZoomLevel methods. - */ - return { - pg: pageIndex, - pctx: xhp / pageInfo.dimensions.width, - pcty: yhp / pageInfo.dimensions.height, - x: xhp, - y: yhp - }; - } - } - - return null; - }; - - Renderer.prototype.load = function (config, viewportPosition, sourceResolver) - { - this._clearAnimation(); - - if (this._hooks.onViewWillLoad) - this._hooks.onViewWillLoad(); - - this._sourceResolver = sourceResolver; - this._config = config; - this._compositeImages = {}; - this._setLayoutToZoomLevel(viewportPosition.zoomLevel); - - // FIXME(wabain): Remove this when there's more confidence the check shouldn't be needed - if (!this.layout.getPageInfo(viewportPosition.anchorPage)) - throw new Error('invalid page: ' + viewportPosition.anchorPage); - - if (this._canvas.width !== this._viewport.width || this._canvas.height !== this._viewport.height) - { - debug('Canvas dimension change: (%s, %s) -> (%s, %s)', this._canvas.width, this._canvas.height, - this._viewport.width, this._viewport.height); - - this._canvas.width = this._viewport.width; - this._canvas.height = this._viewport.height; - } else { - debug('Reload, no size change'); - } - - // FIXME: What hooks should be called here? - this.goto(viewportPosition.anchorPage, viewportPosition.verticalOffset, viewportPosition.horizontalOffset); - - if (this._canvas.parentNode !== this._outerElement) - this._outerElement.insertBefore(this._canvas, this._outerElement.firstChild); - - if (this._hooks.onViewDidLoad) - this._hooks.onViewDidLoad(); - }; - - Renderer.prototype._setViewportPosition = function (viewportPosition) - { - if (viewportPosition.zoomLevel !== this._zoomLevel) - { - if (this._zoomLevel === null) - throw new TypeError('The current view is not zoomable'); - else if (viewportPosition.zoomLevel === null) - throw new TypeError('The current view requires a zoom level'); - - this._setLayoutToZoomLevel(viewportPosition.zoomLevel); - } - - this._goto(viewportPosition.anchorPage, viewportPosition.verticalOffset, viewportPosition.horizontalOffset); - }; - - Renderer.prototype._setLayoutToZoomLevel = function (zoomLevel) - { - this.layout = new DocumentLayout(this._config, zoomLevel); - this._zoomLevel = zoomLevel; - - elt.setAttributes(this._documentElement, { - style: { - height: this.layout.dimensions.height + 'px', - width: this.layout.dimensions.width + 'px' - } - }); - - this._viewport.setInnerDimensions(this.layout.dimensions); - }; - - Renderer.prototype.adjust = function (direction) - { - this._clearAnimation(); - - this._render(direction); - - if (this._hooks.onViewDidUpdate) - { - this._hooks.onViewDidUpdate(this._renderedPages.slice(), null); - } - }; - - - - // FIXME(wabain): Remove the direction argument if it doesn't end up being needed. - Renderer.prototype._render = function (direction) // jshint ignore:line - { - var newRenderedPages = []; - this.layout.pageGroups.forEach(function (group) - { - if (!this._viewport.intersectsRegion(group.region)) - return; - - var visiblePages = group.pages - .filter(function (page) - { - return this.isPageVisible(page.index); - }, this) - .map(function (page) - { - return page.index; - }); - - newRenderedPages.push.apply(newRenderedPages, visiblePages); - }, this); - - this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); - this._paintOutline(newRenderedPages); - - newRenderedPages.forEach(function (pageIndex) - { - if (!this._compositeImages[pageIndex]) - { - var page = this.layout.getPageInfo(pageIndex); - var zoomLevels = this._sourceResolver.getAllZoomLevelsForPage(page); - var composite = new CompositeImage(zoomLevels); - composite.updateFromCache(this._cache); - this._compositeImages[pageIndex] = composite; - } - }, this); - - this._initiateTileRequests(newRenderedPages); - - var changes = findChanges(this._renderedPages || [], newRenderedPages); - - changes.removed.forEach(function (pageIndex) - { - delete this._compositeImages[pageIndex]; - }, this); - - this._renderedPages = newRenderedPages; - this._paint(); - - if (this._hooks.onPageWillLoad) - { - changes.added.forEach(function (pageIndex) - { - this._hooks.onPageWillLoad(pageIndex); - }, this); - } - }; - - Renderer.prototype._paint = function () - { - debug('Repainting'); - - var renderedTiles = []; - - this._renderedPages.forEach(function (pageIndex) - { - this._compositeImages[pageIndex].getTiles(this._zoomLevel).forEach(function (source) - { - var scaled = getScaledTileRecord(source, this._zoomLevel); - - if (this._isTileVisible(pageIndex, scaled)) - { - renderedTiles.push(source.url); - this._drawTile(pageIndex, scaled, this._cache.get(source.url)); - } - }, this); - }, this); - - var cache = this._cache; - - var changes = findChanges(this._renderedTiles || [], renderedTiles); - - changes.added.forEach(function (url) - { - cache.acquire(url); - }); - - changes.removed.forEach(function (url) - { - cache.release(url); - }); - - if (changes.removed) - { - // FIXME: Should only need to update the composite images - // for which tiles were removed - this._renderedPages.forEach(function (pageIndex) - { - this._compositeImages[pageIndex].updateFromCache(this._cache); - }, this); - } - - this._renderedTiles = renderedTiles; - }; - - // Paint a page outline while the tiles are loading. - Renderer.prototype._paintOutline = function (pages) - { - pages.forEach(function (pageIndex) - { - var pageInfo = this.layout.getPageInfo(pageIndex); - var pageOffset = this._getImageOffset(pageIndex); - - // Ensure the document is drawn to the center of the viewport - var viewportPaddingX = Math.max(0, (this._viewport.width - this.layout.dimensions.width) / 2); - var viewportPaddingY = Math.max(0, (this._viewport.height - this.layout.dimensions.height) / 2); - - var viewportOffsetX = pageOffset.left - this._viewport.left + viewportPaddingX; - var viewportOffsetY = pageOffset.top - this._viewport.top + viewportPaddingY; - - var destXOffset = viewportOffsetX < 0 ? -viewportOffsetX : 0; - var destYOffset = viewportOffsetY < 0 ? -viewportOffsetY : 0; - - var canvasX = Math.max(0, viewportOffsetX); - var canvasY = Math.max(0, viewportOffsetY); - - var destWidth = pageInfo.dimensions.width - destXOffset; - var destHeight = pageInfo.dimensions.height - destYOffset; - - this._ctx.strokeStyle = '#AAA'; - // In order to get a 1px wide line using strokes, we need to start at a 'half pixel' - this._ctx.strokeRect(canvasX + 0.5, canvasY + 0.5, destWidth, destHeight); - }, this); - }; - - // This method should be sent all visible pages at once because it will initiate - // all image requests and cancel any remaining image requests. In the case that - // a request is ongoing and the tile is still visible in the viewport, the old request - // is kept active instead of restarting it. The image requests are given a timeout - // before loading in order to debounce them and have a small reaction time - // to cancel them and avoid useless requests. - Renderer.prototype._initiateTileRequests = function(pages) - { - // Only requests in this object are kept alive, since all others are not visible in the viewport - var newPendingRequests = {}; - - // Used later as a closure to initiate the image requests with the right source and pageIndex - var initiateRequest = function (source, pageIndex) - { - var composite = this._compositeImages[pageIndex]; - - newPendingRequests[source.url] = new ImageRequestHandler({ - url: source.url, - timeoutTime: REQUEST_DEBOUNCE_INTERVAL, - load: function (img) - { - delete this._pendingRequests[source.url]; - this._cache.put(source.url, img); - - // Awkward way to check for updates - if (composite === this._compositeImages[pageIndex]) - { - composite.updateWithLoadedUrls([source.url]); - - if (this._isTileForSourceVisible(pageIndex, source)) - this._paint(); - else - debugPaints('Page %s, tile %s no longer visible on image load', pageIndex, source.url); - } - }.bind(this), - error: function () - { - // TODO: Could make a limited number of retries, etc. - delete this._pendingRequests[source.url]; - }.bind(this) - }); - }.bind(this); - - for (var i = 0; i < pages.length; i++) - { - var pageIndex = pages[i]; - var tiles = this._sourceResolver.getBestZoomLevelForPage(this.layout.getPageInfo(pageIndex)).tiles; - - for (var j = 0; j < tiles.length; j++) - { - var source = tiles[j]; - if (this._cache.has(source.url) || !this._isTileForSourceVisible(pageIndex, source)) - continue; - - // Don't create a new request if the tile is already being loaded - if (this._pendingRequests[source.url]) - { - newPendingRequests[source.url] = this._pendingRequests[source.url]; - delete this._pendingRequests[source.url]; - continue; - } - - // Use a closure since the load and error methods are going to be called later and - // we need to keep the right reference to the source and the page index - initiateRequest(source, pageIndex); - } - } - - for (var url in this._pendingRequests) - this._pendingRequests[url].abort(); - this._pendingRequests = newPendingRequests; - }; - - Renderer.prototype._drawTile = function (pageIndex, scaledTile, img) - { - var tileOffset = this._getTileToDocumentOffset(pageIndex, scaledTile); - - // Ensure the document is drawn to the center of the viewport - var viewportPaddingX = Math.max(0, (this._viewport.width - this.layout.dimensions.width) / 2); - var viewportPaddingY = Math.max(0, (this._viewport.height - this.layout.dimensions.height) / 2); - - var viewportOffsetX = tileOffset.left - this._viewport.left + viewportPaddingX; - var viewportOffsetY = tileOffset.top - this._viewport.top + viewportPaddingY; - - var destXOffset = viewportOffsetX < 0 ? -viewportOffsetX : 0; - var destYOffset = viewportOffsetY < 0 ? -viewportOffsetY : 0; - - var sourceXOffset = destXOffset / scaledTile.scaleRatio; - var sourceYOffset = destYOffset / scaledTile.scaleRatio; - - var canvasX = Math.max(0, viewportOffsetX); - var canvasY = Math.max(0, viewportOffsetY); - - // Ensure that the specified dimensions are no greater than the actual - // size of the image. Safari won't display the tile if they are. - var destWidth = Math.min(scaledTile.dimensions.width, img.width * scaledTile.scaleRatio) - destXOffset; - var destHeight = Math.min(scaledTile.dimensions.height, img.height * scaledTile.scaleRatio) - destYOffset; - - destWidth = Math.max(1, destWidth); - destHeight = Math.max(1, destHeight); - - var sourceWidth = Math.abs(destWidth / scaledTile.scaleRatio); - var sourceHeight = Math.abs(destHeight / scaledTile.scaleRatio); - - if (debugPaints.enabled) - { - debugPaints('Drawing page %s, tile %sx (%s, %s) from %s, %s to viewport at %s, %s, scale %s%%', - pageIndex, - scaledTile.sourceZoomLevel, scaledTile.row, scaledTile.col, - sourceXOffset, sourceYOffset, - canvasX, canvasY, - Math.round(scaledTile.scaleRatio * 100)); - } - - this._ctx.drawImage( - img, - sourceXOffset, sourceYOffset, - sourceWidth, sourceHeight, - canvasX, canvasY, - destWidth, destHeight); - }; - - Renderer.prototype._isTileForSourceVisible = function (pageIndex, tileSource) - { - return this._isTileVisible(pageIndex, getScaledTileRecord(tileSource, this._zoomLevel)); - }; - - Renderer.prototype._isTileVisible = function (pageIndex, scaledTile) - { - var tileOffset = this._getTileToDocumentOffset(pageIndex, scaledTile); - - // FIXME(wabain): This check is insufficient during a zoom transition - return this._viewport.intersectsRegion({ - top: tileOffset.top, - bottom: tileOffset.top + scaledTile.dimensions.height, - left: tileOffset.left, - right: tileOffset.left + scaledTile.dimensions.width - }); - }; - - Renderer.prototype._getTileToDocumentOffset = function (pageIndex, scaledTile) - { - var imageOffset = this._getImageOffset(pageIndex); - - return { - top: imageOffset.top + scaledTile.offset.top, - left: imageOffset.left + scaledTile.offset.left - }; - }; - - Renderer.prototype._getImageOffset = function (pageIndex) - { - return this.layout.getPageOffset(pageIndex, {excludePadding: true}); - }; - - // TODO: Update signature - Renderer.prototype.goto = function (pageIndex, verticalOffset, horizontalOffset) - { - this._clearAnimation(); - this._goto(pageIndex, verticalOffset, horizontalOffset); - if (this._hooks.onViewDidUpdate) - { - this._hooks.onViewDidUpdate(this._renderedPages.slice(), pageIndex); - } - }; - - Renderer.prototype._goto = function (pageIndex, verticalOffset, horizontalOffset) - { - // FIXME(wabain): Move this logic to the viewer - var pageOffset = this.layout.getPageOffset(pageIndex); - - var desiredVerticalCenter = pageOffset.top + verticalOffset; - var top = desiredVerticalCenter - parseInt(this._viewport.height / 2, 10); - - var desiredHorizontalCenter = pageOffset.left + horizontalOffset; - var left = desiredHorizontalCenter - parseInt(this._viewport.width / 2, 10); - - this._viewport.top = top; - this._viewport.left = left; - - this._render(0); - }; - - Renderer.prototype.transitionViewportPosition = function (options) - { - this._clearAnimation(); - - var getPosition = options.getPosition; - var self = this; - - var onViewDidTransition = this._hooks.onViewDidTransition; - - this._animation = InterpolateAnimation.animate({ - duration: options.duration, - parameters: options.parameters, - onUpdate: function (values) - { - // TODO: Do image preloading, work with that - self._setViewportPosition(getPosition(values)); - - if (onViewDidTransition) - onViewDidTransition(); - }, - onEnd: function (info) - { - if (options.onEnd) - options.onEnd(info); - - if (self._hooks.onViewDidUpdate && !info.interrupted) - { - self._hooks.onViewDidUpdate(self._renderedPages.slice(), null); - } - } - }); - }; - - Renderer.prototype._clearAnimation = function () - { - if (this._animation) - { - this._animation.cancel(); - this._animation = null; - } - }; - - Renderer.prototype.preload = function () - { - // TODO - }; - - Renderer.prototype.isPageVisible = function (pageIndex) - { - if (!this.layout) - return false; - - var page = this.layout.getPageInfo(pageIndex); - - if (!page) - return false; - - return this._viewport.intersectsRegion(this.layout.getPageRegion(pageIndex)); - }; - - Renderer.prototype.getRenderedPages = function () - { - return this._renderedPages.slice(); - }; - - Renderer.prototype.destroy = function () - { - this._clearAnimation(); - - // FIXME(wabain): I don't know if we should actually do this - Object.keys(this._pendingRequests).forEach(function (req) - { - var handler = this._pendingRequests[req]; - delete this._pendingRequests[req]; - - handler.abort(); - }, this); - - this._canvas.parentNode.removeChild(this._canvas); - }; - - function getScaledTileRecord(source, scaleFactor) - { - var scaleRatio; - - if (scaleFactor === null) - scaleRatio = 1; - else - scaleRatio = Math.pow(2, scaleFactor - source.zoomLevel); - - return { - sourceZoomLevel: source.zoomLevel, - scaleRatio: scaleRatio, - row: source.row, - col: source.col, - dimensions: { - width: source.dimensions.width * scaleRatio, - height: source.dimensions.height * scaleRatio - }, - offset: { - left: source.offset.left * scaleRatio, - top: source.offset.top * scaleRatio - }, - url: source.url - }; - } - - function findChanges(oldArray, newArray) - { - if (oldArray === newArray) - { - return { - added: [], - removed: [] - }; - } - - var removed = oldArray.filter(function (oldEntry) - { - return newArray.indexOf(oldEntry) === -1; - }); - - var added = newArray.filter(function (newEntry) - { - return oldArray.indexOf(newEntry) === -1; - }); - - return { - added: added, - removed: removed - }; - } - - -/***/ }), -/* 26 */ -/***/ (function(module, exports, __webpack_require__) { - - /* WEBPACK VAR INJECTION */(function(process) {/** - * This is the web browser implementation of `debug()`. - * - * Expose `debug()` as the module. - */ - - exports = module.exports = __webpack_require__(28); - exports.log = log; - exports.formatArgs = formatArgs; - exports.save = save; - exports.load = load; - exports.useColors = useColors; - exports.storage = 'undefined' != typeof chrome - && 'undefined' != typeof chrome.storage - ? chrome.storage.local - : localstorage(); - - /** - * Colors. - */ - - exports.colors = [ - 'lightseagreen', - 'forestgreen', - 'goldenrod', - 'dodgerblue', - 'darkorchid', - 'crimson' - ]; - - /** - * Currently only WebKit-based Web Inspectors, Firefox >= v31, - * and the Firebug extension (any Firefox version) are known - * to support "%c" CSS customizations. - * - * TODO: add a `localStorage` variable to explicitly enable/disable colors - */ - - function useColors() { - // NB: In an Electron preload script, document will be defined but not fully - // initialized. Since we know we're in Chrome, we'll just detect this case - // explicitly - if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') { - return true; - } - - // is webkit? http://stackoverflow.com/a/16459606/376773 - // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 - return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) || - // is firebug? http://stackoverflow.com/a/398120/376773 - (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) || - // is firefox >= v31? - // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages - (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) || - // double check webkit in userAgent just in case we are in a worker - (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)); - } - - /** - * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. - */ - - exports.formatters.j = function(v) { - try { - return JSON.stringify(v); - } catch (err) { - return '[UnexpectedJSONParseError]: ' + err.message; - } - }; - - - /** - * Colorize log arguments if enabled. - * - * @api public - */ - - function formatArgs(args) { - var useColors = this.useColors; - - args[0] = (useColors ? '%c' : '') - + this.namespace - + (useColors ? ' %c' : ' ') - + args[0] - + (useColors ? '%c ' : ' ') - + '+' + exports.humanize(this.diff); - - if (!useColors) return; - - var c = 'color: ' + this.color; - args.splice(1, 0, c, 'color: inherit') - - // the final "%c" is somewhat tricky, because there could be other - // arguments passed either before or after the %c, so we need to - // figure out the correct index to insert the CSS into - var index = 0; - var lastC = 0; - args[0].replace(/%[a-zA-Z%]/g, function(match) { - if ('%%' === match) return; - index++; - if ('%c' === match) { - // we only are interested in the *last* %c - // (the user may have provided their own) - lastC = index; - } - }); - - args.splice(lastC, 0, c); - } - - /** - * Invokes `console.log()` when available. - * No-op when `console.log` is not a "function". - * - * @api public - */ - - function log() { - // this hackery is required for IE8/9, where - // the `console.log` function doesn't have 'apply' - return 'object' === typeof console - && console.log - && Function.prototype.apply.call(console.log, console, arguments); - } - - /** - * Save `namespaces`. - * - * @param {String} namespaces - * @api private - */ - - function save(namespaces) { - try { - if (null == namespaces) { - exports.storage.removeItem('debug'); - } else { - exports.storage.debug = namespaces; - } - } catch(e) {} - } - - /** - * Load `namespaces`. - * - * @return {String} returns the previously persisted debug modes - * @api private - */ - - function load() { - var r; - try { - r = exports.storage.debug; - } catch(e) {} - - // If debug isn't set in LS, and we're in Electron, try to load $DEBUG - if (!r && typeof process !== 'undefined' && 'env' in process) { - r = process.env.DEBUG; - } - - return r; - } - - /** - * Enable namespaces listed in `localStorage.debug` initially. - */ - - exports.enable(load()); - - /** - * Localstorage attempts to return the localstorage. - * - * This is necessary because safari throws - * when a user disables cookies/localstorage - * and you attempt to access it. - * - * @return {LocalStorage} - * @api private - */ - - function localstorage() { - try { - return window.localStorage; - } catch (e) {} - } - - /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(27))) - -/***/ }), -/* 27 */ -/***/ (function(module, exports) { - - // shim for using process in browser - var process = module.exports = {}; - - // cached from whatever global is present so that test runners that stub it - // don't break things. But we need to wrap it in a try catch in case it is - // wrapped in strict mode code which doesn't define any globals. It's inside a - // function because try/catches deoptimize in certain engines. - - var cachedSetTimeout; - var cachedClearTimeout; - - function defaultSetTimout() { - throw new Error('setTimeout has not been defined'); - } - function defaultClearTimeout () { - throw new Error('clearTimeout has not been defined'); - } - (function () { - try { - if (typeof setTimeout === 'function') { - cachedSetTimeout = setTimeout; - } else { - cachedSetTimeout = defaultSetTimout; - } - } catch (e) { - cachedSetTimeout = defaultSetTimout; - } - try { - if (typeof clearTimeout === 'function') { - cachedClearTimeout = clearTimeout; - } else { - cachedClearTimeout = defaultClearTimeout; - } - } catch (e) { - cachedClearTimeout = defaultClearTimeout; - } - } ()) - function runTimeout(fun) { - if (cachedSetTimeout === setTimeout) { - //normal enviroments in sane situations - return setTimeout(fun, 0); - } - // if setTimeout wasn't available but was latter defined - if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { - cachedSetTimeout = setTimeout; - return setTimeout(fun, 0); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedSetTimeout(fun, 0); - } catch(e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedSetTimeout.call(null, fun, 0); - } catch(e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error - return cachedSetTimeout.call(this, fun, 0); - } - } - - - } - function runClearTimeout(marker) { - if (cachedClearTimeout === clearTimeout) { - //normal enviroments in sane situations - return clearTimeout(marker); - } - // if clearTimeout wasn't available but was latter defined - if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { - cachedClearTimeout = clearTimeout; - return clearTimeout(marker); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedClearTimeout(marker); - } catch (e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedClearTimeout.call(null, marker); - } catch (e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. - // Some versions of I.E. have different rules for clearTimeout vs setTimeout - return cachedClearTimeout.call(this, marker); - } - } - - - - } - var queue = []; - var draining = false; - var currentQueue; - var queueIndex = -1; - - function cleanUpNextTick() { - if (!draining || !currentQueue) { - return; - } - draining = false; - if (currentQueue.length) { - queue = currentQueue.concat(queue); - } else { - queueIndex = -1; - } - if (queue.length) { - drainQueue(); - } - } - - function drainQueue() { - if (draining) { - return; - } - var timeout = runTimeout(cleanUpNextTick); - draining = true; - - var len = queue.length; - while(len) { - currentQueue = queue; - queue = []; - while (++queueIndex < len) { - if (currentQueue) { - currentQueue[queueIndex].run(); - } - } - queueIndex = -1; - len = queue.length; - } - currentQueue = null; - draining = false; - runClearTimeout(timeout); - } - - process.nextTick = function (fun) { - var args = new Array(arguments.length - 1); - if (arguments.length > 1) { - for (var i = 1; i < arguments.length; i++) { - args[i - 1] = arguments[i]; - } - } - queue.push(new Item(fun, args)); - if (queue.length === 1 && !draining) { - runTimeout(drainQueue); - } - }; - - // v8 likes predictible objects - function Item(fun, array) { - this.fun = fun; - this.array = array; - } - Item.prototype.run = function () { - this.fun.apply(null, this.array); - }; - process.title = 'browser'; - process.browser = true; - process.env = {}; - process.argv = []; - process.version = ''; // empty string to avoid regexp issues - process.versions = {}; - - function noop() {} - - process.on = noop; - process.addListener = noop; - process.once = noop; - process.off = noop; - process.removeListener = noop; - process.removeAllListeners = noop; - process.emit = noop; - process.prependListener = noop; - process.prependOnceListener = noop; - - process.listeners = function (name) { return [] } - - process.binding = function (name) { - throw new Error('process.binding is not supported'); - }; - - process.cwd = function () { return '/' }; - process.chdir = function (dir) { - throw new Error('process.chdir is not supported'); - }; - process.umask = function() { return 0; }; - - -/***/ }), -/* 28 */ -/***/ (function(module, exports, __webpack_require__) { - - - /** - * This is the common logic for both the Node.js and web browser - * implementations of `debug()`. - * - * Expose `debug()` as the module. - */ - - exports = module.exports = createDebug.debug = createDebug['default'] = createDebug; - exports.coerce = coerce; - exports.disable = disable; - exports.enable = enable; - exports.enabled = enabled; - exports.humanize = __webpack_require__(29); - - /** - * The currently active debug mode names, and names to skip. - */ - - exports.names = []; - exports.skips = []; - - /** - * Map of special "%n" handling functions, for the debug "format" argument. - * - * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". - */ - - exports.formatters = {}; - - /** - * Previous log timestamp. - */ - - var prevTime; - - /** - * Select a color. - * @param {String} namespace - * @return {Number} - * @api private - */ - - function selectColor(namespace) { - var hash = 0, i; - - for (i in namespace) { - hash = ((hash << 5) - hash) + namespace.charCodeAt(i); - hash |= 0; // Convert to 32bit integer - } - - return exports.colors[Math.abs(hash) % exports.colors.length]; - } - - /** - * Create a debugger with the given `namespace`. - * - * @param {String} namespace - * @return {Function} - * @api public - */ - - function createDebug(namespace) { - - function debug() { - // disabled? - if (!debug.enabled) return; - - var self = debug; - - // set `diff` timestamp - var curr = +new Date(); - var ms = curr - (prevTime || curr); - self.diff = ms; - self.prev = prevTime; - self.curr = curr; - prevTime = curr; - - // turn the `arguments` into a proper Array - var args = new Array(arguments.length); - for (var i = 0; i < args.length; i++) { - args[i] = arguments[i]; - } - - args[0] = exports.coerce(args[0]); - - if ('string' !== typeof args[0]) { - // anything else let's inspect with %O - args.unshift('%O'); - } - - // apply any `formatters` transformations - var index = 0; - args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) { - // if we encounter an escaped % then don't increase the array index - if (match === '%%') return match; - index++; - var formatter = exports.formatters[format]; - if ('function' === typeof formatter) { - var val = args[index]; - match = formatter.call(self, val); - - // now we need to remove `args[index]` since it's inlined in the `format` - args.splice(index, 1); - index--; - } - return match; - }); - - // apply env-specific formatting (colors, etc.) - exports.formatArgs.call(self, args); - - var logFn = debug.log || exports.log || console.log.bind(console); - logFn.apply(self, args); - } - - debug.namespace = namespace; - debug.enabled = exports.enabled(namespace); - debug.useColors = exports.useColors(); - debug.color = selectColor(namespace); - - // env-specific initialization logic for debug instances - if ('function' === typeof exports.init) { - exports.init(debug); - } - - return debug; - } - - /** - * Enables a debug mode by namespaces. This can include modes - * separated by a colon and wildcards. - * - * @param {String} namespaces - * @api public - */ - - function enable(namespaces) { - exports.save(namespaces); - - exports.names = []; - exports.skips = []; - - var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); - var len = split.length; - - for (var i = 0; i < len; i++) { - if (!split[i]) continue; // ignore empty strings - namespaces = split[i].replace(/\*/g, '.*?'); - if (namespaces[0] === '-') { - exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); - } else { - exports.names.push(new RegExp('^' + namespaces + '$')); - } - } - } - - /** - * Disable debug output. - * - * @api public - */ - - function disable() { - exports.enable(''); - } - - /** - * Returns true if the given mode name is enabled, false otherwise. - * - * @param {String} name - * @return {Boolean} - * @api public - */ - - function enabled(name) { - var i, len; - for (i = 0, len = exports.skips.length; i < len; i++) { - if (exports.skips[i].test(name)) { - return false; - } - } - for (i = 0, len = exports.names.length; i < len; i++) { - if (exports.names[i].test(name)) { - return true; - } - } - return false; - } - - /** - * Coerce `val`. - * - * @param {Mixed} val - * @return {Mixed} - * @api private - */ - - function coerce(val) { - if (val instanceof Error) return val.stack || val.message; - return val; - } - - -/***/ }), -/* 29 */ -/***/ (function(module, exports) { - - /** - * Helpers. - */ - - var s = 1000; - var m = s * 60; - var h = m * 60; - var d = h * 24; - var y = d * 365.25; - - /** - * Parse or format the given `val`. - * - * Options: - * - * - `long` verbose formatting [false] - * - * @param {String|Number} val - * @param {Object} [options] - * @throws {Error} throw an error if val is not a non-empty string or a number - * @return {String|Number} - * @api public - */ - - module.exports = function(val, options) { - options = options || {}; - var type = typeof val; - if (type === 'string' && val.length > 0) { - return parse(val); - } else if (type === 'number' && isNaN(val) === false) { - return options.long ? fmtLong(val) : fmtShort(val); - } - throw new Error( - 'val is not a non-empty string or a valid number. val=' + - JSON.stringify(val) - ); - }; - - /** - * Parse the given `str` and return milliseconds. - * - * @param {String} str - * @return {Number} - * @api private - */ - - function parse(str) { - str = String(str); - if (str.length > 100) { - return; - } - var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec( - str - ); - if (!match) { - return; - } - var n = parseFloat(match[1]); - var type = (match[2] || 'ms').toLowerCase(); - switch (type) { - case 'years': - case 'year': - case 'yrs': - case 'yr': - case 'y': - return n * y; - case 'days': - case 'day': - case 'd': - return n * d; - case 'hours': - case 'hour': - case 'hrs': - case 'hr': - case 'h': - return n * h; - case 'minutes': - case 'minute': - case 'mins': - case 'min': - case 'm': - return n * m; - case 'seconds': - case 'second': - case 'secs': - case 'sec': - case 's': - return n * s; - case 'milliseconds': - case 'millisecond': - case 'msecs': - case 'msec': - case 'ms': - return n; - default: - return undefined; - } - } - - /** - * Short format for `ms`. - * - * @param {Number} ms - * @return {String} - * @api private - */ - - function fmtShort(ms) { - if (ms >= d) { - return Math.round(ms / d) + 'd'; - } - if (ms >= h) { - return Math.round(ms / h) + 'h'; - } - if (ms >= m) { - return Math.round(ms / m) + 'm'; - } - if (ms >= s) { - return Math.round(ms / s) + 's'; - } - return ms + 'ms'; - } - - /** - * Long format for `ms`. - * - * @param {Number} ms - * @return {String} - * @api private - */ - - function fmtLong(ms) { - return plural(ms, d, 'day') || - plural(ms, h, 'hour') || - plural(ms, m, 'minute') || - plural(ms, s, 'second') || - ms + ' ms'; - } - - /** - * Pluralization helper. - */ - - function plural(ms, n, name) { - if (ms < n) { - return; - } - if (ms < n * 1.5) { - return Math.floor(ms / n) + ' ' + name; - } - return Math.ceil(ms / n) + ' ' + name + 's'; - } - - -/***/ }), -/* 30 */ -/***/ (function(module, exports) { - - module.exports = CompositeImage; - - /** - * @class CompositeImage - * @private - * - * Utility class to composite tiles into a complete image - * and track the rendered state of an image as new tiles - * load. - */ - - /** - * @param levels {Array.>} - * @constructor - */ - function CompositeImage(levels) - { - this._levels = levels; // Assume levels sorted high-res first - var urlsToTiles = this._urlsToTiles = {}; - - levels.forEach(function (level) - { - level.tiles.forEach(function (tile) - { - urlsToTiles[tile.url] = { - zoomLevel: level.zoomLevel, - row: tile.row, - col: tile.col - }; - }); - }); - - this.clear(); - } - - CompositeImage.prototype.clear = function () - { - var loadedByLevel = this._loadedByLevel = {}; - - this._levels.forEach(function (level) - { - loadedByLevel[level.zoomLevel] = new TileCoverageMap(level.rows, level.cols); - }); - }; - - CompositeImage.prototype.getTiles = function (baseZoomLevel) - { - var toRenderByLevel = []; - var highestZoomLevel = this._levels[0].zoomLevel; - var covered = new TileCoverageMap(this._levels[0].rows, this._levels[0].cols); - - var bestLevelIndex; - - // Default to the lowest zoom level - if (baseZoomLevel === null) - { - bestLevelIndex = 0; - } - else - { - var ceilLevel = Math.ceil(baseZoomLevel); - bestLevelIndex = findIndex(this._levels, function (level) - { - return level.zoomLevel <= ceilLevel; - }); - } - - - // The best level, followed by higher-res levels in ascending order of resolution, - // followed by lower-res levels in descending order of resolution - var levelsByPreference = this._levels.slice(0, bestLevelIndex + 1).reverse() - .concat(this._levels.slice(bestLevelIndex + 1)); - - levelsByPreference.forEach(function (level) - { - var loaded = this._loadedByLevel[level.zoomLevel]; - - var additionalTiles = level.tiles.filter(function (tile) - { - return loaded.isLoaded(tile.row, tile.col); - }); - - // Filter out entirely covered tiles - - // FIXME: Is it better to draw all of a partially covered tile, - // with some of it ultimately covered, or to pick out the region - // which needs to be drawn? - // See https://github.com/DDMAL/diva.js/issues/358 - - var scaleRatio = Math.pow(2, highestZoomLevel - level.zoomLevel); - - additionalTiles = additionalTiles.filter(function (tile) - { - var isNeeded = false; - - var highResRow = tile.row * scaleRatio; - var highResCol = tile.col * scaleRatio; - - for (var i=0; i < scaleRatio; i++) - { - for (var j=0; j < scaleRatio; j++) - { - if (!covered.isLoaded(highResRow + i, highResCol + j)) - { - isNeeded = true; - covered.set(highResRow + i, highResCol + j, true); - } - } - } - - return isNeeded; - }); - - toRenderByLevel.push(additionalTiles); - }, this); - - // Less-preferred tiles should come first - toRenderByLevel.reverse(); - - var tiles = []; - - toRenderByLevel.forEach(function (byLevel) - { - tiles.push.apply(tiles, byLevel); - }); - - return tiles; - }; - - /** - * Update the composite image to take into account all the URLs - * loaded in an image cache. - * - * @param cache {ImageCache} - */ - CompositeImage.prototype.updateFromCache = function (cache) - { - this.clear(); - - this._levels.forEach(function (level) - { - var loaded = this._loadedByLevel[level.zoomLevel]; - - level.tiles.forEach(function (tile) - { - if (cache.has(tile.url)) - loaded.set(tile.row, tile.col, true); - }); - }, this); - }; - - CompositeImage.prototype.updateWithLoadedUrls = function (urls) - { - urls.forEach(function (url) - { - var entry = this._urlsToTiles[url]; - this._loadedByLevel[entry.zoomLevel].set(entry.row, entry.col, true); - }, this); - }; - - function TileCoverageMap(rows, cols) - { - this._rows = rows; - this._cols = cols; - - this._map = fill(rows).map(function () - { - return fill(cols, false); - }); - } - - TileCoverageMap.prototype.isLoaded = function (row, col) - { - // Return true for out of bounds tiles because they - // don't need to load. (Unfortunately this will also - // mask logical errors.) - if (row >= this._rows || col >= this._cols) - return true; - - return this._map[row][col]; - }; - - TileCoverageMap.prototype.set = function (row, col, value) - { - this._map[row][col] = value; - }; - - function fill(count, value) - { - var arr = new Array(count); - - for (var i=0; i < count; i++) - arr[i] = value; - - return arr; - } - - function findIndex(array, predicate) - { - var length = array.length; - for (var i = 0; i < length; i++) - { - if (predicate(array[i], i)) - return i; - } - - return -1; - } - - -/***/ }), -/* 31 */ -/***/ (function(module, exports) { - - module.exports = DocumentLayout; - - /** - * Translate page layouts, as generated by page-layouts, into an - * object which computes layout information for the document as - * a whole. - */ - function DocumentLayout(config, zoomLevel) - { - var computedLayout = getComputedLayout(config, zoomLevel); - - this.dimensions = computedLayout.dimensions; - this.pageGroups = computedLayout.pageGroups; - this._pageLookup = getPageLookup(computedLayout.pageGroups); - } - - /** - * @typedef {Object} PageInfo - * @property {number} index - * @property {{index, dimensions, pages, region, padding}} group - * @property {{height: number, width: number}} dimensions - * @property {{top: number, left: number}} groupOffset - */ - - /** - * @param pageIndex - * @returns {PageInfo|null} - */ - DocumentLayout.prototype.getPageInfo = function (pageIndex) - { - return this._pageLookup[pageIndex] || null; - }; - - /** - * Get the dimensions of a page - * - * @param pageIndex - * @returns {{height: number, width: number}} - */ - DocumentLayout.prototype.getPageDimensions = function (pageIndex) - { - if (!this._pageLookup || !this._pageLookup[pageIndex]) - return null; - - var region = getPageRegionFromPageInfo(this._pageLookup[pageIndex]); - - return { - height: region.bottom - region.top, - width: region.right - region.left - }; - }; - - // TODO(wabain): Get rid of this; it's a subset of the page region, so - // give that instead - /** - * Get the top-left coordinates of a page, including*** padding - * - * @param pageIndex - * @param options - * @returns {{top: number, left: number} | null} - */ - DocumentLayout.prototype.getPageOffset = function (pageIndex, options) - { - var region = this.getPageRegion(pageIndex, options); - - if (!region) - return null; - - return { - top: region.top, - left: region.left - }; - }; - - DocumentLayout.prototype.getPageRegion = function (pageIndex, options) - { - var pageInfo = this._pageLookup[pageIndex]; - - if (!pageInfo) - return null; - - var region = getPageRegionFromPageInfo(pageInfo); - - if (options && options.excludePadding) - { - // FIXME? - var padding = pageInfo.group.padding; - - return { - top: region.top + padding.top, - left: region.left + padding.left, - bottom: region.bottom, - right: region.right - }; - } - - return region; - }; - - /** - * Get the distance from the top-right of the page to the center of the - * specified viewport region - * - * @param pageIndex - * @param viewport {{top: number, left: number, bottom: number, right: number}} - * @returns {{x: number, y: number}} - */ - DocumentLayout.prototype.getPageToViewportCenterOffset = function (pageIndex, viewport) - { - var scrollLeft = viewport.left; - var elementWidth = viewport.right - viewport.left; - - var offset = this.getPageOffset(pageIndex); - - var x = scrollLeft - offset.left + parseInt(elementWidth / 2, 10); - - var scrollTop = viewport.top; - var elementHeight = viewport.bottom - viewport.top; - - var y = scrollTop - offset.top + parseInt(elementHeight / 2, 10); - - return { - x: x, - y: y - }; - }; - - function getPageRegionFromPageInfo(page) - { - var top = page.groupOffset.top + page.group.region.top; - var bottom = top + page.dimensions.height; - var left = page.groupOffset.left + page.group.region.left; - var right = left + page.dimensions.width; - - return { - top: top, - bottom: bottom, - left: left, - right: right - }; - } - - function getPageLookup(pageGroups) - { - var pageLookup = {}; - - pageGroups.forEach(function (group) - { - group.pages.forEach(function (page) - { - pageLookup[page.index] = { - index: page.index, - group: group, - dimensions: page.dimensions, - groupOffset: page.groupOffset - }; - }); - }); - - return pageLookup; - } - - function getComputedLayout(config, zoomLevel) - { - var scaledLayouts = zoomLevel === null ? config.pageLayouts : getScaledPageLayouts(config, zoomLevel); - - var documentSecondaryExtent = getExtentAlongSecondaryAxis(config, scaledLayouts); - - // The current position in the document along the primary axis - var primaryDocPosition = config.verticallyOriented ? - config.padding.document.top : - config.padding.document.left; - - var pageGroups = []; - - // TODO: Use bottom, right as well - var pagePadding = { - top: config.padding.page.top, - left: config.padding.page.left - }; - - scaledLayouts.forEach(function (layout, index) - { - var top, left; - - if (config.verticallyOriented) - { - top = primaryDocPosition; - left = (documentSecondaryExtent - layout.dimensions.width) / 2; - } - else - { - top = (documentSecondaryExtent - layout.dimensions.height) / 2; - left = primaryDocPosition; - } - - var region = { - top: top, - bottom: top + pagePadding.top + layout.dimensions.height, - left: left, - right: left + pagePadding.left + layout.dimensions.width - }; - - pageGroups.push({ - index: index, - dimensions: layout.dimensions, - pages: layout.pages, - region: region, - padding: pagePadding - }); - - primaryDocPosition = config.verticallyOriented ? region.bottom : region.right; - }); - - var height, width; - - if (config.verticallyOriented) - { - height = primaryDocPosition + pagePadding.top; - width = documentSecondaryExtent; - } - else - { - height = documentSecondaryExtent; - width = primaryDocPosition + pagePadding.left; - } - - return { - dimensions: { - height: height, - width: width - }, - pageGroups: pageGroups - }; - } - - function getScaledPageLayouts(config, zoomLevel) - { - var scaleRatio = Math.pow(2, zoomLevel - config.maxZoomLevel); - - return config.pageLayouts.map(function (group) - { - return { - dimensions: scaleDimensions(group.dimensions, scaleRatio), - pages: group.pages.map(function (page) - { - return { - index: page.index, - groupOffset: { - top: Math.floor(page.groupOffset.top * scaleRatio), - left: Math.floor(page.groupOffset.left * scaleRatio) - }, - dimensions: scaleDimensions(page.dimensions, scaleRatio) - }; - }) - }; - }); - } - - function scaleDimensions(dimensions, scaleRatio) - { - return { - height: Math.floor(dimensions.height * scaleRatio), - width: Math.floor(dimensions.width * scaleRatio) - }; - } - - function getExtentAlongSecondaryAxis(config, scaledLayouts) - { - // Get the extent of the document along the secondary axis - var secondaryDim, secondaryPadding; - var docPadding = config.padding.document; - - if (config.verticallyOriented) - { - secondaryDim = 'width'; - secondaryPadding = docPadding.left + docPadding.right; - } - else - { - secondaryDim = 'height'; - secondaryPadding = docPadding.top + docPadding.bottom; - } - - return secondaryPadding + scaledLayouts.reduce(function (maxDim, layout) - { - return Math.max(layout.dimensions[secondaryDim], maxDim); - }, 0); - } - - -/***/ }), -/* 32 */ -/***/ (function(module, exports, __webpack_require__) { - - 'use strict'; - - var debug = __webpack_require__(26)('diva:ImageCache'); - - module.exports = ImageCache; - - /* FIXME(wabain): The caching strategy here is completely - * arbitrary and the implementation isn't especially efficient. - */ - - var DEFAULT_MAX_KEYS = 100; - - function ImageCache(options) - { - options = options || { maxKeys: DEFAULT_MAX_KEYS }; - this.maxKeys = options.maxKeys || DEFAULT_MAX_KEYS; - - this._held = {}; - this._urls = {}; - this._lru = []; - } - - ImageCache.prototype.get = function (url) - { - var record = this._urls[url]; - return record ? record.img : null; - }; - - ImageCache.prototype.has = function (url) - { - return !!this._urls[url]; - }; - - ImageCache.prototype.put = function (url, img) - { - var record = this._urls[url]; - if (record) - { - // FIXME: Does this make sense for this use case? - record.img = img; - this._promote(record); - } - else - { - record = { - img: img, - url: url - }; - - this._urls[url] = record; - this._tryEvict(1); - this._lru.unshift(record); - } - }; - - ImageCache.prototype._promote = function (record) - { - var index = this._lru.indexOf(record); - this._lru.splice(index, 1); - this._lru.unshift(record); - }; - - ImageCache.prototype._tryEvict = function (extraCapacity) - { - var allowedEntryCount = this.maxKeys - extraCapacity; - - if (this._lru.length <= allowedEntryCount) - return; - - var evictionIndex = this._lru.length - 1; - - for (;;) - { - var target = this._lru[evictionIndex]; - - if (!this._held[target.url]) - { - debug('Evicting image %s', target.url); - this._lru.splice(evictionIndex, 1); - delete this._urls[target.url]; - - if (this._lru.length <= allowedEntryCount) - break; - } - - if (evictionIndex === 0) - { - /* istanbul ignore next */ - debug.enabled && debug('Cache overfull by %s (all entries are being held)', - this._lru.length - allowedEntryCount); - - break; - } - - evictionIndex--; - } - }; - - ImageCache.prototype.acquire = function (url) - { - this._held[url] = (this._held[url] || 0) + 1; - this._promote(this._urls[url]); - }; - - ImageCache.prototype.release = function (url) - { - var count = this._held[url]; - - if (count > 1) - this._held[url]--; - else - delete this._held[url]; - - this._tryEvict(0); - }; - - -/***/ }), -/* 33 */ -/***/ (function(module, exports, __webpack_require__) { - - var debug = __webpack_require__(26)('diva:ImageRequestHandler'); - - module.exports = ImageRequestHandler; - - /** - * Handler for the request for an image tile - * - * @param url - * @param callback - * @constructor - */ - function ImageRequestHandler(options) - { - this._url = options.url; - this._callback = options.load; - this._errorCallback = options.error; - this.timeoutTime = options.timeoutTime || 0; - this._aborted = this._complete = false; - - //Use a timeout to allow the requests to be debounced (as they are in renderer) - this.timeout = setTimeout(function() - { - // Initiate the request - this._image = new Image(); - this._image.crossOrigin = "anonymous"; - this._image.onload = this._handleLoad.bind(this); - this._image.onerror = this._handleError.bind(this); - this._image.src = options.url; - - debug('Requesting image %s', options.url); - }.bind(this), this.timeoutTime); - } - - ImageRequestHandler.prototype.abort = function () - { - debug('Aborting request to %s', this._url); - - clearTimeout(this.timeout); - - // FIXME - // People on the Internet say that doing this {{should/should not}} abort the request. I believe - // it corresponds to what the WHATWG HTML spec says should happen when the UA - // updates the image data if selected source is null. - // - // Sources: - // - // https://html.spec.whatwg.org/multipage/embedded-content.html#the-img-element - // http://stackoverflow.com/questions/7390888/does-changing-the-src-attribute-of-an-image-stop-the-image-from-downloading - if (this._image) - { - this._image.onload = this._image.onerror = null; - - this._image.src = ''; - } - - this._aborted = true; - }; - - ImageRequestHandler.prototype._handleLoad = function () - { - if (this._aborted) - { - console.error('ImageRequestHandler invoked on cancelled request for ' + this._url); - return; - } - - if (this._complete) - { - console.error('ImageRequestHandler invoked on completed request for ' + this._url); - return; - } - - this._complete = true; - - debug('Received image %s', this._url); - this._callback(this._image); - }; - - ImageRequestHandler.prototype._handleError = function () - { - debug('Failed to load image %s', this._url); - this._errorCallback(this._image); - }; - - -/***/ }), -/* 34 */ -/***/ (function(module, exports) { - - /* global performance */ - - // TODO: requestAnimationFrame fallback - - module.exports = { - animate: animate, - easing: { - linear: linearEasing - } - }; - - function animate(options) - { - var durationMs = options.duration; - var parameters = options.parameters; - var onUpdate = options.onUpdate; - var onEnd = options.onEnd; - - // Setup - // Times are in milliseconds from a basically arbitrary start - var start = now(); - var end = start + durationMs; - - var tweenFns = {}; - var values = {}; - var paramKeys = Object.keys(parameters); - - paramKeys.forEach(function (key) - { - var config = parameters[key]; - tweenFns[key] = interpolate(config.from, config.to, config.easing || linearEasing); - }); - - // Run it! - var requestId = requestAnimationFrame(update); - - return { - cancel: function () - { - if (requestId !== null) - { - cancelAnimationFrame(requestId); - handleAnimationCompletion({ - interrupted: true - }); - } - } - }; - - function update() - { - var current = now(); - var elapsed = Math.min((current - start) / durationMs, 1); - - updateValues(elapsed); - onUpdate(values); - - if (current < end) - requestId = requestAnimationFrame(update); - else - handleAnimationCompletion({ - interrupted: false - }); - } - - function updateValues(elapsed) - { - paramKeys.forEach(function (key) - { - values[key] = tweenFns[key](elapsed); - }); - } - - function handleAnimationCompletion(info) - { - requestId = null; - - if (onEnd) - onEnd(info); - } - } - - function interpolate(start, end, easing) - { - return function (elapsed) - { - return start + (end - start) * easing(elapsed); - }; - } - - function linearEasing(e) - { - return e; - } - - var now; - - if (typeof performance !== 'undefined' && performance.now) - { - now = function () - { - return performance.now(); - }; - } - else - { - now = function () - { - return Date.now(); - }; - } - - -/***/ }), -/* 35 */ -/***/ (function(module, exports, __webpack_require__) { - - var getBookLayoutGroups = __webpack_require__(36); - var getSinglesLayoutGroups = __webpack_require__(38); - var getGridLayoutGroups = __webpack_require__(39); - - module.exports = getPageLayouts; - - /** Get the relative positioning of pages for the current view */ - function getPageLayouts(settings) - { - if (settings.inGrid) - { - return getGridLayoutGroups(pluck(settings, [ - 'manifest', - 'viewport', - 'pagesPerRow', - 'fixedHeightGrid', - 'fixedPadding', - 'showNonPagedPages' - ])); - } - else - { - var config = pluck(settings, ['manifest', 'verticallyOriented', 'showNonPagedPages']); - - if (settings.inBookLayout) - return getBookLayoutGroups(config); - else - return getSinglesLayoutGroups(config); - } - } - - function pluck(obj, keys) - { - var out = {}; - keys.forEach(function (key) - { - out[key] = obj[key]; - }); - return out; - } - - -/***/ }), -/* 36 */ -/***/ (function(module, exports, __webpack_require__) { - - var getPageDimensions = __webpack_require__(37); - - module.exports = getBookLayoutGroups; - - function getBookLayoutGroups(viewerConfig) - { - var groupings = getGroupings(viewerConfig); - - return groupings.map(function (grouping) - { - return getGroupLayoutsFromPageGrouping(viewerConfig, grouping); - }); - } - - function getGroupings(viewerConfig) - { - var manifest = viewerConfig.manifest; - - var pagesByGroup = []; - var leftPage = null; - var nonPagedPages = []; // Pages to display below the current group - - var _addNonPagedPages = function() - { - for (var i = 0; i < nonPagedPages.length; i++) - { - pagesByGroup.push([ nonPagedPages[i] ]); - } - nonPagedPages = []; - }; - - manifest.pages.forEach(function (page, index) - { - var pageRecord = { - index: index, - dimensions: getPageDimensions(index, manifest), - paged: (!manifest.paged || page.paged) - }; - - // Only display non-paged pages if specified in the settings - if (!viewerConfig.showNonPagedPages && !pageRecord.paged) - return; - - if (!pageRecord.paged) - { - nonPagedPages.push(pageRecord); - } - else if (index === 0 || page.facingPages) - { - // The first page is placed on its own - pagesByGroup.push([pageRecord]); - _addNonPagedPages(); - } - else if (leftPage === null) - { - leftPage = pageRecord; - } - else - { - pagesByGroup.push([leftPage, pageRecord]); - leftPage = null; - _addNonPagedPages(); - } - }); - - // Flush a final left page - if (leftPage !== null) - { - pagesByGroup.push([leftPage]); - _addNonPagedPages(); - } - - return pagesByGroup; - } - - function getGroupLayoutsFromPageGrouping(viewerConfig, grouping) - { - var verticallyOriented = viewerConfig.verticallyOriented; - - if (grouping.length === 2) - return getFacingPageGroup(grouping[0], grouping[1], verticallyOriented); - - var page = grouping[0]; - var pageDims = page.dimensions; - - // The first page is placed on its own to the right in vertical orientation. - // NB that this needs to be the page with index 0; if the first page is excluded - // from the layout then this special case shouldn't apply. - // If the page is tagged as 'non-paged', center it horizontally - var leftOffset; - if (page.paged) - leftOffset = (page.index === 0 && verticallyOriented) ? pageDims.width : 0; - else - leftOffset = (verticallyOriented) ? pageDims.width / 2 : 0; - - var shouldBeHorizontallyAdjusted = - verticallyOriented && !viewerConfig.manifest.pages[page.index].facingPages; - - // We need to left-align the page in vertical orientation, so we double - // the group width - return { - dimensions: { - height: pageDims.height, - width: shouldBeHorizontallyAdjusted ? pageDims.width * 2 : pageDims.width - }, - pages: [{ - index: page.index, - groupOffset: { - top: 0, - left: leftOffset - }, - dimensions: pageDims - }] - }; - } - - function getFacingPageGroup(leftPage, rightPage, verticallyOriented) - { - var leftDims = leftPage.dimensions; - var rightDims = rightPage.dimensions; - - var height = Math.max(leftDims.height, rightDims.height); - - var width, firstLeftOffset, secondLeftOffset; - - if (verticallyOriented) - { - var midWidth = Math.max(leftDims.width, rightDims.width); - - width = midWidth * 2; - - firstLeftOffset = midWidth - leftDims.width; - secondLeftOffset = midWidth; - } - else - { - width = leftDims.width + rightDims.width; - firstLeftOffset = 0; - secondLeftOffset = leftDims.width; - } - - return { - dimensions: { - height: height, - width: width - }, - pages: [ - { - index: leftPage.index, - dimensions: leftDims, - groupOffset: { - top: 0, - left: firstLeftOffset - } - }, - { - index: rightPage.index, - dimensions: rightDims, - groupOffset: { - top: 0, - left: secondLeftOffset - } - } - ] - }; - } - - -/***/ }), -/* 37 */ -/***/ (function(module, exports) { - - module.exports = function getPageDimensions(pageIndex, manifest) - { - var dims = manifest.getMaxPageDimensions(pageIndex); - - return { - width: Math.floor(dims.width), - height: Math.floor(dims.height) - }; - }; - - -/***/ }), -/* 38 */ -/***/ (function(module, exports, __webpack_require__) { - - var getPageDimensions = __webpack_require__(37); - - module.exports = function getSinglesLayoutGroups(viewerConfig) - { - var manifest = viewerConfig.manifest; - - // Render each page alone in a group - var pages = []; - manifest.pages.forEach(function (page, index) - { - if (!viewerConfig.showNonPagedPages && manifest.paged && !page.paged) - return; - - var pageDims = getPageDimensions(index, manifest); - - pages.push({ - dimensions: pageDims, - pages: [ - { - index: index, - groupOffset: {top: 0, left: 0}, - dimensions: pageDims - } - ] - }); - }); - - return pages; - }; - - -/***/ }), -/* 39 */ -/***/ (function(module, exports) { - - module.exports = getGridLayoutGroups; - - function getGridLayoutGroups(viewerConfig) - { - var viewportWidth = viewerConfig.viewport.width; - var manifest = viewerConfig.manifest; - var pagesPerRow = viewerConfig.pagesPerRow; - var fixedHeightGrid = viewerConfig.fixedHeightGrid; - var fixedPadding = viewerConfig.fixedPadding; - var showNonPagedPages = viewerConfig.showNonPagedPages; - - var horizontalPadding = fixedPadding * (pagesPerRow + 1); - var pageWidth = (viewportWidth - horizontalPadding) / pagesPerRow; - var gridPageWidth = pageWidth; - - // Calculate the row height depending on whether we want to fix the width or the height - var rowHeight = (fixedHeightGrid) ? fixedPadding + manifest.minRatio * pageWidth : fixedPadding + manifest.maxRatio * pageWidth; - - var groups = []; - var currentPages = []; - - var getGridPageDimensions = function (pageData) - { - // Calculate the width, height and horizontal placement of this page - // Get dimensions at max zoom level, although any level should be fine - var pageDimenData = pageData.d[pageData.d.length - 1]; - var heightToWidthRatio = pageDimenData.h / pageDimenData.w; - - var pageWidth, pageHeight; - - if (fixedHeightGrid) - { - pageWidth = (rowHeight - fixedPadding) / heightToWidthRatio; - pageHeight = rowHeight - fixedPadding; - } - else - { - pageWidth = gridPageWidth; - pageHeight = pageWidth * heightToWidthRatio; - } - - return { - width: Math.round(pageWidth), - height: Math.round(pageHeight) - }; - }; - - var rowDimensions = { - height: rowHeight, - width: viewportWidth - }; - - manifest.pages.forEach(function (page, pageIndex) - { - if (!showNonPagedPages && manifest.paged && !page.paged) - return; - - // Calculate the width, height and horizontal placement of this page - var pageDimens = getGridPageDimensions(page); - var leftOffset = Math.floor(currentPages.length * (fixedPadding + gridPageWidth) + fixedPadding); - - // Center the page if the height is fixed (otherwise, there is no horizontal padding) - if (fixedHeightGrid) - { - leftOffset += (gridPageWidth - pageDimens.width) / 2; - } - - // TODO: Precompute page dimensions everywhere - currentPages.push({ - index: pageIndex, - dimensions: pageDimens, - groupOffset: { - top: 0, - left: leftOffset - } - }); - - if (currentPages.length === pagesPerRow) - { - groups.push({ - dimensions: rowDimensions, - pages: currentPages - }); - - currentPages = []; - } - }); - - if (currentPages.length > 0) - { - groups.push({ - dimensions: rowDimensions, - pages: currentPages - }); - } - - return groups; - } - - -/***/ }), -/* 40 */ -/***/ (function(module, exports) { - - module.exports = createSettingsView; - - function createSettingsView(sources) - { - var obj = {}; - - sources.forEach(function (source) - { - registerMixin(obj, source); - }); - - return obj; - } - - function registerMixin(obj, mixin) - { - Object.keys(mixin).forEach(function (key) - { - Object.defineProperty(obj, key, { - get: function () - { - return mixin[key]; - }, - set: function () - { - // TODO: Make everything strict mode so this isn't needed - throw new TypeError('Cannot set settings.' + key); - } - }); - }); - } - - -/***/ }), -/* 41 */ -/***/ (function(module, exports, __webpack_require__) { - - var extend = __webpack_require__(3).extend; - - module.exports = ValidationRunner; - - function ValidationRunner(options) - { - this.whitelistedKeys = options.whitelistedKeys || []; - this.additionalProperties = options.additionalProperties || []; - this.validations = options.validations; - } - - ValidationRunner.prototype.isValid = function (key, value, settings) - { - // Get the validation index - var validationIndex = null; - - this.validations.some(function (validation, index) - { - if (validation.key !== key) - return false; - - validationIndex = index; - return true; - }); - - if (validationIndex === null) - return true; - - // Run the validation - var dummyChanges = {}; - dummyChanges[key] = value; - var proxier = createSettingsProxier(settings, dummyChanges, this); - - return !this._runValidation(validationIndex, value, proxier); - }; - - ValidationRunner.prototype.validate = function (settings) - { - this._validateOptions({}, settings); - }; - - ValidationRunner.prototype.getValidatedOptions = function (settings, options) - { - var cloned = extend({}, options); - this._validateOptions(settings, cloned); - return cloned; - }; - - ValidationRunner.prototype._validateOptions = function (settings, options) - { - var settingsProxier = createSettingsProxier(settings, options, this); - this._applyValidations(options, settingsProxier); - }; - - ValidationRunner.prototype._applyValidations = function (options, proxier) - { - this.validations.forEach(function (validation, index) - { - if (!options.hasOwnProperty(validation.key)) - return; - - var input = options[validation.key]; - var corrected = this._runValidation(index, input, proxier); - - if (corrected) - { - if (!corrected.warningSuppressed) - emitWarning(validation.key, input, corrected.value); - - options[validation.key] = corrected.value; - } - }, this); - }; - - ValidationRunner.prototype._runValidation = function (index, input, proxier) - { - var validation = this.validations[index]; - - proxier.index = index; - - var warningSuppressed = false; - var config = { - suppressWarning: function () - { - warningSuppressed = true; - } - }; - - var outputValue = validation.validate(input, proxier.proxy, config); - - if (outputValue === undefined || outputValue === input) - return null; - - return { - value: outputValue, - warningSuppressed: warningSuppressed - }; - }; - - /** - * The settings proxy wraps the settings object and ensures that - * only values which have previously been validated are accessed, - * throwing a TypeError otherwise. - * - * FIXME(wabain): Is it worth keeping this? When I wrote it I had - * multiple validation stages and it was a lot harder to keep track - * of everything, so this was more valuable. - */ - function createSettingsProxier(settings, options, runner) - { - var proxier = { - proxy: {}, - index: null - }; - - var lookup = lookupValue.bind(null, settings, options); - - var properties = {}; - - runner.whitelistedKeys.forEach(function (whitelisted) - { - properties[whitelisted] = { - get: lookup.bind(null, whitelisted) - }; - }); - - runner.additionalProperties.forEach(function (additional) - { - properties[additional.key] = { - get: additional.get - }; - }); - - runner.validations.forEach(function (validation, validationIndex) - { - properties[validation.key] = { - get: function () - { - if (validationIndex < proxier.index) - return lookup(validation.key); - - var currentKey = runner.validations[proxier.index].key; - throw new TypeError('Cannot access setting ' + validation.key + ' while validating ' + currentKey); - } - }; - }); - - Object.defineProperties(proxier.proxy, properties); - - return proxier; - } - - function emitWarning(key, original, corrected) - { - console.warn('Invalid value for ' + key + ': ' + original + '. Using ' + corrected + ' instead.'); - } - - function lookupValue(base, extension, key) - { - if (key in extension) - return extension[key]; - - return base[key]; - } - - -/***/ }), -/* 42 */ -/***/ (function(module, exports) { - - module.exports = Viewport; - - function Viewport(outer, options) - { - options = options || {}; - - this.intersectionTolerance = options.intersectionTolerance || 0; - this.maxExtent = options.maxExtent || 2000; - - this.outer = outer; - - this._top = this._left = this._width = this._height = this._innerDimensions = null; - - this.invalidate(); - } - - Viewport.prototype.intersectsRegion = function (region) - { - return this.hasHorizontalOverlap(region) && this.hasVerticalOverlap(region); - }; - - Viewport.prototype.hasVerticalOverlap = function (region) - { - var top = this.top - this.intersectionTolerance; - var bottom = this.bottom + this.intersectionTolerance; - - return ( - fallsBetween(region.top, top, bottom) || - fallsBetween(region.bottom, top, bottom) || - (region.top <= top && region.bottom >= bottom) - ); - }; - - Viewport.prototype.hasHorizontalOverlap = function (region) - { - var left = this.left - this.intersectionTolerance; - var right = this.right + this.intersectionTolerance; - - return ( - fallsBetween(region.left, left, right) || - fallsBetween(region.right, left, right) || - (region.left <= left && region.right >= right) - ); - }; - - Viewport.prototype.invalidate = function () - { - // FIXME: Should this check the inner dimensions as well? - this._width = clampMax(this.outer.clientWidth, this.maxExtent); - this._height = clampMax(this.outer.clientHeight, this.maxExtent); - - this._top = this.outer.scrollTop; - this._left = this.outer.scrollLeft; - }; - - Viewport.prototype.setInnerDimensions = function (dimensions) - { - this._innerDimensions = dimensions; - - if (dimensions) - { - this._top = clamp(this._top, 0, dimensions.height - this._height); - this._left = clamp(this._left, 0, dimensions.width - this._width); - } - }; - - Object.defineProperties(Viewport.prototype, { - top: getCoordinateDescriptor('top', 'height'), - left: getCoordinateDescriptor('left', 'width'), - - width: getDimensionDescriptor('width'), - height: getDimensionDescriptor('height'), - - bottom: { - get: function () - { - return this._top + this._height; - } - }, - right: { - get: function () - { - return this._left + this._width; - } - } - }); - - function getCoordinateDescriptor(coord, associatedDimension) - { - var privateProp = '_' + coord; - var source = 'scroll' + coord.charAt(0).toUpperCase() + coord.slice(1); - - return { - get: function () - { - return this[privateProp]; - }, - set: function (newValue) - { - var normalized; - - if (this._innerDimensions) - { - var maxAllowed = this._innerDimensions[associatedDimension] - this[associatedDimension]; - normalized = clamp(newValue, 0, maxAllowed); - } - else - { - normalized = clampMin(newValue, 0); - } - - this[privateProp] = this.outer[source] = normalized; - } - }; - } - - function getDimensionDescriptor(dimen) - { - return { - get: function () - { - return this['_' + dimen]; - } - }; - } - - function fallsBetween(point, start, end) - { - return point >= start && point <= end; - } - - function clamp(value, min, max) - { - return clampMin(clampMax(value, max), min); - } - - function clampMin(value, min) - { - return Math.max(value, min); - } - - function clampMax(value, max) - { - return Math.min(value, max); - } - - -/***/ }), -/* 43 */ -/***/ (function(module, exports, __webpack_require__) { - - /* - - Canvas plugin for diva.js - Adds an adjustment icon next to each image - - */ - - var jQuery = __webpack_require__(3); - var diva = __webpack_require__(7); - - __webpack_require__(16); - - (function ($) - { - module.exports = (function () - { - var canvas = {}, - map = {}, - settings = {}, - image, - sliders, - sliderMode; - - // Set up some default settings (can be overridden the normal way) - var defaults = { - brightnessMax: 150, - brightnessMin: -100, - brightnessStep: 1, - contrastMax: 3, - contrastMin: -1, - contrastStep: 0.05, - localStoragePrefix: 'canvas-', - mobileWebkitMaxZoom: 2, - rgbMax: 50, - rgbMin: -50, - throbberFadeSpeed: 200, - throbberTimeout: 100, - buttons: [ - 'contrast', - 'brightness', - 'rotation', - 'zoom' - ] - }; - - // Convert an angle from degrees to radians - var toRadians = function (angle) - { - return angle * Math.PI / 180; - }; - - // Determine the new center of the page after rotating by the given angle - var getNewCenter = function (currentCenter, angle) - { - var x = currentCenter.x - canvas.centerX; - // Take the negative because the rotation is counterclockwise - var y = -(currentCenter.y - canvas.centerY); - - var theta = toRadians(sliders.rotation.previous - angle); - var newX = Math.cos(theta) * x - Math.sin(theta) * y + canvas.centerX; - var newY = -(Math.sin(theta) * x + Math.cos(theta) * y) + canvas.centerY; - - return {'x': newX, 'y': newY}; - }; - - // Rotates the image on the given canvas by the given angle - var rotateCanvas = function (aCanvas, angle) - { - var context = aCanvas.context; - var center = aCanvas.size / 2; - var startX = -(aCanvas.width / 2); - var startY = -(aCanvas.height / 2); - - // Clear the canvas so that remnants of the old image don't show - context.clearRect(0, 0, aCanvas.size, aCanvas.size); - - // Do the rotation - context.save(); - context.translate(center, center); - context.rotate(toRadians(angle)); - context.drawImage(image, startX, startY, aCanvas.width, aCanvas.height); - context.restore(); - - // Save the new pixel data so that it can later be adjusted in adjustLevels - aCanvas.data = context.getImageData(0, 0, aCanvas.size, aCanvas.size); - }; - - // Determine if we need to update the large canvas - var shouldAdjustLevels = function () - { - var slider; - - // Returns true if something has been changed - for (slider in sliders) - { - if (sliders[slider].current !== sliders[slider].previous) - { - return true; - } - } - - return false; - }; - - // Sets the "previous" value to the "current" value for every slider - var updatePreviousLevels = function () - { - var slider; - - for (slider in sliders) - { - sliders[slider].previous = sliders[slider].current; - } - }; - - // Update the thumbnail preview (called when a slider is moved/reset) - var updateMap = function () - { - rotateCanvas(map, sliders.rotation.current); - adjustLevels(map); - }; - - // Update the large canvas (rotation, zooming, scrolling, pixel manipulation) - var updateCanvas = function () - { - var angle = sliders.rotation.current; - var oldAngle = sliders.rotation.previous; - var zoomLevel = sliders.zoom.current; - var oldZoomLevel = sliders.zoom.previous; - - // Scroll the user to the desired location - if (angle !== oldAngle || zoomLevel !== oldZoomLevel) - { - // First figure out the current center of the viewport - var leftScroll = $('#diva-canvas-wrapper').scrollLeft(); - var topScroll = $('#diva-canvas-wrapper').scrollTop(); - var leftOffset = settings.viewport.width / 2; - var topOffset = settings.viewport.height / 2; - - // Then determine the new center (the same part of the image) - var newCenter = getNewCenter({x: leftScroll + leftOffset, y: topScroll + topOffset}, angle); - - // Incorporate the zoom change ratio (would be 1 if no change) - var zoomChange = Math.pow(2, zoomLevel - oldZoomLevel); - var toLeftScroll = zoomChange * newCenter.x - leftOffset; - var toTopScroll = zoomChange * newCenter.y - topOffset; - - // Rotate the large canvas - rotateCanvas(canvas, angle); - - // Scroll to the new center - $('#diva-canvas-wrapper').scrollLeft(toLeftScroll); - $('#diva-canvas-wrapper').scrollTop(toTopScroll); - } - - // Only call adjustLevels again if we really need to (expensive) - if (shouldAdjustLevels()) - { - adjustLevels(canvas); - updatePreviousLevels(); - } - }; - - // Copies the canvas' pixel array and returns the copy - var copyImageData = function (aCanvas) - { - var oldImageData = aCanvas.data; - var newImageData = aCanvas.context.createImageData(oldImageData); - var pixelArray = newImageData.data; - var i, length; - - for (i = 0, length = pixelArray.length; i < length; i++) - { - pixelArray[i] = oldImageData.data[i]; - } - - return newImageData; - }; - - // Determines whether or not we need to adjust this level - very simple - var shouldAdjust = function (mode) - { - var thisChanged = sliders[mode].current !== sliders[mode].previous; - var thisNotDefault = sliders[mode].current !== sliders[mode].initial; - - return thisChanged || thisNotDefault; - }; - - var adjustLevels = function (aCanvas) - { - // Copy the pixel array to avoid destructively modifying the original - var imageData = copyImageData(aCanvas); - var pixelArray = imageData.data; - - // Store and calculate some scale factors and offsets - var brightness = sliders.brightness.current; - var contrast = sliders.contrast.current; - - var brightMul = 1 + Math.min(settings.brightnessMax, Math.max(settings.brightnessMin, brightness)) / settings.brightnessMax; - var brightTimesContrast = brightMul * contrast; - var contrastOffset = 128 - (contrast * 128); - - var redOffset = sliders.red.current; - var greenOffset = sliders.green.current; - var blueOffset = sliders.blue.current; - - // Determine whether or not we need to adjust certain things - var adjustRed = shouldAdjust('red'); - var adjustGreen = shouldAdjust('green'); - var adjustBlue = shouldAdjust('blue'); - - var adjustBrightness = shouldAdjust('brightness'); - var adjustContrast = shouldAdjust('contrast'); - var adjustOthers = adjustBrightness || adjustContrast; - - var x, y, width, height, offset, r, g, b; - - for (x = 0, width = imageData.width; x < width; x++) - { - for (y = 0, height = imageData.height; y < height; y++) - { - offset = (y * width + x) * 4; - - r = pixelArray[offset]; - g = pixelArray[offset + 1]; - b = pixelArray[offset + 2]; - - // Only do something if the pixel is not black originally - if (r + g + b > 0) - { - // Only adjust individual colour channels if necessary - if (adjustRed && r) - r += redOffset; - - if (adjustGreen && g) - g += greenOffset; - - if (adjustBlue && b) - b += blueOffset; - - // If we need to adjust brightness and/or contrast - if (adjustOthers) - { - if (r) - r = r * brightTimesContrast + contrastOffset; - - if (g) - g = g * brightTimesContrast + contrastOffset; - - if (b) - b = b * brightTimesContrast + contrastOffset; - } - - pixelArray[offset] = r; - pixelArray[offset + 1] = g; - pixelArray[offset + 2] = b; - } - } - } - - aCanvas.context.clearRect(0, 0, width, height); - aCanvas.context.putImageData(imageData, 0, 0); - }; - - // Update the box in the preview showing where you currently are - var updateViewbox = function () - { - // Determine the top left corner coordinates based on our current position - var cornerX = $('#diva-canvas-wrapper').scrollLeft() * map.scaleFactor; - var cornerY = $('#diva-canvas-wrapper').scrollTop() * map.scaleFactor; - - // Subtract 4 to compensate for the borders - var height = Math.min(Math.round(settings.viewport.height * map.scaleFactor), settings.mapSize) - 4; - var width = Math.min(Math.round(settings.viewport.width * map.scaleFactor), settings.mapSize) - 4; - - $('#diva-map-viewbox').height(height).width(width).css({top: cornerY, left: cornerX}); - }; - - // Draw the thumbnail preview in the toolbar - var loadMap = function (image) - { - map.canvas = document.getElementById('diva-canvas-minimap'); - map.size = settings.mapSize; - map.canvas.width = map.size; - map.canvas.height = map.size; - - // Give it a black background - map.context = map.canvas.getContext('2d'); - map.context.fillRect(0, 0, map.size, map.size); - - // Determine the coordinates/dimensions of the preview - map.scaleFactor = settings.mapSize / canvas.size; - map.cornerX = canvas.cornerX * map.scaleFactor; - map.cornerY = canvas.cornerY * map.scaleFactor; - map.width = image.width * map.scaleFactor; - map.height = image.height * map.scaleFactor; - - // Draw the image within the map (no adjustments) and save the pixel array - map.context.drawImage(image, map.cornerX, map.cornerY, map.width, map.height); - map.data = map.context.getImageData(0, 0, settings.mapSize, settings.mapSize); - - // Show the viewbox, make it reflect where we currently are - $('#diva-map-viewbox').show(); - updateViewbox(); - }; - - // Load the image within the large and small canvases - var loadCanvas = function (imageURL, callback) - { - image = new Image(); - image.crossOrigin = "anonymous"; - - image.onload = function () - { - // Determine the size of the (square) canvas based on the hypoteneuse - canvas.size = Math.sqrt(image.width * image.width + image.height * image.height); - - // Resize the canvas if necessary - canvas.canvas = document.getElementById('diva-canvas'); - canvas.canvas.width = canvas.size; - canvas.canvas.height = canvas.size; - canvas.cornerX = (canvas.size - image.width) / 2; - canvas.cornerY = (canvas.size - image.height) / 2; - canvas.width = image.width; - canvas.height = image.height; - canvas.centerX = canvas.size / 2; - canvas.centerY = canvas.size / 2; - - // Draw the image to the large canvas, and save the pixel array - canvas.context = canvas.canvas.getContext('2d'); - canvas.context.drawImage(image, canvas.cornerX, canvas.cornerY, canvas.width, canvas.height); - try - { - canvas.data = canvas.context.getImageData(0, 0, canvas.size, canvas.size); - } - catch (error) - { - var canvasError = '

Error

' + error.message + '

'; - - if (error.name === 'SecurityError') - { - canvasError += '

You may need to update your server configuration in order to use the image manipulation tools. ' + - 'For help, see the canvas cross-site data documentation.

' + - '
'; - } - else - { - throw error; - } - - canvasError += ''; - $('#diva-canvas-backdrop').append(canvasError); - hideThrobber(); - } - - // Only load the map the first time (when there is no callback) - if (callback === undefined) { - loadMap(image); - } - - // Update the map and the canvas if necessary - updateMap(); - updateCanvas(canvas); - - // Hide the throbber if it is visible - hideThrobber(); - - // If the callback function exists, execute it (for zooming) - if (typeof callback === 'function') - callback.call(callback); - }; - - image.src = imageURL; - - // make sure the load event fires for cached images too - if ( image.complete || image.complete === undefined ) { - image.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="; - image.src = imageURL; - } - }; - - var updateSliderLabel = function () - { - var thisSlider = sliders[sliderMode]; - var value = thisSlider.current; - var stringValue = (thisSlider.transform) ? thisSlider.transform(value) : value; - $('#diva-canvas-value').html(stringValue); - }; - - var updateSliderValue = function () - { - $('#diva-canvas-slider').val(sliders[sliderMode].current); - }; - - // Returns the URL for the image at the specified zoom level - var getImageURL = function (zoomLevel) - { - var width = settings.zoomWidthRatio * Math.pow(2, zoomLevel); - - return settings.divaInstance.getPageImageURL(settings.selectedPageIndex, { width: width }); - }; - - var showThrobber = function () - { - // Only show the throbber if it will take a long time - if (sliders.zoom.current > 0 || settings.mobileWebkit) - $(settings.selector + 'throbber').addClass('canvas-throbber').show(); - }; - - // Hides the loading indicator icon - var hideThrobber = function () - { - $(settings.selector + 'throbber').removeClass('canvas-throbber').hide(); - }; - - // If any modifications have been applied, save them to localStorage - var saveSettings = function () - { - var sliderSettings = {}; - var changed = false; - var storageKey = settings.localStoragePrefix + settings.filename; - var slider; - - for (slider in sliders) - { - if (sliders[slider].previous !== sliders[slider].initial) - { - sliderSettings[slider] = sliders[slider].previous; - changed = true; - } - } - - // If modifications need to be saved, update the canvas plugin icon - if (changed) - { - settings.pluginIcon.addClass('new'); - storeObject(storageKey, sliderSettings); - } - else - { - settings.pluginIcon.removeClass('new'); - localStorage.removeItem(storageKey); - } - }; - - // Handles zooming in when the zoom slider is changed and the change is applied - var updateZoom = function (newZoomLevel, callback) - { - settings.zoomLevel = newZoomLevel; - - // Figure out the URL for the image at this new zoom level - var imageURL = getImageURL(newZoomLevel); - - loadCanvas(imageURL, function () - { - // Set the new scale factor and update the viewbox - map.scaleFactor = map.size / canvas.size; - updateViewbox(); - - saveSettings(); - }); - }; - - var bindCanvasKeyEvents = function (event) - { - var upArrowKey = 38, - downArrowKey = 40, - leftArrowKey = 37, - rightArrowKey = 39; - - switch (event.keyCode) - { - case upArrowKey: - // Up arrow - scroll up - $('#diva-canvas-wrapper').scrollTop(document.getElementById('diva-canvas-wrapper').scrollTop - settings.arrowScrollAmount); - return false; - - case downArrowKey: - // Down arrow - scroll down - $('#diva-canvas-wrapper').scrollTop(document.getElementById('diva-canvas-wrapper').scrollTop + settings.arrowScrollAmount); - return false; - - case leftArrowKey: - // Left arrow - scroll left - $('#diva-canvas-wrapper').scrollLeft(document.getElementById('diva-canvas-wrapper').scrollLeft - settings.arrowScrollAmount); - return false; - - case rightArrowKey: - // Right arrow - scroll right - $('#diva-canvas-wrapper').scrollLeft(document.getElementById('diva-canvas-wrapper').scrollLeft + settings.arrowScrollAmount); - return false; - } - }; - - // Serialize an object to JSON and save it in localStorage - var storeObject = function (key, value) { - localStorage.setItem(key, JSON.stringify(value)); - }; - - // Load and deserialize a localStorage object - var loadStoredObject = function (key) { - var value = localStorage.getItem(key); - return value && JSON.parse(value); - }; - - var retval = - { - init: function (divaSettings, divaInstance) - { - // If the browser does not support canvas, do nothing - // And, disable this plugin - var canvasSupported = !!window.HTMLCanvasElement; - if (!canvasSupported) - return false; - - // Override all the configurable settings defined under canvasPlugin - $.extend(settings, defaults, divaSettings.canvasPlugin); - - settings.divaInstance = divaInstance; - settings.inCanvas = false; - settings.iipServerURL = divaSettings.iipServerURL; - settings.imageDir = divaSettings.imageDir; - settings.selector = divaSettings.selector; - settings.mobileWebkit = divaSettings.mobileWebkit; - settings.arrowScrollAmount = divaSettings.arrowScrollAmount; - - // Set up the settings for the sliders/icons - sliders = { - 'contrast': { - 'initial': 1, - 'min': settings.contrastMin, - 'max': settings.contrastMax, - 'step': settings.contrastStep, - 'transform': function (value) { - return value.toFixed(2); - }, - 'title': 'Change the contrast' - }, - 'brightness': { - 'initial': 0, - 'min': settings.brightnessMin, - 'max': settings.brightnessMax, - 'step': settings.brightnessStep, - 'title': 'Adjust the brightness' - }, - 'rotation': { - 'initial': 0, - 'min': 0, - 'max': 359, - 'step': 1, - 'transform': function (value) { - return value + '°'; - }, - 'title': 'Rotate the image' - }, - 'zoom': { - // Default, min and max values updated within setupHook - 'initial': 0, - 'min': 0, - 'max': 0, - 'step': 1, - 'title': 'Adjust the zoom level' - }, - 'red': { - 'initial': 0, - 'min': settings.rgbMin, - 'max': settings.rgbMax, - 'step': 1, - 'title': 'Adjust the red channel' - }, - 'green': { - 'initial': 0, - 'min': settings.rgbMin, - 'max': settings.rgbMax, - 'step': 1, - 'title': 'Adjust the green channel' - }, - 'blue': { - 'initial': 0, - 'min': settings.rgbMin, - 'max': settings.rgbMax, - 'step': 1, - 'title': 'Adjust the blue channel' - } - }; - - // Copy the "default" value into "value" and "previous" for each slider - var resetSliders = function () - { - var defaultValue, thisSlider, slider; - for (slider in sliders) - { - thisSlider = sliders[slider]; - defaultValue = thisSlider.initial; - thisSlider.current = defaultValue; - thisSlider.previous = defaultValue; - } - }; - - resetSliders(); - - // Create the DOM elements if they haven't already been created - if ($('#diva-canvas-backdrop').length) - { - // Return true to keep the plugin enabled - return true; - } - - var canvasButtonsList = []; - var buttonHTML, button, buttonTitle, i; - - for (i in settings.buttons) - { - button = settings.buttons[i]; - buttonTitle = sliders[button].title; - buttonHTML = '
'; - canvasButtonsList.push(buttonHTML); - } - var canvasButtons = canvasButtonsList.join(''); - - var canvasTools = '
' + - '
' + - '
' + - '
' + - 'Test' + - '
' + - '
' + - '
' + - '' + - '
' + - canvasButtons + - '
' + - '
' + - '

' + - 'contrast: ' + - '0 ' + - '(Reset)' + - '

' + - '' + - '
' + - '
' + - '
' + - 'Reset all' + - 'Apply' + - '
' + - '
' + - '
'; - var canvasWrapper = '
' + - '' + - '
'; - var canvasString = '
' + - canvasTools + - canvasWrapper + - '
'; - - $('body').append(canvasString); - - // Save the size of the map, as defined in the CSS - settings.mapSize = $('#diva-canvas-minimap').width(); - - // Adjust the slider when something is clicked, and make that the current mode - $('#diva-canvas-buttons div').click(function () - { - $('#diva-canvas-buttons .clicked').removeClass('clicked'); - updateSlider($(this).attr('class')); - }); - - var updateSlider = function (newMode) - { - sliderMode = newMode; - var sliderData = sliders[sliderMode]; - - $('#diva-canvas-buttons .' + sliderMode).addClass('clicked'); - - $('#diva-canvas-mode').text(sliderMode); - - var newValue = sliderData.current; - var newValueString = (sliderData.transform) ? sliderData.transform(newValue) : newValue; - - var slider = document.getElementById('diva-canvas-slider'); - slider.min = sliderData.min; - slider.max = sliderData.max; - slider.step = sliderData.step; - $('#diva-canvas-slider').val(newValue); - $('#diva-canvas-value').html(newValueString); - }; - - updateSlider('contrast'); - - // Create the slider - $('#diva-canvas-slider').on('input', function(e){ - sliders[sliderMode].current = parseFloat(this.value); - updateSliderLabel(); - updateMap(); - }); - - // Reset all the sliders to the default value - $('#diva-canvas-reset-all').click(function () - { - var slider; - - for (slider in sliders) - { - sliders[slider].current = sliders[slider].initial; - } - - // Change the value of the label - updateSliderLabel(); - updateSliderValue(); - - // Update the preview - updateMap(); - }); - - // Reset the current slider to the default value - $('#diva-canvas-reset').click(function () - { - // Update the current value and the slider - sliders[sliderMode].current = sliders[sliderMode].initial; - updateSliderLabel(); - updateSliderValue(); - - // Update the preview - updateMap(); - }); - - // Update the large canvas when the apply button is clicked - $('#diva-canvas-apply').click(function () - { - if (shouldAdjustLevels()) - { - showThrobber(); - - setTimeout(function () - { - if (sliders.zoom.current !== sliders.zoom.previous) - { - updateZoom(sliders.zoom.current); - } - else - { - updateCanvas(); - hideThrobber(); - - // Save modifications to localSettings (also done in updateZoom callback) - saveSettings(); - } - }, settings.throbberTimeout); - } - }); - - // Handle exiting canvas mode - $('#diva-canvas-close').click(function () - { - $('body').removeClass('overflow-hidden'); - - // Clear the canvases and hide things - // This needs to be improved - not done properly? - canvas.context.clearRect(0, 0, canvas.size, canvas.size); - map.context.clearRect(0, 0, map.size, map.size); - $('#diva-canvas-wrapper').scrollTop(0).scrollLeft(0); - $('#diva-canvas-backdrop').hide(); - $('#diva-map-viewbox').hide(); - hideThrobber(); - - // Re-enable scrolling of diva when it is in the background - divaInstance.enableScrollable(); - $(document).off('keydown', bindCanvasKeyEvents); - - // Reset everything - resetSliders(); - updateSliderLabel(); - updateSliderValue(); - $('#diva-canvas-buttons .clicked').removeClass('clicked'); - updateSlider('contrast'); - - diva.Events.publish("CanvasViewDidHide"); - }); - - // Hide the toolbar when the minimise icon is clicked - $('#diva-canvas-minimise').click(function () - { - $('#diva-canvas-toolwindow').slideToggle('fast'); - }); - - // Adjust the size of the canvas when the browser window is resized - $(window).resize(function () - { - settings.viewport = { - height: window.innerHeight - divaSettings.scrollbarWidth, - width: window.innerWidth - divaSettings.scrollbarWidth - }; - - // Always update the settings but only redraw if in canvas - if (settings.inCanvas) - updateViewbox(); - }); - - // Update the viewbox when the large canvas is scrolled - $('#diva-canvas-wrapper').scroll(function () - { - if (settings.inCanvas) - updateViewbox(); - }); - - // Handle clicking/dragging of the viewbox (should scroll the large canvas) - $('#diva-canvas-minimap, #diva-map-viewbox').mouseup(function (event) - { - // Consider caching this eventually (can't be done in init though) - var offset = $('#diva-canvas-minimap').offset(); - - var scaledX = (event.pageX - offset.left) / map.scaleFactor; - var scaledY = (event.pageY - offset.top) / map.scaleFactor; - - $('#diva-canvas-wrapper').scrollTop(scaledY - settings.viewport.height / 2); - $('#diva-canvas-wrapper').scrollLeft(scaledX - settings.viewport.width / 2); - }); - - // Enable drag scroll - $('#diva-canvas').mousedown(function () - { - $(this).addClass('grabbing'); - }).mouseup(function () - { - $(this).removeClass('grabbing'); - }); - - // touch events - $('#diva-canvas-wrapper').kinetic(); - - // mouse events - $('#diva-canvas-wrapper').dragscrollable({ - acceptPropagatedEvent: true - }); - - diva.Events.subscribe('ObjectDidLoad', this.setupHook, divaSettings.ID); - diva.Events.subscribe('ViewerDidTerminate', this.destroy, divaSettings.ID); - diva.Events.subscribe('PageDidLoad', this.onPageLoad, divaSettings.ID); - - return true; - }, - - pluginName: 'canvas', - - titleText: 'View the image on a canvas and adjust various settings', - - setupHook: function(divaSettings) - { - settings.viewport = { - height: window.innerHeight - divaSettings.scrollbarWidth, - width: window.innerWidth - divaSettings.scrollbarWidth - }; - - // Save the min and max zoom level, and update the zoom slider - settings.minZoomLevel = divaSettings.minZoomLevel; - settings.maxZoomLevel = divaSettings.maxZoomLevel; - - // If we're on the iPad, limit the max zoom level to 2 - // Can't do canvas elements that are > 5 megapixels (issue #112) - if (settings.mobileWebkit) - settings.maxZoomLevel = Math.min(settings.maxZoomLevel, settings.mobileWebkitMaxZoom); - - sliders.zoom.min = settings.minZoomLevel; - sliders.zoom.max = settings.maxZoomLevel; - }, - - handleClick: function(event, divaSettings, divaInstance, selectedPageIndex) - { - // loadCanvas() calls all the other necessary functions to load - var filename = divaInstance.getFilenames()[selectedPageIndex]; - - // TODO: Move rationale for -1 from Wiki (TLDR an old IIP bug) - var width = divaInstance - .getPageDimensions(selectedPageIndex) - .width - 1; - - var zoomLevel = divaSettings.zoomLevel; - var slider; - - settings.zoomWidthRatio = width / Math.pow(2, zoomLevel); - settings.pluginIcon = $(this); - - settings.manifest = divaSettings.manifest; - settings.selectedPageIndex = selectedPageIndex; - - // Limit the max zoom level if we're on the iPad - if (settings.mobileWebkit) { - zoomLevel = Math.min(settings.maxZoomLevel, zoomLevel); - } - - settings.filename = filename; - sliders.zoom.initial = zoomLevel; - sliders.zoom.current = zoomLevel; - - // Find the settings stored in localStorage, if they exist - var sliderSettings = loadStoredObject(settings.localStoragePrefix + settings.filename); - if (sliderSettings) - { - for (slider in sliderSettings) - { - sliders[slider].current = sliderSettings[slider]; - - // If the current slider's value has changed, update it - if (slider === sliderMode) - { - updateSliderLabel(); - updateSliderValue(); - } - - if (slider === 'zoom') - { - zoomLevel = sliderSettings[slider]; - } - } - } - - sliders.zoom.previous = zoomLevel; - - // Prevent scroll in body, and show the canvas backdrop - $('body').addClass('overflow-hidden'); - $('#diva-canvas-backdrop').show(); - - // Disable scrolling on main diva instance - divaInstance.disableScrollable(); - // Enable canvas scrolling - $(document).keydown(bindCanvasKeyEvents); - - // Set this to true so events can be captured - settings.inCanvas = true; - - var imageURL = getImageURL(zoomLevel); - - // Change the title of the page - // FIXME: This is legacy behaviour. Should this be a filename/label? - $('#diva-canvas-info').text('Page ' + (selectedPageIndex + 1)); - - showThrobber(); - - diva.Events.publish('CanvasViewDidActivate', [selectedPageIndex]); - - loadCanvas(imageURL); - }, - - onPageLoad: function(pageIndex, filename, selector) - { - // If something exists for this page in localStorage, then change icon color - var storageKey = settings.localStoragePrefix + filename; - - if (localStorage.getItem(storageKey) !== null) - { - $(selector).find('.diva-canvas-icon').addClass('new'); - } - }, - - destroy: function(divaSettings, divaInstance) - { - $('#diva-canvas-backdrop').remove(); - } - }; - - // this returns an object with all of the necessary hooks and callbacks - // embedded. - return retval; - - })(); - })(jQuery); - - -/***/ }), -/* 44 */ -/***/ (function(module, exports, __webpack_require__) { - - /* - Download plugin for diva.js - Allows you to download images served by IIPImage or IIIF compatible image servers - */ - - var jQuery = __webpack_require__(3); - - (function ($) - { - module.exports = (function() - { - var settings = {}; - var retval = - { - init: function(divaSettings, divaInstance) - { - settings.divaInstance = divaInstance; - return true; - }, - pluginName: 'download', - titleText: 'Download image at the given zoom level', - handleClick: function(event, divaSettings, divaInstance, pageIndex) - { - // TODO: Move rationale for -1 from Wiki (TLDR an old IIP bug) - var width = divaInstance - .getPageDimensions(pageIndex) - .width - 1; - - var image = settings.divaInstance.getPageImageURL(pageIndex, { width: width }); - - window.open(image); - } - }; - - return retval; - })(); - })(jQuery); - - -/***/ }), -/* 45 */ -/***/ (function(module, exports, __webpack_require__) { - - /* - Highlight plugin for diva.js - Allows you to highlight regions of a page image - */ - - var jQuery = __webpack_require__(3); - var elt = __webpack_require__(9); - var diva = __webpack_require__(7); - - (function ($) - { - module.exports = (function() - { - var retval = - { - init: function(divaSettings, divaInstance) - { - var highlightManager = new HighlightManager(divaInstance); - divaSettings.parentObject.data('highlightManager', highlightManager); - - var currentHighlight; - - /* - Reset the highlights object and removes all highlights from the document. - */ - divaInstance.resetHighlights = function() - { - highlightManager.clear(); - }; - - /* - Resets the highlights for a single page. - */ - divaInstance.removeHighlightsOnPage = function(pageIdx) - { - highlightManager.removeHighlightsOnPage(pageIdx); - }; - - /* - Highlights regions on multiple pages. - @param pageIdxs An array of page index numbers - @param regions An array of regions - @param colour (optional) A colour for the highlighting, specified in RGBA CSS format - */ - divaInstance.highlightOnPages = function(pageIdxs, regions, colour, divClass) - { - var j = pageIdxs.length; - while (j--) - { - divaInstance.highlightOnPage(pageIdxs[j], regions[j], colour, divClass); - } - }; - - /* - Highlights regions on a page. - @param pageIdx A page index number - @param regions An array of regions. Use {'width':i, 'height':i, 'ulx':i, 'uly': i, 'divID': str} for each region. - @param colour (optional) A colour for the highlighting, specified in RGBA CSS format - @param divClass (optional) A class to identify a group of highlighted regions on a specific page by - */ - divaInstance.highlightOnPage = function(pageIdx, regions, colour, divClass) - { - if (colour === undefined) - { - colour = 'rgba(255, 0, 0, 0.2)'; - } - - if (divClass === undefined) - { - divClass = divaSettings.ID + 'highlight diva-highlight'; - } - else - { - divClass = divaSettings.ID + 'highlight diva-highlight ' + divClass; - } - - highlightManager.addHighlight({ - page: pageIdx, - regions: regions, - colour: colour, - divClass: divClass - }); - - return true; - }; - - /* - Jumps to a highlight somewhere in the document. - @param divID The ID of the div to jump to. This ID must be attached to the div using .highlightOnPage(s) as the highlight may not be currently appended to the DOM. - */ - divaInstance.gotoHighlight = function(divID) - { - var result = highlightManager.getHighlightByRegionId(divID); - - if (result) - return gotoDiv(result.highlight.page, result.region); - - console.warn("Diva just tried to find a highlight that doesn't exist."); - return false; - }; - - /** - * Moves the diva pane to (page) and makes a darker border on (thisDiv) - */ - var gotoDiv = function(page, thisDiv) - { - //gets center of the div - var centerYOfDiv = parseFloat(thisDiv.uly) + parseFloat(thisDiv.height) / 2; - var centerXOfDiv = parseFloat(thisDiv.ulx) + parseFloat(thisDiv.width) / 2; - - var desiredY = divaInstance.translateFromMaxZoomLevel(centerYOfDiv); - var desiredX = divaInstance.translateFromMaxZoomLevel(centerXOfDiv); - - //navigates to the page - page = parseInt(page, 10); - divaInstance.gotoPageByIndex(page); - var viewportObject = divaInstance.getSettings().viewportObject; - var currentTop = viewportObject.scrollTop() + desiredY - (viewportObject.height() / 2) + divaSettings.verticalPadding; - var currentLeft = viewportObject.scrollLeft() + desiredX - (viewportObject.width() / 2) + divaSettings.horizontalPadding; - - //changes the scroll location to center on the div as much as is possible - viewportObject.scrollTop(currentTop); - viewportObject.scrollLeft(currentLeft); - - currentHighlight = { - region: thisDiv, - page: page - }; - - diva.Events.publish("SelectedHighlightChanged", [thisDiv.id, currentHighlight.page]); - - //selects the highlight - updateCurrentHighlight(divaInstance, currentHighlight); - return thisDiv.id; - }; - - var getDivCenter = function(thisDiv) - { - if (divaSettings.verticallyOriented) return divaInstance.translateFromMaxZoomLevel(parseFloat(thisDiv.uly) + parseFloat(thisDiv.height) / 2); - else return divaInstance.translateFromMaxZoomLevel(parseFloat(thisDiv.ulx) + parseFloat(thisDiv.width) / 2); - }; - - /* - Jumps to the next highlight along the primary axis of the document. - */ - var findAdjacentHighlight = function(forward) - { - var centerOfTargetDiv; - var highlightFound = false; - var centerOfCurrentDiv; - var currentPage; - var regionArr, arrIndex; - var pageDims; - var centerOfDiv, targetDiv; - - var thisDiv; - var compFunction; - - // If currentHighlight does not already exists, - // just pretend we're starting at the northwest corner of diva-inner - if (!currentHighlight) - { - centerOfCurrentDiv = 0; - currentPage = 0; - } - else { - currentPage = currentHighlight.page; - - //find the center of the current div - centerOfCurrentDiv = getDivCenter(currentHighlight.region); - } - - //if we do have a current highlight, try to find the next one in the same page - - regionArr = highlightManager.getHighlightRegions(currentPage); - arrIndex = regionArr.length; - pageDims = divaInstance.getPageDimensionsAtZoomLevel(currentPage, divaInstance.getZoomLevel()); - - //initialize the center of the div to the maximum possible value - if(forward) centerOfTargetDiv = (divaSettings.verticallyOriented) ? pageDims.height : pageDims.width; - else centerOfTargetDiv = 0; - - if(forward) - { - compFunction = function(thisC, curC, targetC) - { - return (thisC > curC && thisC < targetC); - }; - } - else - { - compFunction = function(thisC, curC, targetC) - { - return (thisC < curC && thisC > targetC); - }; - } - - while(arrIndex--) - { - thisDiv = regionArr[arrIndex]; - centerOfDiv = getDivCenter(thisDiv); - - //if this div is farther along the main axis but closer than the current closest - if (compFunction(centerOfDiv, centerOfCurrentDiv, centerOfTargetDiv)) - { - //update targetDiv - highlightFound = true; - centerOfTargetDiv = centerOfDiv; - targetDiv = thisDiv; - } - } - - //if a highlight was found on the current page that was next; this can get overwritten but we're still good - if (highlightFound) return gotoDiv(currentPage, targetDiv); - //if it wasn't found, continue on... - - //find the minimum div on the next page with highlights and loop around if necessary - - //find the next page in the pageArr; this will be in order - var pageArr = highlightManager.getHighlightedPages(); - var curIdx = pageArr.indexOf(currentPage.toString()); - - var targetPage; - - if(forward) - { - while (!targetPage || !divaInstance.isPageIndexValid (targetPage)) - { - //default to first page, move to next if possible - if (curIdx == pageArr.length - 1) targetPage = pageArr[0]; - else targetPage = pageArr[++curIdx]; - } - } - - else - { - while (!targetPage || !divaInstance.isPageIndexValid (targetPage)) - { - //default to last page, move to previous if possible - if (curIdx === 0) targetPage = pageArr[pageArr.length - 1]; - else targetPage = pageArr[--curIdx]; - } - } - - //reset regionArr and centerOfTargetDiv for the new page we're testing - regionArr = highlightManager.getHighlightRegions(targetPage); - arrIndex = regionArr.length; - pageDims = divaInstance.getPageDimensionsAtZoomLevel(targetPage, divaInstance.getMaxZoomLevel()); - - if(forward) centerOfTargetDiv = (divaSettings.verticallyOriented) ? pageDims.height : pageDims.width; - else centerOfTargetDiv = 0; - - //find the minimum this time - if(forward) - { - compFunction = function(thisC, targetC) - { - return (thisC < targetC); - }; - } - else - { - compFunction = function(thisC, targetC) - { - return (thisC > targetC); - }; - } - - while(arrIndex--) - { - thisDiv = regionArr[arrIndex]; - centerOfDiv = getDivCenter(thisDiv); - if (compFunction(centerOfDiv, centerOfTargetDiv)) - { - highlightFound = true; - centerOfTargetDiv = centerOfDiv; - targetDiv = thisDiv; - } - } - - //we've found it this time, as there'll be a region in the full regionArr to be the minimum - return gotoDiv(targetPage, targetDiv); - }; - - /* - Jumps to the next highlight along the primary axis of the document. - */ - divaInstance.gotoNextHighlight = function() - { - if (highlightManager.getHighlightCount() > 0) - return findAdjacentHighlight(true); - else - return false; - }; - - /* - Jumps to the previous highlight along the primary axis of the document. - */ - divaInstance.gotoPreviousHighlight = function() - { - if (highlightManager.getHighlightCount() > 0) - return findAdjacentHighlight(false); - else - return false; - }; - - diva.Events.subscribe('ViewerWillTerminate', this.destroy, divaSettings.ID); - - return true; - }, - destroy: function (divaSettings) - { - var highlightManager = divaSettings.parentObject.data('highlightManager'); - highlightManager.clear(); - divaSettings.parentObject.removeData('highlightManager'); - }, - pluginName: 'highlight', - titleText: 'Highlight regions of pages', - - // Exposed export - HighlightManager: HighlightManager - }; - return retval; - })(); - })(jQuery); - - /** Manages the addition and removal of the page overlays which display the highlights */ - function HighlightManager(divaInstance, getCurrentHighlight) - { - this._divaInstance = divaInstance; - this._overlays = {}; - this._getCurrentHighlight = getCurrentHighlight; - } - - HighlightManager.prototype.getHighlightCount = function () - { - var count = 0; - Object.keys(this._overlays).forEach(function (key) - { - count += this._overlays[key].highlight.regions.length; - }, this); - - return count; - }; - - HighlightManager.prototype.getHighlightRegions = function (pageIndex) - { - if (!this._overlays[pageIndex]) - return []; - - return this._overlays[pageIndex].highlight.regions; - }; - - HighlightManager.prototype.getHighlightedPages = function () - { - // FIXME: Conceptually awkward that these are strings - return Object.keys(this._overlays); - }; - - HighlightManager.prototype.getHighlightByRegionId = function (id) - { - for (var i in this._overlays) - { - if (!this._overlays.hasOwnProperty(i)) - continue; - - var regions = this._overlays[i].highlight.regions; - for (var j in regions) - { - if (!regions.hasOwnProperty(j)) - continue; - - if (regions[j].divID === id) - { - return { - highlight: this._overlays[i].highlight, - region: regions[j] - }; - } - } - } - - return null; - }; - - HighlightManager.prototype.addHighlight = function (highlight) - { - var existingOverlay = this._overlays[highlight.page]; - - if (existingOverlay) - this._divaInstance.__removePageOverlay(existingOverlay); - - var overlay = new HighlightPageOverlay(highlight, this._divaInstance, this._getCurrentHighlight); - this._overlays[highlight.page] = overlay; - this._divaInstance.__addPageOverlay(overlay); - }; - - HighlightManager.prototype.removeHighlightsOnPage = function (pageIndex) - { - if (!this._overlays[pageIndex]) - return; - - this._divaInstance.__removePageOverlay(this._overlays[pageIndex]); - delete this._overlays[pageIndex]; - }; - - HighlightManager.prototype.clear = function () - { - for (var i in this._overlays) - { - if (!this._overlays.hasOwnProperty(i)) - continue; - - this._divaInstance.__removePageOverlay(this._overlays[i]); - } - - this._overlays = {}; - }; - - /** - When a new page is loaded, this overlay will be called with the - page index for the page. It looks at the 'highlights' data object - set on the diva parent element, and determines whether - highlights exist for that page. - - If so, the overlay will create and render elements for every - highlighted box. - - @param highlight - @param divaInstance - @param getCurrentHighlight (optional) - */ - function HighlightPageOverlay(highlight, divaInstance, getCurrentHighlight) - { - this.page = highlight.page; - this.highlight = highlight; - this._highlightRegions = []; - this._divaInstance = divaInstance; - this._getCurrentHighlight = getCurrentHighlight; - } - - HighlightPageOverlay.prototype.mount = function () - { - var divaSettings = this._divaInstance.getSettings(); - - var highlight = this.highlight; - var regions = highlight.regions; - var colour = highlight.colour; - var divClass = highlight.divClass; - - var j = regions.length; - while (j--) - { - var region = regions[j]; - - // FIXME: Use CSS class instead of inline style - var box = elt('div', { - class: divClass, - style: { - background: colour, - border: "1px solid #555", - position: "absolute", - zIndex: 100 - } - }); - - if (region.divID !== undefined) - { - box.setAttribute('data-highlight-id', region.divID); - } - - // Used by IIIFHighlight - if (region.name !== undefined) - { - box.setAttribute('data-name', region.name); - } - - this._highlightRegions.push({ - element: box, - region: region - }); - } - - this.refresh(); - - var frag = document.createDocumentFragment(); - this._highlightRegions.forEach(function (highlight) - { - frag.appendChild(highlight.element); - }); - - divaSettings.innerElement.appendChild(frag); - - if (this._getCurrentHighlight) - updateCurrentHighlight(this._divaInstance, this._getCurrentHighlight()); - - diva.Events.publish("HighlightCompleted", [this.page, this._divaInstance.getFilenames()[this.page]]); - }; - - HighlightPageOverlay.prototype.unmount = function () - { - var innerElement = this._divaInstance.getSettings().innerElement; - - this._highlightRegions.forEach(function (highlight) - { - innerElement.removeChild(highlight.element); - }); - - this._highlightRegions = []; - }; - - // FIXME: Updating a box per highlight region might be too expensive - // Maybe stick all the elements in a container and then scale it using CSS transforms? - HighlightPageOverlay.prototype.refresh = function () - { - var maxZoom = this._divaInstance.getMaxZoomLevel(); - - var maxZoomWidth = this._divaInstance.getPageDimensionsAtZoomLevel(this.page, maxZoom).width; - var currentWidth = this._divaInstance.getPageDimensions(this.page).width; - var zoomDifference = Math.log(maxZoomWidth / currentWidth) / Math.log(2); - - var pageOffset = this._divaInstance.getPageOffset(this.page, { - excludePadding: true, - incorporateViewport: true - }); - - this._highlightRegions.forEach(function (highlight) - { - var region = highlight.region; - - elt.setAttributes(highlight.element, { - style: { - width: incorporateZoom(region.width, zoomDifference) + "px", - height: incorporateZoom(region.height, zoomDifference) + "px", - top: pageOffset.top + incorporateZoom(region.uly, zoomDifference) + "px", - left: pageOffset.left + incorporateZoom(region.ulx, zoomDifference) + "px" - } - }); - }); - }; - - function incorporateZoom(position, zoomDifference) - { - return position / Math.pow(2, zoomDifference); - } - - function updateCurrentHighlight(divaInstance, currentHighlight) - { - var classString = divaInstance.getInstanceId() + "selected-highlight"; - var classElem = document.getElementsByClassName(classString); - var idx; - var box; - var boxes; - - for (idx = 0; idx < classElem.length; idx++) - { - box = classElem[idx]; - if (box.id !== currentHighlight.id) - { - box.className = box.className.replace(' '+classString, ''); - box.style.border = "1px solid #555"; - } - } - - if (divaInstance.isPageInViewport(currentHighlight.page)) - { - boxes = document.querySelectorAll("*[data-highlight-id=" + currentHighlight.id + "]"); - for(idx = 0; idx < boxes.length; idx++) - { - box = boxes[idx]; - box.className = box.className + " " + classString; - box.style.border = "2px solid #000"; - } - } - } - - -/***/ }), -/* 46 */ -/***/ (function(module, exports, __webpack_require__) { - - /* - IIIF Highlight plugin for diva.js - Allows you to highlight regions of a page image based off of annotations in a IIIF Manifest - */ - - var jQuery = __webpack_require__(3); - var diva = __webpack_require__(7); - var HighlightManager = __webpack_require__(45).HighlightManager; - - (function ($) - { - module.exports = (function() - { - var settings = {}; - var retval = - { - init: function(divaSettings, divaInstance) - { - var highlightManager = new HighlightManager(divaInstance); - divaSettings.parentObject.data('highlightManager', highlightManager); - - settings.highlightedPages = []; - - /* - Reset the highlights object and removes all highlights from the document. - */ - divaInstance.resetHighlights = function() - { - highlightManager.clear(); - }; - - /* - Resets the highlights for a single page. - */ - divaInstance.removeHighlightsOnPage = function(pageIdx) - { - highlightManager.removeHighlightsOnPage(pageIdx); - }; - - divaInstance.hideHighlights = function() - { - settings.highlightsVisible = false; - $(divaSettings.innerElement).addClass('annotations-hidden'); - }; - - divaInstance.showHighlights = function() - { - settings.highlightsVisible = true; - $(divaSettings.innerElement).removeClass('annotations-hidden'); - }; - - /* - Highlights regions on multiple pages. - @param pageIdxs An array of page index numbers - @param regions An array of regions - @param colour (optional) A colour for the highlighting, specified in RGBA CSS format - */ - divaInstance.highlightOnPages = function(pageIdxs, regions, colour, divClass) - { - var j = pageIdxs.length; - while (j--) - { - divaInstance.highlightOnPage(pageIdxs[j], regions[j], colour, divClass); - } - }; - - /* - Highlights regions on a page. - @param pageIdx A page index number - @param regions An array of regions. Use {'width':i, 'height':i, 'ulx':i, 'uly': i, 'divID': str} for each region. - @param colour (optional) A colour for the highlighting, specified in RGBA CSS format - @param divClass (optional) A class to identify a group of highlighted regions on a specific page by - */ - divaInstance.highlightOnPage = function(pageIdx, regions, colour, divClass) - { - if (colour === undefined) - { - colour = 'rgba(255, 0, 0, 0.2)'; - } - - if (divClass === undefined) - { - divClass = divaSettings.ID + 'highlight diva-highlight'; - } - else - { - divClass = divaSettings.ID + 'highlight diva-highlight ' + divClass; - } - - highlightManager.addHighlight({ - page: pageIdx, - regions: regions, - colour: colour, - divClass: divClass - }); - - return true; - }; - - /* - Jumps to a highlight somewhere in the document. - @param divID The ID of the div to jump to. This ID must be attached to the div using .highlightOnPage(s) as the highlight may not be appended to the DOM. - */ - divaInstance.gotoHighlight = function(divID) - { - var result = highlightManager.getHighlightByRegionId(divID); - - if (result) - return gotoDiv(result.highlight.page, result.region); - - console.warn("Diva just tried to find a highlight that doesn't exist."); - return false; - }; - - /** - * Moves the diva pane to (page) - */ - var gotoDiv = function(page, thisDiv) - { - //gets center of the div - var centerYOfDiv = parseFloat(thisDiv.uly) + parseFloat(thisDiv.height) / 2; - var centerXOfDiv = parseFloat(thisDiv.ulx) + parseFloat(thisDiv.width) / 2; - - var desiredY = divaInstance.translateFromMaxZoomLevel(centerYOfDiv); - var desiredX = divaInstance.translateFromMaxZoomLevel(centerXOfDiv); - - //navigates to the page - page = parseInt(page, 10); - divaInstance.gotoPageByIndex(page); - var viewportObject = divaInstance.getSettings().viewportObject; - var currentTop = viewportObject.scrollTop() + desiredY - (viewportObject.height() / 2) + divaSettings.verticalPadding; - var currentLeft = viewportObject.scrollLeft() + desiredX - (viewportObject.width() / 2) + divaSettings.horizontalPadding; - - //changes the scroll location to center on the div as much as is possible - viewportObject.scrollTop(currentTop); - viewportObject.scrollLeft(currentLeft); - }; - - var showAnnotations = function(canvasIndex) - { - return function(data, status, jqXHR) - { - var canvasAnnotations = data; - var numAnnotations = data.length; - - //convert annotations in annotations object to diva highlight objects - var regions = []; - - //loop over annotations in a single canvas - for (var k = 0; k < numAnnotations; k++) - { - var currentAnnotation = canvasAnnotations[k]; - // get text content - var text = currentAnnotation.resource.chars; - - // get x,y,w,h (slice string from '#xywh=' to end) - var onString = currentAnnotation.on; - var coordString = onString.slice(onString.indexOf('#xywh=') + 6); - var coordinates = coordString.split(','); - - var region = { - ulx: parseInt(coordinates[0], 10), - uly: parseInt(coordinates[1], 10), - width: parseInt(coordinates[2], 10), - height: parseInt(coordinates[3], 10), - name: text - }; - - regions.push(region); - } - - divaInstance.highlightOnPage(canvasIndex, regions); - //flag this page's annotations as having been retrieved - settings.highlightedPages.push(canvasIndex); - }; - }; - - var getAnnotationsList = function(pageIndex) - { - //if page has annotationList - var canvases = settings.manifest.sequences[0].canvases; - - if (canvases[pageIndex].hasOwnProperty('otherContent')) - { - var otherContent = canvases[pageIndex].otherContent; - - for (var j = 0; j < otherContent.length; j++) - { - if (otherContent[j]['@type'] === 'sc:AnnotationList') - { - // canvas has annotations. get the annotations: - $.ajax({ - url: otherContent[j]['@id'], - cache: true, - dataType: 'json', - success: showAnnotations(pageIndex) - }); - } - } - } - }; - - var setManifest = function(manifest) - { - settings.manifest = manifest; - }; - - diva.Events.subscribe('ManifestDidLoad', setManifest, divaSettings.ID); - - diva.Events.subscribe('PageWillLoad', function(pageIndex) - { - if (!settings.highlightsVisible) - { - return; - } - - //if highlights for this page have already been checked/loaded, return - for (var i = 0; i < settings.highlightedPages.length; i++) - { - if (settings.highlightedPages[i] === pageIndex) - { - return; - } - } - - getAnnotationsList(pageIndex, settings.manifest); - }, divaSettings.ID); - - var activeOverlays = []; - - //on mouseover, show the annotation text - divaSettings.innerObject.on('mouseenter', '.' + divaSettings.ID + 'highlight', function(e) - { - var annotationElement = e.target; - var name = annotationElement.dataset.name; - var textOverlay = document.createElement('div'); - - textOverlay.style.top = (annotationElement.offsetTop + annotationElement.offsetHeight - 1) + 'px'; - textOverlay.style.left = annotationElement.style.left; - textOverlay.style.background = '#fff'; - textOverlay.style.border = '1px solid #555'; - textOverlay.style.position = 'absolute'; - textOverlay.style.zIndex = 101; - textOverlay.className = 'annotation-overlay'; - textOverlay.textContent = name; - - annotationElement.parentNode.appendChild(textOverlay); - activeOverlays.push(textOverlay); - }); - - divaSettings.innerObject.on('mouseleave', '.' + divaSettings.ID + 'highlight', function(e) - { - while (activeOverlays.length) - { - var textOverlay = activeOverlays.pop(); - textOverlay.parentNode.removeChild(textOverlay); - } - }); - - diva.Events.subscribe('ViewerDidLoad', function(){ - //button to toggle annotations - $('#' + divaSettings.ID + 'page-nav').before('
'); - - $(divaSettings.selector + 'annotations-icon').addClass('annotations-icon-active'); - - $('#' + divaSettings.ID + 'annotations-icon').on('click', function(e) - { - //toggle visibility of annotations - if (settings.highlightsVisible) - { - divaInstance.hideHighlights(); - $(divaSettings.selector + 'annotations-icon').removeClass('annotations-icon-active'); - } - else - { - divaInstance.showHighlights(); - $(divaSettings.selector + 'annotations-icon').addClass('annotations-icon-active'); - } - }); - }, divaSettings.ID); - - //enable annotations by default - settings.highlightsVisible = true; - - return true; - }, - destroy: function (divaSettings, divaInstance) - { - divaSettings.parentObject.removeData('highlights'); - }, - pluginName: 'IIIFHighlight', - titleText: 'Highlight regions of pages' - }; - return retval; - })(); - })(jQuery); - - -/***/ }), -/* 47 */ -/***/ (function(module, exports, __webpack_require__) { - - // IIIF Metadata plugin for diva.js - // Displays object metadata from a IIIF manifest - - var jQuery = __webpack_require__(3); - var diva = __webpack_require__(7); - - (function ($) - { - module.exports = (function() - { - var retval = - { - init: function(divaSettings, divaInstance) - { - var _displayMetadata = function(manifest) - { - var showMetadata = function(label, value) - { - var labelProper = label.charAt(0).toUpperCase() + label.slice(1); - var labelFormatted = labelProper.replace('_', ' '); - - if (value.match(/^https?:\/\//)) - { - value = '' + value + ''; - } - - return ''; - }; - - var getDataForLanguage = function(data, language) - { - for (var i = 0; i < data.length; i++) - { - if (data[i]['@language'] === language) - { - return data[i]['@value']; - } - } - - // Handle the case where no language is specified, or when a single object is passed - return data[0]['@value'] || data['@value']; - }; - - /** - * Shows metadata from label names (if the metadata exists). - * @param names {Array} - An array of strings representing field names to display. - */ - var showMetadataFromLabelNames = function(names) - { - var elements = ''; - - for (var i = 0; i < names.length; i++) - { - var field = names[i]; - - if (manifest.hasOwnProperty(field)) - { - if (manifest[field].constructor === Array) - { - //multiple languages - var localizedData = getDataForLanguage(manifest[field], 'en'); - elements += showMetadata(field, localizedData); - } - else - { - elements += showMetadata(field, manifest[field]); - } - } - } - - return elements; - }; - - var metadataElement = '
'; - metadataElement += showMetadataFromLabelNames(['label']); - - if (manifest.hasOwnProperty('metadata')) - { - var metadataField = manifest.metadata; - - for (var i = 0; i < metadataField.length; i++) - { - if (metadataField[i].value.constructor === Array) - { - var canonicalData = getDataForLanguage(metadataField[i].value, 'en'); - metadataElement += showMetadata(metadataField[i].label, canonicalData); - } - else - { - metadataElement += showMetadata(metadataField[i].label, metadataField[i].value); - } - } - } - - metadataElement += showMetadataFromLabelNames([ - 'description', - 'within', - 'see_also', - 'license', - 'attribution' - ]); - - metadataElement += '
'; - - divaSettings.parentObject.prepend(metadataElement); - $(divaSettings.selector + 'metadata').hide(); - }; - - //subscribe to ManifestDidLoad event, get the manifest - diva.Events.subscribe('ManifestDidLoad', _displayMetadata, divaSettings.ID); - - divaSettings.parentObject.prepend(''); - // $(divaSettings.selector + 'title').append(''); - - $(divaSettings.selector + 'metadata-link').on('click', function(e) - { - $(divaSettings.selector + 'metadata').fadeToggle('fast'); - }); - - return true; - }, - destroy: function (divaSettings, divaInstance) - { - }, - pluginName: 'IIIFMetadata', - titleText: 'Show metadata from a IIIF manifest' - }; - return retval; - })(); - })(jQuery); - - -/***/ }) -/******/ ]) -}); -; -//# sourceMappingURL=diva.js.map \ No newline at end of file diff --git a/build/js/diva.js.map b/build/js/diva.js.map deleted file mode 100644 index 987067de..00000000 --- a/build/js/diva.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"diva.js","sources":["webpack:///webpack/universalModuleDefinition","webpack:///webpack/bootstrap 9ce7cf8344c0e9d343cf","webpack:///./source/js/register-builtin-plugins.js","webpack:///./source/js/diva-global.js","webpack:///./source/js/utils/events.js","webpack:///./source/js/plugin-registry.js","webpack:///./source/js/plugins/autoscroll.js","webpack:///./source/js/diva.js","webpack:///./~/array.prototype.fill/index.js","webpack:///./source/js/utils/elt.js","webpack:///./source/js/utils/hash-params.js","webpack:///./source/js/active-diva-controller.js","webpack:///./source/js/image-manifest.js","webpack:///./source/js/parse-iiif-manifest.js","webpack:///./source/js/toolbar.js","webpack:///./source/js/viewer-core.js","webpack:///./source/js/utils/jquery-extensions.js","webpack:///./source/js/utils/get-scrollbar-width.js","webpack:///./source/js/gesture-events.js","webpack:///./source/js/document-handler.js","webpack:///./~/lodash.maxby/index.js","webpack:///(webpack)/buildin/module.js","webpack:///./source/js/page-tools-overlay.js","webpack:///./source/js/grid-handler.js","webpack:///./source/js/page-overlay-manager.js","webpack:///./source/js/renderer.js","webpack:///./~/debug/src/browser.js","webpack:///./~/process/browser.js","webpack:///./~/debug/src/debug.js","webpack:///./~/ms/index.js","webpack:///./source/js/composite-image.js","webpack:///./source/js/document-layout.js","webpack:///./source/js/image-cache.js","webpack:///./source/js/image-request-handler.js","webpack:///./source/js/interpolate-animation.js","webpack:///./source/js/page-layouts/index.js","webpack:///./source/js/page-layouts/book-layout.js","webpack:///./source/js/page-layouts/page-dimensions.js","webpack:///./source/js/page-layouts/singles-layout.js","webpack:///./source/js/page-layouts/grid-layout.js","webpack:///./source/js/settings-view.js","webpack:///./source/js/validation-runner.js","webpack:///./source/js/viewport.js","webpack:///./source/js/plugins/canvas.js","webpack:///./source/js/plugins/download.js","webpack:///./source/js/plugins/highlight.js","webpack:///./source/js/plugins/iiif-highlight.js","webpack:///./source/js/plugins/iiif-metadata.js"],"sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"jquery\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"jquery\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"diva\"] = factory(require(\"jquery\"));\n\telse\n\t\troot[\"diva\"] = factory(root[\"jQuery\"]);\n})(this, function(__WEBPACK_EXTERNAL_MODULE_3__) {\nreturn \n\n\n// WEBPACK FOOTER //\n// webpack/universalModuleDefinition"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId])\n \t\t\treturn installedModules[moduleId].exports;\n\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\texports: {},\n \t\t\tid: moduleId,\n \t\t\tloaded: false\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.loaded = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(0);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 9ce7cf8344c0e9d343cf","var diva = require('./diva-global');\n\ndiva.registerPlugin(require('./plugins/autoscroll'));\ndiva.registerPlugin(require('./plugins/canvas'));\ndiva.registerPlugin(require('./plugins/download'));\ndiva.registerPlugin(require('./plugins/highlight'));\ndiva.registerPlugin(require('./plugins/iiif-highlight'));\ndiva.registerPlugin(require('./plugins/iiif-metadata'));\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/register-builtin-plugins.js\n// module id = 1\n// module chunks = 0","var $ = require('jquery');\n\nvar Events = require('./utils/events');\nvar PluginRegistry = require('./plugin-registry');\n\nvar diva = module.exports = {\n Events: new Events(),\n\n registerPlugin: function (plugin)\n {\n PluginRegistry.register(plugin);\n },\n\n /**\n * Create a new Diva instance at the given element\n *\n * @param element {Element}\n * @param options {Object}\n * @returns {Diva}\n */\n create: function (element, options)\n {\n if (diva.find(element))\n throw new Error('Diva is already initialized on ' + reprElem(element));\n\n var $elem = $(element);\n $elem.diva(options);\n\n return $elem.data('diva');\n },\n\n /**\n * Return the Diva instance attached to the\n * element, if any.\n *\n * @param element\n * @returns {Diva|null}\n */\n find: function (element)\n {\n return $(element).data('diva') || null;\n }\n};\n\nfunction reprElem(elem)\n{\n var id = elem.id ? '#' + elem.id : elem.id;\n var classes = elem.className ? '.' + elem.className.split(/\\s+/g).join('.') : '';\n\n return (id ? id : elem.tagName.toLowerCase()) + classes;\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/diva-global.js\n// module id = 2\n// module chunks = 0","module.exports = Events;\n\n/**\n * Events. Pub/Sub system for Loosely Coupled logic.\n * Based on Peter Higgins' port from Dojo to jQuery\n * https://github.com/phiggins42/bloody-jquery-plugins/blob/master/pubsub.js\n *\n * Re-adapted to vanilla Javascript\n *\n * @class Events\n */\nfunction Events()\n{\n this._cache = {};\n}\n\n/**\n * diva.Events.publish\n * e.g.: diva.Events.publish(\"PageDidLoad\", [pageIndex, filename, pageSelector], this);\n *\n * @class Events\n * @method publish\n * @param topic {String}\n * @param args {Array}\n * @param scope {Object=} Optional - Subscribed functions will be executed with the supplied object as `this`.\n * It is necessary to supply this argument with the self variable when within a Diva instance.\n * The scope argument is matched with the instance ID of subscribers to determine whether they\n * should be executed. (See instanceID argument of subscribe.)\n */\nEvents.prototype.publish = function (topic, args, scope)\n{\n if (this._cache[topic])\n {\n var thisTopic = this._cache[topic];\n\n if (typeof thisTopic.global !== 'undefined')\n {\n var thisTopicGlobal = thisTopic.global;\n var globalCount = thisTopicGlobal.length;\n\n for (var i=0; i < globalCount; i++)\n {\n thisTopicGlobal[i].apply(scope || null, args || []);\n }\n }\n\n if (scope && typeof scope.getInstanceId !== 'undefined')\n {\n // get publisher instance ID from scope arg, compare, and execute if match\n var instanceID = scope.getInstanceId();\n\n if (this._cache[topic][instanceID])\n {\n var thisTopicInstance = this._cache[topic][instanceID];\n var scopedCount = thisTopicInstance.length;\n\n for (var j=0; j < scopedCount; j++)\n {\n thisTopicInstance[j].apply(scope, args || []);\n }\n }\n }\n }\n};\n\n/**\n * diva.Events.subscribe\n * e.g.: diva.Events.subscribe(\"PageDidLoad\", highlight, settings.ID)\n *\n * @class Events\n * @method subscribe\n * @param topic {String}\n * @param callback {Function}\n * @param instanceID {String=} Optional - String representing the ID of a Diva instance; if provided,\n * callback only fires for events published from that instance.\n * @return Event handler {Array}\n */\nEvents.prototype.subscribe = function (topic, callback, instanceID)\n{\n if (!this._cache[topic])\n {\n this._cache[topic] = {};\n }\n\n if (typeof instanceID === 'string')\n {\n if (!this._cache[topic][instanceID])\n {\n this._cache[topic][instanceID] = [];\n }\n\n this._cache[topic][instanceID].push(callback);\n }\n else\n {\n if (!this._cache[topic].global)\n {\n this._cache[topic].global = [];\n }\n\n this._cache[topic].global.push(callback);\n }\n\n var handle = instanceID ? [topic, callback, instanceID] : [topic, callback];\n\n return handle;\n};\n\n/**\n * diva.Events.unsubscribe\n * e.g.: var handle = Events.subscribe(\"PageDidLoad\", highlight);\n * Events.unsubscribe(handle);\n *\n * @class Events\n * @method unsubscribe\n * @param handle {Array}\n * @param completely {Boolean=} - Unsubscribe all events for a given topic.\n * @return success {Boolean}\n */\nEvents.prototype.unsubscribe = function (handle, completely)\n{\n var t = handle[0];\n\n if (this._cache[t])\n {\n var topicArray;\n var instanceID = handle.length === 3 ? handle[2] : 'global';\n\n topicArray = this._cache[t][instanceID];\n\n if (!topicArray)\n {\n return false;\n }\n\n if (completely)\n {\n delete this._cache[t][instanceID];\n return topicArray.length > 0;\n }\n\n var i = topicArray.length;\n while (i--)\n {\n if (topicArray[i] === handle[1])\n {\n this._cache[t][instanceID].splice(i, 1);\n return true;\n }\n }\n }\n\n return false;\n};\n\n/**\n * diva.Events.unsubscribeAll\n * e.g.: diva.Events.unsubscribeAll('global');\n *\n * @class Events\n * @param instanceID {String=} Optional - instance ID to remove subscribers from or 'global' (if omitted,\n * subscribers in all scopes removed)\n * @method unsubscribeAll\n */\nEvents.prototype.unsubscribeAll = function (instanceID)\n{\n if (instanceID)\n {\n var topics = Object.keys(this._cache);\n var i = topics.length;\n var topic;\n\n while (i--)\n {\n topic = topics[i];\n\n if (typeof this._cache[topic][instanceID] !== 'undefined')\n {\n delete this._cache[topic][instanceID];\n }\n }\n }\n else\n {\n this._cache = {};\n }\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/utils/events.js\n// module id = 4\n// module chunks = 0","/**\n * @module\n * @private\n * The global plugin registry.\n */\n\nvar plugins = [];\n\nmodule.exports = {\n register: function (plugin)\n {\n plugins.push(plugin);\n },\n getAll: function ()\n {\n return plugins;\n }\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/plugin-registry.js\n// module id = 5\n// module chunks = 0","/*\nDiva.JS autoscroll plugin\nAuthor: Andrew Horwitz\n\nLets Diva scroll in the primary direction (as determined by\nsettings.verticallyOriented) automatically at a given/changeable rate.\n\nRelevant settings:\n -scrollSpeed: pixels per second (defaults to 10)\n -disableManualScroll: disables manual scroll while automatic scroll is on (defaults to false)\n -currentlyAutoScrolling: whether or not autoscroll is currently on\n -autoScrollRefresh: ms between scrolling actions\n -disableAutoscrollPrefs: disables the autoscroll preferences panel\n\nRelevant methods:\n -startScrolling, stopScrolling, toggleScrolling\n -changeRefresh, changeScrollSpeed (setters for respective options)\n -disableManualScroll, enableManualScroll\n*/\n\nvar jQuery = require('jquery');\nvar diva = require('../diva');\n\n(function ($)\n{\n module.exports = (function()\n {\n var settings = {};\n var retval =\n {\n init: function(divaSettings, divaInstance)\n {\n var pixelsPerScroll;\n var disableManualScroll;\n var autoScrollRefresh;\n var defaultAutoRefresh;\n var scrollSpeed;\n\n function log10(x)\n {\n return Math.log(x) / Math.log(10);\n }\n\n divaInstance.startScrolling = function()\n {\n if (divaSettings.currentlyAutoScrolling)\n {\n console.warn(\"You are trying to start autoscrolling, but it is already scrolling.\");\n return;\n }\n\n $(\"#\" + divaSettings.ID + \"autoscroll-toggle\").text(\"Turn off\");\n if (disableManualScroll)\n {\n divaInstance.disableScrollable();\n }\n\n divaSettings.currentlyAutoScrolling = true;\n restartScrollingInterval();\n };\n\n var restartScrollingInterval = function()\n {\n clearInterval(divaSettings.autoScrollInterval);\n if (divaSettings.verticallyOriented)\n {\n divaSettings.autoScrollInterval = setInterval(function(){\n divaSettings.viewportObject.scrollTop(divaSettings.viewportObject.scrollTop() + pixelsPerScroll);\n }, autoScrollRefresh);\n }\n else\n {\n divaSettings.autoScrollInterval = setInterval(function(){\n divaSettings.viewportObject.scrollLeft(divaSettings.viewportObject.scrollLeft() + pixelsPerScroll);\n }, autoScrollRefresh);\n }\n };\n\n divaInstance.stopScrolling = function()\n {\n if (!divaSettings.currentlyAutoScrolling)\n {\n console.warn(\"You are trying to stop autoscrolling, but it is not currently active.\");\n return;\n }\n\n $(\"#\" + divaSettings.ID + \"autoscroll-toggle\").text(\"Turn on\");\n if (disableManualScroll)\n {\n divaInstance.enableScrollable();\n }\n\n divaSettings.currentlyAutoScrolling = false;\n clearInterval(divaSettings.autoScrollInterval);\n };\n\n divaInstance.toggleScrolling = function()\n {\n if (divaSettings.currentlyAutoScrolling)\n divaInstance.stopScrolling();\n else\n divaInstance.startScrolling();\n };\n\n divaInstance.changeRefresh = function(newRefresh)\n {\n autoScrollRefresh = newRefresh;\n updatePixelsPerScroll();\n };\n\n divaInstance.changeScrollSpeed = function(newSpeed)\n {\n scrollSpeed = newSpeed;\n updatePixelsPerScroll();\n\n $(\"#\" + divaSettings.ID + \"autoscroll-pps\").val(log10(scrollSpeed));\n if (divaSettings.currentlyAutoScrolling)\n {\n restartScrollingInterval();\n }\n };\n\n var updatePixelsPerScroll = function()\n {\n autoScrollRefresh = defaultAutoRefresh;\n pixelsPerScroll = scrollSpeed / (1000 / autoScrollRefresh);\n\n //should be minimum of one otherwise it won't change the actual value\n //user can change autoscrollrefresh or scrollspeed; this may overwrite autoScrollRefresh\n if (pixelsPerScroll < 1)\n {\n autoScrollRefresh = autoScrollRefresh * (1 / pixelsPerScroll);\n pixelsPerScroll = scrollSpeed / (1000 / autoScrollRefresh);\n }\n };\n\n divaInstance.disableManualScroll = function()\n {\n disableManualScroll = true;\n if (divaSettings.currentlyAutoScrolling)\n {\n divaInstance.disableScrollable();\n }\n };\n\n divaInstance.enableManualScroll = function()\n {\n disableManualScroll = false;\n if (divaSettings.currentlyAutoScrolling)\n {\n divaInstance.enableScrollable();\n }\n };\n\n divaSettings.currentlyAutoScrolling = false;\n divaSettings.autoScrollInterval = \"\";\n\n disableManualScroll = divaSettings.disableManualScroll || false;\n autoScrollRefresh = divaSettings.autoScrollRefresh || 50;\n defaultAutoRefresh = autoScrollRefresh;\n\n divaInstance.changeScrollSpeed((divaSettings.scrollSpeed || 10));\n\n $(window).on('keyup', function(e)\n {\n if (e.shiftKey && e.keyCode === 32)\n {\n divaInstance.toggleScrolling();\n }\n });\n\n if (!divaSettings.disableAutoscrollPrefs)\n {\n var setPosition = function(isFullscreen)\n {\n if (divaSettings.inFullscreen)\n {\n var fullscreenTools = $(divaSettings.selector + 'tools');\n var toolsMargin = fullscreenTools.css('right');\n settings.jqObj.css({\n 'right': toolsMargin,\n 'margin-right': 0,\n 'top': fullscreenTools.offset().top + fullscreenTools.outerHeight() + 15\n });\n }\n else\n {\n settings.jqObj.css({\n 'right': $(window).width() - (divaSettings.viewportObject.offset().left + divaSettings.viewportObject.outerWidth()) + divaSettings.scrollbarWidth,\n 'margin-right': '.6em'\n });\n settings.jqObj.offset({'top': divaSettings.viewportObject.offset().top + 1});\n }\n };\n\n diva.Events.subscribe('ModeDidSwitch', setPosition, divaSettings.ID);\n\n diva.Events.subscribe('ViewerDidLoad', function(s)\n {\n var autoscrollPrefsString =\n \"
\" +\n \"Autoscrolling options:
\" +\n \"Speed:\" +\n \"
\" +\n \"Allow manual scroll:\" +\n \"
\" +\n \"\" +\n \"
\";\n $(\"#\" + divaSettings.ID + \"page-nav\").before(\"
\");\n $(\"body\").prepend(autoscrollPrefsString);\n\n $(\"#\" + divaSettings.ID + \"autoscroll-pps\").on('change', function(e)\n {\n divaInstance.changeScrollSpeed(Math.pow(10, e.target.value));\n });\n\n $(\"#\" + divaSettings.ID + \"autoscroll-manual\").on('change', function(e)\n {\n if (e.target.checked)\n divaInstance.enableManualScroll();\n else\n divaInstance.disableManualScroll();\n });\n\n $(\"#\" + divaSettings.ID + \"autoscroll-toggle\").on('click', divaInstance.toggleScrolling);\n\n $(\"#\" + divaSettings.ID + \"autoscroll-icon\").on('click', function(e)\n {\n settings.jqObj = $(\"#\" + divaSettings.ID + \"autoscroll-prefs\");\n\n if (settings.jqObj.css('display') === 'none')\n {\n settings.jqObj.css({'display': 'block'});\n\n setPosition(divaSettings.inFullscreen);\n\n }\n else\n {\n settings.jqObj.css('display', 'none');\n }\n });\n }, divaSettings.ID);\n }\n },\n pluginName: 'autoscroll',\n titleText: 'Automatically scrolls page along primary axis'\n };\n return retval;\n })();\n})(jQuery);\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/plugins/autoscroll.js\n// module id = 6\n// module chunks = 0","/*\nCopyright (C) 2011-2016 by Wendy Liu, Evan Magoni, Andrew Hankinson, Andrew Horwitz, Laurent Pugin\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n*/\nrequire('array.prototype.fill');\n\nvar jQuery = require('jquery');\n\nvar elt = require('./utils/elt');\nvar HashParams = require('./utils/hash-params');\n\nvar ActiveDivaController = require('./active-diva-controller');\nvar diva = require('./diva-global');\nvar ImageManifest = require('./image-manifest');\nvar createToolbar = require('./toolbar');\nvar ViewerCore = require('./viewer-core');\n\n// Start the active Diva tracker\nvar activeDiva = new ActiveDivaController(); // jshint ignore: line\n\nmodule.exports = diva;\n\n// this pattern was taken from http://www.virgentech.com/blog/2009/10/building-object-oriented-jquery-plugin.html\n(function ($)\n{\n var Diva = function (element, options)\n {\n // Global instance variables (set way down in `init`)\n var settings, viewerState, divaState;\n var self = this;\n\n // These are elements that can be overridden upon instantiation\n // See https://github.com/DDMAL/diva.js/wiki/Settings for more details\n options = $.extend({\n adaptivePadding: 0.05, // The ratio of padding to the page dimension\n arrowScrollAmount: 40, // The amount (in pixels) to scroll by when using arrow keys\n blockMobileMove: false, // Prevent moving or scrolling the page on mobile devices\n objectData: '', // A IIIF Manifest or a JSON file generated by process.py that provides the object dimension data, or a URL pointing to such data - *REQUIRED*\n enableAutoTitle: true, // Shows the title within a div of id diva-title\n enableFilename: true, // Uses filenames and not page numbers for links (i=bm_001.tif, not p=1)\n enableFullscreen: true, // Enable or disable fullscreen icon (mode still available)\n enableGotoPage: true, // A \"go to page\" jump box\n enableGotoSuggestions: true, // Controls whether suggestions are shown under the input field when the user is typing in the 'go to page' form\n enableGridIcon: true, // A grid view of all the pages\n enableGridControls: 'buttons', // Specify control of pages per grid row in Grid view. Possible values: 'buttons' (+/-), 'slider'. Any other value disables the controls.\n enableImageTitles: true, // Adds \"Page {n}\" title to page images if true\n enableKeyScroll: true, // Captures scrolling using the arrow and page up/down keys regardless of page focus. When off, defers to default browser scrolling behavior.\n enableLinkIcon: true, // Controls the visibility of the link icon\n enableNonPagedVisibilityIcon: true, // Controls the visibility of the icon to toggle the visibility of non-paged pages. (Automatically hidden if no 'non-paged' pages).\n enableSpaceScroll: false, // Scrolling down by pressing the space key\n enableToolbar: true, // Enables the toolbar. Note that disabling this means you have to handle all controls yourself.\n enableZoomControls: 'buttons', // Specify controls for zooming in and out. Possible values: 'buttons' (+/-), 'slider'. Any other value disables the controls.\n fillParentHeight: true, // Use a flexbox layout to allow Diva to fill its parent's height\n fixedPadding: 10, // Fallback if adaptive padding is set to 0\n fixedHeightGrid: true, // So each page in grid view has the same height (only widths differ)\n goDirectlyTo: 0, // Default initial page to show (0-indexed)\n hashParamSuffix: null, // Used when there are multiple document viewers on a page\n iipServerURL: '', // The URL to the IIPImage installation, including the `?FIF=` - *REQUIRED*, unless using IIIF\n inFullscreen: false, // Set to true to load fullscreen mode initially\n inBookLayout: false, // Set to true to view the document with facing pages in document mode\n inGrid: false, // Set to true to load grid view initially\n imageDir: '', // Image directory, either absolute path or relative to IIP's FILESYSTEM_PREFIX - *REQUIRED*, unless using IIIF\n maxPagesPerRow: 8, // Maximum number of pages per row in grid view\n maxZoomLevel: -1, // Optional; defaults to the max zoom returned in the JSON response\n minPagesPerRow: 2, // Minimum pages per row in grid view. Recommended default.\n minZoomLevel: 0, // Defaults to 0 (the minimum zoom)\n onGotoSubmit: null, // When set to a function that takes a string and returns a page index, this will override the default behaviour of the 'go to page' form submission\n pageAliases: {}, // An object mapping specific page indices to aliases (has priority over 'pageAliasFunction'\n pageAliasFunction: function(){return false;}, // A function mapping page indices to an alias. If false is returned, default page number is displayed\n pageLoadTimeout: 200, // Number of milliseconds to wait before loading pages\n pagesPerRow: 5, // The default number of pages per row in grid view\n showNonPagedPages: false, // Whether pages tagged as 'non-paged' (in IIIF manifests only) should be visible after initial load\n throbberTimeout: 100, // Number of milliseconds to wait before showing throbber\n tileHeight: 256, // The height of each tile, in pixels; usually 256\n tileWidth: 256, // The width of each tile, in pixels; usually 256\n toolbarParentObject: null, // The toolbar parent object.\n verticallyOriented: true, // Determines vertical vs. horizontal orientation\n viewportMargin: 200, // Pretend tiles +/- 200px away from viewport are in\n zoomLevel: 2 // The initial zoom level (used to store the current zoom level)\n }, options);\n\n // Returns the page index associated with the given filename; must called after setting settings.manifest\n var getPageIndex = function (filename)\n {\n return getPageIndexForManifest(settings.manifest, filename);\n };\n\n var NotAnIIIFManifestException = function (message)\n {\n return message;\n };\n\n var getPageIndexForManifest = function (manifest, filename)\n {\n var i,\n np = manifest.pages.length;\n\n for (i = 0; i < np; i++)\n {\n if (!manifest.pages[i])\n {\n return -1;\n }\n\n if (manifest.pages[i].f === filename)\n {\n return i;\n }\n }\n\n return -1;\n };\n\n // Check if a page index is valid\n var isPageValid = function (pageIndex)\n {\n return settings.manifest.isPageValid(pageIndex, settings.showNonPagedPages);\n };\n\n var reloadViewer = function (newOptions)\n {\n return divaState.viewerCore.reload(newOptions);\n };\n\n // Called when the change view icon is clicked\n var changeView = function (destinationView)\n {\n switch (destinationView)\n {\n case 'document':\n return reloadViewer({\n inGrid: false,\n inBookLayout: false\n });\n\n case 'book':\n return reloadViewer({\n inGrid: false,\n inBookLayout: true\n });\n\n case 'grid':\n return reloadViewer({\n inGrid: true\n });\n\n default:\n return false;\n }\n };\n\n //toggles between orientations\n var toggleOrientation = function ()\n {\n var verticallyOriented = !settings.verticallyOriented;\n\n //if in grid, switch out of grid\n reloadViewer({\n inGrid: false,\n verticallyOriented: verticallyOriented,\n goDirectlyTo: settings.currentPageIndex,\n verticalOffset: divaState.viewerCore.getYOffset(),\n horizontalOffset: divaState.viewerCore.getXOffset()\n });\n\n return verticallyOriented;\n };\n\n // Called when the fullscreen icon is clicked\n var toggleFullscreen = function ()\n {\n reloadViewer({\n inFullscreen: !settings.inFullscreen\n });\n };\n\n var getState = function ()\n {\n var view;\n\n if (settings.inGrid)\n {\n view = 'g';\n }\n else if (settings.inBookLayout)\n {\n view = 'b';\n }\n else\n {\n view = 'd';\n }\n\n var layout = divaState.viewerCore.getCurrentLayout();\n var pageOffset = layout.getPageToViewportCenterOffset(settings.currentPageIndex, viewerState.viewport);\n\n var state = {\n 'f': settings.inFullscreen,\n 'v': view,\n 'z': settings.zoomLevel,\n 'n': settings.pagesPerRow,\n 'i': settings.enableFilename ? settings.manifest.pages[settings.currentPageIndex].f : false,\n 'p': settings.enableFilename ? false : settings.currentPageIndex + 1,\n 'y': pageOffset ? pageOffset.y : false,\n 'x': pageOffset ? pageOffset.x : false\n };\n\n return state;\n };\n\n var getLoadOptionsForState = function (state, manifest)\n {\n manifest = manifest || settings.manifest;\n\n var options = ('v' in state) ? getViewState(state.v) : {};\n\n if ('f' in state)\n options.inFullscreen = state.f;\n\n if ('z' in state)\n options.zoomLevel = state.z;\n\n if ('n' in state)\n options.pagesPerRow = state.n;\n\n // Only change specify the page if state.i or state.p is valid\n var pageIndex = getPageIndexForManifest(manifest, state.i);\n\n if (!(pageIndex >= 0 && pageIndex < manifest.pages.length))\n {\n pageIndex = state.p - 1;\n\n // Possibly NaN\n if (!(pageIndex >= 0 && pageIndex < manifest.pages.length))\n pageIndex = null;\n }\n\n if (pageIndex !== null)\n {\n var horizontalOffset = parseInt(state.x, 10);\n var verticalOffset = parseInt(state.y, 10);\n\n options.goDirectlyTo = pageIndex;\n options.horizontalOffset = horizontalOffset;\n options.verticalOffset = verticalOffset;\n }\n\n return options;\n };\n\n var getURLHash = function ()\n {\n var hashParams = getState();\n var hashStringBuilder = [];\n var param;\n\n for (param in hashParams)\n {\n if (hashParams[param] !== false)\n hashStringBuilder.push(param + settings.hashParamSuffix + '=' + encodeURIComponent(hashParams[param]));\n }\n\n return hashStringBuilder.join('&');\n };\n\n // Returns the URL to the current state of the document viewer (so it should be an exact replica)\n var getCurrentURL = function ()\n {\n return location.protocol + '//' + location.host + location.pathname + location.search + '#' + getURLHash();\n };\n\n var getViewState = function(view)\n {\n switch (view)\n {\n case 'd':\n return {\n inGrid: false,\n inBookLayout: false\n };\n\n case 'b':\n return {\n inGrid: false,\n inBookLayout: true\n };\n\n case 'g':\n return {\n inGrid: true,\n inBookLayout: false\n };\n\n default:\n return null;\n }\n };\n\n var showError = function(message)\n {\n divaState.viewerCore.showError(message);\n };\n\n var ajaxError = function(jqxhr, status, error)\n {\n // Show a basic error message within the document viewer pane\n\n var errorMessage = ['Invalid objectData setting. Error code: ' + jqxhr.status + ' ' + error];\n\n // Detect and handle CORS errors\n var dataHasAbsolutePath = settings.objectData.lastIndexOf('http', 0) === 0;\n\n if (dataHasAbsolutePath && error === '')\n {\n var jsonHost = settings.objectData.replace(/https?:\\/\\//i, \"\").split(/[/?#]/)[0];\n\n if (location.hostname !== jsonHost)\n {\n errorMessage.push(\n elt('p', 'Attempted to access cross-origin data without CORS.'),\n elt('p',\n 'You may need to update your server configuration to support CORS. For help, see the ',\n elt('a', {\n href: 'https://github.com/DDMAL/diva.js/wiki/Installation#a-note-about-cross-site-requests',\n target: '_blank'\n }, 'cross-site request documentation.')\n )\n );\n }\n }\n\n showError(errorMessage);\n };\n\n var loadObjectData = function (responseData, hashState)\n {\n var manifest;\n\n // TODO improve IIIF detection method\n if (!responseData.hasOwnProperty('@context') && (responseData['@context'].indexOf('iiif') === -1 || responseData['@context'].indexOf('shared-canvas') === -1))\n {\n throw new NotAnIIIFManifestException('This does not appear to be a IIIF Manifest.');\n }\n\n // trigger ManifestDidLoad event\n // FIXME: Why is this triggered before the manifest is parsed? See https://github.com/DDMAL/diva.js/issues/357\n diva.Events.publish('ManifestDidLoad', [responseData], self);\n\n manifest = ImageManifest.fromIIIF(responseData);\n var loadOptions = hashState ? getLoadOptionsForState(hashState, manifest) : {};\n\n divaState.viewerCore.setManifest(manifest, loadOptions);\n };\n\n /** Parse the hash parameters into the format used by getState and setState */\n var getHashParamState = function ()\n {\n var state = {};\n\n ['f', 'v', 'z', 'n', 'i', 'p', 'y', 'x'].forEach(function (param)\n {\n var value = HashParams.get(param + settings.hashParamSuffix);\n\n // `false` is returned if the value is missing\n if (value !== false)\n state[param] = value;\n });\n\n // Do some awkward special-casing, since this format is kind of weird.\n\n // For inFullscreen (f), true and false strings should be interpreted\n // as booleans.\n if (state.f === 'true')\n state.f = true;\n else if (state.f === 'false')\n state.f = false;\n\n // Convert numerical values to integers, if provided\n ['z', 'n', 'p', 'x', 'y'].forEach(function (param)\n {\n if (param in state)\n state[param] = parseInt(state[param], 10);\n });\n\n return state;\n };\n\n var checkLoaded = function()\n {\n if (!viewerState.loaded)\n {\n console.warn(\"The viewer is not completely initialized. This is likely because it is still downloading data. To fix this, only call this function if the isReady() method returns true.\");\n return false;\n }\n return true;\n };\n\n var init = function ()\n {\n // In order to fill the height, use a wrapper div displayed using a flexbox layout\n var wrapperElement = elt('div', {\n class: \"diva-wrapper\" + (options.fillParentHeight ? \" diva-wrapper-flexbox\" : \"\")\n });\n element.appendChild(wrapperElement);\n options.toolbarParentObject = options.toolbarParentObject || $(wrapperElement);\n\n var viewerCore = new ViewerCore(wrapperElement, options, self);\n\n viewerState = viewerCore.getInternalState();\n settings = viewerCore.getSettings();\n\n // Add the ID to the wrapper element now that the ID has been generated by the viewer core\n wrapperElement.id = settings.ID + 'wrapper';\n\n divaState = {\n viewerCore: viewerCore,\n toolbar: settings.enableToolbar ? createToolbar(self) : null\n };\n\n var hashState = getHashParamState();\n\n if (typeof settings.objectData === 'object')\n {\n // Defer execution until initialization has completed\n setTimeout(function ()\n {\n loadObjectData(settings.objectData, hashState);\n }, 0);\n }\n else\n {\n var pendingManifestRequest = $.ajax({\n url: settings.objectData,\n cache: true,\n dataType: 'json',\n error: ajaxError,\n success: function (responseData)\n {\n loadObjectData(responseData, hashState);\n }\n });\n\n // Store the pending request so that it can be cancelled in the event that Diva needs to be destroyed\n viewerCore.setPendingManifestRequest(pendingManifestRequest);\n }\n };\n\n /* PUBLIC FUNCTIONS\n ===============================================\n */\n\n // Returns the title of the document, based on the directory name\n this.getItemTitle = function ()\n {\n return settings.manifest.itemTitle;\n };\n\n // Go to a particular page by its page number (with indexing starting at 1)\n //xAnchor may either be \"left\", \"right\", or default \"center\"; the (xAnchor) side of the page will be anchored to the (xAnchor) side of the diva-outer element\n //yAnchor may either be \"top\", \"bottom\", or default \"center\"; same process as xAnchor.\n // returns True if the page number passed is valid; false if it is not.\n this.gotoPageByNumber = function (pageNumber, xAnchor, yAnchor)\n {\n console.warn(\"This method is deprecated. Consider using gotoPageByIndex(pageIndex, xAnchor, yAnchor) instead.\");\n var pageIndex = parseInt(pageNumber, 10) - 1;\n return this.gotoPageByIndex(pageIndex, xAnchor, yAnchor);\n };\n\n // Go to a particular page (with indexing starting at 0)\n //xAnchor may either be \"left\", \"right\", or default \"center\"; the (xAnchor) side of the page will be anchored to the (xAnchor) side of the diva-outer element\n //yAnchor may either be \"top\", \"bottom\", or default \"center\"; same process as xAnchor.\n // returns True if the page index is valid; false if it is not.\n this.gotoPageByIndex = function (pageIndex, xAnchor, yAnchor)\n {\n pageIndex = parseInt(pageIndex, 10);\n\n if (isPageValid(pageIndex))\n {\n var xOffset = divaState.viewerCore.getXOffset(pageIndex, xAnchor);\n var yOffset = divaState.viewerCore.getYOffset(pageIndex, yAnchor);\n\n viewerState.renderer.goto(pageIndex, yOffset, xOffset);\n return true;\n }\n\n return false;\n };\n\n this.getNumberOfPages = function ()\n {\n if (!checkLoaded())\n return false;\n\n return settings.numPages;\n };\n\n // Get page dimensions in the current view and zoom level\n this.getPageDimensions = function (pageIndex)\n {\n if (!checkLoaded())\n return null;\n\n return divaState.viewerCore.getCurrentLayout().getPageDimensions(pageIndex);\n };\n\n // Returns the dimensions of a given page index at a given zoom level\n this.getPageDimensionsAtZoomLevel = function (pageIdx, zoomLevel)\n {\n if (!checkLoaded())\n return false;\n\n if (zoomLevel > settings.maxZoomLevel)\n zoomLevel = settings.maxZoomLevel;\n\n var pg = settings.manifest.pages[parseInt(pageIdx, 10)];\n var pgAtZoom = pg.d[parseInt(zoomLevel, 10)];\n return {'width': pgAtZoom.w, 'height': pgAtZoom.h};\n };\n\n // Returns the dimensions of a given page at the current zoom level\n // The current page index will be used if no pageIndex is specified\n // Also works in Grid view\n this.getPageDimensionsAtCurrentZoomLevel = function(pageIndex)\n {\n pageIndex = isPageValid(pageIndex) ? pageIndex : settings.currentPageIndex;\n\n if (!isPageValid(pageIndex))\n throw new Error('Invalid Page Index');\n\n return divaState.viewerCore.getCurrentLayout().getPageDimensions(pageIndex);\n };\n\n // Returns the dimensions of the current page at the current zoom level\n // Also works in Grid view\n this.getCurrentPageDimensionsAtCurrentZoomLevel = function ()\n {\n return this.getPageDimensionsAtCurrentZoomLevel(settings.currentPageIndex);\n };\n\n this.isReady = function ()\n {\n return viewerState.loaded;\n };\n\n this.getCurrentPageIndex = function ()\n {\n return settings.currentPageIndex;\n };\n\n this.getCurrentPageFilename = function ()\n {\n return settings.manifest.pages[settings.currentPageIndex].f;\n };\n\n this.getCurrentCanvas = function (settings)\n {\n return settings.manifest.pages[settings.currentPageIndex].canvas;\n };\n\n this.getCurrentPageNumber = function ()\n {\n console.warn(\"This method is deprecated. Consider using getCurrentPageIndex() instead.\");\n return settings.currentPageIndex + 1;\n };\n\n // Returns an array of all filenames in the document\n this.getFilenames = function ()\n {\n var filenames = [];\n\n for (var i = 0; i < settings.numPages; i++)\n {\n filenames[i] = settings.manifest.pages[i].f;\n }\n\n return filenames;\n };\n\n // Returns the current zoom level\n this.getZoomLevel = function ()\n {\n return settings.zoomLevel;\n };\n\n // gets the maximum zoom level for the entire document\n this.getMaxZoomLevel = function ()\n {\n return settings.maxZoomLevel;\n };\n\n // gets the max zoom level for a given page\n this.getMaxZoomLevelForPage = function (pageIdx)\n {\n if (!checkLoaded)\n return false;\n\n return settings.manifest.pages[pageIdx].m;\n };\n\n this.getMinZoomLevel = function ()\n {\n return settings.minZoomLevel;\n };\n\n // Use the provided zoom level (will check for validity first)\n // Returns false if the zoom level is invalid, true otherwise\n this.setZoomLevel = function (zoomLevel)\n {\n if (settings.inGrid)\n {\n reloadViewer({\n inGrid: false\n });\n }\n\n return divaState.viewerCore.zoom(zoomLevel);\n };\n\n this.getGridPagesPerRow = function ()\n {\n // TODO(wabain): Add test case\n return this.pagesPerRow;\n };\n\n this.setGridPagesPerRow = function (newValue)\n {\n // TODO(wabain): Add test case\n if (!divaState.viewerCore.isValidOption('pagesPerRow', newValue))\n return false;\n\n return reloadViewer({\n inGrid: true,\n pagesPerRow: newValue\n });\n };\n\n // Zoom in. Will return false if it's at the maximum zoom\n this.zoomIn = function ()\n {\n return this.setZoomLevel(settings.zoomLevel + 1);\n };\n\n // Zoom out. Will return false if it's at the minimum zoom\n this.zoomOut = function ()\n {\n return this.setZoomLevel(settings.zoomLevel - 1);\n };\n\n // Check if something (e.g. a highlight box on a particular page) is visible\n this.isRegionInViewport = function (pageIndex, leftOffset, topOffset, width, height)\n {\n var layout = divaState.viewerCore.getCurrentLayout();\n\n if (!layout)\n return false;\n\n var offset = layout.getPageOffset(pageIndex);\n\n var top = offset.top + topOffset;\n var left = offset.left + leftOffset;\n\n return viewerState.viewport.intersectsRegion({\n top: top,\n bottom: top + height,\n left: left,\n right: left + width\n });\n };\n\n //Public wrapper for isPageVisible\n //Determines if a page is currently in the viewport\n this.isPageInViewport = function (pageIndex)\n {\n return viewerState.renderer.isPageVisible(pageIndex);\n };\n\n //Public wrapper for isPageLoaded\n //Determines if a page is currently in the DOM\n this.isPageLoaded = function (pageIndex)\n {\n console.warn(\"This method is deprecated. Consider using isPageInViewport(pageIndex) instead.\");\n return this.isPageInViewport(pageIndex);\n };\n\n // Toggle fullscreen mode\n this.toggleFullscreenMode = function ()\n {\n toggleFullscreen();\n };\n\n // Show/Hide non-paged pages\n this.toggleNonPagedPagesVisibility = function ()\n {\n reloadViewer({ showNonPagedPages: !settings.showNonPagedPages });\n };\n\n // Show non-paged pages\n this.showNonPagedPages = function ()\n {\n reloadViewer({ showNonPagedPages: true });\n };\n\n // Hide non-paged pages\n this.hideNonPagedPages = function ()\n {\n reloadViewer({ showNonPagedPages: false });\n };\n\n // Close toolbar popups\n this.closePopups = function ()\n {\n divaState.toolbar.closePopups();\n };\n\n // Enter fullscreen mode if currently not in fullscreen mode\n // Returns false if in fullscreen mode initially, true otherwise\n // This function will work even if enableFullscreen is set to false\n this.enterFullscreenMode = function ()\n {\n if (!settings.inFullscreen)\n {\n toggleFullscreen();\n return true;\n }\n\n return false;\n };\n\n // Leave fullscreen mode if currently in fullscreen mode\n // Returns true if in fullscreen mode intitially, false otherwise\n this.leaveFullscreenMode = function ()\n {\n if (settings.inFullscreen)\n {\n toggleFullscreen();\n return true;\n }\n\n return false;\n };\n\n this.isInFullscreen = function ()\n {\n return settings.inFullscreen;\n };\n\n // Change views. Takes 'document', 'book', or 'grid' to specify which view to switch into\n this.changeView = function(destinationView)\n {\n return changeView(destinationView);\n };\n\n // Enter grid view if currently not in grid view\n // Returns false if in grid view initially, true otherwise\n this.enterGridView = function ()\n {\n if (!settings.inGrid)\n {\n changeView('grid');\n return true;\n }\n\n return false;\n };\n\n // Leave grid view if currently in grid view\n // Returns true if in grid view initially, false otherwise\n this.leaveGridView = function ()\n {\n if (settings.inGrid)\n {\n reloadViewer({ inGrid: false });\n return true;\n }\n\n return false;\n };\n\n // Jump to a page based on its filename\n // Returns true if successful and false if the filename is invalid\n this.gotoPageByName = function (filename, xAnchor, yAnchor)\n {\n var pageIndex = getPageIndex(filename);\n return this.gotoPageByIndex(pageIndex, xAnchor, yAnchor);\n };\n\n this.gotoPageByLabel = function (label, xAnchor, yAnchor)\n {\n var pages = settings.manifest.pages;\n for (var i = 0, len = pages.length; i < len; i++)\n {\n if (pages[i].l.toLowerCase().indexOf(label.toLowerCase()) > -1)\n return this.gotoPageByIndex(i, xAnchor, yAnchor);\n }\n\n // If no label was found, try to parse a page number\n var pageIndex = parseInt(label, 10) - 1;\n return this.gotoPageByIndex(pageIndex, xAnchor, yAnchor);\n };\n\n // Get the page index (0-based) corresponding to a given filename\n // If the page index doesn't exist, this will return -1\n this.getPageIndex = function (filename)\n {\n return getPageIndex(filename);\n };\n\n // Get the current URL (exposes the private method)\n this.getCurrentURL = function ()\n {\n return getCurrentURL();\n };\n\n // Check if a page index is within the range of the document\n this.isPageIndexValid = function (pageIndex)\n {\n return isPageValid(pageIndex);\n };\n\n // Get the hash part only of the current URL (without the leading #)\n this.getURLHash = function ()\n {\n return getURLHash();\n };\n\n // Get an object representing the state of this diva instance (for setState)\n this.getState = function ()\n {\n return getState();\n };\n\n // Align this diva instance with a state object (as returned by getState)\n this.setState = function (state)\n {\n reloadViewer(getLoadOptionsForState(state));\n };\n\n // Get the instance selector for this instance, since it's auto-generated.\n this.getInstanceSelector = function ()\n {\n return settings.selector;\n };\n\n // Get the instance ID -- essentially the selector without the leading '#'.\n this.getInstanceId = function ()\n {\n return settings.ID;\n };\n\n this.getSettings = function ()\n {\n return settings;\n };\n\n /*\n Translates a measurement from the zoom level on the largest size\n to one on the current zoom level. Takes a single number from the\n max zoom level and translates that to a number scaled to the current\n zoom level.\n\n For example, a point 1000 on an image that is on zoom level 2 of 5\n translates to a position of 111.111... (1000 / (5 - 2)^2).\n\n Works for a single pixel co-ordinate or a dimension (e.g., translates a box\n that is 1000 pixels wide on the original to one that is 111.111 pixels wide\n on the current zoom level).\n */\n this.translateFromMaxZoomLevel = function (position)\n {\n var zoomDifference = settings.maxZoomLevel - settings.zoomLevel;\n return position / Math.pow(2, zoomDifference);\n };\n\n /*\n Translates a measurement from the current zoom level to the position on the\n largest zoom level. Takes a single number and returns that number's value on the\n image at the max zoom level.\n\n Works for a single pixel co-ordinate or a dimension (e.g., translates a box\n that is 111.111 pixels wide on the current image to one that is 1000 pixels wide\n on the current zoom level).\n */\n this.translateToMaxZoomLevel = function (position)\n {\n var zoomDifference = settings.maxZoomLevel - settings.zoomLevel;\n\n // if there is no difference, it's a number on the max zoom level and\n // we can just return the position.\n if (zoomDifference === 0)\n return position;\n\n return position * Math.pow(2, zoomDifference);\n };\n\n // Re-enables document dragging, scrolling (by keyboard if set), and zooming by double-clicking\n this.enableScrollable = function()\n {\n divaState.viewerCore.enableScrollable();\n };\n\n // Disables document dragging, scrolling (by keyboard if set), and zooming by double-clicking\n this.disableScrollable = function ()\n {\n divaState.viewerCore.disableScrollable();\n };\n\n //Changes between horizontal layout and vertical layout. Returns true if document is now vertically oriented, false otherwise.\n this.toggleOrientation = function ()\n {\n return toggleOrientation();\n };\n\n //Returns distance between the northwest corners of diva-inner and page index\n this.getPageOffset = function(pageIndex, options)\n {\n var region = divaState.viewerCore.getPageRegion(pageIndex, options);\n\n return {\n top: region.top,\n left: region.left\n };\n };\n\n //shortcut to getPageOffset for current page\n this.getCurrentPageOffset = function()\n {\n return this.getPageOffset(settings.currentPageIndex);\n };\n\n //Returns the page dimensions of given page at the current zoom level\n this.getPageDimensionsAtCurrentGridLevel = function(pageIndex)\n {\n console.warn(\"This method is deprecated. Consider using getPageDimensionsAtCurrentZoomLevel(pageIndex) instead.\");\n return this.getPageDimensionsAtCurrentZoomLevel(pageIndex);\n };\n\n /*\n Given a pageX and pageY value (as could be retreived from a jQuery event object),\n returns either the page visible at that (x,y) position or -1 if no page is.\n */\n this.getPageIndexForPageXYValues = function(pageX, pageY)\n {\n //get the four edges of the outer element\n var outerOffset = viewerState.outerElement.getBoundingClientRect();\n var outerTop = outerOffset.top;\n var outerLeft = outerOffset.left;\n var outerBottom = outerOffset.bottom;\n var outerRight = outerOffset.right;\n\n //if the clicked position was outside the diva-outer object, it was not on a visible portion of a page\n if (pageX < outerLeft || pageX > outerRight)\n return -1;\n\n if (pageY < outerTop || pageY > outerBottom)\n return -1;\n\n //navigate through all diva page objects\n var pages = document.getElementsByClassName('diva-page');\n var curPageIdx = pages.length;\n while (curPageIdx--)\n {\n //get the offset for each page\n var curPage = pages[curPageIdx];\n var curOffset = curPage.getBoundingClientRect();\n\n //if this point is outside the horizontal boundaries of the page, continue\n if (pageX < curOffset.left || pageX > curOffset.right)\n continue;\n\n //same with vertical boundaries\n if (pageY < curOffset.top || pageY > curOffset.bottom)\n continue;\n\n //if we made it through the above two, we found the page we're looking for\n return curPage.getAttribute('data-index');\n }\n\n //if we made it through that entire while loop, we didn't click on a page\n return -1;\n };\n\n /*\n * Given a set of clientX, clientY co-ordinates, returns an object\n *\n **/\n this.getPageCoordinatesHit = function(clientX, clientY)\n {\n if (viewerState.renderer)\n {\n return viewerState.renderer.getPageHit(clientX, clientY);\n }\n\n return null;\n };\n\n /**\n * Returns a URL for the image of the page at the given index. The\n * optional size parameter supports setting the image width or height\n * (default is full-sized).\n */\n this.getPageImageURL = function (pageIndex, size)\n {\n return settings.manifest.getPageImageURL(pageIndex, size);\n };\n\n //Pretty self-explanatory.\n this.isVerticallyOriented = function()\n {\n return settings.verticallyOriented;\n };\n\n this.changeObject = function(objectData)\n {\n viewerState.loaded = false;\n divaState.viewerCore.clear();\n\n if (viewerState.renderer)\n viewerState.renderer.destroy();\n\n viewerState.options.objectData = objectData;\n\n if (typeof objectData === 'object')\n {\n setTimeout(function ()\n {\n loadObjectData(objectData);\n });\n\n return;\n }\n\n viewerState.throbberTimeoutID = setTimeout(function ()\n {\n $(settings.selector + 'throbber').show();\n }, settings.throbberTimeout);\n\n $.ajax({\n url: settings.objectData,\n cache: true,\n dataType: 'json',\n error: ajaxError,\n success: function (responseData)\n {\n loadObjectData(responseData);\n }\n });\n };\n\n this.activate = function ()\n {\n viewerState.isActiveDiva = true;\n };\n\n this.deactivate = function ()\n {\n viewerState.isActiveDiva = false;\n };\n\n // Destroys this instance, tells plugins to do the same (for testing)\n this.destroy = function ()\n {\n divaState.viewerCore.destroy();\n };\n\n // \"Secretly\" expose the page overlay API for the highlight plugin\n this.__addPageOverlay = function (overlay)\n {\n divaState.viewerCore.addPageOverlay(overlay);\n };\n\n this.__removePageOverlay = function (overlay)\n {\n divaState.viewerCore.removePageOverlay(overlay);\n };\n\n /**** Page Alias Functions ****/\n /*\n Main function. Will return the first of these three that\n resolves to boolean true:\n -Explicit alias as defined in pageAliases\n -Result of pageAliasFunction\n -originalPageIndex + 1 (to simulate the original mapping)\n\n Else the function will return false.\n */\n this.getAliasForPageIndex = function(originalPageIndex)\n {\n var pageIndex = parseInt(originalPageIndex, 10);\n return settings.pageAliases[pageIndex] || settings.pageAliasFunction(pageIndex) || pageIndex + 1;\n };\n\n /*\n Returns the first page index found for a given aliased number or false if not found.\n This may cause issues if a specific alias is found for multiple page indices; use getPageIndicesForAlias and reimplement functions as necessary if this is the case.\n */\n this.getPageIndexForAlias = function(aliasedNumber)\n {\n for(var idx = 0; idx < settings.numPages; idx++)\n {\n if(this.getAliasForPageIndex(idx) === aliasedNumber)\n {\n return idx;\n }\n }\n return false;\n };\n\n //Returns array of page indices for a given aliased number. Returns an empty array if none are found.\n this.getPageIndicesForAlias = function(aliasedNumber)\n {\n var indexArr = [];\n for(var idx = 0; idx < settings.numPages; idx++)\n {\n if(this.getAliasForPageIndex(idx) === aliasedNumber)\n {\n indexArr.push(idx);\n }\n }\n return indexArr;\n };\n\n\n //Maps the current page index to getAliasForPageIndex\n this.getCurrentAliasedPageIndex = function()\n {\n return this.getAliasForPageIndex(settings.currentPageIndex);\n };\n\n //Wrapper for gotoPageByIndex, keeping the aliased numbers in mind\n this.gotoPageByAliasedNumber = function(aliasedNumber, xAnchor, yAnchor)\n {\n return this.gotoPageByIndex(this.getPageIndexForAlias(aliasedNumber), xAnchor, yAnchor);\n };\n\n // Call the init function when this object is created.\n init();\n };\n\n $.fn.diva = function (options)\n {\n return this.each(function ()\n {\n var divaParent = $(this);\n\n // Return early if this element already has a plugin instance\n if (divaParent.data('diva'))\n return;\n\n // Throw an error if the element is not in the DOM, since it causes some problems\n if (!document.body.contains(this))\n throw new Error('Diva could not be initialized because this element is not attached to the DOM');\n\n // Otherwise, instantiate the document viewer\n var diva = new Diva(this, options);\n divaParent.data('diva', diva);\n });\n };\n})(jQuery);\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/diva.js\n// module id = 7\n// module chunks = 0","(function () {\n if (Array.prototype.fill) return;\n\n var fill = function (value) {\n // Steps 1-2.\n if (this == null) {\n throw new TypeError(\"this is null or not defined\");\n }\n\n var O = Object(this);\n\n // Steps 3-5.\n var len = O.length >>> 0;\n\n // Steps 6-7.\n var start = arguments[1];\n var relativeStart = start >> 0;\n\n // Step 8.\n var k = relativeStart < 0 ?\n Math.max(len + relativeStart, 0) :\n Math.min(relativeStart, len);\n\n // Steps 9-10.\n var end = arguments[2];\n var relativeEnd = end === undefined ?\n len : end >> 0;\n\n // Step 11.\n var last = relativeEnd < 0 ?\n Math.max(len + relativeEnd, 0) :\n Math.min(relativeEnd, len);\n\n // Step 12.\n while (k < last) {\n O[k] = value;\n k++;\n }\n\n // Step 13.\n return O;\n };\n\n if (Object.defineProperty) {\n try {\n Object.defineProperty(Array.prototype, 'fill', {\n value: fill,\n configurable: true,\n enumerable: false,\n writable: true\n });\n } catch(e) {}\n }\n\n if (!Array.prototype.fill) {\n Array.prototype.fill = fill;\n }\n})(this);\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./~/array.prototype.fill/index.js\n// module id = 8\n// module chunks = 0","module.exports = elt;\nmodule.exports.setAttributes = setDOMAttributes;\n\n/**\n * Convenience function to create a DOM element, set attributes on it, and\n * append children. All arguments which are not of primitive type, are not\n * arrays, and are not DOM nodes are treated as attribute hashes and are\n * handled as described for setDOMAttributes. Children can either be a DOM\n * node or a primitive value, which is converted to a text node. Arrays are\n * handled recursively. Null and undefined values are ignored.\n *\n * Inspired by the ProseMirror helper of the same name.\n */\nfunction elt(tag)\n{\n var el = document.createElement(tag);\n var args = Array.prototype.slice.call(arguments, 1);\n\n while (args.length)\n {\n var arg = args.shift();\n handleEltConstructorArg(el, arg);\n }\n\n return el;\n}\n\nfunction handleEltConstructorArg(el, arg)\n{\n if (arg == null)\n return;\n\n if (typeof arg !== 'object' && typeof arg !== 'function')\n {\n // Coerce to string\n el.appendChild(document.createTextNode(arg));\n }\n else if (arg instanceof window.Node)\n {\n el.appendChild(arg);\n }\n else if (arg instanceof Array)\n {\n var childCount = arg.length;\n for (var i = 0; i < childCount; i++)\n handleEltConstructorArg(el, arg[i]);\n }\n else\n {\n setDOMAttributes(el, arg);\n }\n}\n\n/**\n * Set attributes of a DOM element. The `style` property is special-cased to\n * accept either a string or an object whose own attributes are assigned to\n * el.style.\n */\nfunction setDOMAttributes(el, attributes)\n{\n for (var prop in attributes)\n {\n if (!attributes.hasOwnProperty(prop))\n continue;\n\n if (prop === 'style')\n {\n setStyle(el, attributes.style);\n }\n else\n {\n el.setAttribute(prop, attributes[prop]);\n }\n }\n}\n\nfunction setStyle(el, style)\n{\n if (!style)\n return;\n\n if (typeof style !== 'object')\n {\n el.style.cssText = style;\n return;\n }\n\n for (var cssProp in style)\n {\n if (!style.hasOwnProperty(cssProp))\n continue;\n\n el.style[cssProp] = style[cssProp];\n }\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/utils/elt.js\n// module id = 9\n// module chunks = 0","module.exports.get = getHashParam;\nmodule.exports.update = updateHashParam;\n\n// For getting the #key values from the URL. For specifying a page and zoom level\n// Look into caching, because we only need to get this during the initial load\n// Although for the tests I guess we would need to override caching somehow\nfunction getHashParam(key) {\n var hash = window.location.hash;\n if (hash !== '') {\n // Check if there is something that looks like either &key= or #key=\n var startIndex = (hash.indexOf('&' + key + '=') > 0) ? hash.indexOf('&' + key + '=') : hash.indexOf('#' + key + '=');\n\n // If startIndex is still -1, it means it can't find either\n if (startIndex >= 0) {\n // Add the length of the key plus the & and =\n startIndex += key.length + 2;\n\n // Either to the next ampersand or to the end of the string\n var endIndex = hash.indexOf('&', startIndex);\n if (endIndex > startIndex) {\n return decodeURIComponent(hash.substring(startIndex, endIndex));\n } else if (endIndex < 0) {\n // This means this hash param is the last one\n return decodeURIComponent(hash.substring(startIndex));\n }\n // If the key doesn't have a value I think\n return '';\n } else {\n // If it can't find the key\n return false;\n }\n } else {\n // If there are no hash params just return false\n return false;\n }\n}\n\nfunction updateHashParam(key, value) {\n // First make sure that we have to do any work at all\n var originalValue = getHashParam(key);\n var hash = window.location.hash;\n if (originalValue !== value) {\n // Is the key already in the URL?\n if (typeof originalValue == 'string') {\n // Already in the URL. Just get rid of the original value\n var startIndex = (hash.indexOf('&' + key + '=') > 0) ? hash.indexOf('&' + key + '=') : hash.indexOf('#' + key + '=');\n var endIndex = startIndex + key.length + 2 + originalValue.length;\n // # if it's the first, & otherwise\n var startThing = (startIndex === 0) ? '#' : '&';\n window.location.replace(hash.substring(0, startIndex) + startThing + key + '=' + value + hash.substring(endIndex));\n } else {\n // It's not present - add it\n if (hash.length === 0) {\n window.location.replace('#' + key + '=' + value);\n } else {\n // Append it\n window.location.replace(hash + '&' + key + '=' + value);\n }\n }\n }\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/utils/hash-params.js\n// module id = 10\n// module chunks = 0","var jQuery = require('jquery');\n\n//Used to keep track of whether Diva was last clicked or which Diva was last clicked when there are multiple\nvar ActiveDivaController = (function ($)\n{\n return function ()\n {\n var active;\n\n //global click listener\n $(document).on('click', function(e)\n {\n updateActive($(e.target));\n });\n\n //parameter should already be a jQuery selector\n var updateActive = function (target)\n {\n var nearestOuter;\n\n //these will find 0 or 1 objects, never more\n var findOuter = target.find('.diva-outer');\n var closestOuter = target.closest('.diva-outer');\n var outers = document.getElementsByClassName('diva-outer');\n var outerLen = outers.length;\n var idx;\n\n //clicked on something that was not either a parent or sibling of a diva-outer\n if (findOuter.length > 0)\n {\n nearestOuter = findOuter;\n }\n //clicked on something that was a child of a diva-outer\n else if (closestOuter.length > 0)\n {\n nearestOuter = closestOuter;\n }\n //clicked on something that was not in any Diva tree\n else\n {\n //deactivate everything and return\n for (idx = 0; idx < outerLen; idx++)\n {\n $(outers[idx].parentElement.parentElement).data('diva').deactivate();\n }\n return;\n }\n\n //if we found one, activate it...\n nearestOuter.parent().parent().data('diva').activate();\n active = nearestOuter.parent();\n\n //...and deactivate all the others\n outers = document.getElementsByClassName('diva-outer');\n for(idx = 0; idx < outerLen; idx++)\n {\n //getAttribute to attr - comparing DOM element to jQuery element\n if (outers[idx].getAttribute('id') != nearestOuter.attr('id'))\n $(outers[idx].parentElement.parentElement).data('diva').deactivate();\n }\n };\n\n //public accessor in case. Will return a jQuery selector.\n this.getActive = function()\n {\n return active;\n };\n };\n})(jQuery);\n\nmodule.exports = ActiveDivaController;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/active-diva-controller.js\n// module id = 11\n// module chunks = 0","/* jshint unused: true */\n\nvar parseIIIFManifest = require('./parse-iiif-manifest');\n\nmodule.exports = ImageManifest;\n\nfunction ImageManifest(data, urlAdapter)\n{\n // Save all the data we need\n this.pages = data.pgs;\n this.maxZoom = data.max_zoom;\n this.maxRatio = data.dims.max_ratio;\n this.minRatio = data.dims.min_ratio;\n this.itemTitle = data.item_title;\n\n // Only given for IIIF manifests\n this.paged = !!data.paged;\n\n // These are arrays, the index corresponding to the zoom level\n this._maxWidths = data.dims.max_w;\n this._maxHeights = data.dims.max_h;\n this._averageWidths = data.dims.a_wid;\n this._averageHeights = data.dims.a_hei;\n this._totalHeights = data.dims.t_hei;\n this._totalWidths = data.dims.t_wid;\n\n this._urlAdapter = urlAdapter;\n}\n\nImageManifest.fromIIIF = function (iiifManifest)\n{\n var data = parseIIIFManifest(iiifManifest);\n return new ImageManifest(data, new IIIFSourceAdapter());\n};\n\nImageManifest.fromLegacyManifest = function (data, config)\n{\n // For IIP manifests, use the page number (indexed starting from 1) as a label for each page\n for (var i = 0, len = data.pgs.length; i < len; i++)\n data.pgs[i].l = (i + 1).toString();\n\n return new ImageManifest(data, new LegacyManifestSourceAdapter(config));\n};\n\nImageManifest.prototype.isPageValid = function (pageIndex, showNonPagedPages)\n{\n if (!showNonPagedPages && this.paged && !this.pages[pageIndex].paged)\n return false;\n\n return pageIndex >= 0 && pageIndex < this.pages.length;\n};\n\nImageManifest.prototype.getMaxPageDimensions = function (pageIndex)\n{\n var maxDims = this.pages[pageIndex].d[this.maxZoom];\n\n return {\n height: maxDims.h,\n width: maxDims.w\n };\n};\n\nImageManifest.prototype.getPageDimensionsAtZoomLevel = function (pageIndex, zoomLevel)\n{\n var maxDims = this.pages[pageIndex].d[this.maxZoom];\n\n var scaleRatio = getScaleRatio(this.maxZoom, zoomLevel);\n\n return {\n height: maxDims.h * scaleRatio,\n width: maxDims.w * scaleRatio\n };\n};\n\n/**\n * Returns a URL for the image of the given page. The optional size\n * parameter supports setting the image width or height (default is\n * full-sized).\n */\nImageManifest.prototype.getPageImageURL = function (pageIndex, size)\n{\n return this._urlAdapter.getPageImageURL(this, pageIndex, size);\n};\n\n/**\n * Return an array of tile objects for the specified page and integer zoom level\n */\nImageManifest.prototype.getPageImageTiles = function (pageIndex, zoomLevel, tileDimensions)\n{\n var page = this.pages[pageIndex];\n\n if (!isFinite(zoomLevel) || zoomLevel % 1 !== 0)\n throw new TypeError('Zoom level must be an integer: ' + zoomLevel);\n\n var rows = Math.ceil(page.d[zoomLevel].h / tileDimensions.height);\n var cols = Math.ceil(page.d[zoomLevel].w / tileDimensions.width);\n\n var tiles = [];\n\n var row, col, url;\n\n for (row = 0; row < rows; row++)\n {\n for (col = 0; col < cols; col++)\n {\n url = this._urlAdapter.getTileImageURL(this, pageIndex, {\n row: row,\n col: col,\n rowCount: rows,\n colCount: cols,\n zoomLevel: zoomLevel,\n tileDimensions: tileDimensions\n });\n\n // FIXME: Dimensions should account for partial tiles (e.g. the\n // last row and column in a tiled image)\n tiles.push({\n row: row,\n col: col,\n zoomLevel: zoomLevel,\n dimensions: {\n height: tileDimensions.height,\n width: tileDimensions.width\n },\n offset: {\n top: row * tileDimensions.height,\n left: col * tileDimensions.width\n },\n url: url\n });\n }\n }\n\n return {\n zoomLevel: zoomLevel,\n rows: rows,\n cols: cols,\n tiles: tiles\n };\n};\n\nImageManifest.prototype.getMaxWidth = zoomedPropertyGetter('_maxWidths');\nImageManifest.prototype.getMaxHeight = zoomedPropertyGetter('_maxHeights');\nImageManifest.prototype.getAverageWidth = zoomedPropertyGetter('_averageWidths');\nImageManifest.prototype.getAverageHeight = zoomedPropertyGetter('_averageHeights');\nImageManifest.prototype.getTotalWidth = zoomedPropertyGetter('_totalWidths');\nImageManifest.prototype.getTotalHeight = zoomedPropertyGetter('_totalHeights');\n\nfunction zoomedPropertyGetter(privateName)\n{\n return function (zoomLevel)\n {\n return this[privateName][zoomLevel];\n };\n}\n\nfunction getScaleRatio(sourceZoomLevel, targetZoomLevel)\n{\n return 1 / Math.pow(2, sourceZoomLevel - targetZoomLevel);\n}\n\nfunction IIIFSourceAdapter()\n{\n // No-op\n}\n\nIIIFSourceAdapter.prototype.getPageImageURL = function (manifest, pageIndex, size)\n{\n var dimens;\n\n if (!size || (size.width == null && size.height == null))\n dimens = 'full';\n else\n dimens = (size.width == null ? '' : size.width) + ',' + (size.height == null ? '' : size.height);\n\n var page = manifest.pages[pageIndex];\n var quality = (page.api > 1.1) ? 'default' : 'native';\n\n return encodeURI(page.url + 'full/' + dimens + '/0/' + quality + '.jpg');\n};\n\nIIIFSourceAdapter.prototype.getTileImageURL = function (manifest, pageIndex, params)\n{\n var page = manifest.pages[pageIndex];\n\n var height, width;\n\n if (params.row === params.rowCount - 1)\n height = page.d[params.zoomLevel].h - (params.rowCount - 1) * params.tileDimensions.height;\n else\n height = params.tileDimensions.height;\n\n if (params.col === params.colCount - 1)\n width = page.d[params.zoomLevel].w - (params.colCount - 1) * params.tileDimensions.width;\n else\n width = params.tileDimensions.width;\n\n var zoomDifference = Math.pow(2, manifest.maxZoom - params.zoomLevel);\n\n var x = params.col * params.tileDimensions.width * zoomDifference;\n var y = params.row * params.tileDimensions.height * zoomDifference;\n\n if (page.hasOwnProperty('xoffset'))\n {\n x += page.xoffset;\n y += page.yoffset;\n }\n\n var region = [x, y, width * zoomDifference, height * zoomDifference].join(',');\n\n var quality = (page.api > 1.1) ? 'default' : 'native';\n\n return encodeURI(page.url + region + '/' + width + ',' + height + '/0/' + quality + '.jpg');\n};\n\nfunction LegacyManifestSourceAdapter(config)\n{\n this._config = config;\n}\n\nLegacyManifestSourceAdapter.prototype.getPageImageURL = function (manifest, pageIndex, size)\n{\n // Without width or height specified, IIPImage defaults to full-size\n var dimens = '';\n\n if (size)\n {\n if (size.width != null)\n dimens += '&WID=' + size.width;\n\n if (size.height != null)\n dimens += '&HEI=' + size.height;\n }\n\n var filename = manifest.pages[pageIndex].f;\n\n return this._config.iipServerURL + \"?FIF=\" + this._config.imageDir + '/' + filename + dimens + '&CVT=JPEG';\n};\n\nLegacyManifestSourceAdapter.prototype.getTileImageURL = function (manifest, pageIndex, params)\n{\n var page = manifest.pages[pageIndex];\n var requestedZoomLevel = params.zoomLevel + page.m - manifest.maxZoom;\n var index = (params.row * params.colCount) + params.col;\n var jtl = requestedZoomLevel + ',' + index;\n\n return encodeURI(this._config.iipServerURL + \"?FIF=\" + this._config.imageDir + '/' + page.f + '&JTL=' + jtl + '&CVT=JPEG');\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/image-manifest.js\n// module id = 12\n// module chunks = 0","/* jshint unused: true */\n\nmodule.exports = parseIIIFManifest;\n\nvar getMaxZoomLevel = function (width, height)\n{\n var largestDimension = Math.max(width, height);\n return Math.ceil(Math.log((largestDimension + 1) / (256 + 1)) / Math.log(2));\n};\n\nvar incorporateZoom = function (imageDimension, zoomDifference)\n{\n return imageDimension / (Math.pow(2, zoomDifference));\n};\n\nvar getOtherImageData = function(otherImages, lowestMaxZoom, canvasWidth, canvasHeight)\n{\n return otherImages.map(\n function (itm)\n {\n var w = itm.width || canvasWidth;\n var h = itm.height || canvasHeight;\n\n var dims = new Array(lowestMaxZoom + 1);\n for (var j = 0; j < lowestMaxZoom + 1; j++)\n {\n dims[j] = {\n h: Math.floor(incorporateZoom(h, lowestMaxZoom - j)),\n w: Math.floor(incorporateZoom(w, lowestMaxZoom - j))\n };\n }\n return {\n label: itm.label || \"\",\n dims: dims\n };\n }\n );\n};\n\n/**\n * Parses a IIIF Presentation API Manifest and converts it into a Diva.js-format object\n * (See https://github.com/DDMAL/diva.js/wiki/Development-notes#data-received-through-ajax-request)\n * (This is a client-side re-implementation of generate_json.py)\n *\n * @param {Object} manifest - an object that represents a valid IIIF manifest\n * @returns {Object} divaServiceBlock - the data needed by Diva to show a view of a single document\n */\nfunction parseIIIFManifest(manifest)\n{\n var sequence = manifest.sequences[0];\n var canvases = sequence.canvases;\n var numCanvases = canvases.length;\n\n var pages = new Array(canvases.length);\n\n var thisCanvas, thisResource, thisImage, otherImages, context, url, info, imageAPIVersion,\n width, height, maxZoom, canvas, label, imageLabel, zoomDimensions, widthAtCurrentZoomLevel,\n heightAtCurrentZoomLevel;\n\n var lowestMaxZoom = 100;\n var maxRatio = 0;\n var minRatio = 100;\n\n // quickly determine the lowest possible max zoom level (i.e., the upper bound for images) across all canvases.\n // while we're here, compute the global ratios as well.\n for (var z = 0; z < numCanvases; z++)\n {\n var c = canvases[z];\n var w = c.width;\n var h = c.height;\n var mz = getMaxZoomLevel(w, h);\n var ratio = h / w;\n maxRatio = Math.max(ratio, maxRatio);\n minRatio = Math.min(ratio, minRatio);\n\n lowestMaxZoom = Math.min(lowestMaxZoom, mz);\n }\n\n // Uint8Arrays are pre-initialized with zeroes. These ones need to be\n // pre-initialized since we will do arithmetic and value checking on them\n var totalWidths = new Array(lowestMaxZoom + 1).fill(0);\n var totalHeights = new Array(lowestMaxZoom + 1).fill(0);\n var maxWidths = new Array(lowestMaxZoom + 1).fill(0);\n var maxHeights = new Array(lowestMaxZoom + 1).fill(0);\n\n for (var i = 0; i < numCanvases; i++)\n {\n thisCanvas = canvases[i];\n canvas = thisCanvas['@id'];\n label = thisCanvas.label;\n thisResource = thisCanvas.images[0].resource;\n\n /*\n * If a canvas has multiple images it will be encoded\n * with a resource type of \"oa:Choice\". The primary image will be available\n * on the 'default' key, with other images available under 'item.'\n * */\n if (thisResource['@type'] === \"oa:Choice\")\n {\n thisImage = thisResource.default;\n }\n else\n {\n thisImage = thisResource;\n }\n\n // Prioritize the canvas height / width first, since images may not have h/w\n width = thisCanvas.width || thisImage.width;\n height = thisCanvas.height || thisImage.height;\n if (width <= 0 || height <= 0)\n {\n console.warn('Invalid width or height for canvas ' + label + '. Skipping');\n continue;\n }\n\n maxZoom = getMaxZoomLevel(width, height);\n\n if (thisResource.item)\n {\n otherImages = getOtherImageData(thisResource.item, lowestMaxZoom, width, height);\n }\n\n imageLabel = thisImage.label || null;\n\n info = parseImageInfo(thisImage);\n url = info.url.slice(-1) !== '/' ? info.url + '/' : info.url; // append trailing slash to url if it's not there.\n\n context = thisImage.service['@context'];\n\n if (context === 'http://iiif.io/api/image/2/context.json')\n {\n imageAPIVersion = 2;\n }\n else if (context === 'http://library.stanford.edu/iiif/image-api/1.1/context.json')\n {\n imageAPIVersion = 1.1;\n }\n else\n {\n imageAPIVersion = 1.0;\n }\n\n zoomDimensions = new Array(lowestMaxZoom + 1);\n\n for (var k = 0; k < lowestMaxZoom + 1; k++)\n {\n widthAtCurrentZoomLevel = Math.floor(incorporateZoom(width, lowestMaxZoom - k));\n heightAtCurrentZoomLevel = Math.floor(incorporateZoom(height, lowestMaxZoom - k));\n zoomDimensions[k] = {\n h: heightAtCurrentZoomLevel,\n w: widthAtCurrentZoomLevel\n };\n\n totalWidths[k] += widthAtCurrentZoomLevel;\n totalHeights[k] += heightAtCurrentZoomLevel;\n maxWidths[k] = Math.max(widthAtCurrentZoomLevel, maxWidths[k]);\n maxHeights[k] = Math.max(heightAtCurrentZoomLevel, maxHeights[k]);\n }\n\n pages[i] = {\n d: zoomDimensions,\n m: maxZoom,\n l: label, // canvas label ('page 1, page 2', etc.)\n il: imageLabel, // default image label ('primary image', 'UV light', etc.)\n f: info.url,\n url: url,\n api: imageAPIVersion,\n paged: thisCanvas.viewingHint !== 'non-paged',\n facingPages: thisCanvas.viewingHint === 'facing-pages',\n canvas: canvas,\n otherImages: otherImages,\n xoffset: info.x || null,\n yoffset: info.y || null\n };\n }\n\n var averageWidths = new Array(lowestMaxZoom + 1);\n var averageHeights = new Array(lowestMaxZoom + 1);\n\n for (var a = 0; a < lowestMaxZoom + 1; a++)\n {\n averageWidths[a] = totalWidths[a] / numCanvases;\n averageHeights[a] = totalHeights[a] / numCanvases;\n }\n\n var dims = {\n a_wid: averageWidths,\n a_hei: averageHeights,\n max_w: maxWidths,\n max_h: maxHeights,\n max_ratio: maxRatio,\n min_ratio: minRatio,\n t_hei: totalHeights,\n t_wid: totalWidths\n };\n\n return {\n item_title: manifest.label,\n dims: dims,\n max_zoom: lowestMaxZoom,\n pgs: pages,\n paged: manifest.viewingHint === 'paged' || sequence.viewingHint === 'paged'\n };\n}\n\n/**\n * Takes in a resource block from a canvas and outputs the following information associated with that resource:\n * - Image URL\n * - Image region to be displayed\n *\n * @param {Object} resource - an object representing the resource block of a canvas section in a IIIF manifest\n * @returns {Object} imageInfo - an object containing image URL and region\n */\nfunction parseImageInfo(resource)\n{\n var url = resource['@id'];\n var fragmentRegex = /#xywh=([0-9]+,[0-9]+,[0-9]+,[0-9]+)/;\n var xywh = '';\n var stripURL = true;\n\n if (/\\/([0-9]+,[0-9]+,[0-9]+,[0-9]+)\\//.test(url))\n {\n // if resource in image API format, extract region x,y,w,h from URL (after 4th slash from last)\n // matches coordinates in URLs of the form http://www.example.org/iiif/book1-page1/40,50,1200,1800/full/0/default.jpg\n var urlArray = url.split('/');\n xywh = urlArray[urlArray.length - 4];\n }\n else if (fragmentRegex.test(url))\n {\n // matches coordinates of the style http://www.example.org/iiif/book1/canvas/p1#xywh=50,50,320,240\n var result = fragmentRegex.exec(url);\n xywh = result[1];\n }\n else if (resource.service && resource.service['@id'])\n {\n // assume canvas size based on image size\n url = resource.service['@id'];\n // this URL excludes region parameters so we don't need to remove them\n stripURL = false;\n }\n\n if (stripURL)\n {\n // extract URL up to identifier (we eliminate the last 5 parameters: /region/size/rotation/quality.format)\n url = url.split('/').slice(0, -4).join('/');\n }\n\n var imageInfo = {\n url: url\n };\n\n if (xywh.length)\n {\n // parse into separate components\n var dimensions = xywh.split(',');\n imageInfo.x = parseInt(dimensions[0], 10);\n imageInfo.y = parseInt(dimensions[1], 10);\n imageInfo.w = parseInt(dimensions[2], 10);\n imageInfo.h = parseInt(dimensions[3], 10);\n }\n\n return imageInfo;\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/parse-iiif-manifest.js\n// module id = 13\n// module chunks = 0","var $ = require('jquery');\n\nvar diva = require('./diva-global');\nvar elt = require('./utils/elt');\n\nmodule.exports = createToolbar;\n\nfunction createToolbar(viewer)\n{\n var settings = viewer.getSettings();\n\n // FIXME(wabain): Temporarily copied from within Diva\n var elemAttrs = function (ident, base)\n {\n var attrs = {\n id: settings.ID + ident,\n class: 'diva-' + ident\n };\n\n if (base)\n return $.extend(attrs, base);\n else\n return attrs;\n };\n\n /** Convenience function to subscribe to a Diva event */\n var subscribe = function (event, callback)\n {\n diva.Events.subscribe(event, callback, settings.ID);\n };\n\n // Creates a toolbar button\n var createButtonElement = function(name, label, callback)\n {\n var button = elt('button', {\n type: 'button',\n id: settings.ID + name,\n class: 'diva-' + name + ' diva-button',\n title: label\n });\n\n if (callback)\n button.addEventListener('click', callback, false);\n\n return button;\n };\n\n // Higher-level function for creators of zoom and grid controls\n var getResolutionControlCreator = function (config)\n {\n return function ()\n {\n var controls;\n\n switch (settings[config.controllerSetting])\n {\n case 'slider':\n controls = config.createSlider();\n break;\n\n case 'buttons':\n controls = config.createButtons();\n break;\n\n default:\n // Don't display anything\n return null;\n }\n\n var wrapper = elt('span',\n controls,\n config.createLabel()\n );\n\n var updateWrapper = function ()\n {\n if (settings.inGrid === config.showInGrid)\n wrapper.style.display = 'inline';\n else\n wrapper.style.display = 'none';\n };\n\n subscribe('ViewDidSwitch', updateWrapper);\n subscribe('ObjectDidLoad', updateWrapper);\n\n // Set initial value\n updateWrapper();\n\n return wrapper;\n };\n };\n\n // Zoom controls\n var createZoomControls = getResolutionControlCreator({\n controllerSetting: 'enableZoomControls',\n showInGrid: false,\n\n createSlider: function ()\n {\n var elem = createSlider('zoom-slider', {\n step: 0.1,\n value: settings.zoomLevel,\n min: settings.minZoomLevel,\n max: settings.maxZoomLevel\n });\n var $elem = $(elem);\n\n $elem.on('input', function()\n {\n var floatValue = parseFloat(this.value);\n viewer.setZoomLevel(floatValue);\n });\n\n $elem.on('change', function ()\n {\n var floatValue = parseFloat(this.value);\n if (floatValue !== settings.zoomLevel)\n viewer.setZoomLevel(floatValue);\n });\n\n var updateSlider = function ()\n {\n if (settings.zoomLevel !== $elem.val())\n $elem.val(settings.zoomLevel);\n };\n\n subscribe('ZoomLevelDidChange', updateSlider);\n subscribe('ViewerDidLoad', function ()\n {\n elt.setAttributes(elem, {\n min: settings.minZoomLevel,\n max: settings.maxZoomLevel\n });\n\n updateSlider();\n });\n\n return elem;\n },\n\n createButtons: function ()\n {\n return elt('span',\n createButtonElement('zoom-out-button', 'Zoom Out', function ()\n {\n viewer.setZoomLevel(settings.zoomLevel - 1);\n }),\n createButtonElement('zoom-in-button', 'Zoom In', function ()\n {\n viewer.setZoomLevel(settings.zoomLevel + 1);\n })\n );\n },\n\n createLabel: function ()\n {\n var elem = createLabel('diva-zoom-label', 'zoom-label', 'Zoom level: ', 'zoom-level', settings.zoomLevel);\n var textSpan = $(elem).find(settings.selector + 'zoom-level')[0];\n\n var updateText = function ()\n {\n textSpan.textContent = settings.zoomLevel.toFixed(2);\n };\n\n subscribe('ZoomLevelDidChange', updateText);\n subscribe('ViewerDidLoad', updateText);\n\n return elem;\n }\n });\n\n // Grid controls\n var createGridControls = getResolutionControlCreator({\n controllerSetting: 'enableGridControls',\n showInGrid: true,\n\n createSlider: function ()\n {\n var elem = createSlider('grid-slider', {\n value: settings.pagesPerRow,\n min: settings.minPagesPerRow,\n max: settings.maxPagesPerRow\n });\n var $elem = $(elem);\n\n $elem.on('input', function()\n {\n var intValue = parseInt(elem.value, 10);\n viewer.setGridPagesPerRow(intValue);\n });\n\n $elem.on('change', function ()\n {\n var intValue = parseInt(elem.value, 10);\n if (intValue !== settings.pagesPerRow)\n viewer.setGridPagesPerRow(intValue);\n });\n\n subscribe('GridRowNumberDidChange', function ()\n {\n // Update the position of the handle within the slider\n if (settings.pagesPerRow !== $elem.val())\n $elem.val(settings.pagesPerRow);\n });\n\n return elem;\n },\n\n createButtons: function ()\n {\n return elt('span',\n createButtonElement('grid-out-button', 'Zoom Out', function ()\n {\n viewer.setGridPagesPerRow(settings.pagesPerRow - 1);\n }),\n createButtonElement('grid-in-button', 'Zoom In', function ()\n {\n viewer.setGridPagesPerRow(settings.pagesPerRow + 1);\n })\n );\n },\n\n createLabel: function ()\n {\n var elem = createLabel('diva-grid-label', 'grid-label', 'Pages per row: ', 'pages-per-row', settings.pagesPerRow);\n var textSpan = $(elem).find(settings.selector + 'pages-per-row')[0];\n\n subscribe('GridRowNumberDidChange', function ()\n {\n textSpan.textContent = settings.pagesPerRow;\n });\n\n return elem;\n }\n });\n\n var createViewMenu = function()\n {\n var viewOptionsList = elt('div', elemAttrs('view-options'));\n\n var changeViewButton = createButtonElement('view-icon', 'Change view', function ()\n {\n $(viewOptionsList).toggle();\n });\n\n $(document).mouseup(function (event)\n {\n var container = $(viewOptionsList);\n\n if (!container.is(event.target) && container.has(event.target).length === 0 && event.target.id !== settings.ID + 'view-icon')\n {\n container.hide();\n }\n });\n\n var selectView = function (view)\n {\n viewer.changeView(view);\n\n //hide view menu\n $(viewOptionsList).hide();\n };\n\n var updateViewMenu = function()\n {\n var viewIconClasses = ' diva-view-icon diva-button';\n\n // display the icon of the mode we're currently in (?)\n if (settings.inGrid)\n {\n changeViewButton.className = 'diva-grid-icon' + viewIconClasses;\n }\n else if (settings.inBookLayout)\n {\n changeViewButton.className = 'diva-book-icon' + viewIconClasses;\n }\n else\n {\n changeViewButton.className = 'diva-document-icon' + viewIconClasses;\n }\n\n var viewOptions = document.createDocumentFragment();\n\n // then display document, book, and grid buttons in that order, excluding the current view\n if (settings.inGrid || settings.inBookLayout)\n viewOptions.appendChild(createButtonElement('document-icon', 'Document View', selectView.bind(null, 'document')));\n\n if (settings.inGrid || !settings.inBookLayout)\n viewOptions.appendChild(createButtonElement('book-icon', 'Book View', selectView.bind(null, 'book')));\n\n if (!settings.inGrid)\n viewOptions.appendChild(createButtonElement('grid-icon', 'Grid View', selectView.bind(null, 'grid')));\n\n // remove old menu\n while (viewOptionsList.firstChild)\n {\n viewOptionsList.removeChild(viewOptionsList.firstChild);\n }\n\n // insert new menu\n viewOptionsList.appendChild(viewOptions);\n };\n\n subscribe('ViewDidSwitch', updateViewMenu);\n subscribe('ObjectDidLoad', updateViewMenu);\n\n return elt('div', elemAttrs('view-menu'),\n changeViewButton,\n viewOptionsList\n );\n };\n\n var createSlider = function(name, options)\n {\n return elt('input', options, {\n id: settings.ID + name,\n class: 'diva-' + name + ' diva-slider',\n type: 'range'\n });\n };\n\n var createLabel = function(name, id, label, innerName, innerValue)\n {\n return elt('div', {\n id: settings.ID + id,\n class: name + ' diva-label'\n },\n [\n label,\n elt('span', {\n id: settings.ID + innerName\n }, innerValue)\n ]);\n };\n\n var createPageNavigationControls = function ()\n {\n // Go to page form\n var gotoForm = settings.enableGotoPage ? createGotoPageForm() : null;\n\n return elt('span', elemAttrs('page-nav'),\n createPageLabel(), // 'Page x of y' label\n gotoForm\n );\n };\n\n var createGotoPageForm = function ()\n {\n var gotoPageInput = elt('input', {\n id: settings.ID + 'goto-page-input',\n class: 'diva-input diva-goto-page-input',\n autocomplete: 'off',\n type: 'text'\n });\n\n var gotoPageSubmit = elt('input', {\n id: settings.ID + 'goto-page-submit',\n class: 'diva-button diva-button-text',\n type: 'submit',\n value: 'Go'\n });\n\n var inputSuggestions = elt('div', {\n id: settings.ID + 'input-suggestions',\n class: 'diva-input-suggestions'\n }\n );\n\n var gotoForm = elt('form', {\n id: settings.ID + 'goto-page',\n class: 'diva-goto-form'\n },\n gotoPageInput,\n gotoPageSubmit,\n inputSuggestions\n );\n\n $(gotoForm).on('submit', function ()\n {\n var desiredPageLabel = gotoPageInput.value;\n\n if (settings.onGotoSubmit && typeof settings.onGotoSubmit === \"function\")\n {\n var pageIndex = settings.onGotoSubmit(desiredPageLabel);\n if (!viewer.gotoPageByIndex(pageIndex))\n alert(\"No page could be found with that label or page number\");\n\n }\n else // Default if no function is specified in the settings\n {\n if (!viewer.gotoPageByLabel(desiredPageLabel))\n alert(\"No page could be found with that label or page number\");\n }\n\n // Hide the suggestions\n inputSuggestions.style.display = 'none';\n\n // Prevent the default action of reloading the page\n return false;\n });\n\n $(gotoPageInput).on('input focus', function ()\n {\n inputSuggestions.innerHTML = ''; // Remove all previous suggestions\n\n var value = gotoPageInput.value;\n var numSuggestions = 0;\n if (settings.enableGotoSuggestions && value)\n {\n var pages = settings.manifest.pages;\n for (var i = 0, len = pages.length; i < len && numSuggestions < 10; i++)\n {\n if (pages[i].l.toLowerCase().indexOf(value.toLowerCase()) > -1)\n {\n var newInputSuggestion = elt('div', {\n class: 'diva-input-suggestion'\n },\n pages[i].l\n );\n\n inputSuggestions.appendChild(newInputSuggestion);\n\n numSuggestions++;\n }\n }\n\n // Show label suggestions\n if (numSuggestions > 0)\n inputSuggestions.style.display = 'block';\n }\n else\n inputSuggestions.style.display = 'none';\n });\n\n $(gotoPageInput).on('keydown', function (e)\n {\n var el;\n if (e.keyCode === 13) // 'Enter' key\n {\n var active = $('.active', inputSuggestions);\n if (active.length)\n gotoPageInput.value = active.text();\n\n }\n if (e.keyCode === 38) // Up arrow key\n {\n el = $('.active', inputSuggestions);\n var prevEl = el.prev();\n if (prevEl.length)\n {\n el.removeClass('active');\n prevEl.addClass('active');\n }\n else\n {\n el.removeClass('active');\n $('.diva-input-suggestion:last', inputSuggestions).addClass('active');\n }\n }\n else if (e.keyCode === 40) // Down arrow key\n {\n el = $('.active', inputSuggestions);\n var nextEl = el.next();\n if (nextEl.length)\n {\n el.removeClass('active');\n nextEl.addClass('active');\n }\n else\n {\n el.removeClass('active');\n $('.diva-input-suggestion:first', inputSuggestions).addClass('active');\n }\n }\n });\n\n $(inputSuggestions).on('mousedown', '.diva-input-suggestion', function()\n {\n gotoPageInput.value = this.textContent;\n inputSuggestions.style.display = 'none';\n $(gotoPageInput).trigger('submit');\n });\n\n $(gotoPageInput).on('blur', function ()\n {\n // Hide label suggestions\n inputSuggestions.style.display = 'none';\n });\n\n return gotoForm;\n };\n\n var createPageLabel = function()\n {\n // Current page\n var currentPage = elt('span', {\n id: settings.ID + 'current-page'\n });\n\n var updateCurrentPage = function ()\n {\n currentPage.textContent = viewer.getCurrentAliasedPageIndex();\n };\n\n subscribe('VisiblePageDidChange', updateCurrentPage);\n subscribe('ViewerDidLoad', updateCurrentPage);\n\n // Number of pages\n var numPages = elt('span', {\n id: settings.ID + 'num-pages'\n });\n\n var updateNumPages = function ()\n {\n numPages.textContent = settings.numPages;\n };\n\n subscribe('NumberOfPagesDidChange', updateNumPages);\n subscribe('ObjectDidLoad', updateNumPages);\n\n return elt('span', {\n class: 'diva-page-label diva-label'\n },\n 'Page ', currentPage, ' of ', numPages\n );\n };\n\n var createToolbarButtonGroup = function ()\n {\n var buttons = [createViewMenu()];\n\n if (settings.enableLinkIcon)\n buttons.push(createLinkIcon());\n\n if (settings.enableNonPagedVisibilityIcon)\n buttons.push(createToggleNonPagedButton());\n\n if (settings.enableFullscreen)\n buttons.push(createFullscreenButton());\n\n return elt('span', elemAttrs('toolbar-button-group'), buttons);\n };\n\n var createLinkIcon = function ()\n {\n var elem = createButtonElement('link-icon', 'Link to this page');\n var linkIcon = $(elem);\n\n linkIcon.on('click', function ()\n {\n $('body').prepend(\n elt('div', {\n id: settings.ID + 'link-popup',\n class: 'diva-popup diva-link-popup'\n }, [\n elt('input', {\n id: settings.ID + 'link-popup-input',\n class: 'diva-input',\n type: 'text',\n value: viewer.getCurrentURL()\n })\n ])\n );\n\n if (settings.inFullscreen)\n {\n $(settings.selector + 'link-popup').addClass('in-fullscreen');\n }\n else\n {\n // Calculate the left and top offsets\n var leftOffset = linkIcon.offset().left - 222 + linkIcon.outerWidth();\n var topOffset = linkIcon.offset().top + linkIcon.outerHeight() - 1;\n\n $(settings.selector + 'link-popup').css({\n 'top': topOffset + 'px',\n 'left': leftOffset + 'px'\n });\n }\n\n // Catch onmouseup events outside of this div\n $('body').mouseup(function (event)\n {\n var targetID = event.target.id;\n\n if (targetID !== settings.ID + 'link-popup' && targetID !== settings.ID + 'link-popup-input')\n $(settings.selector + 'link-popup').remove();\n });\n\n // Also delete it upon scroll and page up/down key events\n // FIXME(wabain): This is aggressive\n settings.viewportObject.scroll(function ()\n {\n $(settings.selector + 'link-popup').remove();\n });\n $(settings.selector + 'link-popup input').click(function ()\n {\n $(this).focus().select();\n });\n\n return false;\n });\n\n return elem;\n };\n\n var createFullscreenButton = function ()\n {\n return createButtonElement('fullscreen-icon', 'Toggle fullscreen mode', function ()\n {\n viewer.toggleFullscreenMode();\n });\n };\n\n var createToggleNonPagedButton = function ()\n {\n var getClassName = function()\n {\n return 'toggle-nonpaged-icon' + (viewer.getSettings().showNonPagedPages ? '-active' : '');\n };\n\n var toggleNonPagedButton = createButtonElement(getClassName(), 'Toggle visibility of non-paged pages', function()\n {\n viewer.toggleNonPagedPagesVisibility();\n var newClassName = 'diva-' + getClassName();\n this.className = this.className.replace(/diva-toggle-nonpaged-icon(-active)?/, newClassName);\n });\n\n var updateNonPagedButtonVisibility = function ()\n {\n var pages = settings.manifest.pages;\n for (var i = 0; i < pages.length; i++)\n {\n if (settings.manifest.paged && !pages[i].paged)\n {\n // Show the button, there is at least one non-paged page\n toggleNonPagedButton.style.display = 'inline-block';\n return;\n }\n }\n\n // No non-paged pages were found, hide the button\n toggleNonPagedButton.style.display = 'none';\n };\n subscribe('ObjectDidLoad', updateNonPagedButtonVisibility);\n\n return toggleNonPagedButton;\n };\n\n // Handles all status updating etc (both fullscreen and not)\n var init = function ()\n {\n var leftTools = [createZoomControls(), createGridControls()];\n var rightTools = [createPageNavigationControls(), createToolbarButtonGroup()];\n\n var tools = elt('div', elemAttrs('tools'),\n elt('div', elemAttrs('tools-left'), leftTools),\n elt('div', elemAttrs('tools-right'), rightTools)\n );\n\n settings.toolbarParentObject.prepend(tools);\n\n // Handle entry to and exit from fullscreen mode\n var switchMode = function ()\n {\n var toolsRightElement = document.getElementById(settings.ID + 'tools-right');\n var pageNavElement = document.getElementById(settings.ID + 'page-nav');\n\n if (!settings.inFullscreen)\n {\n // Leaving fullscreen\n $(tools).removeClass('diva-fullscreen-tools');\n\n //move ID-page-nav to beginning of tools right\n toolsRightElement.removeChild(pageNavElement);\n toolsRightElement.insertBefore(pageNavElement, toolsRightElement.firstChild);\n }\n else\n {\n // Entering fullscreen\n $(tools).addClass('diva-fullscreen-tools');\n\n //move ID-page-nav to end of tools right\n toolsRightElement.removeChild(pageNavElement);\n toolsRightElement.appendChild(pageNavElement);\n }\n };\n\n subscribe('ModeDidSwitch', switchMode);\n subscribe('ViewerDidLoad', switchMode);\n\n var toolbar = {\n element: tools,\n closePopups: function ()\n {\n $('.diva-popup').css('display', 'none');\n }\n };\n\n return toolbar;\n };\n\n return init();\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/toolbar.js\n// module id = 14\n// module chunks = 0","var $ = require('jquery');\n\nrequire('./utils/jquery-extensions');\n\nvar elt = require('./utils/elt');\nvar getScrollbarWidth = require('./utils/get-scrollbar-width');\n\nvar gestureEvents = require('./gesture-events');\nvar diva = require('./diva-global');\nvar DocumentHandler = require('./document-handler');\nvar GridHandler = require('./grid-handler');\nvar PageOverlayManager = require('./page-overlay-manager');\nvar PluginRegistry = require('./plugin-registry');\nvar Renderer = require('./renderer');\nvar getPageLayouts = require('./page-layouts');\nvar createSettingsView = require('./settings-view');\nvar ValidationRunner = require('./validation-runner');\nvar Viewport = require('./viewport');\n\nvar debug = require('debug')('diva:ViewerCore');\n\nmodule.exports = ViewerCore;\n\n// Define validations\nvar optionsValidations = [\n {\n key: 'goDirectlyTo',\n validate: function (value, settings)\n {\n if (value < 0 || value >= settings.manifest.pages.length)\n return 0;\n }\n },\n {\n key: 'minPagesPerRow',\n validate: function (value)\n {\n return Math.max(2, value);\n }\n },\n {\n key: 'maxPagesPerRow',\n validate: function (value, settings)\n {\n return Math.max(value, settings.minPagesPerRow);\n }\n },\n {\n key: 'pagesPerRow',\n validate: function (value, settings)\n {\n // Default to the maximum\n if (value < settings.minPagesPerRow || value > settings.maxPagesPerRow)\n return settings.maxPagesPerRow;\n }\n },\n {\n key: 'maxZoomLevel',\n validate: function (value, settings, config)\n {\n // Changing this value isn't really an error, it just depends on the\n // source manifest\n config.suppressWarning();\n\n if (value < 0 || value > settings.manifest.maxZoom)\n return settings.manifest.maxZoom;\n }\n },\n {\n key: 'minZoomLevel',\n validate: function (value, settings, config)\n {\n // Changes based on the manifest value shouldn't trigger a\n // warning\n if (value > settings.manifest.maxZoom)\n {\n config.suppressWarning();\n return 0;\n }\n\n if (value < 0 || value > settings.maxZoomLevel)\n return 0;\n }\n },\n {\n key: 'zoomLevel',\n validate: function (value, settings, config)\n {\n if (value > settings.manifest.maxZoom)\n {\n config.suppressWarning();\n return 0;\n }\n\n if (value < settings.minZoomLevel || value > settings.maxZoomLevel)\n return settings.minZoomLevel;\n }\n }\n];\n\nfunction ViewerCore(element, options, publicInstance)\n{\n var self = this;\n var parentObject = $(element);\n\n // Things that cannot be changed because of the way they are used by the script\n // Many of these are declared with arbitrary values that are changed later on\n var viewerState = {\n currentPageIndex: 0, // The current page in the viewport (center-most page)\n horizontalOffset: 0, // Distance from the center of the diva element to the top of the current page\n horizontalPadding: 0, // Either the fixed padding or adaptive padding\n ID: null, // The prefix of the IDs of the elements (usually 1-diva-)\n initialKeyScroll: false, // Holds the initial state of enableKeyScroll\n initialSpaceScroll: false, // Holds the initial state of enableSpaceScroll\n innerElement: null, // The native .diva-outer DOM object\n innerObject: {}, // $(settings.ID + 'inner'), for selecting the .diva-inner element\n isActiveDiva: true, // In the case that multiple diva panes exist on the same page, this should have events funneled to it.\n isScrollable: true, // Used in enable/disableScrollable public methods\n isZooming: false, // Flag to keep track of whether zooming is still in progress, for handleZoom\n loaded: false, // A flag for when everything is loaded and ready to go.\n manifest: null,\n mobileWebkit: false, // Checks if the user is on a touch device (iPad/iPod/iPhone/Android)\n numPages: 0, // Number of pages in the array\n oldZoomLevel: -1, // Holds the previous zoom level after zooming in or out\n options: options,\n outerElement: null, // The native .diva-outer DOM object\n outerObject: {}, // $(settings.ID + 'outer'), for selecting the .diva-outer element\n pageOverlays: new PageOverlayManager(),\n pageTools: [], // The plugins which are enabled as page tools\n parentObject: parentObject, // JQuery object referencing the parent element\n pendingManifestRequest: null, // Reference to the xhr request retrieving the manifest. Used to cancel the request on destroy()\n plugins: [], // Filled with the enabled plugins from the registry\n renderer: null,\n resizeTimer: -1, // Holds the ID of the timeout used when resizing the window (for clearing)\n scrollbarWidth: 0, // Set to the actual scrollbar width in init()\n selector: '', // Uses the generated ID prefix to easily select elements\n throbberTimeoutID: -1, // Holds the ID of the throbber loading timeout\n toolbar: null, // Holds an object with some toolbar-related functions\n verticalOffset: 0, // Distance from the center of the diva element to the left side of the current page\n verticalPadding: 0, // Either the fixed padding or adaptive padding\n viewHandler: null,\n viewport: null, // Object caching the viewport dimensions\n viewportElement: null,\n viewportObject: null\n };\n\n var settings = createSettingsView([options, viewerState]);\n\n // Aliases for compatibilty\n Object.defineProperties(settings, {\n // Height of the document viewer pane\n panelHeight: {\n get: function ()\n {\n return viewerState.viewport.height;\n }\n },\n // Width of the document viewer pane\n panelWidth: {\n get: function ()\n {\n return viewerState.viewport.width;\n }\n }\n });\n\n var optionsValidator = new ValidationRunner({\n additionalProperties: [\n {\n key: 'manifest',\n get: function ()\n {\n return viewerState.manifest;\n }\n }\n ],\n\n validations: optionsValidations\n });\n\n var isValidOption = function (key, value)\n {\n return optionsValidator.isValid(key, value, viewerState.options);\n };\n\n var elemAttrs = function (ident, base)\n {\n var attrs = {\n id: settings.ID + ident,\n class: 'diva-' + ident\n };\n\n if (base)\n return $.extend(attrs, base);\n else\n return attrs;\n };\n\n var getPageData = function (pageIndex, attribute)\n {\n return settings.manifest.pages[pageIndex].d[settings.zoomLevel][attribute];\n };\n\n // Reset some settings and empty the viewport\n var clearViewer = function ()\n {\n viewerState.viewport.top = 0;\n\n // Clear all the timeouts to prevent undesired pages from loading\n clearTimeout(viewerState.resizeTimer);\n };\n\n /**\n * Update settings to match the specified options. Load the viewer,\n * fire appropriate events for changed options.\n */\n var reloadViewer = function (newOptions)\n {\n var queuedEvents = [];\n\n newOptions = optionsValidator.getValidatedOptions(settings, newOptions);\n\n // Set the zoom level if valid and fire a ZoomLevelDidChange event\n if (hasChangedOption(newOptions, 'zoomLevel'))\n {\n viewerState.oldZoomLevel = settings.zoomLevel;\n viewerState.options.zoomLevel = newOptions.zoomLevel;\n queuedEvents.push([\"ZoomLevelDidChange\", newOptions.zoomLevel]);\n }\n\n // Set the pages per row if valid and fire an event\n if (hasChangedOption(newOptions, 'pagesPerRow'))\n {\n viewerState.options.pagesPerRow = newOptions.pagesPerRow;\n queuedEvents.push([\"GridRowNumberDidChange\", newOptions.pagesPerRow]);\n }\n\n // Update verticallyOriented (no event fired)\n if (hasChangedOption(newOptions, 'verticallyOriented'))\n viewerState.options.verticallyOriented = newOptions.verticallyOriented;\n\n // Show/Hide non-paged pages\n if (hasChangedOption(newOptions, 'showNonPagedPages'))\n {\n viewerState.options.showNonPagedPages = newOptions.showNonPagedPages;\n }\n\n // Update page position (no event fired here)\n if ('goDirectlyTo' in newOptions)\n {\n viewerState.options.goDirectlyTo = newOptions.goDirectlyTo;\n\n if ('verticalOffset' in newOptions)\n viewerState.verticalOffset = newOptions.verticalOffset;\n\n if ('horizontalOffset' in newOptions)\n viewerState.horizontalOffset = newOptions.horizontalOffset;\n }\n else\n {\n // Otherwise the default is to remain on the current page\n viewerState.options.goDirectlyTo = settings.currentPageIndex;\n }\n\n if (hasChangedOption(newOptions, 'inGrid') || hasChangedOption(newOptions, 'inBookLayout'))\n {\n if ('inGrid' in newOptions)\n viewerState.options.inGrid = newOptions.inGrid;\n\n if ('inBookLayout' in newOptions)\n viewerState.options.inBookLayout = newOptions.inBookLayout;\n\n queuedEvents.push([\"ViewDidSwitch\", settings.inGrid]);\n }\n\n // Note: prepareModeChange() depends on inGrid and the vertical/horizontalOffset (for now)\n if (hasChangedOption(newOptions, 'inFullscreen'))\n {\n viewerState.options.inFullscreen = newOptions.inFullscreen;\n prepareModeChange(newOptions);\n queuedEvents.push([\"ModeDidSwitch\", settings.inFullscreen]);\n }\n\n clearViewer();\n updateViewHandlerAndRendering();\n\n if (viewerState.renderer)\n {\n // TODO: The usage of padding variables is still really\n // messy and inconsistent\n var rendererConfig = {\n pageLayouts: getPageLayouts(settings),\n padding: getPadding(),\n maxZoomLevel: settings.inGrid ? null : viewerState.manifest.maxZoom,\n verticallyOriented: settings.verticallyOriented || settings.inGrid,\n };\n\n var viewportPosition = {\n zoomLevel: settings.inGrid ? null : settings.zoomLevel,\n anchorPage: settings.goDirectlyTo,\n verticalOffset: viewerState.verticalOffset,\n horizontalOffset: viewerState.horizontalOffset\n };\n\n var sourceProvider = getCurrentSourceProvider();\n\n if (debug.enabled)\n {\n var serialized = Object.keys(rendererConfig)\n .filter(function (key)\n {\n // Too long\n return key !== 'pageLayouts' && key !== 'padding';\n })\n .map(function (key)\n {\n var value = rendererConfig[key];\n return key + ': ' + JSON.stringify(value);\n })\n .join(', ');\n\n debug('reload with %s', serialized);\n }\n\n viewerState.renderer.load(rendererConfig, viewportPosition, sourceProvider);\n }\n\n queuedEvents.forEach(function (params)\n {\n publish.apply(null, params);\n });\n\n return true;\n };\n\n var hasChangedOption = function (options, key)\n {\n return key in options && options[key] !== settings[key];\n };\n\n // Handles switching in and out of fullscreen mode\n var prepareModeChange = function (options)\n {\n // Toggle the classes\n var changeClass = options.inFullscreen ? 'addClass' : 'removeClass';\n viewerState.outerObject[changeClass]('diva-fullscreen');\n $('body')[changeClass]('diva-hide-scrollbar');\n settings.parentObject[changeClass]('diva-full-width');\n\n // Adjust Diva's internal panel size, keeping the old values\n var storedHeight = settings.panelHeight;\n var storedWidth = settings.panelWidth;\n viewerState.viewport.invalidate();\n\n // If this isn't the original load, the offsets matter, and the position isn't being changed...\n if (!viewerState.loaded && !settings.inGrid && !('verticalOffset' in options))\n {\n //get the updated panel size\n var newHeight = settings.panelHeight;\n var newWidth = settings.panelWidth;\n\n //and re-center the new panel on the same point\n viewerState.verticalOffset += ((storedHeight - newHeight) / 2);\n viewerState.horizontalOffset += ((storedWidth - newWidth) / 2);\n }\n\n //turn on/off escape key listener\n if (options.inFullscreen)\n $(document).on('keyup', escapeListener);\n else\n $(document).off('keyup', escapeListener);\n };\n\n // Update the view handler and the view rendering for the current view\n var updateViewHandlerAndRendering = function ()\n {\n var Handler = settings.inGrid ? GridHandler : DocumentHandler;\n\n if (viewerState.viewHandler && !(viewerState.viewHandler instanceof Handler))\n {\n viewerState.viewHandler.destroy();\n viewerState.viewHandler = null;\n }\n\n if (!viewerState.viewHandler)\n viewerState.viewHandler = new Handler(self);\n\n if (!viewerState.renderer)\n initializeRenderer();\n };\n\n // TODO: This could probably be done upon ViewerCore initialization\n var initializeRenderer = function ()\n {\n var compatErrors = Renderer.getCompatibilityErrors();\n\n if (compatErrors)\n {\n showError(compatErrors);\n }\n else\n {\n var options = {\n viewport: viewerState.viewport,\n outerElement: viewerState.outerElement,\n innerElement: viewerState.innerElement\n };\n\n var hooks = {\n onViewWillLoad: function ()\n {\n viewerState.viewHandler.onViewWillLoad();\n },\n onViewDidLoad: function ()\n {\n updatePageOverlays();\n viewerState.viewHandler.onViewDidLoad();\n },\n onViewDidUpdate: function (pages, targetPage)\n {\n updatePageOverlays();\n viewerState.viewHandler.onViewDidUpdate(pages, targetPage);\n },\n onViewDidTransition: function ()\n {\n updatePageOverlays();\n },\n onPageWillLoad: function (pageIndex)\n {\n publish('PageWillLoad', pageIndex);\n }\n };\n\n viewerState.renderer = new Renderer(options, hooks);\n }\n };\n\n var getCurrentSourceProvider = function ()\n {\n if (settings.inGrid)\n {\n var gridSourceProvider = {\n getAllZoomLevelsForPage: function (page)\n {\n return [gridSourceProvider.getBestZoomLevelForPage(page)];\n },\n getBestZoomLevelForPage: function (page)\n {\n var url = settings.manifest.getPageImageURL(page.index, {\n width: page.dimensions.width\n });\n\n return {\n zoomLevel: 1, // FIXME\n rows: 1,\n cols: 1,\n tiles: [{\n url: url,\n zoomLevel: 1, // FIXME\n row: 0,\n col: 0,\n dimensions: page.dimensions,\n offset: {\n top: 0,\n left: 0\n }\n }]\n };\n }\n };\n\n return gridSourceProvider;\n }\n\n var tileDimens = {\n width: settings.tileWidth,\n height: settings.tileHeight\n };\n\n return {\n getBestZoomLevelForPage: function (page)\n {\n return settings.manifest.getPageImageTiles(page.index, Math.ceil(settings.zoomLevel), tileDimens);\n },\n getAllZoomLevelsForPage: function (page)\n {\n var levels = [];\n\n var levelCount = viewerState.manifest.maxZoom;\n for (var level=0; level <= levelCount; level++)\n {\n levels.push(settings.manifest.getPageImageTiles(page.index, level, tileDimens));\n }\n\n levels.reverse();\n\n return levels;\n }\n };\n };\n\n var getPadding = function ()\n {\n var topPadding, leftPadding;\n var docVPadding, docHPadding;\n\n if (settings.inGrid)\n {\n docVPadding = settings.fixedPadding;\n topPadding = leftPadding = docHPadding = 0;\n }\n else\n {\n topPadding = settings.verticallyOriented ? viewerState.verticalPadding : 0;\n leftPadding = settings.verticallyOriented ? 0 : viewerState.horizontalPadding;\n\n docVPadding = settings.verticallyOriented ? 0 : viewerState.verticalPadding;\n docHPadding = settings.verticallyOriented ? viewerState.horizontalPadding : 0;\n }\n\n return {\n document: {\n top: docVPadding,\n bottom: docVPadding,\n left: docHPadding,\n right: docHPadding\n },\n page: {\n top: topPadding,\n bottom: 0,\n left: leftPadding,\n right: 0\n }\n };\n };\n\n var updatePageOverlays = function ()\n {\n viewerState.pageOverlays.updateOverlays(viewerState.renderer.getRenderedPages());\n };\n\n //Shortcut for closing fullscreen with the escape key\n var escapeListener = function (e)\n {\n if (e.keyCode == 27)\n {\n reloadViewer({\n inFullscreen: !settings.inFullscreen\n });\n }\n };\n\n // Called to handle any zoom level\n var handleZoom = function (newZoomLevel, focalPoint)\n {\n // If the zoom level provided is invalid, return false\n if (!isValidOption('zoomLevel', newZoomLevel))\n return false;\n\n // If no focal point was given, zoom on the center of the viewport\n if (focalPoint == null)\n {\n var viewport = viewerState.viewport;\n var currentRegion = viewerState.renderer.layout.getPageRegion(settings.currentPageIndex);\n\n focalPoint = {\n anchorPage: settings.currentPageIndex,\n offset: {\n left: (viewport.width / 2) - (currentRegion.left - viewport.left),\n top: (viewport.height / 2) - (currentRegion.top - viewport.top)\n }\n };\n }\n\n var pageRegion = viewerState.renderer.layout.getPageRegion(focalPoint.anchorPage);\n\n // calculate distance from cursor coordinates to center of viewport\n var focalXToCenter = (pageRegion.left + focalPoint.offset.left) -\n (settings.viewport.left + (settings.viewport.width / 2));\n var focalYToCenter = (pageRegion.top + focalPoint.offset.top) -\n (settings.viewport.top + (settings.viewport.height / 2));\n\n function getPositionForZoomLevel(zoomLevel)\n {\n var zoomRatio = Math.pow(2, zoomLevel - initialZoomLevel);\n\n //TODO(jeromepl): Calculate position from page top left to viewport top left\n // calculate horizontal/verticalOffset: distance from viewport center to page upper left corner\n var horizontalOffset = (focalPoint.offset.left * zoomRatio) - focalXToCenter;\n var verticalOffset = (focalPoint.offset.top * zoomRatio) - focalYToCenter;\n\n return {\n zoomLevel: zoomLevel,\n anchorPage: focalPoint.anchorPage,\n verticalOffset: verticalOffset,\n horizontalOffset: horizontalOffset\n };\n }\n\n var initialZoomLevel = viewerState.oldZoomLevel = settings.zoomLevel;\n viewerState.options.zoomLevel = newZoomLevel;\n\n var endPosition = getPositionForZoomLevel(newZoomLevel);\n viewerState.options.goDirectlyTo = endPosition.anchorPage;\n viewerState.verticalOffset = endPosition.verticalOffset;\n viewerState.horizontalOffset = endPosition.horizontalOffset;\n\n viewerState.renderer.transitionViewportPosition({\n duration: 300,\n parameters: {\n zoomLevel: {\n from: initialZoomLevel,\n to: newZoomLevel\n }\n },\n getPosition: function (parameters)\n {\n return getPositionForZoomLevel(parameters.zoomLevel);\n },\n onEnd: function (info)\n {\n viewerState.viewportObject.scroll(scrollFunction);\n\n if (info.interrupted)\n viewerState.oldZoomLevel = newZoomLevel;\n }\n });\n\n // Update the slider\n publish(\"ZoomLevelDidChange\", newZoomLevel);\n\n // While zooming, don't update scroll offsets based on the scaled version of diva-inner\n viewerState.viewportObject.off('scroll');\n\n return true;\n };\n\n /*\n Gets the Y-offset for a specific point on a specific page\n Acceptable values for \"anchor\":\n \"top\" (default) - will anchor top of the page to the top of the diva-outer element\n \"bottom\" - top, s/top/bottom\n \"center\" - will center the page on the diva element\n Returned value will be the distance from the center of the diva-outer element to the top of the current page for the specified anchor\n */\n var getYOffset = function (pageIndex, anchor)\n {\n pageIndex = (typeof(pageIndex) === \"undefined\" ? settings.currentPageIndex : pageIndex);\n\n if (anchor === \"center\" || anchor === \"centre\") //how you can tell an American coded this\n {\n return parseInt(getPageData(pageIndex, \"h\") / 2, 10);\n }\n else if (anchor === \"bottom\")\n {\n return parseInt(getPageData(pageIndex, \"h\") - settings.panelHeight / 2, 10);\n }\n else\n {\n return parseInt(settings.panelHeight / 2, 10);\n }\n };\n\n //Same as getYOffset with \"left\" and \"right\" as acceptable values instead of \"top\" and \"bottom\"\n var getXOffset = function (pageIndex, anchor)\n {\n pageIndex = (typeof(pageIndex) === \"undefined\" ? settings.currentPageIndex : pageIndex);\n\n if (anchor === \"left\")\n {\n return parseInt(settings.panelWidth / 2, 10);\n }\n else if (anchor === \"right\")\n {\n return parseInt(getPageData(pageIndex, \"w\") - settings.panelWidth / 2, 10);\n }\n else\n {\n return parseInt(getPageData(pageIndex, \"w\") / 2, 10);\n }\n };\n\n // updates panelHeight/panelWidth on resize\n var updatePanelSize = function ()\n {\n viewerState.viewport.invalidate();\n\n // FIXME(wabain): This should really only be called after initial load\n if (viewerState.renderer)\n {\n updateOffsets();\n viewerState.renderer.goto(settings.currentPageIndex, viewerState.verticalOffset, viewerState.horizontalOffset);\n }\n\n return true;\n };\n\n var updateOffsets = function ()\n {\n var pageOffset = viewerState.renderer.layout.getPageToViewportCenterOffset(settings.currentPageIndex, viewerState.viewport);\n\n if (pageOffset)\n {\n viewerState.horizontalOffset = pageOffset.x;\n viewerState.verticalOffset = pageOffset.y;\n }\n };\n\n // Bind mouse events (drag to scroll, double-click)\n var bindMouseEvents = function()\n {\n // Set drag scroll on first descendant of class dragger on both selected elements\n viewerState.viewportObject.dragscrollable({dragSelector: '.diva-dragger', acceptPropagatedEvent: true});\n viewerState.innerObject.dragscrollable({dragSelector: '.diva-dragger', acceptPropagatedEvent: true});\n\n gestureEvents.onDoubleClick(viewerState.viewportObject, function (event, coords)\n {\n debug('Double click at %s, %s', coords.left, coords.top);\n viewerState.viewHandler.onDoubleClick(event, coords);\n });\n };\n\n var onResize = function()\n {\n updatePanelSize();\n // Cancel any previously-set resize timeouts\n clearTimeout(viewerState.resizeTimer);\n\n viewerState.resizeTimer = setTimeout(function ()\n {\n var pageOffset = viewerState.renderer.layout.getPageToViewportCenterOffset(settings.currentPageIndex, viewerState.viewport);\n\n if (pageOffset)\n {\n reloadViewer({\n goDirectlyTo: settings.currentPageIndex,\n verticalOffset: pageOffset.y,\n horizontalOffset: pageOffset.x\n });\n }\n else\n {\n reloadViewer({\n goDirectlyTo: settings.currentPageIndex\n });\n }\n }, 200);\n };\n\n // Bind touch and orientation change events\n var bindTouchEvents = function()\n {\n // Block the user from moving the window only if it's not integrated\n if (settings.blockMobileMove)\n {\n $('body').bind('touchmove', function (event)\n {\n var e = event.originalEvent;\n e.preventDefault();\n\n return false;\n });\n }\n\n // Touch events for swiping in the viewport to scroll pages\n viewerState.viewportObject.kinetic({\n triggerHardware: true\n });\n\n gestureEvents.onPinch(viewerState.viewportObject, function (event, coords, start, end)\n {\n debug('Pinch %s at %s, %s', end - start, coords.left, coords.top);\n viewerState.viewHandler.onPinch(event, coords, start, end);\n });\n\n gestureEvents.onDoubleTap(viewerState.viewportObject, function (event, coords)\n {\n debug('Double tap at %s, %s', coords.left, coords.top);\n viewerState.viewHandler.onDoubleClick(event, coords);\n });\n };\n\n // Handle the scroll\n var scrollFunction = function ()\n {\n var previousTopScroll = viewerState.viewport.top;\n var previousLeftScroll = viewerState.viewport.left;\n\n var direction;\n\n viewerState.viewport.invalidate();\n\n var newScrollTop = viewerState.viewport.top;\n var newScrollLeft = viewerState.viewport.left;\n\n if (settings.verticallyOriented || settings.inGrid)\n direction = newScrollTop - previousTopScroll;\n else\n direction = newScrollLeft - previousLeftScroll;\n\n //give adjust the direction we care about\n viewerState.renderer.adjust(direction);\n\n var primaryScroll = (settings.verticallyOriented || settings.inGrid) ? newScrollTop : newScrollLeft;\n\n publish(\"ViewerDidScroll\", primaryScroll);\n\n if (direction > 0)\n {\n publish(\"ViewerDidScrollDown\", primaryScroll);\n }\n else if (direction < 0)\n {\n publish(\"ViewerDidScrollUp\", primaryScroll);\n }\n\n updateOffsets();\n };\n\n // Binds most of the event handlers (some more in createToolbar)\n var handleEvents = function ()\n {\n // Change the cursor for dragging\n viewerState.innerObject.mousedown(function ()\n {\n viewerState.innerObject.addClass('diva-grabbing');\n });\n\n viewerState.innerObject.mouseup(function ()\n {\n viewerState.innerObject.removeClass('diva-grabbing');\n });\n\n bindMouseEvents();\n\n viewerState.viewportObject.scroll(scrollFunction);\n\n var upArrowKey = 38,\n downArrowKey = 40,\n leftArrowKey = 37,\n rightArrowKey = 39,\n spaceKey = 32,\n pageUpKey = 33,\n pageDownKey = 34,\n homeKey = 36,\n endKey = 35;\n\n // Catch the key presses in document\n $(document).on('keydown.diva', function (event)\n {\n if (!viewerState.isActiveDiva)\n return true;\n\n // Space or page down - go to the next page\n if ((settings.enableSpaceScroll && !event.shiftKey && event.keyCode === spaceKey) || (settings.enableKeyScroll && event.keyCode === pageDownKey))\n {\n viewerState.viewport.top += settings.panelHeight;\n return false;\n }\n else if (!settings.enableSpaceScroll && event.keyCode === spaceKey)\n {\n event.preventDefault();\n }\n\n if (settings.enableKeyScroll)\n {\n // Don't steal keyboard shortcuts (metaKey = command [OS X], super [Win/Linux])\n if (event.shiftKey || event.ctrlKey || event.metaKey)\n return true;\n\n switch (event.keyCode)\n {\n case pageUpKey:\n // Page up - go to the previous page\n viewerState.viewport.top -= settings.panelHeight;\n return false;\n\n case upArrowKey:\n // Up arrow - scroll up\n viewerState.viewport.top -= settings.arrowScrollAmount;\n return false;\n\n case downArrowKey:\n // Down arrow - scroll down\n viewerState.viewport.top += settings.arrowScrollAmount;\n return false;\n\n case leftArrowKey:\n // Left arrow - scroll left\n viewerState.viewport.left -= settings.arrowScrollAmount;\n return false;\n\n case rightArrowKey:\n // Right arrow - scroll right\n viewerState.viewport.left += settings.arrowScrollAmount;\n return false;\n\n case homeKey:\n // Home key - go to the beginning of the document\n viewerState.viewport.top = 0;\n return false;\n\n case endKey:\n // End key - go to the end of the document\n // Count on the viewport coordinate value being normalized\n if (settings.verticallyOriented)\n viewerState.viewport.top = Infinity;\n else\n viewerState.viewport.left = Infinity;\n\n return false;\n\n default:\n return true;\n }\n }\n return true;\n });\n\n diva.Events.subscribe('ViewerDidTerminate', function()\n {\n $(document).off('keydown.diva');\n }, settings.ID);\n\n bindTouchEvents();\n\n // Handle window resizing events\n window.addEventListener('resize', onResize, false);\n\n diva.Events.subscribe('ViewerDidTerminate', function()\n {\n window.removeEventListener('resize', onResize, false);\n }, settings.ID);\n\n // Handle orientation change separately\n if ('onorientationchange' in window)\n {\n window.addEventListener('orientationchange', onResize, false);\n\n diva.Events.subscribe('ViewerDidTerminate', function()\n {\n window.removeEventListener('orientationchange', onResize, false);\n }, settings.ID);\n }\n\n diva.Events.subscribe('PanelSizeDidChange', updatePanelSize, settings.ID);\n\n // Clear page and resize timeouts when the viewer is destroyed\n diva.Events.subscribe('ViewerDidTerminate', function ()\n {\n if (viewerState.renderer)\n viewerState.renderer.destroy();\n\n clearTimeout(viewerState.resizeTimer);\n }, settings.ID);\n };\n\n var initPlugins = function ()\n {\n // Add all the plugins that have not been explicitly disabled to\n // settings.plugins\n PluginRegistry.getAll().forEach(function (plugin)\n {\n var pluginProperName = plugin.pluginName[0].toUpperCase() + plugin.pluginName.substring(1);\n\n if (settings['enable' + pluginProperName])\n {\n // Call the init function and check return value\n var enablePlugin = plugin.init(settings, publicInstance);\n\n // If int returns false, consider the plugin disabled\n if (!enablePlugin)\n return;\n\n // Create the pageTools bar if handleClick is set to a function\n if (typeof plugin.handleClick === 'function')\n {\n viewerState.pageTools.push(plugin);\n }\n\n // Add it to settings.plugins so it can be used later\n settings.plugins.push(plugin);\n }\n });\n };\n\n var showThrobber = function ()\n {\n hideThrobber();\n\n viewerState.throbberTimeoutID = setTimeout(function ()\n {\n $(settings.selector + 'throbber').show();\n }, settings.throbberTimeout);\n };\n\n var hideThrobber = function ()\n {\n // Clear the timeout, if it hasn't executed yet\n clearTimeout(viewerState.throbberTimeoutID);\n\n // Hide the throbber if it has already executed\n $(settings.selector + 'throbber').hide();\n };\n\n var showError = function(message)\n {\n var errorElement = elt('div', elemAttrs('error'), [\n elt('button', elemAttrs('error-close', {'aria-label': 'Close dialog'})),\n elt('p',\n elt('strong', 'Error')\n ),\n elt('div', message)\n ]);\n\n viewerState.outerObject.append(errorElement);\n\n //bind dialog close button\n $(settings.selector + 'error-close').on('click', function()\n {\n errorElement.parentNode.removeChild(errorElement);\n });\n };\n\n var setManifest = function (manifest, loadOptions)\n {\n viewerState.manifest = manifest;\n\n hideThrobber();\n\n // Convenience value\n viewerState.numPages = settings.manifest.pages.length;\n\n optionsValidator.validate(viewerState.options);\n\n publish('NumberOfPagesDidChange', settings.numPages);\n\n if (settings.enableAutoTitle)\n {\n if ($(settings.selector + 'title').length)\n $(settings.selector + 'title').html(settings.manifest.itemTitle);\n else\n settings.parentObject.prepend(elt('div', elemAttrs('title'), [settings.manifest.itemTitle]));\n }\n\n // Calculate the horizontal and vertical inter-page padding based on the dimensions of the average zoom level\n if (settings.adaptivePadding > 0)\n {\n var z = Math.floor((settings.minZoomLevel + settings.maxZoomLevel) / 2);\n viewerState.horizontalPadding = parseInt(settings.manifest.getAverageWidth(z) * settings.adaptivePadding, 10);\n viewerState.verticalPadding = parseInt(settings.manifest.getAverageHeight(z) * settings.adaptivePadding, 10);\n }\n else\n {\n // It's less than or equal to 0; use fixedPadding instead\n viewerState.horizontalPadding = settings.fixedPadding;\n viewerState.verticalPadding = settings.fixedPadding;\n }\n\n // Make sure the vertical padding is at least 40, if plugin icons are enabled\n if (viewerState.pageTools.length)\n {\n viewerState.verticalPadding = Math.max(40, viewerState.verticalPadding);\n }\n\n // If we detect a viewingHint of 'paged' in the manifest or sequence, enable book view by default\n if (settings.manifest.paged)\n {\n viewerState.options.inBookLayout = true;\n }\n\n // Plugin setup hooks should be bound to the ObjectDidLoad event\n publish('ObjectDidLoad', settings);\n\n // Adjust the document panel dimensions\n updatePanelSize();\n\n var needsXCoord, needsYCoord;\n\n var anchoredVertically = false;\n var anchoredHorizontally = false;\n\n // NB: `==` here will check both null and undefined\n if (loadOptions.goDirectlyTo == null)\n {\n loadOptions.goDirectlyTo = settings.goDirectlyTo;\n needsXCoord = needsYCoord = true;\n }\n else\n {\n needsXCoord = loadOptions.horizontalOffset == null || isNaN(loadOptions.horizontalOffset);\n needsYCoord = loadOptions.verticalOffset == null || isNaN(loadOptions.verticalOffset);\n }\n\n // Set default values for the horizontal and vertical offsets\n if (needsXCoord)\n {\n // FIXME: What if inBookLayout/verticallyOriented is changed by loadOptions?\n if (loadOptions.goDirectlyTo === 0 && settings.inBookLayout && settings.verticallyOriented)\n {\n // if in book layout, center the first opening by default\n loadOptions.horizontalOffset = viewerState.horizontalPadding;\n }\n else\n {\n anchoredHorizontally = true;\n loadOptions.horizontalOffset = getXOffset(loadOptions.goDirectlyTo, \"center\");\n }\n }\n\n if (needsYCoord)\n {\n anchoredVertically = true;\n loadOptions.verticalOffset = getYOffset(loadOptions.goDirectlyTo, \"top\");\n }\n\n reloadViewer(loadOptions);\n\n //prep dimensions one last time now that pages have loaded\n updatePanelSize();\n\n // FIXME: This is a hack to ensure that the outerElement scrollbars are taken into account\n if (settings.verticallyOriented)\n viewerState.innerElement.style.minWidth = settings.panelWidth + 'px';\n else\n viewerState.innerElement.style.minHeight = settings.panelHeight + 'px';\n\n // FIXME: If the page was supposed to be positioned relative to the viewport we need to\n // recalculate it to take into account the scrollbars\n if (anchoredVertically || anchoredHorizontally)\n {\n if (anchoredVertically)\n viewerState.verticalOffset = getYOffset(settings.currentPageIndex, \"top\");\n\n if (anchoredHorizontally)\n viewerState.horizontalOffset = getXOffset(settings.currentPageIndex, \"center\");\n\n viewerState.renderer.goto(settings.currentPageIndex, viewerState.verticalOffset, viewerState.horizontalOffset);\n }\n\n // signal that everything should be set up and ready to go.\n viewerState.loaded = true;\n\n publish(\"ViewerDidLoad\", settings);\n };\n\n var publish = function (event)\n {\n var args = Array.prototype.slice.call(arguments, 1);\n diva.Events.publish(event, args, publicInstance);\n };\n\n var init = function ()\n {\n // First figure out the width of the scrollbar in this browser\n // TODO(wabain): Cache this somewhere else\n // Only some of the plugins rely on this now\n viewerState.scrollbarWidth = getScrollbarWidth();\n\n // If window.orientation is defined, then it's probably mobileWebkit\n viewerState.mobileWebkit = window.orientation !== undefined;\n\n // Generate an ID that can be used as a prefix for all the other IDs\n var idNumber = generateId();\n viewerState.ID = 'diva-' + idNumber + '-';\n viewerState.selector = '#' + settings.ID;\n\n if (options.hashParamSuffix === null)\n {\n // Omit the suffix from the first instance\n if (idNumber === 1)\n options.hashParamSuffix = '';\n else\n options.hashParamSuffix = idNumber + '';\n }\n\n // Create the inner and outer panels\n var innerElem = elt('div', elemAttrs('inner', { class: 'diva-inner diva-dragger' }));\n var viewportElem = elt('div', elemAttrs('viewport'), innerElem);\n var outerElem = elt('div', elemAttrs('outer'),\n viewportElem,\n elt('div', elemAttrs('throbber')));\n\n viewerState.innerElement = innerElem;\n viewerState.viewportElement = viewportElem;\n viewerState.outerElement = outerElem;\n\n viewerState.innerObject = $(innerElem);\n viewerState.viewportObject = $(viewportElem);\n viewerState.outerObject = $(outerElem);\n\n settings.parentObject.append(outerElem);\n\n viewerState.viewport = new Viewport(viewerState.viewportElement, {\n intersectionTolerance: settings.viewportMargin\n });\n\n // Do all the plugin initialisation\n initPlugins();\n\n handleEvents();\n\n // Show the throbber while waiting for the manifest to load\n showThrobber();\n };\n\n this.getSettings = function ()\n {\n return settings;\n };\n\n // Temporary accessor for the state of the viewer core\n // TODO: Replace this with a more restricted view of whatever needs\n // be exposed through settings for backwards compat\n this.getInternalState = function ()\n {\n return viewerState;\n };\n\n this.getPublicInstance = function ()\n {\n return publicInstance;\n };\n\n this.getPageTools = function ()\n {\n return viewerState.pageTools;\n };\n\n this.getCurrentLayout = function ()\n {\n return viewerState.renderer ? viewerState.renderer.layout : null;\n };\n\n /** Get a copy of the current viewport dimensions */\n this.getViewport = function ()\n {\n var viewport = viewerState.viewport;\n\n return {\n top: viewport.top,\n left: viewport.left,\n bottom: viewport.bottom,\n right: viewport.right,\n\n width: viewport.width,\n height: viewport.height\n };\n };\n\n this.addPageOverlay = function (overlay)\n {\n viewerState.pageOverlays.addOverlay(overlay);\n };\n\n this.removePageOverlay = function (overlay)\n {\n viewerState.pageOverlays.removeOverlay(overlay);\n };\n\n this.getPageRegion = function (pageIndex, options)\n {\n var layout = viewerState.renderer.layout;\n var region = layout.getPageRegion(pageIndex, options);\n\n if (options && options.incorporateViewport)\n {\n var secondaryDim = settings.verticallyOriented ? 'width' : 'height';\n\n if (viewerState.viewport[secondaryDim] > layout.dimensions[secondaryDim])\n {\n var docOffset = (viewerState.viewport[secondaryDim] - layout.dimensions[secondaryDim]) / 2;\n\n if (settings.verticallyOriented)\n {\n return {\n top: region.top,\n bottom: region.bottom,\n\n left: region.left + docOffset,\n right: region.right + docOffset\n };\n }\n else\n {\n return {\n top: region.top + docOffset,\n bottom: region.bottom + docOffset,\n\n left: region.left,\n right: region.right\n };\n }\n }\n }\n\n return region;\n };\n\n this.getPagePositionAtViewportOffset = function (coords)\n {\n var docCoords = {\n left: coords.left + viewerState.viewport.left,\n top: coords.top + viewerState.viewport.top\n };\n\n var renderedPages = viewerState.renderer.getRenderedPages();\n var pageCount = renderedPages.length;\n\n // Find the page on which the coords occur\n for (var i=0; i < pageCount; i++)\n {\n var pageIndex = renderedPages[i];\n var region = viewerState.renderer.layout.getPageRegion(pageIndex);\n\n if (region.left <= docCoords.left && region.right >= docCoords.left &&\n region.top <= docCoords.top && region.bottom >= docCoords.top)\n {\n return {\n anchorPage: pageIndex,\n offset: {\n left: docCoords.left - region.left,\n top: docCoords.top - region.top\n }\n };\n }\n }\n\n // Fall back to current page\n // FIXME: Would be better to use the closest page or something\n var currentRegion = viewerState.renderer.layout.getPageRegion(settings.currentPageIndex);\n\n return {\n anchorPage: settings.currentPageIndex,\n offset: {\n left: docCoords.left - currentRegion.left,\n top: docCoords.top - currentRegion.top\n }\n };\n };\n\n this.setManifest = function (manifest, loadOptions)\n {\n setManifest(manifest, loadOptions || {});\n };\n\n /**\n * Set the current page to the given index, firing VisiblePageDidChange\n *\n * @param pageIndex\n */\n this.setCurrentPage = function (pageIndex)\n {\n if (viewerState.currentPageIndex !== pageIndex)\n {\n viewerState.currentPageIndex = pageIndex;\n publish(\"VisiblePageDidChange\", pageIndex, this.getPageName(pageIndex));\n }\n };\n\n this.getPageName = function (pageIndex)\n {\n return viewerState.manifest.pages[pageIndex].f;\n };\n\n this.reload = function (newOptions)\n {\n reloadViewer(newOptions);\n };\n\n this.zoom = function (zoomLevel, focalPoint)\n {\n return handleZoom(zoomLevel, focalPoint);\n };\n\n this.enableScrollable = function ()\n {\n if (!viewerState.isScrollable)\n {\n bindMouseEvents();\n viewerState.options.enableKeyScroll = viewerState.initialKeyScroll;\n viewerState.options.enableSpaceScroll = viewerState.initialSpaceScroll;\n viewerState.viewportElement.style.overflow = 'auto';\n viewerState.isScrollable = true;\n }\n };\n\n this.disableScrollable = function ()\n {\n if (viewerState.isScrollable)\n {\n // block dragging/double-click zooming\n if (viewerState.innerObject.hasClass('diva-dragger'))\n viewerState.innerObject.unbind('mousedown');\n viewerState.outerObject.unbind('dblclick');\n viewerState.outerObject.unbind('contextmenu');\n\n // disable all other scrolling actions\n viewerState.viewportElement.style.overflow = 'hidden';\n\n // block scrolling keys behavior, respecting initial scroll settings\n viewerState.initialKeyScroll = settings.enableKeyScroll;\n viewerState.initialSpaceScroll = settings.enableSpaceScroll;\n viewerState.options.enableKeyScroll = false;\n viewerState.options.enableSpaceScroll = false;\n\n viewerState.isScrollable = false;\n }\n };\n\n this.isValidOption = function (key, value)\n {\n return isValidOption(key, value);\n };\n\n this.showError = function (message)\n {\n // FIXME: Not totally sure it makes sense to always do that here\n hideThrobber();\n\n var errorElement = elt('div', elemAttrs('error'), [\n elt('button', elemAttrs('error-close', {'aria-label': 'Close dialog'})),\n elt('p',\n elt('strong', 'Error')\n ),\n elt('div', message)\n ]);\n\n viewerState.outerObject.append(errorElement);\n\n //bind dialog close button\n $(settings.selector + 'error-close').on('click', function()\n {\n errorElement.parentNode.removeChild(errorElement);\n });\n };\n\n this.getXOffset = function (pageIndex, xAnchor)\n {\n return getXOffset(pageIndex, xAnchor);\n };\n\n this.getYOffset = function (pageIndex, yAnchor)\n {\n return getYOffset(pageIndex, yAnchor);\n };\n\n this.publish = publish;\n\n this.clear = function ()\n {\n clearViewer();\n };\n\n this.setPendingManifestRequest = function (pendingManifestRequest)\n {\n viewerState.pendingManifestRequest = pendingManifestRequest;\n };\n\n // Destroys this instance, tells plugins to do the same (for testing)\n this.destroy = function ()\n {\n // Useful event to access elements in diva before they get destroyed. Used by the highlight plugin.\n publish('ViewerWillTerminate', settings);\n\n // Cancel any pending request retrieving a manifest\n if (settings.pendingManifestRequest)\n settings.pendingManifestRequest.abort();\n\n // Removes the hide-scrollbar class from the body\n $('body').removeClass('diva-hide-scrollbar');\n\n // Empty the parent container and remove any diva-related data\n settings.parentObject.parent().empty().removeData('diva');\n\n // Remove any additional styling on the parent element\n settings.parentObject.parent().removeAttr('style').removeAttr('class');\n\n publish('ViewerDidTerminate', settings);\n\n // Clear the Events cache\n diva.Events.unsubscribeAll(settings.ID);\n };\n\n // Call the init function when this object is created.\n init();\n}\n\ngenerateId.counter = 1;\n\nfunction generateId() {\n return generateId.counter++;\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/viewer-core.js\n// module id = 15\n// module chunks = 0","/* jshint unused: false */\n\nvar jQuery = require('jquery');\n\n/* istanbul ignore next This is a vendored dependency */\n/*\n * jQuery dragscrollable Plugin\n * version: 1.0 (25-Jun-2009)\n * Copyright (c) 2009 Miquel Herrera\n * http://plugins.jquery.com/project/Dragscrollable\n *\n * Dual licensed under the MIT and GPL licenses:\n * http://www.opensource.org/licenses/mit-license.php\n * http://www.gnu.org/licenses/gpl.html\n *\n */\n(function ($) { // secure $ jQuery alias\n\n /**\n * Adds the ability to manage elements scroll by dragging\n * one or more of its descendant elements. Options parameter\n * allow to specifically select which inner elements will\n * respond to the drag events.\n *\n * options properties:\n * ------------------------------------------------------------------------\n * dragSelector | jquery selector to apply to each wrapped element\n * | to find which will be the dragging elements.\n * | Defaults to '>:first' which is the first child of\n * | scrollable element\n * ------------------------------------------------------------------------\n * acceptPropagatedEvent| Will the dragging element accept propagated\n * | events? default is yes, a propagated mouse event\n * | on a inner element will be accepted and processed.\n * | If set to false, only events originated on the\n * | draggable elements will be processed.\n * ------------------------------------------------------------------------\n * preventDefault | Prevents the event to propagate further effectivey\n * | dissabling other default actions. Defaults to true\n * ------------------------------------------------------------------------\n *\n * usage examples:\n *\n * To add the scroll by drag to the element id=viewport when dragging its\n * first child accepting any propagated events\n * $('#viewport').dragscrollable();\n *\n * To add the scroll by drag ability to any element div of class viewport\n * when dragging its first descendant of class dragMe responding only to\n * evcents originated on the '.dragMe' elements.\n * $('div.viewport').dragscrollable({dragSelector:'.dragMe:first',\n * acceptPropagatedEvent: false});\n *\n * Notice that some 'viewports' could be nested within others but events\n * would not interfere as acceptPropagatedEvent is set to false.\n *\n */\n $.fn.dragscrollable = function( options ){\n\n var settings = $.extend(\n {\n dragSelector:'>:first',\n acceptPropagatedEvent: true,\n preventDefault: true\n },options || {});\n\n\n var dragscroll= {\n mouseDownHandler : function(event) {\n // mousedown, left click, check propagation\n if (event.which!=1 ||\n (!event.data.acceptPropagatedEvent && event.target != this)){\n return false;\n }\n\n // Initial coordinates will be the last when dragging\n event.data.lastCoord = {left: event.clientX, top: event.clientY};\n\n $.event.add( document, \"mouseup\",\n dragscroll.mouseUpHandler, event.data );\n $.event.add( document, \"mousemove\",\n dragscroll.mouseMoveHandler, event.data );\n if (event.data.preventDefault) {\n event.preventDefault();\n return false;\n }\n },\n mouseMoveHandler : function(event) { // User is dragging\n // How much did the mouse move?\n var delta = {left: (event.clientX - event.data.lastCoord.left),\n top: (event.clientY - event.data.lastCoord.top)};\n\n // Set the scroll position relative to what ever the scroll is now\n event.data.scrollable.scrollLeft(\n event.data.scrollable.scrollLeft() - delta.left);\n event.data.scrollable.scrollTop(\n event.data.scrollable.scrollTop() - delta.top);\n\n // Save where the cursor is\n event.data.lastCoord={left: event.clientX, top: event.clientY};\n if (event.data.preventDefault) {\n event.preventDefault();\n return false;\n }\n\n },\n mouseUpHandler : function(event) { // Stop scrolling\n $.event.remove( document, \"mousemove\", dragscroll.mouseMoveHandler);\n $.event.remove( document, \"mouseup\", dragscroll.mouseUpHandler);\n if (event.data.preventDefault) {\n event.preventDefault();\n return false;\n }\n }\n };\n\n // set up the initial events\n this.each(function() {\n // closure object data for each scrollable element\n var data = {scrollable : $(this),\n acceptPropagatedEvent : settings.acceptPropagatedEvent,\n preventDefault : settings.preventDefault };\n // Set mouse initiating event on the desired descendant\n $(this).find(settings.dragSelector).\n bind('mousedown', data, dragscroll.mouseDownHandler);\n });\n }; //end plugin dragscrollable\n\n})( jQuery ); // confine scope\n\n/* istanbul ignore next This is a vendored dependency */\n/**\n jQuery.kinetic v2.2.1\n Dave Taylor http://davetayls.me\n\n @license The MIT License (MIT)\n @preserve Copyright (c) 2012 Dave Taylor http://davetayls.me\n */\n(function ($){\n 'use strict';\n\n var ACTIVE_CLASS = 'kinetic-active';\n\n /**\n * Provides requestAnimationFrame in a cross browser way.\n * http://paulirish.com/2011/requestanimationframe-for-smart-animating/\n */\n if (!window.requestAnimationFrame){\n\n window.requestAnimationFrame = ( function (){\n\n return window.webkitRequestAnimationFrame ||\n window.mozRequestAnimationFrame ||\n window.oRequestAnimationFrame ||\n window.msRequestAnimationFrame ||\n function (/* function FrameRequestCallback */ callback, /* DOMElement Element */ element){\n window.setTimeout(callback, 1000 / 60);\n };\n\n }());\n\n }\n\n // add touch checker to jQuery.support\n $.support = $.support || {};\n $.extend($.support, {\n touch: 'ontouchend' in document\n });\n\n\n // KINETIC CLASS DEFINITION\n // ======================\n\n var Kinetic = function (element, settings) {\n this.settings = settings;\n this.el = element;\n this.$el = $(element);\n\n this._initElements();\n\n return this;\n };\n\n Kinetic.DATA_KEY = 'kinetic';\n Kinetic.DEFAULTS = {\n cursor: 'move',\n decelerate: true,\n triggerHardware: false,\n threshold: 0,\n y: true,\n x: true,\n slowdown: 0.9,\n maxvelocity: 40,\n throttleFPS: 60,\n invert: false,\n movingClass: {\n up: 'kinetic-moving-up',\n down: 'kinetic-moving-down',\n left: 'kinetic-moving-left',\n right: 'kinetic-moving-right'\n },\n deceleratingClass: {\n up: 'kinetic-decelerating-up',\n down: 'kinetic-decelerating-down',\n left: 'kinetic-decelerating-left',\n right: 'kinetic-decelerating-right'\n }\n };\n\n\n // Public functions\n\n Kinetic.prototype.start = function (options){\n this.settings = $.extend(this.settings, options);\n this.velocity = options.velocity || this.velocity;\n this.velocityY = options.velocityY || this.velocityY;\n this.settings.decelerate = false;\n this._move();\n };\n\n Kinetic.prototype.end = function (){\n this.settings.decelerate = true;\n };\n\n Kinetic.prototype.stop = function (){\n this.velocity = 0;\n this.velocityY = 0;\n this.settings.decelerate = true;\n if ($.isFunction(this.settings.stopped)){\n this.settings.stopped.call(this);\n }\n };\n\n Kinetic.prototype.detach = function (){\n this._detachListeners();\n this.$el\n .removeClass(ACTIVE_CLASS)\n .css('cursor', '');\n };\n\n Kinetic.prototype.attach = function (){\n if (this.$el.hasClass(ACTIVE_CLASS)) {\n return;\n }\n this._attachListeners(this.$el);\n this.$el\n .addClass(ACTIVE_CLASS)\n .css('cursor', this.settings.cursor);\n };\n\n\n // Internal functions\n\n Kinetic.prototype._initElements = function (){\n this.$el.addClass(ACTIVE_CLASS);\n\n $.extend(this, {\n xpos: null,\n prevXPos: false,\n ypos: null,\n prevYPos: false,\n mouseDown: false,\n throttleTimeout: 1000 / this.settings.throttleFPS,\n lastMove: null,\n elementFocused: null\n });\n\n this.velocity = 0;\n this.velocityY = 0;\n\n // make sure we reset everything when mouse up\n $(document)\n .mouseup($.proxy(this._resetMouse, this))\n .click($.proxy(this._resetMouse, this));\n\n this._initEvents();\n\n this.$el.css('cursor', this.settings.cursor);\n\n if (this.settings.triggerHardware){\n this.$el.css({\n '-webkit-transform': 'translate3d(0,0,0)',\n '-webkit-perspective': '1000',\n '-webkit-backface-visibility': 'hidden'\n });\n }\n };\n\n Kinetic.prototype._initEvents = function(){\n var self = this;\n this.settings.events = {\n touchStart: function (e){\n var touch;\n if (self._useTarget(e.target, e)){\n touch = e.originalEvent.touches[0];\n self.threshold = self._threshold(e.target, e);\n self._start(touch.clientX, touch.clientY);\n e.stopPropagation();\n }\n },\n touchMove: function (e){\n var touch;\n if (self.mouseDown){\n touch = e.originalEvent.touches[0];\n self._inputmove(touch.clientX, touch.clientY);\n if (e.preventDefault){\n e.preventDefault();\n }\n }\n },\n inputDown: function (e){\n if (self._useTarget(e.target, e)){\n self.threshold = self._threshold(e.target, e);\n self._start(e.clientX, e.clientY);\n self.elementFocused = e.target;\n if (e.target.nodeName === 'IMG'){\n e.preventDefault();\n }\n e.stopPropagation();\n }\n },\n inputEnd: function (e){\n if (self._useTarget(e.target, e)){\n self._end();\n self.elementFocused = null;\n if (e.preventDefault){\n e.preventDefault();\n }\n }\n },\n inputMove: function (e){\n if (self.mouseDown){\n self._inputmove(e.clientX, e.clientY);\n if (e.preventDefault){\n e.preventDefault();\n }\n }\n },\n scroll: function (e){\n if ($.isFunction(self.settings.moved)){\n self.settings.moved.call(self, self.settings);\n }\n if (e.preventDefault){\n e.preventDefault();\n }\n },\n inputClick: function (e){\n if (Math.abs(self.velocity) > 0){\n e.preventDefault();\n return false;\n }\n },\n // prevent drag and drop images in ie\n dragStart: function (e){\n if (self._useTarget(e.target, e) && self.elementFocused){\n return false;\n }\n },\n // prevent selection when dragging\n selectStart: function (e){\n if ($.isFunction(self.settings.selectStart)){\n return self.settings.selectStart.apply(self, arguments);\n } else if (self._useTarget(e.target, e)) {\n return false;\n }\n }\n };\n\n this._attachListeners(this.$el, this.settings);\n\n };\n\n Kinetic.prototype._inputmove = function (clientX, clientY){\n var $this = this.$el;\n var el = this.el;\n\n if (!this.lastMove || new Date() > new Date(this.lastMove.getTime() + this.throttleTimeout)){\n this.lastMove = new Date();\n\n if (this.mouseDown && (this.xpos || this.ypos)){\n var movedX = (clientX - this.xpos);\n var movedY = (clientY - this.ypos);\n if (this.settings.invert) {\n movedX *= -1;\n movedY *= -1;\n }\n if(this.threshold > 0){\n var moved = Math.sqrt(movedX * movedX + movedY * movedY);\n if(this.threshold > moved){\n return;\n } else {\n this.threshold = 0;\n }\n }\n if (this.elementFocused){\n $(this.elementFocused).blur();\n this.elementFocused = null;\n $this.focus();\n }\n\n this.settings.decelerate = false;\n this.velocity = this.velocityY = 0;\n\n var scrollLeft = this.scrollLeft();\n var scrollTop = this.scrollTop();\n\n this.scrollLeft(this.settings.x ? scrollLeft - movedX : scrollLeft);\n this.scrollTop(this.settings.y ? scrollTop - movedY : scrollTop);\n\n this.prevXPos = this.xpos;\n this.prevYPos = this.ypos;\n this.xpos = clientX;\n this.ypos = clientY;\n\n this._calculateVelocities();\n this._setMoveClasses(this.settings.movingClass);\n\n if ($.isFunction(this.settings.moved)){\n this.settings.moved.call(this, this.settings);\n }\n }\n }\n };\n\n Kinetic.prototype._calculateVelocities = function (){\n this.velocity = this._capVelocity(this.prevXPos - this.xpos, this.settings.maxvelocity);\n this.velocityY = this._capVelocity(this.prevYPos - this.ypos, this.settings.maxvelocity);\n if (this.settings.invert) {\n this.velocity *= -1;\n this.velocityY *= -1;\n }\n };\n\n Kinetic.prototype._end = function (){\n if (this.xpos && this.prevXPos && this.settings.decelerate === false){\n this.settings.decelerate = true;\n this._calculateVelocities();\n this.xpos = this.prevXPos = this.mouseDown = false;\n this._move();\n }\n };\n\n Kinetic.prototype._useTarget = function (target, event){\n if ($.isFunction(this.settings.filterTarget)){\n return this.settings.filterTarget.call(this, target, event) !== false;\n }\n return true;\n };\n\n Kinetic.prototype._threshold = function (target, event){\n if ($.isFunction(this.settings.threshold)){\n return this.settings.threshold.call(this, target, event);\n }\n return this.settings.threshold;\n };\n\n Kinetic.prototype._start = function (clientX, clientY){\n this.mouseDown = true;\n this.velocity = this.prevXPos = 0;\n this.velocityY = this.prevYPos = 0;\n this.xpos = clientX;\n this.ypos = clientY;\n };\n\n Kinetic.prototype._resetMouse = function (){\n this.xpos = false;\n this.ypos = false;\n this.mouseDown = false;\n };\n\n Kinetic.prototype._decelerateVelocity = function (velocity, slowdown){\n return Math.floor(Math.abs(velocity)) === 0 ? 0 // is velocity less than 1?\n : velocity * slowdown; // reduce slowdown\n };\n\n Kinetic.prototype._capVelocity = function (velocity, max){\n var newVelocity = velocity;\n if (velocity > 0){\n if (velocity > max){\n newVelocity = max;\n }\n } else {\n if (velocity < (0 - max)){\n newVelocity = (0 - max);\n }\n }\n return newVelocity;\n };\n\n Kinetic.prototype._setMoveClasses = function (classes){\n // FIXME: consider if we want to apply PL #44, this should not remove\n // classes we have not defined on the element!\n var settings = this.settings;\n var $this = this.$el;\n\n $this.removeClass(settings.movingClass.up)\n .removeClass(settings.movingClass.down)\n .removeClass(settings.movingClass.left)\n .removeClass(settings.movingClass.right)\n .removeClass(settings.deceleratingClass.up)\n .removeClass(settings.deceleratingClass.down)\n .removeClass(settings.deceleratingClass.left)\n .removeClass(settings.deceleratingClass.right);\n\n if (this.velocity > 0){\n $this.addClass(classes.right);\n }\n if (this.velocity < 0){\n $this.addClass(classes.left);\n }\n if (this.velocityY > 0){\n $this.addClass(classes.down);\n }\n if (this.velocityY < 0){\n $this.addClass(classes.up);\n }\n\n };\n\n\n // do the actual kinetic movement\n Kinetic.prototype._move = function (){\n var $scroller = this._getScroller();\n var scroller = $scroller[0];\n var self = this;\n var settings = self.settings;\n\n // set scrollLeft\n if (settings.x && scroller.scrollWidth > 0){\n this.scrollLeft(this.scrollLeft() + this.velocity);\n if (Math.abs(this.velocity) > 0){\n this.velocity = settings.decelerate ?\n self._decelerateVelocity(this.velocity, settings.slowdown) : this.velocity;\n }\n } else {\n this.velocity = 0;\n }\n\n // set scrollTop\n if (settings.y && scroller.scrollHeight > 0){\n this.scrollTop(this.scrollTop() + this.velocityY);\n if (Math.abs(this.velocityY) > 0){\n this.velocityY = settings.decelerate ?\n self._decelerateVelocity(this.velocityY, settings.slowdown) : this.velocityY;\n }\n } else {\n this.velocityY = 0;\n }\n\n self._setMoveClasses(settings.deceleratingClass);\n\n if ($.isFunction(settings.moved)){\n settings.moved.call(this, settings);\n }\n\n if (Math.abs(this.velocity) > 0 || Math.abs(this.velocityY) > 0){\n if (!this.moving) {\n this.moving = true;\n // tick for next movement\n window.requestAnimationFrame(function (){\n self.moving = false;\n self._move();\n });\n }\n } else {\n self.stop();\n }\n };\n\n // get current scroller to apply positioning to\n Kinetic.prototype._getScroller = function(){\n var $scroller = this.$el;\n if (this.$el.is('body') || this.$el.is('html')){\n $scroller = $(window);\n }\n return $scroller;\n };\n\n // set the scroll position\n Kinetic.prototype.scrollLeft = function(left){\n var $scroller = this._getScroller();\n if (typeof left === 'number'){\n $scroller.scrollLeft(left);\n this.settings.scrollLeft = left;\n } else {\n return $scroller.scrollLeft();\n }\n };\n Kinetic.prototype.scrollTop = function(top){\n var $scroller = this._getScroller();\n if (typeof top === 'number'){\n $scroller.scrollTop(top);\n this.settings.scrollTop = top;\n } else {\n return $scroller.scrollTop();\n }\n };\n\n Kinetic.prototype._attachListeners = function (){\n var $this = this.$el;\n var settings = this.settings;\n\n if ($.support.touch){\n $this\n .bind('touchstart', settings.events.touchStart)\n .bind('touchend', settings.events.inputEnd)\n .bind('touchmove', settings.events.touchMove);\n }\n\n $this\n .mousedown(settings.events.inputDown)\n .mouseup(settings.events.inputEnd)\n .mousemove(settings.events.inputMove);\n\n $this\n .click(settings.events.inputClick)\n .scroll(settings.events.scroll)\n .bind('selectstart', settings.events.selectStart)\n .bind('dragstart', settings.events.dragStart);\n };\n\n Kinetic.prototype._detachListeners = function (){\n var $this = this.$el;\n var settings = this.settings;\n if ($.support.touch){\n $this\n .unbind('touchstart', settings.events.touchStart)\n .unbind('touchend', settings.events.inputEnd)\n .unbind('touchmove', settings.events.touchMove);\n }\n\n $this\n .unbind('mousedown', settings.events.inputDown)\n .unbind('mouseup', settings.events.inputEnd)\n .unbind('mousemove', settings.events.inputMove);\n\n $this\n .unbind('click', settings.events.inputClick)\n .unbind('scroll', settings.events.scroll)\n .unbind('selectstart', settings.events.selectStart)\n .unbind('dragstart', settings.events.dragStart);\n };\n\n\n // EXPOSE KINETIC CONSTRUCTOR\n // ==========================\n $.Kinetic = Kinetic;\n\n // KINETIC PLUGIN DEFINITION\n // =======================\n\n $.fn.kinetic = function (option, callOptions) {\n return this.each(function () {\n var $this = $(this);\n var instance = $this.data(Kinetic.DATA_KEY);\n var options = $.extend({}, Kinetic.DEFAULTS, $this.data(), typeof option === 'object' && option);\n\n if (!instance) {\n $this.data(Kinetic.DATA_KEY, (instance = new Kinetic(this, options)));\n }\n\n if (typeof option === 'string') {\n instance[option](callOptions);\n }\n\n });\n };\n\n}(jQuery));\n\n/* istanbul ignore next\n We should maybe be testing this, but realistically that would mean maintaining a real fork */\n\n// jQuery.kinetic core modifications for diva.js (compatible with jQuery.kinetic 2.2.1)\n// use jQuery.kinetic for touch handlers only since we are using dragscrollable for mouse handlers\n// - (kinetic provides inertial scrolling [ease into stopped state on release] for touch events and dragscrollable\n// allows non-inertial scrolling which we like for mice)\n\n(function($)\n{\n $.Kinetic.prototype._attachListeners = function()\n {\n // attach only touch listeners\n var $this = this.$el;\n var settings = this.settings;\n\n if ($.support.touch)\n {\n $this\n .bind('touchstart', settings.events.touchStart)\n .bind('touchend', settings.events.inputEnd)\n .bind('touchmove', settings.events.touchMove);\n }\n\n $this\n .click(settings.events.inputClick)\n .scroll(settings.events.scroll)\n .bind('selectstart', settings.events.selectStart)\n .bind('dragstart', settings.events.dragStart);\n };\n\n $.Kinetic.prototype._detachListeners = function()\n {\n // detach only touch listeners\n var $this = this.$el;\n var settings = this.settings;\n\n if ($.support.touch)\n {\n $this\n .unbind('touchstart', settings.events.touchStart)\n .unbind('touchend', settings.events.inputEnd)\n .unbind('touchmove', settings.events.touchMove);\n }\n\n $this\n .unbind('click', settings.events.inputClick)\n .unbind('scroll', settings.events.scroll)\n .unbind('selectstart', settings.events.selectStart)\n .unbind('dragstart', settings.events.dragStart);\n };\n})(jQuery);\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/utils/jquery-extensions.js\n// module id = 16\n// module chunks = 0","// From http://www.alexandre-gomes.com/?p=115, modified slightly\nmodule.exports = function getScrollbarWidth() {\n var inner = document.createElement('p');\n inner.style.width = '100%';\n inner.style.height = '200px';\n\n var outer = document.createElement('div');\n outer.style.position = 'absolute';\n outer.style.top = '0px';\n outer.style.left = '0px';\n outer.style.visibility = 'hidden';\n outer.style.width = '200px';\n outer.style.height = '150px';\n outer.style.overflow = 'hidden';\n outer.appendChild(inner);\n\n document.body.appendChild(outer);\n\n var w1 = inner.offsetWidth;\n outer.style.overflow = 'scroll';\n var w2 = inner.offsetWidth;\n if (w1 == w2) {\n w2 = outer.clientWidth; // for IE i think\n }\n\n document.body.removeChild(outer);\n return w1 - w2;\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/utils/get-scrollbar-width.js\n// module id = 17\n// module chunks = 0","module.exports = {\n onDoubleClick: onDoubleClick,\n onPinch: onPinch,\n onDoubleTap: onDoubleTap\n};\n\nvar DOUBLE_CLICK_TIMEOUT = 500;\n\nvar DOUBLE_TAP_DISTANCE_THRESHOLD = 50;\nvar DOUBLE_TAP_TIMEOUT = 250;\n\nfunction onDoubleClick(elem, callback)\n{\n elem.on('dblclick', function (event)\n {\n if (!event.ctrlKey)\n {\n callback(event, getRelativeOffset(event.currentTarget, event));\n }\n });\n\n // Handle the control key for macs (in conjunction with double-clicking)\n // FIXME: Does a click get handled with ctrl pressed on non-Macs?\n var tracker = createDoubleEventTracker(DOUBLE_CLICK_TIMEOUT);\n\n elem.on('contextmenu', function (event)\n {\n event.preventDefault();\n\n if (event.ctrlKey)\n {\n if (tracker.isTriggered())\n {\n tracker.reset();\n callback(event, getRelativeOffset(event.currentTarget, event));\n }\n else\n {\n tracker.trigger();\n }\n }\n });\n}\n\nfunction onPinch(elem, callback)\n{\n var startDistance = 0;\n\n elem.on('touchstart', function(event)\n {\n // Prevent mouse event from firing\n event.preventDefault();\n\n if (event.originalEvent.touches.length === 2)\n {\n startDistance = distance(\n event.originalEvent.touches[0].clientX,\n event.originalEvent.touches[0].clientY,\n event.originalEvent.touches[1].clientX,\n event.originalEvent.touches[1].clientY\n );\n }\n });\n\n elem.on('touchmove', function(event)\n {\n // Prevent mouse event from firing\n event.preventDefault();\n\n if (event.originalEvent.touches.length === 2)\n {\n var touches = event.originalEvent.touches;\n\n var moveDistance = distance(\n touches[0].clientX,\n touches[0].clientY,\n touches[1].clientX,\n touches[1].clientY\n );\n\n var zoomDelta = moveDistance - startDistance;\n\n if (Math.abs(zoomDelta) > 0)\n {\n var touchCenter = {\n pageX: (touches[0].clientX + touches[1].clientX) / 2,\n pageY: (touches[0].clientY + touches[1].clientY) / 2\n };\n\n callback(event, getRelativeOffset(event.currentTarget, touchCenter), startDistance, moveDistance);\n }\n }\n });\n}\n\nfunction onDoubleTap(elem, callback)\n{\n var tracker = createDoubleEventTracker(DOUBLE_TAP_TIMEOUT);\n var firstTap = null;\n\n elem.on('touchend', function (event)\n {\n // Prevent mouse event from firing\n event.preventDefault();\n\n if (tracker.isTriggered())\n {\n tracker.reset();\n\n // Doubletap has occurred\n var secondTap = {\n pageX: event.originalEvent.changedTouches[0].clientX,\n pageY: event.originalEvent.changedTouches[0].clientY\n };\n\n // If first tap is close to second tap (prevents interference with scale event)\n var tapDistance = distance(firstTap.pageX, firstTap.pageY, secondTap.pageX, secondTap.pageY);\n\n // TODO: Could give something higher-level than secondTap to callback\n if (tapDistance < DOUBLE_TAP_DISTANCE_THRESHOLD)\n callback(event, getRelativeOffset(event.currentTarget, secondTap));\n\n firstTap = null;\n }\n else\n {\n firstTap = {\n pageX: event.originalEvent.changedTouches[0].clientX,\n pageY: event.originalEvent.changedTouches[0].clientY\n };\n\n tracker.trigger();\n }\n });\n}\n\n// Pythagorean theorem to get the distance between two points (used for\n// calculating finger distance for double-tap and pinch-zoom)\nfunction distance(x1, y1, x2, y2)\n{\n return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));\n}\n\n// Utility to keep track of whether an event has been triggered twice\n// during a a given duration\nfunction createDoubleEventTracker(timeoutDuration)\n{\n var triggered = false;\n var timeoutId = null;\n\n return {\n trigger: function ()\n {\n triggered = true;\n resetTimeout();\n timeoutId = setTimeout(function ()\n {\n triggered = false;\n timeoutId = null;\n }, timeoutDuration);\n },\n isTriggered: function ()\n {\n return triggered;\n },\n reset: function ()\n {\n triggered = false;\n resetTimeout();\n }\n };\n\n function resetTimeout()\n {\n if (timeoutId !== null)\n {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n }\n}\n\nfunction getRelativeOffset(elem, pageCoords)\n{\n var bounds = elem.getBoundingClientRect();\n\n return {\n left: pageCoords.pageX - bounds.left,\n top: pageCoords.pageY - bounds.top\n };\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/gesture-events.js\n// module id = 18\n// module chunks = 0","var maxBy = require('lodash.maxby');\nvar PageToolsOverlay = require('./page-tools-overlay');\n\nmodule.exports = DocumentHandler;\n\nfunction DocumentHandler(viewerCore)\n{\n this._viewerCore = viewerCore;\n this._viewerState = viewerCore.getInternalState();\n this._overlays = [];\n\n if (viewerCore.getPageTools().length)\n {\n var numPages = viewerCore.getSettings().numPages;\n\n for (var i=0; i < numPages; i++)\n {\n var overlay = new PageToolsOverlay(i, viewerCore);\n this._overlays.push(overlay);\n viewerCore.addPageOverlay(overlay);\n }\n }\n}\n\n// USER EVENTS\nDocumentHandler.prototype.onDoubleClick = function (event, coords)\n{\n var settings = this._viewerCore.getSettings();\n var newZoomLevel = event.ctrlKey ? settings.zoomLevel - 1 : settings.zoomLevel + 1;\n\n var position = this._viewerCore.getPagePositionAtViewportOffset(coords);\n\n this._viewerCore.zoom(newZoomLevel, position);\n};\n\nDocumentHandler.prototype.onPinch = function (event, coords, startDistance, endDistance)\n{\n // FIXME: Do this check in a way which is less spaghetti code-y\n var viewerState = this._viewerCore.getInternalState();\n var settings = this._viewerCore.getSettings();\n\n var newZoomLevel = Math.log(Math.pow(2, settings.zoomLevel) * endDistance / (startDistance * Math.log(2))) / Math.log(2);\n newZoomLevel = Math.max(settings.minZoomLevel, newZoomLevel);\n newZoomLevel = Math.min(settings.maxZoomLevel, newZoomLevel);\n\n if (newZoomLevel === settings.zoomLevel)\n return;\n\n var position = this._viewerCore.getPagePositionAtViewportOffset(coords);\n\n var layout = this._viewerCore.getCurrentLayout();\n var centerOffset = layout.getPageToViewportCenterOffset(position.anchorPage, viewerState.viewport);\n var scaleRatio = 1 / Math.pow(2, settings.zoomLevel - newZoomLevel);\n\n this._viewerCore.reload({\n zoomLevel: newZoomLevel,\n goDirectlyTo: position.anchorPage,\n horizontalOffset: (centerOffset.x - position.offset.left) + position.offset.left * scaleRatio,\n verticalOffset: (centerOffset.y - position.offset.top) + position.offset.top * scaleRatio\n });\n};\n\n// VIEW EVENTS\nDocumentHandler.prototype.onViewWillLoad = function ()\n{\n this._viewerCore.publish('DocumentWillLoad', this._viewerCore.getSettings());\n};\n\nDocumentHandler.prototype.onViewDidLoad = function ()\n{\n // TODO: Should only be necessary to handle changes on view update, not\n // initial load\n this._handleZoomLevelChange();\n\n var currentPageIndex = this._viewerCore.getSettings().currentPageIndex;\n var fileName = this._viewerCore.getPageName(currentPageIndex);\n this._viewerCore.publish(\"DocumentDidLoad\", currentPageIndex, fileName);\n};\n\nDocumentHandler.prototype.onViewDidUpdate = function (renderedPages, targetPage)\n{\n var currentPage = (targetPage !== null) ?\n targetPage :\n getCentermostPage(renderedPages, this._viewerCore.getCurrentLayout(), this._viewerCore.getViewport());\n\n // Don't change the current page if there is no page in the viewport\n // FIXME: Would be better to fall back to the page closest to the viewport\n if (currentPage !== null)\n this._viewerCore.setCurrentPage(currentPage);\n\n if (targetPage !== null)\n this._viewerCore.publish(\"ViewerDidJump\", targetPage);\n\n this._handleZoomLevelChange();\n};\n\nDocumentHandler.prototype._handleZoomLevelChange = function ()\n{\n var viewerState = this._viewerState;\n var zoomLevel = viewerState.options.zoomLevel;\n\n // If this is not the initial load, trigger the zoom events\n if (viewerState.oldZoomLevel !== zoomLevel && viewerState.oldZoomLevel >= 0)\n {\n if (viewerState.oldZoomLevel < zoomLevel)\n {\n this._viewerCore.publish(\"ViewerDidZoomIn\", zoomLevel);\n }\n else\n {\n this._viewerCore.publish(\"ViewerDidZoomOut\", zoomLevel);\n }\n\n this._viewerCore.publish(\"ViewerDidZoom\", zoomLevel);\n }\n\n viewerState.oldZoomLevel = zoomLevel;\n};\n\nDocumentHandler.prototype.destroy = function ()\n{\n this._overlays.forEach(function (overlay)\n {\n this._viewerCore.removePageOverlay(overlay);\n }, this);\n};\n\nfunction getCentermostPage(renderedPages, layout, viewport)\n{\n var centerY = viewport.top + (viewport.height / 2);\n var centerX = viewport.left + (viewport.width / 2);\n\n // Find the minimum distance from the viewport center to a page.\n // Compute minus the squared distance from viewport center to the page's border.\n // http://gamedev.stackexchange.com/questions/44483/how-do-i-calculate-distance-between-a-point-and-an-axis-aligned-rectangle\n var centerPage = maxBy(renderedPages, function (pageIndex)\n {\n var dims = layout.getPageDimensions(pageIndex);\n var imageOffset = layout.getPageOffset(pageIndex, {excludePadding: false});\n\n var midX = imageOffset.left + (dims.width / 2);\n var midY = imageOffset.top + (dims.height / 2);\n\n var dx = Math.max(Math.abs(centerX - midX) - (dims.width / 2), 0);\n var dy = Math.max(Math.abs(centerY - midY) - (dims.height / 2), 0);\n\n return -(dx * dx + dy * dy);\n });\n\n return centerPage != null ? centerPage : null;\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/document-handler.js\n// module id = 19\n// module chunks = 0","/**\n * lodash (Custom Build) \n * Build: `lodash modularize exports=\"npm\" -o ./`\n * Copyright jQuery Foundation and other contributors \n * Released under MIT license \n * Based on Underscore.js 1.8.3 \n * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors\n */\n\n/** Used as the size to enable large array optimizations. */\nvar LARGE_ARRAY_SIZE = 200;\n\n/** Used as the `TypeError` message for \"Functions\" methods. */\nvar FUNC_ERROR_TEXT = 'Expected a function';\n\n/** Used to stand-in for `undefined` hash values. */\nvar HASH_UNDEFINED = '__lodash_hash_undefined__';\n\n/** Used to compose bitmasks for comparison styles. */\nvar UNORDERED_COMPARE_FLAG = 1,\n PARTIAL_COMPARE_FLAG = 2;\n\n/** Used as references for various `Number` constants. */\nvar INFINITY = 1 / 0,\n MAX_SAFE_INTEGER = 9007199254740991;\n\n/** `Object#toString` result references. */\nvar argsTag = '[object Arguments]',\n arrayTag = '[object Array]',\n boolTag = '[object Boolean]',\n dateTag = '[object Date]',\n errorTag = '[object Error]',\n funcTag = '[object Function]',\n genTag = '[object GeneratorFunction]',\n mapTag = '[object Map]',\n numberTag = '[object Number]',\n objectTag = '[object Object]',\n promiseTag = '[object Promise]',\n regexpTag = '[object RegExp]',\n setTag = '[object Set]',\n stringTag = '[object String]',\n symbolTag = '[object Symbol]',\n weakMapTag = '[object WeakMap]';\n\nvar arrayBufferTag = '[object ArrayBuffer]',\n dataViewTag = '[object DataView]',\n float32Tag = '[object Float32Array]',\n float64Tag = '[object Float64Array]',\n int8Tag = '[object Int8Array]',\n int16Tag = '[object Int16Array]',\n int32Tag = '[object Int32Array]',\n uint8Tag = '[object Uint8Array]',\n uint8ClampedTag = '[object Uint8ClampedArray]',\n uint16Tag = '[object Uint16Array]',\n uint32Tag = '[object Uint32Array]';\n\n/** Used to match property names within property paths. */\nvar reIsDeepProp = /\\.|\\[(?:[^[\\]]*|([\"'])(?:(?!\\1)[^\\\\]|\\\\.)*?\\1)\\]/,\n reIsPlainProp = /^\\w*$/,\n reLeadingDot = /^\\./,\n rePropName = /[^.[\\]]+|\\[(?:(-?\\d+(?:\\.\\d+)?)|([\"'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2)\\]|(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))/g;\n\n/**\n * Used to match `RegExp`\n * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).\n */\nvar reRegExpChar = /[\\\\^$.*+?()[\\]{}|]/g;\n\n/** Used to match backslashes in property paths. */\nvar reEscapeChar = /\\\\(\\\\)?/g;\n\n/** Used to detect host constructors (Safari). */\nvar reIsHostCtor = /^\\[object .+?Constructor\\]$/;\n\n/** Used to detect unsigned integer values. */\nvar reIsUint = /^(?:0|[1-9]\\d*)$/;\n\n/** Used to identify `toStringTag` values of typed arrays. */\nvar typedArrayTags = {};\ntypedArrayTags[float32Tag] = typedArrayTags[float64Tag] =\ntypedArrayTags[int8Tag] = typedArrayTags[int16Tag] =\ntypedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =\ntypedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =\ntypedArrayTags[uint32Tag] = true;\ntypedArrayTags[argsTag] = typedArrayTags[arrayTag] =\ntypedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =\ntypedArrayTags[dataViewTag] = typedArrayTags[dateTag] =\ntypedArrayTags[errorTag] = typedArrayTags[funcTag] =\ntypedArrayTags[mapTag] = typedArrayTags[numberTag] =\ntypedArrayTags[objectTag] = typedArrayTags[regexpTag] =\ntypedArrayTags[setTag] = typedArrayTags[stringTag] =\ntypedArrayTags[weakMapTag] = false;\n\n/** Detect free variable `global` from Node.js. */\nvar freeGlobal = typeof global == 'object' && global && global.Object === Object && global;\n\n/** Detect free variable `self`. */\nvar freeSelf = typeof self == 'object' && self && self.Object === Object && self;\n\n/** Used as a reference to the global object. */\nvar root = freeGlobal || freeSelf || Function('return this')();\n\n/** Detect free variable `exports`. */\nvar freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;\n\n/** Detect free variable `module`. */\nvar freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;\n\n/** Detect the popular CommonJS extension `module.exports`. */\nvar moduleExports = freeModule && freeModule.exports === freeExports;\n\n/** Detect free variable `process` from Node.js. */\nvar freeProcess = moduleExports && freeGlobal.process;\n\n/** Used to access faster Node.js helpers. */\nvar nodeUtil = (function() {\n try {\n return freeProcess && freeProcess.binding('util');\n } catch (e) {}\n}());\n\n/* Node.js helper references. */\nvar nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;\n\n/**\n * A specialized version of `_.some` for arrays without support for iteratee\n * shorthands.\n *\n * @private\n * @param {Array} [array] The array to iterate over.\n * @param {Function} predicate The function invoked per iteration.\n * @returns {boolean} Returns `true` if any element passes the predicate check,\n * else `false`.\n */\nfunction arraySome(array, predicate) {\n var index = -1,\n length = array ? array.length : 0;\n\n while (++index < length) {\n if (predicate(array[index], index, array)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * The base implementation of `_.property` without support for deep paths.\n *\n * @private\n * @param {string} key The key of the property to get.\n * @returns {Function} Returns the new accessor function.\n */\nfunction baseProperty(key) {\n return function(object) {\n return object == null ? undefined : object[key];\n };\n}\n\n/**\n * The base implementation of `_.times` without support for iteratee shorthands\n * or max array length checks.\n *\n * @private\n * @param {number} n The number of times to invoke `iteratee`.\n * @param {Function} iteratee The function invoked per iteration.\n * @returns {Array} Returns the array of results.\n */\nfunction baseTimes(n, iteratee) {\n var index = -1,\n result = Array(n);\n\n while (++index < n) {\n result[index] = iteratee(index);\n }\n return result;\n}\n\n/**\n * The base implementation of `_.unary` without support for storing metadata.\n *\n * @private\n * @param {Function} func The function to cap arguments for.\n * @returns {Function} Returns the new capped function.\n */\nfunction baseUnary(func) {\n return function(value) {\n return func(value);\n };\n}\n\n/**\n * Gets the value at `key` of `object`.\n *\n * @private\n * @param {Object} [object] The object to query.\n * @param {string} key The key of the property to get.\n * @returns {*} Returns the property value.\n */\nfunction getValue(object, key) {\n return object == null ? undefined : object[key];\n}\n\n/**\n * Checks if `value` is a host object in IE < 9.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a host object, else `false`.\n */\nfunction isHostObject(value) {\n // Many host objects are `Object` objects that can coerce to strings\n // despite having improperly defined `toString` methods.\n var result = false;\n if (value != null && typeof value.toString != 'function') {\n try {\n result = !!(value + '');\n } catch (e) {}\n }\n return result;\n}\n\n/**\n * Converts `map` to its key-value pairs.\n *\n * @private\n * @param {Object} map The map to convert.\n * @returns {Array} Returns the key-value pairs.\n */\nfunction mapToArray(map) {\n var index = -1,\n result = Array(map.size);\n\n map.forEach(function(value, key) {\n result[++index] = [key, value];\n });\n return result;\n}\n\n/**\n * Creates a unary function that invokes `func` with its argument transformed.\n *\n * @private\n * @param {Function} func The function to wrap.\n * @param {Function} transform The argument transform.\n * @returns {Function} Returns the new function.\n */\nfunction overArg(func, transform) {\n return function(arg) {\n return func(transform(arg));\n };\n}\n\n/**\n * Converts `set` to an array of its values.\n *\n * @private\n * @param {Object} set The set to convert.\n * @returns {Array} Returns the values.\n */\nfunction setToArray(set) {\n var index = -1,\n result = Array(set.size);\n\n set.forEach(function(value) {\n result[++index] = value;\n });\n return result;\n}\n\n/** Used for built-in method references. */\nvar arrayProto = Array.prototype,\n funcProto = Function.prototype,\n objectProto = Object.prototype;\n\n/** Used to detect overreaching core-js shims. */\nvar coreJsData = root['__core-js_shared__'];\n\n/** Used to detect methods masquerading as native. */\nvar maskSrcKey = (function() {\n var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');\n return uid ? ('Symbol(src)_1.' + uid) : '';\n}());\n\n/** Used to resolve the decompiled source of functions. */\nvar funcToString = funcProto.toString;\n\n/** Used to check objects for own properties. */\nvar hasOwnProperty = objectProto.hasOwnProperty;\n\n/**\n * Used to resolve the\n * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)\n * of values.\n */\nvar objectToString = objectProto.toString;\n\n/** Used to detect if a method is native. */\nvar reIsNative = RegExp('^' +\n funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\\\$&')\n .replace(/hasOwnProperty|(function).*?(?=\\\\\\()| for .+?(?=\\\\\\])/g, '$1.*?') + '$'\n);\n\n/** Built-in value references. */\nvar Symbol = root.Symbol,\n Uint8Array = root.Uint8Array,\n propertyIsEnumerable = objectProto.propertyIsEnumerable,\n splice = arrayProto.splice;\n\n/* Built-in method references for those with the same name as other `lodash` methods. */\nvar nativeKeys = overArg(Object.keys, Object);\n\n/* Built-in method references that are verified to be native. */\nvar DataView = getNative(root, 'DataView'),\n Map = getNative(root, 'Map'),\n Promise = getNative(root, 'Promise'),\n Set = getNative(root, 'Set'),\n WeakMap = getNative(root, 'WeakMap'),\n nativeCreate = getNative(Object, 'create');\n\n/** Used to detect maps, sets, and weakmaps. */\nvar dataViewCtorString = toSource(DataView),\n mapCtorString = toSource(Map),\n promiseCtorString = toSource(Promise),\n setCtorString = toSource(Set),\n weakMapCtorString = toSource(WeakMap);\n\n/** Used to convert symbols to primitives and strings. */\nvar symbolProto = Symbol ? Symbol.prototype : undefined,\n symbolValueOf = symbolProto ? symbolProto.valueOf : undefined,\n symbolToString = symbolProto ? symbolProto.toString : undefined;\n\n/**\n * Creates a hash object.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction Hash(entries) {\n var index = -1,\n length = entries ? entries.length : 0;\n\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n}\n\n/**\n * Removes all key-value entries from the hash.\n *\n * @private\n * @name clear\n * @memberOf Hash\n */\nfunction hashClear() {\n this.__data__ = nativeCreate ? nativeCreate(null) : {};\n}\n\n/**\n * Removes `key` and its value from the hash.\n *\n * @private\n * @name delete\n * @memberOf Hash\n * @param {Object} hash The hash to modify.\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction hashDelete(key) {\n return this.has(key) && delete this.__data__[key];\n}\n\n/**\n * Gets the hash value for `key`.\n *\n * @private\n * @name get\n * @memberOf Hash\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction hashGet(key) {\n var data = this.__data__;\n if (nativeCreate) {\n var result = data[key];\n return result === HASH_UNDEFINED ? undefined : result;\n }\n return hasOwnProperty.call(data, key) ? data[key] : undefined;\n}\n\n/**\n * Checks if a hash value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf Hash\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction hashHas(key) {\n var data = this.__data__;\n return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key);\n}\n\n/**\n * Sets the hash `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf Hash\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the hash instance.\n */\nfunction hashSet(key, value) {\n var data = this.__data__;\n data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;\n return this;\n}\n\n// Add methods to `Hash`.\nHash.prototype.clear = hashClear;\nHash.prototype['delete'] = hashDelete;\nHash.prototype.get = hashGet;\nHash.prototype.has = hashHas;\nHash.prototype.set = hashSet;\n\n/**\n * Creates an list cache object.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction ListCache(entries) {\n var index = -1,\n length = entries ? entries.length : 0;\n\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n}\n\n/**\n * Removes all key-value entries from the list cache.\n *\n * @private\n * @name clear\n * @memberOf ListCache\n */\nfunction listCacheClear() {\n this.__data__ = [];\n}\n\n/**\n * Removes `key` and its value from the list cache.\n *\n * @private\n * @name delete\n * @memberOf ListCache\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction listCacheDelete(key) {\n var data = this.__data__,\n index = assocIndexOf(data, key);\n\n if (index < 0) {\n return false;\n }\n var lastIndex = data.length - 1;\n if (index == lastIndex) {\n data.pop();\n } else {\n splice.call(data, index, 1);\n }\n return true;\n}\n\n/**\n * Gets the list cache value for `key`.\n *\n * @private\n * @name get\n * @memberOf ListCache\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction listCacheGet(key) {\n var data = this.__data__,\n index = assocIndexOf(data, key);\n\n return index < 0 ? undefined : data[index][1];\n}\n\n/**\n * Checks if a list cache value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf ListCache\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction listCacheHas(key) {\n return assocIndexOf(this.__data__, key) > -1;\n}\n\n/**\n * Sets the list cache `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf ListCache\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the list cache instance.\n */\nfunction listCacheSet(key, value) {\n var data = this.__data__,\n index = assocIndexOf(data, key);\n\n if (index < 0) {\n data.push([key, value]);\n } else {\n data[index][1] = value;\n }\n return this;\n}\n\n// Add methods to `ListCache`.\nListCache.prototype.clear = listCacheClear;\nListCache.prototype['delete'] = listCacheDelete;\nListCache.prototype.get = listCacheGet;\nListCache.prototype.has = listCacheHas;\nListCache.prototype.set = listCacheSet;\n\n/**\n * Creates a map cache object to store key-value pairs.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction MapCache(entries) {\n var index = -1,\n length = entries ? entries.length : 0;\n\n this.clear();\n while (++index < length) {\n var entry = entries[index];\n this.set(entry[0], entry[1]);\n }\n}\n\n/**\n * Removes all key-value entries from the map.\n *\n * @private\n * @name clear\n * @memberOf MapCache\n */\nfunction mapCacheClear() {\n this.__data__ = {\n 'hash': new Hash,\n 'map': new (Map || ListCache),\n 'string': new Hash\n };\n}\n\n/**\n * Removes `key` and its value from the map.\n *\n * @private\n * @name delete\n * @memberOf MapCache\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction mapCacheDelete(key) {\n return getMapData(this, key)['delete'](key);\n}\n\n/**\n * Gets the map value for `key`.\n *\n * @private\n * @name get\n * @memberOf MapCache\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction mapCacheGet(key) {\n return getMapData(this, key).get(key);\n}\n\n/**\n * Checks if a map value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf MapCache\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction mapCacheHas(key) {\n return getMapData(this, key).has(key);\n}\n\n/**\n * Sets the map `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf MapCache\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the map cache instance.\n */\nfunction mapCacheSet(key, value) {\n getMapData(this, key).set(key, value);\n return this;\n}\n\n// Add methods to `MapCache`.\nMapCache.prototype.clear = mapCacheClear;\nMapCache.prototype['delete'] = mapCacheDelete;\nMapCache.prototype.get = mapCacheGet;\nMapCache.prototype.has = mapCacheHas;\nMapCache.prototype.set = mapCacheSet;\n\n/**\n *\n * Creates an array cache object to store unique values.\n *\n * @private\n * @constructor\n * @param {Array} [values] The values to cache.\n */\nfunction SetCache(values) {\n var index = -1,\n length = values ? values.length : 0;\n\n this.__data__ = new MapCache;\n while (++index < length) {\n this.add(values[index]);\n }\n}\n\n/**\n * Adds `value` to the array cache.\n *\n * @private\n * @name add\n * @memberOf SetCache\n * @alias push\n * @param {*} value The value to cache.\n * @returns {Object} Returns the cache instance.\n */\nfunction setCacheAdd(value) {\n this.__data__.set(value, HASH_UNDEFINED);\n return this;\n}\n\n/**\n * Checks if `value` is in the array cache.\n *\n * @private\n * @name has\n * @memberOf SetCache\n * @param {*} value The value to search for.\n * @returns {number} Returns `true` if `value` is found, else `false`.\n */\nfunction setCacheHas(value) {\n return this.__data__.has(value);\n}\n\n// Add methods to `SetCache`.\nSetCache.prototype.add = SetCache.prototype.push = setCacheAdd;\nSetCache.prototype.has = setCacheHas;\n\n/**\n * Creates a stack cache object to store key-value pairs.\n *\n * @private\n * @constructor\n * @param {Array} [entries] The key-value pairs to cache.\n */\nfunction Stack(entries) {\n this.__data__ = new ListCache(entries);\n}\n\n/**\n * Removes all key-value entries from the stack.\n *\n * @private\n * @name clear\n * @memberOf Stack\n */\nfunction stackClear() {\n this.__data__ = new ListCache;\n}\n\n/**\n * Removes `key` and its value from the stack.\n *\n * @private\n * @name delete\n * @memberOf Stack\n * @param {string} key The key of the value to remove.\n * @returns {boolean} Returns `true` if the entry was removed, else `false`.\n */\nfunction stackDelete(key) {\n return this.__data__['delete'](key);\n}\n\n/**\n * Gets the stack value for `key`.\n *\n * @private\n * @name get\n * @memberOf Stack\n * @param {string} key The key of the value to get.\n * @returns {*} Returns the entry value.\n */\nfunction stackGet(key) {\n return this.__data__.get(key);\n}\n\n/**\n * Checks if a stack value for `key` exists.\n *\n * @private\n * @name has\n * @memberOf Stack\n * @param {string} key The key of the entry to check.\n * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.\n */\nfunction stackHas(key) {\n return this.__data__.has(key);\n}\n\n/**\n * Sets the stack `key` to `value`.\n *\n * @private\n * @name set\n * @memberOf Stack\n * @param {string} key The key of the value to set.\n * @param {*} value The value to set.\n * @returns {Object} Returns the stack cache instance.\n */\nfunction stackSet(key, value) {\n var cache = this.__data__;\n if (cache instanceof ListCache) {\n var pairs = cache.__data__;\n if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) {\n pairs.push([key, value]);\n return this;\n }\n cache = this.__data__ = new MapCache(pairs);\n }\n cache.set(key, value);\n return this;\n}\n\n// Add methods to `Stack`.\nStack.prototype.clear = stackClear;\nStack.prototype['delete'] = stackDelete;\nStack.prototype.get = stackGet;\nStack.prototype.has = stackHas;\nStack.prototype.set = stackSet;\n\n/**\n * Creates an array of the enumerable property names of the array-like `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @param {boolean} inherited Specify returning inherited property names.\n * @returns {Array} Returns the array of property names.\n */\nfunction arrayLikeKeys(value, inherited) {\n // Safari 8.1 makes `arguments.callee` enumerable in strict mode.\n // Safari 9 makes `arguments.length` enumerable in strict mode.\n var result = (isArray(value) || isArguments(value))\n ? baseTimes(value.length, String)\n : [];\n\n var length = result.length,\n skipIndexes = !!length;\n\n for (var key in value) {\n if ((inherited || hasOwnProperty.call(value, key)) &&\n !(skipIndexes && (key == 'length' || isIndex(key, length)))) {\n result.push(key);\n }\n }\n return result;\n}\n\n/**\n * Gets the index at which the `key` is found in `array` of key-value pairs.\n *\n * @private\n * @param {Array} array The array to inspect.\n * @param {*} key The key to search for.\n * @returns {number} Returns the index of the matched value, else `-1`.\n */\nfunction assocIndexOf(array, key) {\n var length = array.length;\n while (length--) {\n if (eq(array[length][0], key)) {\n return length;\n }\n }\n return -1;\n}\n\n/**\n * The base implementation of methods like `_.max` and `_.min` which accepts a\n * `comparator` to determine the extremum value.\n *\n * @private\n * @param {Array} array The array to iterate over.\n * @param {Function} iteratee The iteratee invoked per iteration.\n * @param {Function} comparator The comparator used to compare values.\n * @returns {*} Returns the extremum value.\n */\nfunction baseExtremum(array, iteratee, comparator) {\n var index = -1,\n length = array.length;\n\n while (++index < length) {\n var value = array[index],\n current = iteratee(value);\n\n if (current != null && (computed === undefined\n ? (current === current && !isSymbol(current))\n : comparator(current, computed)\n )) {\n var computed = current,\n result = value;\n }\n }\n return result;\n}\n\n/**\n * The base implementation of `_.get` without support for default values.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Array|string} path The path of the property to get.\n * @returns {*} Returns the resolved value.\n */\nfunction baseGet(object, path) {\n path = isKey(path, object) ? [path] : castPath(path);\n\n var index = 0,\n length = path.length;\n\n while (object != null && index < length) {\n object = object[toKey(path[index++])];\n }\n return (index && index == length) ? object : undefined;\n}\n\n/**\n * The base implementation of `getTag`.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\nfunction baseGetTag(value) {\n return objectToString.call(value);\n}\n\n/**\n * The base implementation of `_.gt` which doesn't coerce arguments.\n *\n * @private\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if `value` is greater than `other`,\n * else `false`.\n */\nfunction baseGt(value, other) {\n return value > other;\n}\n\n/**\n * The base implementation of `_.hasIn` without support for deep paths.\n *\n * @private\n * @param {Object} [object] The object to query.\n * @param {Array|string} key The key to check.\n * @returns {boolean} Returns `true` if `key` exists, else `false`.\n */\nfunction baseHasIn(object, key) {\n return object != null && key in Object(object);\n}\n\n/**\n * The base implementation of `_.isEqual` which supports partial comparisons\n * and tracks traversed objects.\n *\n * @private\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @param {Function} [customizer] The function to customize comparisons.\n * @param {boolean} [bitmask] The bitmask of comparison flags.\n * The bitmask may be composed of the following flags:\n * 1 - Unordered comparison\n * 2 - Partial comparison\n * @param {Object} [stack] Tracks traversed `value` and `other` objects.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n */\nfunction baseIsEqual(value, other, customizer, bitmask, stack) {\n if (value === other) {\n return true;\n }\n if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) {\n return value !== value && other !== other;\n }\n return baseIsEqualDeep(value, other, baseIsEqual, customizer, bitmask, stack);\n}\n\n/**\n * A specialized version of `baseIsEqual` for arrays and objects which performs\n * deep comparisons and tracks traversed objects enabling objects with circular\n * references to be compared.\n *\n * @private\n * @param {Object} object The object to compare.\n * @param {Object} other The other object to compare.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Function} [customizer] The function to customize comparisons.\n * @param {number} [bitmask] The bitmask of comparison flags. See `baseIsEqual`\n * for more details.\n * @param {Object} [stack] Tracks traversed `object` and `other` objects.\n * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.\n */\nfunction baseIsEqualDeep(object, other, equalFunc, customizer, bitmask, stack) {\n var objIsArr = isArray(object),\n othIsArr = isArray(other),\n objTag = arrayTag,\n othTag = arrayTag;\n\n if (!objIsArr) {\n objTag = getTag(object);\n objTag = objTag == argsTag ? objectTag : objTag;\n }\n if (!othIsArr) {\n othTag = getTag(other);\n othTag = othTag == argsTag ? objectTag : othTag;\n }\n var objIsObj = objTag == objectTag && !isHostObject(object),\n othIsObj = othTag == objectTag && !isHostObject(other),\n isSameTag = objTag == othTag;\n\n if (isSameTag && !objIsObj) {\n stack || (stack = new Stack);\n return (objIsArr || isTypedArray(object))\n ? equalArrays(object, other, equalFunc, customizer, bitmask, stack)\n : equalByTag(object, other, objTag, equalFunc, customizer, bitmask, stack);\n }\n if (!(bitmask & PARTIAL_COMPARE_FLAG)) {\n var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),\n othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');\n\n if (objIsWrapped || othIsWrapped) {\n var objUnwrapped = objIsWrapped ? object.value() : object,\n othUnwrapped = othIsWrapped ? other.value() : other;\n\n stack || (stack = new Stack);\n return equalFunc(objUnwrapped, othUnwrapped, customizer, bitmask, stack);\n }\n }\n if (!isSameTag) {\n return false;\n }\n stack || (stack = new Stack);\n return equalObjects(object, other, equalFunc, customizer, bitmask, stack);\n}\n\n/**\n * The base implementation of `_.isMatch` without support for iteratee shorthands.\n *\n * @private\n * @param {Object} object The object to inspect.\n * @param {Object} source The object of property values to match.\n * @param {Array} matchData The property names, values, and compare flags to match.\n * @param {Function} [customizer] The function to customize comparisons.\n * @returns {boolean} Returns `true` if `object` is a match, else `false`.\n */\nfunction baseIsMatch(object, source, matchData, customizer) {\n var index = matchData.length,\n length = index,\n noCustomizer = !customizer;\n\n if (object == null) {\n return !length;\n }\n object = Object(object);\n while (index--) {\n var data = matchData[index];\n if ((noCustomizer && data[2])\n ? data[1] !== object[data[0]]\n : !(data[0] in object)\n ) {\n return false;\n }\n }\n while (++index < length) {\n data = matchData[index];\n var key = data[0],\n objValue = object[key],\n srcValue = data[1];\n\n if (noCustomizer && data[2]) {\n if (objValue === undefined && !(key in object)) {\n return false;\n }\n } else {\n var stack = new Stack;\n if (customizer) {\n var result = customizer(objValue, srcValue, key, object, source, stack);\n }\n if (!(result === undefined\n ? baseIsEqual(srcValue, objValue, customizer, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG, stack)\n : result\n )) {\n return false;\n }\n }\n }\n return true;\n}\n\n/**\n * The base implementation of `_.isNative` without bad shim checks.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a native function,\n * else `false`.\n */\nfunction baseIsNative(value) {\n if (!isObject(value) || isMasked(value)) {\n return false;\n }\n var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor;\n return pattern.test(toSource(value));\n}\n\n/**\n * The base implementation of `_.isTypedArray` without Node.js optimizations.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.\n */\nfunction baseIsTypedArray(value) {\n return isObjectLike(value) &&\n isLength(value.length) && !!typedArrayTags[objectToString.call(value)];\n}\n\n/**\n * The base implementation of `_.iteratee`.\n *\n * @private\n * @param {*} [value=_.identity] The value to convert to an iteratee.\n * @returns {Function} Returns the iteratee.\n */\nfunction baseIteratee(value) {\n // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.\n // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.\n if (typeof value == 'function') {\n return value;\n }\n if (value == null) {\n return identity;\n }\n if (typeof value == 'object') {\n return isArray(value)\n ? baseMatchesProperty(value[0], value[1])\n : baseMatches(value);\n }\n return property(value);\n}\n\n/**\n * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n */\nfunction baseKeys(object) {\n if (!isPrototype(object)) {\n return nativeKeys(object);\n }\n var result = [];\n for (var key in Object(object)) {\n if (hasOwnProperty.call(object, key) && key != 'constructor') {\n result.push(key);\n }\n }\n return result;\n}\n\n/**\n * The base implementation of `_.matches` which doesn't clone `source`.\n *\n * @private\n * @param {Object} source The object of property values to match.\n * @returns {Function} Returns the new spec function.\n */\nfunction baseMatches(source) {\n var matchData = getMatchData(source);\n if (matchData.length == 1 && matchData[0][2]) {\n return matchesStrictComparable(matchData[0][0], matchData[0][1]);\n }\n return function(object) {\n return object === source || baseIsMatch(object, source, matchData);\n };\n}\n\n/**\n * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`.\n *\n * @private\n * @param {string} path The path of the property to get.\n * @param {*} srcValue The value to match.\n * @returns {Function} Returns the new spec function.\n */\nfunction baseMatchesProperty(path, srcValue) {\n if (isKey(path) && isStrictComparable(srcValue)) {\n return matchesStrictComparable(toKey(path), srcValue);\n }\n return function(object) {\n var objValue = get(object, path);\n return (objValue === undefined && objValue === srcValue)\n ? hasIn(object, path)\n : baseIsEqual(srcValue, objValue, undefined, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG);\n };\n}\n\n/**\n * A specialized version of `baseProperty` which supports deep paths.\n *\n * @private\n * @param {Array|string} path The path of the property to get.\n * @returns {Function} Returns the new accessor function.\n */\nfunction basePropertyDeep(path) {\n return function(object) {\n return baseGet(object, path);\n };\n}\n\n/**\n * The base implementation of `_.toString` which doesn't convert nullish\n * values to empty strings.\n *\n * @private\n * @param {*} value The value to process.\n * @returns {string} Returns the string.\n */\nfunction baseToString(value) {\n // Exit early for strings to avoid a performance hit in some environments.\n if (typeof value == 'string') {\n return value;\n }\n if (isSymbol(value)) {\n return symbolToString ? symbolToString.call(value) : '';\n }\n var result = (value + '');\n return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;\n}\n\n/**\n * Casts `value` to a path array if it's not one.\n *\n * @private\n * @param {*} value The value to inspect.\n * @returns {Array} Returns the cast property path array.\n */\nfunction castPath(value) {\n return isArray(value) ? value : stringToPath(value);\n}\n\n/**\n * A specialized version of `baseIsEqualDeep` for arrays with support for\n * partial deep comparisons.\n *\n * @private\n * @param {Array} array The array to compare.\n * @param {Array} other The other array to compare.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Function} customizer The function to customize comparisons.\n * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual`\n * for more details.\n * @param {Object} stack Tracks traversed `array` and `other` objects.\n * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.\n */\nfunction equalArrays(array, other, equalFunc, customizer, bitmask, stack) {\n var isPartial = bitmask & PARTIAL_COMPARE_FLAG,\n arrLength = array.length,\n othLength = other.length;\n\n if (arrLength != othLength && !(isPartial && othLength > arrLength)) {\n return false;\n }\n // Assume cyclic values are equal.\n var stacked = stack.get(array);\n if (stacked && stack.get(other)) {\n return stacked == other;\n }\n var index = -1,\n result = true,\n seen = (bitmask & UNORDERED_COMPARE_FLAG) ? new SetCache : undefined;\n\n stack.set(array, other);\n stack.set(other, array);\n\n // Ignore non-index properties.\n while (++index < arrLength) {\n var arrValue = array[index],\n othValue = other[index];\n\n if (customizer) {\n var compared = isPartial\n ? customizer(othValue, arrValue, index, other, array, stack)\n : customizer(arrValue, othValue, index, array, other, stack);\n }\n if (compared !== undefined) {\n if (compared) {\n continue;\n }\n result = false;\n break;\n }\n // Recursively compare arrays (susceptible to call stack limits).\n if (seen) {\n if (!arraySome(other, function(othValue, othIndex) {\n if (!seen.has(othIndex) &&\n (arrValue === othValue || equalFunc(arrValue, othValue, customizer, bitmask, stack))) {\n return seen.add(othIndex);\n }\n })) {\n result = false;\n break;\n }\n } else if (!(\n arrValue === othValue ||\n equalFunc(arrValue, othValue, customizer, bitmask, stack)\n )) {\n result = false;\n break;\n }\n }\n stack['delete'](array);\n stack['delete'](other);\n return result;\n}\n\n/**\n * A specialized version of `baseIsEqualDeep` for comparing objects of\n * the same `toStringTag`.\n *\n * **Note:** This function only supports comparing values with tags of\n * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.\n *\n * @private\n * @param {Object} object The object to compare.\n * @param {Object} other The other object to compare.\n * @param {string} tag The `toStringTag` of the objects to compare.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Function} customizer The function to customize comparisons.\n * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual`\n * for more details.\n * @param {Object} stack Tracks traversed `object` and `other` objects.\n * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.\n */\nfunction equalByTag(object, other, tag, equalFunc, customizer, bitmask, stack) {\n switch (tag) {\n case dataViewTag:\n if ((object.byteLength != other.byteLength) ||\n (object.byteOffset != other.byteOffset)) {\n return false;\n }\n object = object.buffer;\n other = other.buffer;\n\n case arrayBufferTag:\n if ((object.byteLength != other.byteLength) ||\n !equalFunc(new Uint8Array(object), new Uint8Array(other))) {\n return false;\n }\n return true;\n\n case boolTag:\n case dateTag:\n case numberTag:\n // Coerce booleans to `1` or `0` and dates to milliseconds.\n // Invalid dates are coerced to `NaN`.\n return eq(+object, +other);\n\n case errorTag:\n return object.name == other.name && object.message == other.message;\n\n case regexpTag:\n case stringTag:\n // Coerce regexes to strings and treat strings, primitives and objects,\n // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring\n // for more details.\n return object == (other + '');\n\n case mapTag:\n var convert = mapToArray;\n\n case setTag:\n var isPartial = bitmask & PARTIAL_COMPARE_FLAG;\n convert || (convert = setToArray);\n\n if (object.size != other.size && !isPartial) {\n return false;\n }\n // Assume cyclic values are equal.\n var stacked = stack.get(object);\n if (stacked) {\n return stacked == other;\n }\n bitmask |= UNORDERED_COMPARE_FLAG;\n\n // Recursively compare objects (susceptible to call stack limits).\n stack.set(object, other);\n var result = equalArrays(convert(object), convert(other), equalFunc, customizer, bitmask, stack);\n stack['delete'](object);\n return result;\n\n case symbolTag:\n if (symbolValueOf) {\n return symbolValueOf.call(object) == symbolValueOf.call(other);\n }\n }\n return false;\n}\n\n/**\n * A specialized version of `baseIsEqualDeep` for objects with support for\n * partial deep comparisons.\n *\n * @private\n * @param {Object} object The object to compare.\n * @param {Object} other The other object to compare.\n * @param {Function} equalFunc The function to determine equivalents of values.\n * @param {Function} customizer The function to customize comparisons.\n * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual`\n * for more details.\n * @param {Object} stack Tracks traversed `object` and `other` objects.\n * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.\n */\nfunction equalObjects(object, other, equalFunc, customizer, bitmask, stack) {\n var isPartial = bitmask & PARTIAL_COMPARE_FLAG,\n objProps = keys(object),\n objLength = objProps.length,\n othProps = keys(other),\n othLength = othProps.length;\n\n if (objLength != othLength && !isPartial) {\n return false;\n }\n var index = objLength;\n while (index--) {\n var key = objProps[index];\n if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) {\n return false;\n }\n }\n // Assume cyclic values are equal.\n var stacked = stack.get(object);\n if (stacked && stack.get(other)) {\n return stacked == other;\n }\n var result = true;\n stack.set(object, other);\n stack.set(other, object);\n\n var skipCtor = isPartial;\n while (++index < objLength) {\n key = objProps[index];\n var objValue = object[key],\n othValue = other[key];\n\n if (customizer) {\n var compared = isPartial\n ? customizer(othValue, objValue, key, other, object, stack)\n : customizer(objValue, othValue, key, object, other, stack);\n }\n // Recursively compare objects (susceptible to call stack limits).\n if (!(compared === undefined\n ? (objValue === othValue || equalFunc(objValue, othValue, customizer, bitmask, stack))\n : compared\n )) {\n result = false;\n break;\n }\n skipCtor || (skipCtor = key == 'constructor');\n }\n if (result && !skipCtor) {\n var objCtor = object.constructor,\n othCtor = other.constructor;\n\n // Non `Object` object instances with different constructors are not equal.\n if (objCtor != othCtor &&\n ('constructor' in object && 'constructor' in other) &&\n !(typeof objCtor == 'function' && objCtor instanceof objCtor &&\n typeof othCtor == 'function' && othCtor instanceof othCtor)) {\n result = false;\n }\n }\n stack['delete'](object);\n stack['delete'](other);\n return result;\n}\n\n/**\n * Gets the data for `map`.\n *\n * @private\n * @param {Object} map The map to query.\n * @param {string} key The reference key.\n * @returns {*} Returns the map data.\n */\nfunction getMapData(map, key) {\n var data = map.__data__;\n return isKeyable(key)\n ? data[typeof key == 'string' ? 'string' : 'hash']\n : data.map;\n}\n\n/**\n * Gets the property names, values, and compare flags of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @returns {Array} Returns the match data of `object`.\n */\nfunction getMatchData(object) {\n var result = keys(object),\n length = result.length;\n\n while (length--) {\n var key = result[length],\n value = object[key];\n\n result[length] = [key, value, isStrictComparable(value)];\n }\n return result;\n}\n\n/**\n * Gets the native function at `key` of `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {string} key The key of the method to get.\n * @returns {*} Returns the function if it's native, else `undefined`.\n */\nfunction getNative(object, key) {\n var value = getValue(object, key);\n return baseIsNative(value) ? value : undefined;\n}\n\n/**\n * Gets the `toStringTag` of `value`.\n *\n * @private\n * @param {*} value The value to query.\n * @returns {string} Returns the `toStringTag`.\n */\nvar getTag = baseGetTag;\n\n// Fallback for data views, maps, sets, and weak maps in IE 11,\n// for data views in Edge < 14, and promises in Node.js.\nif ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||\n (Map && getTag(new Map) != mapTag) ||\n (Promise && getTag(Promise.resolve()) != promiseTag) ||\n (Set && getTag(new Set) != setTag) ||\n (WeakMap && getTag(new WeakMap) != weakMapTag)) {\n getTag = function(value) {\n var result = objectToString.call(value),\n Ctor = result == objectTag ? value.constructor : undefined,\n ctorString = Ctor ? toSource(Ctor) : undefined;\n\n if (ctorString) {\n switch (ctorString) {\n case dataViewCtorString: return dataViewTag;\n case mapCtorString: return mapTag;\n case promiseCtorString: return promiseTag;\n case setCtorString: return setTag;\n case weakMapCtorString: return weakMapTag;\n }\n }\n return result;\n };\n}\n\n/**\n * Checks if `path` exists on `object`.\n *\n * @private\n * @param {Object} object The object to query.\n * @param {Array|string} path The path to check.\n * @param {Function} hasFunc The function to check properties.\n * @returns {boolean} Returns `true` if `path` exists, else `false`.\n */\nfunction hasPath(object, path, hasFunc) {\n path = isKey(path, object) ? [path] : castPath(path);\n\n var result,\n index = -1,\n length = path.length;\n\n while (++index < length) {\n var key = toKey(path[index]);\n if (!(result = object != null && hasFunc(object, key))) {\n break;\n }\n object = object[key];\n }\n if (result) {\n return result;\n }\n var length = object ? object.length : 0;\n return !!length && isLength(length) && isIndex(key, length) &&\n (isArray(object) || isArguments(object));\n}\n\n/**\n * Checks if `value` is a valid array-like index.\n *\n * @private\n * @param {*} value The value to check.\n * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.\n * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.\n */\nfunction isIndex(value, length) {\n length = length == null ? MAX_SAFE_INTEGER : length;\n return !!length &&\n (typeof value == 'number' || reIsUint.test(value)) &&\n (value > -1 && value % 1 == 0 && value < length);\n}\n\n/**\n * Checks if `value` is a property name and not a property path.\n *\n * @private\n * @param {*} value The value to check.\n * @param {Object} [object] The object to query keys on.\n * @returns {boolean} Returns `true` if `value` is a property name, else `false`.\n */\nfunction isKey(value, object) {\n if (isArray(value)) {\n return false;\n }\n var type = typeof value;\n if (type == 'number' || type == 'symbol' || type == 'boolean' ||\n value == null || isSymbol(value)) {\n return true;\n }\n return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||\n (object != null && value in Object(object));\n}\n\n/**\n * Checks if `value` is suitable for use as unique object key.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is suitable, else `false`.\n */\nfunction isKeyable(value) {\n var type = typeof value;\n return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')\n ? (value !== '__proto__')\n : (value === null);\n}\n\n/**\n * Checks if `func` has its source masked.\n *\n * @private\n * @param {Function} func The function to check.\n * @returns {boolean} Returns `true` if `func` is masked, else `false`.\n */\nfunction isMasked(func) {\n return !!maskSrcKey && (maskSrcKey in func);\n}\n\n/**\n * Checks if `value` is likely a prototype object.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.\n */\nfunction isPrototype(value) {\n var Ctor = value && value.constructor,\n proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;\n\n return value === proto;\n}\n\n/**\n * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` if suitable for strict\n * equality comparisons, else `false`.\n */\nfunction isStrictComparable(value) {\n return value === value && !isObject(value);\n}\n\n/**\n * A specialized version of `matchesProperty` for source values suitable\n * for strict equality comparisons, i.e. `===`.\n *\n * @private\n * @param {string} key The key of the property to get.\n * @param {*} srcValue The value to match.\n * @returns {Function} Returns the new spec function.\n */\nfunction matchesStrictComparable(key, srcValue) {\n return function(object) {\n if (object == null) {\n return false;\n }\n return object[key] === srcValue &&\n (srcValue !== undefined || (key in Object(object)));\n };\n}\n\n/**\n * Converts `string` to a property path array.\n *\n * @private\n * @param {string} string The string to convert.\n * @returns {Array} Returns the property path array.\n */\nvar stringToPath = memoize(function(string) {\n string = toString(string);\n\n var result = [];\n if (reLeadingDot.test(string)) {\n result.push('');\n }\n string.replace(rePropName, function(match, number, quote, string) {\n result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match));\n });\n return result;\n});\n\n/**\n * Converts `value` to a string key if it's not a string or symbol.\n *\n * @private\n * @param {*} value The value to inspect.\n * @returns {string|symbol} Returns the key.\n */\nfunction toKey(value) {\n if (typeof value == 'string' || isSymbol(value)) {\n return value;\n }\n var result = (value + '');\n return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;\n}\n\n/**\n * Converts `func` to its source code.\n *\n * @private\n * @param {Function} func The function to process.\n * @returns {string} Returns the source code.\n */\nfunction toSource(func) {\n if (func != null) {\n try {\n return funcToString.call(func);\n } catch (e) {}\n try {\n return (func + '');\n } catch (e) {}\n }\n return '';\n}\n\n/**\n * Creates a function that memoizes the result of `func`. If `resolver` is\n * provided, it determines the cache key for storing the result based on the\n * arguments provided to the memoized function. By default, the first argument\n * provided to the memoized function is used as the map cache key. The `func`\n * is invoked with the `this` binding of the memoized function.\n *\n * **Note:** The cache is exposed as the `cache` property on the memoized\n * function. Its creation may be customized by replacing the `_.memoize.Cache`\n * constructor with one whose instances implement the\n * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object)\n * method interface of `delete`, `get`, `has`, and `set`.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Function\n * @param {Function} func The function to have its output memoized.\n * @param {Function} [resolver] The function to resolve the cache key.\n * @returns {Function} Returns the new memoized function.\n * @example\n *\n * var object = { 'a': 1, 'b': 2 };\n * var other = { 'c': 3, 'd': 4 };\n *\n * var values = _.memoize(_.values);\n * values(object);\n * // => [1, 2]\n *\n * values(other);\n * // => [3, 4]\n *\n * object.a = 2;\n * values(object);\n * // => [1, 2]\n *\n * // Modify the result cache.\n * values.cache.set(object, ['a', 'b']);\n * values(object);\n * // => ['a', 'b']\n *\n * // Replace `_.memoize.Cache`.\n * _.memoize.Cache = WeakMap;\n */\nfunction memoize(func, resolver) {\n if (typeof func != 'function' || (resolver && typeof resolver != 'function')) {\n throw new TypeError(FUNC_ERROR_TEXT);\n }\n var memoized = function() {\n var args = arguments,\n key = resolver ? resolver.apply(this, args) : args[0],\n cache = memoized.cache;\n\n if (cache.has(key)) {\n return cache.get(key);\n }\n var result = func.apply(this, args);\n memoized.cache = cache.set(key, result);\n return result;\n };\n memoized.cache = new (memoize.Cache || MapCache);\n return memoized;\n}\n\n// Assign cache to `_.memoize`.\nmemoize.Cache = MapCache;\n\n/**\n * Performs a\n * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)\n * comparison between two values to determine if they are equivalent.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to compare.\n * @param {*} other The other value to compare.\n * @returns {boolean} Returns `true` if the values are equivalent, else `false`.\n * @example\n *\n * var object = { 'a': 1 };\n * var other = { 'a': 1 };\n *\n * _.eq(object, object);\n * // => true\n *\n * _.eq(object, other);\n * // => false\n *\n * _.eq('a', 'a');\n * // => true\n *\n * _.eq('a', Object('a'));\n * // => false\n *\n * _.eq(NaN, NaN);\n * // => true\n */\nfunction eq(value, other) {\n return value === other || (value !== value && other !== other);\n}\n\n/**\n * Checks if `value` is likely an `arguments` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an `arguments` object,\n * else `false`.\n * @example\n *\n * _.isArguments(function() { return arguments; }());\n * // => true\n *\n * _.isArguments([1, 2, 3]);\n * // => false\n */\nfunction isArguments(value) {\n // Safari 8.1 makes `arguments.callee` enumerable in strict mode.\n return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') &&\n (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag);\n}\n\n/**\n * Checks if `value` is classified as an `Array` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array, else `false`.\n * @example\n *\n * _.isArray([1, 2, 3]);\n * // => true\n *\n * _.isArray(document.body.children);\n * // => false\n *\n * _.isArray('abc');\n * // => false\n *\n * _.isArray(_.noop);\n * // => false\n */\nvar isArray = Array.isArray;\n\n/**\n * Checks if `value` is array-like. A value is considered array-like if it's\n * not a function and has a `value.length` that's an integer greater than or\n * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is array-like, else `false`.\n * @example\n *\n * _.isArrayLike([1, 2, 3]);\n * // => true\n *\n * _.isArrayLike(document.body.children);\n * // => true\n *\n * _.isArrayLike('abc');\n * // => true\n *\n * _.isArrayLike(_.noop);\n * // => false\n */\nfunction isArrayLike(value) {\n return value != null && isLength(value.length) && !isFunction(value);\n}\n\n/**\n * This method is like `_.isArrayLike` except that it also checks if `value`\n * is an object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an array-like object,\n * else `false`.\n * @example\n *\n * _.isArrayLikeObject([1, 2, 3]);\n * // => true\n *\n * _.isArrayLikeObject(document.body.children);\n * // => true\n *\n * _.isArrayLikeObject('abc');\n * // => false\n *\n * _.isArrayLikeObject(_.noop);\n * // => false\n */\nfunction isArrayLikeObject(value) {\n return isObjectLike(value) && isArrayLike(value);\n}\n\n/**\n * Checks if `value` is classified as a `Function` object.\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a function, else `false`.\n * @example\n *\n * _.isFunction(_);\n * // => true\n *\n * _.isFunction(/abc/);\n * // => false\n */\nfunction isFunction(value) {\n // The use of `Object#toString` avoids issues with the `typeof` operator\n // in Safari 8-9 which returns 'object' for typed array and other constructors.\n var tag = isObject(value) ? objectToString.call(value) : '';\n return tag == funcTag || tag == genTag;\n}\n\n/**\n * Checks if `value` is a valid array-like length.\n *\n * **Note:** This method is loosely based on\n * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.\n * @example\n *\n * _.isLength(3);\n * // => true\n *\n * _.isLength(Number.MIN_VALUE);\n * // => false\n *\n * _.isLength(Infinity);\n * // => false\n *\n * _.isLength('3');\n * // => false\n */\nfunction isLength(value) {\n return typeof value == 'number' &&\n value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;\n}\n\n/**\n * Checks if `value` is the\n * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)\n * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)\n *\n * @static\n * @memberOf _\n * @since 0.1.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is an object, else `false`.\n * @example\n *\n * _.isObject({});\n * // => true\n *\n * _.isObject([1, 2, 3]);\n * // => true\n *\n * _.isObject(_.noop);\n * // => true\n *\n * _.isObject(null);\n * // => false\n */\nfunction isObject(value) {\n var type = typeof value;\n return !!value && (type == 'object' || type == 'function');\n}\n\n/**\n * Checks if `value` is object-like. A value is object-like if it's not `null`\n * and has a `typeof` result of \"object\".\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is object-like, else `false`.\n * @example\n *\n * _.isObjectLike({});\n * // => true\n *\n * _.isObjectLike([1, 2, 3]);\n * // => true\n *\n * _.isObjectLike(_.noop);\n * // => false\n *\n * _.isObjectLike(null);\n * // => false\n */\nfunction isObjectLike(value) {\n return !!value && typeof value == 'object';\n}\n\n/**\n * Checks if `value` is classified as a `Symbol` primitive or object.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.\n * @example\n *\n * _.isSymbol(Symbol.iterator);\n * // => true\n *\n * _.isSymbol('abc');\n * // => false\n */\nfunction isSymbol(value) {\n return typeof value == 'symbol' ||\n (isObjectLike(value) && objectToString.call(value) == symbolTag);\n}\n\n/**\n * Checks if `value` is classified as a typed array.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Lang\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.\n * @example\n *\n * _.isTypedArray(new Uint8Array);\n * // => true\n *\n * _.isTypedArray([]);\n * // => false\n */\nvar isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;\n\n/**\n * Converts `value` to a string. An empty string is returned for `null`\n * and `undefined` values. The sign of `-0` is preserved.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Lang\n * @param {*} value The value to process.\n * @returns {string} Returns the string.\n * @example\n *\n * _.toString(null);\n * // => ''\n *\n * _.toString(-0);\n * // => '-0'\n *\n * _.toString([1, 2, 3]);\n * // => '1,2,3'\n */\nfunction toString(value) {\n return value == null ? '' : baseToString(value);\n}\n\n/**\n * Gets the value at `path` of `object`. If the resolved value is\n * `undefined`, the `defaultValue` is returned in its place.\n *\n * @static\n * @memberOf _\n * @since 3.7.0\n * @category Object\n * @param {Object} object The object to query.\n * @param {Array|string} path The path of the property to get.\n * @param {*} [defaultValue] The value returned for `undefined` resolved values.\n * @returns {*} Returns the resolved value.\n * @example\n *\n * var object = { 'a': [{ 'b': { 'c': 3 } }] };\n *\n * _.get(object, 'a[0].b.c');\n * // => 3\n *\n * _.get(object, ['a', '0', 'b', 'c']);\n * // => 3\n *\n * _.get(object, 'a.b.c', 'default');\n * // => 'default'\n */\nfunction get(object, path, defaultValue) {\n var result = object == null ? undefined : baseGet(object, path);\n return result === undefined ? defaultValue : result;\n}\n\n/**\n * Checks if `path` is a direct or inherited property of `object`.\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Object\n * @param {Object} object The object to query.\n * @param {Array|string} path The path to check.\n * @returns {boolean} Returns `true` if `path` exists, else `false`.\n * @example\n *\n * var object = _.create({ 'a': _.create({ 'b': 2 }) });\n *\n * _.hasIn(object, 'a');\n * // => true\n *\n * _.hasIn(object, 'a.b');\n * // => true\n *\n * _.hasIn(object, ['a', 'b']);\n * // => true\n *\n * _.hasIn(object, 'b');\n * // => false\n */\nfunction hasIn(object, path) {\n return object != null && hasPath(object, path, baseHasIn);\n}\n\n/**\n * Creates an array of the own enumerable property names of `object`.\n *\n * **Note:** Non-object values are coerced to objects. See the\n * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)\n * for more details.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Object\n * @param {Object} object The object to query.\n * @returns {Array} Returns the array of property names.\n * @example\n *\n * function Foo() {\n * this.a = 1;\n * this.b = 2;\n * }\n *\n * Foo.prototype.c = 3;\n *\n * _.keys(new Foo);\n * // => ['a', 'b'] (iteration order is not guaranteed)\n *\n * _.keys('hi');\n * // => ['0', '1']\n */\nfunction keys(object) {\n return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);\n}\n\n/**\n * This method returns the first argument it receives.\n *\n * @static\n * @since 0.1.0\n * @memberOf _\n * @category Util\n * @param {*} value Any value.\n * @returns {*} Returns `value`.\n * @example\n *\n * var object = { 'a': 1 };\n *\n * console.log(_.identity(object) === object);\n * // => true\n */\nfunction identity(value) {\n return value;\n}\n\n/**\n * Creates a function that returns the value at `path` of a given object.\n *\n * @static\n * @memberOf _\n * @since 2.4.0\n * @category Util\n * @param {Array|string} path The path of the property to get.\n * @returns {Function} Returns the new accessor function.\n * @example\n *\n * var objects = [\n * { 'a': { 'b': 2 } },\n * { 'a': { 'b': 1 } }\n * ];\n *\n * _.map(objects, _.property('a.b'));\n * // => [2, 1]\n *\n * _.map(_.sortBy(objects, _.property(['a', 'b'])), 'a.b');\n * // => [1, 2]\n */\nfunction property(path) {\n return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path);\n}\n\n/**\n * This method is like `_.max` except that it accepts `iteratee` which is\n * invoked for each element in `array` to generate the criterion by which\n * the value is ranked. The iteratee is invoked with one argument: (value).\n *\n * @static\n * @memberOf _\n * @since 4.0.0\n * @category Math\n * @param {Array} array The array to iterate over.\n * @param {Function} [iteratee=_.identity] The iteratee invoked per element.\n * @returns {*} Returns the maximum value.\n * @example\n *\n * var objects = [{ 'n': 1 }, { 'n': 2 }];\n *\n * _.maxBy(objects, function(o) { return o.n; });\n * // => { 'n': 2 }\n *\n * // The `_.property` iteratee shorthand.\n * _.maxBy(objects, 'n');\n * // => { 'n': 2 }\n */\nfunction maxBy(array, iteratee) {\n return (array && array.length)\n ? baseExtremum(array, baseIteratee(iteratee, 2), baseGt)\n : undefined;\n}\n\nmodule.exports = maxBy;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./~/lodash.maxby/index.js\n// module id = 20\n// module chunks = 0","module.exports = function(module) {\r\n\tif(!module.webpackPolyfill) {\r\n\t\tmodule.deprecate = function() {};\r\n\t\tmodule.paths = [];\r\n\t\t// module.parent = undefined by default\r\n\t\tmodule.children = [];\r\n\t\tmodule.webpackPolyfill = 1;\r\n\t}\r\n\treturn module;\r\n}\r\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// (webpack)/buildin/module.js\n// module id = 21\n// module chunks = 0","var elt = require('./utils/elt');\n\nmodule.exports = PageToolsOverlay;\n\nfunction PageToolsOverlay(pageIndex, viewerCore)\n{\n this.page = pageIndex;\n\n this._viewerCore = viewerCore;\n\n this._innerElement = viewerCore.getSettings().innerElement;\n this._pageToolsElem = null;\n}\n\nPageToolsOverlay.prototype.mount = function ()\n{\n if (this._pageToolsElem === null)\n {\n var buttons = this._initializePageToolButtons();\n\n this._pageToolsElem = elt('div', {class: 'diva-page-tools-wrapper'},\n elt('div', {class: 'diva-page-tools'}, buttons)\n );\n }\n\n this.refresh();\n this._innerElement.appendChild(this._pageToolsElem);\n};\n\nPageToolsOverlay.prototype._initializePageToolButtons = function ()\n{\n // Callback parameters\n var settings = this._viewerCore.getSettings();\n var publicInstance = this._viewerCore.getPublicInstance();\n var pageIndex = this.page;\n\n return this._viewerCore.getPageTools().map(function (plugin)\n {\n // If the title text is undefined, use the name of the plugin\n var titleText = plugin.titleText || plugin.pluginName[0].toUpperCase() + plugin.pluginName.substring(1) + \" plugin\";\n\n var button = elt('div', {\n class: 'diva-' + plugin.pluginName + '-icon',\n title: titleText\n });\n\n button.addEventListener('click', function (event)\n {\n plugin.handleClick.call(this, event, settings, publicInstance, pageIndex);\n }, false);\n\n button.addEventListener('touchend', function (event)\n {\n // Prevent firing of emulated mouse events\n event.preventDefault();\n\n plugin.handleClick.call(this, event, settings, publicInstance, pageIndex);\n }, false);\n\n return button;\n }, this);\n};\n\nPageToolsOverlay.prototype.unmount = function ()\n{\n this._innerElement.removeChild(this._pageToolsElem);\n};\n\nPageToolsOverlay.prototype.refresh = function ()\n{\n var pos = this._viewerCore.getPageRegion(this.page, {\n excludePadding: true,\n incorporateViewport: true\n });\n\n this._pageToolsElem.style.top = pos.top + 'px';\n this._pageToolsElem.style.left = pos.left + 'px';\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/page-tools-overlay.js\n// module id = 22\n// module chunks = 0","var maxBy = require('lodash.maxby');\n\nmodule.exports = GridHandler;\n\nfunction GridHandler(viewerCore)\n{\n this._viewerCore = viewerCore;\n}\n\n// USER EVENTS\nGridHandler.prototype.onDoubleClick = function (event, coords)\n{\n var position = this._viewerCore.getPagePositionAtViewportOffset(coords);\n\n var layout = this._viewerCore.getCurrentLayout();\n var viewport = this._viewerCore.getViewport();\n var pageToViewportCenterOffset = layout.getPageToViewportCenterOffset(position.anchorPage, viewport);\n\n this._viewerCore.reload({\n inGrid: false,\n goDirectlyTo: position.anchorPage,\n horizontalOffset: pageToViewportCenterOffset.x + position.offset.left,\n verticalOffset: pageToViewportCenterOffset.y + position.offset.top\n });\n};\n\nGridHandler.prototype.onPinch = function ()\n{\n this._viewerCore.reload({ inGrid: false });\n};\n\n// VIEW EVENTS\nGridHandler.prototype.onViewWillLoad = function ()\n{\n // FIXME(wabain): Should something happen here?\n /* No-op */\n};\n\nGridHandler.prototype.onViewDidLoad = function ()\n{\n // FIXME(wabain): Should something happen here?\n /* No-op */\n};\n\nGridHandler.prototype.onViewDidUpdate = function (renderedPages, targetPage)\n{\n // return early if there are no rendered pages in view.\n if (renderedPages.length === 0) return;\n\n if (targetPage !== null)\n {\n this._viewerCore.setCurrentPage(targetPage);\n return;\n }\n\n // Select the current page from the first row if it is fully visible, or from\n // the second row if it is fully visible, or from the centermost row otherwise.\n // If the current page is in that group then don't change it. Otherwise, set\n // the current page to the group's first page.\n\n var layout = this._viewerCore.getCurrentLayout();\n var groups = [];\n\n renderedPages.forEach(function (pageIndex)\n {\n var group = layout.getPageInfo(pageIndex).group;\n if (groups.length === 0 || group !== groups[groups.length - 1])\n groups.push(group);\n });\n\n var viewport = this._viewerCore.getViewport();\n var chosenGroup;\n\n if (groups.length === 1 || groups[0].region.top >= viewport.top)\n chosenGroup = groups[0];\n else if (groups[1].region.bottom <= viewport.bottom)\n chosenGroup = groups[1];\n else\n chosenGroup = getCentermostGroup(groups, viewport);\n\n var currentPage = this._viewerCore.getSettings().currentPageIndex;\n\n var hasCurrentPage = chosenGroup.pages.some(function (page)\n {\n return page.index === currentPage;\n });\n\n if (!hasCurrentPage)\n this._viewerCore.setCurrentPage(chosenGroup.pages[0].index);\n};\n\nGridHandler.prototype.destroy = function ()\n{\n // No-op\n};\n\nfunction getCentermostGroup(groups, viewport)\n{\n var viewportMiddle = viewport.top + viewport.height / 2;\n\n return maxBy(groups, function (group)\n {\n var groupMiddle = group.region.top + group.dimensions.height / 2;\n return -Math.abs(viewportMiddle - groupMiddle);\n });\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/grid-handler.js\n// module id = 23\n// module chunks = 0","module.exports = PageOverlayManager;\n\n/**\n * Manages a collection of page overlays, which implement a low-level\n * API for synchronizing HTML pages to the canvas. Each overlay needs\n * to implement the following protocol:\n *\n * mount(): Called when a page is first rendered\n * refresh(): Called when a page is moved\n * unmount(): Called when a previously rendered page has stopped being rendered\n *\n * @class\n */\n\nfunction PageOverlayManager()\n{\n this._pages = {};\n this._renderedPages = [];\n this._renderedPageMap = {};\n}\n\nPageOverlayManager.prototype.addOverlay = function (overlay)\n{\n var overlaysByPage = this._pages[overlay.page] || (this._pages[overlay.page] = []);\n\n overlaysByPage.push(overlay);\n\n if (this._renderedPageMap[overlay.page])\n overlay.mount();\n};\n\nPageOverlayManager.prototype.removeOverlay = function (overlay)\n{\n var page = overlay.page;\n var overlaysByPage = this._pages[page];\n\n if (!overlaysByPage)\n return;\n\n var overlayIndex = overlaysByPage.indexOf(overlay);\n\n if (overlayIndex === -1)\n return;\n\n if (this._renderedPageMap[page])\n overlaysByPage[overlayIndex].unmount();\n\n overlaysByPage.splice(overlayIndex, 1);\n\n if (overlaysByPage.length === 0)\n delete this._pages[page];\n};\n\nPageOverlayManager.prototype.updateOverlays = function (renderedPages)\n{\n var previouslyRendered = this._renderedPages;\n var newRenderedMap = {};\n\n renderedPages.forEach(function (pageIndex)\n {\n newRenderedMap[pageIndex] = true;\n\n if (!this._renderedPageMap[pageIndex])\n {\n this._renderedPageMap[pageIndex] = true;\n\n this._invokeOnOverlays(pageIndex, function (overlay)\n {\n overlay.mount();\n });\n }\n }, this);\n\n previouslyRendered.forEach(function (pageIndex)\n {\n if (newRenderedMap[pageIndex])\n {\n this._invokeOnOverlays(pageIndex, function (overlay)\n {\n overlay.refresh();\n });\n }\n else\n {\n delete this._renderedPageMap[pageIndex];\n\n this._invokeOnOverlays(pageIndex, function (overlay)\n {\n overlay.unmount();\n });\n }\n }, this);\n\n this._renderedPages = renderedPages;\n};\n\nPageOverlayManager.prototype._invokeOnOverlays = function (pageIndex, func)\n{\n var overlays = this._pages[pageIndex];\n if (overlays)\n overlays.forEach(func, this);\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/page-overlay-manager.js\n// module id = 24\n// module chunks = 0","'use strict';\n\nvar debug = require('debug')('diva:Renderer');\nvar debugPaints = require('debug')('diva:Renderer:paints');\n\nvar elt = require('./utils/elt');\n\nvar CompositeImage = require('./composite-image');\nvar DocumentLayout = require('./document-layout');\nvar ImageCache = require('./image-cache');\nvar ImageRequestHandler = require('./image-request-handler');\nvar InterpolateAnimation = require('./interpolate-animation');\n\nvar REQUEST_DEBOUNCE_INTERVAL = 250;\n\n\nmodule.exports = Renderer;\n\nfunction Renderer(options, hooks)\n{\n this._viewport = options.viewport;\n this._outerElement = options.outerElement;\n this._documentElement = options.innerElement;\n\n this._hooks = hooks || {};\n\n this._canvas = elt('canvas', { class: 'diva-viewer-canvas', tabindex: '1' });\n this._ctx = this._canvas.getContext('2d');\n\n this.layout = null;\n\n this._sourceResolver = null;\n this._renderedPages = null;\n this._config = null;\n this._zoomLevel = null;\n this._compositeImages = null;\n this._renderedTiles = null;\n this._animation = null;\n\n // FIXME(wabain): What level should this be maintained at?\n // Diva global?\n this._cache = new ImageCache();\n this._pendingRequests = {};\n}\n\nRenderer.getCompatibilityErrors = function ()\n{\n if (typeof HTMLCanvasElement !== 'undefined')\n return null;\n\n return [\n 'Your browser lacks support for the ', elt('pre', 'canvas'),\n ' element. Please upgrade your browser.'\n ];\n};\n\n/*\n* Method supplied by Joseph Jezerinac, https://github.com/jezerinac\n*\n* https://github.com/DDMAL/diva.js/pull/370\n*\n* Given a pair of x,y co-ordinates, translate that to a position on a rendered\n* page. Returns an object containing the page index, a percentage of the w/h of\n* the image, and the absolute value of the hit point on the image, e.g., a click in\n* the top left corner of the image would return 0,0.\n*\n**/\nRenderer.prototype.getPageHit = function (clientX, clientY)\n{\n var bounds = this._outerElement.getBoundingClientRect();\n if (clientX < bounds.left || clientY < bounds.top ||\n clientX > bounds.left + bounds.width || clientY > bounds.top + bounds.height)\n {\n return null;\n }\n\n clientX -= bounds.left;\n clientY -= bounds.top;\n\n var numRenderedPages = this._renderedPages.length;\n\n for (var i = 0; i < numRenderedPages; i++)\n {\n var pageIndex = this._renderedPages[i];\n var pageInfo = this.layout.getPageInfo(pageIndex);\n var pageOffset = this._getImageOffset(pageIndex);\n\n var viewportPaddingX = Math.max(0, (this._viewport.width - this.layout.dimensions.width) / 2);\n var viewportPaddingY = Math.max(0, (this._viewport.height - this.layout.dimensions.height) / 2);\n\n var viewportOffsetX = pageOffset.left - this._viewport.left + viewportPaddingX;\n var viewportOffsetY = pageOffset.top - this._viewport.top + viewportPaddingY;\n\n var destXOffset = viewportOffsetX < 0 ? -viewportOffsetX : 0;\n var destYOffset = viewportOffsetY < 0 ? -viewportOffsetY : 0;\n\n var canvasX = Math.max(0, viewportOffsetX);\n var canvasY = Math.max(0, viewportOffsetY);\n\n var destWidth = pageInfo.dimensions.width - destXOffset;\n var destHeight = pageInfo.dimensions.height - destYOffset;\n\n if (clientX >= canvasX && clientX <= canvasX + destWidth && clientY >= canvasY && clientY <= canvasY + destHeight)\n {\n var xhp = ((clientX + destXOffset) - canvasX);\n var yhp = ((clientY + destYOffset) - canvasY);\n /* To get the percentage x and y you need to adjust the by the scroll offset and the canvas position\n Also returns the absolute x and y at a given zoom level; these can be adjusted by clients later\n using the translateFromMaxZoomLevel / translateToMaxZoomLevel methods.\n */\n return {\n pg: pageIndex,\n pctx: xhp / pageInfo.dimensions.width,\n pcty: yhp / pageInfo.dimensions.height,\n x: xhp,\n y: yhp\n };\n }\n }\n\n return null;\n};\n\nRenderer.prototype.load = function (config, viewportPosition, sourceResolver)\n{\n this._clearAnimation();\n\n if (this._hooks.onViewWillLoad)\n this._hooks.onViewWillLoad();\n\n this._sourceResolver = sourceResolver;\n this._config = config;\n this._compositeImages = {};\n this._setLayoutToZoomLevel(viewportPosition.zoomLevel);\n\n // FIXME(wabain): Remove this when there's more confidence the check shouldn't be needed\n if (!this.layout.getPageInfo(viewportPosition.anchorPage))\n throw new Error('invalid page: ' + viewportPosition.anchorPage);\n\n if (this._canvas.width !== this._viewport.width || this._canvas.height !== this._viewport.height)\n {\n debug('Canvas dimension change: (%s, %s) -> (%s, %s)', this._canvas.width, this._canvas.height,\n this._viewport.width, this._viewport.height);\n\n this._canvas.width = this._viewport.width;\n this._canvas.height = this._viewport.height;\n } else {\n debug('Reload, no size change');\n }\n\n // FIXME: What hooks should be called here?\n this.goto(viewportPosition.anchorPage, viewportPosition.verticalOffset, viewportPosition.horizontalOffset);\n\n if (this._canvas.parentNode !== this._outerElement)\n this._outerElement.insertBefore(this._canvas, this._outerElement.firstChild);\n\n if (this._hooks.onViewDidLoad)\n this._hooks.onViewDidLoad();\n};\n\nRenderer.prototype._setViewportPosition = function (viewportPosition)\n{\n if (viewportPosition.zoomLevel !== this._zoomLevel)\n {\n if (this._zoomLevel === null)\n throw new TypeError('The current view is not zoomable');\n else if (viewportPosition.zoomLevel === null)\n throw new TypeError('The current view requires a zoom level');\n\n this._setLayoutToZoomLevel(viewportPosition.zoomLevel);\n }\n\n this._goto(viewportPosition.anchorPage, viewportPosition.verticalOffset, viewportPosition.horizontalOffset);\n};\n\nRenderer.prototype._setLayoutToZoomLevel = function (zoomLevel)\n{\n this.layout = new DocumentLayout(this._config, zoomLevel);\n this._zoomLevel = zoomLevel;\n\n elt.setAttributes(this._documentElement, {\n style: {\n height: this.layout.dimensions.height + 'px',\n width: this.layout.dimensions.width + 'px'\n }\n });\n\n this._viewport.setInnerDimensions(this.layout.dimensions);\n};\n\nRenderer.prototype.adjust = function (direction)\n{\n this._clearAnimation();\n\n this._render(direction);\n\n if (this._hooks.onViewDidUpdate)\n {\n this._hooks.onViewDidUpdate(this._renderedPages.slice(), null);\n }\n};\n\n\n\n// FIXME(wabain): Remove the direction argument if it doesn't end up being needed.\nRenderer.prototype._render = function (direction) // jshint ignore:line\n{\n var newRenderedPages = [];\n this.layout.pageGroups.forEach(function (group)\n {\n if (!this._viewport.intersectsRegion(group.region))\n return;\n\n var visiblePages = group.pages\n .filter(function (page)\n {\n return this.isPageVisible(page.index);\n }, this)\n .map(function (page)\n {\n return page.index;\n });\n\n newRenderedPages.push.apply(newRenderedPages, visiblePages);\n }, this);\n\n this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);\n this._paintOutline(newRenderedPages);\n\n newRenderedPages.forEach(function (pageIndex)\n {\n if (!this._compositeImages[pageIndex])\n {\n var page = this.layout.getPageInfo(pageIndex);\n var zoomLevels = this._sourceResolver.getAllZoomLevelsForPage(page);\n var composite = new CompositeImage(zoomLevels);\n composite.updateFromCache(this._cache);\n this._compositeImages[pageIndex] = composite;\n }\n }, this);\n\n this._initiateTileRequests(newRenderedPages);\n\n var changes = findChanges(this._renderedPages || [], newRenderedPages);\n\n changes.removed.forEach(function (pageIndex)\n {\n delete this._compositeImages[pageIndex];\n }, this);\n\n this._renderedPages = newRenderedPages;\n this._paint();\n\n if (this._hooks.onPageWillLoad)\n {\n changes.added.forEach(function (pageIndex)\n {\n this._hooks.onPageWillLoad(pageIndex);\n }, this);\n }\n};\n\nRenderer.prototype._paint = function ()\n{\n debug('Repainting');\n\n var renderedTiles = [];\n\n this._renderedPages.forEach(function (pageIndex)\n {\n this._compositeImages[pageIndex].getTiles(this._zoomLevel).forEach(function (source)\n {\n var scaled = getScaledTileRecord(source, this._zoomLevel);\n\n if (this._isTileVisible(pageIndex, scaled))\n {\n renderedTiles.push(source.url);\n this._drawTile(pageIndex, scaled, this._cache.get(source.url));\n }\n }, this);\n }, this);\n\n var cache = this._cache;\n\n var changes = findChanges(this._renderedTiles || [], renderedTiles);\n\n changes.added.forEach(function (url)\n {\n cache.acquire(url);\n });\n\n changes.removed.forEach(function (url)\n {\n cache.release(url);\n });\n\n if (changes.removed)\n {\n // FIXME: Should only need to update the composite images\n // for which tiles were removed\n this._renderedPages.forEach(function (pageIndex)\n {\n this._compositeImages[pageIndex].updateFromCache(this._cache);\n }, this);\n }\n\n this._renderedTiles = renderedTiles;\n};\n\n// Paint a page outline while the tiles are loading.\nRenderer.prototype._paintOutline = function (pages)\n{\n pages.forEach(function (pageIndex)\n {\n var pageInfo = this.layout.getPageInfo(pageIndex);\n var pageOffset = this._getImageOffset(pageIndex);\n\n // Ensure the document is drawn to the center of the viewport\n var viewportPaddingX = Math.max(0, (this._viewport.width - this.layout.dimensions.width) / 2);\n var viewportPaddingY = Math.max(0, (this._viewport.height - this.layout.dimensions.height) / 2);\n\n var viewportOffsetX = pageOffset.left - this._viewport.left + viewportPaddingX;\n var viewportOffsetY = pageOffset.top - this._viewport.top + viewportPaddingY;\n\n var destXOffset = viewportOffsetX < 0 ? -viewportOffsetX : 0;\n var destYOffset = viewportOffsetY < 0 ? -viewportOffsetY : 0;\n\n var canvasX = Math.max(0, viewportOffsetX);\n var canvasY = Math.max(0, viewportOffsetY);\n\n var destWidth = pageInfo.dimensions.width - destXOffset;\n var destHeight = pageInfo.dimensions.height - destYOffset;\n\n this._ctx.strokeStyle = '#AAA';\n // In order to get a 1px wide line using strokes, we need to start at a 'half pixel'\n this._ctx.strokeRect(canvasX + 0.5, canvasY + 0.5, destWidth, destHeight);\n }, this);\n};\n\n// This method should be sent all visible pages at once because it will initiate\n// all image requests and cancel any remaining image requests. In the case that\n// a request is ongoing and the tile is still visible in the viewport, the old request\n// is kept active instead of restarting it. The image requests are given a timeout\n// before loading in order to debounce them and have a small reaction time\n// to cancel them and avoid useless requests.\nRenderer.prototype._initiateTileRequests = function(pages)\n{\n // Only requests in this object are kept alive, since all others are not visible in the viewport\n var newPendingRequests = {};\n\n // Used later as a closure to initiate the image requests with the right source and pageIndex\n var initiateRequest = function (source, pageIndex)\n {\n var composite = this._compositeImages[pageIndex];\n\n newPendingRequests[source.url] = new ImageRequestHandler({\n url: source.url,\n timeoutTime: REQUEST_DEBOUNCE_INTERVAL,\n load: function (img)\n {\n delete this._pendingRequests[source.url];\n this._cache.put(source.url, img);\n\n // Awkward way to check for updates\n if (composite === this._compositeImages[pageIndex])\n {\n composite.updateWithLoadedUrls([source.url]);\n\n if (this._isTileForSourceVisible(pageIndex, source))\n this._paint();\n else\n debugPaints('Page %s, tile %s no longer visible on image load', pageIndex, source.url);\n }\n }.bind(this),\n error: function ()\n {\n // TODO: Could make a limited number of retries, etc.\n delete this._pendingRequests[source.url];\n }.bind(this)\n });\n }.bind(this);\n\n for (var i = 0; i < pages.length; i++)\n {\n var pageIndex = pages[i];\n var tiles = this._sourceResolver.getBestZoomLevelForPage(this.layout.getPageInfo(pageIndex)).tiles;\n\n for (var j = 0; j < tiles.length; j++)\n {\n var source = tiles[j];\n if (this._cache.has(source.url) || !this._isTileForSourceVisible(pageIndex, source))\n continue;\n\n // Don't create a new request if the tile is already being loaded\n if (this._pendingRequests[source.url])\n {\n newPendingRequests[source.url] = this._pendingRequests[source.url];\n delete this._pendingRequests[source.url];\n continue;\n }\n\n // Use a closure since the load and error methods are going to be called later and\n // we need to keep the right reference to the source and the page index\n initiateRequest(source, pageIndex);\n }\n }\n\n for (var url in this._pendingRequests)\n this._pendingRequests[url].abort();\n this._pendingRequests = newPendingRequests;\n};\n\nRenderer.prototype._drawTile = function (pageIndex, scaledTile, img)\n{\n var tileOffset = this._getTileToDocumentOffset(pageIndex, scaledTile);\n\n // Ensure the document is drawn to the center of the viewport\n var viewportPaddingX = Math.max(0, (this._viewport.width - this.layout.dimensions.width) / 2);\n var viewportPaddingY = Math.max(0, (this._viewport.height - this.layout.dimensions.height) / 2);\n\n var viewportOffsetX = tileOffset.left - this._viewport.left + viewportPaddingX;\n var viewportOffsetY = tileOffset.top - this._viewport.top + viewportPaddingY;\n\n var destXOffset = viewportOffsetX < 0 ? -viewportOffsetX : 0;\n var destYOffset = viewportOffsetY < 0 ? -viewportOffsetY : 0;\n\n var sourceXOffset = destXOffset / scaledTile.scaleRatio;\n var sourceYOffset = destYOffset / scaledTile.scaleRatio;\n\n var canvasX = Math.max(0, viewportOffsetX);\n var canvasY = Math.max(0, viewportOffsetY);\n\n // Ensure that the specified dimensions are no greater than the actual\n // size of the image. Safari won't display the tile if they are.\n var destWidth = Math.min(scaledTile.dimensions.width, img.width * scaledTile.scaleRatio) - destXOffset;\n var destHeight = Math.min(scaledTile.dimensions.height, img.height * scaledTile.scaleRatio) - destYOffset;\n\n destWidth = Math.max(1, destWidth);\n destHeight = Math.max(1, destHeight);\n\n var sourceWidth = Math.abs(destWidth / scaledTile.scaleRatio);\n var sourceHeight = Math.abs(destHeight / scaledTile.scaleRatio);\n\n if (debugPaints.enabled)\n {\n debugPaints('Drawing page %s, tile %sx (%s, %s) from %s, %s to viewport at %s, %s, scale %s%%',\n pageIndex,\n scaledTile.sourceZoomLevel, scaledTile.row, scaledTile.col,\n sourceXOffset, sourceYOffset,\n canvasX, canvasY,\n Math.round(scaledTile.scaleRatio * 100));\n }\n\n this._ctx.drawImage(\n img,\n sourceXOffset, sourceYOffset,\n sourceWidth, sourceHeight,\n canvasX, canvasY,\n destWidth, destHeight);\n};\n\nRenderer.prototype._isTileForSourceVisible = function (pageIndex, tileSource)\n{\n return this._isTileVisible(pageIndex, getScaledTileRecord(tileSource, this._zoomLevel));\n};\n\nRenderer.prototype._isTileVisible = function (pageIndex, scaledTile)\n{\n var tileOffset = this._getTileToDocumentOffset(pageIndex, scaledTile);\n\n // FIXME(wabain): This check is insufficient during a zoom transition\n return this._viewport.intersectsRegion({\n top: tileOffset.top,\n bottom: tileOffset.top + scaledTile.dimensions.height,\n left: tileOffset.left,\n right: tileOffset.left + scaledTile.dimensions.width\n });\n};\n\nRenderer.prototype._getTileToDocumentOffset = function (pageIndex, scaledTile)\n{\n var imageOffset = this._getImageOffset(pageIndex);\n\n return {\n top: imageOffset.top + scaledTile.offset.top,\n left: imageOffset.left + scaledTile.offset.left\n };\n};\n\nRenderer.prototype._getImageOffset = function (pageIndex)\n{\n return this.layout.getPageOffset(pageIndex, {excludePadding: true});\n};\n\n// TODO: Update signature\nRenderer.prototype.goto = function (pageIndex, verticalOffset, horizontalOffset)\n{\n this._clearAnimation();\n this._goto(pageIndex, verticalOffset, horizontalOffset);\n if (this._hooks.onViewDidUpdate)\n {\n this._hooks.onViewDidUpdate(this._renderedPages.slice(), pageIndex);\n }\n};\n\nRenderer.prototype._goto = function (pageIndex, verticalOffset, horizontalOffset)\n{\n // FIXME(wabain): Move this logic to the viewer\n var pageOffset = this.layout.getPageOffset(pageIndex);\n\n var desiredVerticalCenter = pageOffset.top + verticalOffset;\n var top = desiredVerticalCenter - parseInt(this._viewport.height / 2, 10);\n\n var desiredHorizontalCenter = pageOffset.left + horizontalOffset;\n var left = desiredHorizontalCenter - parseInt(this._viewport.width / 2, 10);\n\n this._viewport.top = top;\n this._viewport.left = left;\n\n this._render(0);\n};\n\nRenderer.prototype.transitionViewportPosition = function (options)\n{\n this._clearAnimation();\n\n var getPosition = options.getPosition;\n var self = this;\n\n var onViewDidTransition = this._hooks.onViewDidTransition;\n\n this._animation = InterpolateAnimation.animate({\n duration: options.duration,\n parameters: options.parameters,\n onUpdate: function (values)\n {\n // TODO: Do image preloading, work with that\n self._setViewportPosition(getPosition(values));\n\n if (onViewDidTransition)\n onViewDidTransition();\n },\n onEnd: function (info)\n {\n if (options.onEnd)\n options.onEnd(info);\n\n if (self._hooks.onViewDidUpdate && !info.interrupted)\n {\n self._hooks.onViewDidUpdate(self._renderedPages.slice(), null);\n }\n }\n });\n};\n\nRenderer.prototype._clearAnimation = function ()\n{\n if (this._animation)\n {\n this._animation.cancel();\n this._animation = null;\n }\n};\n\nRenderer.prototype.preload = function ()\n{\n // TODO\n};\n\nRenderer.prototype.isPageVisible = function (pageIndex)\n{\n if (!this.layout)\n return false;\n\n var page = this.layout.getPageInfo(pageIndex);\n\n if (!page)\n return false;\n\n return this._viewport.intersectsRegion(this.layout.getPageRegion(pageIndex));\n};\n\nRenderer.prototype.getRenderedPages = function ()\n{\n return this._renderedPages.slice();\n};\n\nRenderer.prototype.destroy = function ()\n{\n this._clearAnimation();\n\n // FIXME(wabain): I don't know if we should actually do this\n Object.keys(this._pendingRequests).forEach(function (req)\n {\n var handler = this._pendingRequests[req];\n delete this._pendingRequests[req];\n\n handler.abort();\n }, this);\n\n this._canvas.parentNode.removeChild(this._canvas);\n};\n\nfunction getScaledTileRecord(source, scaleFactor)\n{\n var scaleRatio;\n\n if (scaleFactor === null)\n scaleRatio = 1;\n else\n scaleRatio = Math.pow(2, scaleFactor - source.zoomLevel);\n\n return {\n sourceZoomLevel: source.zoomLevel,\n scaleRatio: scaleRatio,\n row: source.row,\n col: source.col,\n dimensions: {\n width: source.dimensions.width * scaleRatio,\n height: source.dimensions.height * scaleRatio\n },\n offset: {\n left: source.offset.left * scaleRatio,\n top: source.offset.top * scaleRatio\n },\n url: source.url\n };\n}\n\nfunction findChanges(oldArray, newArray)\n{\n if (oldArray === newArray)\n {\n return {\n added: [],\n removed: []\n };\n }\n\n var removed = oldArray.filter(function (oldEntry)\n {\n return newArray.indexOf(oldEntry) === -1;\n });\n\n var added = newArray.filter(function (newEntry)\n {\n return oldArray.indexOf(newEntry) === -1;\n });\n\n return {\n added: added,\n removed: removed\n };\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/renderer.js\n// module id = 25\n// module chunks = 0","/**\n * This is the web browser implementation of `debug()`.\n *\n * Expose `debug()` as the module.\n */\n\nexports = module.exports = require('./debug');\nexports.log = log;\nexports.formatArgs = formatArgs;\nexports.save = save;\nexports.load = load;\nexports.useColors = useColors;\nexports.storage = 'undefined' != typeof chrome\n && 'undefined' != typeof chrome.storage\n ? chrome.storage.local\n : localstorage();\n\n/**\n * Colors.\n */\n\nexports.colors = [\n 'lightseagreen',\n 'forestgreen',\n 'goldenrod',\n 'dodgerblue',\n 'darkorchid',\n 'crimson'\n];\n\n/**\n * Currently only WebKit-based Web Inspectors, Firefox >= v31,\n * and the Firebug extension (any Firefox version) are known\n * to support \"%c\" CSS customizations.\n *\n * TODO: add a `localStorage` variable to explicitly enable/disable colors\n */\n\nfunction useColors() {\n // NB: In an Electron preload script, document will be defined but not fully\n // initialized. Since we know we're in Chrome, we'll just detect this case\n // explicitly\n if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') {\n return true;\n }\n\n // is webkit? http://stackoverflow.com/a/16459606/376773\n // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632\n return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||\n // is firebug? http://stackoverflow.com/a/398120/376773\n (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||\n // is firefox >= v31?\n // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages\n (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\\/(\\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||\n // double check webkit in userAgent just in case we are in a worker\n (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\\/(\\d+)/));\n}\n\n/**\n * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.\n */\n\nexports.formatters.j = function(v) {\n try {\n return JSON.stringify(v);\n } catch (err) {\n return '[UnexpectedJSONParseError]: ' + err.message;\n }\n};\n\n\n/**\n * Colorize log arguments if enabled.\n *\n * @api public\n */\n\nfunction formatArgs(args) {\n var useColors = this.useColors;\n\n args[0] = (useColors ? '%c' : '')\n + this.namespace\n + (useColors ? ' %c' : ' ')\n + args[0]\n + (useColors ? '%c ' : ' ')\n + '+' + exports.humanize(this.diff);\n\n if (!useColors) return;\n\n var c = 'color: ' + this.color;\n args.splice(1, 0, c, 'color: inherit')\n\n // the final \"%c\" is somewhat tricky, because there could be other\n // arguments passed either before or after the %c, so we need to\n // figure out the correct index to insert the CSS into\n var index = 0;\n var lastC = 0;\n args[0].replace(/%[a-zA-Z%]/g, function(match) {\n if ('%%' === match) return;\n index++;\n if ('%c' === match) {\n // we only are interested in the *last* %c\n // (the user may have provided their own)\n lastC = index;\n }\n });\n\n args.splice(lastC, 0, c);\n}\n\n/**\n * Invokes `console.log()` when available.\n * No-op when `console.log` is not a \"function\".\n *\n * @api public\n */\n\nfunction log() {\n // this hackery is required for IE8/9, where\n // the `console.log` function doesn't have 'apply'\n return 'object' === typeof console\n && console.log\n && Function.prototype.apply.call(console.log, console, arguments);\n}\n\n/**\n * Save `namespaces`.\n *\n * @param {String} namespaces\n * @api private\n */\n\nfunction save(namespaces) {\n try {\n if (null == namespaces) {\n exports.storage.removeItem('debug');\n } else {\n exports.storage.debug = namespaces;\n }\n } catch(e) {}\n}\n\n/**\n * Load `namespaces`.\n *\n * @return {String} returns the previously persisted debug modes\n * @api private\n */\n\nfunction load() {\n var r;\n try {\n r = exports.storage.debug;\n } catch(e) {}\n\n // If debug isn't set in LS, and we're in Electron, try to load $DEBUG\n if (!r && typeof process !== 'undefined' && 'env' in process) {\n r = process.env.DEBUG;\n }\n\n return r;\n}\n\n/**\n * Enable namespaces listed in `localStorage.debug` initially.\n */\n\nexports.enable(load());\n\n/**\n * Localstorage attempts to return the localstorage.\n *\n * This is necessary because safari throws\n * when a user disables cookies/localstorage\n * and you attempt to access it.\n *\n * @return {LocalStorage}\n * @api private\n */\n\nfunction localstorage() {\n try {\n return window.localStorage;\n } catch (e) {}\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./~/debug/src/browser.js\n// module id = 26\n// module chunks = 0","// shim for using process in browser\nvar process = module.exports = {};\n\n// cached from whatever global is present so that test runners that stub it\n// don't break things. But we need to wrap it in a try catch in case it is\n// wrapped in strict mode code which doesn't define any globals. It's inside a\n// function because try/catches deoptimize in certain engines.\n\nvar cachedSetTimeout;\nvar cachedClearTimeout;\n\nfunction defaultSetTimout() {\n throw new Error('setTimeout has not been defined');\n}\nfunction defaultClearTimeout () {\n throw new Error('clearTimeout has not been defined');\n}\n(function () {\n try {\n if (typeof setTimeout === 'function') {\n cachedSetTimeout = setTimeout;\n } else {\n cachedSetTimeout = defaultSetTimout;\n }\n } catch (e) {\n cachedSetTimeout = defaultSetTimout;\n }\n try {\n if (typeof clearTimeout === 'function') {\n cachedClearTimeout = clearTimeout;\n } else {\n cachedClearTimeout = defaultClearTimeout;\n }\n } catch (e) {\n cachedClearTimeout = defaultClearTimeout;\n }\n} ())\nfunction runTimeout(fun) {\n if (cachedSetTimeout === setTimeout) {\n //normal enviroments in sane situations\n return setTimeout(fun, 0);\n }\n // if setTimeout wasn't available but was latter defined\n if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {\n cachedSetTimeout = setTimeout;\n return setTimeout(fun, 0);\n }\n try {\n // when when somebody has screwed with setTimeout but no I.E. maddness\n return cachedSetTimeout(fun, 0);\n } catch(e){\n try {\n // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally\n return cachedSetTimeout.call(null, fun, 0);\n } catch(e){\n // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error\n return cachedSetTimeout.call(this, fun, 0);\n }\n }\n\n\n}\nfunction runClearTimeout(marker) {\n if (cachedClearTimeout === clearTimeout) {\n //normal enviroments in sane situations\n return clearTimeout(marker);\n }\n // if clearTimeout wasn't available but was latter defined\n if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {\n cachedClearTimeout = clearTimeout;\n return clearTimeout(marker);\n }\n try {\n // when when somebody has screwed with setTimeout but no I.E. maddness\n return cachedClearTimeout(marker);\n } catch (e){\n try {\n // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally\n return cachedClearTimeout.call(null, marker);\n } catch (e){\n // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.\n // Some versions of I.E. have different rules for clearTimeout vs setTimeout\n return cachedClearTimeout.call(this, marker);\n }\n }\n\n\n\n}\nvar queue = [];\nvar draining = false;\nvar currentQueue;\nvar queueIndex = -1;\n\nfunction cleanUpNextTick() {\n if (!draining || !currentQueue) {\n return;\n }\n draining = false;\n if (currentQueue.length) {\n queue = currentQueue.concat(queue);\n } else {\n queueIndex = -1;\n }\n if (queue.length) {\n drainQueue();\n }\n}\n\nfunction drainQueue() {\n if (draining) {\n return;\n }\n var timeout = runTimeout(cleanUpNextTick);\n draining = true;\n\n var len = queue.length;\n while(len) {\n currentQueue = queue;\n queue = [];\n while (++queueIndex < len) {\n if (currentQueue) {\n currentQueue[queueIndex].run();\n }\n }\n queueIndex = -1;\n len = queue.length;\n }\n currentQueue = null;\n draining = false;\n runClearTimeout(timeout);\n}\n\nprocess.nextTick = function (fun) {\n var args = new Array(arguments.length - 1);\n if (arguments.length > 1) {\n for (var i = 1; i < arguments.length; i++) {\n args[i - 1] = arguments[i];\n }\n }\n queue.push(new Item(fun, args));\n if (queue.length === 1 && !draining) {\n runTimeout(drainQueue);\n }\n};\n\n// v8 likes predictible objects\nfunction Item(fun, array) {\n this.fun = fun;\n this.array = array;\n}\nItem.prototype.run = function () {\n this.fun.apply(null, this.array);\n};\nprocess.title = 'browser';\nprocess.browser = true;\nprocess.env = {};\nprocess.argv = [];\nprocess.version = ''; // empty string to avoid regexp issues\nprocess.versions = {};\n\nfunction noop() {}\n\nprocess.on = noop;\nprocess.addListener = noop;\nprocess.once = noop;\nprocess.off = noop;\nprocess.removeListener = noop;\nprocess.removeAllListeners = noop;\nprocess.emit = noop;\nprocess.prependListener = noop;\nprocess.prependOnceListener = noop;\n\nprocess.listeners = function (name) { return [] }\n\nprocess.binding = function (name) {\n throw new Error('process.binding is not supported');\n};\n\nprocess.cwd = function () { return '/' };\nprocess.chdir = function (dir) {\n throw new Error('process.chdir is not supported');\n};\nprocess.umask = function() { return 0; };\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./~/process/browser.js\n// module id = 27\n// module chunks = 0","\n/**\n * This is the common logic for both the Node.js and web browser\n * implementations of `debug()`.\n *\n * Expose `debug()` as the module.\n */\n\nexports = module.exports = createDebug.debug = createDebug['default'] = createDebug;\nexports.coerce = coerce;\nexports.disable = disable;\nexports.enable = enable;\nexports.enabled = enabled;\nexports.humanize = require('ms');\n\n/**\n * The currently active debug mode names, and names to skip.\n */\n\nexports.names = [];\nexports.skips = [];\n\n/**\n * Map of special \"%n\" handling functions, for the debug \"format\" argument.\n *\n * Valid key names are a single, lower or upper-case letter, i.e. \"n\" and \"N\".\n */\n\nexports.formatters = {};\n\n/**\n * Previous log timestamp.\n */\n\nvar prevTime;\n\n/**\n * Select a color.\n * @param {String} namespace\n * @return {Number}\n * @api private\n */\n\nfunction selectColor(namespace) {\n var hash = 0, i;\n\n for (i in namespace) {\n hash = ((hash << 5) - hash) + namespace.charCodeAt(i);\n hash |= 0; // Convert to 32bit integer\n }\n\n return exports.colors[Math.abs(hash) % exports.colors.length];\n}\n\n/**\n * Create a debugger with the given `namespace`.\n *\n * @param {String} namespace\n * @return {Function}\n * @api public\n */\n\nfunction createDebug(namespace) {\n\n function debug() {\n // disabled?\n if (!debug.enabled) return;\n\n var self = debug;\n\n // set `diff` timestamp\n var curr = +new Date();\n var ms = curr - (prevTime || curr);\n self.diff = ms;\n self.prev = prevTime;\n self.curr = curr;\n prevTime = curr;\n\n // turn the `arguments` into a proper Array\n var args = new Array(arguments.length);\n for (var i = 0; i < args.length; i++) {\n args[i] = arguments[i];\n }\n\n args[0] = exports.coerce(args[0]);\n\n if ('string' !== typeof args[0]) {\n // anything else let's inspect with %O\n args.unshift('%O');\n }\n\n // apply any `formatters` transformations\n var index = 0;\n args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) {\n // if we encounter an escaped % then don't increase the array index\n if (match === '%%') return match;\n index++;\n var formatter = exports.formatters[format];\n if ('function' === typeof formatter) {\n var val = args[index];\n match = formatter.call(self, val);\n\n // now we need to remove `args[index]` since it's inlined in the `format`\n args.splice(index, 1);\n index--;\n }\n return match;\n });\n\n // apply env-specific formatting (colors, etc.)\n exports.formatArgs.call(self, args);\n\n var logFn = debug.log || exports.log || console.log.bind(console);\n logFn.apply(self, args);\n }\n\n debug.namespace = namespace;\n debug.enabled = exports.enabled(namespace);\n debug.useColors = exports.useColors();\n debug.color = selectColor(namespace);\n\n // env-specific initialization logic for debug instances\n if ('function' === typeof exports.init) {\n exports.init(debug);\n }\n\n return debug;\n}\n\n/**\n * Enables a debug mode by namespaces. This can include modes\n * separated by a colon and wildcards.\n *\n * @param {String} namespaces\n * @api public\n */\n\nfunction enable(namespaces) {\n exports.save(namespaces);\n\n exports.names = [];\n exports.skips = [];\n\n var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\\s,]+/);\n var len = split.length;\n\n for (var i = 0; i < len; i++) {\n if (!split[i]) continue; // ignore empty strings\n namespaces = split[i].replace(/\\*/g, '.*?');\n if (namespaces[0] === '-') {\n exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));\n } else {\n exports.names.push(new RegExp('^' + namespaces + '$'));\n }\n }\n}\n\n/**\n * Disable debug output.\n *\n * @api public\n */\n\nfunction disable() {\n exports.enable('');\n}\n\n/**\n * Returns true if the given mode name is enabled, false otherwise.\n *\n * @param {String} name\n * @return {Boolean}\n * @api public\n */\n\nfunction enabled(name) {\n var i, len;\n for (i = 0, len = exports.skips.length; i < len; i++) {\n if (exports.skips[i].test(name)) {\n return false;\n }\n }\n for (i = 0, len = exports.names.length; i < len; i++) {\n if (exports.names[i].test(name)) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Coerce `val`.\n *\n * @param {Mixed} val\n * @return {Mixed}\n * @api private\n */\n\nfunction coerce(val) {\n if (val instanceof Error) return val.stack || val.message;\n return val;\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./~/debug/src/debug.js\n// module id = 28\n// module chunks = 0","/**\n * Helpers.\n */\n\nvar s = 1000;\nvar m = s * 60;\nvar h = m * 60;\nvar d = h * 24;\nvar y = d * 365.25;\n\n/**\n * Parse or format the given `val`.\n *\n * Options:\n *\n * - `long` verbose formatting [false]\n *\n * @param {String|Number} val\n * @param {Object} [options]\n * @throws {Error} throw an error if val is not a non-empty string or a number\n * @return {String|Number}\n * @api public\n */\n\nmodule.exports = function(val, options) {\n options = options || {};\n var type = typeof val;\n if (type === 'string' && val.length > 0) {\n return parse(val);\n } else if (type === 'number' && isNaN(val) === false) {\n return options.long ? fmtLong(val) : fmtShort(val);\n }\n throw new Error(\n 'val is not a non-empty string or a valid number. val=' +\n JSON.stringify(val)\n );\n};\n\n/**\n * Parse the given `str` and return milliseconds.\n *\n * @param {String} str\n * @return {Number}\n * @api private\n */\n\nfunction parse(str) {\n str = String(str);\n if (str.length > 100) {\n return;\n }\n var match = /^((?:\\d+)?\\.?\\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(\n str\n );\n if (!match) {\n return;\n }\n var n = parseFloat(match[1]);\n var type = (match[2] || 'ms').toLowerCase();\n switch (type) {\n case 'years':\n case 'year':\n case 'yrs':\n case 'yr':\n case 'y':\n return n * y;\n case 'days':\n case 'day':\n case 'd':\n return n * d;\n case 'hours':\n case 'hour':\n case 'hrs':\n case 'hr':\n case 'h':\n return n * h;\n case 'minutes':\n case 'minute':\n case 'mins':\n case 'min':\n case 'm':\n return n * m;\n case 'seconds':\n case 'second':\n case 'secs':\n case 'sec':\n case 's':\n return n * s;\n case 'milliseconds':\n case 'millisecond':\n case 'msecs':\n case 'msec':\n case 'ms':\n return n;\n default:\n return undefined;\n }\n}\n\n/**\n * Short format for `ms`.\n *\n * @param {Number} ms\n * @return {String}\n * @api private\n */\n\nfunction fmtShort(ms) {\n if (ms >= d) {\n return Math.round(ms / d) + 'd';\n }\n if (ms >= h) {\n return Math.round(ms / h) + 'h';\n }\n if (ms >= m) {\n return Math.round(ms / m) + 'm';\n }\n if (ms >= s) {\n return Math.round(ms / s) + 's';\n }\n return ms + 'ms';\n}\n\n/**\n * Long format for `ms`.\n *\n * @param {Number} ms\n * @return {String}\n * @api private\n */\n\nfunction fmtLong(ms) {\n return plural(ms, d, 'day') ||\n plural(ms, h, 'hour') ||\n plural(ms, m, 'minute') ||\n plural(ms, s, 'second') ||\n ms + ' ms';\n}\n\n/**\n * Pluralization helper.\n */\n\nfunction plural(ms, n, name) {\n if (ms < n) {\n return;\n }\n if (ms < n * 1.5) {\n return Math.floor(ms / n) + ' ' + name;\n }\n return Math.ceil(ms / n) + ' ' + name + 's';\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./~/ms/index.js\n// module id = 29\n// module chunks = 0","module.exports = CompositeImage;\n\n/**\n * @class CompositeImage\n * @private\n *\n * Utility class to composite tiles into a complete image\n * and track the rendered state of an image as new tiles\n * load.\n */\n\n/**\n * @param levels {Array.>}\n * @constructor\n */\nfunction CompositeImage(levels)\n{\n this._levels = levels; // Assume levels sorted high-res first\n var urlsToTiles = this._urlsToTiles = {};\n\n levels.forEach(function (level)\n {\n level.tiles.forEach(function (tile)\n {\n urlsToTiles[tile.url] = {\n zoomLevel: level.zoomLevel,\n row: tile.row,\n col: tile.col\n };\n });\n });\n\n this.clear();\n}\n\nCompositeImage.prototype.clear = function ()\n{\n var loadedByLevel = this._loadedByLevel = {};\n\n this._levels.forEach(function (level)\n {\n loadedByLevel[level.zoomLevel] = new TileCoverageMap(level.rows, level.cols);\n });\n};\n\nCompositeImage.prototype.getTiles = function (baseZoomLevel)\n{\n var toRenderByLevel = [];\n var highestZoomLevel = this._levels[0].zoomLevel;\n var covered = new TileCoverageMap(this._levels[0].rows, this._levels[0].cols);\n\n var bestLevelIndex;\n\n // Default to the lowest zoom level\n if (baseZoomLevel === null)\n {\n bestLevelIndex = 0;\n }\n else\n {\n var ceilLevel = Math.ceil(baseZoomLevel);\n bestLevelIndex = findIndex(this._levels, function (level)\n {\n return level.zoomLevel <= ceilLevel;\n });\n }\n\n\n // The best level, followed by higher-res levels in ascending order of resolution,\n // followed by lower-res levels in descending order of resolution\n var levelsByPreference = this._levels.slice(0, bestLevelIndex + 1).reverse()\n .concat(this._levels.slice(bestLevelIndex + 1));\n\n levelsByPreference.forEach(function (level)\n {\n var loaded = this._loadedByLevel[level.zoomLevel];\n\n var additionalTiles = level.tiles.filter(function (tile)\n {\n return loaded.isLoaded(tile.row, tile.col);\n });\n\n // Filter out entirely covered tiles\n\n // FIXME: Is it better to draw all of a partially covered tile,\n // with some of it ultimately covered, or to pick out the region\n // which needs to be drawn?\n // See https://github.com/DDMAL/diva.js/issues/358\n\n var scaleRatio = Math.pow(2, highestZoomLevel - level.zoomLevel);\n\n additionalTiles = additionalTiles.filter(function (tile)\n {\n var isNeeded = false;\n\n var highResRow = tile.row * scaleRatio;\n var highResCol = tile.col * scaleRatio;\n\n for (var i=0; i < scaleRatio; i++)\n {\n for (var j=0; j < scaleRatio; j++)\n {\n if (!covered.isLoaded(highResRow + i, highResCol + j))\n {\n isNeeded = true;\n covered.set(highResRow + i, highResCol + j, true);\n }\n }\n }\n\n return isNeeded;\n });\n\n toRenderByLevel.push(additionalTiles);\n }, this);\n\n // Less-preferred tiles should come first\n toRenderByLevel.reverse();\n\n var tiles = [];\n\n toRenderByLevel.forEach(function (byLevel)\n {\n tiles.push.apply(tiles, byLevel);\n });\n\n return tiles;\n};\n\n/**\n * Update the composite image to take into account all the URLs\n * loaded in an image cache.\n *\n * @param cache {ImageCache}\n */\nCompositeImage.prototype.updateFromCache = function (cache)\n{\n this.clear();\n\n this._levels.forEach(function (level)\n {\n var loaded = this._loadedByLevel[level.zoomLevel];\n\n level.tiles.forEach(function (tile)\n {\n if (cache.has(tile.url))\n loaded.set(tile.row, tile.col, true);\n });\n }, this);\n};\n\nCompositeImage.prototype.updateWithLoadedUrls = function (urls)\n{\n urls.forEach(function (url)\n {\n var entry = this._urlsToTiles[url];\n this._loadedByLevel[entry.zoomLevel].set(entry.row, entry.col, true);\n }, this);\n};\n\nfunction TileCoverageMap(rows, cols)\n{\n this._rows = rows;\n this._cols = cols;\n\n this._map = fill(rows).map(function ()\n {\n return fill(cols, false);\n });\n}\n\nTileCoverageMap.prototype.isLoaded = function (row, col)\n{\n // Return true for out of bounds tiles because they\n // don't need to load. (Unfortunately this will also\n // mask logical errors.)\n if (row >= this._rows || col >= this._cols)\n return true;\n\n return this._map[row][col];\n};\n\nTileCoverageMap.prototype.set = function (row, col, value)\n{\n this._map[row][col] = value;\n};\n\nfunction fill(count, value)\n{\n var arr = new Array(count);\n\n for (var i=0; i < count; i++)\n arr[i] = value;\n\n return arr;\n}\n\nfunction findIndex(array, predicate)\n{\n var length = array.length;\n for (var i = 0; i < length; i++)\n {\n if (predicate(array[i], i))\n return i;\n }\n\n return -1;\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/composite-image.js\n// module id = 30\n// module chunks = 0","module.exports = DocumentLayout;\n\n/**\n * Translate page layouts, as generated by page-layouts, into an\n * object which computes layout information for the document as\n * a whole.\n */\nfunction DocumentLayout(config, zoomLevel)\n{\n var computedLayout = getComputedLayout(config, zoomLevel);\n\n this.dimensions = computedLayout.dimensions;\n this.pageGroups = computedLayout.pageGroups;\n this._pageLookup = getPageLookup(computedLayout.pageGroups);\n}\n\n/**\n * @typedef {Object} PageInfo\n * @property {number} index\n * @property {{index, dimensions, pages, region, padding}} group\n * @property {{height: number, width: number}} dimensions\n * @property {{top: number, left: number}} groupOffset\n */\n\n/**\n * @param pageIndex\n * @returns {PageInfo|null}\n */\nDocumentLayout.prototype.getPageInfo = function (pageIndex)\n{\n return this._pageLookup[pageIndex] || null;\n};\n\n/**\n * Get the dimensions of a page\n *\n * @param pageIndex\n * @returns {{height: number, width: number}}\n */\nDocumentLayout.prototype.getPageDimensions = function (pageIndex)\n{\n if (!this._pageLookup || !this._pageLookup[pageIndex])\n return null;\n\n var region = getPageRegionFromPageInfo(this._pageLookup[pageIndex]);\n\n return {\n height: region.bottom - region.top,\n width: region.right - region.left\n };\n};\n\n// TODO(wabain): Get rid of this; it's a subset of the page region, so\n// give that instead\n/**\n * Get the top-left coordinates of a page, including*** padding\n *\n * @param pageIndex\n * @param options\n * @returns {{top: number, left: number} | null}\n */\nDocumentLayout.prototype.getPageOffset = function (pageIndex, options)\n{\n var region = this.getPageRegion(pageIndex, options);\n\n if (!region)\n return null;\n\n return {\n top: region.top,\n left: region.left\n };\n};\n\nDocumentLayout.prototype.getPageRegion = function (pageIndex, options)\n{\n var pageInfo = this._pageLookup[pageIndex];\n\n if (!pageInfo)\n return null;\n\n var region = getPageRegionFromPageInfo(pageInfo);\n\n if (options && options.excludePadding)\n {\n // FIXME?\n var padding = pageInfo.group.padding;\n\n return {\n top: region.top + padding.top,\n left: region.left + padding.left,\n bottom: region.bottom,\n right: region.right\n };\n }\n\n return region;\n};\n\n/**\n * Get the distance from the top-right of the page to the center of the\n * specified viewport region\n *\n * @param pageIndex\n * @param viewport {{top: number, left: number, bottom: number, right: number}}\n * @returns {{x: number, y: number}}\n */\nDocumentLayout.prototype.getPageToViewportCenterOffset = function (pageIndex, viewport)\n{\n var scrollLeft = viewport.left;\n var elementWidth = viewport.right - viewport.left;\n\n var offset = this.getPageOffset(pageIndex);\n\n var x = scrollLeft - offset.left + parseInt(elementWidth / 2, 10);\n\n var scrollTop = viewport.top;\n var elementHeight = viewport.bottom - viewport.top;\n\n var y = scrollTop - offset.top + parseInt(elementHeight / 2, 10);\n\n return {\n x: x,\n y: y\n };\n};\n\nfunction getPageRegionFromPageInfo(page)\n{\n var top = page.groupOffset.top + page.group.region.top;\n var bottom = top + page.dimensions.height;\n var left = page.groupOffset.left + page.group.region.left;\n var right = left + page.dimensions.width;\n\n return {\n top: top,\n bottom: bottom,\n left: left,\n right: right\n };\n}\n\nfunction getPageLookup(pageGroups)\n{\n var pageLookup = {};\n\n pageGroups.forEach(function (group)\n {\n group.pages.forEach(function (page)\n {\n pageLookup[page.index] = {\n index: page.index,\n group: group,\n dimensions: page.dimensions,\n groupOffset: page.groupOffset\n };\n });\n });\n\n return pageLookup;\n}\n\nfunction getComputedLayout(config, zoomLevel)\n{\n var scaledLayouts = zoomLevel === null ? config.pageLayouts : getScaledPageLayouts(config, zoomLevel);\n\n var documentSecondaryExtent = getExtentAlongSecondaryAxis(config, scaledLayouts);\n\n // The current position in the document along the primary axis\n var primaryDocPosition = config.verticallyOriented ?\n config.padding.document.top :\n config.padding.document.left;\n\n var pageGroups = [];\n\n // TODO: Use bottom, right as well\n var pagePadding = {\n top: config.padding.page.top,\n left: config.padding.page.left\n };\n\n scaledLayouts.forEach(function (layout, index)\n {\n var top, left;\n\n if (config.verticallyOriented)\n {\n top = primaryDocPosition;\n left = (documentSecondaryExtent - layout.dimensions.width) / 2;\n }\n else\n {\n top = (documentSecondaryExtent - layout.dimensions.height) / 2;\n left = primaryDocPosition;\n }\n\n var region = {\n top: top,\n bottom: top + pagePadding.top + layout.dimensions.height,\n left: left,\n right: left + pagePadding.left + layout.dimensions.width\n };\n\n pageGroups.push({\n index: index,\n dimensions: layout.dimensions,\n pages: layout.pages,\n region: region,\n padding: pagePadding\n });\n\n primaryDocPosition = config.verticallyOriented ? region.bottom : region.right;\n });\n\n var height, width;\n\n if (config.verticallyOriented)\n {\n height = primaryDocPosition + pagePadding.top;\n width = documentSecondaryExtent;\n }\n else\n {\n height = documentSecondaryExtent;\n width = primaryDocPosition + pagePadding.left;\n }\n\n return {\n dimensions: {\n height: height,\n width: width\n },\n pageGroups: pageGroups\n };\n}\n\nfunction getScaledPageLayouts(config, zoomLevel)\n{\n var scaleRatio = Math.pow(2, zoomLevel - config.maxZoomLevel);\n\n return config.pageLayouts.map(function (group)\n {\n return {\n dimensions: scaleDimensions(group.dimensions, scaleRatio),\n pages: group.pages.map(function (page)\n {\n return {\n index: page.index,\n groupOffset: {\n top: Math.floor(page.groupOffset.top * scaleRatio),\n left: Math.floor(page.groupOffset.left * scaleRatio)\n },\n dimensions: scaleDimensions(page.dimensions, scaleRatio)\n };\n })\n };\n });\n}\n\nfunction scaleDimensions(dimensions, scaleRatio)\n{\n return {\n height: Math.floor(dimensions.height * scaleRatio),\n width: Math.floor(dimensions.width * scaleRatio)\n };\n}\n\nfunction getExtentAlongSecondaryAxis(config, scaledLayouts)\n{\n // Get the extent of the document along the secondary axis\n var secondaryDim, secondaryPadding;\n var docPadding = config.padding.document;\n\n if (config.verticallyOriented)\n {\n secondaryDim = 'width';\n secondaryPadding = docPadding.left + docPadding.right;\n }\n else\n {\n secondaryDim = 'height';\n secondaryPadding = docPadding.top + docPadding.bottom;\n }\n\n return secondaryPadding + scaledLayouts.reduce(function (maxDim, layout)\n {\n return Math.max(layout.dimensions[secondaryDim], maxDim);\n }, 0);\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/document-layout.js\n// module id = 31\n// module chunks = 0","'use strict';\n\nvar debug = require('debug')('diva:ImageCache');\n\nmodule.exports = ImageCache;\n\n/* FIXME(wabain): The caching strategy here is completely\n * arbitrary and the implementation isn't especially efficient.\n */\n\nvar DEFAULT_MAX_KEYS = 100;\n\nfunction ImageCache(options)\n{\n options = options || { maxKeys: DEFAULT_MAX_KEYS };\n this.maxKeys = options.maxKeys || DEFAULT_MAX_KEYS;\n\n this._held = {};\n this._urls = {};\n this._lru = [];\n}\n\nImageCache.prototype.get = function (url)\n{\n var record = this._urls[url];\n return record ? record.img : null;\n};\n\nImageCache.prototype.has = function (url)\n{\n return !!this._urls[url];\n};\n\nImageCache.prototype.put = function (url, img)\n{\n var record = this._urls[url];\n if (record)\n {\n // FIXME: Does this make sense for this use case?\n record.img = img;\n this._promote(record);\n }\n else\n {\n record = {\n img: img,\n url: url\n };\n\n this._urls[url] = record;\n this._tryEvict(1);\n this._lru.unshift(record);\n }\n};\n\nImageCache.prototype._promote = function (record)\n{\n var index = this._lru.indexOf(record);\n this._lru.splice(index, 1);\n this._lru.unshift(record);\n};\n\nImageCache.prototype._tryEvict = function (extraCapacity)\n{\n var allowedEntryCount = this.maxKeys - extraCapacity;\n\n if (this._lru.length <= allowedEntryCount)\n return;\n\n var evictionIndex = this._lru.length - 1;\n\n for (;;)\n {\n var target = this._lru[evictionIndex];\n\n if (!this._held[target.url])\n {\n debug('Evicting image %s', target.url);\n this._lru.splice(evictionIndex, 1);\n delete this._urls[target.url];\n\n if (this._lru.length <= allowedEntryCount)\n break;\n }\n\n if (evictionIndex === 0)\n {\n /* istanbul ignore next */\n debug.enabled && debug('Cache overfull by %s (all entries are being held)',\n this._lru.length - allowedEntryCount);\n\n break;\n }\n\n evictionIndex--;\n }\n};\n\nImageCache.prototype.acquire = function (url)\n{\n this._held[url] = (this._held[url] || 0) + 1;\n this._promote(this._urls[url]);\n};\n\nImageCache.prototype.release = function (url)\n{\n var count = this._held[url];\n\n if (count > 1)\n this._held[url]--;\n else\n delete this._held[url];\n\n this._tryEvict(0);\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/image-cache.js\n// module id = 32\n// module chunks = 0","var debug = require('debug')('diva:ImageRequestHandler');\n\nmodule.exports = ImageRequestHandler;\n\n/**\n * Handler for the request for an image tile\n *\n * @param url\n * @param callback\n * @constructor\n */\nfunction ImageRequestHandler(options)\n{\n this._url = options.url;\n this._callback = options.load;\n this._errorCallback = options.error;\n this.timeoutTime = options.timeoutTime || 0;\n this._aborted = this._complete = false;\n\n //Use a timeout to allow the requests to be debounced (as they are in renderer)\n this.timeout = setTimeout(function()\n {\n // Initiate the request\n this._image = new Image();\n this._image.crossOrigin = \"anonymous\";\n this._image.onload = this._handleLoad.bind(this);\n this._image.onerror = this._handleError.bind(this);\n this._image.src = options.url;\n\n debug('Requesting image %s', options.url);\n }.bind(this), this.timeoutTime);\n}\n\nImageRequestHandler.prototype.abort = function ()\n{\n debug('Aborting request to %s', this._url);\n\n clearTimeout(this.timeout);\n\n // FIXME\n // People on the Internet say that doing this {{should/should not}} abort the request. I believe\n // it corresponds to what the WHATWG HTML spec says should happen when the UA\n // updates the image data if selected source is null.\n //\n // Sources:\n //\n // https://html.spec.whatwg.org/multipage/embedded-content.html#the-img-element\n // http://stackoverflow.com/questions/7390888/does-changing-the-src-attribute-of-an-image-stop-the-image-from-downloading\n if (this._image)\n {\n this._image.onload = this._image.onerror = null;\n\n this._image.src = '';\n }\n\n this._aborted = true;\n};\n\nImageRequestHandler.prototype._handleLoad = function ()\n{\n if (this._aborted)\n {\n console.error('ImageRequestHandler invoked on cancelled request for ' + this._url);\n return;\n }\n\n if (this._complete)\n {\n console.error('ImageRequestHandler invoked on completed request for ' + this._url);\n return;\n }\n\n this._complete = true;\n\n debug('Received image %s', this._url);\n this._callback(this._image);\n};\n\nImageRequestHandler.prototype._handleError = function ()\n{\n debug('Failed to load image %s', this._url);\n this._errorCallback(this._image);\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/image-request-handler.js\n// module id = 33\n// module chunks = 0","/* global performance */\n\n// TODO: requestAnimationFrame fallback\n\nmodule.exports = {\n animate: animate,\n easing: {\n linear: linearEasing\n }\n};\n\nfunction animate(options)\n{\n var durationMs = options.duration;\n var parameters = options.parameters;\n var onUpdate = options.onUpdate;\n var onEnd = options.onEnd;\n\n // Setup\n // Times are in milliseconds from a basically arbitrary start\n var start = now();\n var end = start + durationMs;\n\n var tweenFns = {};\n var values = {};\n var paramKeys = Object.keys(parameters);\n\n paramKeys.forEach(function (key)\n {\n var config = parameters[key];\n tweenFns[key] = interpolate(config.from, config.to, config.easing || linearEasing);\n });\n\n // Run it!\n var requestId = requestAnimationFrame(update);\n\n return {\n cancel: function ()\n {\n if (requestId !== null)\n {\n cancelAnimationFrame(requestId);\n handleAnimationCompletion({\n interrupted: true\n });\n }\n }\n };\n\n function update()\n {\n var current = now();\n var elapsed = Math.min((current - start) / durationMs, 1);\n\n updateValues(elapsed);\n onUpdate(values);\n\n if (current < end)\n requestId = requestAnimationFrame(update);\n else\n handleAnimationCompletion({\n interrupted: false\n });\n }\n\n function updateValues(elapsed)\n {\n paramKeys.forEach(function (key)\n {\n values[key] = tweenFns[key](elapsed);\n });\n }\n\n function handleAnimationCompletion(info)\n {\n requestId = null;\n\n if (onEnd)\n onEnd(info);\n }\n}\n\nfunction interpolate(start, end, easing)\n{\n return function (elapsed)\n {\n return start + (end - start) * easing(elapsed);\n };\n}\n\nfunction linearEasing(e)\n{\n return e;\n}\n\nvar now;\n\nif (typeof performance !== 'undefined' && performance.now)\n{\n now = function ()\n {\n return performance.now();\n };\n}\nelse\n{\n now = function ()\n {\n return Date.now();\n };\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/interpolate-animation.js\n// module id = 34\n// module chunks = 0","var getBookLayoutGroups = require('./book-layout');\nvar getSinglesLayoutGroups = require('./singles-layout');\nvar getGridLayoutGroups = require('./grid-layout');\n\nmodule.exports = getPageLayouts;\n\n/** Get the relative positioning of pages for the current view */\nfunction getPageLayouts(settings)\n{\n if (settings.inGrid)\n {\n return getGridLayoutGroups(pluck(settings, [\n 'manifest',\n 'viewport',\n 'pagesPerRow',\n 'fixedHeightGrid',\n 'fixedPadding',\n 'showNonPagedPages'\n ]));\n }\n else\n {\n var config = pluck(settings, ['manifest', 'verticallyOriented', 'showNonPagedPages']);\n\n if (settings.inBookLayout)\n return getBookLayoutGroups(config);\n else\n return getSinglesLayoutGroups(config);\n }\n}\n\nfunction pluck(obj, keys)\n{\n var out = {};\n keys.forEach(function (key)\n {\n out[key] = obj[key];\n });\n return out;\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/page-layouts/index.js\n// module id = 35\n// module chunks = 0","var getPageDimensions = require('./page-dimensions');\n\nmodule.exports = getBookLayoutGroups;\n\nfunction getBookLayoutGroups(viewerConfig)\n{\n var groupings = getGroupings(viewerConfig);\n\n return groupings.map(function (grouping)\n {\n return getGroupLayoutsFromPageGrouping(viewerConfig, grouping);\n });\n}\n\nfunction getGroupings(viewerConfig)\n{\n var manifest = viewerConfig.manifest;\n\n var pagesByGroup = [];\n var leftPage = null;\n var nonPagedPages = []; // Pages to display below the current group\n\n var _addNonPagedPages = function()\n {\n for (var i = 0; i < nonPagedPages.length; i++)\n {\n pagesByGroup.push([ nonPagedPages[i] ]);\n }\n nonPagedPages = [];\n };\n\n manifest.pages.forEach(function (page, index)\n {\n var pageRecord = {\n index: index,\n dimensions: getPageDimensions(index, manifest),\n paged: (!manifest.paged || page.paged)\n };\n\n // Only display non-paged pages if specified in the settings\n if (!viewerConfig.showNonPagedPages && !pageRecord.paged)\n return;\n\n if (!pageRecord.paged)\n {\n nonPagedPages.push(pageRecord);\n }\n else if (index === 0 || page.facingPages)\n {\n // The first page is placed on its own\n pagesByGroup.push([pageRecord]);\n _addNonPagedPages();\n }\n else if (leftPage === null)\n {\n leftPage = pageRecord;\n }\n else\n {\n pagesByGroup.push([leftPage, pageRecord]);\n leftPage = null;\n _addNonPagedPages();\n }\n });\n\n // Flush a final left page\n if (leftPage !== null)\n {\n pagesByGroup.push([leftPage]);\n _addNonPagedPages();\n }\n\n return pagesByGroup;\n}\n\nfunction getGroupLayoutsFromPageGrouping(viewerConfig, grouping)\n{\n var verticallyOriented = viewerConfig.verticallyOriented;\n\n if (grouping.length === 2)\n return getFacingPageGroup(grouping[0], grouping[1], verticallyOriented);\n\n var page = grouping[0];\n var pageDims = page.dimensions;\n\n // The first page is placed on its own to the right in vertical orientation.\n // NB that this needs to be the page with index 0; if the first page is excluded\n // from the layout then this special case shouldn't apply.\n // If the page is tagged as 'non-paged', center it horizontally\n var leftOffset;\n if (page.paged)\n leftOffset = (page.index === 0 && verticallyOriented) ? pageDims.width : 0;\n else\n leftOffset = (verticallyOriented) ? pageDims.width / 2 : 0;\n\n var shouldBeHorizontallyAdjusted =\n verticallyOriented && !viewerConfig.manifest.pages[page.index].facingPages;\n\n // We need to left-align the page in vertical orientation, so we double\n // the group width\n return {\n dimensions: {\n height: pageDims.height,\n width: shouldBeHorizontallyAdjusted ? pageDims.width * 2 : pageDims.width\n },\n pages: [{\n index: page.index,\n groupOffset: {\n top: 0,\n left: leftOffset\n },\n dimensions: pageDims\n }]\n };\n}\n\nfunction getFacingPageGroup(leftPage, rightPage, verticallyOriented)\n{\n var leftDims = leftPage.dimensions;\n var rightDims = rightPage.dimensions;\n\n var height = Math.max(leftDims.height, rightDims.height);\n\n var width, firstLeftOffset, secondLeftOffset;\n\n if (verticallyOriented)\n {\n var midWidth = Math.max(leftDims.width, rightDims.width);\n\n width = midWidth * 2;\n\n firstLeftOffset = midWidth - leftDims.width;\n secondLeftOffset = midWidth;\n }\n else\n {\n width = leftDims.width + rightDims.width;\n firstLeftOffset = 0;\n secondLeftOffset = leftDims.width;\n }\n\n return {\n dimensions: {\n height: height,\n width: width\n },\n pages: [\n {\n index: leftPage.index,\n dimensions: leftDims,\n groupOffset: {\n top: 0,\n left: firstLeftOffset\n }\n },\n {\n index: rightPage.index,\n dimensions: rightDims,\n groupOffset: {\n top: 0,\n left: secondLeftOffset\n }\n }\n ]\n };\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/page-layouts/book-layout.js\n// module id = 36\n// module chunks = 0","module.exports = function getPageDimensions(pageIndex, manifest)\n{\n var dims = manifest.getMaxPageDimensions(pageIndex);\n\n return {\n width: Math.floor(dims.width),\n height: Math.floor(dims.height)\n };\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/page-layouts/page-dimensions.js\n// module id = 37\n// module chunks = 0","var getPageDimensions = require('./page-dimensions');\n\nmodule.exports = function getSinglesLayoutGroups(viewerConfig)\n{\n var manifest = viewerConfig.manifest;\n\n // Render each page alone in a group\n var pages = [];\n manifest.pages.forEach(function (page, index)\n {\n if (!viewerConfig.showNonPagedPages && manifest.paged && !page.paged)\n return;\n\n var pageDims = getPageDimensions(index, manifest);\n\n pages.push({\n dimensions: pageDims,\n pages: [\n {\n index: index,\n groupOffset: {top: 0, left: 0},\n dimensions: pageDims\n }\n ]\n });\n });\n\n return pages;\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/page-layouts/singles-layout.js\n// module id = 38\n// module chunks = 0","module.exports = getGridLayoutGroups;\n\nfunction getGridLayoutGroups(viewerConfig)\n{\n var viewportWidth = viewerConfig.viewport.width;\n var manifest = viewerConfig.manifest;\n var pagesPerRow = viewerConfig.pagesPerRow;\n var fixedHeightGrid = viewerConfig.fixedHeightGrid;\n var fixedPadding = viewerConfig.fixedPadding;\n var showNonPagedPages = viewerConfig.showNonPagedPages;\n\n var horizontalPadding = fixedPadding * (pagesPerRow + 1);\n var pageWidth = (viewportWidth - horizontalPadding) / pagesPerRow;\n var gridPageWidth = pageWidth;\n\n // Calculate the row height depending on whether we want to fix the width or the height\n var rowHeight = (fixedHeightGrid) ? fixedPadding + manifest.minRatio * pageWidth : fixedPadding + manifest.maxRatio * pageWidth;\n\n var groups = [];\n var currentPages = [];\n\n var getGridPageDimensions = function (pageData)\n {\n // Calculate the width, height and horizontal placement of this page\n // Get dimensions at max zoom level, although any level should be fine\n var pageDimenData = pageData.d[pageData.d.length - 1];\n var heightToWidthRatio = pageDimenData.h / pageDimenData.w;\n\n var pageWidth, pageHeight;\n\n if (fixedHeightGrid)\n {\n pageWidth = (rowHeight - fixedPadding) / heightToWidthRatio;\n pageHeight = rowHeight - fixedPadding;\n }\n else\n {\n pageWidth = gridPageWidth;\n pageHeight = pageWidth * heightToWidthRatio;\n }\n\n return {\n width: Math.round(pageWidth),\n height: Math.round(pageHeight)\n };\n };\n\n var rowDimensions = {\n height: rowHeight,\n width: viewportWidth\n };\n\n manifest.pages.forEach(function (page, pageIndex)\n {\n if (!showNonPagedPages && manifest.paged && !page.paged)\n return;\n\n // Calculate the width, height and horizontal placement of this page\n var pageDimens = getGridPageDimensions(page);\n var leftOffset = Math.floor(currentPages.length * (fixedPadding + gridPageWidth) + fixedPadding);\n\n // Center the page if the height is fixed (otherwise, there is no horizontal padding)\n if (fixedHeightGrid)\n {\n leftOffset += (gridPageWidth - pageDimens.width) / 2;\n }\n\n // TODO: Precompute page dimensions everywhere\n currentPages.push({\n index: pageIndex,\n dimensions: pageDimens,\n groupOffset: {\n top: 0,\n left: leftOffset\n }\n });\n\n if (currentPages.length === pagesPerRow)\n {\n groups.push({\n dimensions: rowDimensions,\n pages: currentPages\n });\n\n currentPages = [];\n }\n });\n\n if (currentPages.length > 0)\n {\n groups.push({\n dimensions: rowDimensions,\n pages: currentPages\n });\n }\n\n return groups;\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/page-layouts/grid-layout.js\n// module id = 39\n// module chunks = 0","module.exports = createSettingsView;\n\nfunction createSettingsView(sources)\n{\n var obj = {};\n\n sources.forEach(function (source)\n {\n registerMixin(obj, source);\n });\n\n return obj;\n}\n\nfunction registerMixin(obj, mixin)\n{\n Object.keys(mixin).forEach(function (key)\n {\n Object.defineProperty(obj, key, {\n get: function ()\n {\n return mixin[key];\n },\n set: function ()\n {\n // TODO: Make everything strict mode so this isn't needed\n throw new TypeError('Cannot set settings.' + key);\n }\n });\n });\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/settings-view.js\n// module id = 40\n// module chunks = 0","var extend = require('jquery').extend;\n\nmodule.exports = ValidationRunner;\n\nfunction ValidationRunner(options)\n{\n this.whitelistedKeys = options.whitelistedKeys || [];\n this.additionalProperties = options.additionalProperties || [];\n this.validations = options.validations;\n}\n\nValidationRunner.prototype.isValid = function (key, value, settings)\n{\n // Get the validation index\n var validationIndex = null;\n\n this.validations.some(function (validation, index)\n {\n if (validation.key !== key)\n return false;\n\n validationIndex = index;\n return true;\n });\n\n if (validationIndex === null)\n return true;\n\n // Run the validation\n var dummyChanges = {};\n dummyChanges[key] = value;\n var proxier = createSettingsProxier(settings, dummyChanges, this);\n\n return !this._runValidation(validationIndex, value, proxier);\n};\n\nValidationRunner.prototype.validate = function (settings)\n{\n this._validateOptions({}, settings);\n};\n\nValidationRunner.prototype.getValidatedOptions = function (settings, options)\n{\n var cloned = extend({}, options);\n this._validateOptions(settings, cloned);\n return cloned;\n};\n\nValidationRunner.prototype._validateOptions = function (settings, options)\n{\n var settingsProxier = createSettingsProxier(settings, options, this);\n this._applyValidations(options, settingsProxier);\n};\n\nValidationRunner.prototype._applyValidations = function (options, proxier)\n{\n this.validations.forEach(function (validation, index)\n {\n if (!options.hasOwnProperty(validation.key))\n return;\n\n var input = options[validation.key];\n var corrected = this._runValidation(index, input, proxier);\n\n if (corrected)\n {\n if (!corrected.warningSuppressed)\n emitWarning(validation.key, input, corrected.value);\n\n options[validation.key] = corrected.value;\n }\n }, this);\n};\n\nValidationRunner.prototype._runValidation = function (index, input, proxier)\n{\n var validation = this.validations[index];\n\n proxier.index = index;\n\n var warningSuppressed = false;\n var config = {\n suppressWarning: function ()\n {\n warningSuppressed = true;\n }\n };\n\n var outputValue = validation.validate(input, proxier.proxy, config);\n\n if (outputValue === undefined || outputValue === input)\n return null;\n\n return {\n value: outputValue,\n warningSuppressed: warningSuppressed\n };\n};\n\n/**\n * The settings proxy wraps the settings object and ensures that\n * only values which have previously been validated are accessed,\n * throwing a TypeError otherwise.\n *\n * FIXME(wabain): Is it worth keeping this? When I wrote it I had\n * multiple validation stages and it was a lot harder to keep track\n * of everything, so this was more valuable.\n */\nfunction createSettingsProxier(settings, options, runner)\n{\n var proxier = {\n proxy: {},\n index: null\n };\n\n var lookup = lookupValue.bind(null, settings, options);\n\n var properties = {};\n\n runner.whitelistedKeys.forEach(function (whitelisted)\n {\n properties[whitelisted] = {\n get: lookup.bind(null, whitelisted)\n };\n });\n\n runner.additionalProperties.forEach(function (additional)\n {\n properties[additional.key] = {\n get: additional.get\n };\n });\n\n runner.validations.forEach(function (validation, validationIndex)\n {\n properties[validation.key] = {\n get: function ()\n {\n if (validationIndex < proxier.index)\n return lookup(validation.key);\n\n var currentKey = runner.validations[proxier.index].key;\n throw new TypeError('Cannot access setting ' + validation.key + ' while validating ' + currentKey);\n }\n };\n });\n\n Object.defineProperties(proxier.proxy, properties);\n\n return proxier;\n}\n\nfunction emitWarning(key, original, corrected)\n{\n console.warn('Invalid value for ' + key + ': ' + original + '. Using ' + corrected + ' instead.');\n}\n\nfunction lookupValue(base, extension, key)\n{\n if (key in extension)\n return extension[key];\n\n return base[key];\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/validation-runner.js\n// module id = 41\n// module chunks = 0","module.exports = Viewport;\n\nfunction Viewport(outer, options)\n{\n options = options || {};\n\n this.intersectionTolerance = options.intersectionTolerance || 0;\n this.maxExtent = options.maxExtent || 2000;\n\n this.outer = outer;\n\n this._top = this._left = this._width = this._height = this._innerDimensions = null;\n\n this.invalidate();\n}\n\nViewport.prototype.intersectsRegion = function (region)\n{\n return this.hasHorizontalOverlap(region) && this.hasVerticalOverlap(region);\n};\n\nViewport.prototype.hasVerticalOverlap = function (region)\n{\n var top = this.top - this.intersectionTolerance;\n var bottom = this.bottom + this.intersectionTolerance;\n\n return (\n fallsBetween(region.top, top, bottom) ||\n fallsBetween(region.bottom, top, bottom) ||\n (region.top <= top && region.bottom >= bottom)\n );\n};\n\nViewport.prototype.hasHorizontalOverlap = function (region)\n{\n var left = this.left - this.intersectionTolerance;\n var right = this.right + this.intersectionTolerance;\n\n return (\n fallsBetween(region.left, left, right) ||\n fallsBetween(region.right, left, right) ||\n (region.left <= left && region.right >= right)\n );\n};\n\nViewport.prototype.invalidate = function ()\n{\n // FIXME: Should this check the inner dimensions as well?\n this._width = clampMax(this.outer.clientWidth, this.maxExtent);\n this._height = clampMax(this.outer.clientHeight, this.maxExtent);\n\n this._top = this.outer.scrollTop;\n this._left = this.outer.scrollLeft;\n};\n\nViewport.prototype.setInnerDimensions = function (dimensions)\n{\n this._innerDimensions = dimensions;\n\n if (dimensions)\n {\n this._top = clamp(this._top, 0, dimensions.height - this._height);\n this._left = clamp(this._left, 0, dimensions.width - this._width);\n }\n};\n\nObject.defineProperties(Viewport.prototype, {\n top: getCoordinateDescriptor('top', 'height'),\n left: getCoordinateDescriptor('left', 'width'),\n\n width: getDimensionDescriptor('width'),\n height: getDimensionDescriptor('height'),\n\n bottom: {\n get: function ()\n {\n return this._top + this._height;\n }\n },\n right: {\n get: function ()\n {\n return this._left + this._width;\n }\n }\n});\n\nfunction getCoordinateDescriptor(coord, associatedDimension)\n{\n var privateProp = '_' + coord;\n var source = 'scroll' + coord.charAt(0).toUpperCase() + coord.slice(1);\n\n return {\n get: function ()\n {\n return this[privateProp];\n },\n set: function (newValue)\n {\n var normalized;\n\n if (this._innerDimensions)\n {\n var maxAllowed = this._innerDimensions[associatedDimension] - this[associatedDimension];\n normalized = clamp(newValue, 0, maxAllowed);\n }\n else\n {\n normalized = clampMin(newValue, 0);\n }\n\n this[privateProp] = this.outer[source] = normalized;\n }\n };\n}\n\nfunction getDimensionDescriptor(dimen)\n{\n return {\n get: function ()\n {\n return this['_' + dimen];\n }\n };\n}\n\nfunction fallsBetween(point, start, end)\n{\n return point >= start && point <= end;\n}\n\nfunction clamp(value, min, max)\n{\n return clampMin(clampMax(value, max), min);\n}\n\nfunction clampMin(value, min)\n{\n return Math.max(value, min);\n}\n\nfunction clampMax(value, max)\n{\n return Math.min(value, max);\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/viewport.js\n// module id = 42\n// module chunks = 0","/*\n\nCanvas plugin for diva.js\nAdds an adjustment icon next to each image\n\n*/\n\nvar jQuery = require('jquery');\nvar diva = require('../diva');\n\nrequire('../utils/jquery-extensions');\n\n(function ($)\n{\n module.exports = (function ()\n {\n var canvas = {},\n map = {},\n settings = {},\n image,\n sliders,\n sliderMode;\n\n // Set up some default settings (can be overridden the normal way)\n var defaults = {\n brightnessMax: 150,\n brightnessMin: -100,\n brightnessStep: 1,\n contrastMax: 3,\n contrastMin: -1,\n contrastStep: 0.05,\n localStoragePrefix: 'canvas-',\n mobileWebkitMaxZoom: 2,\n rgbMax: 50,\n rgbMin: -50,\n throbberFadeSpeed: 200,\n throbberTimeout: 100,\n buttons: [\n 'contrast',\n 'brightness',\n 'rotation',\n 'zoom'\n ]\n };\n\n // Convert an angle from degrees to radians\n var toRadians = function (angle)\n {\n return angle * Math.PI / 180;\n };\n\n // Determine the new center of the page after rotating by the given angle\n var getNewCenter = function (currentCenter, angle)\n {\n var x = currentCenter.x - canvas.centerX;\n // Take the negative because the rotation is counterclockwise\n var y = -(currentCenter.y - canvas.centerY);\n\n var theta = toRadians(sliders.rotation.previous - angle);\n var newX = Math.cos(theta) * x - Math.sin(theta) * y + canvas.centerX;\n var newY = -(Math.sin(theta) * x + Math.cos(theta) * y) + canvas.centerY;\n\n return {'x': newX, 'y': newY};\n };\n\n // Rotates the image on the given canvas by the given angle\n var rotateCanvas = function (aCanvas, angle)\n {\n var context = aCanvas.context;\n var center = aCanvas.size / 2;\n var startX = -(aCanvas.width / 2);\n var startY = -(aCanvas.height / 2);\n\n // Clear the canvas so that remnants of the old image don't show\n context.clearRect(0, 0, aCanvas.size, aCanvas.size);\n\n // Do the rotation\n context.save();\n context.translate(center, center);\n context.rotate(toRadians(angle));\n context.drawImage(image, startX, startY, aCanvas.width, aCanvas.height);\n context.restore();\n\n // Save the new pixel data so that it can later be adjusted in adjustLevels\n aCanvas.data = context.getImageData(0, 0, aCanvas.size, aCanvas.size);\n };\n\n // Determine if we need to update the large canvas\n var shouldAdjustLevels = function ()\n {\n var slider;\n\n // Returns true if something has been changed\n for (slider in sliders)\n {\n if (sliders[slider].current !== sliders[slider].previous)\n {\n return true;\n }\n }\n\n return false;\n };\n\n // Sets the \"previous\" value to the \"current\" value for every slider\n var updatePreviousLevels = function ()\n {\n var slider;\n\n for (slider in sliders)\n {\n sliders[slider].previous = sliders[slider].current;\n }\n };\n\n // Update the thumbnail preview (called when a slider is moved/reset)\n var updateMap = function ()\n {\n rotateCanvas(map, sliders.rotation.current);\n adjustLevels(map);\n };\n\n // Update the large canvas (rotation, zooming, scrolling, pixel manipulation)\n var updateCanvas = function ()\n {\n var angle = sliders.rotation.current;\n var oldAngle = sliders.rotation.previous;\n var zoomLevel = sliders.zoom.current;\n var oldZoomLevel = sliders.zoom.previous;\n\n // Scroll the user to the desired location\n if (angle !== oldAngle || zoomLevel !== oldZoomLevel)\n {\n // First figure out the current center of the viewport\n var leftScroll = $('#diva-canvas-wrapper').scrollLeft();\n var topScroll = $('#diva-canvas-wrapper').scrollTop();\n var leftOffset = settings.viewport.width / 2;\n var topOffset = settings.viewport.height / 2;\n\n // Then determine the new center (the same part of the image)\n var newCenter = getNewCenter({x: leftScroll + leftOffset, y: topScroll + topOffset}, angle);\n\n // Incorporate the zoom change ratio (would be 1 if no change)\n var zoomChange = Math.pow(2, zoomLevel - oldZoomLevel);\n var toLeftScroll = zoomChange * newCenter.x - leftOffset;\n var toTopScroll = zoomChange * newCenter.y - topOffset;\n\n // Rotate the large canvas\n rotateCanvas(canvas, angle);\n\n // Scroll to the new center\n $('#diva-canvas-wrapper').scrollLeft(toLeftScroll);\n $('#diva-canvas-wrapper').scrollTop(toTopScroll);\n }\n\n // Only call adjustLevels again if we really need to (expensive)\n if (shouldAdjustLevels())\n {\n adjustLevels(canvas);\n updatePreviousLevels();\n }\n };\n\n // Copies the canvas' pixel array and returns the copy\n var copyImageData = function (aCanvas)\n {\n var oldImageData = aCanvas.data;\n var newImageData = aCanvas.context.createImageData(oldImageData);\n var pixelArray = newImageData.data;\n var i, length;\n\n for (i = 0, length = pixelArray.length; i < length; i++)\n {\n pixelArray[i] = oldImageData.data[i];\n }\n\n return newImageData;\n };\n\n // Determines whether or not we need to adjust this level - very simple\n var shouldAdjust = function (mode)\n {\n var thisChanged = sliders[mode].current !== sliders[mode].previous;\n var thisNotDefault = sliders[mode].current !== sliders[mode].initial;\n\n return thisChanged || thisNotDefault;\n };\n\n var adjustLevels = function (aCanvas)\n {\n // Copy the pixel array to avoid destructively modifying the original\n var imageData = copyImageData(aCanvas);\n var pixelArray = imageData.data;\n\n // Store and calculate some scale factors and offsets\n var brightness = sliders.brightness.current;\n var contrast = sliders.contrast.current;\n\n var brightMul = 1 + Math.min(settings.brightnessMax, Math.max(settings.brightnessMin, brightness)) / settings.brightnessMax;\n var brightTimesContrast = brightMul * contrast;\n var contrastOffset = 128 - (contrast * 128);\n\n var redOffset = sliders.red.current;\n var greenOffset = sliders.green.current;\n var blueOffset = sliders.blue.current;\n\n // Determine whether or not we need to adjust certain things\n var adjustRed = shouldAdjust('red');\n var adjustGreen = shouldAdjust('green');\n var adjustBlue = shouldAdjust('blue');\n\n var adjustBrightness = shouldAdjust('brightness');\n var adjustContrast = shouldAdjust('contrast');\n var adjustOthers = adjustBrightness || adjustContrast;\n\n var x, y, width, height, offset, r, g, b;\n\n for (x = 0, width = imageData.width; x < width; x++)\n {\n for (y = 0, height = imageData.height; y < height; y++)\n {\n offset = (y * width + x) * 4;\n\n r = pixelArray[offset];\n g = pixelArray[offset + 1];\n b = pixelArray[offset + 2];\n\n // Only do something if the pixel is not black originally\n if (r + g + b > 0)\n {\n // Only adjust individual colour channels if necessary\n if (adjustRed && r)\n r += redOffset;\n\n if (adjustGreen && g)\n g += greenOffset;\n\n if (adjustBlue && b)\n b += blueOffset;\n\n // If we need to adjust brightness and/or contrast\n if (adjustOthers)\n {\n if (r)\n r = r * brightTimesContrast + contrastOffset;\n\n if (g)\n g = g * brightTimesContrast + contrastOffset;\n\n if (b)\n b = b * brightTimesContrast + contrastOffset;\n }\n\n pixelArray[offset] = r;\n pixelArray[offset + 1] = g;\n pixelArray[offset + 2] = b;\n }\n }\n }\n\n aCanvas.context.clearRect(0, 0, width, height);\n aCanvas.context.putImageData(imageData, 0, 0);\n };\n\n // Update the box in the preview showing where you currently are\n var updateViewbox = function ()\n {\n // Determine the top left corner coordinates based on our current position\n var cornerX = $('#diva-canvas-wrapper').scrollLeft() * map.scaleFactor;\n var cornerY = $('#diva-canvas-wrapper').scrollTop() * map.scaleFactor;\n\n // Subtract 4 to compensate for the borders\n var height = Math.min(Math.round(settings.viewport.height * map.scaleFactor), settings.mapSize) - 4;\n var width = Math.min(Math.round(settings.viewport.width * map.scaleFactor), settings.mapSize) - 4;\n\n $('#diva-map-viewbox').height(height).width(width).css({top: cornerY, left: cornerX});\n };\n\n // Draw the thumbnail preview in the toolbar\n var loadMap = function (image)\n {\n map.canvas = document.getElementById('diva-canvas-minimap');\n map.size = settings.mapSize;\n map.canvas.width = map.size;\n map.canvas.height = map.size;\n\n // Give it a black background\n map.context = map.canvas.getContext('2d');\n map.context.fillRect(0, 0, map.size, map.size);\n\n // Determine the coordinates/dimensions of the preview\n map.scaleFactor = settings.mapSize / canvas.size;\n map.cornerX = canvas.cornerX * map.scaleFactor;\n map.cornerY = canvas.cornerY * map.scaleFactor;\n map.width = image.width * map.scaleFactor;\n map.height = image.height * map.scaleFactor;\n\n // Draw the image within the map (no adjustments) and save the pixel array\n map.context.drawImage(image, map.cornerX, map.cornerY, map.width, map.height);\n map.data = map.context.getImageData(0, 0, settings.mapSize, settings.mapSize);\n\n // Show the viewbox, make it reflect where we currently are\n $('#diva-map-viewbox').show();\n updateViewbox();\n };\n\n // Load the image within the large and small canvases\n var loadCanvas = function (imageURL, callback)\n {\n image = new Image();\n image.crossOrigin = \"anonymous\";\n\n image.onload = function ()\n {\n // Determine the size of the (square) canvas based on the hypoteneuse\n canvas.size = Math.sqrt(image.width * image.width + image.height * image.height);\n\n // Resize the canvas if necessary\n canvas.canvas = document.getElementById('diva-canvas');\n canvas.canvas.width = canvas.size;\n canvas.canvas.height = canvas.size;\n canvas.cornerX = (canvas.size - image.width) / 2;\n canvas.cornerY = (canvas.size - image.height) / 2;\n canvas.width = image.width;\n canvas.height = image.height;\n canvas.centerX = canvas.size / 2;\n canvas.centerY = canvas.size / 2;\n\n // Draw the image to the large canvas, and save the pixel array\n canvas.context = canvas.canvas.getContext('2d');\n canvas.context.drawImage(image, canvas.cornerX, canvas.cornerY, canvas.width, canvas.height);\n try\n {\n canvas.data = canvas.context.getImageData(0, 0, canvas.size, canvas.size);\n }\n catch (error)\n {\n var canvasError = '

Error

' + error.message + '

';\n\n if (error.name === 'SecurityError')\n {\n canvasError += '

You may need to update your server configuration in order to use the image manipulation tools. ' +\n 'For help, see the canvas cross-site data documentation.

' +\n '
';\n }\n else\n {\n throw error;\n }\n\n canvasError += '';\n $('#diva-canvas-backdrop').append(canvasError);\n hideThrobber();\n }\n\n // Only load the map the first time (when there is no callback)\n if (callback === undefined) {\n loadMap(image);\n }\n\n // Update the map and the canvas if necessary\n updateMap();\n updateCanvas(canvas);\n\n // Hide the throbber if it is visible\n hideThrobber();\n\n // If the callback function exists, execute it (for zooming)\n if (typeof callback === 'function')\n callback.call(callback);\n };\n\n image.src = imageURL;\n\n // make sure the load event fires for cached images too\n if ( image.complete || image.complete === undefined ) {\n image.src = \"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==\";\n image.src = imageURL;\n }\n };\n\n var updateSliderLabel = function ()\n {\n var thisSlider = sliders[sliderMode];\n var value = thisSlider.current;\n var stringValue = (thisSlider.transform) ? thisSlider.transform(value) : value;\n $('#diva-canvas-value').html(stringValue);\n };\n\n var updateSliderValue = function ()\n {\n $('#diva-canvas-slider').val(sliders[sliderMode].current);\n };\n\n // Returns the URL for the image at the specified zoom level\n var getImageURL = function (zoomLevel)\n {\n var width = settings.zoomWidthRatio * Math.pow(2, zoomLevel);\n\n return settings.divaInstance.getPageImageURL(settings.selectedPageIndex, { width: width });\n };\n\n var showThrobber = function ()\n {\n // Only show the throbber if it will take a long time\n if (sliders.zoom.current > 0 || settings.mobileWebkit)\n $(settings.selector + 'throbber').addClass('canvas-throbber').show();\n };\n\n // Hides the loading indicator icon\n var hideThrobber = function ()\n {\n $(settings.selector + 'throbber').removeClass('canvas-throbber').hide();\n };\n\n // If any modifications have been applied, save them to localStorage\n var saveSettings = function ()\n {\n var sliderSettings = {};\n var changed = false;\n var storageKey = settings.localStoragePrefix + settings.filename;\n var slider;\n\n for (slider in sliders)\n {\n if (sliders[slider].previous !== sliders[slider].initial)\n {\n sliderSettings[slider] = sliders[slider].previous;\n changed = true;\n }\n }\n\n // If modifications need to be saved, update the canvas plugin icon\n if (changed)\n {\n settings.pluginIcon.addClass('new');\n storeObject(storageKey, sliderSettings);\n }\n else\n {\n settings.pluginIcon.removeClass('new');\n localStorage.removeItem(storageKey);\n }\n };\n\n // Handles zooming in when the zoom slider is changed and the change is applied\n var updateZoom = function (newZoomLevel, callback)\n {\n settings.zoomLevel = newZoomLevel;\n\n // Figure out the URL for the image at this new zoom level\n var imageURL = getImageURL(newZoomLevel);\n\n loadCanvas(imageURL, function ()\n {\n // Set the new scale factor and update the viewbox\n map.scaleFactor = map.size / canvas.size;\n updateViewbox();\n\n saveSettings();\n });\n };\n\n var bindCanvasKeyEvents = function (event)\n {\n var upArrowKey = 38,\n downArrowKey = 40,\n leftArrowKey = 37,\n rightArrowKey = 39;\n\n switch (event.keyCode)\n {\n case upArrowKey:\n // Up arrow - scroll up\n $('#diva-canvas-wrapper').scrollTop(document.getElementById('diva-canvas-wrapper').scrollTop - settings.arrowScrollAmount);\n return false;\n\n case downArrowKey:\n // Down arrow - scroll down\n $('#diva-canvas-wrapper').scrollTop(document.getElementById('diva-canvas-wrapper').scrollTop + settings.arrowScrollAmount);\n return false;\n\n case leftArrowKey:\n // Left arrow - scroll left\n $('#diva-canvas-wrapper').scrollLeft(document.getElementById('diva-canvas-wrapper').scrollLeft - settings.arrowScrollAmount);\n return false;\n\n case rightArrowKey:\n // Right arrow - scroll right\n $('#diva-canvas-wrapper').scrollLeft(document.getElementById('diva-canvas-wrapper').scrollLeft + settings.arrowScrollAmount);\n return false;\n }\n };\n\n // Serialize an object to JSON and save it in localStorage\n var storeObject = function (key, value) {\n localStorage.setItem(key, JSON.stringify(value));\n };\n\n // Load and deserialize a localStorage object\n var loadStoredObject = function (key) {\n var value = localStorage.getItem(key);\n return value && JSON.parse(value);\n };\n\n var retval =\n {\n init: function (divaSettings, divaInstance)\n {\n // If the browser does not support canvas, do nothing\n // And, disable this plugin\n var canvasSupported = !!window.HTMLCanvasElement;\n if (!canvasSupported)\n return false;\n\n // Override all the configurable settings defined under canvasPlugin\n $.extend(settings, defaults, divaSettings.canvasPlugin);\n\n settings.divaInstance = divaInstance;\n settings.inCanvas = false;\n settings.iipServerURL = divaSettings.iipServerURL;\n settings.imageDir = divaSettings.imageDir;\n settings.selector = divaSettings.selector;\n settings.mobileWebkit = divaSettings.mobileWebkit;\n settings.arrowScrollAmount = divaSettings.arrowScrollAmount;\n\n // Set up the settings for the sliders/icons\n sliders = {\n 'contrast': {\n 'initial': 1,\n 'min': settings.contrastMin,\n 'max': settings.contrastMax,\n 'step': settings.contrastStep,\n 'transform': function (value) {\n return value.toFixed(2);\n },\n 'title': 'Change the contrast'\n },\n 'brightness': {\n 'initial': 0,\n 'min': settings.brightnessMin,\n 'max': settings.brightnessMax,\n 'step': settings.brightnessStep,\n 'title': 'Adjust the brightness'\n },\n 'rotation': {\n 'initial': 0,\n 'min': 0,\n 'max': 359,\n 'step': 1,\n 'transform': function (value) {\n return value + '°';\n },\n 'title': 'Rotate the image'\n },\n 'zoom': {\n // Default, min and max values updated within setupHook\n 'initial': 0,\n 'min': 0,\n 'max': 0,\n 'step': 1,\n 'title': 'Adjust the zoom level'\n },\n 'red': {\n 'initial': 0,\n 'min': settings.rgbMin,\n 'max': settings.rgbMax,\n 'step': 1,\n 'title': 'Adjust the red channel'\n },\n 'green': {\n 'initial': 0,\n 'min': settings.rgbMin,\n 'max': settings.rgbMax,\n 'step': 1,\n 'title': 'Adjust the green channel'\n },\n 'blue': {\n 'initial': 0,\n 'min': settings.rgbMin,\n 'max': settings.rgbMax,\n 'step': 1,\n 'title': 'Adjust the blue channel'\n }\n };\n\n // Copy the \"default\" value into \"value\" and \"previous\" for each slider\n var resetSliders = function ()\n {\n var defaultValue, thisSlider, slider;\n for (slider in sliders)\n {\n thisSlider = sliders[slider];\n defaultValue = thisSlider.initial;\n thisSlider.current = defaultValue;\n thisSlider.previous = defaultValue;\n }\n };\n\n resetSliders();\n\n // Create the DOM elements if they haven't already been created\n if ($('#diva-canvas-backdrop').length)\n {\n // Return true to keep the plugin enabled\n return true;\n }\n\n var canvasButtonsList = [];\n var buttonHTML, button, buttonTitle, i;\n\n for (i in settings.buttons)\n {\n button = settings.buttons[i];\n buttonTitle = sliders[button].title;\n buttonHTML = '
';\n canvasButtonsList.push(buttonHTML);\n }\n var canvasButtons = canvasButtonsList.join('');\n\n var canvasTools = '
' +\n '
' +\n '
' +\n '
' +\n 'Test' +\n '
' +\n '
' +\n '
' +\n '' +\n '
' +\n canvasButtons +\n '
' +\n '
' +\n '

' +\n 'contrast: ' +\n '0 ' +\n '(Reset)' +\n '

' +\n '' +\n '
' +\n '
' +\n '
' +\n 'Reset all' +\n 'Apply' +\n '
' +\n '
' +\n '
';\n var canvasWrapper = '
' +\n '' +\n '
';\n var canvasString = '
' +\n canvasTools +\n canvasWrapper +\n '
';\n\n $('body').append(canvasString);\n\n // Save the size of the map, as defined in the CSS\n settings.mapSize = $('#diva-canvas-minimap').width();\n\n // Adjust the slider when something is clicked, and make that the current mode\n $('#diva-canvas-buttons div').click(function ()\n {\n $('#diva-canvas-buttons .clicked').removeClass('clicked');\n updateSlider($(this).attr('class'));\n });\n\n var updateSlider = function (newMode)\n {\n sliderMode = newMode;\n var sliderData = sliders[sliderMode];\n\n $('#diva-canvas-buttons .' + sliderMode).addClass('clicked');\n\n $('#diva-canvas-mode').text(sliderMode);\n\n var newValue = sliderData.current;\n var newValueString = (sliderData.transform) ? sliderData.transform(newValue) : newValue;\n\n var slider = document.getElementById('diva-canvas-slider');\n slider.min = sliderData.min;\n slider.max = sliderData.max;\n slider.step = sliderData.step;\n $('#diva-canvas-slider').val(newValue);\n $('#diva-canvas-value').html(newValueString);\n };\n\n updateSlider('contrast');\n\n // Create the slider\n $('#diva-canvas-slider').on('input', function(e){\n sliders[sliderMode].current = parseFloat(this.value);\n updateSliderLabel();\n updateMap();\n });\n\n // Reset all the sliders to the default value\n $('#diva-canvas-reset-all').click(function ()\n {\n var slider;\n\n for (slider in sliders)\n {\n sliders[slider].current = sliders[slider].initial;\n }\n\n // Change the value of the label\n updateSliderLabel();\n updateSliderValue();\n\n // Update the preview\n updateMap();\n });\n\n // Reset the current slider to the default value\n $('#diva-canvas-reset').click(function ()\n {\n // Update the current value and the slider\n sliders[sliderMode].current = sliders[sliderMode].initial;\n updateSliderLabel();\n updateSliderValue();\n\n // Update the preview\n updateMap();\n });\n\n // Update the large canvas when the apply button is clicked\n $('#diva-canvas-apply').click(function ()\n {\n if (shouldAdjustLevels())\n {\n showThrobber();\n\n setTimeout(function ()\n {\n if (sliders.zoom.current !== sliders.zoom.previous)\n {\n updateZoom(sliders.zoom.current);\n }\n else\n {\n updateCanvas();\n hideThrobber();\n\n // Save modifications to localSettings (also done in updateZoom callback)\n saveSettings();\n }\n }, settings.throbberTimeout);\n }\n });\n\n // Handle exiting canvas mode\n $('#diva-canvas-close').click(function ()\n {\n $('body').removeClass('overflow-hidden');\n\n // Clear the canvases and hide things\n // This needs to be improved - not done properly?\n canvas.context.clearRect(0, 0, canvas.size, canvas.size);\n map.context.clearRect(0, 0, map.size, map.size);\n $('#diva-canvas-wrapper').scrollTop(0).scrollLeft(0);\n $('#diva-canvas-backdrop').hide();\n $('#diva-map-viewbox').hide();\n hideThrobber();\n\n // Re-enable scrolling of diva when it is in the background\n divaInstance.enableScrollable();\n $(document).off('keydown', bindCanvasKeyEvents);\n\n // Reset everything\n resetSliders();\n updateSliderLabel();\n updateSliderValue();\n $('#diva-canvas-buttons .clicked').removeClass('clicked');\n updateSlider('contrast');\n\n diva.Events.publish(\"CanvasViewDidHide\");\n });\n\n // Hide the toolbar when the minimise icon is clicked\n $('#diva-canvas-minimise').click(function ()\n {\n $('#diva-canvas-toolwindow').slideToggle('fast');\n });\n\n // Adjust the size of the canvas when the browser window is resized\n $(window).resize(function ()\n {\n settings.viewport = {\n height: window.innerHeight - divaSettings.scrollbarWidth,\n width: window.innerWidth - divaSettings.scrollbarWidth\n };\n\n // Always update the settings but only redraw if in canvas\n if (settings.inCanvas)\n updateViewbox();\n });\n\n // Update the viewbox when the large canvas is scrolled\n $('#diva-canvas-wrapper').scroll(function ()\n {\n if (settings.inCanvas)\n updateViewbox();\n });\n\n // Handle clicking/dragging of the viewbox (should scroll the large canvas)\n $('#diva-canvas-minimap, #diva-map-viewbox').mouseup(function (event)\n {\n // Consider caching this eventually (can't be done in init though)\n var offset = $('#diva-canvas-minimap').offset();\n\n var scaledX = (event.pageX - offset.left) / map.scaleFactor;\n var scaledY = (event.pageY - offset.top) / map.scaleFactor;\n\n $('#diva-canvas-wrapper').scrollTop(scaledY - settings.viewport.height / 2);\n $('#diva-canvas-wrapper').scrollLeft(scaledX - settings.viewport.width / 2);\n });\n\n // Enable drag scroll\n $('#diva-canvas').mousedown(function ()\n {\n $(this).addClass('grabbing');\n }).mouseup(function ()\n {\n $(this).removeClass('grabbing');\n });\n\n // touch events\n $('#diva-canvas-wrapper').kinetic();\n\n // mouse events\n $('#diva-canvas-wrapper').dragscrollable({\n acceptPropagatedEvent: true\n });\n\n diva.Events.subscribe('ObjectDidLoad', this.setupHook, divaSettings.ID);\n diva.Events.subscribe('ViewerDidTerminate', this.destroy, divaSettings.ID);\n diva.Events.subscribe('PageDidLoad', this.onPageLoad, divaSettings.ID);\n\n return true;\n },\n\n pluginName: 'canvas',\n\n titleText: 'View the image on a canvas and adjust various settings',\n\n setupHook: function(divaSettings)\n {\n settings.viewport = {\n height: window.innerHeight - divaSettings.scrollbarWidth,\n width: window.innerWidth - divaSettings.scrollbarWidth\n };\n\n // Save the min and max zoom level, and update the zoom slider\n settings.minZoomLevel = divaSettings.minZoomLevel;\n settings.maxZoomLevel = divaSettings.maxZoomLevel;\n\n // If we're on the iPad, limit the max zoom level to 2\n // Can't do canvas elements that are > 5 megapixels (issue #112)\n if (settings.mobileWebkit)\n settings.maxZoomLevel = Math.min(settings.maxZoomLevel, settings.mobileWebkitMaxZoom);\n\n sliders.zoom.min = settings.minZoomLevel;\n sliders.zoom.max = settings.maxZoomLevel;\n },\n\n handleClick: function(event, divaSettings, divaInstance, selectedPageIndex)\n {\n // loadCanvas() calls all the other necessary functions to load\n var filename = divaInstance.getFilenames()[selectedPageIndex];\n\n // TODO: Move rationale for -1 from Wiki (TLDR an old IIP bug)\n var width = divaInstance\n .getPageDimensions(selectedPageIndex)\n .width - 1;\n\n var zoomLevel = divaSettings.zoomLevel;\n var slider;\n\n settings.zoomWidthRatio = width / Math.pow(2, zoomLevel);\n settings.pluginIcon = $(this);\n\n settings.manifest = divaSettings.manifest;\n settings.selectedPageIndex = selectedPageIndex;\n\n // Limit the max zoom level if we're on the iPad\n if (settings.mobileWebkit) {\n zoomLevel = Math.min(settings.maxZoomLevel, zoomLevel);\n }\n\n settings.filename = filename;\n sliders.zoom.initial = zoomLevel;\n sliders.zoom.current = zoomLevel;\n\n // Find the settings stored in localStorage, if they exist\n var sliderSettings = loadStoredObject(settings.localStoragePrefix + settings.filename);\n if (sliderSettings)\n {\n for (slider in sliderSettings)\n {\n sliders[slider].current = sliderSettings[slider];\n\n // If the current slider's value has changed, update it\n if (slider === sliderMode)\n {\n updateSliderLabel();\n updateSliderValue();\n }\n\n if (slider === 'zoom')\n {\n zoomLevel = sliderSettings[slider];\n }\n }\n }\n\n sliders.zoom.previous = zoomLevel;\n\n // Prevent scroll in body, and show the canvas backdrop\n $('body').addClass('overflow-hidden');\n $('#diva-canvas-backdrop').show();\n\n // Disable scrolling on main diva instance\n divaInstance.disableScrollable();\n // Enable canvas scrolling\n $(document).keydown(bindCanvasKeyEvents);\n\n // Set this to true so events can be captured\n settings.inCanvas = true;\n\n var imageURL = getImageURL(zoomLevel);\n\n // Change the title of the page\n // FIXME: This is legacy behaviour. Should this be a filename/label?\n $('#diva-canvas-info').text('Page ' + (selectedPageIndex + 1));\n\n showThrobber();\n\n diva.Events.publish('CanvasViewDidActivate', [selectedPageIndex]);\n\n loadCanvas(imageURL);\n },\n\n onPageLoad: function(pageIndex, filename, selector)\n {\n // If something exists for this page in localStorage, then change icon color\n var storageKey = settings.localStoragePrefix + filename;\n\n if (localStorage.getItem(storageKey) !== null)\n {\n $(selector).find('.diva-canvas-icon').addClass('new');\n }\n },\n\n destroy: function(divaSettings, divaInstance)\n {\n $('#diva-canvas-backdrop').remove();\n }\n };\n\n // this returns an object with all of the necessary hooks and callbacks\n // embedded.\n return retval;\n\n })();\n})(jQuery);\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/plugins/canvas.js\n// module id = 43\n// module chunks = 0","/*\nDownload plugin for diva.js\nAllows you to download images served by IIPImage or IIIF compatible image servers\n*/\n\nvar jQuery = require('jquery');\n\n(function ($)\n{\n module.exports = (function()\n {\n var settings = {};\n var retval =\n {\n init: function(divaSettings, divaInstance)\n {\n settings.divaInstance = divaInstance;\n return true;\n },\n pluginName: 'download',\n titleText: 'Download image at the given zoom level',\n handleClick: function(event, divaSettings, divaInstance, pageIndex)\n {\n // TODO: Move rationale for -1 from Wiki (TLDR an old IIP bug)\n var width = divaInstance\n .getPageDimensions(pageIndex)\n .width - 1;\n\n var image = settings.divaInstance.getPageImageURL(pageIndex, { width: width });\n\n window.open(image);\n }\n };\n\n return retval;\n })();\n})(jQuery);\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/plugins/download.js\n// module id = 44\n// module chunks = 0","/*\nHighlight plugin for diva.js\nAllows you to highlight regions of a page image\n*/\n\nvar jQuery = require('jquery');\nvar elt = require('../utils/elt');\nvar diva = require('../diva');\n\n(function ($)\n{\n module.exports = (function()\n {\n var retval =\n {\n init: function(divaSettings, divaInstance)\n {\n var highlightManager = new HighlightManager(divaInstance);\n divaSettings.parentObject.data('highlightManager', highlightManager);\n\n var currentHighlight;\n\n /*\n Reset the highlights object and removes all highlights from the document.\n */\n divaInstance.resetHighlights = function()\n {\n highlightManager.clear();\n };\n\n /*\n Resets the highlights for a single page.\n */\n divaInstance.removeHighlightsOnPage = function(pageIdx)\n {\n highlightManager.removeHighlightsOnPage(pageIdx);\n };\n\n /*\n Highlights regions on multiple pages.\n @param pageIdxs An array of page index numbers\n @param regions An array of regions\n @param colour (optional) A colour for the highlighting, specified in RGBA CSS format\n */\n divaInstance.highlightOnPages = function(pageIdxs, regions, colour, divClass)\n {\n var j = pageIdxs.length;\n while (j--)\n {\n divaInstance.highlightOnPage(pageIdxs[j], regions[j], colour, divClass);\n }\n };\n\n /*\n Highlights regions on a page.\n @param pageIdx A page index number\n @param regions An array of regions. Use {'width':i, 'height':i, 'ulx':i, 'uly': i, 'divID': str} for each region.\n @param colour (optional) A colour for the highlighting, specified in RGBA CSS format\n @param divClass (optional) A class to identify a group of highlighted regions on a specific page by\n */\n divaInstance.highlightOnPage = function(pageIdx, regions, colour, divClass)\n {\n if (colour === undefined)\n {\n colour = 'rgba(255, 0, 0, 0.2)';\n }\n\n if (divClass === undefined)\n {\n divClass = divaSettings.ID + 'highlight diva-highlight';\n }\n else\n {\n divClass = divaSettings.ID + 'highlight diva-highlight ' + divClass;\n }\n\n highlightManager.addHighlight({\n page: pageIdx,\n regions: regions,\n colour: colour,\n divClass: divClass\n });\n\n return true;\n };\n\n /*\n Jumps to a highlight somewhere in the document.\n @param divID The ID of the div to jump to. This ID must be attached to the div using .highlightOnPage(s) as the highlight may not be currently appended to the DOM.\n */\n divaInstance.gotoHighlight = function(divID)\n {\n var result = highlightManager.getHighlightByRegionId(divID);\n\n if (result)\n return gotoDiv(result.highlight.page, result.region);\n\n console.warn(\"Diva just tried to find a highlight that doesn't exist.\");\n return false;\n };\n\n /**\n * Moves the diva pane to (page) and makes a darker border on (thisDiv)\n */\n var gotoDiv = function(page, thisDiv)\n {\n //gets center of the div\n var centerYOfDiv = parseFloat(thisDiv.uly) + parseFloat(thisDiv.height) / 2;\n var centerXOfDiv = parseFloat(thisDiv.ulx) + parseFloat(thisDiv.width) / 2;\n\n var desiredY = divaInstance.translateFromMaxZoomLevel(centerYOfDiv);\n var desiredX = divaInstance.translateFromMaxZoomLevel(centerXOfDiv);\n\n //navigates to the page\n page = parseInt(page, 10);\n divaInstance.gotoPageByIndex(page);\n var viewportObject = divaInstance.getSettings().viewportObject;\n var currentTop = viewportObject.scrollTop() + desiredY - (viewportObject.height() / 2) + divaSettings.verticalPadding;\n var currentLeft = viewportObject.scrollLeft() + desiredX - (viewportObject.width() / 2) + divaSettings.horizontalPadding;\n\n //changes the scroll location to center on the div as much as is possible\n viewportObject.scrollTop(currentTop);\n viewportObject.scrollLeft(currentLeft);\n\n currentHighlight = {\n region: thisDiv,\n page: page\n };\n\n diva.Events.publish(\"SelectedHighlightChanged\", [thisDiv.id, currentHighlight.page]);\n\n //selects the highlight\n updateCurrentHighlight(divaInstance, currentHighlight);\n return thisDiv.id;\n };\n\n var getDivCenter = function(thisDiv)\n {\n if (divaSettings.verticallyOriented) return divaInstance.translateFromMaxZoomLevel(parseFloat(thisDiv.uly) + parseFloat(thisDiv.height) / 2);\n else return divaInstance.translateFromMaxZoomLevel(parseFloat(thisDiv.ulx) + parseFloat(thisDiv.width) / 2);\n };\n\n /*\n Jumps to the next highlight along the primary axis of the document.\n */\n var findAdjacentHighlight = function(forward)\n {\n var centerOfTargetDiv;\n var highlightFound = false;\n var centerOfCurrentDiv;\n var currentPage;\n var regionArr, arrIndex;\n var pageDims;\n var centerOfDiv, targetDiv;\n\n var thisDiv;\n var compFunction;\n\n // If currentHighlight does not already exists,\n // just pretend we're starting at the northwest corner of diva-inner\n if (!currentHighlight)\n {\n centerOfCurrentDiv = 0;\n currentPage = 0;\n }\n else {\n currentPage = currentHighlight.page;\n\n //find the center of the current div\n centerOfCurrentDiv = getDivCenter(currentHighlight.region);\n }\n\n //if we do have a current highlight, try to find the next one in the same page\n\n regionArr = highlightManager.getHighlightRegions(currentPage);\n arrIndex = regionArr.length;\n pageDims = divaInstance.getPageDimensionsAtZoomLevel(currentPage, divaInstance.getZoomLevel());\n\n //initialize the center of the div to the maximum possible value\n if(forward) centerOfTargetDiv = (divaSettings.verticallyOriented) ? pageDims.height : pageDims.width;\n else centerOfTargetDiv = 0;\n\n if(forward)\n {\n compFunction = function(thisC, curC, targetC)\n {\n return (thisC > curC && thisC < targetC);\n };\n }\n else\n {\n compFunction = function(thisC, curC, targetC)\n {\n return (thisC < curC && thisC > targetC);\n };\n }\n\n while(arrIndex--)\n {\n thisDiv = regionArr[arrIndex];\n centerOfDiv = getDivCenter(thisDiv);\n\n //if this div is farther along the main axis but closer than the current closest\n if (compFunction(centerOfDiv, centerOfCurrentDiv, centerOfTargetDiv))\n {\n //update targetDiv\n highlightFound = true;\n centerOfTargetDiv = centerOfDiv;\n targetDiv = thisDiv;\n }\n }\n\n //if a highlight was found on the current page that was next; this can get overwritten but we're still good\n if (highlightFound) return gotoDiv(currentPage, targetDiv);\n //if it wasn't found, continue on...\n\n //find the minimum div on the next page with highlights and loop around if necessary\n\n //find the next page in the pageArr; this will be in order\n var pageArr = highlightManager.getHighlightedPages();\n var curIdx = pageArr.indexOf(currentPage.toString());\n\n var targetPage;\n\n if(forward)\n {\n while (!targetPage || !divaInstance.isPageIndexValid (targetPage))\n {\n //default to first page, move to next if possible\n if (curIdx == pageArr.length - 1) targetPage = pageArr[0];\n else targetPage = pageArr[++curIdx];\n }\n }\n\n else\n {\n while (!targetPage || !divaInstance.isPageIndexValid (targetPage))\n {\n //default to last page, move to previous if possible\n if (curIdx === 0) targetPage = pageArr[pageArr.length - 1];\n else targetPage = pageArr[--curIdx];\n }\n }\n\n //reset regionArr and centerOfTargetDiv for the new page we're testing\n regionArr = highlightManager.getHighlightRegions(targetPage);\n arrIndex = regionArr.length;\n pageDims = divaInstance.getPageDimensionsAtZoomLevel(targetPage, divaInstance.getMaxZoomLevel());\n\n if(forward) centerOfTargetDiv = (divaSettings.verticallyOriented) ? pageDims.height : pageDims.width;\n else centerOfTargetDiv = 0;\n\n //find the minimum this time\n if(forward)\n {\n compFunction = function(thisC, targetC)\n {\n return (thisC < targetC);\n };\n }\n else\n {\n compFunction = function(thisC, targetC)\n {\n return (thisC > targetC);\n };\n }\n\n while(arrIndex--)\n {\n thisDiv = regionArr[arrIndex];\n centerOfDiv = getDivCenter(thisDiv);\n if (compFunction(centerOfDiv, centerOfTargetDiv))\n {\n highlightFound = true;\n centerOfTargetDiv = centerOfDiv;\n targetDiv = thisDiv;\n }\n }\n\n //we've found it this time, as there'll be a region in the full regionArr to be the minimum\n return gotoDiv(targetPage, targetDiv);\n };\n\n /*\n Jumps to the next highlight along the primary axis of the document.\n */\n divaInstance.gotoNextHighlight = function()\n {\n if (highlightManager.getHighlightCount() > 0)\n return findAdjacentHighlight(true);\n else\n return false;\n };\n\n /*\n Jumps to the previous highlight along the primary axis of the document.\n */\n divaInstance.gotoPreviousHighlight = function()\n {\n if (highlightManager.getHighlightCount() > 0)\n return findAdjacentHighlight(false);\n else\n return false;\n };\n\n diva.Events.subscribe('ViewerWillTerminate', this.destroy, divaSettings.ID);\n\n return true;\n },\n destroy: function (divaSettings)\n {\n var highlightManager = divaSettings.parentObject.data('highlightManager');\n highlightManager.clear();\n divaSettings.parentObject.removeData('highlightManager');\n },\n pluginName: 'highlight',\n titleText: 'Highlight regions of pages',\n\n // Exposed export\n HighlightManager: HighlightManager\n };\n return retval;\n })();\n})(jQuery);\n\n/** Manages the addition and removal of the page overlays which display the highlights */\nfunction HighlightManager(divaInstance, getCurrentHighlight)\n{\n this._divaInstance = divaInstance;\n this._overlays = {};\n this._getCurrentHighlight = getCurrentHighlight;\n}\n\nHighlightManager.prototype.getHighlightCount = function ()\n{\n var count = 0;\n Object.keys(this._overlays).forEach(function (key)\n {\n count += this._overlays[key].highlight.regions.length;\n }, this);\n\n return count;\n};\n\nHighlightManager.prototype.getHighlightRegions = function (pageIndex)\n{\n if (!this._overlays[pageIndex])\n return [];\n\n return this._overlays[pageIndex].highlight.regions;\n};\n\nHighlightManager.prototype.getHighlightedPages = function ()\n{\n // FIXME: Conceptually awkward that these are strings\n return Object.keys(this._overlays);\n};\n\nHighlightManager.prototype.getHighlightByRegionId = function (id)\n{\n for (var i in this._overlays)\n {\n if (!this._overlays.hasOwnProperty(i))\n continue;\n\n var regions = this._overlays[i].highlight.regions;\n for (var j in regions)\n {\n if (!regions.hasOwnProperty(j))\n continue;\n\n if (regions[j].divID === id)\n {\n return {\n highlight: this._overlays[i].highlight,\n region: regions[j]\n };\n }\n }\n }\n\n return null;\n};\n\nHighlightManager.prototype.addHighlight = function (highlight)\n{\n var existingOverlay = this._overlays[highlight.page];\n\n if (existingOverlay)\n this._divaInstance.__removePageOverlay(existingOverlay);\n\n var overlay = new HighlightPageOverlay(highlight, this._divaInstance, this._getCurrentHighlight);\n this._overlays[highlight.page] = overlay;\n this._divaInstance.__addPageOverlay(overlay);\n};\n\nHighlightManager.prototype.removeHighlightsOnPage = function (pageIndex)\n{\n if (!this._overlays[pageIndex])\n return;\n\n this._divaInstance.__removePageOverlay(this._overlays[pageIndex]);\n delete this._overlays[pageIndex];\n};\n\nHighlightManager.prototype.clear = function ()\n{\n for (var i in this._overlays)\n {\n if (!this._overlays.hasOwnProperty(i))\n continue;\n\n this._divaInstance.__removePageOverlay(this._overlays[i]);\n }\n\n this._overlays = {};\n};\n\n/**\n When a new page is loaded, this overlay will be called with the\n page index for the page. It looks at the 'highlights' data object\n set on the diva parent element, and determines whether\n highlights exist for that page.\n\n If so, the overlay will create and render elements for every\n highlighted box.\n\n @param highlight\n @param divaInstance\n @param getCurrentHighlight (optional)\n */\nfunction HighlightPageOverlay(highlight, divaInstance, getCurrentHighlight)\n{\n this.page = highlight.page;\n this.highlight = highlight;\n this._highlightRegions = [];\n this._divaInstance = divaInstance;\n this._getCurrentHighlight = getCurrentHighlight;\n}\n\nHighlightPageOverlay.prototype.mount = function ()\n{\n var divaSettings = this._divaInstance.getSettings();\n\n var highlight = this.highlight;\n var regions = highlight.regions;\n var colour = highlight.colour;\n var divClass = highlight.divClass;\n\n var j = regions.length;\n while (j--)\n {\n var region = regions[j];\n\n // FIXME: Use CSS class instead of inline style\n var box = elt('div', {\n class: divClass,\n style: {\n background: colour,\n border: \"1px solid #555\",\n position: \"absolute\",\n zIndex: 100\n }\n });\n\n if (region.divID !== undefined)\n {\n box.setAttribute('data-highlight-id', region.divID);\n }\n\n // Used by IIIFHighlight\n if (region.name !== undefined)\n {\n box.setAttribute('data-name', region.name);\n }\n\n this._highlightRegions.push({\n element: box,\n region: region\n });\n }\n\n this.refresh();\n\n var frag = document.createDocumentFragment();\n this._highlightRegions.forEach(function (highlight)\n {\n frag.appendChild(highlight.element);\n });\n\n divaSettings.innerElement.appendChild(frag);\n\n if (this._getCurrentHighlight)\n updateCurrentHighlight(this._divaInstance, this._getCurrentHighlight());\n\n diva.Events.publish(\"HighlightCompleted\", [this.page, this._divaInstance.getFilenames()[this.page]]);\n};\n\nHighlightPageOverlay.prototype.unmount = function ()\n{\n var innerElement = this._divaInstance.getSettings().innerElement;\n\n this._highlightRegions.forEach(function (highlight)\n {\n innerElement.removeChild(highlight.element);\n });\n\n this._highlightRegions = [];\n};\n\n// FIXME: Updating a box per highlight region might be too expensive\n// Maybe stick all the elements in a container and then scale it using CSS transforms?\nHighlightPageOverlay.prototype.refresh = function ()\n{\n var maxZoom = this._divaInstance.getMaxZoomLevel();\n\n var maxZoomWidth = this._divaInstance.getPageDimensionsAtZoomLevel(this.page, maxZoom).width;\n var currentWidth = this._divaInstance.getPageDimensions(this.page).width;\n var zoomDifference = Math.log(maxZoomWidth / currentWidth) / Math.log(2);\n\n var pageOffset = this._divaInstance.getPageOffset(this.page, {\n excludePadding: true,\n incorporateViewport: true\n });\n\n this._highlightRegions.forEach(function (highlight)\n {\n var region = highlight.region;\n\n elt.setAttributes(highlight.element, {\n style: {\n width: incorporateZoom(region.width, zoomDifference) + \"px\",\n height: incorporateZoom(region.height, zoomDifference) + \"px\",\n top: pageOffset.top + incorporateZoom(region.uly, zoomDifference) + \"px\",\n left: pageOffset.left + incorporateZoom(region.ulx, zoomDifference) + \"px\"\n }\n });\n });\n};\n\nfunction incorporateZoom(position, zoomDifference)\n{\n return position / Math.pow(2, zoomDifference);\n}\n\nfunction updateCurrentHighlight(divaInstance, currentHighlight)\n{\n var classString = divaInstance.getInstanceId() + \"selected-highlight\";\n var classElem = document.getElementsByClassName(classString);\n var idx;\n var box;\n var boxes;\n\n for (idx = 0; idx < classElem.length; idx++)\n {\n box = classElem[idx];\n if (box.id !== currentHighlight.id)\n {\n box.className = box.className.replace(' '+classString, '');\n box.style.border = \"1px solid #555\";\n }\n }\n\n if (divaInstance.isPageInViewport(currentHighlight.page))\n {\n boxes = document.querySelectorAll(\"*[data-highlight-id=\" + currentHighlight.id + \"]\");\n for(idx = 0; idx < boxes.length; idx++)\n {\n box = boxes[idx];\n box.className = box.className + \" \" + classString;\n box.style.border = \"2px solid #000\";\n }\n }\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/plugins/highlight.js\n// module id = 45\n// module chunks = 0","/*\nIIIF Highlight plugin for diva.js\nAllows you to highlight regions of a page image based off of annotations in a IIIF Manifest\n*/\n\nvar jQuery = require('jquery');\nvar diva = require('../diva');\nvar HighlightManager = require('./highlight').HighlightManager;\n\n(function ($)\n{\n module.exports = (function()\n {\n var settings = {};\n var retval =\n {\n init: function(divaSettings, divaInstance)\n {\n var highlightManager = new HighlightManager(divaInstance);\n divaSettings.parentObject.data('highlightManager', highlightManager);\n\n settings.highlightedPages = [];\n\n /*\n Reset the highlights object and removes all highlights from the document.\n */\n divaInstance.resetHighlights = function()\n {\n highlightManager.clear();\n };\n\n /*\n Resets the highlights for a single page.\n */\n divaInstance.removeHighlightsOnPage = function(pageIdx)\n {\n highlightManager.removeHighlightsOnPage(pageIdx);\n };\n\n divaInstance.hideHighlights = function()\n {\n settings.highlightsVisible = false;\n $(divaSettings.innerElement).addClass('annotations-hidden');\n };\n\n divaInstance.showHighlights = function()\n {\n settings.highlightsVisible = true;\n $(divaSettings.innerElement).removeClass('annotations-hidden');\n };\n\n /*\n Highlights regions on multiple pages.\n @param pageIdxs An array of page index numbers\n @param regions An array of regions\n @param colour (optional) A colour for the highlighting, specified in RGBA CSS format\n */\n divaInstance.highlightOnPages = function(pageIdxs, regions, colour, divClass)\n {\n var j = pageIdxs.length;\n while (j--)\n {\n divaInstance.highlightOnPage(pageIdxs[j], regions[j], colour, divClass);\n }\n };\n\n /*\n Highlights regions on a page.\n @param pageIdx A page index number\n @param regions An array of regions. Use {'width':i, 'height':i, 'ulx':i, 'uly': i, 'divID': str} for each region.\n @param colour (optional) A colour for the highlighting, specified in RGBA CSS format\n @param divClass (optional) A class to identify a group of highlighted regions on a specific page by\n */\n divaInstance.highlightOnPage = function(pageIdx, regions, colour, divClass)\n {\n if (colour === undefined)\n {\n colour = 'rgba(255, 0, 0, 0.2)';\n }\n\n if (divClass === undefined)\n {\n divClass = divaSettings.ID + 'highlight diva-highlight';\n }\n else\n {\n divClass = divaSettings.ID + 'highlight diva-highlight ' + divClass;\n }\n\n highlightManager.addHighlight({\n page: pageIdx,\n regions: regions,\n colour: colour,\n divClass: divClass\n });\n\n return true;\n };\n\n /*\n Jumps to a highlight somewhere in the document.\n @param divID The ID of the div to jump to. This ID must be attached to the div using .highlightOnPage(s) as the highlight may not be appended to the DOM.\n */\n divaInstance.gotoHighlight = function(divID)\n {\n var result = highlightManager.getHighlightByRegionId(divID);\n\n if (result)\n return gotoDiv(result.highlight.page, result.region);\n\n console.warn(\"Diva just tried to find a highlight that doesn't exist.\");\n return false;\n };\n\n /**\n * Moves the diva pane to (page)\n */\n var gotoDiv = function(page, thisDiv)\n {\n //gets center of the div\n var centerYOfDiv = parseFloat(thisDiv.uly) + parseFloat(thisDiv.height) / 2;\n var centerXOfDiv = parseFloat(thisDiv.ulx) + parseFloat(thisDiv.width) / 2;\n\n var desiredY = divaInstance.translateFromMaxZoomLevel(centerYOfDiv);\n var desiredX = divaInstance.translateFromMaxZoomLevel(centerXOfDiv);\n\n //navigates to the page\n page = parseInt(page, 10);\n divaInstance.gotoPageByIndex(page);\n var viewportObject = divaInstance.getSettings().viewportObject;\n var currentTop = viewportObject.scrollTop() + desiredY - (viewportObject.height() / 2) + divaSettings.verticalPadding;\n var currentLeft = viewportObject.scrollLeft() + desiredX - (viewportObject.width() / 2) + divaSettings.horizontalPadding;\n\n //changes the scroll location to center on the div as much as is possible\n viewportObject.scrollTop(currentTop);\n viewportObject.scrollLeft(currentLeft);\n };\n\n var showAnnotations = function(canvasIndex)\n {\n return function(data, status, jqXHR)\n {\n var canvasAnnotations = data;\n var numAnnotations = data.length;\n\n //convert annotations in annotations object to diva highlight objects\n var regions = [];\n\n //loop over annotations in a single canvas\n for (var k = 0; k < numAnnotations; k++)\n {\n var currentAnnotation = canvasAnnotations[k];\n // get text content\n var text = currentAnnotation.resource.chars;\n\n // get x,y,w,h (slice string from '#xywh=' to end)\n var onString = currentAnnotation.on;\n var coordString = onString.slice(onString.indexOf('#xywh=') + 6);\n var coordinates = coordString.split(',');\n\n var region = {\n ulx: parseInt(coordinates[0], 10),\n uly: parseInt(coordinates[1], 10),\n width: parseInt(coordinates[2], 10),\n height: parseInt(coordinates[3], 10),\n name: text\n };\n\n regions.push(region);\n }\n\n divaInstance.highlightOnPage(canvasIndex, regions);\n //flag this page's annotations as having been retrieved\n settings.highlightedPages.push(canvasIndex);\n };\n };\n\n var getAnnotationsList = function(pageIndex)\n {\n //if page has annotationList\n var canvases = settings.manifest.sequences[0].canvases;\n\n if (canvases[pageIndex].hasOwnProperty('otherContent'))\n {\n var otherContent = canvases[pageIndex].otherContent;\n\n for (var j = 0; j < otherContent.length; j++)\n {\n if (otherContent[j]['@type'] === 'sc:AnnotationList')\n {\n // canvas has annotations. get the annotations:\n $.ajax({\n url: otherContent[j]['@id'],\n cache: true,\n dataType: 'json',\n success: showAnnotations(pageIndex)\n });\n }\n }\n }\n };\n\n var setManifest = function(manifest)\n {\n settings.manifest = manifest;\n };\n\n diva.Events.subscribe('ManifestDidLoad', setManifest, divaSettings.ID);\n\n diva.Events.subscribe('PageWillLoad', function(pageIndex)\n {\n if (!settings.highlightsVisible)\n {\n return;\n }\n\n //if highlights for this page have already been checked/loaded, return\n for (var i = 0; i < settings.highlightedPages.length; i++)\n {\n if (settings.highlightedPages[i] === pageIndex)\n {\n return;\n }\n }\n\n getAnnotationsList(pageIndex, settings.manifest);\n }, divaSettings.ID);\n\n var activeOverlays = [];\n\n //on mouseover, show the annotation text\n divaSettings.innerObject.on('mouseenter', '.' + divaSettings.ID + 'highlight', function(e)\n {\n var annotationElement = e.target;\n var name = annotationElement.dataset.name;\n var textOverlay = document.createElement('div');\n\n textOverlay.style.top = (annotationElement.offsetTop + annotationElement.offsetHeight - 1) + 'px';\n textOverlay.style.left = annotationElement.style.left;\n textOverlay.style.background = '#fff';\n textOverlay.style.border = '1px solid #555';\n textOverlay.style.position = 'absolute';\n textOverlay.style.zIndex = 101;\n textOverlay.className = 'annotation-overlay';\n textOverlay.textContent = name;\n\n annotationElement.parentNode.appendChild(textOverlay);\n activeOverlays.push(textOverlay);\n });\n\n divaSettings.innerObject.on('mouseleave', '.' + divaSettings.ID + 'highlight', function(e)\n {\n while (activeOverlays.length)\n {\n var textOverlay = activeOverlays.pop();\n textOverlay.parentNode.removeChild(textOverlay);\n }\n });\n\n diva.Events.subscribe('ViewerDidLoad', function(){\n //button to toggle annotations\n $('#' + divaSettings.ID + 'page-nav').before('
');\n\n $(divaSettings.selector + 'annotations-icon').addClass('annotations-icon-active');\n\n $('#' + divaSettings.ID + 'annotations-icon').on('click', function(e)\n {\n //toggle visibility of annotations\n if (settings.highlightsVisible)\n {\n divaInstance.hideHighlights();\n $(divaSettings.selector + 'annotations-icon').removeClass('annotations-icon-active');\n }\n else\n {\n divaInstance.showHighlights();\n $(divaSettings.selector + 'annotations-icon').addClass('annotations-icon-active');\n }\n });\n }, divaSettings.ID);\n\n //enable annotations by default\n settings.highlightsVisible = true;\n\n return true;\n },\n destroy: function (divaSettings, divaInstance)\n {\n divaSettings.parentObject.removeData('highlights');\n },\n pluginName: 'IIIFHighlight',\n titleText: 'Highlight regions of pages'\n };\n return retval;\n })();\n})(jQuery);\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/plugins/iiif-highlight.js\n// module id = 46\n// module chunks = 0","// IIIF Metadata plugin for diva.js\n// Displays object metadata from a IIIF manifest\n\nvar jQuery = require('jquery');\nvar diva = require('../diva');\n\n(function ($)\n{\n module.exports = (function()\n {\n var retval =\n {\n init: function(divaSettings, divaInstance)\n {\n var _displayMetadata = function(manifest)\n {\n var showMetadata = function(label, value)\n {\n var labelProper = label.charAt(0).toUpperCase() + label.slice(1);\n var labelFormatted = labelProper.replace('_', ' ');\n\n if (value.match(/^https?:\\/\\//))\n {\n value = '' + value + '';\n }\n\n return '';\n };\n\n var getDataForLanguage = function(data, language)\n {\n for (var i = 0; i < data.length; i++)\n {\n if (data[i]['@language'] === language)\n {\n return data[i]['@value'];\n }\n }\n\n // Handle the case where no language is specified, or when a single object is passed\n return data[0]['@value'] || data['@value'];\n };\n\n /**\n * Shows metadata from label names (if the metadata exists).\n * @param names {Array} - An array of strings representing field names to display.\n */\n var showMetadataFromLabelNames = function(names)\n {\n var elements = '';\n\n for (var i = 0; i < names.length; i++)\n {\n var field = names[i];\n\n if (manifest.hasOwnProperty(field))\n {\n if (manifest[field].constructor === Array)\n {\n //multiple languages\n var localizedData = getDataForLanguage(manifest[field], 'en');\n elements += showMetadata(field, localizedData);\n }\n else\n {\n elements += showMetadata(field, manifest[field]);\n }\n }\n }\n\n return elements;\n };\n\n var metadataElement = '
';\n metadataElement += showMetadataFromLabelNames(['label']);\n\n if (manifest.hasOwnProperty('metadata'))\n {\n var metadataField = manifest.metadata;\n\n for (var i = 0; i < metadataField.length; i++)\n {\n if (metadataField[i].value.constructor === Array)\n {\n var canonicalData = getDataForLanguage(metadataField[i].value, 'en');\n metadataElement += showMetadata(metadataField[i].label, canonicalData);\n }\n else\n {\n metadataElement += showMetadata(metadataField[i].label, metadataField[i].value);\n }\n }\n }\n\n metadataElement += showMetadataFromLabelNames([\n 'description',\n 'within',\n 'see_also',\n 'license',\n 'attribution'\n ]);\n\n metadataElement += '
';\n\n divaSettings.parentObject.prepend(metadataElement);\n $(divaSettings.selector + 'metadata').hide();\n };\n\n //subscribe to ManifestDidLoad event, get the manifest\n diva.Events.subscribe('ManifestDidLoad', _displayMetadata, divaSettings.ID);\n\n divaSettings.parentObject.prepend('');\n // $(divaSettings.selector + 'title').append('');\n\n $(divaSettings.selector + 'metadata-link').on('click', function(e)\n {\n $(divaSettings.selector + 'metadata').fadeToggle('fast');\n });\n\n return true;\n },\n destroy: function (divaSettings, divaInstance)\n {\n },\n pluginName: 'IIIFMetadata',\n titleText: 'Show metadata from a IIIF manifest'\n };\n return retval;\n })();\n})(jQuery);\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./source/js/plugins/iiif-metadata.js\n// module id = 47\n// module chunks = 0"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACVA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;ACtCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACPA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;AClDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AC1LA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACjBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AC1PA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACtpCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACzDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AC9FA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AC5DA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACtEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACvPA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACtQA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AC/rBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACp9CA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACjtBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AC3BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AC9LA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACtJA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;ACztEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACTA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AC7EA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACzGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACrGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AC7oBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;AC1LA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACvLA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACzMA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACvJA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AC/MA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AChSA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AClHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AClFA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AC9GA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACvCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACrKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACRA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AC5BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACjGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AC9BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACnKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AChJA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACt8BA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACpCA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;AC9jBA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;ACvSA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;","sourceRoot":""} \ No newline at end of file diff --git a/build/js/diva.min.js b/build/js/diva.min.js deleted file mode 100644 index 27c0782d..00000000 --- a/build/js/diva.min.js +++ /dev/null @@ -1,13 +0,0 @@ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("jquery")):"function"==typeof define&&define.amd?define(["jquery"],t):"object"==typeof exports?exports.diva=t(require("jquery")):e.diva=t(e.jQuery)}(this,function(e){return function(e){function t(n){if(i[n])return i[n].exports;var o=i[n]={exports:{},id:n,loaded:!1};return e[n].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var i={};return t.m=e,t.c=i,t.p="",t(0)}([function(e,t,i){i(38),e.exports=i(2)},function(t,i){t.exports=e},function(e,t,i){i(11);var n=i(1),o=i(3),r=i(44),a=i(16),s=i(5),l=i(23),c=i(41),u=i(46);new a;e.exports=s,function(e){var t=function(t,i){var n,a,h,d=this;i=e.extend({adaptivePadding:.05,arrowScrollAmount:40,blockMobileMove:!1,objectData:"",enableAutoTitle:!0,enableFilename:!0,enableFullscreen:!0,enableGotoPage:!0,enableGotoSuggestions:!0,enableGridIcon:!0,enableGridControls:"buttons",enableImageTitles:!0,enableKeyScroll:!0,enableLinkIcon:!0,enableNonPagedVisibilityIcon:!0,enableSpaceScroll:!1,enableToolbar:!0,enableZoomControls:"buttons",fillParentHeight:!0,fixedPadding:10,fixedHeightGrid:!0,goDirectlyTo:0,hashParamSuffix:null,iipServerURL:"",inFullscreen:!1,inBookLayout:!1,inGrid:!1,imageDir:"",maxPagesPerRow:8,maxZoomLevel:-1,minPagesPerRow:2,minZoomLevel:0,onGotoSubmit:null,pageAliases:{},pageAliasFunction:function(){return!1},pageLoadTimeout:200,pagesPerRow:5,showNonPagedPages:!1,throbberTimeout:100,tileHeight:256,tileWidth:256,toolbarParentObject:null,verticallyOriented:!0,viewportMargin:200,zoomLevel:2},i);var f=function(e){return p(n.manifest,e)},g=function(e){return e},p=function(e,t){var i,n=e.pages.length;for(i=0;i=0&&o=0&&on.maxZoomLevel&&(t=n.maxZoomLevel);var i=n.manifest.pages[parseInt(e,10)],o=i.d[parseInt(t,10)];return{width:o.w,height:o.h}},this.getPageDimensionsAtCurrentZoomLevel=function(e){if(e=v(e)?e:n.currentPageIndex,!v(e))throw new Error("Invalid Page Index");return h.viewerCore.getCurrentLayout().getPageDimensions(e)},this.getCurrentPageDimensionsAtCurrentZoomLevel=function(){return this.getPageDimensionsAtCurrentZoomLevel(n.currentPageIndex)},this.isReady=function(){return a.loaded},this.getCurrentPageIndex=function(){return n.currentPageIndex},this.getCurrentPageFilename=function(){return n.manifest.pages[n.currentPageIndex].f},this.getCurrentCanvas=function(e){return e.manifest.pages[e.currentPageIndex].canvas},this.getCurrentPageNumber=function(){return console.warn("This method is deprecated. Consider using getCurrentPageIndex() instead."),n.currentPageIndex+1},this.getFilenames=function(){for(var e=[],t=0;t-1)return this.gotoPageByIndex(r,t,i);var s=parseInt(e,10)-1;return this.gotoPageByIndex(s,t,i)},this.getPageIndex=function(e){return f(e)},this.getCurrentURL=function(){return I()},this.isPageIndexValid=function(e){return v(e)},this.getURLHash=function(){return x()},this.getState=function(){return _()},this.setState=function(e){m(P(e))},this.getInstanceSelector=function(){return n.selector},this.getInstanceId=function(){return n.ID},this.getSettings=function(){return n},this.translateFromMaxZoomLevel=function(e){var t=n.maxZoomLevel-n.zoomLevel;return e/Math.pow(2,t)},this.translateToMaxZoomLevel=function(e){var t=n.maxZoomLevel-n.zoomLevel;return 0===t?e:e*Math.pow(2,t)},this.enableScrollable=function(){h.viewerCore.enableScrollable()},this.disableScrollable=function(){h.viewerCore.disableScrollable()},this.toggleOrientation=function(){return y()},this.getPageOffset=function(e,t){var i=h.viewerCore.getPageRegion(e,t);return{top:i.top,left:i.left}},this.getCurrentPageOffset=function(){return this.getPageOffset(n.currentPageIndex)},this.getPageDimensionsAtCurrentGridLevel=function(e){return console.warn("This method is deprecated. Consider using getPageDimensionsAtCurrentZoomLevel(pageIndex) instead."),this.getPageDimensionsAtCurrentZoomLevel(e)},this.getPageIndexForPageXYValues=function(e,t){var i=a.outerElement.getBoundingClientRect(),n=i.top,o=i.left,r=i.bottom,s=i.right;if(es)return-1;if(tr)return-1;for(var l=document.getElementsByClassName("diva-page"),c=l.length;c--;){var u=l[c],h=u.getBoundingClientRect();if(!(eh.right||th.bottom))return u.getAttribute("data-index")}return-1},this.getPageCoordinatesHit=function(e,t){return a.renderer?a.renderer.getPageHit(e,t):null},this.getPageImageURL=function(e,t){return n.manifest.getPageImageURL(e,t)},this.isVerticallyOriented=function(){return n.verticallyOriented},this.changeObject=function(t){return a.loaded=!1,h.viewerCore.clear(),a.renderer&&a.renderer.destroy(),a.options.objectData=t,"object"==typeof t?void setTimeout(function(){D(t)}):(a.throbberTimeoutID=setTimeout(function(){e(n.selector+"throbber").show()},n.throbberTimeout),void e.ajax({url:n.objectData,cache:!0,dataType:"json",error:O,success:function(e){D(e)}}))},this.activate=function(){a.isActiveDiva=!0},this.deactivate=function(){a.isActiveDiva=!1},this.destroy=function(){h.viewerCore.destroy()},this.__addPageOverlay=function(e){h.viewerCore.addPageOverlay(e)},this.__removePageOverlay=function(e){h.viewerCore.removePageOverlay(e)},this.getAliasForPageIndex=function(e){var t=parseInt(e,10);return n.pageAliases[t]||n.pageAliasFunction(t)||t+1},this.getPageIndexForAlias=function(e){for(var t=0;t=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/))}function r(e){var i=this.useColors;if(e[0]=(i?"%c":"")+this.namespace+(i?" %c":" ")+e[0]+(i?"%c ":" ")+"+"+t.humanize(this.diff),i){var n="color: "+this.color;e.splice(1,0,n,"color: inherit");var o=0,r=0;e[0].replace(/%[a-zA-Z%]/g,function(e){"%%"!==e&&(o++,"%c"===e&&(r=o))}),e.splice(r,0,n)}}function a(){return"object"==typeof console&&console.log&&Function.prototype.apply.call(console.log,console,arguments)}function s(e){try{null==e?t.storage.removeItem("debug"):t.storage.debug=e}catch(e){}}function l(){var e;try{e=t.storage.debug}catch(e){}return!e&&"undefined"!=typeof n&&"env"in n&&(e=n.env.DEBUG),e}function c(){try{return window.localStorage}catch(e){}}t=e.exports=i(12),t.log=a,t.formatArgs=r,t.save=s,t.load=l,t.useColors=o,t.storage="undefined"!=typeof chrome&&"undefined"!=typeof chrome.storage?chrome.storage.local:c(),t.colors=["lightseagreen","forestgreen","goldenrod","dodgerblue","darkorchid","crimson"],t.formatters.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}},t.enable(l())}).call(t,i(14))},function(e,t,i){function n(e){var t=e.id?"#"+e.id:e.id,i=e.className?"."+e.className.split(/\s+/g).join("."):"";return(t?t:e.tagName.toLowerCase())+i}var o=i(1),r=i(42),a=i(8),s=e.exports={Events:new r,registerPlugin:function(e){a.register(e)},create:function(e,t){if(s.find(e))throw new Error("Diva is already initialized on "+n(e));var i=o(e);return i.diva(t),i.data("diva")},find:function(e){return o(e).data("diva")||null}}},function(e,t,i){(function(e,i){function n(e,t){for(var i=-1,n=e?e.length:0;++i-1}function x(e,t){var i=this.__data__,n=Z(i,e);return n<0?i.push([e,t]):i[n][1]=t,this}function I(e){var t=-1,i=e?e.length:0;for(this.clear();++tt}function W(e,t){return null!=e&&t in Object(e)}function Y(e,t,i,n,o){return e===t||(null==e||null==t||!Oe(e)&&!De(t)?e!==e&&t!==t:q(e,t,Y,i,n,o))}function q(e,t,i,n,o,r){var a=hi(e),s=hi(t),c=We,u=We;a||(c=ci(e),c=c==Be?Qe:c),s||(u=ci(t),u=u==Be?Qe:u);var h=c==Qe&&!l(e),d=u==Qe&&!l(t),f=c==u;if(f&&!h)return r||(r=new S),a||di(e)?oe(e,t,i,n,o,r):re(e,t,c,i,n,o,r);if(!(o&He)){var g=h&&Ht.call(e,"__wrapped__"),p=d&&Ht.call(t,"__wrapped__");if(g||p){var v=g?e.value():e,m=p?t.value():t;return r||(r=new S),i(v,m,n,o,r)}}return!!f&&(r||(r=new S),ae(e,t,i,n,o,r))}function U(e,t,i,n){var o=i.length,r=o,a=!n;if(null==e)return!r;for(e=Object(e);o--;){var s=i[o];if(a&&s[2]?s[1]!==e[s[0]]:!(s[0]in e))return!1}for(;++ol))return!1;var u=a.get(e);if(u&&a.get(t))return u==t;var h=-1,d=!0,f=r&Ze?new k:void 0;for(a.set(e,t),a.set(t,e);++h-1&&e%1==0&&e-1&&e%1==0&&e<=Ge}function Oe(e){var t=typeof e;return!!e&&("object"==t||"function"==t)}function De(e){return!!e&&"object"==typeof e}function Te(e){return"symbol"==typeof e||De(e)&&Nt.call(e)==ot}function ke(e){return null==e?"":ie(e)}function Ee(e,t,i){var n=null==e?void 0:N(e,t);return void 0===n?i:n}function Me(e,t){return null!=e&&ue(e,t,W)}function Se(e){return xe(e)?V(e):J(e)}function ze(e){return e}function Ae(e){return de(e)?o(we(e)):te(e)}function je(e,t){return e&&e.length?H(e,K(t,2),B):void 0}var Re=200,Fe="Expected a function",Ve="__lodash_hash_undefined__",Ze=1,He=2,Ne=1/0,Ge=9007199254740991,Be="[object Arguments]",We="[object Array]",Ye="[object Boolean]",qe="[object Date]",Ue="[object Error]",Xe="[object Function]",$e="[object GeneratorFunction]",Ke="[object Map]",Je="[object Number]",Qe="[object Object]",et="[object Promise]",tt="[object RegExp]",it="[object Set]",nt="[object String]",ot="[object Symbol]",rt="[object WeakMap]",at="[object ArrayBuffer]",st="[object DataView]",lt="[object Float32Array]",ct="[object Float64Array]",ut="[object Int8Array]",ht="[object Int16Array]",dt="[object Int32Array]",ft="[object Uint8Array]",gt="[object Uint8ClampedArray]",pt="[object Uint16Array]",vt="[object Uint32Array]",mt=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,wt=/^\w*$/,yt=/^\./,bt=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,_t=/[\\^$.*+?()[\]{}|]/g,Pt=/\\(\\)?/g,xt=/^\[object .+?Constructor\]$/,It=/^(?:0|[1-9]\d*)$/,Lt={};Lt[lt]=Lt[ct]=Lt[ut]=Lt[ht]=Lt[dt]=Lt[ft]=Lt[gt]=Lt[pt]=Lt[vt]=!0,Lt[Be]=Lt[We]=Lt[at]=Lt[Ye]=Lt[st]=Lt[qe]=Lt[Ue]=Lt[Xe]=Lt[Ke]=Lt[Je]=Lt[Qe]=Lt[tt]=Lt[it]=Lt[nt]=Lt[rt]=!1;var Ct="object"==typeof e&&e&&e.Object===Object&&e,Ot="object"==typeof self&&self&&self.Object===Object&&self,Dt=Ct||Ot||Function("return this")(),Tt="object"==typeof t&&t&&!t.nodeType&&t,kt=Tt&&"object"==typeof i&&i&&!i.nodeType&&i,Et=kt&&kt.exports===Tt,Mt=Et&&Ct.process,St=function(){try{return Mt&&Mt.binding("util")}catch(e){}}(),zt=St&&St.isTypedArray,At=Array.prototype,jt=Function.prototype,Rt=Object.prototype,Ft=Dt["__core-js_shared__"],Vt=function(){var e=/[^.]+$/.exec(Ft&&Ft.keys&&Ft.keys.IE_PROTO||"");return e?"Symbol(src)_1."+e:""}(),Zt=jt.toString,Ht=Rt.hasOwnProperty,Nt=Rt.toString,Gt=RegExp("^"+Zt.call(Ht).replace(_t,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),Bt=Dt.Symbol,Wt=Dt.Uint8Array,Yt=Rt.propertyIsEnumerable,qt=At.splice,Ut=u(Object.keys,Object),Xt=ce(Dt,"DataView"),$t=ce(Dt,"Map"),Kt=ce(Dt,"Promise"),Jt=ce(Dt,"Set"),Qt=ce(Dt,"WeakMap"),ei=ce(Object,"create"),ti=ye(Xt),ii=ye($t),ni=ye(Kt),oi=ye(Jt),ri=ye(Qt),ai=Bt?Bt.prototype:void 0,si=ai?ai.valueOf:void 0,li=ai?ai.toString:void 0;d.prototype.clear=f,d.prototype.delete=g,d.prototype.get=p,d.prototype.has=v,d.prototype.set=m,w.prototype.clear=y,w.prototype.delete=b,w.prototype.get=_,w.prototype.has=P,w.prototype.set=x,I.prototype.clear=L,I.prototype.delete=C,I.prototype.get=O,I.prototype.has=D,I.prototype.set=T,k.prototype.add=k.prototype.push=E,k.prototype.has=M,S.prototype.clear=z,S.prototype.delete=A,S.prototype.get=j,S.prototype.has=R,S.prototype.set=F;var ci=G;(Xt&&ci(new Xt(new ArrayBuffer(1)))!=st||$t&&ci(new $t)!=Ke||Kt&&ci(Kt.resolve())!=et||Jt&&ci(new Jt)!=it||Qt&&ci(new Qt)!=rt)&&(ci=function(e){var t=Nt.call(e),i=t==Qe?e.constructor:void 0,n=i?ye(i):void 0;if(n)switch(n){case ti:return st;case ii:return Ke;case ni:return et;case oi:return it;case ri:return rt}return t});var ui=be(function(e){e=ke(e);var t=[];return yt.test(e)&&t.push(""),e.replace(bt,function(e,i,n,o){t.push(n?o.replace(Pt,"$1"):i||e)}),t});be.Cache=I;var hi=Array.isArray,di=zt?a(zt):$;i.exports=je}).call(t,function(){return this}(),i(15)(e))},function(e,t){e.exports=function(e,t){var i=t.getMaxPageDimensions(e);return{width:Math.floor(i.width),height:Math.floor(i.height)}}},function(e,t){var i=[];e.exports={register:function(e){i.push(e)},getAll:function(){return i}}},function(e,t,i){function n(e,t){this._divaInstance=e,this._overlays={},this._getCurrentHighlight=t}function o(e,t,i){this.page=e.page,this.highlight=e,this._highlightRegions=[],this._divaInstance=t,this._getCurrentHighlight=i}function r(e,t){return e/Math.pow(2,t)}function a(e,t){var i,n,o,r=e.getInstanceId()+"selected-highlight",a=document.getElementsByClassName(r);for(i=0;it&&ei};h--;)p=u[h],f=s(p),v(f,l,a)&&(m=!0,a=f,g=p);if(m)return r(c,g);var w,y=i.getHighlightedPages(),b=y.indexOf(c.toString());if(n)for(;!w||!t.isPageIndexValid(w);)w=b==y.length-1?y[0]:y[++b];else for(;!w||!t.isPageIndexValid(w);)w=0===b?y[y.length-1]:y[--b];for(u=i.getHighlightRegions(w),h=u.length,d=t.getPageDimensionsAtZoomLevel(w,t.getMaxZoomLevel()),a=n?e.verticallyOriented?d.height:d.width:0,v=n?function(e,t){return et};h--;)p=u[h],f=s(p),v(f,a)&&(m=!0,a=f,g=p);return r(w,g)};return t.gotoNextHighlight=function(){return i.getHighlightCount()>0&&l(!0)},t.gotoPreviousHighlight=function(){return i.getHighlightCount()>0&&l(!1)},c.Events.subscribe("ViewerWillTerminate",this.destroy,e.ID),!0},destroy:function(e){var t=e.parentObject.data("highlightManager");t.clear(),e.parentObject.removeData("highlightManager")},pluginName:"highlight",titleText:"Highlight regions of pages",HighlightManager:n};return e}()}(s),n.prototype.getHighlightCount=function(){var e=0;return Object.keys(this._overlays).forEach(function(t){e+=this._overlays[t].highlight.regions.length},this),e},n.prototype.getHighlightRegions=function(e){return this._overlays[e]?this._overlays[e].highlight.regions:[]},n.prototype.getHighlightedPages=function(){return Object.keys(this._overlays)},n.prototype.getHighlightByRegionId=function(e){for(var t in this._overlays)if(this._overlays.hasOwnProperty(t)){var i=this._overlays[t].highlight.regions;for(var n in i)if(i.hasOwnProperty(n)&&i[n].divID===e)return{highlight:this._overlays[t].highlight,region:i[n]}}return null},n.prototype.addHighlight=function(e){var t=this._overlays[e.page];t&&this._divaInstance.__removePageOverlay(t);var i=new o(e,this._divaInstance,this._getCurrentHighlight);this._overlays[e.page]=i,this._divaInstance.__addPageOverlay(i)},n.prototype.removeHighlightsOnPage=function(e){this._overlays[e]&&(this._divaInstance.__removePageOverlay(this._overlays[e]),delete this._overlays[e])},n.prototype.clear=function(){for(var e in this._overlays)this._overlays.hasOwnProperty(e)&&this._divaInstance.__removePageOverlay(this._overlays[e]);this._overlays={}},o.prototype.mount=function(){for(var e=this._divaInstance.getSettings(),t=this.highlight,i=t.regions,n=t.colour,o=t.divClass,r=i.length;r--;){var s=i[r],u=l("div",{class:o,style:{background:n,border:"1px solid #555",position:"absolute",zIndex:100}});void 0!==s.divID&&u.setAttribute("data-highlight-id",s.divID),void 0!==s.name&&u.setAttribute("data-name",s.name), -this._highlightRegions.push({element:u,region:s})}this.refresh();var h=document.createDocumentFragment();this._highlightRegions.forEach(function(e){h.appendChild(e.element)}),e.innerElement.appendChild(h),this._getCurrentHighlight&&a(this._divaInstance,this._getCurrentHighlight()),c.Events.publish("HighlightCompleted",[this.page,this._divaInstance.getFilenames()[this.page]])},o.prototype.unmount=function(){var e=this._divaInstance.getSettings().innerElement;this._highlightRegions.forEach(function(t){e.removeChild(t.element)}),this._highlightRegions=[]},o.prototype.refresh=function(){var e=this._divaInstance.getMaxZoomLevel(),t=this._divaInstance.getPageDimensionsAtZoomLevel(this.page,e).width,i=this._divaInstance.getPageDimensions(this.page).width,n=Math.log(t/i)/Math.log(2),o=this._divaInstance.getPageOffset(this.page,{excludePadding:!0,incorporateViewport:!0});this._highlightRegions.forEach(function(e){var t=e.region;l.setAttributes(e.element,{style:{width:r(t.width,n)+"px",height:r(t.height,n)+"px",top:o.top+r(t.uly,n)+"px",left:o.left+r(t.ulx,n)+"px"}})})}},function(e,t,i){var n=i(1);!function(e){e.fn.dragscrollable=function(t){var i=e.extend({dragSelector:">:first",acceptPropagatedEvent:!0,preventDefault:!0},t||{}),n={mouseDownHandler:function(t){return!(1!=t.which||!t.data.acceptPropagatedEvent&&t.target!=this)&&(t.data.lastCoord={left:t.clientX,top:t.clientY},e.event.add(document,"mouseup",n.mouseUpHandler,t.data),e.event.add(document,"mousemove",n.mouseMoveHandler,t.data),t.data.preventDefault?(t.preventDefault(),!1):void 0)},mouseMoveHandler:function(e){var t={left:e.clientX-e.data.lastCoord.left,top:e.clientY-e.data.lastCoord.top};if(e.data.scrollable.scrollLeft(e.data.scrollable.scrollLeft()-t.left),e.data.scrollable.scrollTop(e.data.scrollable.scrollTop()-t.top),e.data.lastCoord={left:e.clientX,top:e.clientY},e.data.preventDefault)return e.preventDefault(),!1},mouseUpHandler:function(t){if(e.event.remove(document,"mousemove",n.mouseMoveHandler),e.event.remove(document,"mouseup",n.mouseUpHandler),t.data.preventDefault)return t.preventDefault(),!1}};this.each(function(){var t={scrollable:e(this),acceptPropagatedEvent:i.acceptPropagatedEvent,preventDefault:i.preventDefault};e(this).find(i.dragSelector).bind("mousedown",t,n.mouseDownHandler)})}}(n),/** - jQuery.kinetic v2.2.1 - Dave Taylor http://davetayls.me - - @license The MIT License (MIT) - @preserve Copyright (c) 2012 Dave Taylor http://davetayls.me - */ -function(e){"use strict";var t="kinetic-active";window.requestAnimationFrame||(window.requestAnimationFrame=function(){return window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(e,t){window.setTimeout(e,1e3/60)}}()),e.support=e.support||{},e.extend(e.support,{touch:"ontouchend"in document});var i=function(t,i){return this.settings=i,this.el=t,this.$el=e(t),this._initElements(),this};i.DATA_KEY="kinetic",i.DEFAULTS={cursor:"move",decelerate:!0,triggerHardware:!1,threshold:0,y:!0,x:!0,slowdown:.9,maxvelocity:40,throttleFPS:60,invert:!1,movingClass:{up:"kinetic-moving-up",down:"kinetic-moving-down",left:"kinetic-moving-left",right:"kinetic-moving-right"},deceleratingClass:{up:"kinetic-decelerating-up",down:"kinetic-decelerating-down",left:"kinetic-decelerating-left",right:"kinetic-decelerating-right"}},i.prototype.start=function(t){this.settings=e.extend(this.settings,t),this.velocity=t.velocity||this.velocity,this.velocityY=t.velocityY||this.velocityY,this.settings.decelerate=!1,this._move()},i.prototype.end=function(){this.settings.decelerate=!0},i.prototype.stop=function(){this.velocity=0,this.velocityY=0,this.settings.decelerate=!0,e.isFunction(this.settings.stopped)&&this.settings.stopped.call(this)},i.prototype.detach=function(){this._detachListeners(),this.$el.removeClass(t).css("cursor","")},i.prototype.attach=function(){this.$el.hasClass(t)||(this._attachListeners(this.$el),this.$el.addClass(t).css("cursor",this.settings.cursor))},i.prototype._initElements=function(){this.$el.addClass(t),e.extend(this,{xpos:null,prevXPos:!1,ypos:null,prevYPos:!1,mouseDown:!1,throttleTimeout:1e3/this.settings.throttleFPS,lastMove:null,elementFocused:null}),this.velocity=0,this.velocityY=0,e(document).mouseup(e.proxy(this._resetMouse,this)).click(e.proxy(this._resetMouse,this)),this._initEvents(),this.$el.css("cursor",this.settings.cursor),this.settings.triggerHardware&&this.$el.css({"-webkit-transform":"translate3d(0,0,0)","-webkit-perspective":"1000","-webkit-backface-visibility":"hidden"})},i.prototype._initEvents=function(){var t=this;this.settings.events={touchStart:function(e){var i;t._useTarget(e.target,e)&&(i=e.originalEvent.touches[0],t.threshold=t._threshold(e.target,e),t._start(i.clientX,i.clientY),e.stopPropagation())},touchMove:function(e){var i;t.mouseDown&&(i=e.originalEvent.touches[0],t._inputmove(i.clientX,i.clientY),e.preventDefault&&e.preventDefault())},inputDown:function(e){t._useTarget(e.target,e)&&(t.threshold=t._threshold(e.target,e),t._start(e.clientX,e.clientY),t.elementFocused=e.target,"IMG"===e.target.nodeName&&e.preventDefault(),e.stopPropagation())},inputEnd:function(e){t._useTarget(e.target,e)&&(t._end(),t.elementFocused=null,e.preventDefault&&e.preventDefault())},inputMove:function(e){t.mouseDown&&(t._inputmove(e.clientX,e.clientY),e.preventDefault&&e.preventDefault())},scroll:function(i){e.isFunction(t.settings.moved)&&t.settings.moved.call(t,t.settings),i.preventDefault&&i.preventDefault()},inputClick:function(e){if(Math.abs(t.velocity)>0)return e.preventDefault(),!1},dragStart:function(e){if(t._useTarget(e.target,e)&&t.elementFocused)return!1},selectStart:function(i){return e.isFunction(t.settings.selectStart)?t.settings.selectStart.apply(t,arguments):!t._useTarget(i.target,i)&&void 0}},this._attachListeners(this.$el,this.settings)},i.prototype._inputmove=function(t,i){var n=this.$el;this.el;if((!this.lastMove||new Date>new Date(this.lastMove.getTime()+this.throttleTimeout))&&(this.lastMove=new Date,this.mouseDown&&(this.xpos||this.ypos))){var o=t-this.xpos,r=i-this.ypos;if(this.settings.invert&&(o*=-1,r*=-1),this.threshold>0){var a=Math.sqrt(o*o+r*r);if(this.threshold>a)return;this.threshold=0}this.elementFocused&&(e(this.elementFocused).blur(),this.elementFocused=null,n.focus()),this.settings.decelerate=!1,this.velocity=this.velocityY=0;var s=this.scrollLeft(),l=this.scrollTop();this.scrollLeft(this.settings.x?s-o:s),this.scrollTop(this.settings.y?l-r:l),this.prevXPos=this.xpos,this.prevYPos=this.ypos,this.xpos=t,this.ypos=i,this._calculateVelocities(),this._setMoveClasses(this.settings.movingClass),e.isFunction(this.settings.moved)&&this.settings.moved.call(this,this.settings)}},i.prototype._calculateVelocities=function(){this.velocity=this._capVelocity(this.prevXPos-this.xpos,this.settings.maxvelocity),this.velocityY=this._capVelocity(this.prevYPos-this.ypos,this.settings.maxvelocity),this.settings.invert&&(this.velocity*=-1,this.velocityY*=-1)},i.prototype._end=function(){this.xpos&&this.prevXPos&&this.settings.decelerate===!1&&(this.settings.decelerate=!0,this._calculateVelocities(),this.xpos=this.prevXPos=this.mouseDown=!1,this._move())},i.prototype._useTarget=function(t,i){return!e.isFunction(this.settings.filterTarget)||this.settings.filterTarget.call(this,t,i)!==!1},i.prototype._threshold=function(t,i){return e.isFunction(this.settings.threshold)?this.settings.threshold.call(this,t,i):this.settings.threshold},i.prototype._start=function(e,t){this.mouseDown=!0,this.velocity=this.prevXPos=0,this.velocityY=this.prevYPos=0,this.xpos=e,this.ypos=t},i.prototype._resetMouse=function(){this.xpos=!1,this.ypos=!1,this.mouseDown=!1},i.prototype._decelerateVelocity=function(e,t){return 0===Math.floor(Math.abs(e))?0:e*t},i.prototype._capVelocity=function(e,t){var i=e;return e>0?e>t&&(i=t):e<0-t&&(i=0-t),i},i.prototype._setMoveClasses=function(e){var t=this.settings,i=this.$el;i.removeClass(t.movingClass.up).removeClass(t.movingClass.down).removeClass(t.movingClass.left).removeClass(t.movingClass.right).removeClass(t.deceleratingClass.up).removeClass(t.deceleratingClass.down).removeClass(t.deceleratingClass.left).removeClass(t.deceleratingClass.right),this.velocity>0&&i.addClass(e.right),this.velocity<0&&i.addClass(e.left),this.velocityY>0&&i.addClass(e.down),this.velocityY<0&&i.addClass(e.up)},i.prototype._move=function(){var t=this._getScroller(),i=t[0],n=this,o=n.settings;o.x&&i.scrollWidth>0?(this.scrollLeft(this.scrollLeft()+this.velocity),Math.abs(this.velocity)>0&&(this.velocity=o.decelerate?n._decelerateVelocity(this.velocity,o.slowdown):this.velocity)):this.velocity=0,o.y&&i.scrollHeight>0?(this.scrollTop(this.scrollTop()+this.velocityY),Math.abs(this.velocityY)>0&&(this.velocityY=o.decelerate?n._decelerateVelocity(this.velocityY,o.slowdown):this.velocityY)):this.velocityY=0,n._setMoveClasses(o.deceleratingClass),e.isFunction(o.moved)&&o.moved.call(this,o),Math.abs(this.velocity)>0||Math.abs(this.velocityY)>0?this.moving||(this.moving=!0,window.requestAnimationFrame(function(){n.moving=!1,n._move()})):n.stop()},i.prototype._getScroller=function(){var t=this.$el;return(this.$el.is("body")||this.$el.is("html"))&&(t=e(window)),t},i.prototype.scrollLeft=function(e){var t=this._getScroller();return"number"!=typeof e?t.scrollLeft():(t.scrollLeft(e),void(this.settings.scrollLeft=e))},i.prototype.scrollTop=function(e){var t=this._getScroller();return"number"!=typeof e?t.scrollTop():(t.scrollTop(e),void(this.settings.scrollTop=e))},i.prototype._attachListeners=function(){var t=this.$el,i=this.settings;e.support.touch&&t.bind("touchstart",i.events.touchStart).bind("touchend",i.events.inputEnd).bind("touchmove",i.events.touchMove),t.mousedown(i.events.inputDown).mouseup(i.events.inputEnd).mousemove(i.events.inputMove),t.click(i.events.inputClick).scroll(i.events.scroll).bind("selectstart",i.events.selectStart).bind("dragstart",i.events.dragStart)},i.prototype._detachListeners=function(){var t=this.$el,i=this.settings;e.support.touch&&t.unbind("touchstart",i.events.touchStart).unbind("touchend",i.events.inputEnd).unbind("touchmove",i.events.touchMove),t.unbind("mousedown",i.events.inputDown).unbind("mouseup",i.events.inputEnd).unbind("mousemove",i.events.inputMove),t.unbind("click",i.events.inputClick).unbind("scroll",i.events.scroll).unbind("selectstart",i.events.selectStart).unbind("dragstart",i.events.dragStart)},e.Kinetic=i,e.fn.kinetic=function(t,n){return this.each(function(){var o=e(this),r=o.data(i.DATA_KEY),a=e.extend({},i.DEFAULTS,o.data(),"object"==typeof t&&t);r||o.data(i.DATA_KEY,r=new i(this,a)),"string"==typeof t&&r[t](n)})}}(n),function(e){e.Kinetic.prototype._attachListeners=function(){var t=this.$el,i=this.settings;e.support.touch&&t.bind("touchstart",i.events.touchStart).bind("touchend",i.events.inputEnd).bind("touchmove",i.events.touchMove),t.click(i.events.inputClick).scroll(i.events.scroll).bind("selectstart",i.events.selectStart).bind("dragstart",i.events.dragStart)},e.Kinetic.prototype._detachListeners=function(){var t=this.$el,i=this.settings;e.support.touch&&t.unbind("touchstart",i.events.touchStart).unbind("touchend",i.events.inputEnd).unbind("touchmove",i.events.touchMove),t.unbind("click",i.events.inputClick).unbind("scroll",i.events.scroll).unbind("selectstart",i.events.selectStart).unbind("dragstart",i.events.dragStart)}}(n)},function(e,t){!function(){if(!Array.prototype.fill){var e=function(e){if(null==this)throw new TypeError("this is null or not defined");for(var t=Object(this),i=t.length>>>0,n=arguments[1],o=n>>0,r=o<0?Math.max(i+o,0):Math.min(o,i),a=arguments[2],s=void 0===a?i:a>>0,l=s<0?Math.max(i+s,0):Math.min(s,i);r100)){var t=/^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(e);if(t){var i=parseFloat(t[1]),n=(t[2]||"ms").toLowerCase();switch(n){case"years":case"year":case"yrs":case"yr":case"y":return i*u;case"days":case"day":case"d":return i*c;case"hours":case"hour":case"hrs":case"hr":case"h":return i*l;case"minutes":case"minute":case"mins":case"min":case"m":return i*s;case"seconds":case"second":case"secs":case"sec":case"s":return i*a;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return i;default:return}}}}function n(e){return e>=c?Math.round(e/c)+"d":e>=l?Math.round(e/l)+"h":e>=s?Math.round(e/s)+"m":e>=a?Math.round(e/a)+"s":e+"ms"}function o(e){return r(e,c,"day")||r(e,l,"hour")||r(e,s,"minute")||r(e,a,"second")||e+" ms"}function r(e,t,i){if(!(e0)return i(e);if("number"===r&&isNaN(e)===!1)return t.long?o(e):n(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))}},function(e,t){function i(){throw new Error("setTimeout has not been defined")}function n(){throw new Error("clearTimeout has not been defined")}function o(e){if(u===setTimeout)return setTimeout(e,0);if((u===i||!u)&&setTimeout)return u=setTimeout,setTimeout(e,0);try{return u(e,0)}catch(t){try{return u.call(null,e,0)}catch(t){return u.call(this,e,0)}}}function r(e){if(h===clearTimeout)return clearTimeout(e);if((h===n||!h)&&clearTimeout)return h=clearTimeout,clearTimeout(e);try{return h(e)}catch(t){try{return h.call(null,e)}catch(t){return h.call(this,e)}}}function a(){p&&f&&(p=!1,f.length?g=f.concat(g):v=-1,g.length&&s())}function s(){if(!p){var e=o(a);p=!0;for(var t=g.length;t;){for(f=g,g=[];++v1)for(var i=1;i0)n=r;else{if(!(a.length>0)){for(o=0;o=this._rows||t>=this._cols||this._map[e][t]},n.prototype.set=function(e,t,i){this._map[e][t]=i}},function(e,t,i){function n(e){if(this._viewerCore=e,this._viewerState=e.getInternalState(),this._overlays=[],e.getPageTools().length)for(var t=e.getSettings().numPages,i=0;i=0&&(e.oldZoomLevel0){var l={pageX:(n[0].clientX+n[1].clientX)/2,pageY:(n[0].clientY+n[1].clientY)/2};t(e,s(e.currentTarget,l),i,o)}}})}function o(e,t){var i=a(u),n=null;e.on("touchend",function(e){if(e.preventDefault(),i.isTriggered()){i.reset();var o={pageX:e.originalEvent.changedTouches[0].clientX,pageY:e.originalEvent.changedTouches[0].clientY},a=r(n.pageX,n.pageY,o.pageX,o.pageY);a=a.top?n[0]:n[1].region.bottom<=a.bottom?n[1]:o(n,a);var s=this._viewerCore.getSettings().currentPageIndex,l=r.pages.some(function(e){return e.index===s});l||this._viewerCore.setCurrentPage(r.pages[0].index)}},n.prototype.destroy=function(){}},function(e,t,i){"use strict";function n(e){e=e||{maxKeys:r},this.maxKeys=e.maxKeys||r,this._held={},this._urls={},this._lru=[]}var o=i(4)("diva:ImageCache");e.exports=n;var r=100;n.prototype.get=function(e){var t=this._urls[e];return t?t.img:null},n.prototype.has=function(e){return!!this._urls[e]},n.prototype.put=function(e,t){var i=this._urls[e];i?(i.img=t,this._promote(i)):(i={img:t,url:e},this._urls[e]=i,this._tryEvict(1),this._lru.unshift(i))},n.prototype._promote=function(e){var t=this._lru.indexOf(e);this._lru.splice(t,1),this._lru.unshift(e)},n.prototype._tryEvict=function(e){var t=this.maxKeys-e;if(!(this._lru.length<=t))for(var i=this._lru.length-1;;){var n=this._lru[i];if(!this._held[n.url]&&(o("Evicting image %s",n.url),this._lru.splice(i,1),delete this._urls[n.url],this._lru.length<=t))break;if(0===i){o.enabled&&o("Cache overfull by %s (all entries are being held)",this._lru.length-t);break}i--}},n.prototype.acquire=function(e){this._held[e]=(this._held[e]||0)+1,this._promote(this._urls[e])},n.prototype.release=function(e){var t=this._held[e];t>1?this._held[e]--:delete this._held[e],this._tryEvict(0)}},function(e,t,i){function n(e,t){this.pages=e.pgs,this.maxZoom=e.max_zoom,this.maxRatio=e.dims.max_ratio,this.minRatio=e.dims.min_ratio,this.itemTitle=e.item_title,this.paged=!!e.paged,this._maxWidths=e.dims.max_w,this._maxHeights=e.dims.max_h,this._averageWidths=e.dims.a_wid,this._averageHeights=e.dims.a_hei,this._totalHeights=e.dims.t_hei,this._totalWidths=e.dims.t_wid,this._urlAdapter=t}function o(e){return function(t){return this[e][t]}}function r(e,t){return 1/Math.pow(2,e-t)}function a(){}function s(e){this._config=e}var l=i(32);e.exports=n,n.fromIIIF=function(e){var t=l(e);return new n(t,new a)},n.fromLegacyManifest=function(e,t){for(var i=0,o=e.pgs.length;i=0&&e1.1?"default":"native";return encodeURI(o.url+"full/"+n+"/0/"+r+".jpg")},a.prototype.getTileImageURL=function(e,t,i){var n,o,r=e.pages[t];n=i.row===i.rowCount-1?r.d[i.zoomLevel].h-(i.rowCount-1)*i.tileDimensions.height:i.tileDimensions.height,o=i.col===i.colCount-1?r.d[i.zoomLevel].w-(i.colCount-1)*i.tileDimensions.width:i.tileDimensions.width;var a=Math.pow(2,e.maxZoom-i.zoomLevel),s=i.col*i.tileDimensions.width*a,l=i.row*i.tileDimensions.height*a;r.hasOwnProperty("xoffset")&&(s+=r.xoffset,l+=r.yoffset);var c=[s,l,o*a,n*a].join(","),u=r.api>1.1?"default":"native";return encodeURI(r.url+c+"/"+o+","+n+"/0/"+u+".jpg")},s.prototype.getPageImageURL=function(e,t,i){var n="";i&&(null!=i.width&&(n+="&WID="+i.width),null!=i.height&&(n+="&HEI="+i.height));var o=e.pages[t].f;return this._config.iipServerURL+"?FIF="+this._config.imageDir+"/"+o+n+"&CVT=JPEG"},s.prototype.getTileImageURL=function(e,t,i){var n=e.pages[t],o=i.zoomLevel+n.m-e.maxZoom,r=i.row*i.colCount+i.col,a=o+","+r;return encodeURI(this._config.iipServerURL+"?FIF="+this._config.imageDir+"/"+n.f+"&JTL="+a+"&CVT=JPEG")}},function(e,t,i){function n(e){this._url=e.url,this._callback=e.load,this._errorCallback=e.error,this.timeoutTime=e.timeoutTime||0,this._aborted=this._complete=!1,this.timeout=setTimeout(function(){this._image=new Image,this._image.crossOrigin="anonymous",this._image.onload=this._handleLoad.bind(this),this._image.onerror=this._handleError.bind(this),this._image.src=e.url,o("Requesting image %s",e.url)}.bind(this),this.timeoutTime)}var o=i(4)("diva:ImageRequestHandler");e.exports=n,n.prototype.abort=function(){o("Aborting request to %s",this._url),clearTimeout(this.timeout),this._image&&(this._image.onload=this._image.onerror=null,this._image.src=""),this._aborted=!0},n.prototype._handleLoad=function(){return this._aborted?void console.error("ImageRequestHandler invoked on cancelled request for "+this._url):this._complete?void console.error("ImageRequestHandler invoked on completed request for "+this._url):(this._complete=!0,o("Received image %s",this._url),void this._callback(this._image))},n.prototype._handleError=function(){o("Failed to load image %s",this._url),this._errorCallback(this._image)}},function(e,t){function i(e){function t(){var e=r(),n=Math.min((e-h)/s,1);i(n),c(g),e0&&h.push({dimensions:g,pages:d}),h}e.exports=i},function(e,t,i){function n(e){if(e.inGrid)return s(o(e,["manifest","viewport","pagesPerRow","fixedHeightGrid","fixedPadding","showNonPagedPages"]));var t=o(e,["manifest","verticallyOriented","showNonPagedPages"]);return e.inBookLayout?r(t):a(t)}function o(e,t){var i={};return t.forEach(function(t){i[t]=e[t]}),i}var r=i(26),a=i(29),s=i(27);e.exports=n},function(e,t,i){var n=i(7);e.exports=function(e){var t=e.manifest,i=[];return t.pages.forEach(function(o,r){if(e.showNonPagedPages||!t.paged||o.paged){var a=n(r,t);i.push({dimensions:a,pages:[{index:r,groupOffset:{top:0,left:0},dimensions:a}]})}}),i}},function(e,t){function i(){this._pages={},this._renderedPages=[],this._renderedPageMap={}}e.exports=i,i.prototype.addOverlay=function(e){var t=this._pages[e.page]||(this._pages[e.page]=[]);t.push(e),this._renderedPageMap[e.page]&&e.mount()},i.prototype.removeOverlay=function(e){var t=e.page,i=this._pages[t];if(i){var n=i.indexOf(e);n!==-1&&(this._renderedPageMap[t]&&i[n].unmount(),i.splice(n,1),0===i.length&&delete this._pages[t])}},i.prototype.updateOverlays=function(e){var t=this._renderedPages,i={};e.forEach(function(e){i[e]=!0,this._renderedPageMap[e]||(this._renderedPageMap[e]=!0,this._invokeOnOverlays(e,function(e){e.mount()}))},this),t.forEach(function(e){i[e]?this._invokeOnOverlays(e,function(e){e.refresh()}):(delete this._renderedPageMap[e],this._invokeOnOverlays(e,function(e){e.unmount()}))},this),this._renderedPages=e},i.prototype._invokeOnOverlays=function(e,t){var i=this._pages[e];i&&i.forEach(t,this)}},function(e,t,i){function n(e,t){this.page=e,this._viewerCore=t,this._innerElement=t.getSettings().innerElement,this._pageToolsElem=null}var o=i(3);e.exports=n,n.prototype.mount=function(){if(null===this._pageToolsElem){var e=this._initializePageToolButtons();this._pageToolsElem=o("div",{class:"diva-page-tools-wrapper"},o("div",{class:"diva-page-tools"},e))}this.refresh(),this._innerElement.appendChild(this._pageToolsElem)},n.prototype._initializePageToolButtons=function(){var e=this._viewerCore.getSettings(),t=this._viewerCore.getPublicInstance(),i=this.page;return this._viewerCore.getPageTools().map(function(n){var r=n.titleText||n.pluginName[0].toUpperCase()+n.pluginName.substring(1)+" plugin",a=o("div",{class:"diva-"+n.pluginName+"-icon",title:r});return a.addEventListener("click",function(o){n.handleClick.call(this,o,e,t,i)},!1),a.addEventListener("touchend",function(o){o.preventDefault(),n.handleClick.call(this,o,e,t,i)},!1),a},this)},n.prototype.unmount=function(){this._innerElement.removeChild(this._pageToolsElem)},n.prototype.refresh=function(){var e=this._viewerCore.getPageRegion(this.page,{excludePadding:!0,incorporateViewport:!0});this._pageToolsElem.style.top=e.top+"px",this._pageToolsElem.style.left=e.left+"px"}},function(e,t){function i(e){for(var t,i,s,l,c,u,h,d,f,g,p,v,m,w,y,b,_,P=e.sequences[0],x=P.canvases,I=x.length,L=new Array(x.length),C=100,O=0,D=100,T=0;TAutoscrolling options:
Speed:
Allow manual scroll:
";t("#"+i.ID+"page-nav").before("
"),t("body").prepend(a),t("#"+i.ID+"autoscroll-pps").on("change",function(e){n.changeScrollSpeed(Math.pow(10,e.target.value))}),t("#"+i.ID+"autoscroll-manual").on("change",function(e){e.target.checked?n.enableManualScroll():n.disableManualScroll()}),t("#"+i.ID+"autoscroll-toggle").on("click",n.toggleScrolling),t("#"+i.ID+"autoscroll-icon").on("click",function(n){e.jqObj=t("#"+i.ID+"autoscroll-prefs"),"none"===e.jqObj.css("display")?(e.jqObj.css({display:"block"}),f(i.inFullscreen)):e.jqObj.css("display","none")})},i.ID)}},pluginName:"autoscroll",titleText:"Automatically scrolls page along primary axis"};return i}()}(n)},function(e,t,i){var n=i(1),o=i(2);i(10),function(t){e.exports=function(){var e,i,n,r={},a={},s={},l={brightnessMax:150,brightnessMin:-100,brightnessStep:1,contrastMax:3,contrastMin:-1,contrastStep:.05,localStoragePrefix:"canvas-",mobileWebkitMaxZoom:2,rgbMax:50,rgbMin:-50,throbberFadeSpeed:200,throbberTimeout:100,buttons:["contrast","brightness","rotation","zoom"]},c=function(e){return e*Math.PI/180},u=function(e,t){var n=e.x-r.centerX,o=-(e.y-r.centerY),a=c(i.rotation.previous-t),s=Math.cos(a)*n-Math.sin(a)*o+r.centerX,l=-(Math.sin(a)*n+Math.cos(a)*o)+r.centerY;return{x:s,y:l}},h=function(t,i){var n=t.context,o=t.size/2,r=-(t.width/2),a=-(t.height/2);n.clearRect(0,0,t.size,t.size),n.save(),n.translate(o,o),n.rotate(c(i)),n.drawImage(e,r,a,t.width,t.height),n.restore(),t.data=n.getImageData(0,0,t.size,t.size)},d=function(){var e;for(e in i)if(i[e].current!==i[e].previous)return!0;return!1},f=function(){var e;for(e in i)i[e].previous=i[e].current},g=function(){h(a,i.rotation.current),w(a)},p=function(){var e=i.rotation.current,n=i.rotation.previous,o=i.zoom.current,a=i.zoom.previous;if(e!==n||o!==a){var l=t("#diva-canvas-wrapper").scrollLeft(),c=t("#diva-canvas-wrapper").scrollTop(),g=s.viewport.width/2,p=s.viewport.height/2,v=u({x:l+g,y:c+p},e),m=Math.pow(2,o-a),y=m*v.x-g,b=m*v.y-p;h(r,e),t("#diva-canvas-wrapper").scrollLeft(y),t("#diva-canvas-wrapper").scrollTop(b)}d()&&(w(r),f())},v=function(e){var t,i,n=e.data,o=e.context.createImageData(n),r=o.data;for(t=0,i=r.length;t0&&(x&&l&&(l+=b),I&&c&&(c+=_),L&&u&&(u+=P),D&&(l&&(l=l*w+y),c&&(c=c*w+y),u&&(u=u*w+y)),d[a]=l,d[a+1]=c,d[a+2]=u);e.context.clearRect(0,0,o,r),e.context.putImageData(h,0,0)},y=function(){var e=t("#diva-canvas-wrapper").scrollLeft()*a.scaleFactor,i=t("#diva-canvas-wrapper").scrollTop()*a.scaleFactor,n=Math.min(Math.round(s.viewport.height*a.scaleFactor),s.mapSize)-4,o=Math.min(Math.round(s.viewport.width*a.scaleFactor),s.mapSize)-4;t("#diva-map-viewbox").height(n).width(o).css({top:i,left:e})},b=function(e){a.canvas=document.getElementById("diva-canvas-minimap"),a.size=s.mapSize,a.canvas.width=a.size,a.canvas.height=a.size,a.context=a.canvas.getContext("2d"),a.context.fillRect(0,0,a.size,a.size),a.scaleFactor=s.mapSize/r.size,a.cornerX=r.cornerX*a.scaleFactor,a.cornerY=r.cornerY*a.scaleFactor,a.width=e.width*a.scaleFactor,a.height=e.height*a.scaleFactor,a.context.drawImage(e,a.cornerX,a.cornerY,a.width,a.height),a.data=a.context.getImageData(0,0,s.mapSize,s.mapSize),t("#diva-map-viewbox").show(),y()},_=function(i,n){e=new Image,e.crossOrigin="anonymous",e.onload=function(){r.size=Math.sqrt(e.width*e.width+e.height*e.height),r.canvas=document.getElementById("diva-canvas"),r.canvas.width=r.size,r.canvas.height=r.size,r.cornerX=(r.size-e.width)/2,r.cornerY=(r.size-e.height)/2,r.width=e.width,r.height=e.height,r.centerX=r.size/2,r.centerY=r.size/2,r.context=r.canvas.getContext("2d"),r.context.drawImage(e,r.cornerX,r.cornerY,r.width,r.height);try{r.data=r.context.getImageData(0,0,r.size,r.size)}catch(e){var i='

Error

'+e.message+"

";if("SecurityError"!==e.name)throw e;i+='

You may need to update your server configuration in order to use the image manipulation tools. For help, see the canvas cross-site data documentation.

',i+="",t("#diva-canvas-backdrop").append(i),C()}void 0===n&&b(e),g(),p(r),C(),"function"==typeof n&&n.call(n)},e.src=i,(e.complete||void 0===e.complete)&&(e.src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==",e.src=i)},P=function(){var e=i[n],o=e.current,r=e.transform?e.transform(o):o;t("#diva-canvas-value").html(r)},x=function(){t("#diva-canvas-slider").val(i[n].current)},I=function(e){var t=s.zoomWidthRatio*Math.pow(2,e);return s.divaInstance.getPageImageURL(s.selectedPageIndex,{width:t})},L=function(){(i.zoom.current>0||s.mobileWebkit)&&t(s.selector+"throbber").addClass("canvas-throbber").show()},C=function(){t(s.selector+"throbber").removeClass("canvas-throbber").hide()},O=function(){var e,t={},n=!1,o=s.localStoragePrefix+s.filename;for(e in i)i[e].previous!==i[e].initial&&(t[e]=i[e].previous,n=!0);n?(s.pluginIcon.addClass("new"),k(o,t)):(s.pluginIcon.removeClass("new"),localStorage.removeItem(o))},D=function(e,t){s.zoomLevel=e;var i=I(e);_(i,function(){a.scaleFactor=a.size/r.size,y(),O()})},T=function(e){var i=38,n=40,o=37,r=39;switch(e.keyCode){case i:return t("#diva-canvas-wrapper").scrollTop(document.getElementById("diva-canvas-wrapper").scrollTop-s.arrowScrollAmount),!1;case n:return t("#diva-canvas-wrapper").scrollTop(document.getElementById("diva-canvas-wrapper").scrollTop+s.arrowScrollAmount),!1;case o:return t("#diva-canvas-wrapper").scrollLeft(document.getElementById("diva-canvas-wrapper").scrollLeft-s.arrowScrollAmount),!1;case r:return t("#diva-canvas-wrapper").scrollLeft(document.getElementById("diva-canvas-wrapper").scrollLeft+s.arrowScrollAmount),!1}},k=function(e,t){localStorage.setItem(e,JSON.stringify(t))},E=function(e){var t=localStorage.getItem(e);return t&&JSON.parse(t)},M={init:function(e,c){var u=!!window.HTMLCanvasElement;if(!u)return!1;t.extend(s,l,e.canvasPlugin),s.divaInstance=c,s.inCanvas=!1,s.iipServerURL=e.iipServerURL,s.imageDir=e.imageDir,s.selector=e.selector,s.mobileWebkit=e.mobileWebkit,s.arrowScrollAmount=e.arrowScrollAmount,i={contrast:{initial:1,min:s.contrastMin,max:s.contrastMax,step:s.contrastStep,transform:function(e){return e.toFixed(2)},title:"Change the contrast"},brightness:{initial:0,min:s.brightnessMin,max:s.brightnessMax,step:s.brightnessStep,title:"Adjust the brightness"},rotation:{initial:0,min:0,max:359,step:1,transform:function(e){return e+"°"},title:"Rotate the image"},zoom:{initial:0,min:0,max:0,step:1,title:"Adjust the zoom level"},red:{initial:0,min:s.rgbMin,max:s.rgbMax,step:1,title:"Adjust the red channel"},green:{initial:0,min:s.rgbMin,max:s.rgbMax,step:1,title:"Adjust the green channel"},blue:{initial:0,min:s.rgbMin,max:s.rgbMax,step:1,title:"Adjust the blue channel"}};var h=function(){var e,t,n;for(n in i)t=i[n],e=t.initial,t.current=e,t.previous=e};if(h(),t("#diva-canvas-backdrop").length)return!0;var f,v,m,w,b=[];for(w in s.buttons)v=s.buttons[w],m=i[v].title,f='
',b.push(f);var _=b.join(""),I='
Test
'+_+'

contrast: 0 (Reset)


',k='
',E='
'+I+k+"
";t("body").append(E),s.mapSize=t("#diva-canvas-minimap").width(),t("#diva-canvas-buttons div").click(function(){t("#diva-canvas-buttons .clicked").removeClass("clicked"),M(t(this).attr("class"))});var M=function(e){n=e;var o=i[n];t("#diva-canvas-buttons ."+n).addClass("clicked"),t("#diva-canvas-mode").text(n);var r=o.current,a=o.transform?o.transform(r):r,s=document.getElementById("diva-canvas-slider");s.min=o.min,s.max=o.max,s.step=o.step,t("#diva-canvas-slider").val(r),t("#diva-canvas-value").html(a)};return M("contrast"),t("#diva-canvas-slider").on("input",function(e){i[n].current=parseFloat(this.value),P(),g()}),t("#diva-canvas-reset-all").click(function(){var e;for(e in i)i[e].current=i[e].initial;P(),x(),g()}),t("#diva-canvas-reset").click(function(){i[n].current=i[n].initial,P(),x(),g()}),t("#diva-canvas-apply").click(function(){d()&&(L(),setTimeout(function(){i.zoom.current!==i.zoom.previous?D(i.zoom.current):(p(),C(),O())},s.throbberTimeout))}),t("#diva-canvas-close").click(function(){t("body").removeClass("overflow-hidden"),r.context.clearRect(0,0,r.size,r.size),a.context.clearRect(0,0,a.size,a.size),t("#diva-canvas-wrapper").scrollTop(0).scrollLeft(0),t("#diva-canvas-backdrop").hide(),t("#diva-map-viewbox").hide(),C(),c.enableScrollable(),t(document).off("keydown",T),h(),P(),x(),t("#diva-canvas-buttons .clicked").removeClass("clicked"),M("contrast"),o.Events.publish("CanvasViewDidHide")}),t("#diva-canvas-minimise").click(function(){t("#diva-canvas-toolwindow").slideToggle("fast")}),t(window).resize(function(){s.viewport={height:window.innerHeight-e.scrollbarWidth,width:window.innerWidth-e.scrollbarWidth},s.inCanvas&&y()}),t("#diva-canvas-wrapper").scroll(function(){s.inCanvas&&y()}),t("#diva-canvas-minimap, #diva-map-viewbox").mouseup(function(e){var i=t("#diva-canvas-minimap").offset(),n=(e.pageX-i.left)/a.scaleFactor,o=(e.pageY-i.top)/a.scaleFactor;t("#diva-canvas-wrapper").scrollTop(o-s.viewport.height/2),t("#diva-canvas-wrapper").scrollLeft(n-s.viewport.width/2)}),t("#diva-canvas").mousedown(function(){t(this).addClass("grabbing")}).mouseup(function(){t(this).removeClass("grabbing")}),t("#diva-canvas-wrapper").kinetic(),t("#diva-canvas-wrapper").dragscrollable({acceptPropagatedEvent:!0}),o.Events.subscribe("ObjectDidLoad",this.setupHook,e.ID),o.Events.subscribe("ViewerDidTerminate",this.destroy,e.ID),o.Events.subscribe("PageDidLoad",this.onPageLoad,e.ID),!0},pluginName:"canvas",titleText:"View the image on a canvas and adjust various settings",setupHook:function(e){s.viewport={height:window.innerHeight-e.scrollbarWidth,width:window.innerWidth-e.scrollbarWidth},s.minZoomLevel=e.minZoomLevel,s.maxZoomLevel=e.maxZoomLevel,s.mobileWebkit&&(s.maxZoomLevel=Math.min(s.maxZoomLevel,s.mobileWebkitMaxZoom)),i.zoom.min=s.minZoomLevel,i.zoom.max=s.maxZoomLevel},handleClick:function(e,r,a,l){var c,u=a.getFilenames()[l],h=a.getPageDimensions(l).width-1,d=r.zoomLevel;s.zoomWidthRatio=h/Math.pow(2,d),s.pluginIcon=t(this),s.manifest=r.manifest,s.selectedPageIndex=l,s.mobileWebkit&&(d=Math.min(s.maxZoomLevel,d)),s.filename=u,i.zoom.initial=d,i.zoom.current=d;var f=E(s.localStoragePrefix+s.filename);if(f)for(c in f)i[c].current=f[c],c===n&&(P(),x()),"zoom"===c&&(d=f[c]);i.zoom.previous=d,t("body").addClass("overflow-hidden"),t("#diva-canvas-backdrop").show(),a.disableScrollable(),t(document).keydown(T),s.inCanvas=!0;var g=I(d);t("#diva-canvas-info").text("Page "+(l+1)),L(),o.Events.publish("CanvasViewDidActivate",[l]),_(g)},onPageLoad:function(e,i,n){var o=s.localStoragePrefix+i;null!==localStorage.getItem(o)&&t(n).find(".diva-canvas-icon").addClass("new")},destroy:function(e,i){t("#diva-canvas-backdrop").remove()}};return M}()}(n)},function(e,t,i){var n=i(1);!function(t){e.exports=function(){var e={},t={init:function(t,i){return e.divaInstance=i,!0},pluginName:"download",titleText:"Download image at the given zoom level",handleClick:function(t,i,n,o){var r=n.getPageDimensions(o).width-1,a=e.divaInstance.getPageImageURL(o,{width:r});window.open(a)}};return t}()}(n)},function(e,t,i){var n=i(1),o=i(2),r=i(9).HighlightManager;!function(t){e.exports=function(){var e={},i={init:function(i,n){var a=new r(n);i.parentObject.data("highlightManager",a),e.highlightedPages=[],n.resetHighlights=function(){a.clear()},n.removeHighlightsOnPage=function(e){a.removeHighlightsOnPage(e)},n.hideHighlights=function(){e.highlightsVisible=!1,t(i.innerElement).addClass("annotations-hidden")},n.showHighlights=function(){e.highlightsVisible=!0,t(i.innerElement).removeClass("annotations-hidden")},n.highlightOnPages=function(e,t,i,o){for(var r=e.length;r--;)n.highlightOnPage(e[r],t[r],i,o)},n.highlightOnPage=function(e,t,n,o){return void 0===n&&(n="rgba(255, 0, 0, 0.2)"),o=void 0===o?i.ID+"highlight diva-highlight":i.ID+"highlight diva-highlight "+o,a.addHighlight({page:e,regions:t,colour:n,divClass:o}),!0},n.gotoHighlight=function(e){var t=a.getHighlightByRegionId(e);return t?s(t.highlight.page,t.region):(console.warn("Diva just tried to find a highlight that doesn't exist."),!1)};var s=function(e,t){var o=parseFloat(t.uly)+parseFloat(t.height)/2,r=parseFloat(t.ulx)+parseFloat(t.width)/2,a=n.translateFromMaxZoomLevel(o),s=n.translateFromMaxZoomLevel(r);e=parseInt(e,10),n.gotoPageByIndex(e);var l=n.getSettings().viewportObject,c=l.scrollTop()+a-l.height()/2+i.verticalPadding,u=l.scrollLeft()+s-l.width()/2+i.horizontalPadding;l.scrollTop(c),l.scrollLeft(u)},l=function(t){return function(i,o,r){for(var a=i,s=i.length,l=[],c=0;c'),t(i.selector+"annotations-icon").addClass("annotations-icon-active"),t("#"+i.ID+"annotations-icon").on("click",function(o){e.highlightsVisible?(n.hideHighlights(),t(i.selector+"annotations-icon").removeClass("annotations-icon-active")):(n.showHighlights(),t(i.selector+"annotations-icon").addClass("annotations-icon-active"))})},i.ID),e.highlightsVisible=!0,!0},destroy:function(e,t){e.parentObject.removeData("highlights")},pluginName:"IIIFHighlight",titleText:"Highlight regions of pages"};return i}()}(n)},function(e,t,i){var n=i(1),o=i(2);!function(t){e.exports=function(){var e={init:function(e,i){var n=function(i){var n=function(e,t){var i=e.charAt(0).toUpperCase()+e.slice(1),n=i.replace("_"," ");return t.match(/^https?:\/\//)&&(t=''+t+""),'"},o=function(e,t){for(var i=0;i';if(a+=r(["label"]),i.hasOwnProperty("metadata"))for(var s=i.metadata,l=0;l",e.parentObject.prepend(a),t(e.selector+"metadata").hide()};return o.Events.subscribe("ManifestDidLoad",n,e.ID),e.parentObject.prepend(''),t(e.selector+"metadata-link").on("click",function(i){t(e.selector+"metadata").fadeToggle("fast")}),!0},destroy:function(e,t){},pluginName:"IIIFMetadata",titleText:"Show metadata from a IIIF manifest"};return e}()}(n)},function(e,t,i){var n=i(5);n.registerPlugin(i(33)),n.registerPlugin(i(34)),n.registerPlugin(i(35)),n.registerPlugin(i(9)),n.registerPlugin(i(36)),n.registerPlugin(i(37))},function(e,t,i){"use strict";function n(e,t){this._viewport=e.viewport,this._outerElement=e.outerElement,this._documentElement=e.innerElement,this._hooks=t||{},this._canvas=l("canvas",{class:"diva-viewer-canvas",tabindex:"1"}),this._ctx=this._canvas.getContext("2d"),this.layout=null,this._sourceResolver=null,this._renderedPages=null,this._config=null,this._zoomLevel=null,this._compositeImages=null,this._renderedTiles=null,this._animation=null,this._cache=new h,this._pendingRequests={}}function o(e,t){var i;return i=null===t?1:Math.pow(2,t-e.zoomLevel),{sourceZoomLevel:e.zoomLevel,scaleRatio:i,row:e.row,col:e.col,dimensions:{width:e.dimensions.width*i,height:e.dimensions.height*i},offset:{left:e.offset.left*i,top:e.offset.top*i},url:e.url}}function r(e,t){if(e===t)return{added:[],removed:[]};var i=e.filter(function(e){return t.indexOf(e)===-1}),n=t.filter(function(t){return e.indexOf(t)===-1});return{added:n,removed:i}}var a=i(4)("diva:Renderer"),s=i(4)("diva:Renderer:paints"),l=i(3),c=i(17),u=i(19),h=i(22),d=i(24),f=i(25),g=250;e.exports=n,n.getCompatibilityErrors=function(){return"undefined"!=typeof HTMLCanvasElement?null:["Your browser lacks support for the ",l("pre","canvas")," element. Please upgrade your browser."]},n.prototype.getPageHit=function(e,t){var i=this._outerElement.getBoundingClientRect();if(ei.left+i.width||t>i.top+i.height)return null;e-=i.left,t-=i.top;for(var n=this._renderedPages.length,o=0;o=g&&e<=g+v&&t>=p&&t<=p+m){var w=e+d-g,y=t+f-p;return{pg:r,pctx:w/a.dimensions.width,pcty:y/a.dimensions.height,x:w,y:y}}}return null},n.prototype.load=function(e,t,i){if(this._clearAnimation(),this._hooks.onViewWillLoad&&this._hooks.onViewWillLoad(),this._sourceResolver=i,this._config=e,this._compositeImages={},this._setLayoutToZoomLevel(t.zoomLevel),!this.layout.getPageInfo(t.anchorPage))throw new Error("invalid page: "+t.anchorPage);this._canvas.width!==this._viewport.width||this._canvas.height!==this._viewport.height?(a("Canvas dimension change: (%s, %s) -> (%s, %s)",this._canvas.width,this._canvas.height,this._viewport.width,this._viewport.height),this._canvas.width=this._viewport.width,this._canvas.height=this._viewport.height):a("Reload, no size change"),this.goto(t.anchorPage,t.verticalOffset,t.horizontalOffset),this._canvas.parentNode!==this._outerElement&&this._outerElement.insertBefore(this._canvas,this._outerElement.firstChild),this._hooks.onViewDidLoad&&this._hooks.onViewDidLoad()},n.prototype._setViewportPosition=function(e){if(e.zoomLevel!==this._zoomLevel){if(null===this._zoomLevel)throw new TypeError("The current view is not zoomable");if(null===e.zoomLevel)throw new TypeError("The current view requires a zoom level");this._setLayoutToZoomLevel(e.zoomLevel)}this._goto(e.anchorPage,e.verticalOffset,e.horizontalOffset)},n.prototype._setLayoutToZoomLevel=function(e){this.layout=new u(this._config,e),this._zoomLevel=e,l.setAttributes(this._documentElement,{style:{height:this.layout.dimensions.height+"px",width:this.layout.dimensions.width+"px"}}),this._viewport.setInnerDimensions(this.layout.dimensions)},n.prototype.adjust=function(e){this._clearAnimation(),this._render(e),this._hooks.onViewDidUpdate&&this._hooks.onViewDidUpdate(this._renderedPages.slice(),null)},n.prototype._render=function(e){var t=[];this.layout.pageGroups.forEach(function(e){if(this._viewport.intersectsRegion(e.region)){var i=e.pages.filter(function(e){return this.isPageVisible(e.index)},this).map(function(e){return e.index});t.push.apply(t,i)}},this),this._ctx.clearRect(0,0,this._canvas.width,this._canvas.height),this._paintOutline(t),t.forEach(function(e){if(!this._compositeImages[e]){var t=this.layout.getPageInfo(e),i=this._sourceResolver.getAllZoomLevelsForPage(t),n=new c(i);n.updateFromCache(this._cache),this._compositeImages[e]=n}},this),this._initiateTileRequests(t);var i=r(this._renderedPages||[],t);i.removed.forEach(function(e){delete this._compositeImages[e]},this),this._renderedPages=t,this._paint(),this._hooks.onPageWillLoad&&i.added.forEach(function(e){this._hooks.onPageWillLoad(e)},this)},n.prototype._paint=function(){a("Repainting");var e=[];this._renderedPages.forEach(function(t){this._compositeImages[t].getTiles(this._zoomLevel).forEach(function(i){var n=o(i,this._zoomLevel);this._isTileVisible(t,n)&&(e.push(i.url),this._drawTile(t,n,this._cache.get(i.url)))},this)},this);var t=this._cache,i=r(this._renderedTiles||[],e);i.added.forEach(function(e){t.acquire(e)}),i.removed.forEach(function(e){t.release(e)}),i.removed&&this._renderedPages.forEach(function(e){this._compositeImages[e].updateFromCache(this._cache)},this),this._renderedTiles=e},n.prototype._paintOutline=function(e){e.forEach(function(e){var t=this.layout.getPageInfo(e),i=this._getImageOffset(e),n=Math.max(0,(this._viewport.width-this.layout.dimensions.width)/2),o=Math.max(0,(this._viewport.height-this.layout.dimensions.height)/2),r=i.left-this._viewport.left+n,a=i.top-this._viewport.top+o,s=r<0?-r:0,l=a<0?-a:0,c=Math.max(0,r),u=Math.max(0,a),h=t.dimensions.width-s,d=t.dimensions.height-l;this._ctx.strokeStyle="#AAA",this._ctx.strokeRect(c+.5,u+.5,h,d)},this)},n.prototype._initiateTileRequests=function(e){for(var t={},i=function(e,i){var n=this._compositeImages[i];t[e.url]=new d({url:e.url,timeoutTime:g,load:function(t){delete this._pendingRequests[e.url],this._cache.put(e.url,t),n===this._compositeImages[i]&&(n.updateWithLoadedUrls([e.url]),this._isTileForSourceVisible(i,e)?this._paint():s("Page %s, tile %s no longer visible on image load",i,e.url))}.bind(this),error:function(){delete this._pendingRequests[e.url]}.bind(this)})}.bind(this),n=0;n-1){var c=a("div",{class:"diva-input-suggestion"},o[s].l);r.appendChild(c),n++}n>0&&(r.style.display="block")}else r.style.display="none"}),o(i).on("keydown",function(e){var t;if(13===e.keyCode){var n=o(".active",r);n.length&&(i.value=n.text())}if(38===e.keyCode){t=o(".active",r);var a=t.prev();a.length?(t.removeClass("active"),a.addClass("active")):(t.removeClass("active"),o(".diva-input-suggestion:last",r).addClass("active"))}else if(40===e.keyCode){t=o(".active",r);var s=t.next();s.length?(t.removeClass("active"),s.addClass("active")):(t.removeClass("active"),o(".diva-input-suggestion:first",r).addClass("active"))}}),o(r).on("mousedown",".diva-input-suggestion",function(){i.value=this.textContent,r.style.display="none",o(i).trigger("submit")}),o(i).on("blur",function(){r.style.display="none"}),s},v=function(){var i=a("span",{id:t.ID+"current-page"}),o=function(){i.textContent=e.getCurrentAliasedPageIndex()};n("VisiblePageDidChange",o),n("ViewerDidLoad",o);var r=a("span",{id:t.ID+"num-pages"}),s=function(){r.textContent=t.numPages};return n("NumberOfPagesDidChange",s),n("ObjectDidLoad",s),a("span",{class:"diva-page-label diva-label"},"Page ",i," of ",r)},m=function(){var e=[h()];return t.enableLinkIcon&&e.push(w()),t.enableNonPagedVisibilityIcon&&e.push(b()),t.enableFullscreen&&e.push(y()),a("span",i("toolbar-button-group"),e)},w=function(){var i=s("link-icon","Link to this page"),n=o(i);return n.on("click",function(){if(o("body").prepend(a("div",{id:t.ID+"link-popup",class:"diva-popup diva-link-popup"},[a("input",{id:t.ID+"link-popup-input",class:"diva-input",type:"text",value:e.getCurrentURL()})])),t.inFullscreen)o(t.selector+"link-popup").addClass("in-fullscreen");else{var i=n.offset().left-222+n.outerWidth(),r=n.offset().top+n.outerHeight()-1;o(t.selector+"link-popup").css({top:r+"px",left:i+"px"})}return o("body").mouseup(function(e){var i=e.target.id;i!==t.ID+"link-popup"&&i!==t.ID+"link-popup-input"&&o(t.selector+"link-popup").remove()}),t.viewportObject.scroll(function(){o(t.selector+"link-popup").remove()}),o(t.selector+"link-popup input").click(function(){o(this).focus().select()}),!1}),i},y=function(){return s("fullscreen-icon","Toggle fullscreen mode",function(){e.toggleFullscreenMode()})},b=function(){var i=function(){return"toggle-nonpaged-icon"+(e.getSettings().showNonPagedPages?"-active":"")},o=s(i(),"Toggle visibility of non-paged pages",function(){e.toggleNonPagedPagesVisibility();var t="diva-"+i();this.className=this.className.replace(/diva-toggle-nonpaged-icon(-active)?/,t)}),r=function(){for(var e=t.manifest.pages,i=0;i0;for(var r=n.length;r--;)if(n[r]===e[1])return this._cache[i][o].splice(r,1),!0}return!1},i.prototype.unsubscribeAll=function(e){if(e)for(var t,i=Object.keys(this._cache),n=i.length;n--;)t=i[n],"undefined"!=typeof this._cache[t][e]&&delete this._cache[t][e];else this._cache={}}},function(e,t){e.exports=function(){var e=document.createElement("p");e.style.width="100%",e.style.height="200px";var t=document.createElement("div");t.style.position="absolute",t.style.top="0px",t.style.left="0px",t.style.visibility="hidden",t.style.width="200px",t.style.height="150px",t.style.overflow="hidden",t.appendChild(e),document.body.appendChild(t);var i=e.offsetWidth;t.style.overflow="scroll";var n=e.offsetWidth;return i==n&&(n=t.clientWidth),document.body.removeChild(t),i-n}},function(e,t){function i(e){var t=window.location.hash;if(""!==t){var i=t.indexOf("&"+e+"=")>0?t.indexOf("&"+e+"="):t.indexOf("#"+e+"=");if(i>=0){i+=e.length+2;var n=t.indexOf("&",i);return n>i?decodeURIComponent(t.substring(i,n)):n<0?decodeURIComponent(t.substring(i)):""}return!1}return!1}function n(e,t){var n=i(e),o=window.location.hash;if(n!==t)if("string"==typeof n){var r=o.indexOf("&"+e+"=")>0?o.indexOf("&"+e+"="):o.indexOf("#"+e+"="),a=r+e.length+2+n.length,s=0===r?"#":"&";window.location.replace(o.substring(0,r)+s+e+"="+t+o.substring(a))}else 0===o.length?window.location.replace("#"+e+"="+t):window.location.replace(o+"&"+e+"="+t)}e.exports.get=i,e.exports.update=n},function(e,t,i){function n(e){this.whitelistedKeys=e.whitelistedKeys||[],this.additionalProperties=e.additionalProperties||[],this.validations=e.validations}function o(e,t,i){var n={proxy:{},index:null},o=a.bind(null,e,t),r={};return i.whitelistedKeys.forEach(function(e){r[e]={get:o.bind(null,e)}}),i.additionalProperties.forEach(function(e){r[e.key]={get:e.get}}),i.validations.forEach(function(e,t){r[e.key]={get:function(){if(t0?Q("ViewerDidScrollDown",r):e<0&&Q("ViewerDidScrollUp",r),N()},q=function(){P.innerObject.mousedown(function(){P.innerObject.addClass("diva-grabbing")}),P.innerObject.mouseup(function(){P.innerObject.removeClass("diva-grabbing")}),G(),P.viewportObject.scroll(Y);var e=38,t=40,i=37,n=39,o=32,a=33,s=34,l=36,u=35;r(document).on("keydown.diva",function(r){if(!P.isActiveDiva)return!0;if(x.enableSpaceScroll&&!r.shiftKey&&r.keyCode===o||x.enableKeyScroll&&r.keyCode===s)return P.viewport.top+=x.panelHeight,!1;if(x.enableSpaceScroll||r.keyCode!==o||r.preventDefault(),x.enableKeyScroll){if(r.shiftKey||r.ctrlKey||r.metaKey)return!0;switch(r.keyCode){case a:return P.viewport.top-=x.panelHeight,!1;case e:return P.viewport.top-=x.arrowScrollAmount,!1;case t:return P.viewport.top+=x.arrowScrollAmount,!1;case i:return P.viewport.left-=x.arrowScrollAmount,!1;case n:return P.viewport.left+=x.arrowScrollAmount,!1;case l:return P.viewport.top=0,!1;case u:return x.verticallyOriented?P.viewport.top=1/0:P.viewport.left=1/0,!1;default:return!0}}return!0}),c.Events.subscribe("ViewerDidTerminate",function(){r(document).off("keydown.diva")},x.ID),W(),window.addEventListener("resize",B,!1),c.Events.subscribe("ViewerDidTerminate",function(){window.removeEventListener("resize",B,!1)},x.ID),"onorientationchange"in window&&(window.addEventListener("orientationchange",B,!1),c.Events.subscribe("ViewerDidTerminate",function(){window.removeEventListener("orientationchange",B,!1)},x.ID)),c.Events.subscribe("PanelSizeDidChange",H,x.ID),c.Events.subscribe("ViewerDidTerminate",function(){P.renderer&&P.renderer.destroy(),clearTimeout(P.resizeTimer)},x.ID)},U=function(){f.getAll().forEach(function(e){var t=e.pluginName[0].toUpperCase()+e.pluginName.substring(1);if(x["enable"+t]){var n=e.init(x,i);if(!n)return;"function"==typeof e.handleClick&&P.pageTools.push(e),x.plugins.push(e)}})},X=function(){$(),P.throbberTimeoutID=setTimeout(function(){r(x.selector+"throbber").show()},x.throbberTimeout)},$=function(){clearTimeout(P.throbberTimeoutID),r(x.selector+"throbber").hide()},K=function(e){var t=a("div",C("error"),[a("button",C("error-close",{"aria-label":"Close dialog"})),a("p",a("strong","Error")),a("div",e)]);P.outerObject.append(t),r(x.selector+"error-close").on("click",function(){t.parentNode.removeChild(t)})},J=function(e,t){if(P.manifest=e,$(),P.numPages=x.manifest.pages.length,I.validate(P.options),Q("NumberOfPagesDidChange",x.numPages),x.enableAutoTitle&&(r(x.selector+"title").length?r(x.selector+"title").html(x.manifest.itemTitle):x.parentObject.prepend(a("div",C("title"),[x.manifest.itemTitle]))),x.adaptivePadding>0){var i=Math.floor((x.minZoomLevel+x.maxZoomLevel)/2);P.horizontalPadding=parseInt(x.manifest.getAverageWidth(i)*x.adaptivePadding,10),P.verticalPadding=parseInt(x.manifest.getAverageHeight(i)*x.adaptivePadding,10)}else P.horizontalPadding=x.fixedPadding,P.verticalPadding=x.fixedPadding;P.pageTools.length&&(P.verticalPadding=Math.max(40,P.verticalPadding)),x.manifest.paged&&(P.options.inBookLayout=!0),Q("ObjectDidLoad",x),H();var n,o,s=!1,l=!1;null==t.goDirectlyTo?(t.goDirectlyTo=x.goDirectlyTo,n=o=!0):(n=null==t.horizontalOffset||isNaN(t.horizontalOffset),o=null==t.verticalOffset||isNaN(t.verticalOffset)),n&&(0===t.goDirectlyTo&&x.inBookLayout&&x.verticallyOriented?t.horizontalOffset=P.horizontalPadding:(l=!0,t.horizontalOffset=Z(t.goDirectlyTo,"center"))),o&&(s=!0,t.verticalOffset=V(t.goDirectlyTo,"top")),T(t),H(),x.verticallyOriented?P.innerElement.style.minWidth=x.panelWidth+"px":P.innerElement.style.minHeight=x.panelHeight+"px",(s||l)&&(s&&(P.verticalOffset=V(x.currentPageIndex,"top")),l&&(P.horizontalOffset=Z(x.currentPageIndex,"center")),P.renderer.goto(x.currentPageIndex,P.verticalOffset,P.horizontalOffset)),P.loaded=!0,Q("ViewerDidLoad",x)},Q=function(e){var t=Array.prototype.slice.call(arguments,1);c.Events.publish(e,t,i)},ee=function(){P.scrollbarWidth=s(),P.mobileWebkit=void 0!==window.orientation;var e=o();P.ID="diva-"+e+"-",P.selector="#"+x.ID,null===t.hashParamSuffix&&(1===e?t.hashParamSuffix="":t.hashParamSuffix=e+"");var i=a("div",C("inner",{class:"diva-inner diva-dragger"})),n=a("div",C("viewport"),i),l=a("div",C("outer"),n,a("div",C("throbber")));P.innerElement=i,P.viewportElement=n,P.outerElement=l,P.innerObject=r(i),P.viewportObject=r(n),P.outerObject=r(l),x.parentObject.append(l),P.viewport=new w(P.viewportElement,{intersectionTolerance:x.viewportMargin}),U(),q(),X()};this.getSettings=function(){return x},this.getInternalState=function(){return P},this.getPublicInstance=function(){return i},this.getPageTools=function(){return P.pageTools},this.getCurrentLayout=function(){return P.renderer?P.renderer.layout:null},this.getViewport=function(){var e=P.viewport;return{top:e.top,left:e.left,bottom:e.bottom,right:e.right,width:e.width,height:e.height}},this.addPageOverlay=function(e){P.pageOverlays.addOverlay(e)},this.removePageOverlay=function(e){P.pageOverlays.removeOverlay(e)},this.getPageRegion=function(e,t){var i=P.renderer.layout,n=i.getPageRegion(e,t);if(t&&t.incorporateViewport){var o=x.verticallyOriented?"width":"height";if(P.viewport[o]>i.dimensions[o]){var r=(P.viewport[o]-i.dimensions[o])/2;return x.verticallyOriented?{top:n.top,bottom:n.bottom,left:n.left+r,right:n.right+r}:{top:n.top+r,bottom:n.bottom+r,left:n.left,right:n.right}}}return n},this.getPagePositionAtViewportOffset=function(e){for(var t={left:e.left+P.viewport.left,top:e.top+P.viewport.top},i=P.renderer.getRenderedPages(),n=i.length,o=0;o=t.left&&a.top<=t.top&&a.bottom>=t.top)return{anchorPage:r,offset:{left:t.left-a.left,top:t.top-a.top}}}var s=P.renderer.layout.getPageRegion(x.currentPageIndex);return{anchorPage:x.currentPageIndex,offset:{left:t.left-s.left,top:t.top-s.top}}},this.setManifest=function(e,t){J(e,t||{})},this.setCurrentPage=function(e){P.currentPageIndex!==e&&(P.currentPageIndex=e,Q("VisiblePageDidChange",e,this.getPageName(e)))},this.getPageName=function(e){return P.manifest.pages[e].f},this.reload=function(e){T(e)},this.zoom=function(e,t){return F(e,t)},this.enableScrollable=function(){P.isScrollable||(G(),P.options.enableKeyScroll=P.initialKeyScroll,P.options.enableSpaceScroll=P.initialSpaceScroll,P.viewportElement.style.overflow="auto",P.isScrollable=!0)},this.disableScrollable=function(){P.isScrollable&&(P.innerObject.hasClass("diva-dragger")&&P.innerObject.unbind("mousedown"),P.outerObject.unbind("dblclick"),P.outerObject.unbind("contextmenu"),P.viewportElement.style.overflow="hidden",P.initialKeyScroll=x.enableKeyScroll,P.initialSpaceScroll=x.enableSpaceScroll,P.options.enableKeyScroll=!1,P.options.enableSpaceScroll=!1,P.isScrollable=!1)},this.isValidOption=function(e,t){return L(e,t)},this.showError=function(e){$();var t=a("div",C("error"),[a("button",C("error-close",{"aria-label":"Close dialog"})),a("p",a("strong","Error")),a("div",e)]);P.outerObject.append(t),r(x.selector+"error-close").on("click",function(){t.parentNode.removeChild(t)})},this.getXOffset=function(e,t){return Z(e,t)},this.getYOffset=function(e,t){return V(e,t)},this.publish=Q,this.clear=function(){D()},this.setPendingManifestRequest=function(e){P.pendingManifestRequest=e},this.destroy=function(){Q("ViewerWillTerminate",x),x.pendingManifestRequest&&x.pendingManifestRequest.abort(),r("body").removeClass("diva-hide-scrollbar"),x.parentObject.parent().empty().removeData("diva"),x.parentObject.parent().removeAttr("style").removeAttr("class"),Q("ViewerDidTerminate",x),c.Events.unsubscribeAll(x.ID)},ee()}function o(){return o.counter++}var r=i(1);i(10);var a=i(3),s=i(43),l=i(20),c=i(5),u=i(18),h=i(21),d=i(30),f=i(8),g=i(39),p=i(28),v=i(40),m=i(45),w=i(47),y=i(4)("diva:ViewerCore");e.exports=n;var b=[{key:"goDirectlyTo",validate:function(e,t){if(e<0||e>=t.manifest.pages.length)return 0}},{key:"minPagesPerRow",validate:function(e){return Math.max(2,e)}},{key:"maxPagesPerRow",validate:function(e,t){return Math.max(e,t.minPagesPerRow)}},{key:"pagesPerRow",validate:function(e,t){if(et.maxPagesPerRow)return t.maxPagesPerRow}},{key:"maxZoomLevel",validate:function(e,t,i){if(i.suppressWarning(),e<0||e>t.manifest.maxZoom)return t.manifest.maxZoom}},{key:"minZoomLevel",validate:function(e,t,i){return e>t.manifest.maxZoom?(i.suppressWarning(),0):e<0||e>t.maxZoomLevel?0:void 0}},{key:"zoomLevel",validate:function(e,t,i){return e>t.manifest.maxZoom?(i.suppressWarning(),0):et.maxZoomLevel?t.minZoomLevel:void 0}}];o.counter=1},function(e,t){function i(e,t){t=t||{},this.intersectionTolerance=t.intersectionTolerance||0,this.maxExtent=t.maxExtent||2e3,this.outer=e,this._top=this._left=this._width=this._height=this._innerDimensions=null,this.invalidate()}function n(e,t){var i="_"+e,n="scroll"+e.charAt(0).toUpperCase()+e.slice(1);return{get:function(){return this[i]},set:function(e){var o;if(this._innerDimensions){var r=this._innerDimensions[t]-this[t];o=a(e,0,r)}else o=s(e,0); -this[i]=this.outer[n]=o}}}function o(e){return{get:function(){return this["_"+e]}}}function r(e,t,i){return e>=t&&e<=i}function a(e,t,i){return s(l(e,i),t)}function s(e,t){return Math.max(e,t)}function l(e,t){return Math.min(e,t)}e.exports=i,i.prototype.intersectsRegion=function(e){return this.hasHorizontalOverlap(e)&&this.hasVerticalOverlap(e)},i.prototype.hasVerticalOverlap=function(e){var t=this.top-this.intersectionTolerance,i=this.bottom+this.intersectionTolerance;return r(e.top,t,i)||r(e.bottom,t,i)||e.top<=t&&e.bottom>=i},i.prototype.hasHorizontalOverlap=function(e){var t=this.left-this.intersectionTolerance,i=this.right+this.intersectionTolerance;return r(e.left,t,i)||r(e.right,t,i)||e.left<=t&&e.right>=i},i.prototype.invalidate=function(){this._width=l(this.outer.clientWidth,this.maxExtent),this._height=l(this.outer.clientHeight,this.maxExtent),this._top=this.outer.scrollTop,this._left=this.outer.scrollLeft},i.prototype.setInnerDimensions=function(e){this._innerDimensions=e,e&&(this._top=a(this._top,0,e.height-this._height),this._left=a(this._left,0,e.width-this._width))},Object.defineProperties(i.prototype,{top:n("top","height"),left:n("left","width"),width:o("width"),height:o("height"),bottom:{get:function(){return this._top+this._height}},right:{get:function(){return this._left+this._width}}})}])}); -//# sourceMappingURL=diva.min.js.map \ No newline at end of file diff --git a/build/js/diva.min.js.map b/build/js/diva.min.js.map deleted file mode 100644 index 451d67a1..00000000 --- a/build/js/diva.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"diva.min.js","sources":["webpack:///diva.min.js"],"sourcesContent":["!function(e,t){\"object\"==typeof exports&&\"object\"==typeof module?module.exports=t(require(\"jquery\")):\"function\"==typeof define&&define.amd?define([\"jquery\"],t):\"object\"==typeof exports?exports.diva=t(require(\"jquery\")):e.diva=t(e.jQuery)}(this,function(e){return function(e){function t(n){if(i[n])return i[n].exports;var o=i[n]={exports:{},id:n,loaded:!1};return e[n].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var i={};return t.m=e,t.c=i,t.p=\"\",t(0)}([function(e,t,i){i(38),e.exports=i(2)},function(t,i){t.exports=e},function(e,t,i){i(11);var n=i(1),o=i(3),r=i(44),a=i(16),s=i(5),l=i(23),c=i(41),u=i(46);new a;e.exports=s,function(e){var t=function(t,i){var n,a,h,d=this;i=e.extend({adaptivePadding:.05,arrowScrollAmount:40,blockMobileMove:!1,objectData:\"\",enableAutoTitle:!0,enableFilename:!0,enableFullscreen:!0,enableGotoPage:!0,enableGotoSuggestions:!0,enableGridIcon:!0,enableGridControls:\"buttons\",enableImageTitles:!0,enableKeyScroll:!0,enableLinkIcon:!0,enableNonPagedVisibilityIcon:!0,enableSpaceScroll:!1,enableToolbar:!0,enableZoomControls:\"buttons\",fillParentHeight:!0,fixedPadding:10,fixedHeightGrid:!0,goDirectlyTo:0,hashParamSuffix:null,iipServerURL:\"\",inFullscreen:!1,inBookLayout:!1,inGrid:!1,imageDir:\"\",maxPagesPerRow:8,maxZoomLevel:-1,minPagesPerRow:2,minZoomLevel:0,onGotoSubmit:null,pageAliases:{},pageAliasFunction:function(){return!1},pageLoadTimeout:200,pagesPerRow:5,showNonPagedPages:!1,throbberTimeout:100,tileHeight:256,tileWidth:256,toolbarParentObject:null,verticallyOriented:!0,viewportMargin:200,zoomLevel:2},i);var f=function(e){return p(n.manifest,e)},g=function(e){return e},p=function(e,t){var i,n=e.pages.length;for(i=0;i=0&&o=0&&on.maxZoomLevel&&(t=n.maxZoomLevel);var i=n.manifest.pages[parseInt(e,10)],o=i.d[parseInt(t,10)];return{width:o.w,height:o.h}},this.getPageDimensionsAtCurrentZoomLevel=function(e){if(e=v(e)?e:n.currentPageIndex,!v(e))throw new Error(\"Invalid Page Index\");return h.viewerCore.getCurrentLayout().getPageDimensions(e)},this.getCurrentPageDimensionsAtCurrentZoomLevel=function(){return this.getPageDimensionsAtCurrentZoomLevel(n.currentPageIndex)},this.isReady=function(){return a.loaded},this.getCurrentPageIndex=function(){return n.currentPageIndex},this.getCurrentPageFilename=function(){return n.manifest.pages[n.currentPageIndex].f},this.getCurrentCanvas=function(e){return e.manifest.pages[e.currentPageIndex].canvas},this.getCurrentPageNumber=function(){return console.warn(\"This method is deprecated. Consider using getCurrentPageIndex() instead.\"),n.currentPageIndex+1},this.getFilenames=function(){for(var e=[],t=0;t-1)return this.gotoPageByIndex(r,t,i);var s=parseInt(e,10)-1;return this.gotoPageByIndex(s,t,i)},this.getPageIndex=function(e){return f(e)},this.getCurrentURL=function(){return I()},this.isPageIndexValid=function(e){return v(e)},this.getURLHash=function(){return x()},this.getState=function(){return _()},this.setState=function(e){m(P(e))},this.getInstanceSelector=function(){return n.selector},this.getInstanceId=function(){return n.ID},this.getSettings=function(){return n},this.translateFromMaxZoomLevel=function(e){var t=n.maxZoomLevel-n.zoomLevel;return e/Math.pow(2,t)},this.translateToMaxZoomLevel=function(e){var t=n.maxZoomLevel-n.zoomLevel;return 0===t?e:e*Math.pow(2,t)},this.enableScrollable=function(){h.viewerCore.enableScrollable()},this.disableScrollable=function(){h.viewerCore.disableScrollable()},this.toggleOrientation=function(){return y()},this.getPageOffset=function(e,t){var i=h.viewerCore.getPageRegion(e,t);return{top:i.top,left:i.left}},this.getCurrentPageOffset=function(){return this.getPageOffset(n.currentPageIndex)},this.getPageDimensionsAtCurrentGridLevel=function(e){return console.warn(\"This method is deprecated. Consider using getPageDimensionsAtCurrentZoomLevel(pageIndex) instead.\"),this.getPageDimensionsAtCurrentZoomLevel(e)},this.getPageIndexForPageXYValues=function(e,t){var i=a.outerElement.getBoundingClientRect(),n=i.top,o=i.left,r=i.bottom,s=i.right;if(es)return-1;if(tr)return-1;for(var l=document.getElementsByClassName(\"diva-page\"),c=l.length;c--;){var u=l[c],h=u.getBoundingClientRect();if(!(eh.right||th.bottom))return u.getAttribute(\"data-index\")}return-1},this.getPageCoordinatesHit=function(e,t){return a.renderer?a.renderer.getPageHit(e,t):null},this.getPageImageURL=function(e,t){return n.manifest.getPageImageURL(e,t)},this.isVerticallyOriented=function(){return n.verticallyOriented},this.changeObject=function(t){return a.loaded=!1,h.viewerCore.clear(),a.renderer&&a.renderer.destroy(),a.options.objectData=t,\"object\"==typeof t?void setTimeout(function(){D(t)}):(a.throbberTimeoutID=setTimeout(function(){e(n.selector+\"throbber\").show()},n.throbberTimeout),void e.ajax({url:n.objectData,cache:!0,dataType:\"json\",error:O,success:function(e){D(e)}}))},this.activate=function(){a.isActiveDiva=!0},this.deactivate=function(){a.isActiveDiva=!1},this.destroy=function(){h.viewerCore.destroy()},this.__addPageOverlay=function(e){h.viewerCore.addPageOverlay(e)},this.__removePageOverlay=function(e){h.viewerCore.removePageOverlay(e)},this.getAliasForPageIndex=function(e){var t=parseInt(e,10);return n.pageAliases[t]||n.pageAliasFunction(t)||t+1},this.getPageIndexForAlias=function(e){for(var t=0;t=31||\"undefined\"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\\/(\\d+)/))}function r(e){var i=this.useColors;if(e[0]=(i?\"%c\":\"\")+this.namespace+(i?\" %c\":\" \")+e[0]+(i?\"%c \":\" \")+\"+\"+t.humanize(this.diff),i){var n=\"color: \"+this.color;e.splice(1,0,n,\"color: inherit\");var o=0,r=0;e[0].replace(/%[a-zA-Z%]/g,function(e){\"%%\"!==e&&(o++,\"%c\"===e&&(r=o))}),e.splice(r,0,n)}}function a(){return\"object\"==typeof console&&console.log&&Function.prototype.apply.call(console.log,console,arguments)}function s(e){try{null==e?t.storage.removeItem(\"debug\"):t.storage.debug=e}catch(e){}}function l(){var e;try{e=t.storage.debug}catch(e){}return!e&&\"undefined\"!=typeof n&&\"env\"in n&&(e=n.env.DEBUG),e}function c(){try{return window.localStorage}catch(e){}}t=e.exports=i(12),t.log=a,t.formatArgs=r,t.save=s,t.load=l,t.useColors=o,t.storage=\"undefined\"!=typeof chrome&&\"undefined\"!=typeof chrome.storage?chrome.storage.local:c(),t.colors=[\"lightseagreen\",\"forestgreen\",\"goldenrod\",\"dodgerblue\",\"darkorchid\",\"crimson\"],t.formatters.j=function(e){try{return JSON.stringify(e)}catch(e){return\"[UnexpectedJSONParseError]: \"+e.message}},t.enable(l())}).call(t,i(14))},function(e,t,i){function n(e){var t=e.id?\"#\"+e.id:e.id,i=e.className?\".\"+e.className.split(/\\s+/g).join(\".\"):\"\";return(t?t:e.tagName.toLowerCase())+i}var o=i(1),r=i(42),a=i(8),s=e.exports={Events:new r,registerPlugin:function(e){a.register(e)},create:function(e,t){if(s.find(e))throw new Error(\"Diva is already initialized on \"+n(e));var i=o(e);return i.diva(t),i.data(\"diva\")},find:function(e){return o(e).data(\"diva\")||null}}},function(e,t,i){(function(e,i){function n(e,t){for(var i=-1,n=e?e.length:0;++i-1}function x(e,t){var i=this.__data__,n=Z(i,e);return n<0?i.push([e,t]):i[n][1]=t,this}function I(e){var t=-1,i=e?e.length:0;for(this.clear();++tt}function W(e,t){return null!=e&&t in Object(e)}function Y(e,t,i,n,o){return e===t||(null==e||null==t||!Oe(e)&&!De(t)?e!==e&&t!==t:q(e,t,Y,i,n,o))}function q(e,t,i,n,o,r){var a=hi(e),s=hi(t),c=We,u=We;a||(c=ci(e),c=c==Be?Qe:c),s||(u=ci(t),u=u==Be?Qe:u);var h=c==Qe&&!l(e),d=u==Qe&&!l(t),f=c==u;if(f&&!h)return r||(r=new S),a||di(e)?oe(e,t,i,n,o,r):re(e,t,c,i,n,o,r);if(!(o&He)){var g=h&&Ht.call(e,\"__wrapped__\"),p=d&&Ht.call(t,\"__wrapped__\");if(g||p){var v=g?e.value():e,m=p?t.value():t;return r||(r=new S),i(v,m,n,o,r)}}return!!f&&(r||(r=new S),ae(e,t,i,n,o,r))}function U(e,t,i,n){var o=i.length,r=o,a=!n;if(null==e)return!r;for(e=Object(e);o--;){var s=i[o];if(a&&s[2]?s[1]!==e[s[0]]:!(s[0]in e))return!1}for(;++ol))return!1;var u=a.get(e);if(u&&a.get(t))return u==t;var h=-1,d=!0,f=r&Ze?new k:void 0;for(a.set(e,t),a.set(t,e);++h-1&&e%1==0&&e-1&&e%1==0&&e<=Ge}function Oe(e){var t=typeof e;return!!e&&(\"object\"==t||\"function\"==t)}function De(e){return!!e&&\"object\"==typeof e}function Te(e){return\"symbol\"==typeof e||De(e)&&Nt.call(e)==ot}function ke(e){return null==e?\"\":ie(e)}function Ee(e,t,i){var n=null==e?void 0:N(e,t);return void 0===n?i:n}function Me(e,t){return null!=e&&ue(e,t,W)}function Se(e){return xe(e)?V(e):J(e)}function ze(e){return e}function Ae(e){return de(e)?o(we(e)):te(e)}function je(e,t){return e&&e.length?H(e,K(t,2),B):void 0}var Re=200,Fe=\"Expected a function\",Ve=\"__lodash_hash_undefined__\",Ze=1,He=2,Ne=1/0,Ge=9007199254740991,Be=\"[object Arguments]\",We=\"[object Array]\",Ye=\"[object Boolean]\",qe=\"[object Date]\",Ue=\"[object Error]\",Xe=\"[object Function]\",$e=\"[object GeneratorFunction]\",Ke=\"[object Map]\",Je=\"[object Number]\",Qe=\"[object Object]\",et=\"[object Promise]\",tt=\"[object RegExp]\",it=\"[object Set]\",nt=\"[object String]\",ot=\"[object Symbol]\",rt=\"[object WeakMap]\",at=\"[object ArrayBuffer]\",st=\"[object DataView]\",lt=\"[object Float32Array]\",ct=\"[object Float64Array]\",ut=\"[object Int8Array]\",ht=\"[object Int16Array]\",dt=\"[object Int32Array]\",ft=\"[object Uint8Array]\",gt=\"[object Uint8ClampedArray]\",pt=\"[object Uint16Array]\",vt=\"[object Uint32Array]\",mt=/\\.|\\[(?:[^[\\]]*|([\"'])(?:(?!\\1)[^\\\\]|\\\\.)*?\\1)\\]/,wt=/^\\w*$/,yt=/^\\./,bt=/[^.[\\]]+|\\[(?:(-?\\d+(?:\\.\\d+)?)|([\"'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2)\\]|(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))/g,_t=/[\\\\^$.*+?()[\\]{}|]/g,Pt=/\\\\(\\\\)?/g,xt=/^\\[object .+?Constructor\\]$/,It=/^(?:0|[1-9]\\d*)$/,Lt={};Lt[lt]=Lt[ct]=Lt[ut]=Lt[ht]=Lt[dt]=Lt[ft]=Lt[gt]=Lt[pt]=Lt[vt]=!0,Lt[Be]=Lt[We]=Lt[at]=Lt[Ye]=Lt[st]=Lt[qe]=Lt[Ue]=Lt[Xe]=Lt[Ke]=Lt[Je]=Lt[Qe]=Lt[tt]=Lt[it]=Lt[nt]=Lt[rt]=!1;var Ct=\"object\"==typeof e&&e&&e.Object===Object&&e,Ot=\"object\"==typeof self&&self&&self.Object===Object&&self,Dt=Ct||Ot||Function(\"return this\")(),Tt=\"object\"==typeof t&&t&&!t.nodeType&&t,kt=Tt&&\"object\"==typeof i&&i&&!i.nodeType&&i,Et=kt&&kt.exports===Tt,Mt=Et&&Ct.process,St=function(){try{return Mt&&Mt.binding(\"util\")}catch(e){}}(),zt=St&&St.isTypedArray,At=Array.prototype,jt=Function.prototype,Rt=Object.prototype,Ft=Dt[\"__core-js_shared__\"],Vt=function(){var e=/[^.]+$/.exec(Ft&&Ft.keys&&Ft.keys.IE_PROTO||\"\");return e?\"Symbol(src)_1.\"+e:\"\"}(),Zt=jt.toString,Ht=Rt.hasOwnProperty,Nt=Rt.toString,Gt=RegExp(\"^\"+Zt.call(Ht).replace(_t,\"\\\\$&\").replace(/hasOwnProperty|(function).*?(?=\\\\\\()| for .+?(?=\\\\\\])/g,\"$1.*?\")+\"$\"),Bt=Dt.Symbol,Wt=Dt.Uint8Array,Yt=Rt.propertyIsEnumerable,qt=At.splice,Ut=u(Object.keys,Object),Xt=ce(Dt,\"DataView\"),$t=ce(Dt,\"Map\"),Kt=ce(Dt,\"Promise\"),Jt=ce(Dt,\"Set\"),Qt=ce(Dt,\"WeakMap\"),ei=ce(Object,\"create\"),ti=ye(Xt),ii=ye($t),ni=ye(Kt),oi=ye(Jt),ri=ye(Qt),ai=Bt?Bt.prototype:void 0,si=ai?ai.valueOf:void 0,li=ai?ai.toString:void 0;d.prototype.clear=f,d.prototype.delete=g,d.prototype.get=p,d.prototype.has=v,d.prototype.set=m,w.prototype.clear=y,w.prototype.delete=b,w.prototype.get=_,w.prototype.has=P,w.prototype.set=x,I.prototype.clear=L,I.prototype.delete=C,I.prototype.get=O,I.prototype.has=D,I.prototype.set=T,k.prototype.add=k.prototype.push=E,k.prototype.has=M,S.prototype.clear=z,S.prototype.delete=A,S.prototype.get=j,S.prototype.has=R,S.prototype.set=F;var ci=G;(Xt&&ci(new Xt(new ArrayBuffer(1)))!=st||$t&&ci(new $t)!=Ke||Kt&&ci(Kt.resolve())!=et||Jt&&ci(new Jt)!=it||Qt&&ci(new Qt)!=rt)&&(ci=function(e){var t=Nt.call(e),i=t==Qe?e.constructor:void 0,n=i?ye(i):void 0;if(n)switch(n){case ti:return st;case ii:return Ke;case ni:return et;case oi:return it;case ri:return rt}return t});var ui=be(function(e){e=ke(e);var t=[];return yt.test(e)&&t.push(\"\"),e.replace(bt,function(e,i,n,o){t.push(n?o.replace(Pt,\"$1\"):i||e)}),t});be.Cache=I;var hi=Array.isArray,di=zt?a(zt):$;i.exports=je}).call(t,function(){return this}(),i(15)(e))},function(e,t){e.exports=function(e,t){var i=t.getMaxPageDimensions(e);return{width:Math.floor(i.width),height:Math.floor(i.height)}}},function(e,t){var i=[];e.exports={register:function(e){i.push(e)},getAll:function(){return i}}},function(e,t,i){function n(e,t){this._divaInstance=e,this._overlays={},this._getCurrentHighlight=t}function o(e,t,i){this.page=e.page,this.highlight=e,this._highlightRegions=[],this._divaInstance=t,this._getCurrentHighlight=i}function r(e,t){return e/Math.pow(2,t)}function a(e,t){var i,n,o,r=e.getInstanceId()+\"selected-highlight\",a=document.getElementsByClassName(r);for(i=0;it&&ei};h--;)p=u[h],f=s(p),v(f,l,a)&&(m=!0,a=f,g=p);if(m)return r(c,g);var w,y=i.getHighlightedPages(),b=y.indexOf(c.toString());if(n)for(;!w||!t.isPageIndexValid(w);)w=b==y.length-1?y[0]:y[++b];else for(;!w||!t.isPageIndexValid(w);)w=0===b?y[y.length-1]:y[--b];for(u=i.getHighlightRegions(w),h=u.length,d=t.getPageDimensionsAtZoomLevel(w,t.getMaxZoomLevel()),a=n?e.verticallyOriented?d.height:d.width:0,v=n?function(e,t){return et};h--;)p=u[h],f=s(p),v(f,a)&&(m=!0,a=f,g=p);return r(w,g)};return t.gotoNextHighlight=function(){return i.getHighlightCount()>0&&l(!0)},t.gotoPreviousHighlight=function(){return i.getHighlightCount()>0&&l(!1)},c.Events.subscribe(\"ViewerWillTerminate\",this.destroy,e.ID),!0},destroy:function(e){var t=e.parentObject.data(\"highlightManager\");t.clear(),e.parentObject.removeData(\"highlightManager\")},pluginName:\"highlight\",titleText:\"Highlight regions of pages\",HighlightManager:n};return e}()}(s),n.prototype.getHighlightCount=function(){var e=0;return Object.keys(this._overlays).forEach(function(t){e+=this._overlays[t].highlight.regions.length},this),e},n.prototype.getHighlightRegions=function(e){return this._overlays[e]?this._overlays[e].highlight.regions:[]},n.prototype.getHighlightedPages=function(){return Object.keys(this._overlays)},n.prototype.getHighlightByRegionId=function(e){for(var t in this._overlays)if(this._overlays.hasOwnProperty(t)){var i=this._overlays[t].highlight.regions;for(var n in i)if(i.hasOwnProperty(n)&&i[n].divID===e)return{highlight:this._overlays[t].highlight,region:i[n]}}return null},n.prototype.addHighlight=function(e){var t=this._overlays[e.page];t&&this._divaInstance.__removePageOverlay(t);var i=new o(e,this._divaInstance,this._getCurrentHighlight);this._overlays[e.page]=i,this._divaInstance.__addPageOverlay(i)},n.prototype.removeHighlightsOnPage=function(e){this._overlays[e]&&(this._divaInstance.__removePageOverlay(this._overlays[e]),delete this._overlays[e])},n.prototype.clear=function(){for(var e in this._overlays)this._overlays.hasOwnProperty(e)&&this._divaInstance.__removePageOverlay(this._overlays[e]);this._overlays={}},o.prototype.mount=function(){for(var e=this._divaInstance.getSettings(),t=this.highlight,i=t.regions,n=t.colour,o=t.divClass,r=i.length;r--;){var s=i[r],u=l(\"div\",{class:o,style:{background:n,border:\"1px solid #555\",position:\"absolute\",zIndex:100}});void 0!==s.divID&&u.setAttribute(\"data-highlight-id\",s.divID),void 0!==s.name&&u.setAttribute(\"data-name\",s.name),\nthis._highlightRegions.push({element:u,region:s})}this.refresh();var h=document.createDocumentFragment();this._highlightRegions.forEach(function(e){h.appendChild(e.element)}),e.innerElement.appendChild(h),this._getCurrentHighlight&&a(this._divaInstance,this._getCurrentHighlight()),c.Events.publish(\"HighlightCompleted\",[this.page,this._divaInstance.getFilenames()[this.page]])},o.prototype.unmount=function(){var e=this._divaInstance.getSettings().innerElement;this._highlightRegions.forEach(function(t){e.removeChild(t.element)}),this._highlightRegions=[]},o.prototype.refresh=function(){var e=this._divaInstance.getMaxZoomLevel(),t=this._divaInstance.getPageDimensionsAtZoomLevel(this.page,e).width,i=this._divaInstance.getPageDimensions(this.page).width,n=Math.log(t/i)/Math.log(2),o=this._divaInstance.getPageOffset(this.page,{excludePadding:!0,incorporateViewport:!0});this._highlightRegions.forEach(function(e){var t=e.region;l.setAttributes(e.element,{style:{width:r(t.width,n)+\"px\",height:r(t.height,n)+\"px\",top:o.top+r(t.uly,n)+\"px\",left:o.left+r(t.ulx,n)+\"px\"}})})}},function(e,t,i){var n=i(1);!function(e){e.fn.dragscrollable=function(t){var i=e.extend({dragSelector:\">:first\",acceptPropagatedEvent:!0,preventDefault:!0},t||{}),n={mouseDownHandler:function(t){return!(1!=t.which||!t.data.acceptPropagatedEvent&&t.target!=this)&&(t.data.lastCoord={left:t.clientX,top:t.clientY},e.event.add(document,\"mouseup\",n.mouseUpHandler,t.data),e.event.add(document,\"mousemove\",n.mouseMoveHandler,t.data),t.data.preventDefault?(t.preventDefault(),!1):void 0)},mouseMoveHandler:function(e){var t={left:e.clientX-e.data.lastCoord.left,top:e.clientY-e.data.lastCoord.top};if(e.data.scrollable.scrollLeft(e.data.scrollable.scrollLeft()-t.left),e.data.scrollable.scrollTop(e.data.scrollable.scrollTop()-t.top),e.data.lastCoord={left:e.clientX,top:e.clientY},e.data.preventDefault)return e.preventDefault(),!1},mouseUpHandler:function(t){if(e.event.remove(document,\"mousemove\",n.mouseMoveHandler),e.event.remove(document,\"mouseup\",n.mouseUpHandler),t.data.preventDefault)return t.preventDefault(),!1}};this.each(function(){var t={scrollable:e(this),acceptPropagatedEvent:i.acceptPropagatedEvent,preventDefault:i.preventDefault};e(this).find(i.dragSelector).bind(\"mousedown\",t,n.mouseDownHandler)})}}(n),/**\n\t jQuery.kinetic v2.2.1\n\t Dave Taylor http://davetayls.me\n\t\n\t @license The MIT License (MIT)\n\t @preserve Copyright (c) 2012 Dave Taylor http://davetayls.me\n\t */\nfunction(e){\"use strict\";var t=\"kinetic-active\";window.requestAnimationFrame||(window.requestAnimationFrame=function(){return window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(e,t){window.setTimeout(e,1e3/60)}}()),e.support=e.support||{},e.extend(e.support,{touch:\"ontouchend\"in document});var i=function(t,i){return this.settings=i,this.el=t,this.$el=e(t),this._initElements(),this};i.DATA_KEY=\"kinetic\",i.DEFAULTS={cursor:\"move\",decelerate:!0,triggerHardware:!1,threshold:0,y:!0,x:!0,slowdown:.9,maxvelocity:40,throttleFPS:60,invert:!1,movingClass:{up:\"kinetic-moving-up\",down:\"kinetic-moving-down\",left:\"kinetic-moving-left\",right:\"kinetic-moving-right\"},deceleratingClass:{up:\"kinetic-decelerating-up\",down:\"kinetic-decelerating-down\",left:\"kinetic-decelerating-left\",right:\"kinetic-decelerating-right\"}},i.prototype.start=function(t){this.settings=e.extend(this.settings,t),this.velocity=t.velocity||this.velocity,this.velocityY=t.velocityY||this.velocityY,this.settings.decelerate=!1,this._move()},i.prototype.end=function(){this.settings.decelerate=!0},i.prototype.stop=function(){this.velocity=0,this.velocityY=0,this.settings.decelerate=!0,e.isFunction(this.settings.stopped)&&this.settings.stopped.call(this)},i.prototype.detach=function(){this._detachListeners(),this.$el.removeClass(t).css(\"cursor\",\"\")},i.prototype.attach=function(){this.$el.hasClass(t)||(this._attachListeners(this.$el),this.$el.addClass(t).css(\"cursor\",this.settings.cursor))},i.prototype._initElements=function(){this.$el.addClass(t),e.extend(this,{xpos:null,prevXPos:!1,ypos:null,prevYPos:!1,mouseDown:!1,throttleTimeout:1e3/this.settings.throttleFPS,lastMove:null,elementFocused:null}),this.velocity=0,this.velocityY=0,e(document).mouseup(e.proxy(this._resetMouse,this)).click(e.proxy(this._resetMouse,this)),this._initEvents(),this.$el.css(\"cursor\",this.settings.cursor),this.settings.triggerHardware&&this.$el.css({\"-webkit-transform\":\"translate3d(0,0,0)\",\"-webkit-perspective\":\"1000\",\"-webkit-backface-visibility\":\"hidden\"})},i.prototype._initEvents=function(){var t=this;this.settings.events={touchStart:function(e){var i;t._useTarget(e.target,e)&&(i=e.originalEvent.touches[0],t.threshold=t._threshold(e.target,e),t._start(i.clientX,i.clientY),e.stopPropagation())},touchMove:function(e){var i;t.mouseDown&&(i=e.originalEvent.touches[0],t._inputmove(i.clientX,i.clientY),e.preventDefault&&e.preventDefault())},inputDown:function(e){t._useTarget(e.target,e)&&(t.threshold=t._threshold(e.target,e),t._start(e.clientX,e.clientY),t.elementFocused=e.target,\"IMG\"===e.target.nodeName&&e.preventDefault(),e.stopPropagation())},inputEnd:function(e){t._useTarget(e.target,e)&&(t._end(),t.elementFocused=null,e.preventDefault&&e.preventDefault())},inputMove:function(e){t.mouseDown&&(t._inputmove(e.clientX,e.clientY),e.preventDefault&&e.preventDefault())},scroll:function(i){e.isFunction(t.settings.moved)&&t.settings.moved.call(t,t.settings),i.preventDefault&&i.preventDefault()},inputClick:function(e){if(Math.abs(t.velocity)>0)return e.preventDefault(),!1},dragStart:function(e){if(t._useTarget(e.target,e)&&t.elementFocused)return!1},selectStart:function(i){return e.isFunction(t.settings.selectStart)?t.settings.selectStart.apply(t,arguments):!t._useTarget(i.target,i)&&void 0}},this._attachListeners(this.$el,this.settings)},i.prototype._inputmove=function(t,i){var n=this.$el;this.el;if((!this.lastMove||new Date>new Date(this.lastMove.getTime()+this.throttleTimeout))&&(this.lastMove=new Date,this.mouseDown&&(this.xpos||this.ypos))){var o=t-this.xpos,r=i-this.ypos;if(this.settings.invert&&(o*=-1,r*=-1),this.threshold>0){var a=Math.sqrt(o*o+r*r);if(this.threshold>a)return;this.threshold=0}this.elementFocused&&(e(this.elementFocused).blur(),this.elementFocused=null,n.focus()),this.settings.decelerate=!1,this.velocity=this.velocityY=0;var s=this.scrollLeft(),l=this.scrollTop();this.scrollLeft(this.settings.x?s-o:s),this.scrollTop(this.settings.y?l-r:l),this.prevXPos=this.xpos,this.prevYPos=this.ypos,this.xpos=t,this.ypos=i,this._calculateVelocities(),this._setMoveClasses(this.settings.movingClass),e.isFunction(this.settings.moved)&&this.settings.moved.call(this,this.settings)}},i.prototype._calculateVelocities=function(){this.velocity=this._capVelocity(this.prevXPos-this.xpos,this.settings.maxvelocity),this.velocityY=this._capVelocity(this.prevYPos-this.ypos,this.settings.maxvelocity),this.settings.invert&&(this.velocity*=-1,this.velocityY*=-1)},i.prototype._end=function(){this.xpos&&this.prevXPos&&this.settings.decelerate===!1&&(this.settings.decelerate=!0,this._calculateVelocities(),this.xpos=this.prevXPos=this.mouseDown=!1,this._move())},i.prototype._useTarget=function(t,i){return!e.isFunction(this.settings.filterTarget)||this.settings.filterTarget.call(this,t,i)!==!1},i.prototype._threshold=function(t,i){return e.isFunction(this.settings.threshold)?this.settings.threshold.call(this,t,i):this.settings.threshold},i.prototype._start=function(e,t){this.mouseDown=!0,this.velocity=this.prevXPos=0,this.velocityY=this.prevYPos=0,this.xpos=e,this.ypos=t},i.prototype._resetMouse=function(){this.xpos=!1,this.ypos=!1,this.mouseDown=!1},i.prototype._decelerateVelocity=function(e,t){return 0===Math.floor(Math.abs(e))?0:e*t},i.prototype._capVelocity=function(e,t){var i=e;return e>0?e>t&&(i=t):e<0-t&&(i=0-t),i},i.prototype._setMoveClasses=function(e){var t=this.settings,i=this.$el;i.removeClass(t.movingClass.up).removeClass(t.movingClass.down).removeClass(t.movingClass.left).removeClass(t.movingClass.right).removeClass(t.deceleratingClass.up).removeClass(t.deceleratingClass.down).removeClass(t.deceleratingClass.left).removeClass(t.deceleratingClass.right),this.velocity>0&&i.addClass(e.right),this.velocity<0&&i.addClass(e.left),this.velocityY>0&&i.addClass(e.down),this.velocityY<0&&i.addClass(e.up)},i.prototype._move=function(){var t=this._getScroller(),i=t[0],n=this,o=n.settings;o.x&&i.scrollWidth>0?(this.scrollLeft(this.scrollLeft()+this.velocity),Math.abs(this.velocity)>0&&(this.velocity=o.decelerate?n._decelerateVelocity(this.velocity,o.slowdown):this.velocity)):this.velocity=0,o.y&&i.scrollHeight>0?(this.scrollTop(this.scrollTop()+this.velocityY),Math.abs(this.velocityY)>0&&(this.velocityY=o.decelerate?n._decelerateVelocity(this.velocityY,o.slowdown):this.velocityY)):this.velocityY=0,n._setMoveClasses(o.deceleratingClass),e.isFunction(o.moved)&&o.moved.call(this,o),Math.abs(this.velocity)>0||Math.abs(this.velocityY)>0?this.moving||(this.moving=!0,window.requestAnimationFrame(function(){n.moving=!1,n._move()})):n.stop()},i.prototype._getScroller=function(){var t=this.$el;return(this.$el.is(\"body\")||this.$el.is(\"html\"))&&(t=e(window)),t},i.prototype.scrollLeft=function(e){var t=this._getScroller();return\"number\"!=typeof e?t.scrollLeft():(t.scrollLeft(e),void(this.settings.scrollLeft=e))},i.prototype.scrollTop=function(e){var t=this._getScroller();return\"number\"!=typeof e?t.scrollTop():(t.scrollTop(e),void(this.settings.scrollTop=e))},i.prototype._attachListeners=function(){var t=this.$el,i=this.settings;e.support.touch&&t.bind(\"touchstart\",i.events.touchStart).bind(\"touchend\",i.events.inputEnd).bind(\"touchmove\",i.events.touchMove),t.mousedown(i.events.inputDown).mouseup(i.events.inputEnd).mousemove(i.events.inputMove),t.click(i.events.inputClick).scroll(i.events.scroll).bind(\"selectstart\",i.events.selectStart).bind(\"dragstart\",i.events.dragStart)},i.prototype._detachListeners=function(){var t=this.$el,i=this.settings;e.support.touch&&t.unbind(\"touchstart\",i.events.touchStart).unbind(\"touchend\",i.events.inputEnd).unbind(\"touchmove\",i.events.touchMove),t.unbind(\"mousedown\",i.events.inputDown).unbind(\"mouseup\",i.events.inputEnd).unbind(\"mousemove\",i.events.inputMove),t.unbind(\"click\",i.events.inputClick).unbind(\"scroll\",i.events.scroll).unbind(\"selectstart\",i.events.selectStart).unbind(\"dragstart\",i.events.dragStart)},e.Kinetic=i,e.fn.kinetic=function(t,n){return this.each(function(){var o=e(this),r=o.data(i.DATA_KEY),a=e.extend({},i.DEFAULTS,o.data(),\"object\"==typeof t&&t);r||o.data(i.DATA_KEY,r=new i(this,a)),\"string\"==typeof t&&r[t](n)})}}(n),function(e){e.Kinetic.prototype._attachListeners=function(){var t=this.$el,i=this.settings;e.support.touch&&t.bind(\"touchstart\",i.events.touchStart).bind(\"touchend\",i.events.inputEnd).bind(\"touchmove\",i.events.touchMove),t.click(i.events.inputClick).scroll(i.events.scroll).bind(\"selectstart\",i.events.selectStart).bind(\"dragstart\",i.events.dragStart)},e.Kinetic.prototype._detachListeners=function(){var t=this.$el,i=this.settings;e.support.touch&&t.unbind(\"touchstart\",i.events.touchStart).unbind(\"touchend\",i.events.inputEnd).unbind(\"touchmove\",i.events.touchMove),t.unbind(\"click\",i.events.inputClick).unbind(\"scroll\",i.events.scroll).unbind(\"selectstart\",i.events.selectStart).unbind(\"dragstart\",i.events.dragStart)}}(n)},function(e,t){!function(){if(!Array.prototype.fill){var e=function(e){if(null==this)throw new TypeError(\"this is null or not defined\");for(var t=Object(this),i=t.length>>>0,n=arguments[1],o=n>>0,r=o<0?Math.max(i+o,0):Math.min(o,i),a=arguments[2],s=void 0===a?i:a>>0,l=s<0?Math.max(i+s,0):Math.min(s,i);r100)){var t=/^((?:\\d+)?\\.?\\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(e);if(t){var i=parseFloat(t[1]),n=(t[2]||\"ms\").toLowerCase();switch(n){case\"years\":case\"year\":case\"yrs\":case\"yr\":case\"y\":return i*u;case\"days\":case\"day\":case\"d\":return i*c;case\"hours\":case\"hour\":case\"hrs\":case\"hr\":case\"h\":return i*l;case\"minutes\":case\"minute\":case\"mins\":case\"min\":case\"m\":return i*s;case\"seconds\":case\"second\":case\"secs\":case\"sec\":case\"s\":return i*a;case\"milliseconds\":case\"millisecond\":case\"msecs\":case\"msec\":case\"ms\":return i;default:return}}}}function n(e){return e>=c?Math.round(e/c)+\"d\":e>=l?Math.round(e/l)+\"h\":e>=s?Math.round(e/s)+\"m\":e>=a?Math.round(e/a)+\"s\":e+\"ms\"}function o(e){return r(e,c,\"day\")||r(e,l,\"hour\")||r(e,s,\"minute\")||r(e,a,\"second\")||e+\" ms\"}function r(e,t,i){if(!(e0)return i(e);if(\"number\"===r&&isNaN(e)===!1)return t.long?o(e):n(e);throw new Error(\"val is not a non-empty string or a valid number. val=\"+JSON.stringify(e))}},function(e,t){function i(){throw new Error(\"setTimeout has not been defined\")}function n(){throw new Error(\"clearTimeout has not been defined\")}function o(e){if(u===setTimeout)return setTimeout(e,0);if((u===i||!u)&&setTimeout)return u=setTimeout,setTimeout(e,0);try{return u(e,0)}catch(t){try{return u.call(null,e,0)}catch(t){return u.call(this,e,0)}}}function r(e){if(h===clearTimeout)return clearTimeout(e);if((h===n||!h)&&clearTimeout)return h=clearTimeout,clearTimeout(e);try{return h(e)}catch(t){try{return h.call(null,e)}catch(t){return h.call(this,e)}}}function a(){p&&f&&(p=!1,f.length?g=f.concat(g):v=-1,g.length&&s())}function s(){if(!p){var e=o(a);p=!0;for(var t=g.length;t;){for(f=g,g=[];++v1)for(var i=1;i0)n=r;else{if(!(a.length>0)){for(o=0;o=this._rows||t>=this._cols||this._map[e][t]},n.prototype.set=function(e,t,i){this._map[e][t]=i}},function(e,t,i){function n(e){if(this._viewerCore=e,this._viewerState=e.getInternalState(),this._overlays=[],e.getPageTools().length)for(var t=e.getSettings().numPages,i=0;i=0&&(e.oldZoomLevel0){var l={pageX:(n[0].clientX+n[1].clientX)/2,pageY:(n[0].clientY+n[1].clientY)/2};t(e,s(e.currentTarget,l),i,o)}}})}function o(e,t){var i=a(u),n=null;e.on(\"touchend\",function(e){if(e.preventDefault(),i.isTriggered()){i.reset();var o={pageX:e.originalEvent.changedTouches[0].clientX,pageY:e.originalEvent.changedTouches[0].clientY},a=r(n.pageX,n.pageY,o.pageX,o.pageY);a=a.top?n[0]:n[1].region.bottom<=a.bottom?n[1]:o(n,a);var s=this._viewerCore.getSettings().currentPageIndex,l=r.pages.some(function(e){return e.index===s});l||this._viewerCore.setCurrentPage(r.pages[0].index)}},n.prototype.destroy=function(){}},function(e,t,i){\"use strict\";function n(e){e=e||{maxKeys:r},this.maxKeys=e.maxKeys||r,this._held={},this._urls={},this._lru=[]}var o=i(4)(\"diva:ImageCache\");e.exports=n;var r=100;n.prototype.get=function(e){var t=this._urls[e];return t?t.img:null},n.prototype.has=function(e){return!!this._urls[e]},n.prototype.put=function(e,t){var i=this._urls[e];i?(i.img=t,this._promote(i)):(i={img:t,url:e},this._urls[e]=i,this._tryEvict(1),this._lru.unshift(i))},n.prototype._promote=function(e){var t=this._lru.indexOf(e);this._lru.splice(t,1),this._lru.unshift(e)},n.prototype._tryEvict=function(e){var t=this.maxKeys-e;if(!(this._lru.length<=t))for(var i=this._lru.length-1;;){var n=this._lru[i];if(!this._held[n.url]&&(o(\"Evicting image %s\",n.url),this._lru.splice(i,1),delete this._urls[n.url],this._lru.length<=t))break;if(0===i){o.enabled&&o(\"Cache overfull by %s (all entries are being held)\",this._lru.length-t);break}i--}},n.prototype.acquire=function(e){this._held[e]=(this._held[e]||0)+1,this._promote(this._urls[e])},n.prototype.release=function(e){var t=this._held[e];t>1?this._held[e]--:delete this._held[e],this._tryEvict(0)}},function(e,t,i){function n(e,t){this.pages=e.pgs,this.maxZoom=e.max_zoom,this.maxRatio=e.dims.max_ratio,this.minRatio=e.dims.min_ratio,this.itemTitle=e.item_title,this.paged=!!e.paged,this._maxWidths=e.dims.max_w,this._maxHeights=e.dims.max_h,this._averageWidths=e.dims.a_wid,this._averageHeights=e.dims.a_hei,this._totalHeights=e.dims.t_hei,this._totalWidths=e.dims.t_wid,this._urlAdapter=t}function o(e){return function(t){return this[e][t]}}function r(e,t){return 1/Math.pow(2,e-t)}function a(){}function s(e){this._config=e}var l=i(32);e.exports=n,n.fromIIIF=function(e){var t=l(e);return new n(t,new a)},n.fromLegacyManifest=function(e,t){for(var i=0,o=e.pgs.length;i=0&&e1.1?\"default\":\"native\";return encodeURI(o.url+\"full/\"+n+\"/0/\"+r+\".jpg\")},a.prototype.getTileImageURL=function(e,t,i){var n,o,r=e.pages[t];n=i.row===i.rowCount-1?r.d[i.zoomLevel].h-(i.rowCount-1)*i.tileDimensions.height:i.tileDimensions.height,o=i.col===i.colCount-1?r.d[i.zoomLevel].w-(i.colCount-1)*i.tileDimensions.width:i.tileDimensions.width;var a=Math.pow(2,e.maxZoom-i.zoomLevel),s=i.col*i.tileDimensions.width*a,l=i.row*i.tileDimensions.height*a;r.hasOwnProperty(\"xoffset\")&&(s+=r.xoffset,l+=r.yoffset);var c=[s,l,o*a,n*a].join(\",\"),u=r.api>1.1?\"default\":\"native\";return encodeURI(r.url+c+\"/\"+o+\",\"+n+\"/0/\"+u+\".jpg\")},s.prototype.getPageImageURL=function(e,t,i){var n=\"\";i&&(null!=i.width&&(n+=\"&WID=\"+i.width),null!=i.height&&(n+=\"&HEI=\"+i.height));var o=e.pages[t].f;return this._config.iipServerURL+\"?FIF=\"+this._config.imageDir+\"/\"+o+n+\"&CVT=JPEG\"},s.prototype.getTileImageURL=function(e,t,i){var n=e.pages[t],o=i.zoomLevel+n.m-e.maxZoom,r=i.row*i.colCount+i.col,a=o+\",\"+r;return encodeURI(this._config.iipServerURL+\"?FIF=\"+this._config.imageDir+\"/\"+n.f+\"&JTL=\"+a+\"&CVT=JPEG\")}},function(e,t,i){function n(e){this._url=e.url,this._callback=e.load,this._errorCallback=e.error,this.timeoutTime=e.timeoutTime||0,this._aborted=this._complete=!1,this.timeout=setTimeout(function(){this._image=new Image,this._image.crossOrigin=\"anonymous\",this._image.onload=this._handleLoad.bind(this),this._image.onerror=this._handleError.bind(this),this._image.src=e.url,o(\"Requesting image %s\",e.url)}.bind(this),this.timeoutTime)}var o=i(4)(\"diva:ImageRequestHandler\");e.exports=n,n.prototype.abort=function(){o(\"Aborting request to %s\",this._url),clearTimeout(this.timeout),this._image&&(this._image.onload=this._image.onerror=null,this._image.src=\"\"),this._aborted=!0},n.prototype._handleLoad=function(){return this._aborted?void console.error(\"ImageRequestHandler invoked on cancelled request for \"+this._url):this._complete?void console.error(\"ImageRequestHandler invoked on completed request for \"+this._url):(this._complete=!0,o(\"Received image %s\",this._url),void this._callback(this._image))},n.prototype._handleError=function(){o(\"Failed to load image %s\",this._url),this._errorCallback(this._image)}},function(e,t){function i(e){function t(){var e=r(),n=Math.min((e-h)/s,1);i(n),c(g),e0&&h.push({dimensions:g,pages:d}),h}e.exports=i},function(e,t,i){function n(e){if(e.inGrid)return s(o(e,[\"manifest\",\"viewport\",\"pagesPerRow\",\"fixedHeightGrid\",\"fixedPadding\",\"showNonPagedPages\"]));var t=o(e,[\"manifest\",\"verticallyOriented\",\"showNonPagedPages\"]);return e.inBookLayout?r(t):a(t)}function o(e,t){var i={};return t.forEach(function(t){i[t]=e[t]}),i}var r=i(26),a=i(29),s=i(27);e.exports=n},function(e,t,i){var n=i(7);e.exports=function(e){var t=e.manifest,i=[];return t.pages.forEach(function(o,r){if(e.showNonPagedPages||!t.paged||o.paged){var a=n(r,t);i.push({dimensions:a,pages:[{index:r,groupOffset:{top:0,left:0},dimensions:a}]})}}),i}},function(e,t){function i(){this._pages={},this._renderedPages=[],this._renderedPageMap={}}e.exports=i,i.prototype.addOverlay=function(e){var t=this._pages[e.page]||(this._pages[e.page]=[]);t.push(e),this._renderedPageMap[e.page]&&e.mount()},i.prototype.removeOverlay=function(e){var t=e.page,i=this._pages[t];if(i){var n=i.indexOf(e);n!==-1&&(this._renderedPageMap[t]&&i[n].unmount(),i.splice(n,1),0===i.length&&delete this._pages[t])}},i.prototype.updateOverlays=function(e){var t=this._renderedPages,i={};e.forEach(function(e){i[e]=!0,this._renderedPageMap[e]||(this._renderedPageMap[e]=!0,this._invokeOnOverlays(e,function(e){e.mount()}))},this),t.forEach(function(e){i[e]?this._invokeOnOverlays(e,function(e){e.refresh()}):(delete this._renderedPageMap[e],this._invokeOnOverlays(e,function(e){e.unmount()}))},this),this._renderedPages=e},i.prototype._invokeOnOverlays=function(e,t){var i=this._pages[e];i&&i.forEach(t,this)}},function(e,t,i){function n(e,t){this.page=e,this._viewerCore=t,this._innerElement=t.getSettings().innerElement,this._pageToolsElem=null}var o=i(3);e.exports=n,n.prototype.mount=function(){if(null===this._pageToolsElem){var e=this._initializePageToolButtons();this._pageToolsElem=o(\"div\",{class:\"diva-page-tools-wrapper\"},o(\"div\",{class:\"diva-page-tools\"},e))}this.refresh(),this._innerElement.appendChild(this._pageToolsElem)},n.prototype._initializePageToolButtons=function(){var e=this._viewerCore.getSettings(),t=this._viewerCore.getPublicInstance(),i=this.page;return this._viewerCore.getPageTools().map(function(n){var r=n.titleText||n.pluginName[0].toUpperCase()+n.pluginName.substring(1)+\" plugin\",a=o(\"div\",{class:\"diva-\"+n.pluginName+\"-icon\",title:r});return a.addEventListener(\"click\",function(o){n.handleClick.call(this,o,e,t,i)},!1),a.addEventListener(\"touchend\",function(o){o.preventDefault(),n.handleClick.call(this,o,e,t,i)},!1),a},this)},n.prototype.unmount=function(){this._innerElement.removeChild(this._pageToolsElem)},n.prototype.refresh=function(){var e=this._viewerCore.getPageRegion(this.page,{excludePadding:!0,incorporateViewport:!0});this._pageToolsElem.style.top=e.top+\"px\",this._pageToolsElem.style.left=e.left+\"px\"}},function(e,t){function i(e){for(var t,i,s,l,c,u,h,d,f,g,p,v,m,w,y,b,_,P=e.sequences[0],x=P.canvases,I=x.length,L=new Array(x.length),C=100,O=0,D=100,T=0;TAutoscrolling options:
Speed:
Allow manual scroll:
\";t(\"#\"+i.ID+\"page-nav\").before(\"
\"),t(\"body\").prepend(a),t(\"#\"+i.ID+\"autoscroll-pps\").on(\"change\",function(e){n.changeScrollSpeed(Math.pow(10,e.target.value))}),t(\"#\"+i.ID+\"autoscroll-manual\").on(\"change\",function(e){e.target.checked?n.enableManualScroll():n.disableManualScroll()}),t(\"#\"+i.ID+\"autoscroll-toggle\").on(\"click\",n.toggleScrolling),t(\"#\"+i.ID+\"autoscroll-icon\").on(\"click\",function(n){e.jqObj=t(\"#\"+i.ID+\"autoscroll-prefs\"),\"none\"===e.jqObj.css(\"display\")?(e.jqObj.css({display:\"block\"}),f(i.inFullscreen)):e.jqObj.css(\"display\",\"none\")})},i.ID)}},pluginName:\"autoscroll\",titleText:\"Automatically scrolls page along primary axis\"};return i}()}(n)},function(e,t,i){var n=i(1),o=i(2);i(10),function(t){e.exports=function(){var e,i,n,r={},a={},s={},l={brightnessMax:150,brightnessMin:-100,brightnessStep:1,contrastMax:3,contrastMin:-1,contrastStep:.05,localStoragePrefix:\"canvas-\",mobileWebkitMaxZoom:2,rgbMax:50,rgbMin:-50,throbberFadeSpeed:200,throbberTimeout:100,buttons:[\"contrast\",\"brightness\",\"rotation\",\"zoom\"]},c=function(e){return e*Math.PI/180},u=function(e,t){var n=e.x-r.centerX,o=-(e.y-r.centerY),a=c(i.rotation.previous-t),s=Math.cos(a)*n-Math.sin(a)*o+r.centerX,l=-(Math.sin(a)*n+Math.cos(a)*o)+r.centerY;return{x:s,y:l}},h=function(t,i){var n=t.context,o=t.size/2,r=-(t.width/2),a=-(t.height/2);n.clearRect(0,0,t.size,t.size),n.save(),n.translate(o,o),n.rotate(c(i)),n.drawImage(e,r,a,t.width,t.height),n.restore(),t.data=n.getImageData(0,0,t.size,t.size)},d=function(){var e;for(e in i)if(i[e].current!==i[e].previous)return!0;return!1},f=function(){var e;for(e in i)i[e].previous=i[e].current},g=function(){h(a,i.rotation.current),w(a)},p=function(){var e=i.rotation.current,n=i.rotation.previous,o=i.zoom.current,a=i.zoom.previous;if(e!==n||o!==a){var l=t(\"#diva-canvas-wrapper\").scrollLeft(),c=t(\"#diva-canvas-wrapper\").scrollTop(),g=s.viewport.width/2,p=s.viewport.height/2,v=u({x:l+g,y:c+p},e),m=Math.pow(2,o-a),y=m*v.x-g,b=m*v.y-p;h(r,e),t(\"#diva-canvas-wrapper\").scrollLeft(y),t(\"#diva-canvas-wrapper\").scrollTop(b)}d()&&(w(r),f())},v=function(e){var t,i,n=e.data,o=e.context.createImageData(n),r=o.data;for(t=0,i=r.length;t0&&(x&&l&&(l+=b),I&&c&&(c+=_),L&&u&&(u+=P),D&&(l&&(l=l*w+y),c&&(c=c*w+y),u&&(u=u*w+y)),d[a]=l,d[a+1]=c,d[a+2]=u);e.context.clearRect(0,0,o,r),e.context.putImageData(h,0,0)},y=function(){var e=t(\"#diva-canvas-wrapper\").scrollLeft()*a.scaleFactor,i=t(\"#diva-canvas-wrapper\").scrollTop()*a.scaleFactor,n=Math.min(Math.round(s.viewport.height*a.scaleFactor),s.mapSize)-4,o=Math.min(Math.round(s.viewport.width*a.scaleFactor),s.mapSize)-4;t(\"#diva-map-viewbox\").height(n).width(o).css({top:i,left:e})},b=function(e){a.canvas=document.getElementById(\"diva-canvas-minimap\"),a.size=s.mapSize,a.canvas.width=a.size,a.canvas.height=a.size,a.context=a.canvas.getContext(\"2d\"),a.context.fillRect(0,0,a.size,a.size),a.scaleFactor=s.mapSize/r.size,a.cornerX=r.cornerX*a.scaleFactor,a.cornerY=r.cornerY*a.scaleFactor,a.width=e.width*a.scaleFactor,a.height=e.height*a.scaleFactor,a.context.drawImage(e,a.cornerX,a.cornerY,a.width,a.height),a.data=a.context.getImageData(0,0,s.mapSize,s.mapSize),t(\"#diva-map-viewbox\").show(),y()},_=function(i,n){e=new Image,e.crossOrigin=\"anonymous\",e.onload=function(){r.size=Math.sqrt(e.width*e.width+e.height*e.height),r.canvas=document.getElementById(\"diva-canvas\"),r.canvas.width=r.size,r.canvas.height=r.size,r.cornerX=(r.size-e.width)/2,r.cornerY=(r.size-e.height)/2,r.width=e.width,r.height=e.height,r.centerX=r.size/2,r.centerY=r.size/2,r.context=r.canvas.getContext(\"2d\"),r.context.drawImage(e,r.cornerX,r.cornerY,r.width,r.height);try{r.data=r.context.getImageData(0,0,r.size,r.size)}catch(e){var i='

Error

'+e.message+\"

\";if(\"SecurityError\"!==e.name)throw e;i+='

You may need to update your server configuration in order to use the image manipulation tools. For help, see the canvas cross-site data documentation.

',i+=\"\",t(\"#diva-canvas-backdrop\").append(i),C()}void 0===n&&b(e),g(),p(r),C(),\"function\"==typeof n&&n.call(n)},e.src=i,(e.complete||void 0===e.complete)&&(e.src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==\",e.src=i)},P=function(){var e=i[n],o=e.current,r=e.transform?e.transform(o):o;t(\"#diva-canvas-value\").html(r)},x=function(){t(\"#diva-canvas-slider\").val(i[n].current)},I=function(e){var t=s.zoomWidthRatio*Math.pow(2,e);return s.divaInstance.getPageImageURL(s.selectedPageIndex,{width:t})},L=function(){(i.zoom.current>0||s.mobileWebkit)&&t(s.selector+\"throbber\").addClass(\"canvas-throbber\").show()},C=function(){t(s.selector+\"throbber\").removeClass(\"canvas-throbber\").hide()},O=function(){var e,t={},n=!1,o=s.localStoragePrefix+s.filename;for(e in i)i[e].previous!==i[e].initial&&(t[e]=i[e].previous,n=!0);n?(s.pluginIcon.addClass(\"new\"),k(o,t)):(s.pluginIcon.removeClass(\"new\"),localStorage.removeItem(o))},D=function(e,t){s.zoomLevel=e;var i=I(e);_(i,function(){a.scaleFactor=a.size/r.size,y(),O()})},T=function(e){var i=38,n=40,o=37,r=39;switch(e.keyCode){case i:return t(\"#diva-canvas-wrapper\").scrollTop(document.getElementById(\"diva-canvas-wrapper\").scrollTop-s.arrowScrollAmount),!1;case n:return t(\"#diva-canvas-wrapper\").scrollTop(document.getElementById(\"diva-canvas-wrapper\").scrollTop+s.arrowScrollAmount),!1;case o:return t(\"#diva-canvas-wrapper\").scrollLeft(document.getElementById(\"diva-canvas-wrapper\").scrollLeft-s.arrowScrollAmount),!1;case r:return t(\"#diva-canvas-wrapper\").scrollLeft(document.getElementById(\"diva-canvas-wrapper\").scrollLeft+s.arrowScrollAmount),!1}},k=function(e,t){localStorage.setItem(e,JSON.stringify(t))},E=function(e){var t=localStorage.getItem(e);return t&&JSON.parse(t)},M={init:function(e,c){var u=!!window.HTMLCanvasElement;if(!u)return!1;t.extend(s,l,e.canvasPlugin),s.divaInstance=c,s.inCanvas=!1,s.iipServerURL=e.iipServerURL,s.imageDir=e.imageDir,s.selector=e.selector,s.mobileWebkit=e.mobileWebkit,s.arrowScrollAmount=e.arrowScrollAmount,i={contrast:{initial:1,min:s.contrastMin,max:s.contrastMax,step:s.contrastStep,transform:function(e){return e.toFixed(2)},title:\"Change the contrast\"},brightness:{initial:0,min:s.brightnessMin,max:s.brightnessMax,step:s.brightnessStep,title:\"Adjust the brightness\"},rotation:{initial:0,min:0,max:359,step:1,transform:function(e){return e+\"°\"},title:\"Rotate the image\"},zoom:{initial:0,min:0,max:0,step:1,title:\"Adjust the zoom level\"},red:{initial:0,min:s.rgbMin,max:s.rgbMax,step:1,title:\"Adjust the red channel\"},green:{initial:0,min:s.rgbMin,max:s.rgbMax,step:1,title:\"Adjust the green channel\"},blue:{initial:0,min:s.rgbMin,max:s.rgbMax,step:1,title:\"Adjust the blue channel\"}};var h=function(){var e,t,n;for(n in i)t=i[n],e=t.initial,t.current=e,t.previous=e};if(h(),t(\"#diva-canvas-backdrop\").length)return!0;var f,v,m,w,b=[];for(w in s.buttons)v=s.buttons[w],m=i[v].title,f='
',b.push(f);var _=b.join(\"\"),I='
Test
'+_+'

contrast: 0 (Reset)


',k='
',E='
'+I+k+\"
\";t(\"body\").append(E),s.mapSize=t(\"#diva-canvas-minimap\").width(),t(\"#diva-canvas-buttons div\").click(function(){t(\"#diva-canvas-buttons .clicked\").removeClass(\"clicked\"),M(t(this).attr(\"class\"))});var M=function(e){n=e;var o=i[n];t(\"#diva-canvas-buttons .\"+n).addClass(\"clicked\"),t(\"#diva-canvas-mode\").text(n);var r=o.current,a=o.transform?o.transform(r):r,s=document.getElementById(\"diva-canvas-slider\");s.min=o.min,s.max=o.max,s.step=o.step,t(\"#diva-canvas-slider\").val(r),t(\"#diva-canvas-value\").html(a)};return M(\"contrast\"),t(\"#diva-canvas-slider\").on(\"input\",function(e){i[n].current=parseFloat(this.value),P(),g()}),t(\"#diva-canvas-reset-all\").click(function(){var e;for(e in i)i[e].current=i[e].initial;P(),x(),g()}),t(\"#diva-canvas-reset\").click(function(){i[n].current=i[n].initial,P(),x(),g()}),t(\"#diva-canvas-apply\").click(function(){d()&&(L(),setTimeout(function(){i.zoom.current!==i.zoom.previous?D(i.zoom.current):(p(),C(),O())},s.throbberTimeout))}),t(\"#diva-canvas-close\").click(function(){t(\"body\").removeClass(\"overflow-hidden\"),r.context.clearRect(0,0,r.size,r.size),a.context.clearRect(0,0,a.size,a.size),t(\"#diva-canvas-wrapper\").scrollTop(0).scrollLeft(0),t(\"#diva-canvas-backdrop\").hide(),t(\"#diva-map-viewbox\").hide(),C(),c.enableScrollable(),t(document).off(\"keydown\",T),h(),P(),x(),t(\"#diva-canvas-buttons .clicked\").removeClass(\"clicked\"),M(\"contrast\"),o.Events.publish(\"CanvasViewDidHide\")}),t(\"#diva-canvas-minimise\").click(function(){t(\"#diva-canvas-toolwindow\").slideToggle(\"fast\")}),t(window).resize(function(){s.viewport={height:window.innerHeight-e.scrollbarWidth,width:window.innerWidth-e.scrollbarWidth},s.inCanvas&&y()}),t(\"#diva-canvas-wrapper\").scroll(function(){s.inCanvas&&y()}),t(\"#diva-canvas-minimap, #diva-map-viewbox\").mouseup(function(e){var i=t(\"#diva-canvas-minimap\").offset(),n=(e.pageX-i.left)/a.scaleFactor,o=(e.pageY-i.top)/a.scaleFactor;t(\"#diva-canvas-wrapper\").scrollTop(o-s.viewport.height/2),t(\"#diva-canvas-wrapper\").scrollLeft(n-s.viewport.width/2)}),t(\"#diva-canvas\").mousedown(function(){t(this).addClass(\"grabbing\")}).mouseup(function(){t(this).removeClass(\"grabbing\")}),t(\"#diva-canvas-wrapper\").kinetic(),t(\"#diva-canvas-wrapper\").dragscrollable({acceptPropagatedEvent:!0}),o.Events.subscribe(\"ObjectDidLoad\",this.setupHook,e.ID),o.Events.subscribe(\"ViewerDidTerminate\",this.destroy,e.ID),o.Events.subscribe(\"PageDidLoad\",this.onPageLoad,e.ID),!0},pluginName:\"canvas\",titleText:\"View the image on a canvas and adjust various settings\",setupHook:function(e){s.viewport={height:window.innerHeight-e.scrollbarWidth,width:window.innerWidth-e.scrollbarWidth},s.minZoomLevel=e.minZoomLevel,s.maxZoomLevel=e.maxZoomLevel,s.mobileWebkit&&(s.maxZoomLevel=Math.min(s.maxZoomLevel,s.mobileWebkitMaxZoom)),i.zoom.min=s.minZoomLevel,i.zoom.max=s.maxZoomLevel},handleClick:function(e,r,a,l){var c,u=a.getFilenames()[l],h=a.getPageDimensions(l).width-1,d=r.zoomLevel;s.zoomWidthRatio=h/Math.pow(2,d),s.pluginIcon=t(this),s.manifest=r.manifest,s.selectedPageIndex=l,s.mobileWebkit&&(d=Math.min(s.maxZoomLevel,d)),s.filename=u,i.zoom.initial=d,i.zoom.current=d;var f=E(s.localStoragePrefix+s.filename);if(f)for(c in f)i[c].current=f[c],c===n&&(P(),x()),\"zoom\"===c&&(d=f[c]);i.zoom.previous=d,t(\"body\").addClass(\"overflow-hidden\"),t(\"#diva-canvas-backdrop\").show(),a.disableScrollable(),t(document).keydown(T),s.inCanvas=!0;var g=I(d);t(\"#diva-canvas-info\").text(\"Page \"+(l+1)),L(),o.Events.publish(\"CanvasViewDidActivate\",[l]),_(g)},onPageLoad:function(e,i,n){var o=s.localStoragePrefix+i;null!==localStorage.getItem(o)&&t(n).find(\".diva-canvas-icon\").addClass(\"new\")},destroy:function(e,i){t(\"#diva-canvas-backdrop\").remove()}};return M}()}(n)},function(e,t,i){var n=i(1);!function(t){e.exports=function(){var e={},t={init:function(t,i){return e.divaInstance=i,!0},pluginName:\"download\",titleText:\"Download image at the given zoom level\",handleClick:function(t,i,n,o){var r=n.getPageDimensions(o).width-1,a=e.divaInstance.getPageImageURL(o,{width:r});window.open(a)}};return t}()}(n)},function(e,t,i){var n=i(1),o=i(2),r=i(9).HighlightManager;!function(t){e.exports=function(){var e={},i={init:function(i,n){var a=new r(n);i.parentObject.data(\"highlightManager\",a),e.highlightedPages=[],n.resetHighlights=function(){a.clear()},n.removeHighlightsOnPage=function(e){a.removeHighlightsOnPage(e)},n.hideHighlights=function(){e.highlightsVisible=!1,t(i.innerElement).addClass(\"annotations-hidden\")},n.showHighlights=function(){e.highlightsVisible=!0,t(i.innerElement).removeClass(\"annotations-hidden\")},n.highlightOnPages=function(e,t,i,o){for(var r=e.length;r--;)n.highlightOnPage(e[r],t[r],i,o)},n.highlightOnPage=function(e,t,n,o){return void 0===n&&(n=\"rgba(255, 0, 0, 0.2)\"),o=void 0===o?i.ID+\"highlight diva-highlight\":i.ID+\"highlight diva-highlight \"+o,a.addHighlight({page:e,regions:t,colour:n,divClass:o}),!0},n.gotoHighlight=function(e){var t=a.getHighlightByRegionId(e);return t?s(t.highlight.page,t.region):(console.warn(\"Diva just tried to find a highlight that doesn't exist.\"),!1)};var s=function(e,t){var o=parseFloat(t.uly)+parseFloat(t.height)/2,r=parseFloat(t.ulx)+parseFloat(t.width)/2,a=n.translateFromMaxZoomLevel(o),s=n.translateFromMaxZoomLevel(r);e=parseInt(e,10),n.gotoPageByIndex(e);var l=n.getSettings().viewportObject,c=l.scrollTop()+a-l.height()/2+i.verticalPadding,u=l.scrollLeft()+s-l.width()/2+i.horizontalPadding;l.scrollTop(c),l.scrollLeft(u)},l=function(t){return function(i,o,r){for(var a=i,s=i.length,l=[],c=0;c'),t(i.selector+\"annotations-icon\").addClass(\"annotations-icon-active\"),t(\"#\"+i.ID+\"annotations-icon\").on(\"click\",function(o){e.highlightsVisible?(n.hideHighlights(),t(i.selector+\"annotations-icon\").removeClass(\"annotations-icon-active\")):(n.showHighlights(),t(i.selector+\"annotations-icon\").addClass(\"annotations-icon-active\"))})},i.ID),e.highlightsVisible=!0,!0},destroy:function(e,t){e.parentObject.removeData(\"highlights\")},pluginName:\"IIIFHighlight\",titleText:\"Highlight regions of pages\"};return i}()}(n)},function(e,t,i){var n=i(1),o=i(2);!function(t){e.exports=function(){var e={init:function(e,i){var n=function(i){var n=function(e,t){var i=e.charAt(0).toUpperCase()+e.slice(1),n=i.replace(\"_\",\" \");return t.match(/^https?:\\/\\//)&&(t=''+t+\"\"),'\"},o=function(e,t){for(var i=0;i';if(a+=r([\"label\"]),i.hasOwnProperty(\"metadata\"))for(var s=i.metadata,l=0;l\",e.parentObject.prepend(a),t(e.selector+\"metadata\").hide()};return o.Events.subscribe(\"ManifestDidLoad\",n,e.ID),e.parentObject.prepend(''),t(e.selector+\"metadata-link\").on(\"click\",function(i){t(e.selector+\"metadata\").fadeToggle(\"fast\")}),!0},destroy:function(e,t){},pluginName:\"IIIFMetadata\",titleText:\"Show metadata from a IIIF manifest\"};return e}()}(n)},function(e,t,i){var n=i(5);n.registerPlugin(i(33)),n.registerPlugin(i(34)),n.registerPlugin(i(35)),n.registerPlugin(i(9)),n.registerPlugin(i(36)),n.registerPlugin(i(37))},function(e,t,i){\"use strict\";function n(e,t){this._viewport=e.viewport,this._outerElement=e.outerElement,this._documentElement=e.innerElement,this._hooks=t||{},this._canvas=l(\"canvas\",{class:\"diva-viewer-canvas\",tabindex:\"1\"}),this._ctx=this._canvas.getContext(\"2d\"),this.layout=null,this._sourceResolver=null,this._renderedPages=null,this._config=null,this._zoomLevel=null,this._compositeImages=null,this._renderedTiles=null,this._animation=null,this._cache=new h,this._pendingRequests={}}function o(e,t){var i;return i=null===t?1:Math.pow(2,t-e.zoomLevel),{sourceZoomLevel:e.zoomLevel,scaleRatio:i,row:e.row,col:e.col,dimensions:{width:e.dimensions.width*i,height:e.dimensions.height*i},offset:{left:e.offset.left*i,top:e.offset.top*i},url:e.url}}function r(e,t){if(e===t)return{added:[],removed:[]};var i=e.filter(function(e){return t.indexOf(e)===-1}),n=t.filter(function(t){return e.indexOf(t)===-1});return{added:n,removed:i}}var a=i(4)(\"diva:Renderer\"),s=i(4)(\"diva:Renderer:paints\"),l=i(3),c=i(17),u=i(19),h=i(22),d=i(24),f=i(25),g=250;e.exports=n,n.getCompatibilityErrors=function(){return\"undefined\"!=typeof HTMLCanvasElement?null:[\"Your browser lacks support for the \",l(\"pre\",\"canvas\"),\" element. Please upgrade your browser.\"]},n.prototype.getPageHit=function(e,t){var i=this._outerElement.getBoundingClientRect();if(ei.left+i.width||t>i.top+i.height)return null;e-=i.left,t-=i.top;for(var n=this._renderedPages.length,o=0;o=g&&e<=g+v&&t>=p&&t<=p+m){var w=e+d-g,y=t+f-p;return{pg:r,pctx:w/a.dimensions.width,pcty:y/a.dimensions.height,x:w,y:y}}}return null},n.prototype.load=function(e,t,i){if(this._clearAnimation(),this._hooks.onViewWillLoad&&this._hooks.onViewWillLoad(),this._sourceResolver=i,this._config=e,this._compositeImages={},this._setLayoutToZoomLevel(t.zoomLevel),!this.layout.getPageInfo(t.anchorPage))throw new Error(\"invalid page: \"+t.anchorPage);this._canvas.width!==this._viewport.width||this._canvas.height!==this._viewport.height?(a(\"Canvas dimension change: (%s, %s) -> (%s, %s)\",this._canvas.width,this._canvas.height,this._viewport.width,this._viewport.height),this._canvas.width=this._viewport.width,this._canvas.height=this._viewport.height):a(\"Reload, no size change\"),this.goto(t.anchorPage,t.verticalOffset,t.horizontalOffset),this._canvas.parentNode!==this._outerElement&&this._outerElement.insertBefore(this._canvas,this._outerElement.firstChild),this._hooks.onViewDidLoad&&this._hooks.onViewDidLoad()},n.prototype._setViewportPosition=function(e){if(e.zoomLevel!==this._zoomLevel){if(null===this._zoomLevel)throw new TypeError(\"The current view is not zoomable\");if(null===e.zoomLevel)throw new TypeError(\"The current view requires a zoom level\");this._setLayoutToZoomLevel(e.zoomLevel)}this._goto(e.anchorPage,e.verticalOffset,e.horizontalOffset)},n.prototype._setLayoutToZoomLevel=function(e){this.layout=new u(this._config,e),this._zoomLevel=e,l.setAttributes(this._documentElement,{style:{height:this.layout.dimensions.height+\"px\",width:this.layout.dimensions.width+\"px\"}}),this._viewport.setInnerDimensions(this.layout.dimensions)},n.prototype.adjust=function(e){this._clearAnimation(),this._render(e),this._hooks.onViewDidUpdate&&this._hooks.onViewDidUpdate(this._renderedPages.slice(),null)},n.prototype._render=function(e){var t=[];this.layout.pageGroups.forEach(function(e){if(this._viewport.intersectsRegion(e.region)){var i=e.pages.filter(function(e){return this.isPageVisible(e.index)},this).map(function(e){return e.index});t.push.apply(t,i)}},this),this._ctx.clearRect(0,0,this._canvas.width,this._canvas.height),this._paintOutline(t),t.forEach(function(e){if(!this._compositeImages[e]){var t=this.layout.getPageInfo(e),i=this._sourceResolver.getAllZoomLevelsForPage(t),n=new c(i);n.updateFromCache(this._cache),this._compositeImages[e]=n}},this),this._initiateTileRequests(t);var i=r(this._renderedPages||[],t);i.removed.forEach(function(e){delete this._compositeImages[e]},this),this._renderedPages=t,this._paint(),this._hooks.onPageWillLoad&&i.added.forEach(function(e){this._hooks.onPageWillLoad(e)},this)},n.prototype._paint=function(){a(\"Repainting\");var e=[];this._renderedPages.forEach(function(t){this._compositeImages[t].getTiles(this._zoomLevel).forEach(function(i){var n=o(i,this._zoomLevel);this._isTileVisible(t,n)&&(e.push(i.url),this._drawTile(t,n,this._cache.get(i.url)))},this)},this);var t=this._cache,i=r(this._renderedTiles||[],e);i.added.forEach(function(e){t.acquire(e)}),i.removed.forEach(function(e){t.release(e)}),i.removed&&this._renderedPages.forEach(function(e){this._compositeImages[e].updateFromCache(this._cache)},this),this._renderedTiles=e},n.prototype._paintOutline=function(e){e.forEach(function(e){var t=this.layout.getPageInfo(e),i=this._getImageOffset(e),n=Math.max(0,(this._viewport.width-this.layout.dimensions.width)/2),o=Math.max(0,(this._viewport.height-this.layout.dimensions.height)/2),r=i.left-this._viewport.left+n,a=i.top-this._viewport.top+o,s=r<0?-r:0,l=a<0?-a:0,c=Math.max(0,r),u=Math.max(0,a),h=t.dimensions.width-s,d=t.dimensions.height-l;this._ctx.strokeStyle=\"#AAA\",this._ctx.strokeRect(c+.5,u+.5,h,d)},this)},n.prototype._initiateTileRequests=function(e){for(var t={},i=function(e,i){var n=this._compositeImages[i];t[e.url]=new d({url:e.url,timeoutTime:g,load:function(t){delete this._pendingRequests[e.url],this._cache.put(e.url,t),n===this._compositeImages[i]&&(n.updateWithLoadedUrls([e.url]),this._isTileForSourceVisible(i,e)?this._paint():s(\"Page %s, tile %s no longer visible on image load\",i,e.url))}.bind(this),error:function(){delete this._pendingRequests[e.url]}.bind(this)})}.bind(this),n=0;n-1){var c=a(\"div\",{class:\"diva-input-suggestion\"},o[s].l);r.appendChild(c),n++}n>0&&(r.style.display=\"block\")}else r.style.display=\"none\"}),o(i).on(\"keydown\",function(e){var t;if(13===e.keyCode){var n=o(\".active\",r);n.length&&(i.value=n.text())}if(38===e.keyCode){t=o(\".active\",r);var a=t.prev();a.length?(t.removeClass(\"active\"),a.addClass(\"active\")):(t.removeClass(\"active\"),o(\".diva-input-suggestion:last\",r).addClass(\"active\"))}else if(40===e.keyCode){t=o(\".active\",r);var s=t.next();s.length?(t.removeClass(\"active\"),s.addClass(\"active\")):(t.removeClass(\"active\"),o(\".diva-input-suggestion:first\",r).addClass(\"active\"))}}),o(r).on(\"mousedown\",\".diva-input-suggestion\",function(){i.value=this.textContent,r.style.display=\"none\",o(i).trigger(\"submit\")}),o(i).on(\"blur\",function(){r.style.display=\"none\"}),s},v=function(){var i=a(\"span\",{id:t.ID+\"current-page\"}),o=function(){i.textContent=e.getCurrentAliasedPageIndex()};n(\"VisiblePageDidChange\",o),n(\"ViewerDidLoad\",o);var r=a(\"span\",{id:t.ID+\"num-pages\"}),s=function(){r.textContent=t.numPages};return n(\"NumberOfPagesDidChange\",s),n(\"ObjectDidLoad\",s),a(\"span\",{class:\"diva-page-label diva-label\"},\"Page \",i,\" of \",r)},m=function(){var e=[h()];return t.enableLinkIcon&&e.push(w()),t.enableNonPagedVisibilityIcon&&e.push(b()),t.enableFullscreen&&e.push(y()),a(\"span\",i(\"toolbar-button-group\"),e)},w=function(){var i=s(\"link-icon\",\"Link to this page\"),n=o(i);return n.on(\"click\",function(){if(o(\"body\").prepend(a(\"div\",{id:t.ID+\"link-popup\",class:\"diva-popup diva-link-popup\"},[a(\"input\",{id:t.ID+\"link-popup-input\",class:\"diva-input\",type:\"text\",value:e.getCurrentURL()})])),t.inFullscreen)o(t.selector+\"link-popup\").addClass(\"in-fullscreen\");else{var i=n.offset().left-222+n.outerWidth(),r=n.offset().top+n.outerHeight()-1;o(t.selector+\"link-popup\").css({top:r+\"px\",left:i+\"px\"})}return o(\"body\").mouseup(function(e){var i=e.target.id;i!==t.ID+\"link-popup\"&&i!==t.ID+\"link-popup-input\"&&o(t.selector+\"link-popup\").remove()}),t.viewportObject.scroll(function(){o(t.selector+\"link-popup\").remove()}),o(t.selector+\"link-popup input\").click(function(){o(this).focus().select()}),!1}),i},y=function(){return s(\"fullscreen-icon\",\"Toggle fullscreen mode\",function(){e.toggleFullscreenMode()})},b=function(){var i=function(){return\"toggle-nonpaged-icon\"+(e.getSettings().showNonPagedPages?\"-active\":\"\")},o=s(i(),\"Toggle visibility of non-paged pages\",function(){e.toggleNonPagedPagesVisibility();var t=\"diva-\"+i();this.className=this.className.replace(/diva-toggle-nonpaged-icon(-active)?/,t)}),r=function(){for(var e=t.manifest.pages,i=0;i0;for(var r=n.length;r--;)if(n[r]===e[1])return this._cache[i][o].splice(r,1),!0}return!1},i.prototype.unsubscribeAll=function(e){if(e)for(var t,i=Object.keys(this._cache),n=i.length;n--;)t=i[n],\"undefined\"!=typeof this._cache[t][e]&&delete this._cache[t][e];else this._cache={}}},function(e,t){e.exports=function(){var e=document.createElement(\"p\");e.style.width=\"100%\",e.style.height=\"200px\";var t=document.createElement(\"div\");t.style.position=\"absolute\",t.style.top=\"0px\",t.style.left=\"0px\",t.style.visibility=\"hidden\",t.style.width=\"200px\",t.style.height=\"150px\",t.style.overflow=\"hidden\",t.appendChild(e),document.body.appendChild(t);var i=e.offsetWidth;t.style.overflow=\"scroll\";var n=e.offsetWidth;return i==n&&(n=t.clientWidth),document.body.removeChild(t),i-n}},function(e,t){function i(e){var t=window.location.hash;if(\"\"!==t){var i=t.indexOf(\"&\"+e+\"=\")>0?t.indexOf(\"&\"+e+\"=\"):t.indexOf(\"#\"+e+\"=\");if(i>=0){i+=e.length+2;var n=t.indexOf(\"&\",i);return n>i?decodeURIComponent(t.substring(i,n)):n<0?decodeURIComponent(t.substring(i)):\"\"}return!1}return!1}function n(e,t){var n=i(e),o=window.location.hash;if(n!==t)if(\"string\"==typeof n){var r=o.indexOf(\"&\"+e+\"=\")>0?o.indexOf(\"&\"+e+\"=\"):o.indexOf(\"#\"+e+\"=\"),a=r+e.length+2+n.length,s=0===r?\"#\":\"&\";window.location.replace(o.substring(0,r)+s+e+\"=\"+t+o.substring(a))}else 0===o.length?window.location.replace(\"#\"+e+\"=\"+t):window.location.replace(o+\"&\"+e+\"=\"+t)}e.exports.get=i,e.exports.update=n},function(e,t,i){function n(e){this.whitelistedKeys=e.whitelistedKeys||[],this.additionalProperties=e.additionalProperties||[],this.validations=e.validations}function o(e,t,i){var n={proxy:{},index:null},o=a.bind(null,e,t),r={};return i.whitelistedKeys.forEach(function(e){r[e]={get:o.bind(null,e)}}),i.additionalProperties.forEach(function(e){r[e.key]={get:e.get}}),i.validations.forEach(function(e,t){r[e.key]={get:function(){if(t0?Q(\"ViewerDidScrollDown\",r):e<0&&Q(\"ViewerDidScrollUp\",r),N()},q=function(){P.innerObject.mousedown(function(){P.innerObject.addClass(\"diva-grabbing\")}),P.innerObject.mouseup(function(){P.innerObject.removeClass(\"diva-grabbing\")}),G(),P.viewportObject.scroll(Y);var e=38,t=40,i=37,n=39,o=32,a=33,s=34,l=36,u=35;r(document).on(\"keydown.diva\",function(r){if(!P.isActiveDiva)return!0;if(x.enableSpaceScroll&&!r.shiftKey&&r.keyCode===o||x.enableKeyScroll&&r.keyCode===s)return P.viewport.top+=x.panelHeight,!1;if(x.enableSpaceScroll||r.keyCode!==o||r.preventDefault(),x.enableKeyScroll){if(r.shiftKey||r.ctrlKey||r.metaKey)return!0;switch(r.keyCode){case a:return P.viewport.top-=x.panelHeight,!1;case e:return P.viewport.top-=x.arrowScrollAmount,!1;case t:return P.viewport.top+=x.arrowScrollAmount,!1;case i:return P.viewport.left-=x.arrowScrollAmount,!1;case n:return P.viewport.left+=x.arrowScrollAmount,!1;case l:return P.viewport.top=0,!1;case u:return x.verticallyOriented?P.viewport.top=1/0:P.viewport.left=1/0,!1;default:return!0}}return!0}),c.Events.subscribe(\"ViewerDidTerminate\",function(){r(document).off(\"keydown.diva\")},x.ID),W(),window.addEventListener(\"resize\",B,!1),c.Events.subscribe(\"ViewerDidTerminate\",function(){window.removeEventListener(\"resize\",B,!1)},x.ID),\"onorientationchange\"in window&&(window.addEventListener(\"orientationchange\",B,!1),c.Events.subscribe(\"ViewerDidTerminate\",function(){window.removeEventListener(\"orientationchange\",B,!1)},x.ID)),c.Events.subscribe(\"PanelSizeDidChange\",H,x.ID),c.Events.subscribe(\"ViewerDidTerminate\",function(){P.renderer&&P.renderer.destroy(),clearTimeout(P.resizeTimer)},x.ID)},U=function(){f.getAll().forEach(function(e){var t=e.pluginName[0].toUpperCase()+e.pluginName.substring(1);if(x[\"enable\"+t]){var n=e.init(x,i);if(!n)return;\"function\"==typeof e.handleClick&&P.pageTools.push(e),x.plugins.push(e)}})},X=function(){$(),P.throbberTimeoutID=setTimeout(function(){r(x.selector+\"throbber\").show()},x.throbberTimeout)},$=function(){clearTimeout(P.throbberTimeoutID),r(x.selector+\"throbber\").hide()},K=function(e){var t=a(\"div\",C(\"error\"),[a(\"button\",C(\"error-close\",{\"aria-label\":\"Close dialog\"})),a(\"p\",a(\"strong\",\"Error\")),a(\"div\",e)]);P.outerObject.append(t),r(x.selector+\"error-close\").on(\"click\",function(){t.parentNode.removeChild(t)})},J=function(e,t){if(P.manifest=e,$(),P.numPages=x.manifest.pages.length,I.validate(P.options),Q(\"NumberOfPagesDidChange\",x.numPages),x.enableAutoTitle&&(r(x.selector+\"title\").length?r(x.selector+\"title\").html(x.manifest.itemTitle):x.parentObject.prepend(a(\"div\",C(\"title\"),[x.manifest.itemTitle]))),x.adaptivePadding>0){var i=Math.floor((x.minZoomLevel+x.maxZoomLevel)/2);P.horizontalPadding=parseInt(x.manifest.getAverageWidth(i)*x.adaptivePadding,10),P.verticalPadding=parseInt(x.manifest.getAverageHeight(i)*x.adaptivePadding,10)}else P.horizontalPadding=x.fixedPadding,P.verticalPadding=x.fixedPadding;P.pageTools.length&&(P.verticalPadding=Math.max(40,P.verticalPadding)),x.manifest.paged&&(P.options.inBookLayout=!0),Q(\"ObjectDidLoad\",x),H();var n,o,s=!1,l=!1;null==t.goDirectlyTo?(t.goDirectlyTo=x.goDirectlyTo,n=o=!0):(n=null==t.horizontalOffset||isNaN(t.horizontalOffset),o=null==t.verticalOffset||isNaN(t.verticalOffset)),n&&(0===t.goDirectlyTo&&x.inBookLayout&&x.verticallyOriented?t.horizontalOffset=P.horizontalPadding:(l=!0,t.horizontalOffset=Z(t.goDirectlyTo,\"center\"))),o&&(s=!0,t.verticalOffset=V(t.goDirectlyTo,\"top\")),T(t),H(),x.verticallyOriented?P.innerElement.style.minWidth=x.panelWidth+\"px\":P.innerElement.style.minHeight=x.panelHeight+\"px\",(s||l)&&(s&&(P.verticalOffset=V(x.currentPageIndex,\"top\")),l&&(P.horizontalOffset=Z(x.currentPageIndex,\"center\")),P.renderer.goto(x.currentPageIndex,P.verticalOffset,P.horizontalOffset)),P.loaded=!0,Q(\"ViewerDidLoad\",x)},Q=function(e){var t=Array.prototype.slice.call(arguments,1);c.Events.publish(e,t,i)},ee=function(){P.scrollbarWidth=s(),P.mobileWebkit=void 0!==window.orientation;var e=o();P.ID=\"diva-\"+e+\"-\",P.selector=\"#\"+x.ID,null===t.hashParamSuffix&&(1===e?t.hashParamSuffix=\"\":t.hashParamSuffix=e+\"\");var i=a(\"div\",C(\"inner\",{class:\"diva-inner diva-dragger\"})),n=a(\"div\",C(\"viewport\"),i),l=a(\"div\",C(\"outer\"),n,a(\"div\",C(\"throbber\")));P.innerElement=i,P.viewportElement=n,P.outerElement=l,P.innerObject=r(i),P.viewportObject=r(n),P.outerObject=r(l),x.parentObject.append(l),P.viewport=new w(P.viewportElement,{intersectionTolerance:x.viewportMargin}),U(),q(),X()};this.getSettings=function(){return x},this.getInternalState=function(){return P},this.getPublicInstance=function(){return i},this.getPageTools=function(){return P.pageTools},this.getCurrentLayout=function(){return P.renderer?P.renderer.layout:null},this.getViewport=function(){var e=P.viewport;return{top:e.top,left:e.left,bottom:e.bottom,right:e.right,width:e.width,height:e.height}},this.addPageOverlay=function(e){P.pageOverlays.addOverlay(e)},this.removePageOverlay=function(e){P.pageOverlays.removeOverlay(e)},this.getPageRegion=function(e,t){var i=P.renderer.layout,n=i.getPageRegion(e,t);if(t&&t.incorporateViewport){var o=x.verticallyOriented?\"width\":\"height\";if(P.viewport[o]>i.dimensions[o]){var r=(P.viewport[o]-i.dimensions[o])/2;return x.verticallyOriented?{top:n.top,bottom:n.bottom,left:n.left+r,right:n.right+r}:{top:n.top+r,bottom:n.bottom+r,left:n.left,right:n.right}}}return n},this.getPagePositionAtViewportOffset=function(e){for(var t={left:e.left+P.viewport.left,top:e.top+P.viewport.top},i=P.renderer.getRenderedPages(),n=i.length,o=0;o=t.left&&a.top<=t.top&&a.bottom>=t.top)return{anchorPage:r,offset:{left:t.left-a.left,top:t.top-a.top}}}var s=P.renderer.layout.getPageRegion(x.currentPageIndex);return{anchorPage:x.currentPageIndex,offset:{left:t.left-s.left,top:t.top-s.top}}},this.setManifest=function(e,t){J(e,t||{})},this.setCurrentPage=function(e){P.currentPageIndex!==e&&(P.currentPageIndex=e,Q(\"VisiblePageDidChange\",e,this.getPageName(e)))},this.getPageName=function(e){return P.manifest.pages[e].f},this.reload=function(e){T(e)},this.zoom=function(e,t){return F(e,t)},this.enableScrollable=function(){P.isScrollable||(G(),P.options.enableKeyScroll=P.initialKeyScroll,P.options.enableSpaceScroll=P.initialSpaceScroll,P.viewportElement.style.overflow=\"auto\",P.isScrollable=!0)},this.disableScrollable=function(){P.isScrollable&&(P.innerObject.hasClass(\"diva-dragger\")&&P.innerObject.unbind(\"mousedown\"),P.outerObject.unbind(\"dblclick\"),P.outerObject.unbind(\"contextmenu\"),P.viewportElement.style.overflow=\"hidden\",P.initialKeyScroll=x.enableKeyScroll,P.initialSpaceScroll=x.enableSpaceScroll,P.options.enableKeyScroll=!1,P.options.enableSpaceScroll=!1,P.isScrollable=!1)},this.isValidOption=function(e,t){return L(e,t)},this.showError=function(e){$();var t=a(\"div\",C(\"error\"),[a(\"button\",C(\"error-close\",{\"aria-label\":\"Close dialog\"})),a(\"p\",a(\"strong\",\"Error\")),a(\"div\",e)]);P.outerObject.append(t),r(x.selector+\"error-close\").on(\"click\",function(){t.parentNode.removeChild(t)})},this.getXOffset=function(e,t){return Z(e,t)},this.getYOffset=function(e,t){return V(e,t)},this.publish=Q,this.clear=function(){D()},this.setPendingManifestRequest=function(e){P.pendingManifestRequest=e},this.destroy=function(){Q(\"ViewerWillTerminate\",x),x.pendingManifestRequest&&x.pendingManifestRequest.abort(),r(\"body\").removeClass(\"diva-hide-scrollbar\"),x.parentObject.parent().empty().removeData(\"diva\"),x.parentObject.parent().removeAttr(\"style\").removeAttr(\"class\"),Q(\"ViewerDidTerminate\",x),c.Events.unsubscribeAll(x.ID)},ee()}function o(){return o.counter++}var r=i(1);i(10);var a=i(3),s=i(43),l=i(20),c=i(5),u=i(18),h=i(21),d=i(30),f=i(8),g=i(39),p=i(28),v=i(40),m=i(45),w=i(47),y=i(4)(\"diva:ViewerCore\");e.exports=n;var b=[{key:\"goDirectlyTo\",validate:function(e,t){if(e<0||e>=t.manifest.pages.length)return 0}},{key:\"minPagesPerRow\",validate:function(e){return Math.max(2,e)}},{key:\"maxPagesPerRow\",validate:function(e,t){return Math.max(e,t.minPagesPerRow)}},{key:\"pagesPerRow\",validate:function(e,t){if(et.maxPagesPerRow)return t.maxPagesPerRow}},{key:\"maxZoomLevel\",validate:function(e,t,i){if(i.suppressWarning(),e<0||e>t.manifest.maxZoom)return t.manifest.maxZoom}},{key:\"minZoomLevel\",validate:function(e,t,i){return e>t.manifest.maxZoom?(i.suppressWarning(),0):e<0||e>t.maxZoomLevel?0:void 0}},{key:\"zoomLevel\",validate:function(e,t,i){return e>t.manifest.maxZoom?(i.suppressWarning(),0):et.maxZoomLevel?t.minZoomLevel:void 0}}];o.counter=1},function(e,t){function i(e,t){t=t||{},this.intersectionTolerance=t.intersectionTolerance||0,this.maxExtent=t.maxExtent||2e3,this.outer=e,this._top=this._left=this._width=this._height=this._innerDimensions=null,this.invalidate()}function n(e,t){var i=\"_\"+e,n=\"scroll\"+e.charAt(0).toUpperCase()+e.slice(1);return{get:function(){return this[i]},set:function(e){var o;if(this._innerDimensions){var r=this._innerDimensions[t]-this[t];o=a(e,0,r)}else o=s(e,0);\nthis[i]=this.outer[n]=o}}}function o(e){return{get:function(){return this[\"_\"+e]}}}function r(e,t,i){return e>=t&&e<=i}function a(e,t,i){return s(l(e,i),t)}function s(e,t){return Math.max(e,t)}function l(e,t){return Math.min(e,t)}e.exports=i,i.prototype.intersectsRegion=function(e){return this.hasHorizontalOverlap(e)&&this.hasVerticalOverlap(e)},i.prototype.hasVerticalOverlap=function(e){var t=this.top-this.intersectionTolerance,i=this.bottom+this.intersectionTolerance;return r(e.top,t,i)||r(e.bottom,t,i)||e.top<=t&&e.bottom>=i},i.prototype.hasHorizontalOverlap=function(e){var t=this.left-this.intersectionTolerance,i=this.right+this.intersectionTolerance;return r(e.left,t,i)||r(e.right,t,i)||e.left<=t&&e.right>=i},i.prototype.invalidate=function(){this._width=l(this.outer.clientWidth,this.maxExtent),this._height=l(this.outer.clientHeight,this.maxExtent),this._top=this.outer.scrollTop,this._left=this.outer.scrollLeft},i.prototype.setInnerDimensions=function(e){this._innerDimensions=e,e&&(this._top=a(this._top,0,e.height-this._height),this._left=a(this._left,0,e.width-this._width))},Object.defineProperties(i.prototype,{top:n(\"top\",\"height\"),left:n(\"left\",\"width\"),width:o(\"width\"),height:o(\"height\"),bottom:{get:function(){return this._top+this._height}},right:{get:function(){return this._left+this._width}}})}])});\n\n\n// WEBPACK FOOTER //\n// diva.min.js"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","sourceRoot":""} \ No newline at end of file