7
|
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); |