Mercurial > hg > ismi-richfaces
comparison src/main/webapp/imageServer/resources/js/vendor/diva-old.js @ 203:719475ad0923 iiif_diva
more work on new diva.js in imageServer
author | casties |
---|---|
date | Fri, 05 Jul 2019 16:05:57 +0200 |
parents | src/main/webapp/imageServer/resources/js/diva.js@764f47286679 |
children |
comparison
equal
deleted
inserted
replaced
202:81f761f9c015 | 203:719475ad0923 |
---|---|
1 window.divaPlugins = []; | |
2 | |
3 // this pattern was taken from http://www.virgentech.com/blog/2009/10/building-object-oriented-jquery-plugin.html | |
4 (function ($) | |
5 { | |
6 var Diva = function (element, options) | |
7 { | |
8 // These are elements that can be overridden upon instantiation | |
9 // See https://github.com/DDMAL/diva.js/wiki/Code-documentation for more details | |
10 var defaults = { | |
11 adaptivePadding: 0.05, // The ratio of padding to the page dimension | |
12 blockMobileMove: true, // Prevent moving or scrolling the page on mobile devices | |
13 contained: false, // Determines the location of the fullscreen icon | |
14 objectData: '', // URL to the JSON file that provides the object dimension data - *MANDATORY* | |
15 enableAutoHeight: false, // Automatically adjust height based on the window size | |
16 enableAutoTitle: true, // Shows the title within a div of id diva-title | |
17 enableAutoWidth: true, // Automatically adjust width based on the window size | |
18 enableCanvas: true, // Used for the canvas plugin | |
19 enableDownload: true, // Used for the download plugin | |
20 enableFilename: true, // Uses filenames and not page numbers for links (i=bm_001.tif, not p=1) | |
21 enableFullscreen: true, // Enable or disable fullscreen icon (mode still available) | |
22 enableGotoPage: true, // A "go to page" jump box | |
23 enableGridIcon: true, // A grid view of all the pages | |
24 enableGridSlider: true, // Slider to control the pages per grid row | |
25 enableKeyScroll: true, // Scrolling using the page up/down keys | |
26 enableLinkIcon: true, // Controls the visibility of the link icon | |
27 enableSpaceScroll: false, // Scrolling down by pressing the space key | |
28 enableToolbar: true, // Enables the toolbar. Note that disabling this means you have to handle all controls yourself. | |
29 enableZoomSlider: true, // Enable or disable the zoom slider (for zooming in and out) | |
30 fixedPadding: 10, // Fallback if adaptive padding is set to 0 | |
31 fixedHeightGrid: true, // So each page in grid view has the same height (only widths differ) | |
32 goDirectlyTo: 0, // Default initial page to show (0-indexed) | |
33 iipServerURL: '', // The URL to the IIPImage installation, including the `?FIF=` - *MANDATORY* | |
34 inFullscreen: false, // Set to true to load fullscreen mode initially | |
35 inGrid: false, // Set to true to load grid view initially | |
36 imageDir: '', // Image directory, either absolute path or relative to IIP's FILESYSTEM_PREFIX - *MANDATORY* | |
37 maxPagesPerRow: 8, // Maximum number of pages per row, grid view | |
38 maxZoomLevel: -1, // Optional; defaults to the max zoom returned in the JSON response | |
39 minPagesPerRow: 2, // 2 for the spread view. Recommended to leave it | |
40 minZoomLevel: 0, // Defaults to 0 (the minimum zoom) | |
41 onDocumentLoaded: null, // Callback function for when the document is fully loaded | |
42 onModeToggle: null, // Callback for toggling fullscreen mode | |
43 onViewToggle: null, // Callback for switching between grid and document view | |
44 onJump: null, // Callback function for jumping to a specific page (using the gotoPage feature) | |
45 onPageLoad: null, // Callback function for loading pages | |
46 onPageLoaded: null, // Callback function for after the page has been loaded | |
47 onReady: null, // Callback function for initial load | |
48 onScroll: null, // Callback function for scrolling | |
49 onScrollDown: null, // Callback function for scrolling down, only | |
50 onScrollUp: null, // Callback function for scrolling up only | |
51 onSetCurrentPage: null, // Callback function for when the current page is set | |
52 onZoom: null, // Callback function for zooming in general | |
53 onZoomIn: null, // Callback function for zooming in only | |
54 onZoomOut: null, // Callback function for zooming out only | |
55 pageLoadTimeout: 200, // Number of milliseconds to wait before loading pages | |
56 pagesPerRow: 5, // The default number of pages per row in grid view | |
57 rowLoadTimeout: 50, // Number of milliseconds to wait before loading a row | |
58 throbberTimeout: 100, // Number of milliseconds to wait before showing throbber | |
59 tileHeight: 256, // The height of each tile, in pixels; usually 256 | |
60 tileWidth: 256, // The width of each tile, in pixels; usually 256 | |
61 toolbarParentSelector: null, // The toolbar parent selector. If null, it defaults to the primary diva element. Must be a jQuery selector (leading '#') | |
62 viewerHeightPadding: 15, // Vertical padding when resizing the viewer, if enableAutoHeight is set | |
63 viewerWidthPadding: 30, // Horizontal padding when resizing the viewer, if enableAutoHeight is set | |
64 viewportMargin: 200, // Pretend tiles +/- 200px away from viewport are in | |
65 zoomLevel: 2 // The initial zoom level (used to store the current zoom level) | |
66 }; | |
67 | |
68 // Apply the defaults, or override them with passed-in options. | |
69 var settings = $.extend({}, defaults, options); | |
70 | |
71 // Things that cannot be changed because of the way they are used by the script | |
72 // Many of these are declared with arbitrary values that are changed later on | |
73 var globals = { | |
74 allTilesLoaded: [], // A boolean for each page, indicating if all tiles have been loaded | |
75 averageHeights: [], // The average page height for each zoom level | |
76 averageWidths: [], // The average page width for each zoom level | |
77 currentPageIndex: 0, // The current page in the viewport (center-most page) | |
78 dimAfterZoom: 0, // Used for storing the item dimensions after zooming | |
79 firstPageLoaded: -1, // The ID of the first page loaded (value set later) | |
80 firstRowLoaded: -1, // The index of the first row loaded | |
81 gridPageWidth: 0, // Holds the max width of each row in grid view. Calculated in loadGrid() | |
82 hashParamSuffix: '', // Used when there are multiple document viewers on a page | |
83 heightAbovePages: [], // The height above each page at the current zoom level | |
84 horizontalOffset: 0, // Used in documentScroll for scrolling more precisely | |
85 horizontalPadding: 0, // Either the fixed padding or adaptive padding | |
86 ID: null, // The prefix of the IDs of the elements (usually 1-diva-) | |
87 innerSelector: '', // settings.selector + 'inner', for selecting the .diva-inner element | |
88 itemTitle: '', // The title of the document | |
89 lastPageLoaded: -1, // The ID of the last page loaded (value set later) | |
90 lastRowLoaded: -1, // The index of the last row loaded | |
91 leftScrollSoFar: 0, // Current scroll from the left edge of the pane | |
92 loaded: false, // A flag for when everything is loaded and ready to go. | |
93 maxWidths: [], // The width of the widest page for each zoom level | |
94 maxRatio: 0, // The max height/width ratio (for grid view) | |
95 minHeight: 0, // Minimum height of the .diva-outer element, as defined in the CSS | |
96 minRatio: 0, // The minimum height/width ratio for a page | |
97 minWidth: 0, // Minimum width of the .diva-outer element, as defined in the CSS | |
98 mobileWebkit: false, // Checks if the user is on a touch device (iPad/iPod/iPhone/Android) | |
99 numPages: 0, // Number of pages in the array | |
100 numRows: 0, // Number of rows | |
101 oldPagesPerRow: 0, // Holds the previous number of pages per row after it is changed | |
102 oldZoomLevel: -1, // Holds the previous zoom level after zooming in or out | |
103 orientationChange: false, // For handling device orientation changes for touch devices | |
104 originalHeight: 0, // Stores the original height of the .diva-outer element | |
105 originalWidth: 0, // Stores the original width of the .diva-outer element | |
106 outerSelector: '', // settings.selector + 'outer', for selecting the .diva-outer element | |
107 pages: [], // An array containing the data for all the pages | |
108 pageLeftOffsets: [], // Offset from the left side of the pane to the edge of the page | |
109 pageTimeouts: [], // Stack to hold the loadPage timeouts | |
110 pageTools: '', // The string for page tools | |
111 panelHeight: 0, // Height of the document viewer pane | |
112 panelWidth: 0, // Width of the document viewer pane | |
113 plugins: [], // Filled with the enabled plugins from window.divaPlugins | |
114 previousTopScroll: 0, // Used to determine vertical scroll direction | |
115 preZoomOffset: null, // Holds the offset prior to zooming when double-clicking | |
116 realMaxZoom: -1, // To hold the true max zoom level of the document (needed for calculations) | |
117 resizeTimer: -1, // Holds the ID of the timeout used when resizing the window (for clearing) | |
118 rowHeight: 0, // Holds the max height of each row in grid view. Calculated in loadGrid() | |
119 scaleWait: false, // For preventing double-zoom on touch devices (iPad, etc) | |
120 selector: '', // Uses the generated ID prefix to easily select elements | |
121 singleClick: false, // Used for catching ctrl+double-click events in Firefox in Mac OS | |
122 scrollbarWidth: 0, // Set to the actual scrollbar width in init() | |
123 throbberTimeoutID: -1, // Holds the ID of the throbber loading timeout | |
124 toolbar: null, // Holds an object with some toolbar-related functions | |
125 topScrollSoFar: 0, // Holds the number of pixels of vertical scroll | |
126 totalHeights: [], // The total height of all pages (stacked together) for each zoom level | |
127 totalHeight: 0, // The total height for the current zoom level (including padding) | |
128 verticalOffset: 0, // See horizontalOffset | |
129 verticalPadding: 0, // Either the fixed padding or adaptive padding | |
130 viewerXOffset: 0, // Distance between left edge of viewer and document left edge | |
131 viewerYOffset: 0 // Like viewerXOffset but for the top edges | |
132 }; | |
133 | |
134 $.extend(settings, globals); | |
135 | |
136 // Executes a callback function with the diva instance set as the context | |
137 // Can take an unlimited number to arguments to pass to the callback function | |
138 var self = this; | |
139 | |
140 var executeCallback = function (callback) | |
141 { | |
142 var args, i, length; | |
143 | |
144 if (typeof callback === "function") | |
145 { | |
146 args = []; | |
147 for (i = 1, length = arguments.length; i < length; i++) | |
148 { | |
149 args.push(arguments[i]); | |
150 } | |
151 | |
152 callback.apply(self, args); | |
153 | |
154 return true; | |
155 } | |
156 | |
157 return false; | |
158 }; | |
159 | |
160 var getPageData = function (pageIndex, attribute) | |
161 { | |
162 return settings.pages[pageIndex].d[settings.zoomLevel][attribute]; | |
163 }; | |
164 | |
165 // Returns the page index associated with the given filename; must called after settings settings.pages | |
166 var getPageIndex = function (filename) | |
167 { | |
168 var i, | |
169 np = settings.numPages; | |
170 | |
171 for (i = 0; i < np; i++) | |
172 { | |
173 if (settings.pages[i].f === filename) | |
174 { | |
175 return i; | |
176 } | |
177 } | |
178 | |
179 return -1; | |
180 }; | |
181 | |
182 // Checks if a tile is within the viewport horizontally | |
183 var isHorizontallyInViewport = function (left, right) | |
184 { | |
185 var panelWidth = settings.panelWidth; | |
186 var leftOfViewport = settings.leftScrollSoFar - settings.viewportMargin; | |
187 var rightOfViewport = leftOfViewport + panelWidth + settings.viewportMargin * 2; | |
188 | |
189 var leftVisible = left >= leftOfViewport && left <= rightOfViewport; | |
190 var rightVisible = right >= leftOfViewport && right <= rightOfViewport; | |
191 var middleVisible = left <= leftOfViewport && right >= rightOfViewport; | |
192 | |
193 return (leftVisible || middleVisible || rightVisible); | |
194 }; | |
195 | |
196 // Checks if a page or tile is within the viewport vertically | |
197 var isVerticallyInViewport = function (top, bottom) | |
198 { | |
199 var panelHeight = settings.panelHeight; | |
200 var topOfViewport = settings.topScrollSoFar - settings.viewportMargin; | |
201 var bottomOfViewport = topOfViewport + panelHeight + settings.viewportMargin * 2; | |
202 | |
203 var topVisible = top >= topOfViewport && top <= bottomOfViewport; | |
204 var middleVisible = top <= topOfViewport && bottom >= bottomOfViewport; | |
205 var bottomVisible = bottom >= topOfViewport && bottom <= bottomOfViewport; | |
206 | |
207 return (topVisible || middleVisible || bottomVisible); | |
208 }; | |
209 | |
210 // Check if a tile is near the viewport and thus should be loaded | |
211 var isTileVisible = function (pageIndex, tileRow, tileCol) | |
212 { | |
213 var tileTop = settings.heightAbovePages[pageIndex] + (tileRow * settings.tileHeight) + settings.verticalPadding; | |
214 var tileBottom = tileTop + settings.tileHeight; | |
215 var tileLeft = settings.pageLeftOffsets[pageIndex] + (tileCol * settings.tileWidth); | |
216 var tileRight = tileLeft + settings.tileWidth; | |
217 | |
218 return isVerticallyInViewport(tileTop, tileBottom) && isHorizontallyInViewport(tileLeft, tileRight); | |
219 }; | |
220 | |
221 // Check if a tile has been appended to the DOM | |
222 var isTileLoaded = function (pageIndex, tileIndex) | |
223 { | |
224 return document.getElementById(settings.ID + 'tile-' + pageIndex + '-' + tileIndex) === false; | |
225 }; | |
226 | |
227 // Check if a page index is valid | |
228 var isPageValid = function (pageIndex) | |
229 { | |
230 return pageIndex >= 0 && pageIndex < settings.numPages; | |
231 }; | |
232 | |
233 // Check if a page is in or near the viewport and thus should be loaded | |
234 var isPageVisible = function (pageIndex) | |
235 { | |
236 var topOfPage = settings.heightAbovePages[pageIndex]; | |
237 var bottomOfPage = topOfPage + getPageData(pageIndex, 'h') + settings.verticalPadding; | |
238 | |
239 return isVerticallyInViewport(topOfPage, bottomOfPage); | |
240 }; | |
241 | |
242 // Check if a page has been appended to the DOM | |
243 var isPageLoaded = function (pageIndex) | |
244 { | |
245 return $(document.getElementById(settings.ID + 'page-' + pageIndex)).length > 0; | |
246 }; | |
247 | |
248 // Appends the page directly into the document body, or loads the relevant tiles | |
249 var loadPage = function (pageIndex) | |
250 { | |
251 // If the page and all of its tiles have been loaded, exit | |
252 if (isPageLoaded(pageIndex) && settings.allTilesLoaded[pageIndex]) | |
253 { | |
254 return; | |
255 } | |
256 | |
257 // Load some data for this page | |
258 var filename = settings.pages[pageIndex].f; | |
259 var width = getPageData(pageIndex, 'w'); | |
260 var height = getPageData(pageIndex, 'h'); | |
261 var heightFromTop = settings.heightAbovePages[pageIndex] + settings.verticalPadding; | |
262 var pageSelector = settings.selector + 'page-' + pageIndex; | |
263 var plugin; | |
264 | |
265 // If the page has not been loaded yet, append the div to the DOM | |
266 if (!isPageLoaded(pageIndex)) | |
267 { | |
268 $(document.getElementById(settings.ID + "inner")).append('<div id="' + settings.ID + 'page-' + pageIndex + '" style="top: ' + heightFromTop + 'px; width: ' + width + 'px; height: ' + height + 'px;" class="diva-document-page" title="Page ' + (pageIndex + 1) + '" data-index="' + pageIndex + '" data-filename="' + filename + '">' + settings.pageTools + '</div>'); | |
269 | |
270 // Call the callback function | |
271 executeCallback(settings.onPageLoad, pageIndex, filename, pageSelector); | |
272 Events.publish("PageHasLoaded", [pageIndex, filename, pageSelector]); | |
273 | |
274 // @TODO: Replace this with a notification. | |
275 // Execute the callback functions for any of the enabled plugins | |
276 for (plugin in settings.plugins) { | |
277 executeCallback(settings.plugins[plugin].onPageLoad, pageIndex, filename, pageSelector); | |
278 } | |
279 } | |
280 | |
281 // There are still tiles to load, so try to load those (after a delay) | |
282 settings.pageTimeouts.push(setTimeout(function () | |
283 { | |
284 // If the page is no longer in the viewport, don't load any tiles | |
285 if (!isPageVisible(pageIndex)) | |
286 { | |
287 return; | |
288 } | |
289 | |
290 var imdir = settings.imageDir + "/"; | |
291 // Load some more data and initialise some variables | |
292 var rows = getPageData(pageIndex, 'r'); | |
293 var cols = getPageData(pageIndex, 'c'); | |
294 var maxZoom = settings.pages[pageIndex].m; | |
295 var baseURL = settings.iipServerURL + "?FIF=" + imdir + filename + '&JTL='; | |
296 var content = []; | |
297 var allTilesLoaded = true; | |
298 var tileIndex = 0; | |
299 var i; | |
300 | |
301 // Calculate the width and height of outer tiles (non-standard dimensions) | |
302 var lastHeight = height - (rows - 1) * settings.tileHeight; | |
303 var lastWidth = width - (cols - 1) * settings.tileWidth; | |
304 | |
305 // Declare variables used within the loops | |
306 var row, col, tileHeight, tileWidth, top, left, displayStyle, zoomLevel, imageURL; | |
307 | |
308 // Adjust the zoom level based on the max zoom level of the page | |
309 zoomLevel = settings.zoomLevel + maxZoom - settings.realMaxZoom; | |
310 baseImageURL = baseURL + zoomLevel + ','; | |
311 | |
312 // Loop through all the tiles in this page | |
313 row = 0; | |
314 while (row < rows) | |
315 { | |
316 col = 0; | |
317 while (col < cols) | |
318 { | |
319 top = row * settings.tileHeight; | |
320 left = col * settings.tileWidth; | |
321 | |
322 // If the tile is in the last row or column, its dimensions will be different | |
323 tileHeight = (row === rows - 1) ? lastHeight : settings.tileHeight; | |
324 tileWidth = (col === cols - 1) ? lastWidth : settings.tileWidth; | |
325 | |
326 imageURL = baseImageURL + tileIndex; | |
327 | |
328 // this check looks to see if the tile is already loaded, and then if | |
329 // it isn't, if it should be visible. | |
330 if (!isTileLoaded(pageIndex, tileIndex)) { | |
331 if (isTileVisible(pageIndex, row, col)) { | |
332 content.push('<div id="' + settings.ID + 'tile-' + pageIndex + '-' + tileIndex + '" style="display:inline; position: absolute; top: ' + top + 'px; left: ' + left + 'px; background-image: url(\'' + imageURL + '\'); height: ' + tileHeight + 'px; width: ' + tileWidth + 'px;"></div>'); | |
333 } else { | |
334 // The tile does not need to be loaded - not all have been loaded | |
335 allTilesLoaded = false; | |
336 } | |
337 } | |
338 tileIndex++; | |
339 col++; | |
340 } | |
341 row++; | |
342 } | |
343 | |
344 settings.allTilesLoaded[pageIndex] = allTilesLoaded; | |
345 $(document.getElementById(settings.ID + 'page-' + pageIndex)).append(content.join('')); | |
346 | |
347 executeCallback(settings.onPageLoaded, pageIndex, filename, pageSelector); | |
348 | |
349 }, settings.pageLoadTimeout)); | |
350 }; | |
351 | |
352 // Delete a page from the DOM; will occur when a page is scrolled out of the viewport | |
353 var deletePage = function (pageIndex) | |
354 { | |
355 $(document.getElementById(settings.ID + 'page-' + pageIndex)).empty().remove(); | |
356 }; | |
357 | |
358 // Check if the bottom of a page is above the top of a viewport (scrolling down) | |
359 // For when you want to keep looping but don't want to load a specific page | |
360 var pageAboveViewport = function (pageIndex) | |
361 { | |
362 var bottomOfPage = settings.heightAbovePages[pageIndex] + getPageData(pageIndex, 'h') + settings.verticalPadding; | |
363 var topOfViewport = settings.topScrollSoFar; | |
364 | |
365 return bottomOfPage < topOfViewport; | |
366 }; | |
367 | |
368 // Check if the top of a page is below the bottom of a viewport (scrolling up) | |
369 var pageBelowViewport = function (pageIndex) | |
370 { | |
371 var topOfPage = settings.heightAbovePages[pageIndex]; | |
372 var bottomOfViewport = settings.topScrollSoFar + settings.panelHeight; | |
373 | |
374 return topOfPage > bottomOfViewport; | |
375 }; | |
376 | |
377 // Called by adjust pages - determine what pages should be visible, and show them | |
378 var attemptPageShow = function (pageIndex, direction) | |
379 { | |
380 if (direction > 0) | |
381 { | |
382 // Direction is positive - we're scrolling down | |
383 if (isPageValid(pageIndex)) | |
384 { | |
385 // If the page should be visible, then yes, add it | |
386 if (isPageVisible(pageIndex)) | |
387 { | |
388 loadPage(pageIndex); | |
389 | |
390 settings.lastPageLoaded = pageIndex; | |
391 | |
392 // Recursively call this function until there's nothing to add | |
393 attemptPageShow(settings.lastPageLoaded + 1, direction); | |
394 } | |
395 else if (pageAboveViewport(pageIndex)) | |
396 { | |
397 // If the page is below the viewport. try to load the next one | |
398 attemptPageShow(pageIndex + 1, direction); | |
399 } | |
400 } | |
401 } | |
402 else | |
403 { | |
404 // Direction is negative - we're scrolling up | |
405 if (isPageValid(pageIndex)) | |
406 { | |
407 // If it's near the viewport, yes, add it | |
408 if (isPageVisible(pageIndex)) | |
409 { | |
410 loadPage(pageIndex); | |
411 | |
412 // Reset the first page loaded to this one | |
413 settings.firstPageLoaded = pageIndex; | |
414 | |
415 // Recursively call this function until there's nothing to add | |
416 attemptPageShow(settings.firstPageLoaded - 1, direction); | |
417 } | |
418 else if (pageBelowViewport(pageIndex)) | |
419 { | |
420 // Attempt to call this on the next page, do not increment anything | |
421 attemptPageShow(pageIndex - 1, direction); | |
422 } | |
423 } | |
424 } | |
425 }; | |
426 | |
427 // Called by adjustPages - see what pages need to be hidden, and hide them | |
428 var attemptPageHide = function (pageIndex, direction) | |
429 { | |
430 if (direction > 0) | |
431 { | |
432 // Scrolling down - see if this page needs to be deleted from the DOM | |
433 if (isPageValid(pageIndex) && pageAboveViewport(pageIndex)) | |
434 { | |
435 // Yes, delete it, reset the first page loaded | |
436 deletePage(pageIndex); | |
437 settings.firstPageLoaded = pageIndex + 1; | |
438 | |
439 // Try to call this function recursively until there's nothing to delete | |
440 attemptPageHide(settings.firstPageLoaded, direction); | |
441 } | |
442 } | |
443 else | |
444 { | |
445 // Direction must be negative (not 0 - see adjustPages), we're scrolling up | |
446 if (isPageValid(pageIndex) && pageBelowViewport(pageIndex)) | |
447 { | |
448 // Yes, delete it, reset the last page loaded | |
449 deletePage(pageIndex); | |
450 settings.lastPageLoaded = pageIndex - 1; | |
451 | |
452 // Try to call this function recursively until there's nothing to delete | |
453 attemptPageHide(settings.lastPageLoaded, direction); | |
454 } | |
455 } | |
456 }; | |
457 | |
458 // Handles showing and hiding pages when the user scrolls | |
459 var adjustPages = function (direction) | |
460 { | |
461 var i; | |
462 | |
463 // Direction is negative, so we're scrolling up | |
464 if (direction < 0) | |
465 { | |
466 attemptPageShow(settings.firstPageLoaded, direction); | |
467 setCurrentPage(-1); | |
468 attemptPageHide(settings.lastPageLoaded, direction); | |
469 } | |
470 else if (direction > 0) | |
471 { | |
472 // Direction is positive so we're scrolling down | |
473 attemptPageShow(settings.lastPageLoaded, direction); | |
474 setCurrentPage(1); | |
475 attemptPageHide(settings.firstPageLoaded, direction); | |
476 } | |
477 else | |
478 { | |
479 // Horizontal scroll, check if we need to reveal any tiles | |
480 var lpl = settings.lastPageLoaded; | |
481 for (i = Math.max(settings.firstPageLoaded, 0); i <= lpl; i++) | |
482 { | |
483 if (isPageVisible(i)) | |
484 { | |
485 loadPage(i); | |
486 } | |
487 } | |
488 } | |
489 | |
490 executeCallback(settings.onScroll, settings.topScrollSoFar); | |
491 | |
492 // If we're scrolling down | |
493 if (direction > 0) | |
494 { | |
495 executeCallback(settings.onScrollDown, settings.topScrollSoFar); | |
496 } | |
497 else if (direction < 0) | |
498 { | |
499 // We're scrolling up | |
500 executeCallback(settings.onScrollUp, settings.topScrollSoFar); | |
501 } | |
502 }; | |
503 | |
504 // Check if a row index is valid | |
505 var isRowValid = function (rowIndex) | |
506 { | |
507 return rowIndex >= 0 && rowIndex < settings.numRows; | |
508 }; | |
509 | |
510 // Check if a row should be visible in the viewport | |
511 var isRowVisible = function (rowIndex) | |
512 { | |
513 var topOfRow = settings.rowHeight * rowIndex; | |
514 var bottomOfRow = topOfRow + settings.rowHeight + settings.fixedPadding; | |
515 | |
516 return isVerticallyInViewport(topOfRow, bottomOfRow); | |
517 }; | |
518 | |
519 // Check if a row (in grid view) is present in the DOM | |
520 var isRowLoaded = function (rowIndex) | |
521 { | |
522 return $(settings.selector + 'row-' + rowIndex).length > 0; | |
523 }; | |
524 | |
525 var loadRow = function (rowIndex) | |
526 { | |
527 // If the row has already been loaded, don't attempt to load it again | |
528 if (isRowLoaded(rowIndex)) | |
529 { | |
530 return; | |
531 } | |
532 | |
533 // Load some data for this and initialise some variables | |
534 var heightFromTop = (settings.rowHeight * rowIndex) + settings.fixedPadding; | |
535 var content = []; | |
536 | |
537 // Create the opening tag for the row div | |
538 content.push('<div class="diva-row" id="' + settings.ID + 'row-' + rowIndex + '" style="height: ' + settings.rowHeight + '; top: ' + heightFromTop + 'px;">'); | |
539 | |
540 // Declare variables used in the loop | |
541 var i, pageIndex, filename, realWidth, realHeight, pageWidth, pageHeight, leftOffset, imageURL; | |
542 var imdir = settings.imageDir + "/"; | |
543 | |
544 // Load each page within that row | |
545 var ppr = settings.pagesPerRow; | |
546 for (i = 0; i < ppr; i++) | |
547 { | |
548 pageIndex = rowIndex * settings.pagesPerRow + i; | |
549 | |
550 // If this page is the last row, don't try to load a nonexistent page | |
551 if (!isPageValid(pageIndex)) | |
552 { | |
553 break; | |
554 } | |
555 | |
556 // Calculate the width, height and horizontal placement of this page | |
557 filename = settings.pages[pageIndex].f; | |
558 realWidth = getPageData(pageIndex, 'w'); | |
559 realHeight = getPageData(pageIndex, 'h'); | |
560 pageWidth = (settings.fixedHeightGrid) ? (settings.rowHeight - settings.fixedPadding) * realWidth / realHeight : settings.gridPageWidth; | |
561 pageHeight = (settings.fixedHeightGrid) ? settings.rowHeight - settings.fixedPadding : pageWidth / realWidth * realHeight; | |
562 leftOffset = parseInt(i * (settings.fixedPadding + settings.gridPageWidth) + settings.fixedPadding, 10); | |
563 | |
564 // Make sure they're all integers for nice, round numbers | |
565 pageWidth = parseInt(pageWidth, 10); | |
566 pageHeight = parseInt(pageHeight, 10); | |
567 | |
568 // Center the page if the height is fixed (otherwise, there is no horizontal padding) | |
569 leftOffset += (settings.fixedHeightGrid) ? (settings.gridPageWidth - pageWidth) / 2 : 0; | |
570 imageURL = settings.iipServerURL + "?FIF=" + imdir + filename + '&HEI=' + (pageHeight + 2) + '&CVT=JPEG'; | |
571 | |
572 // Append the HTML for this page to the string builder array | |
573 content.push('<div id="' + settings.ID + 'page-' + pageIndex + '" class="diva-page" style="width: ' + pageWidth + 'px; height: ' + pageHeight + 'px; left: ' + leftOffset + 'px;" title="Page ' + (pageIndex + 1) + '"></div>'); | |
574 | |
575 // Add each image to a queue so that images aren't loaded unnecessarily | |
576 addPageToQueue(rowIndex, pageIndex, imageURL, pageWidth, pageHeight); | |
577 } | |
578 | |
579 // Append this row to the DOM | |
580 content.push('</div>'); | |
581 $(document.getElementById(settings.ID + "inner")).append(content.join('')); | |
582 }; | |
583 | |
584 var deleteRow = function (rowIndex) | |
585 { | |
586 $(document.getElementById(settings.ID + 'row-' + rowIndex)).empty().remove(); | |
587 }; | |
588 | |
589 // Check if the bottom of a row is above the top of the viewport (scrolling down) | |
590 var rowAboveViewport = function (rowIndex) | |
591 { | |
592 var bottomOfRow = settings.rowHeight * (rowIndex + 1); | |
593 var topOfViewport = settings.topScrollSoFar; | |
594 | |
595 return (bottomOfRow < topOfViewport); | |
596 }; | |
597 | |
598 // Check if the top of a row is below the bottom of the viewport (scrolling up) | |
599 var rowBelowViewport = function (rowIndex) | |
600 { | |
601 var topOfRow = settings.rowHeight * rowIndex; | |
602 var bottomOfViewport = settings.topScrollSoFar + settings.panelHeight; | |
603 | |
604 return (topOfRow > bottomOfViewport); | |
605 }; | |
606 | |
607 // Same thing as attemptPageShow only with rows | |
608 var attemptRowShow = function (rowIndex, direction) | |
609 { | |
610 if (direction > 0) | |
611 { | |
612 if (isRowValid(rowIndex)) | |
613 { | |
614 if (isRowVisible(rowIndex)) | |
615 { | |
616 loadRow(rowIndex); | |
617 settings.lastRowLoaded = rowIndex; | |
618 | |
619 attemptRowShow(settings.lastRowLoaded + 1, direction); | |
620 } | |
621 else if (rowAboveViewport(rowIndex)) | |
622 { | |
623 attemptRowShow(rowIndex + 1, direction); | |
624 } | |
625 } | |
626 } | |
627 else | |
628 { | |
629 if (isRowValid(rowIndex)) | |
630 { | |
631 if (isRowVisible(rowIndex)) | |
632 { | |
633 loadRow(rowIndex); | |
634 settings.firstRowLoaded = rowIndex; | |
635 | |
636 attemptRowShow(settings.firstRowLoaded - 1, direction); | |
637 } | |
638 else if (rowBelowViewport(rowIndex)) | |
639 { | |
640 attemptRowShow(rowIndex - 1, direction); | |
641 } | |
642 } | |
643 } | |
644 }; | |
645 | |
646 var attemptRowHide = function (rowIndex, direction) | |
647 { | |
648 if (direction > 0) | |
649 { | |
650 if (isRowValid(rowIndex) && rowAboveViewport(rowIndex)) | |
651 { | |
652 deleteRow(rowIndex); | |
653 settings.firstRowLoaded++; | |
654 | |
655 attemptRowHide(settings.firstRowLoaded, direction); | |
656 } | |
657 } | |
658 else | |
659 { | |
660 if (isRowValid(rowIndex) && rowBelowViewport(rowIndex)) | |
661 { | |
662 deleteRow(rowIndex); | |
663 settings.lastRowLoaded--; | |
664 | |
665 attemptRowHide(settings.lastRowLoaded, direction); | |
666 } | |
667 } | |
668 }; | |
669 | |
670 var adjustRows = function (direction) | |
671 { | |
672 if (direction < 0) | |
673 { | |
674 attemptRowShow(settings.firstRowLoaded, -1); | |
675 setCurrentRow(-1); | |
676 attemptRowHide(settings.lastRowLoaded, -1); | |
677 } | |
678 else if (direction > 0) | |
679 { | |
680 attemptRowShow(settings.lastRowLoaded, 1); | |
681 setCurrentRow(1); | |
682 attemptRowHide(settings.firstRowLoaded, 1); | |
683 } | |
684 | |
685 executeCallback(settings.onScroll, settings.topScrollSoFar); | |
686 | |
687 // If we're scrolling down | |
688 if (direction > 0) | |
689 { | |
690 executeCallback(settings.onScrollDown, settings.topScrollSoFar); | |
691 } | |
692 else if (direction < 0) | |
693 { | |
694 // We're scrolling up | |
695 executeCallback(settings.onScrollUp, settings.topScrollSoFar); | |
696 } | |
697 }; | |
698 | |
699 // Used to delay loading of page images in grid view to prevent unnecessary loads | |
700 var addPageToQueue = function (rowIndex, pageIndex, imageURL, pageWidth, pageHeight) | |
701 { | |
702 settings.pageTimeouts.push(setTimeout(function () | |
703 { | |
704 if (isRowVisible(rowIndex)) | |
705 { | |
706 $(settings.selector + 'page-' + pageIndex).html('<img src="' + imageURL + '" style="width: ' + pageWidth + 'px; height: ' + pageHeight + 'px;" />'); | |
707 } | |
708 }, settings.rowLoadTimeout)); | |
709 }; | |
710 | |
711 // Determines and sets the "current page" (settings.currentPageIndex); called within adjustPages | |
712 // The "direction" is either 1 (downward scroll) or -1 (upward scroll) | |
713 var setCurrentPage = function (direction) | |
714 { | |
715 var middleOfViewport = settings.topScrollSoFar + (settings.panelHeight / 2); | |
716 var currentPage = settings.currentPageIndex; | |
717 var pageToConsider = settings.currentPageIndex + direction; | |
718 var changeCurrentPage = false; | |
719 var pageSelector = settings.selector + 'page-' + pageToConsider; | |
720 | |
721 // When scrolling up: | |
722 if (direction < 0) | |
723 { | |
724 // If the previous page > middle of viewport | |
725 if (pageToConsider >= 0 && (settings.heightAbovePages[pageToConsider] + getPageData(pageToConsider, 'h') + (settings.verticalPadding) >= middleOfViewport)) | |
726 { | |
727 changeCurrentPage = true; | |
728 } | |
729 } | |
730 else if (direction > 0) | |
731 { | |
732 // When scrolling down: | |
733 // If this page < middle of viewport | |
734 if (settings.heightAbovePages[currentPage] + getPageData(currentPage, 'h') + settings.verticalPadding < middleOfViewport) | |
735 { | |
736 changeCurrentPage = true; | |
737 } | |
738 } | |
739 | |
740 if (changeCurrentPage) | |
741 { | |
742 // Set this to the current page | |
743 settings.currentPageIndex = pageToConsider; | |
744 // Now try to change the next page, given that we're not going to a specific page | |
745 // Calls itself recursively - this way we accurately obtain the current page | |
746 if (direction !== 0) | |
747 { | |
748 if (!setCurrentPage(direction)) | |
749 { | |
750 var filename = settings.pages[pageToConsider].f; | |
751 executeCallback(settings.onSetCurrentPage, pageToConsider, filename); | |
752 Events.publish("VisiblePageDidChange", [pageToConsider, filename]); | |
753 } | |
754 } | |
755 return true; | |
756 } | |
757 | |
758 return false; | |
759 }; | |
760 | |
761 // Sets the current page in grid view | |
762 var setCurrentRow = function (direction) | |
763 { | |
764 var currentRow = Math.floor(settings.currentPageIndex / settings.pagesPerRow); | |
765 var rowToConsider = currentRow + parseInt(direction, 10); | |
766 var middleOfViewport = settings.topScrollSoFar + (settings.panelHeight / 2); | |
767 var changeCurrentRow = false; | |
768 | |
769 if (direction < 0) | |
770 { | |
771 if (rowToConsider >= 0 && (settings.rowHeight * currentRow >= middleOfViewport || settings.rowHeight * rowToConsider >= settings.topScrollSoFar)) | |
772 { | |
773 changeCurrentRow = true; | |
774 } | |
775 } | |
776 else if (direction > 0) | |
777 { | |
778 if ((settings.rowHeight * (currentRow + 1)) < settings.topScrollSoFar && isRowValid(rowToConsider)) | |
779 { | |
780 changeCurrentRow = true; | |
781 } | |
782 } | |
783 | |
784 if (changeCurrentRow) | |
785 { | |
786 settings.currentPageIndex = rowToConsider * settings.pagesPerRow; | |
787 | |
788 if (direction !== 0) | |
789 { | |
790 if (!setCurrentRow(direction)) | |
791 { | |
792 var pageIndex = settings.currentPageIndex; | |
793 var filename = settings.pages[pageIndex].f; | |
794 Events.publish("VisiblePageDidChange", [pageIndex, filename]); | |
795 } | |
796 } | |
797 | |
798 return true; | |
799 } | |
800 | |
801 return false; | |
802 }; | |
803 | |
804 // Helper function for going to a particular page | |
805 // Vertical offset: from the top of the page (including the top padding) | |
806 // Horizontal offset: from the center of the page; can be negative if to the left | |
807 var gotoPage = function (pageIndex, verticalOffset, horizontalOffset) | |
808 { | |
809 verticalOffset = (typeof verticalOffset !== 'undefined') ? verticalOffset : 0; | |
810 horizontalOffset = (typeof horizontalOffset !== 'undefined') ? horizontalOffset: 0; | |
811 var desiredTop = settings.heightAbovePages[pageIndex] + verticalOffset; | |
812 var desiredLeft = (settings.maxWidths[settings.zoomLevel] - settings.panelWidth) / 2 + settings.horizontalPadding + horizontalOffset; | |
813 | |
814 $(settings.outerSelector).scrollTop(desiredTop); | |
815 $(settings.outerSelector).scrollLeft(desiredLeft); | |
816 | |
817 // Pretend that this is the current page | |
818 settings.currentPageIndex = pageIndex; | |
819 //settings.toolbar.updateCurrentPage(); | |
820 var filename = settings.pages[pageIndex].f; | |
821 | |
822 Events.publish("VisiblePageDidChange", [pageIndex, filename]); | |
823 executeCallback(settings.onSetCurrentPage, pageIndex, filename); | |
824 | |
825 // Execute the onJump callback | |
826 executeCallback(settings.onJump, pageIndex); | |
827 }; | |
828 | |
829 // Calculates the desired row, then scrolls there | |
830 var gotoRow = function (pageIndex) | |
831 { | |
832 var desiredRow = Math.floor(pageIndex / settings.pagesPerRow); | |
833 var desiredTop = desiredRow * settings.rowHeight; | |
834 $(settings.outerSelector).scrollTop(desiredTop); | |
835 | |
836 // Pretend that this is the current page (it probably isn't) | |
837 settings.currentPageIndex = pageIndex; | |
838 var filename = settings.pages[pageIndex].f; | |
839 Events.publish("VisiblePageDidChange", [pageIndex, filename]); | |
840 }; | |
841 | |
842 // Helper function called by loadDocument to scroll to the desired place | |
843 var documentScroll = function () | |
844 { | |
845 // If settings.preZoomOffset is defined, the zoom was trigged by double-clicking | |
846 // We then zoom in on a specific region | |
847 if (settings.preZoomOffset) | |
848 { | |
849 var clickedPage = settings.preZoomOffset.i; | |
850 var heightAbovePage = settings.heightAbovePages[clickedPage] + settings.verticalPadding; | |
851 var pageLeftOffset = settings.pageLeftOffsets[clickedPage]; | |
852 var zoomRatio = Math.pow(2, settings.zoomLevel - settings.oldZoomLevel); | |
853 | |
854 var distanceFromViewport = { | |
855 x: settings.preZoomOffset.originalX - settings.viewerXOffset, | |
856 y: settings.preZoomOffset.originalY - settings.viewerYOffset | |
857 }; | |
858 | |
859 var newDistanceToEdge = { | |
860 x: settings.preZoomOffset.x * zoomRatio, | |
861 y: settings.preZoomOffset.y * zoomRatio | |
862 }; | |
863 | |
864 var newScroll = { | |
865 x: newDistanceToEdge.x - distanceFromViewport.x + pageLeftOffset, | |
866 y: newDistanceToEdge.y - distanceFromViewport.y + heightAbovePage | |
867 }; | |
868 | |
869 $(settings.outerSelector).scrollTop(newScroll.y).scrollLeft(newScroll.x); | |
870 | |
871 settings.preZoomOffset = undefined; | |
872 } | |
873 else | |
874 { | |
875 // Otherwise, we just scroll to the page saved in settings.goDirectlyTo (must be valid) | |
876 // Make sure the value for settings.goDirectlyTo is valid | |
877 if (!isPageValid(settings.goDirectlyTo)) | |
878 { | |
879 settings.goDirectlyTo = 0; | |
880 } | |
881 | |
882 // We use the stored y/x offsets (relative to the top of the page and the center, respectively) | |
883 gotoPage(settings.goDirectlyTo, settings.verticalOffset, settings.horizontalOffset); | |
884 settings.horizontalOffset = 0; | |
885 settings.verticalOffset = 0; | |
886 } | |
887 }; | |
888 | |
889 // Don't call this when not in grid mode please | |
890 // Scrolls to the relevant place when in grid view | |
891 var gridScroll = function () | |
892 { | |
893 // Figure out and scroll to the row containing the current page | |
894 gotoRow(settings.goDirectlyTo); | |
895 }; | |
896 | |
897 // If the given zoom level is valid, returns it; else, returns the min | |
898 var getValidZoomLevel = function (zoomLevel) | |
899 { | |
900 return (zoomLevel >= settings.minZoomLevel && zoomLevel <= settings.maxZoomLevel) ? zoomLevel : settings.minZoomLevel; | |
901 }; | |
902 | |
903 var getValidPagesPerRow = function (pagesPerRow) | |
904 { | |
905 return (pagesPerRow >= settings.minPagesPerRow && pagesPerRow <= settings.maxPagesPerRow) ? pagesPerRow : settings.maxPagesPerRow; | |
906 }; | |
907 | |
908 // Reset some settings and empty the viewport | |
909 var clearViewer = function () | |
910 { | |
911 settings.allTilesLoaded = []; | |
912 $(settings.outerSelector).scrollTop(0); | |
913 settings.topScrollSoFar = 0; | |
914 $(settings.innerSelector).empty(); | |
915 settings.firstPageLoaded = 0; | |
916 settings.firstRowLoaded = -1; | |
917 settings.previousTopScroll = 0; | |
918 | |
919 // Clear all the timeouts to prevent undesired pages from loading | |
920 clearTimeout(settings.resizeTimer); | |
921 | |
922 while (settings.pageTimeouts.length) | |
923 { | |
924 clearTimeout(settings.pageTimeouts.pop()); | |
925 } | |
926 }; | |
927 | |
928 // Called when we don't necessarily know which view to go into | |
929 var loadViewer = function () | |
930 { | |
931 if (settings.inGrid) | |
932 { | |
933 loadGrid(); | |
934 } | |
935 else | |
936 { | |
937 loadDocument(); | |
938 } | |
939 }; | |
940 | |
941 // Called every time we need to load document view (after zooming, fullscreen, etc) | |
942 var loadDocument = function () | |
943 { | |
944 clearViewer(); | |
945 | |
946 // Make sure the zoom level we've been given is valid | |
947 settings.zoomLevel = getValidZoomLevel(settings.zoomLevel); | |
948 var z = settings.zoomLevel; | |
949 | |
950 // Calculate the horizontal and vertical inter-page padding | |
951 if (settings.adaptivePadding > 0) | |
952 { | |
953 settings.horizontalPadding = settings.averageWidths[z] * settings.adaptivePadding; | |
954 settings.verticalPadding = settings.averageHeights[z] * settings.adaptivePadding; | |
955 } | |
956 else | |
957 { | |
958 // It's less than or equal to 0; use fixedPadding instead | |
959 settings.horizontalPadding = settings.fixedPadding; | |
960 settings.verticalPadding = settings.fixedPadding; | |
961 } | |
962 | |
963 // Make sure the vertical padding is at least 40, if plugin icons are enabled | |
964 if (settings.pageTools.length) | |
965 { | |
966 settings.verticalPadding = Math.max(40, settings.horizontalPadding); | |
967 } | |
968 | |
969 // Now reset some things that need to be changed after each zoom | |
970 settings.totalHeight = settings.totalHeights[z] + settings.verticalPadding * (settings.numPages + 1); | |
971 settings.dimAfterZoom = settings.totalHeight; | |
972 | |
973 // Determine the width of the inner element (based on the max width) | |
974 var maxWidthToSet = settings.maxWidths[z] + settings.horizontalPadding * 2; | |
975 var widthToSet = Math.max(maxWidthToSet, settings.panelWidth); | |
976 | |
977 // Needed to set settings.heightAbovePages - initially just the top padding | |
978 var heightSoFar = 0; | |
979 var i; | |
980 | |
981 for (i = 0; i < settings.numPages; i++) | |
982 { | |
983 // First set the height above that page by adding this height to the previous total | |
984 // A page includes the padding above it | |
985 settings.heightAbovePages[i] = heightSoFar; | |
986 | |
987 // Has to be done this way otherwise you get the height of the page included too | |
988 heightSoFar = settings.heightAbovePages[i] + getPageData(i, 'h') + settings.verticalPadding; | |
989 | |
990 // Figure out the pageLeftOffset stuff | |
991 settings.pageLeftOffsets[i] = (widthToSet - getPageData(i, 'w')) / 2; | |
992 | |
993 // Now try to load the page ONLY if the page needs to be loaded | |
994 // Take scrolling into account later, just try this for now | |
995 if (isPageVisible(i)) | |
996 { | |
997 loadPage(i); | |
998 settings.lastPageLoaded = i; | |
999 } | |
1000 } | |
1001 | |
1002 // If this is not the initial load, execute the zoom callbacks | |
1003 if (settings.oldZoomLevel >= 0) | |
1004 { | |
1005 if (settings.oldZoomLevel < settings.zoomLevel) | |
1006 { | |
1007 executeCallback(settings.onZoomIn, z); | |
1008 } | |
1009 else | |
1010 { | |
1011 executeCallback(settings.onZoomOut, z); | |
1012 } | |
1013 | |
1014 executeCallback(settings.onZoom, z); | |
1015 } | |
1016 | |
1017 // Set the height and width of documentpane (necessary for dragscrollable) | |
1018 $(settings.innerSelector).height(Math.round(settings.totalHeight)); | |
1019 $(settings.innerSelector).width(Math.round(widthToSet)); | |
1020 | |
1021 // Scroll to the proper place | |
1022 documentScroll(); | |
1023 | |
1024 // For the iPad - wait until this request finishes before accepting others | |
1025 if (settings.scaleWait) | |
1026 { | |
1027 settings.scaleWait = false; | |
1028 } | |
1029 | |
1030 var fileName = settings.pages[settings.currentPageIndex].f; | |
1031 executeCallback(settings.onDocumentLoaded, settings.lastPageLoaded, fileName); | |
1032 Events.publish("DocumentHasFinishedLoading", [settings.lastPageLoaded, fileName]); | |
1033 }; | |
1034 | |
1035 var loadGrid = function () | |
1036 { | |
1037 clearViewer(); | |
1038 | |
1039 // Make sure the pages per row setting is valid | |
1040 settings.pagesPerRow = getValidPagesPerRow(settings.pagesPerRow); | |
1041 | |
1042 var horizontalPadding = settings.fixedPadding * (settings.pagesPerRow + 1); | |
1043 var pageWidth = (settings.panelWidth - horizontalPadding) / settings.pagesPerRow; | |
1044 settings.gridPageWidth = pageWidth; | |
1045 | |
1046 // Calculate the row height depending on whether we want to fix the width or the height | |
1047 settings.rowHeight = (settings.fixedHeightGrid) ? settings.fixedPadding + settings.minRatio * pageWidth : settings.fixedPadding + settings.maxRatio * pageWidth; | |
1048 settings.numRows = Math.ceil(settings.numPages / settings.pagesPerRow); | |
1049 settings.totalHeight = settings.numRows * settings.rowHeight + settings.fixedPadding; | |
1050 | |
1051 $(settings.innerSelector).height(Math.round(settings.totalHeight)); | |
1052 $(settings.innerSelector).width(Math.round(settings.panelWidth)); | |
1053 | |
1054 // First scroll directly to the row containing the current page | |
1055 gridScroll(); | |
1056 | |
1057 var i, rowIndex; | |
1058 | |
1059 // Figure out the row each page is in | |
1060 var np = settings.numPages; | |
1061 for (i = 0; i < np; i += settings.pagesPerRow) | |
1062 { | |
1063 rowIndex = Math.floor(i / settings.pagesPerRow); | |
1064 | |
1065 if (isRowVisible(rowIndex)) | |
1066 { | |
1067 settings.firstRowLoaded = (settings.firstRowLoaded < 0) ? rowIndex : settings.firstRowLoaded; | |
1068 loadRow(rowIndex); | |
1069 settings.lastRowLoaded = rowIndex; | |
1070 } | |
1071 } | |
1072 }; | |
1073 | |
1074 // Handles switching in and out of fullscreen mode | |
1075 // Should only be called after changing settings.inFullscreen | |
1076 var handleModeChange = function (changeView) | |
1077 { | |
1078 // Save some offsets (required for scrolling properly), if it's not the initial load | |
1079 if (settings.oldZoomLevel >= 0) | |
1080 { | |
1081 if (!settings.inGrid) | |
1082 { | |
1083 var pageOffset = $(settings.selector + 'page-' + settings.currentPageIndex).offset(); | |
1084 var topOffset = -(pageOffset.top - settings.verticalPadding - settings.viewerYOffset); | |
1085 var expectedLeft = (settings.panelWidth - getPageData(settings.currentPageIndex, 'w')) / 2; | |
1086 var leftOffset = -(pageOffset.left - settings.viewerXOffset - expectedLeft); | |
1087 settings.verticalOffset = topOffset; | |
1088 settings.horizontalOffset = leftOffset; | |
1089 } | |
1090 } | |
1091 | |
1092 // Change the look of the toolbar | |
1093 Events.publish("ModeDidSwitch", null); | |
1094 | |
1095 // Toggle the classes | |
1096 $(settings.selector + 'fullscreen').toggleClass('diva-in-fullscreen'); | |
1097 $(settings.outerSelector).toggleClass('diva-fullscreen'); | |
1098 $('body').toggleClass('diva-hide-scrollbar'); | |
1099 $(settings.parentSelector).toggleClass('diva-full-width'); | |
1100 | |
1101 // Reset the panel dimensions | |
1102 settings.panelHeight = $(settings.outerSelector).height(); | |
1103 settings.panelWidth = $(settings.outerSelector).width() - settings.scrollbarWidth; | |
1104 $(settings.innerSelector).width(settings.panelWidth); | |
1105 | |
1106 // Recalculate the viewer offsets | |
1107 settings.viewerXOffset = $(settings.outerSelector).offset().left; | |
1108 settings.viewerYOffset = $(settings.outerSelector).offset().top; | |
1109 | |
1110 // Used by setState when we need to change the view and the mode | |
1111 if (changeView) | |
1112 { | |
1113 settings.inGrid = !settings.inGrid; | |
1114 handleViewChange(); | |
1115 } | |
1116 else | |
1117 { | |
1118 loadViewer(); | |
1119 } | |
1120 | |
1121 // Execute callbacks | |
1122 executeCallback(settings.onModeToggle, settings.inFullscreen); | |
1123 Events.publish("ModeHasChanged", [settings.inFullScreen]); | |
1124 }; | |
1125 | |
1126 // Handles switching in and out of grid view | |
1127 // Should only be called after changing settings.inGrid | |
1128 var handleViewChange = function () | |
1129 { | |
1130 // Switch the slider | |
1131 // Events.publish("ViewDidSwitch", null); | |
1132 | |
1133 loadViewer(); | |
1134 executeCallback(settings.onViewToggle, settings.inGrid); | |
1135 Events.publish("ViewDidSwitch", [settings.inGrid]); | |
1136 }; | |
1137 | |
1138 // Called when the fullscreen icon is clicked | |
1139 var toggleFullscreen = function () | |
1140 { | |
1141 settings.goDirectlyTo = settings.currentPageIndex; | |
1142 settings.inFullscreen = !settings.inFullscreen; | |
1143 handleModeChange(false); | |
1144 }; | |
1145 | |
1146 // Called when the grid icon is clicked | |
1147 var toggleGrid = function () | |
1148 { | |
1149 settings.goDirectlyTo = settings.currentPageIndex; | |
1150 settings.inGrid = !settings.inGrid; | |
1151 handleViewChange(); | |
1152 }; | |
1153 | |
1154 // Called after double-click or ctrl+double-click events on pages in document view | |
1155 var handleDocumentDoubleClick = function (event) | |
1156 { | |
1157 var pageOffset = $(this).offset(); | |
1158 var offsetX = event.pageX - pageOffset.left; | |
1159 var offsetY = event.pageY - pageOffset.top; | |
1160 | |
1161 // Store the offset information so that it can be used in documentScroll() | |
1162 settings.preZoomOffset = { | |
1163 x: offsetX, | |
1164 y: offsetY, | |
1165 originalX: event.pageX, | |
1166 originalY: event.pageY, | |
1167 i: $(this).attr('data-index') | |
1168 }; | |
1169 | |
1170 // Hold control to zoom out, otherwise, zoom in | |
1171 var newZoomLevel = (event.ctrlKey) ? settings.zoomLevel - 1 : settings.zoomLevel + 1; | |
1172 | |
1173 handleZoom(newZoomLevel); | |
1174 }; | |
1175 | |
1176 // Called after double-clicking on a page in grid view | |
1177 var handleGridDoubleClick = function (event) | |
1178 { | |
1179 // Figure out the page that was clicked, scroll to that page | |
1180 var sel = document.getElementById(settings.ID + "outer"); | |
1181 var centerX = (event.pageX - settings.viewerXOffset) + sel.scrollLeft; | |
1182 var centerY = (event.pageY - settings.viewerYOffset) + sel.scrollTop; | |
1183 var rowIndex = Math.floor(centerY / settings.rowHeight); | |
1184 var colIndex = Math.floor(centerX / (settings.panelWidth / settings.pagesPerRow)); | |
1185 var pageIndex = rowIndex * settings.pagesPerRow + colIndex; | |
1186 settings.goDirectlyTo = pageIndex; | |
1187 | |
1188 // Leave grid view, jump directly to the desired page | |
1189 settings.inGrid = false; | |
1190 handleViewChange(); | |
1191 }; | |
1192 | |
1193 // Handles pinch-zooming for mobile devices | |
1194 var handlePinchZoom = function (event) | |
1195 { | |
1196 var newZoomLevel = settings.zoomLevel; | |
1197 | |
1198 // First figure out the new zoom level: | |
1199 if (event.scale > 1 && newZoomLevel < settings.maxZoomLevel) | |
1200 { | |
1201 newZoomLevel++; | |
1202 } | |
1203 else if (event.scale < 1 && newZoomLevel > settings.minZoomLevel) | |
1204 { | |
1205 newZoomLevel--; | |
1206 } | |
1207 else | |
1208 { | |
1209 return; | |
1210 } | |
1211 | |
1212 // Set it to true so we have to wait for this one to finish | |
1213 settings.scaleWait = true; | |
1214 | |
1215 // Has to call handleZoomSlide so that the coordinates are kept | |
1216 handleZoom(newZoomLevel); | |
1217 }; | |
1218 | |
1219 // Called to handle any zoom level | |
1220 var handleZoom = function (newValue) | |
1221 { | |
1222 var newZoomLevel = getValidZoomLevel(newValue); | |
1223 | |
1224 // If the zoom level provided is invalid, return false | |
1225 if (newZoomLevel !== newValue) | |
1226 { | |
1227 return false; | |
1228 } | |
1229 | |
1230 settings.oldZoomLevel = settings.zoomLevel; | |
1231 settings.zoomLevel = newZoomLevel; | |
1232 | |
1233 // Update the slider | |
1234 Events.publish("ZoomLevelDidChange", null); | |
1235 | |
1236 loadDocument(); | |
1237 | |
1238 return true; | |
1239 }; | |
1240 | |
1241 // Called to handle changing the pages per row slider | |
1242 var handleGrid = function (newValue) | |
1243 { | |
1244 var newPagesPerRow = getValidPagesPerRow(newValue); | |
1245 | |
1246 // If the value provided is invalid, return false | |
1247 if (newPagesPerRow !== newValue) | |
1248 { | |
1249 return false; | |
1250 } | |
1251 | |
1252 settings.oldPagesPerRow = settings.zoomLevel; | |
1253 settings.pagesPerRow = newPagesPerRow; | |
1254 | |
1255 // Update the slider | |
1256 Events.publish("GridRowNumberDidChange", null); | |
1257 | |
1258 loadGrid(); | |
1259 }; | |
1260 | |
1261 var getYOffset = function () | |
1262 { | |
1263 var yScroll = document.getElementById(settings.ID + "outer").scrollTop; | |
1264 var topOfPage = settings.heightAbovePages[settings.currentPageIndex]; | |
1265 | |
1266 return parseInt(yScroll - topOfPage, 10); | |
1267 }; | |
1268 | |
1269 var getXOffset = function () | |
1270 { | |
1271 var innerWidth = settings.maxWidths[settings.zoomLevel] + settings.horizontalPadding * 2; | |
1272 var centerX = (innerWidth - settings.panelWidth) / 2; | |
1273 var xoff = document.getElementById(settings.ID + "outer").scrollLeft - centerX; | |
1274 return parseInt(xoff, 10); | |
1275 }; | |
1276 | |
1277 var getState = function () | |
1278 { | |
1279 var state = { | |
1280 'f': settings.inFullscreen, | |
1281 'g': settings.inGrid, | |
1282 'z': settings.zoomLevel, | |
1283 'n': settings.pagesPerRow, | |
1284 'i': (settings.enableFilename) ? settings.pages[settings.currentPageIndex].f : false, | |
1285 'p': (settings.enableFilename) ? false : settings.currentPageIndex + 1, | |
1286 'y': (settings.inGrid) ? false : getYOffset(), | |
1287 'x': (settings.inGrid) ? false : getXOffset(), | |
1288 'h': (settings.inFullscreen) ? false : settings.panelHeight, | |
1289 'w': (settings.inFullscreen) ? false : $(settings.outerSelector).width() | |
1290 }; | |
1291 | |
1292 return state; | |
1293 }; | |
1294 | |
1295 var getURLHash = function () | |
1296 { | |
1297 var hashParams = getState(); | |
1298 var hashStringBuilder = []; | |
1299 var param; | |
1300 | |
1301 for (param in hashParams) | |
1302 { | |
1303 if (hashParams[param] !== false) | |
1304 { | |
1305 hashStringBuilder.push(param + settings.hashParamSuffix + '=' + hashParams[param]); | |
1306 } | |
1307 } | |
1308 | |
1309 return hashStringBuilder.join('&'); | |
1310 }; | |
1311 | |
1312 // Returns the URL to the current state of the document viewer (so it should be an exact replica) | |
1313 var getCurrentURL = function () | |
1314 { | |
1315 return location.protocol + '//' + location.host + location.pathname + '#' + getURLHash(); | |
1316 }; | |
1317 | |
1318 // Called in init and when the orientation changes | |
1319 var adjustMobileWebkitDims = function () | |
1320 { | |
1321 var outerOffset = $(settings.outerSelector).offset().top; | |
1322 settings.panelHeight = window.innerHeight - outerOffset - settings.viewerHeightPadding; | |
1323 settings.panelWidth = window.innerWidth - settings.viewerWidthPadding; | |
1324 | |
1325 // $(settings.parentSelector).width(settings.panelWidth); | |
1326 // document.getElementById(settings.parentSelector.substring(1)).style.width = settings.panelWidth + "px"; | |
1327 settings.parentSelector.style.width = settings.panelWidth + "px"; | |
1328 | |
1329 if (settings.enableAutoHeight) | |
1330 { | |
1331 document.getElementById(settings.ID + "outer").style.height = settings.panelHeight + "px"; | |
1332 } | |
1333 | |
1334 if (settings.enableAutoWidth) | |
1335 { | |
1336 document.getElementById(settings.ID + "outer").style.width = settings.panelWidth + "px"; | |
1337 } | |
1338 }; | |
1339 | |
1340 // Will return true if something has changed, false otherwise | |
1341 var adjustBrowserDims = function () | |
1342 { | |
1343 // Only resize if the browser viewport is too small | |
1344 var newHeight = $(settings.outerSelector).height(); | |
1345 var newWidth = $(settings.parentSelector).width() - settings.scrollbarWidth; | |
1346 var outerOffset = $(settings.outerSelector).offset().top; | |
1347 | |
1348 var windowHeight = window.innerHeight || document.documentElement.clientHeight; | |
1349 var windowWidth = window.innerWidth || document.documentElement.clientWidth; | |
1350 // 2 or 1 pixels for the border | |
1351 var desiredWidth = windowWidth - settings.viewerWidthPadding - settings.scrollbarWidth - 2; | |
1352 var desiredHeight = windowHeight - outerOffset - settings.viewerHeightPadding - 1; | |
1353 | |
1354 if (settings.enableAutoHeight) | |
1355 { | |
1356 if (newHeight + outerOffset + 16 > window.innerHeight) | |
1357 { | |
1358 newHeight = desiredHeight; | |
1359 } | |
1360 else if (newHeight <= settings.originalHeight) | |
1361 { | |
1362 newHeight = Math.min(desiredHeight, settings.originalHeight); | |
1363 } | |
1364 } | |
1365 | |
1366 if (settings.enableAutoWidth) | |
1367 { | |
1368 if (newWidth + 32 > window.innerWidth) | |
1369 { | |
1370 newWidth = desiredWidth; | |
1371 } | |
1372 else if (newWidth <= settings.originalWidth) | |
1373 { | |
1374 newWidth = Math.min(desiredWidth, settings.originalWidth); | |
1375 } | |
1376 | |
1377 settings.parentSelector[0].style.width = newWidth + settings.scrollbarWidth; | |
1378 } | |
1379 | |
1380 if (newWidth !== settings.panelWidth || newHeight !== settings.panelHeight) | |
1381 { | |
1382 var el = document.getElementById(settings.ID + "outer"); | |
1383 el.style.height = newHeight + "px"; | |
1384 el.style.width = newWidth + settings.scrollbarWidth + "px"; | |
1385 settings.panelWidth = newWidth; | |
1386 settings.panelHeight = newHeight; | |
1387 return true; | |
1388 } | |
1389 | |
1390 return false; | |
1391 }; | |
1392 | |
1393 // Update the panelHeight and panelWidth based on the window size | |
1394 var adjustFullscreenDims = function () | |
1395 { | |
1396 settings.panelWidth = window.innerWidth - settings.scrollbarWidth; | |
1397 settings.panelHeight = window.innerHeight; | |
1398 | |
1399 return true; | |
1400 }; | |
1401 | |
1402 var resizeViewer = function (newWidth, newHeight) | |
1403 { | |
1404 if (newWidth >= settings.minWidth) | |
1405 { | |
1406 settings.originalWidth = newWidth; | |
1407 $(settings.outerSelector).width(newWidth); | |
1408 document.getElementById(settings.ID + "outer").style.width = newWidth + "px"; | |
1409 | |
1410 settings.panelWidth = newWidth - settings.scrollbarWidth; | |
1411 | |
1412 // Should also change the width of the container | |
1413 settings.parentSelector[0].style.width = newWidth + "px"; | |
1414 } | |
1415 | |
1416 if (newHeight >= settings.minHeight) | |
1417 { | |
1418 settings.originalHeight = newHeight; | |
1419 document.getElementById(settings.ID + "outer").style.height = newHeight + "px"; | |
1420 | |
1421 settings.panelHeight = newHeight; | |
1422 } | |
1423 }; | |
1424 | |
1425 // Binds most of the event handlers (some more in createToolbar) | |
1426 var handleEvents = function () | |
1427 { | |
1428 // Create the fullscreen toggle icon if fullscreen is enabled | |
1429 if (settings.enableFullscreen) | |
1430 { | |
1431 // Event handler for fullscreen toggling | |
1432 $(settings.selector + 'fullscreen').click(function () | |
1433 { | |
1434 toggleFullscreen(); | |
1435 }); | |
1436 } | |
1437 | |
1438 // Change the cursor for dragging | |
1439 $(settings.innerSelector).mouseover(function () | |
1440 { | |
1441 $(this).removeClass('diva-grabbing').addClass('diva-grab'); | |
1442 }); | |
1443 | |
1444 $(settings.innerSelector).mouseout(function () | |
1445 { | |
1446 $(this).removeClass('diva-grab'); | |
1447 }); | |
1448 | |
1449 $(settings.innerSelector).mousedown(function () | |
1450 { | |
1451 $(this).removeClass('diva-grab').addClass('diva-grabbing'); | |
1452 }); | |
1453 | |
1454 $(settings.innerSelector).mouseup(function () | |
1455 { | |
1456 $(this).removeClass('diva-grabbing').addClass('diva-grab'); | |
1457 }); | |
1458 | |
1459 // Set drag scroll on first descendant of class dragger on both selected elements | |
1460 $(settings.outerSelector + ', ' + settings.innerSelector).dragscrollable({dragSelector: '.diva-dragger', acceptPropagatedEvent: true}); | |
1461 | |
1462 // Handle the scroll | |
1463 $(settings.outerSelector).scroll(function () | |
1464 { | |
1465 settings.topScrollSoFar = document.getElementById(settings.ID + "outer").scrollTop; | |
1466 var direction = settings.topScrollSoFar - settings.previousTopScroll; | |
1467 | |
1468 if (settings.inGrid) | |
1469 { | |
1470 adjustRows(direction); | |
1471 } | |
1472 else | |
1473 { | |
1474 adjustPages(direction); | |
1475 settings.leftScrollSoFar = $(this).scrollLeft(); | |
1476 } | |
1477 | |
1478 settings.previousTopScroll = settings.topScrollSoFar; | |
1479 }); | |
1480 | |
1481 // Double-click to zoom | |
1482 $(settings.outerSelector).on('dblclick', '.diva-document-page', function (event) | |
1483 { | |
1484 handleDocumentDoubleClick.call(this, event); | |
1485 }); | |
1486 | |
1487 // Handle the control key for macs (in conjunction with double-clicking) | |
1488 $(settings.outerSelector).on('contextmenu', '.diva-document-page', function (event) | |
1489 { | |
1490 if (event.ctrlKey) | |
1491 { | |
1492 // In Firefox, this doesn't trigger a double-click, so we apply one manually | |
1493 clearTimeout(settings.singleClickTimeout); | |
1494 | |
1495 if (settings.singleClick) | |
1496 { | |
1497 handleDocumentDoubleClick.call(this, event); | |
1498 settings.singleClick = false; | |
1499 } | |
1500 else | |
1501 { | |
1502 settings.singleClick = true; | |
1503 | |
1504 // Set it to false again after 500 milliseconds (standard double-click timeout) | |
1505 settings.singleClickTimeout = setTimeout(function () | |
1506 { | |
1507 settings.singleClick = false; | |
1508 }, 500); | |
1509 } | |
1510 | |
1511 return false; | |
1512 } | |
1513 }); | |
1514 | |
1515 $(settings.outerSelector).on('dblclick', '.diva-row', function (event) | |
1516 { | |
1517 handleGridDoubleClick.call(this, event); | |
1518 }); | |
1519 | |
1520 // Check if the user is on a iPhone or iPod touch or iPad | |
1521 if (settings.mobileWebkit) | |
1522 { | |
1523 // Prevent resizing (below from http://matt.might.net/articles/how-to-native-iphone-ipad-apps-in-javascript/) | |
1524 var toAppend = []; | |
1525 toAppend.push('<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1" />'); | |
1526 | |
1527 // Eliminate URL and button bars if added to home screen | |
1528 toAppend.push('<meta name="apple-mobile-web-app-capable" content="yes" />'); | |
1529 | |
1530 // Choose how to handle the phone status bar | |
1531 toAppend.push('<meta name="apple-mobile-web-app-status-bar-style" content="black" />'); | |
1532 $('head').append(toAppend.join('\n')); | |
1533 | |
1534 // Block the user from moving the window only if it's not integrated | |
1535 if (settings.blockMobileMove) | |
1536 { | |
1537 $('body').bind('touchmove', function (event) | |
1538 { | |
1539 var e = event.originalEvent; | |
1540 e.preventDefault(); | |
1541 | |
1542 return false; | |
1543 }); | |
1544 } | |
1545 | |
1546 // Allow pinch-zooming | |
1547 $('body').bind('gestureend', function (event) | |
1548 { | |
1549 var e = event.originalEvent; | |
1550 | |
1551 if (!settings.scaleWait) | |
1552 { | |
1553 // Save the page we're currently on so we scroll there | |
1554 settings.goDirectlyTo = settings.currentPageIndex; | |
1555 | |
1556 if (settings.inGrid) | |
1557 { | |
1558 settings.inGrid = false; | |
1559 | |
1560 handleViewChange(); | |
1561 } | |
1562 else | |
1563 { | |
1564 handlePinchZoom(e); | |
1565 } | |
1566 } | |
1567 return false; | |
1568 }); | |
1569 | |
1570 // Listen to orientation change event | |
1571 $(window).bind('orientationchange', function (event) | |
1572 { | |
1573 settings.orientationChange = true; | |
1574 adjustMobileWebkitDims(); | |
1575 | |
1576 // Reload the viewer to account for the resized viewport | |
1577 settings.goDirectlyTo = settings.currentPageIndex; | |
1578 loadViewer(); | |
1579 }); | |
1580 | |
1581 // Inertial scrolling | |
1582 $(settings.outerSelector).kinetic(); | |
1583 } | |
1584 | |
1585 // Only check if either scrollBySpace or scrollByKeys is enabled | |
1586 if (settings.enableSpaceScroll || settings.enableKeyScroll) | |
1587 { | |
1588 var spaceKey = $.ui.keyCode.SPACE; | |
1589 var pageUpKey = $.ui.keyCode.PAGE_UP; | |
1590 var pageDownKey = $.ui.keyCode.PAGE_DOWN; | |
1591 var homeKey = $.ui.keyCode.HOME; | |
1592 var endKey = $.ui.keyCode.END; | |
1593 | |
1594 // Catch the key presses in document | |
1595 $(document).keydown(function (event) | |
1596 { | |
1597 // Space or page down - go to the next page | |
1598 if ((settings.enableSpaceScroll && event.keyCode === spaceKey) || (settings.enableKeyScroll && event.keyCode === pageDownKey)) | |
1599 { | |
1600 $(settings.outerSelector).scrollTop(settings.topScrollSoFar + settings.panelHeight); | |
1601 return false; | |
1602 } | |
1603 | |
1604 // Page up - go to the previous page | |
1605 if (settings.enableKeyScroll && event.keyCode === pageUpKey) | |
1606 { | |
1607 $(settings.outerSelector).scrollTop(settings.topScrollSoFar - settings.panelHeight); | |
1608 return false; | |
1609 } | |
1610 | |
1611 // Home key - go to the beginning of the document | |
1612 if (settings.enableKeyScroll && event.keyCode === homeKey) | |
1613 { | |
1614 $(settings.outerSelector).scrollTop(0); | |
1615 return false; | |
1616 } | |
1617 | |
1618 // End key - go to the end of the document | |
1619 if (settings.enableKeyScroll && event.keyCode === endKey) | |
1620 { | |
1621 $(settings.outerSelector).scrollTop(settings.totalHeight); | |
1622 return false; | |
1623 } | |
1624 }); | |
1625 | |
1626 // Handle window resizing events | |
1627 if (!settings.mobileWebkit) | |
1628 { | |
1629 $(window).resize(function () | |
1630 { | |
1631 var adjustSuccess = (settings.inFullscreen) ? adjustFullscreenDims() : adjustBrowserDims(); | |
1632 | |
1633 if (adjustSuccess) | |
1634 { | |
1635 // Cancel any previously-set resize timeouts | |
1636 clearTimeout(settings.resizeTimer); | |
1637 | |
1638 settings.resizeTimer = setTimeout(function () | |
1639 { | |
1640 settings.goDirectlyTo = settings.currentPageIndex; | |
1641 loadViewer(); | |
1642 }, 200); | |
1643 } | |
1644 }); | |
1645 } | |
1646 } | |
1647 }; | |
1648 | |
1649 // Handles all status updating etc (both fullscreen and not) | |
1650 var createToolbar = function () { | |
1651 // Prepare the HTML for the various components | |
1652 var gridIconHTML = (settings.enableGridIcon) ? '<div class="diva-grid-icon' + (settings.inGrid ? ' diva-in-grid' : '') + '" id="' + settings.ID + 'grid-icon" title="Toggle grid view"></div>' : ''; | |
1653 var linkIconHTML = (settings.enableLinkIcon) ? '<div class="diva-link-icon" id="' + settings.ID + 'link-icon" style="' + (settings.enableGridIcon ? 'border-left: 0px' : '') + '" title="Link to this page"></div>' : ''; | |
1654 var zoomSliderHTML = (settings.enableZoomSlider) ? '<div id="' + settings.ID + 'zoom-slider"></div>' : ''; | |
1655 var gridSliderHTML = (settings.enableGridSlider) ? '<div id="' + settings.ID + 'grid-slider"></div>' : ''; | |
1656 var gotoPageHTML = (settings.enableGotoPage) ? '<form id="' + settings.ID + 'goto-page" class="diva-goto-form"><input type="text" id="' + settings.ID + 'goto-page-input" / class="diva-input"> <input type="submit" value="Go" style="margin-top: 0px;" /></form>' : ''; | |
1657 var zoomSliderLabelHTML = (settings.enableZoomSlider) ? '<div id="' + settings.ID + 'zoom-slider-label" class="diva-slider-label">Zoom level: <span id="' + settings.ID + 'zoom-level">' + settings.zoomLevel + '</span></div>' : ''; | |
1658 var gridSliderLabelHTML = (settings.enableGridSlider) ? '<div id="' + settings.ID + 'grid-slider-label" class="diva-slider-label">Pages per row: <span id="' + settings.ID + 'pages-per-row">' + settings.pagesPerRow + '</span></div>' : ''; | |
1659 var pageNumberHTML = '<div class="diva-page-label">Page <span id="' + settings.ID + 'current-page">1</span> of <span id="' + settings.ID + 'num-pages">' + settings.numPages + '</span></div>'; | |
1660 | |
1661 // If the viewer is specified to be "contained", we make room for the fullscreen icon | |
1662 var otherToolbarClass = ''; | |
1663 | |
1664 if (settings.contained) | |
1665 { | |
1666 // Make sure the container element does not have a static position | |
1667 // (Needed for the fullscreen icon to be contained) | |
1668 if ($(settings.parentSelector).css('position') === 'static') | |
1669 { | |
1670 $(settings.parentSelector).addClass('diva-relative-position'); | |
1671 } | |
1672 | |
1673 otherToolbarClass = ' diva-fullscreen-space'; | |
1674 | |
1675 // If enableAutoTitle is set to TRUE, move it down | |
1676 if (settings.enableAutoTitle) | |
1677 { | |
1678 $(settings.selector + 'fullscreen').addClass('diva-contained'); | |
1679 } | |
1680 } | |
1681 | |
1682 var toolbarHTML = '<div id="' + settings.ID + 'tools-left" class="diva-tools-left' + otherToolbarClass + '">' + zoomSliderHTML + gridSliderHTML + zoomSliderLabelHTML + gridSliderLabelHTML + '</div><div id="' + settings.ID + 'tools-right" class="diva-tools-right">' + linkIconHTML + gridIconHTML + '<div class="diva-page-nav">' + gotoPageHTML + pageNumberHTML + '</div></div>'; | |
1683 | |
1684 if (settings.toolbarParentSelector) | |
1685 { | |
1686 $(settings.toolbarParentSelector).prepend('<div id="' + settings.ID + 'tools" class="diva-tools">' + toolbarHTML + '</div>'); | |
1687 } | |
1688 else | |
1689 { | |
1690 $(settings.parentSelector).prepend('<div id="' + settings.ID + 'tools" class="diva-tools">' + toolbarHTML + '</div>'); | |
1691 } | |
1692 | |
1693 // Create the zoom slider | |
1694 $(settings.selector + 'zoom-slider').slider({ | |
1695 value: settings.zoomLevel, | |
1696 min: settings.minZoomLevel, | |
1697 max: settings.maxZoomLevel, | |
1698 step: 1, | |
1699 slide: function (event, ui) | |
1700 { | |
1701 var i = settings.currentPageIndex; | |
1702 settings.goDirectlyTo = i; | |
1703 | |
1704 // Figure out the horizontal and vertical offsets | |
1705 // (Try to zoom in on the current center) | |
1706 var zoomRatio = Math.pow(2, ui.value - settings.zoomLevel); | |
1707 var innerWidth = settings.maxWidths[settings.zoomLevel] + settings.horizontalPadding * 2; | |
1708 var centerX = $(settings.outerSelector).scrollLeft() - (innerWidth - settings.panelWidth) / 2; | |
1709 settings.horizontalOffset = (innerWidth > settings.panelWidth) ? centerX * zoomRatio : 0; | |
1710 settings.verticalOffset = zoomRatio * ($(settings.outerSelector).scrollTop() - settings.heightAbovePages[i]); | |
1711 | |
1712 handleZoom(ui.value); | |
1713 }, | |
1714 change: function (event, ui) | |
1715 { | |
1716 if (ui.value !== settings.zoomLevel) | |
1717 { | |
1718 handleZoom(ui.value); | |
1719 } | |
1720 } | |
1721 }); | |
1722 | |
1723 // Create the grid slider | |
1724 $(settings.selector + 'grid-slider').slider( | |
1725 { | |
1726 value: settings.pagesPerRow, | |
1727 min: settings.minPagesPerRow, | |
1728 max: settings.maxPagesPerRow, | |
1729 step: 1, | |
1730 slide: function (event, ui) | |
1731 { | |
1732 handleGrid(ui.value); | |
1733 }, | |
1734 change: function (event, ui) | |
1735 { | |
1736 if (ui.value !== settings.pagesPerRow) | |
1737 { | |
1738 handleGrid(ui.value); | |
1739 } | |
1740 } | |
1741 }); | |
1742 | |
1743 // Handle clicking of the grid icon | |
1744 $(settings.selector + 'grid-icon').click(function () | |
1745 { | |
1746 toggleGrid(); | |
1747 }); | |
1748 | |
1749 // Handle going to a specific page using the input box | |
1750 $(settings.selector + 'goto-page').submit(function () | |
1751 { | |
1752 var desiredPage = parseInt($(settings.selector + 'goto-page-input').val(), 10); | |
1753 var pageIndex = desiredPage - 1; | |
1754 | |
1755 if (!isPageValid(pageIndex)) | |
1756 { | |
1757 alert("Invalid page number"); | |
1758 } | |
1759 else | |
1760 { | |
1761 if (settings.inGrid) | |
1762 { | |
1763 gotoRow(pageIndex); | |
1764 } | |
1765 else | |
1766 { | |
1767 gotoPage(pageIndex, 0, 0); | |
1768 } | |
1769 } | |
1770 | |
1771 // Prevent the default action of reloading the page | |
1772 return false; | |
1773 }); | |
1774 | |
1775 // Handle the creation of the link popup box | |
1776 $(settings.selector + 'link-icon').click(function () | |
1777 { | |
1778 $('body').prepend('<div id="' + settings.ID + 'link-popup" class="diva-link-popup"><input id="' + settings.ID + 'link-popup-input" class="diva-input" type="text" value="' + getCurrentURL() + '"/></div>'); | |
1779 | |
1780 if (settings.inFullscreen) | |
1781 { | |
1782 $(settings.selector + 'link-popup').addClass('in-fullscreen'); | |
1783 } | |
1784 else | |
1785 { | |
1786 // Calculate the left and top offsets | |
1787 // Compensate for border, popup width | |
1788 var leftOffset = $(settings.outerSelector).offset().left + settings.panelWidth; | |
1789 leftOffset += settings.scrollbarWidth - 240 - 1; | |
1790 var topOffset = $(settings.outerSelector).offset().top + 1; | |
1791 | |
1792 $(settings.selector + 'link-popup').removeClass('in-fullscreen').css( | |
1793 { | |
1794 'top': topOffset + 'px', | |
1795 'left': leftOffset + 'px' | |
1796 }); | |
1797 } | |
1798 | |
1799 // Catch onmouseup events outside of this div | |
1800 $('body').mouseup(function (event) | |
1801 { | |
1802 var targetID = event.target.id; | |
1803 | |
1804 if (targetID !== settings.ID + 'link-popup' && targetID !== settings.ID + 'link-popup-input') | |
1805 { | |
1806 $(settings.selector + 'link-popup').remove(); | |
1807 } | |
1808 }); | |
1809 | |
1810 // Also delete it upon scroll and page up/down key events | |
1811 $(settings.outerSelector).scroll(function () | |
1812 { | |
1813 $(settings.selector + 'link-popup').remove(); | |
1814 }); | |
1815 $(settings.selector + 'link-popup input').click(function () | |
1816 { | |
1817 $(this).focus().select(); | |
1818 }); | |
1819 return false; | |
1820 }); | |
1821 | |
1822 // Show the relevant slider | |
1823 var currentSlider = (settings.inGrid) ? 'grid' : 'zoom'; | |
1824 $(settings.selector + currentSlider + '-slider').show(); | |
1825 $(settings.selector + currentSlider + '-slider-label').show(); | |
1826 | |
1827 var switchMode = function () | |
1828 { | |
1829 // Switch from fullscreen to not | |
1830 $(settings.selector + 'tools').toggleClass('diva-fullscreen-tools'); | |
1831 | |
1832 if (!settings.inFullscreen) | |
1833 { | |
1834 // Leaving fullscreen | |
1835 $(settings.selector + 'tools-left').after($(settings.selector + 'tools-right')); | |
1836 $(settings.selector + 'tools-left').removeClass('in-fullscreen'); | |
1837 } | |
1838 else | |
1839 { | |
1840 // Entering fullscreen | |
1841 $(settings.selector + 'tools-right').after($(settings.selector + 'tools-left')); | |
1842 $(settings.selector + 'tools-left').addClass('in-fullscreen'); | |
1843 } | |
1844 }; | |
1845 | |
1846 var switchView = function () | |
1847 { | |
1848 // Switch from grid to document view etc | |
1849 $(settings.selector + currentSlider + '-slider').hide(); | |
1850 $(settings.selector + currentSlider + '-slider-label').hide(); | |
1851 currentSlider = (settings.inGrid) ? 'grid' : 'zoom'; | |
1852 $(settings.selector + currentSlider + '-slider').show(); | |
1853 $(settings.selector + currentSlider + '-slider-label').show(); | |
1854 | |
1855 // Also change the image for the grid icon | |
1856 $(settings.selector + 'grid-icon').toggleClass('diva-in-grid'); | |
1857 }; | |
1858 | |
1859 var toolbar = | |
1860 { | |
1861 updateCurrentPage: function () | |
1862 { | |
1863 $(settings.selector + 'current-page').text(settings.currentPageIndex + 1); | |
1864 }, | |
1865 setNumPages: function (newNumber) | |
1866 { | |
1867 $(settings.selector + 'num-pages').text(newNumber); | |
1868 }, | |
1869 updateZoomSlider: function () | |
1870 { | |
1871 // Update the position of the handle within the slider | |
1872 if (settings.zoomLevel !== $(settings.selector + 'zoom-slider').slider('value')) | |
1873 { | |
1874 $(settings.selector + 'zoom-slider').slider( | |
1875 { | |
1876 value: settings.zoomLevel | |
1877 }); | |
1878 } | |
1879 | |
1880 // Update the slider label | |
1881 $(settings.selector + 'zoom-level').text(settings.zoomLevel); | |
1882 }, | |
1883 updateGridSlider: function () | |
1884 { | |
1885 // Update the position of the handle within the slider | |
1886 if (settings.pagesPerRow !== $(settings.selector + 'grid-slider').slider('value')) | |
1887 { | |
1888 $(settings.selector + 'grid-slider').slider( | |
1889 { | |
1890 value: settings.pagesPerRow | |
1891 }); | |
1892 } | |
1893 | |
1894 // Update the slider label | |
1895 $(settings.selector + 'pages-per-row').text(settings.pagesPerRow); | |
1896 }, | |
1897 switchView: switchView, | |
1898 switchMode: switchMode | |
1899 }; | |
1900 return toolbar; | |
1901 }; | |
1902 | |
1903 var initPlugins = function () | |
1904 { | |
1905 if (window.divaPlugins) | |
1906 { | |
1907 var pageTools = []; | |
1908 | |
1909 // Add all the plugins that have not been explicitly disabled to settings.plugins | |
1910 $.each(window.divaPlugins, function (index, plugin) | |
1911 { | |
1912 var pluginProperName = plugin.pluginName[0].toUpperCase() + plugin.pluginName.substring(1); | |
1913 | |
1914 if (settings['enable' + pluginProperName]) | |
1915 { | |
1916 // Call the init function and check return value | |
1917 var enablePlugin = plugin.init(settings, self); | |
1918 | |
1919 // If int returns false, consider the plugin disabled | |
1920 if (!enablePlugin) | |
1921 { | |
1922 return; | |
1923 } | |
1924 | |
1925 // If the title text is undefined, use the name of the plugin | |
1926 var titleText = plugin.titleText || pluginProperName + " plugin"; | |
1927 | |
1928 // Create the pageTools bar if handleClick is set to a function | |
1929 if (typeof plugin.handleClick === 'function') | |
1930 { | |
1931 pageTools.push('<div class="diva-' + plugin.pluginName + '-icon" title="' + titleText + '"></div>'); | |
1932 | |
1933 // Delegate the click event - pass it the settings | |
1934 $(settings.outerSelector).delegate('.diva-' + plugin.pluginName + '-icon', 'click', function (event) | |
1935 { | |
1936 plugin.handleClick.call(this, event, settings); | |
1937 }); | |
1938 } | |
1939 | |
1940 // Add it to settings.plugins so it can be used later | |
1941 settings.plugins.push(plugin); | |
1942 } | |
1943 }); | |
1944 | |
1945 // Save the page tools bar so it can be added for each page | |
1946 if (pageTools.length) | |
1947 { | |
1948 settings.pageTools = '<div class="diva-page-tools">' + pageTools.join('') + '</div>'; | |
1949 } | |
1950 } | |
1951 }; | |
1952 | |
1953 var hideThrobber = function () | |
1954 { | |
1955 // Clear the timeout, if it hasn't executed yet | |
1956 clearTimeout(settings.throbberTimeoutID); | |
1957 | |
1958 // Hide the throbber if it has already executed | |
1959 $(settings.selector + 'throbber').hide(); | |
1960 }; | |
1961 | |
1962 var setupViewer = function () | |
1963 { | |
1964 // Create the throbber element | |
1965 var throbberHTML = '<div id="' + settings.ID + 'throbber" class="diva-throbber"></div>'; | |
1966 $(settings.outerSelector).append(throbberHTML); | |
1967 | |
1968 // If the request hasn't completed after a specified time, show it | |
1969 settings.throbberTimeoutID = setTimeout(function () | |
1970 { | |
1971 $(settings.selector + 'throbber').show(); | |
1972 }, settings.throbberTimeout); | |
1973 | |
1974 $.ajax({ | |
1975 url: settings.objectData, | |
1976 cache: true, | |
1977 dataType: 'json', | |
1978 error: function (jqxhr, status, error) | |
1979 { | |
1980 hideThrobber(); | |
1981 | |
1982 // Show a basic error message within the document viewer pane | |
1983 $(settings.outerSelector).text("Invalid URL. Error code: " + status + " " + error); | |
1984 }, | |
1985 success: function (data, status, jqxhr) | |
1986 { | |
1987 hideThrobber(); | |
1988 | |
1989 // Save all the data we need | |
1990 settings.pages = data.pgs; | |
1991 settings.maxRatio = data.dims.max_ratio; | |
1992 settings.minRatio = data.dims.min_ratio; | |
1993 settings.itemTitle = data.item_title; | |
1994 settings.numPages = data.pgs.length; | |
1995 | |
1996 // These are arrays, the index corresponding to the zoom level | |
1997 settings.maxWidths = data.dims.max_w; | |
1998 settings.averageWidths = data.dims.a_wid; | |
1999 settings.averageHeights = data.dims.a_hei; | |
2000 settings.totalHeights = data.dims.t_hei; | |
2001 | |
2002 // Make sure the set max and min values are valid | |
2003 settings.realMaxZoom = data.max_zoom; | |
2004 settings.maxZoomLevel = (settings.maxZoomLevel >= 0 && settings.maxZoomLevel <= data.max_zoom) ? settings.maxZoomLevel : data.max_zoom; | |
2005 settings.minZoomLevel = (settings.minZoomLevel >= 0 && settings.minZoomLevel <= settings.maxZoomLevel) ? settings.minZoomLevel : 0; | |
2006 settings.minPagesPerRow = Math.max(2, settings.minPagesPerRow); | |
2007 settings.maxPagesPerRow = Math.max(settings.minPagesPerRow, settings.maxPagesPerRow); | |
2008 | |
2009 // Check that the desired page is in range | |
2010 if (settings.enableFilename) | |
2011 { | |
2012 var iParam = $.getHashParam('i' + settings.hashParamSuffix); | |
2013 var iParamPage = getPageIndex(iParam); | |
2014 | |
2015 if (isPageValid(iParamPage)) | |
2016 { | |
2017 settings.goDirectlyTo = iParamPage; | |
2018 } | |
2019 } | |
2020 else | |
2021 { | |
2022 // Not using the i parameter, check the p parameter | |
2023 // Subtract 1 to get the page index | |
2024 var pParam = parseInt($.getHashParam('p' + settings.hashParamSuffix), 10) - 1; | |
2025 | |
2026 if (isPageValid(pParam)) | |
2027 { | |
2028 settings.goDirectlyTo = pParam; | |
2029 } | |
2030 } | |
2031 | |
2032 // Execute the setup hook for each plugin (if defined) | |
2033 $.each(settings.plugins, function (index, plugin) | |
2034 { | |
2035 executeCallback(plugin.setupHook, settings); | |
2036 }); | |
2037 | |
2038 // Create the toolbar and display the title + total number of pages | |
2039 if (settings.enableToolbar) | |
2040 { | |
2041 settings.toolbar = createToolbar(); | |
2042 Events.subscribe("VisiblePageDidChange", settings.toolbar.updateCurrentPage); | |
2043 Events.subscribe("ModeDidSwitch", settings.toolbar.switchMode); | |
2044 Events.subscribe("ViewDidSwitch", settings.toolbar.switchView); | |
2045 Events.subscribe("ZoomLevelDidChange", settings.toolbar.updateZoomSlider); | |
2046 Events.subscribe("GridRowNumberDidChange", settings.toolbar.updateGridSlider); | |
2047 } | |
2048 | |
2049 $(settings.selector + 'current label').text(settings.numPages); | |
2050 | |
2051 if (settings.enableAutoTitle) | |
2052 { | |
2053 $(settings.parentSelector).prepend('<div id="' + settings.ID + 'title" class="diva-title">' + settings.itemTitle + '</div>'); | |
2054 } | |
2055 | |
2056 // Adjust the document panel dimensions for touch devices | |
2057 if (settings.mobileWebkit) | |
2058 { | |
2059 adjustMobileWebkitDims(); | |
2060 } | |
2061 else | |
2062 { | |
2063 settings.originalWidth = $(settings.parentSelector).width() - settings.scrollbarWidth; | |
2064 settings.originalHeight = $(settings.outerSelector).height(); | |
2065 adjustBrowserDims(); | |
2066 } | |
2067 | |
2068 // Calculate the viewer x and y offsets | |
2069 var viewerOffset = $(settings.outerSelector).offset(); | |
2070 settings.viewerXOffset = viewerOffset.left; | |
2071 settings.viewerYOffset = viewerOffset.top; | |
2072 | |
2073 if (settings.inFullscreen) | |
2074 { | |
2075 handleModeChange(false); | |
2076 } | |
2077 else | |
2078 { | |
2079 loadViewer(); | |
2080 } | |
2081 | |
2082 // Execute the callback | |
2083 executeCallback(settings.onReady, settings); | |
2084 Events.publish("ViewerHasFinishedLoading", [settings]); | |
2085 | |
2086 // signal that everything should be set up and ready to go. | |
2087 settings.loaded = true; | |
2088 } | |
2089 }); | |
2090 }; | |
2091 | |
2092 var checkLoaded = function() | |
2093 { | |
2094 if (!settings.loaded) | |
2095 { | |
2096 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."); | |
2097 return false; | |
2098 } | |
2099 return true; | |
2100 }; | |
2101 | |
2102 var init = function () | |
2103 { | |
2104 // First figure out the width of the scrollbar in this browser | |
2105 settings.scrollbarWidth = $.getScrollbarWidth(); | |
2106 | |
2107 // If window.orientation is defined, then it's probably mobileWebkit | |
2108 settings.mobileWebkit = window.orientation !== undefined; | |
2109 | |
2110 // Generate an ID that can be used as a prefix for all the other IDs | |
2111 settings.ID = $.generateId('diva-'); | |
2112 settings.selector = '#' + settings.ID; | |
2113 | |
2114 // Figure out the hashParamSuffix from the ID | |
2115 var divaNumber = parseInt(settings.ID, 10); | |
2116 | |
2117 if (divaNumber > 1) | |
2118 { | |
2119 // If this is document viewer #1, don't use a suffix; otherwise, use the document viewer number | |
2120 settings.hashParamSuffix = divaNumber; | |
2121 } | |
2122 | |
2123 // Since we need to reference these two a lot | |
2124 settings.outerSelector = settings.selector + 'outer'; | |
2125 settings.innerSelector = settings.selector + 'inner'; | |
2126 | |
2127 // Create the inner and outer panels | |
2128 $(settings.parentSelector).append('<div id="' + settings.ID + 'outer" class="diva-outer"></div>'); | |
2129 $(settings.outerSelector).append('<div id="' + settings.ID + 'inner" class="diva-inner diva-dragger"></div>'); | |
2130 | |
2131 // Create the fullscreen icon | |
2132 if (settings.enableFullscreen) | |
2133 { | |
2134 $(settings.parentSelector).prepend('<div id="' + settings.ID + 'fullscreen" class="diva-fullscreen-icon" title="Toggle fullscreen mode"></div>'); | |
2135 } | |
2136 | |
2137 // First, n - check if it's in range | |
2138 var nParam = parseInt($.getHashParam('n' + settings.hashParamSuffix), 10); | |
2139 | |
2140 if (nParam >= settings.minPagesPerRow && nParam <= settings.maxPagesPerRow) | |
2141 { | |
2142 settings.pagesPerRow = nParam; | |
2143 } | |
2144 | |
2145 // Now z - check that it's in range | |
2146 var zParam = $.getHashParam('z' + settings.hashParamSuffix); | |
2147 | |
2148 if (zParam !== '') | |
2149 { | |
2150 // If it's empty, we don't want to change the default zoom level | |
2151 zParam = parseInt(zParam, 10); | |
2152 | |
2153 // Can't check if it exceeds the max zoom level or not because that data is not available yet ... | |
2154 if (zParam >= settings.minZoomLevel) | |
2155 { | |
2156 settings.zoomLevel = zParam; | |
2157 } | |
2158 } | |
2159 | |
2160 // y - vertical offset from the top of the relevant page | |
2161 var yParam = parseInt($.getHashParam('y' + settings.hashParamSuffix), 10); | |
2162 | |
2163 if (!isNaN(yParam)) | |
2164 { | |
2165 settings.verticalOffset = yParam; | |
2166 } | |
2167 | |
2168 // x - horizontal offset from the center of the page | |
2169 var xParam = parseInt($.getHashParam('x' + settings.hashParamSuffix), 10); | |
2170 | |
2171 if (!isNaN(xParam)) | |
2172 { | |
2173 settings.horizontalOffset = xParam; | |
2174 } | |
2175 | |
2176 // If the "fullscreen" hash param is true, go to fullscreen initially | |
2177 // If the grid hash param is true, go to grid view initially | |
2178 var gridParam = $.getHashParam('g' + settings.hashParamSuffix); | |
2179 var goIntoGrid = gridParam === 'true'; | |
2180 var fullscreenParam = $.getHashParam('f' + settings.hashParamSuffix); | |
2181 var goIntoFullscreen = fullscreenParam === 'true'; | |
2182 | |
2183 settings.inGrid = (settings.inGrid && gridParam !== 'false') || goIntoGrid; | |
2184 settings.inFullscreen = (settings.inFullscreen && fullscreenParam !== 'false') || goIntoFullscreen; | |
2185 | |
2186 // Store the height and width of the viewer (the outer div), if present | |
2187 var desiredHeight = parseInt($.getHashParam('h' + settings.hashParamSuffix), 10); | |
2188 var desiredWidth = parseInt($.getHashParam('w' + settings.hashParamSuffix), 10); | |
2189 | |
2190 // Store the minimum and maximum height too | |
2191 settings.minHeight = parseInt($(settings.outerSelector).css('min-height'), 10); | |
2192 settings.minWidth = parseInt($(settings.outerSelector).css('min-width'), 10); | |
2193 | |
2194 // Just call resize, it'll take care of bounds-checking etc | |
2195 if (desiredHeight > 0 || desiredWidth > 0) | |
2196 { | |
2197 resizeViewer(desiredWidth, desiredHeight); | |
2198 } | |
2199 | |
2200 // Do the initial AJAX request and viewer loading | |
2201 setupViewer(); | |
2202 | |
2203 // Do all the plugin initialisation | |
2204 initPlugins(); | |
2205 | |
2206 handleEvents(); | |
2207 }; | |
2208 | |
2209 // Call the init function when this object is created. | |
2210 init(); | |
2211 | |
2212 /* PUBLIC FUNCTIONS | |
2213 =============================================== | |
2214 */ | |
2215 | |
2216 // Returns the title of the document, based on the directory name | |
2217 this.getItemTitle = function () | |
2218 { | |
2219 return settings.itemTitle; | |
2220 }; | |
2221 | |
2222 // Go to a particular page by its page number (with indexing starting at 1) | |
2223 // returns True if the page number passed is valid; false if it is not. | |
2224 this.gotoPageByNumber = function (pageNumber) | |
2225 { | |
2226 var pageIndex = pageNumber - 1; | |
2227 if (isPageValid(pageIndex)) | |
2228 { | |
2229 gotoPage(pageIndex, 0, 0); | |
2230 return true; | |
2231 } | |
2232 return false; | |
2233 }; | |
2234 | |
2235 // Go to a particular page (with indexing starting at 0) | |
2236 // returns True if the page index is valid; false if it is not. | |
2237 this.gotoPageByIndex = function (pageIndex) | |
2238 { | |
2239 if (isPageValid(pageIndex)) | |
2240 { | |
2241 gotoPage(pageIndex, 0, 0); | |
2242 return true; | |
2243 } | |
2244 return false; | |
2245 }; | |
2246 | |
2247 // Returns the page index (with indexing starting at 0) | |
2248 this.getCurrentPage = function () | |
2249 { | |
2250 console.warn("Deprecated. Use getCurrentPageIndex instead."); | |
2251 return settings.currentPageIndex; | |
2252 }; | |
2253 | |
2254 this.getNumberOfPages = function() | |
2255 { | |
2256 if (!checkLoaded()) | |
2257 { | |
2258 return false; | |
2259 } | |
2260 | |
2261 return settings.numPages; | |
2262 } | |
2263 | |
2264 // Returns the dimensions of a given page index at a given zoom level | |
2265 this.getPageDimensionsAtZoomLevel = function(pageIdx, zoomLevel) | |
2266 { | |
2267 if (!checkLoaded()) | |
2268 { | |
2269 return false; | |
2270 } | |
2271 | |
2272 var zoomLevel = zoomLevel - 1; // zoom levels are 1-based, but our array is 0-based; | |
2273 var pg = settings.pages[pageIdx]; | |
2274 var pgAtZoom = pg.d[parseInt(zoomLevel, 10)]; | |
2275 return {'width': pgAtZoom.w, 'height': pgAtZoom.h} | |
2276 }; | |
2277 | |
2278 // Returns the dimensions of the current page at the current zoom level | |
2279 this.getCurrentPageDimensionsAtCurrentZoomLevel = function() | |
2280 { | |
2281 return this.getPageDimensionsAtZoomLevel(settings.currentPageIndex, settings.zoomLevel); | |
2282 }; | |
2283 | |
2284 this.isReady = function() | |
2285 { | |
2286 return settings.loaded; | |
2287 }; | |
2288 | |
2289 this.getCurrentPageIndex = function () | |
2290 { | |
2291 return settings.currentPageIndex; | |
2292 }; | |
2293 | |
2294 this.getCurrentPageFilename = function () | |
2295 { | |
2296 return settings.pages[settings.currentPageIndex].f; | |
2297 }; | |
2298 | |
2299 this.getCurrentPageNumber = function () | |
2300 { | |
2301 return settings.currentPageIndex + 1; | |
2302 }; | |
2303 | |
2304 // Returns the current zoom level | |
2305 this.getZoomLevel = function () | |
2306 { | |
2307 return settings.zoomLevel; | |
2308 }; | |
2309 | |
2310 // gets the maximum zoom level for the entire document | |
2311 this.getMaxZoomLevel = function () | |
2312 { | |
2313 return settings.maxZoomLevel; | |
2314 }; | |
2315 | |
2316 // gets the max zoom level for a given page | |
2317 this.getMaxZoomLevelForPage = function(pageIdx) | |
2318 { | |
2319 if (!checkLoaded) | |
2320 { | |
2321 return false; | |
2322 } | |
2323 | |
2324 return settings.pages[pageIdx].m; | |
2325 } | |
2326 | |
2327 this.getMinZoomLevel = function () | |
2328 { | |
2329 return settings.minZoomLevel; | |
2330 }; | |
2331 | |
2332 // Use the provided zoom level (will check for validity first) | |
2333 // Returns false if the zoom level is invalid, true otherwise | |
2334 this.setZoomLevel = function (zoomLevel) | |
2335 { | |
2336 if (settings.inGrid) | |
2337 { | |
2338 toggleGrid(); | |
2339 } | |
2340 | |
2341 return handleZoom(zoomLevel); | |
2342 }; | |
2343 | |
2344 // Zoom in. Will return false if it's at the maximum zoom | |
2345 this.zoomIn = function () | |
2346 { | |
2347 return this.setZoomLevel(settings.zoomLevel + 1); | |
2348 }; | |
2349 | |
2350 // Zoom out. Will return false if it's at the minimum zoom | |
2351 this.zoomOut = function () | |
2352 { | |
2353 return this.setZoomLevel(settings.zoomLevel - 1); | |
2354 }; | |
2355 | |
2356 // Uses the isVerticallyInViewport() function, but relative to a page | |
2357 // Check if something (e.g. a highlight box on a particular page) is visible | |
2358 this.inViewport = function (pageNumber, topOffset, height) | |
2359 { | |
2360 var pageIndex = pageNumber - 1; | |
2361 var top = settings.heightAbovePages[pageIndex] + topOffset; | |
2362 var bottom = top + height; | |
2363 | |
2364 return isVerticallyInViewport(top, bottom); | |
2365 }; | |
2366 | |
2367 // Toggle fullscreen mode | |
2368 this.toggleFullscreenMode = function () | |
2369 { | |
2370 toggleFullscreen(); | |
2371 }; | |
2372 | |
2373 // Enter fullscreen mode if currently not in fullscreen mode | |
2374 // Returns false if in fullscreen mode initially, true otherwise | |
2375 // This function will work even if enableFullscreen is set to false | |
2376 this.enterFullscreenMode = function () | |
2377 { | |
2378 if (!settings.inFullscreen) | |
2379 { | |
2380 toggleFullscreen(); | |
2381 return true; | |
2382 } | |
2383 | |
2384 return false; | |
2385 }; | |
2386 | |
2387 // Leave fullscreen mode if currently in fullscreen mode | |
2388 // Returns true if in fullscreen mode intitially, false otherwise | |
2389 this.leaveFullscreenMode = function () | |
2390 { | |
2391 if (settings.inFullscreen) | |
2392 { | |
2393 toggleFullscreen(); | |
2394 return true; | |
2395 } | |
2396 | |
2397 return false; | |
2398 }; | |
2399 | |
2400 // Toggle grid view | |
2401 this.toggleGridView = function () | |
2402 { | |
2403 toggleGrid(); | |
2404 }; | |
2405 | |
2406 // Enter grid view if currently not in grid view | |
2407 // Returns false if in grid view initially, true otherwise | |
2408 this.enterGridView = function () | |
2409 { | |
2410 if (!settings.inGrid) { | |
2411 toggleGrid(); | |
2412 return true; | |
2413 } | |
2414 | |
2415 return false; | |
2416 }; | |
2417 | |
2418 // Leave grid view if currently in grid view | |
2419 // Returns true if in grid view initially, false otherwise | |
2420 this.leaveGridView = function () | |
2421 { | |
2422 if (settings.inGrid) | |
2423 { | |
2424 toggleGrid(); | |
2425 return true; | |
2426 } | |
2427 | |
2428 return false; | |
2429 }; | |
2430 | |
2431 // Jump to a page based on its filename | |
2432 // Returns true if successful and false if the filename is invalid | |
2433 this.gotoPageByName = function (filename) | |
2434 { | |
2435 var pageIndex = getPageIndex(filename); | |
2436 if (isPageValid(pageIndex)) | |
2437 { | |
2438 gotoPage(pageIndex, 0, 0); | |
2439 return true; | |
2440 } | |
2441 | |
2442 return false; | |
2443 }; | |
2444 | |
2445 // Get the page index (0-based) corresponding to a given filename | |
2446 // If the page index doesn't exist, this will return -1 | |
2447 this.getPageIndex = function (filename) | |
2448 { | |
2449 return getPageIndex(filename); | |
2450 }; | |
2451 | |
2452 // Get the current URL (exposes the private method) | |
2453 this.getCurrentURL = function () | |
2454 { | |
2455 return getCurrentURL(); | |
2456 }; | |
2457 | |
2458 // Get the hash part only of the current URL (without the leading #) | |
2459 this.getURLHash = function () | |
2460 { | |
2461 return getURLHash(); | |
2462 }; | |
2463 | |
2464 // Get an object representing the state of this diva instance (for setState) | |
2465 this.getState = function () | |
2466 { | |
2467 return getState(); | |
2468 }; | |
2469 | |
2470 // Get the instance selector for this instance, since it's auto-generated. | |
2471 this.getInstanceSelector = function () | |
2472 { | |
2473 return settings.selector; | |
2474 }; | |
2475 | |
2476 // Get the instance ID -- essentially the selector without the leading '#'. | |
2477 this.getInstanceId = function() | |
2478 { | |
2479 return settings.ID; | |
2480 }; | |
2481 | |
2482 this.getSettings = function() | |
2483 { | |
2484 return settings; | |
2485 }; | |
2486 | |
2487 // Align this diva instance with a state object (as returned by getState) | |
2488 this.setState = function (state) | |
2489 { | |
2490 var pageIndex; | |
2491 | |
2492 // If we need to resize the viewer, do that first | |
2493 resizeViewer(state.w, state.h); | |
2494 | |
2495 // Only change settings.goDirectlyTo if state.i or state.p is valid | |
2496 pageIndex = getPageIndex(state.i); | |
2497 | |
2498 if (isPageValid(pageIndex)) | |
2499 { | |
2500 settings.goDirectlyTo = pageIndex; | |
2501 } | |
2502 else if (isPageValid(state.p)) | |
2503 { | |
2504 settings.goDirectlyTo = state.p; | |
2505 } | |
2506 | |
2507 settings.horizontalOffset = parseInt(state.x, 10); | |
2508 settings.verticalOffset = parseInt(state.y, 10); | |
2509 | |
2510 // Only change the zoom if state.z is valid | |
2511 if (state.z >= settings.minZoomLevel && state.z <= settings.maxZoomLevel) | |
2512 { | |
2513 settings.zoomLevel = state.z; | |
2514 } | |
2515 | |
2516 // Only change the pages per row setting if state.n is valid | |
2517 if (state.n >= settings.minPagesPerRow && state.n <= settings.maxPagesPerRow) | |
2518 { | |
2519 settings.pagesPerRow = state.n; | |
2520 } | |
2521 | |
2522 if (settings.inFullscreen !== state.f) | |
2523 { | |
2524 // The parameter determines if we need to change the view as well | |
2525 settings.inFullscreen = state.f; | |
2526 handleModeChange(settings.inGrid !== state.g); | |
2527 } | |
2528 else | |
2529 { | |
2530 // Don't need to change the mode, may need to change view | |
2531 if (settings.inGrid !== state.g) | |
2532 { | |
2533 settings.inGrid = state.g; | |
2534 handleViewChange(); | |
2535 } | |
2536 else | |
2537 { | |
2538 // Reload the viewer, just in case | |
2539 loadViewer(); | |
2540 } | |
2541 } | |
2542 }; | |
2543 | |
2544 // Resizes the outer div to the specified width and height | |
2545 this.resize = function (newWidth, newHeight) | |
2546 { | |
2547 resizeViewer(newWidth, newHeight); | |
2548 loadViewer(); | |
2549 }; | |
2550 | |
2551 // Destroys this instance, tells plugins to do the same (for testing) | |
2552 this.destroy = function () | |
2553 { | |
2554 // Removes the hide-scrollbar class from the body | |
2555 $('body').removeClass('diva-hide-scrollbar'); | |
2556 | |
2557 // Empty the parent container and remove any diva-related data | |
2558 $(settings.parentSelector).empty().removeData('diva'); | |
2559 | |
2560 // Call the destroy function for all the enabled plugins (if it exists) | |
2561 $.each(settings.plugins, function (index, plugin) | |
2562 { | |
2563 executeCallback(plugin.destroy); | |
2564 }); | |
2565 | |
2566 // Remove any additional styling on the parent element | |
2567 $(settings.parentSelector).removeAttr('style').removeAttr('class'); | |
2568 }; | |
2569 }; | |
2570 | |
2571 $.fn.diva = function (options) | |
2572 { | |
2573 return this.each(function () | |
2574 { | |
2575 var element = $(this); | |
2576 | |
2577 // Return early if this element already has a plugin instance | |
2578 if (element.data('diva')) | |
2579 { | |
2580 return; | |
2581 } | |
2582 | |
2583 // Save the reference to the container element | |
2584 options.parentSelector = element; | |
2585 | |
2586 // Otherwise, instantiate the document viewer | |
2587 var diva = new Diva(this, options); | |
2588 element.data('diva', diva); | |
2589 }); | |
2590 }; | |
2591 | |
2592 })(jQuery); |