diva.js 6.0.2 → 7.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/.clang-format +7 -0
  2. package/.github/workflows/npm-publish.yml +45 -0
  3. package/LICENSE +55 -0
  4. package/Makefile +75 -0
  5. package/README.md +15 -114
  6. package/elm.json +32 -0
  7. package/package.json +12 -59
  8. package/review/elm.json +52 -0
  9. package/review/src/ReviewConfig.elm +87 -0
  10. package/scripts/elm-esm.sh +40 -0
  11. package/scripts/minify-css.mjs +31 -0
  12. package/src/Filters.elm +1044 -0
  13. package/src/Main.elm +1217 -0
  14. package/src/Model.elm +213 -0
  15. package/src/Msg.elm +59 -0
  16. package/src/Utilities.elm +46 -0
  17. package/src/View/CollectionExplorer.elm +172 -0
  18. package/src/View/Helpers.elm +86 -0
  19. package/src/View/HtmlRenderer.elm +136 -0
  20. package/src/View/Icons.elm +159 -0
  21. package/src/View/ManifestInfoModal.elm +363 -0
  22. package/src/View/PageViewModal.elm +1046 -0
  23. package/src/View/Sidebar.elm +786 -0
  24. package/src/View/Toolbar.elm +189 -0
  25. package/src/View.elm +244 -0
  26. package/src/diva.ts +802 -0
  27. package/src/filters.ts +1843 -0
  28. package/src/styles/app.css +328 -0
  29. package/src/styles/collection.css +75 -0
  30. package/src/styles/modal.css +388 -0
  31. package/src/styles/sidebar.css +215 -0
  32. package/src/styles/theme.css +39 -0
  33. package/src/styles/toolbar.css +154 -0
  34. package/src/viewer-element.ts +1307 -0
  35. package/testing/index.html +52 -0
  36. package/testing/testing.html +231 -0
  37. package/tsconfig.json +12 -0
  38. package/AUTHORS +0 -22
  39. package/build/diva.css +0 -554
  40. package/build/diva.css.map +0 -1
  41. package/build/diva.js +0 -9
  42. package/build/diva.js.map +0 -1
  43. package/build/plugins/download.js +0 -2
  44. package/build/plugins/download.js.map +0 -1
  45. package/build/plugins/manipulation.js +0 -2
  46. package/build/plugins/manipulation.js.map +0 -1
  47. package/build/plugins/metadata.js +0 -2
  48. package/build/plugins/metadata.js.map +0 -1
  49. package/index.html +0 -28
  50. package/karma.conf.js +0 -87
  51. package/source/css/_mixins.scss +0 -43
  52. package/source/css/_variables.scss +0 -50
  53. package/source/css/_viewer.scss +0 -462
  54. package/source/css/diva.scss +0 -15
  55. package/source/css/plugins/_manipulation.scss +0 -228
  56. package/source/css/plugins/_metadata.scss +0 -31
  57. package/source/img/adjust.svg +0 -11
  58. package/source/img/book-view.svg +0 -6
  59. package/source/img/close.svg +0 -6
  60. package/source/img/download.svg +0 -6
  61. package/source/img/from-fullscreen.svg +0 -8
  62. package/source/img/grid-fewer.svg +0 -6
  63. package/source/img/grid-more.svg +0 -6
  64. package/source/img/grid-view.svg +0 -6
  65. package/source/img/link.svg +0 -6
  66. package/source/img/metadata.svg +0 -9
  67. package/source/img/page-view.svg +0 -6
  68. package/source/img/to-fullscreen.svg +0 -11
  69. package/source/img/zoom-in.svg +0 -6
  70. package/source/img/zoom-out.svg +0 -7
  71. package/source/js/composite-image.js +0 -174
  72. package/source/js/diva-global.js +0 -7
  73. package/source/js/diva.js +0 -1543
  74. package/source/js/document-handler.js +0 -180
  75. package/source/js/document-layout.js +0 -286
  76. package/source/js/exceptions.js +0 -26
  77. package/source/js/gesture-events.js +0 -190
  78. package/source/js/grid-handler.js +0 -122
  79. package/source/js/iiif-source-adapter.js +0 -63
  80. package/source/js/image-cache.js +0 -113
  81. package/source/js/image-manifest.js +0 -157
  82. package/source/js/image-request-handler.js +0 -76
  83. package/source/js/interpolate-animation.js +0 -122
  84. package/source/js/page-layouts/book-layout.js +0 -161
  85. package/source/js/page-layouts/grid-layout.js +0 -97
  86. package/source/js/page-layouts/index.js +0 -38
  87. package/source/js/page-layouts/page-dimensions.js +0 -9
  88. package/source/js/page-layouts/singles-layout.js +0 -27
  89. package/source/js/page-overlay-manager.js +0 -102
  90. package/source/js/page-tools-overlay.js +0 -95
  91. package/source/js/parse-iiif-manifest.js +0 -302
  92. package/source/js/plugins/_filters.js +0 -679
  93. package/source/js/plugins/download.js +0 -83
  94. package/source/js/plugins/manipulation.js +0 -837
  95. package/source/js/plugins/metadata.js +0 -190
  96. package/source/js/renderer.js +0 -584
  97. package/source/js/settings-view.js +0 -30
  98. package/source/js/tile-coverage-map.js +0 -25
  99. package/source/js/toolbar.js +0 -573
  100. package/source/js/utils/dragscroll.js +0 -106
  101. package/source/js/utils/elt.js +0 -94
  102. package/source/js/utils/events.js +0 -190
  103. package/source/js/utils/get-scrollbar-width.js +0 -29
  104. package/source/js/utils/hash-params.js +0 -86
  105. package/source/js/utils/parse-label-value.js +0 -34
  106. package/source/js/utils/vanilla.kinetic.js +0 -527
  107. package/source/js/validation-runner.js +0 -177
  108. package/source/js/viewer-core.js +0 -1514
  109. package/source/js/viewport.js +0 -143
  110. package/test/_setup.js +0 -13
  111. package/test/composite-image_test.js +0 -94
  112. package/test/diva_test.js +0 -43
  113. package/test/hash-params_test.js +0 -221
  114. package/test/image-cache_test.js +0 -106
  115. package/test/main.js +0 -6
  116. package/test/manifests/beromunsterManifest.json +0 -15514
  117. package/test/manifests/iiifv2.json +0 -11032
  118. package/test/manifests/iiifv2pages.json +0 -30437
  119. package/test/manifests/iiifv3.json +0 -10965
  120. package/test/navigation_test.js +0 -355
  121. package/test/parse-iiif-manifest_test.js +0 -68
  122. package/test/public_test.js +0 -881
  123. package/test/settings_test.js +0 -487
  124. package/test/utils/book-layout_test.js +0 -148
  125. package/test/utils/elt_test.js +0 -102
  126. package/test/utils/events_test.js +0 -245
  127. package/test/utils/hash-params_test.js +0 -79
  128. package/test/utils/parse-label-value_test.js +0 -45
  129. package/test/z_plugins_test.js +0 -180
  130. package/webpack.config.js +0 -58
  131. package/webpack.config.test.js +0 -45
@@ -1,1514 +0,0 @@
1
- import { elt } from './utils/elt';
2
- import getScrollbarWidth from './utils/get-scrollbar-width';
3
- import gestureEvents from './gesture-events';
4
- import diva from './diva-global';
5
- import DocumentHandler from './document-handler';
6
- import GridHandler from './grid-handler';
7
- import PageOverlayManager from './page-overlay-manager';
8
- import Renderer from './renderer';
9
- import getPageLayouts from './page-layouts';
10
- import createSettingsView from './settings-view';
11
- import ValidationRunner from './validation-runner';
12
- import Viewport from './viewport';
13
-
14
- const debug = require('debug')('diva:ViewerCore');
15
-
16
- function generateId() {
17
- return generateId.counter++;
18
- }
19
- generateId.counter = 1;
20
-
21
-
22
- // Define validations
23
- const optionsValidations = [
24
- {
25
- key: 'goDirectlyTo',
26
- validate: (value, settings) =>
27
- {
28
- if (value < 0 || value >= settings.manifest.pages.length)
29
- return 0;
30
- }
31
- },
32
- {
33
- key: 'minPagesPerRow',
34
- validate: (value) =>
35
- {
36
- return Math.max(2, value);
37
- }
38
- },
39
- {
40
- key: 'maxPagesPerRow',
41
- validate: (value, settings) =>
42
- {
43
- return Math.max(value, settings.minPagesPerRow);
44
- }
45
- },
46
- {
47
- key: 'pagesPerRow',
48
- validate: (value, settings) =>
49
- {
50
- // Default to the maximum
51
- if (value < settings.minPagesPerRow || value > settings.maxPagesPerRow)
52
- return settings.maxPagesPerRow;
53
- }
54
- },
55
- {
56
- key: 'maxZoomLevel',
57
- validate: (value, settings, config) =>
58
- {
59
- // Changing this value isn't really an error, it just depends on the
60
- // source manifest
61
- config.suppressWarning();
62
-
63
- if (value < 0 || value > settings.manifest.maxZoom)
64
- return settings.manifest.maxZoom;
65
- }
66
- },
67
- {
68
- key: 'minZoomLevel',
69
- validate: (value, settings, config) =>
70
- {
71
- // Changes based on the manifest value shouldn't trigger a
72
- // warning
73
- if (value > settings.manifest.maxZoom)
74
- {
75
- config.suppressWarning();
76
- return 0;
77
- }
78
-
79
- if (value < 0 || value > settings.maxZoomLevel)
80
- return 0;
81
- }
82
- },
83
- {
84
- key: 'zoomLevel',
85
- validate: (value, settings, config) =>
86
- {
87
- if (value > settings.manifest.maxZoom)
88
- {
89
- config.suppressWarning();
90
- return 0;
91
- }
92
-
93
- if (value < settings.minZoomLevel || value > settings.maxZoomLevel)
94
- return settings.minZoomLevel;
95
- }
96
- }
97
- ];
98
-
99
- export default class ViewerCore
100
- {
101
- constructor (element, options, publicInstance)
102
- {
103
- this.parentObject = element;
104
- this.publicInstance = publicInstance;
105
-
106
- // Things that cannot be changed because of the way they are used by the script
107
- // Many of these are declared with arbitrary values that are changed later on
108
- this.viewerState = {
109
- currentPageIndices: [0], // The visible pages in the viewport
110
- activePageIndex: 0, // The current 'active' page in the viewport
111
- horizontalOffset: 0, // Distance from the center of the diva element to the top of the current page
112
- horizontalPadding: 0, // Either the fixed padding or adaptive padding
113
- ID: null, // The prefix of the IDs of the elements (usually 1-diva-)
114
- initialKeyScroll: false, // Holds the initial state of enableKeyScroll
115
- initialSpaceScroll: false, // Holds the initial state of enableSpaceScroll
116
- innerElement: null, // The native .diva-outer DOM object
117
- innerObject: {}, // document.getElementById(settings.ID + 'inner'), for selecting the .diva-inner element
118
- isActiveDiva: true, // In the case that multiple diva panes exist on the same page, this should have events funneled to it.
119
- isScrollable: true, // Used in enable/disableScrollable public methods
120
- isZooming: false, // Flag to keep track of whether zooming is still in progress, for handleZoom
121
- loaded: false, // A flag for when everything is loaded and ready to go.
122
- manifest: null,
123
- mobileWebkit: false, // Checks if the user is on a touch device (iPad/iPod/iPhone/Android)
124
- numPages: 0, // Number of pages in the array
125
- oldZoomLevel: -1, // Holds the previous zoom level after zooming in or out
126
- options: options,
127
- outerElement: null, // The native .diva-outer DOM object
128
- outerObject: {}, // document.getElementById(settings.ID + 'outer'), for selecting the .diva-outer element
129
- pageOverlays: new PageOverlayManager(),
130
- pageTools: [], // The plugins which are enabled as page tools
131
- parentObject: this.parentObject, // JQuery object referencing the parent element
132
- pendingManifestRequest: null, // Reference to the xhr request retrieving the manifest. Used to cancel the request on destroy()
133
- pluginInstances: [], // Filled with the enabled plugins from the registry
134
- renderer: null,
135
- resizeTimer: -1, // Holds the ID of the timeout used when resizing the window (for clearing)
136
- scrollbarWidth: 0, // Set to the actual scrollbar width in init()
137
- selector: '', // Uses the generated ID prefix to easily select elements
138
- throbberTimeoutID: -1, // Holds the ID of the throbber loading timeout
139
- toolbar: null, // Holds an object with some toolbar-related functions
140
- verticalOffset: 0, // Distance from the center of the diva element to the left side of the current page
141
- verticalPadding: 0, // Either the fixed padding or adaptive padding
142
- viewHandler: null,
143
- viewport: null, // Object caching the viewport dimensions
144
- viewportElement: null,
145
- viewportObject: null,
146
- zoomDuration: 400
147
- };
148
-
149
- this.settings = createSettingsView([options, this.viewerState]);
150
-
151
- // Generate an ID that can be used as a prefix for all the other IDs
152
- const idNumber = generateId();
153
- this.viewerState.ID = 'diva-' + idNumber + '-';
154
- this.viewerState.selector = this.settings.ID;
155
-
156
- // Aliases for compatibility
157
- Object.defineProperties(this.settings, {
158
- // Height of the document viewer pane
159
- panelHeight: {
160
- get: () =>
161
- {
162
- return this.viewerState.viewport.height;
163
- }
164
- },
165
- // Width of the document viewer pane
166
- panelWidth: {
167
- get: () =>
168
- {
169
- return this.viewerState.viewport.width;
170
- }
171
- }
172
- });
173
-
174
- this.optionsValidator = new ValidationRunner({
175
- additionalProperties: [
176
- {
177
- key: 'manifest',
178
- get: () =>
179
- {
180
- return this.viewerState.manifest;
181
- }
182
- }
183
- ],
184
-
185
- validations: optionsValidations
186
- });
187
-
188
- this.viewerState.scrollbarWidth = getScrollbarWidth();
189
-
190
- // If window.orientation is defined, then it's probably mobileWebkit
191
- this.viewerState.mobileWebkit = window.orientation !== undefined;
192
-
193
- if (options.hashParamSuffix === null)
194
- {
195
- // Omit the suffix from the first instance
196
- if (idNumber === 1)
197
- options.hashParamSuffix = '';
198
- else
199
- options.hashParamSuffix = idNumber + '';
200
- }
201
-
202
- // Create the inner and outer panels
203
- const innerElem = elt('div', this.elemAttrs('inner', { class: 'diva-inner' }));
204
- const viewportElem = elt('div', this.elemAttrs('viewport'), innerElem);
205
- const outerElem = elt('div', this.elemAttrs('outer'),
206
- viewportElem,
207
- elt('div', this.elemAttrs('throbber'),
208
- [
209
- elt('div', { class: 'cube cube1' }),
210
- elt('div', { class: 'cube cube2' }),
211
- elt('div', { class: 'cube cube3' }),
212
- elt('div', { class: 'cube cube4' }),
213
- elt('div', { class: 'cube cube5' }),
214
- elt('div', { class: 'cube cube6' }),
215
- elt('div', { class: 'cube cube7' }),
216
- elt('div', { class: 'cube cube8' }),
217
- elt('div', { class: 'cube cube9' }),
218
- ]
219
- ));
220
-
221
- this.viewerState.innerElement = innerElem;
222
- this.viewerState.viewportElement = viewportElem;
223
- this.viewerState.outerElement = outerElem;
224
-
225
- this.viewerState.innerObject = innerElem;
226
- this.viewerState.viewportObject = viewportElem;
227
- this.viewerState.outerObject = outerElem;
228
-
229
- this.settings.parentObject.append(outerElem);
230
-
231
- this.viewerState.viewport = new Viewport(this.viewerState.viewportElement, {
232
- intersectionTolerance: this.settings.viewportMargin
233
- });
234
-
235
- this.boundScrollFunction = this.scrollFunction.bind(this);
236
- this.boundEscapeListener = this.escapeListener.bind(this);
237
-
238
- // Do all the plugin initialisation
239
- this.initPlugins();
240
- this.handleEvents();
241
-
242
- // Show the throbber while waiting for the manifest to load
243
- this.showThrobber();
244
- }
245
-
246
- isValidOption (key, value)
247
- {
248
- return this.optionsValidator.isValid(key, value, this.viewerState.options);
249
- }
250
-
251
- elemAttrs (ident, base)
252
- {
253
- const attrs = {
254
- id: this.settings.ID + ident,
255
- class: 'diva-' + ident
256
- };
257
-
258
- if (base)
259
- return Object.assign(attrs, base);
260
- else
261
- return attrs;
262
- }
263
-
264
- getPageData (pageIndex, attribute)
265
- {
266
- return this.settings.manifest.pages[pageIndex].d[this.settings.zoomLevel][attribute];
267
- }
268
-
269
- // Reset some settings and empty the viewport
270
- clearViewer ()
271
- {
272
- this.viewerState.viewport.top = 0;
273
-
274
- // Clear all the timeouts to prevent undesired pages from loading
275
- clearTimeout(this.viewerState.resizeTimer);
276
- }
277
-
278
- hasChangedOption (options, key)
279
- {
280
- return key in options && options[key] !== this.settings[key];
281
- }
282
-
283
- //Shortcut for closing fullscreen with the escape key
284
- escapeListener (e)
285
- {
286
- if (e.keyCode === 27)
287
- {
288
- this.publicInstance.leaveFullscreenMode();
289
- }
290
- }
291
-
292
- /**
293
- * Update settings to match the specified options. Load the viewer,
294
- * fire appropriate events for changed options.
295
- */
296
- reloadViewer (newOptions)
297
- {
298
- const queuedEvents = [];
299
-
300
- newOptions = this.optionsValidator.getValidatedOptions(this.settings, newOptions);
301
-
302
- // Set the zoom level if valid and fire a ZoomLevelDidChange event
303
- if (this.hasChangedOption(newOptions, 'zoomLevel'))
304
- {
305
- this.viewerState.oldZoomLevel = this.settings.zoomLevel;
306
- this.viewerState.options.zoomLevel = newOptions.zoomLevel;
307
- queuedEvents.push(["ZoomLevelDidChange", newOptions.zoomLevel]);
308
- }
309
-
310
- // Set the pages per row if valid and fire an event
311
- if (this.hasChangedOption(newOptions, 'pagesPerRow'))
312
- {
313
- this.viewerState.options.pagesPerRow = newOptions.pagesPerRow;
314
- queuedEvents.push(["GridRowNumberDidChange", newOptions.pagesPerRow]);
315
- }
316
-
317
- // Update verticallyOriented (no event fired)
318
- if (this.hasChangedOption(newOptions, 'verticallyOriented'))
319
- this.viewerState.options.verticallyOriented = newOptions.verticallyOriented;
320
-
321
- // Show/Hide non-paged pages
322
- if (this.hasChangedOption(newOptions, 'showNonPagedPages'))
323
- {
324
- this.viewerState.options.showNonPagedPages = newOptions.showNonPagedPages;
325
- }
326
-
327
- // Update page position (no event fired here)
328
- if ('goDirectlyTo' in newOptions)
329
- {
330
- this.viewerState.options.goDirectlyTo = newOptions.goDirectlyTo;
331
-
332
- if ('verticalOffset' in newOptions)
333
- this.viewerState.verticalOffset = newOptions.verticalOffset;
334
-
335
- if ('horizontalOffset' in newOptions)
336
- this.viewerState.horizontalOffset = newOptions.horizontalOffset;
337
- }
338
- else
339
- {
340
- // Otherwise the default is to remain on the current page
341
- this.viewerState.options.goDirectlyTo = this.settings.activePageIndex;
342
- }
343
-
344
- if (this.hasChangedOption(newOptions, 'inGrid') || this.hasChangedOption(newOptions, 'inBookLayout'))
345
- {
346
- if ('inGrid' in newOptions)
347
- this.viewerState.options.inGrid = newOptions.inGrid;
348
-
349
- if ('inBookLayout' in newOptions)
350
- this.viewerState.options.inBookLayout = newOptions.inBookLayout;
351
-
352
- queuedEvents.push(["ViewDidSwitch", this.settings.inGrid]);
353
- }
354
-
355
- // Note: prepareModeChange() depends on inGrid and the vertical/horizontalOffset (for now)
356
- if (this.hasChangedOption(newOptions, 'inFullscreen'))
357
- {
358
- this.viewerState.options.inFullscreen = newOptions.inFullscreen;
359
- this.prepareModeChange(newOptions);
360
- queuedEvents.push(["ModeDidSwitch", this.settings.inFullscreen]);
361
- }
362
-
363
- this.clearViewer();
364
- this.updateViewHandlerAndRendering();
365
-
366
- if (this.viewerState.renderer)
367
- {
368
- // TODO: The usage of padding variables is still really
369
- // messy and inconsistent
370
- const rendererConfig = {
371
- pageLayouts: getPageLayouts(this.settings),
372
- padding: this.getPadding(),
373
- maxZoomLevel: this.settings.inGrid ? null : this.viewerState.manifest.maxZoom,
374
- verticallyOriented: this.settings.verticallyOriented || this.settings.inGrid,
375
- };
376
-
377
- const viewportPosition = {
378
- zoomLevel: this.settings.inGrid ? null : this.settings.zoomLevel,
379
- anchorPage: this.settings.goDirectlyTo,
380
- verticalOffset: this.viewerState.verticalOffset,
381
- horizontalOffset: this.viewerState.horizontalOffset
382
- };
383
-
384
- const sourceProvider = this.getCurrentSourceProvider();
385
-
386
- if (debug.enabled)
387
- {
388
- const serialized = Object.keys(rendererConfig)
389
- .filter(function (key)
390
- {
391
- // Too long
392
- return key !== 'pageLayouts' && key !== 'padding';
393
- })
394
- .map(function (key)
395
- {
396
- const value = rendererConfig[key];
397
- return key + ': ' + JSON.stringify(value);
398
- })
399
- .join(', ');
400
-
401
- debug('reload with %s', serialized);
402
- }
403
-
404
- this.viewerState.renderer.load(rendererConfig, viewportPosition, sourceProvider);
405
- }
406
-
407
- queuedEvents.forEach( (params) =>
408
- {
409
- this.publish.apply(this, params);
410
- });
411
-
412
- return true;
413
- }
414
-
415
- // Handles switching in and out of fullscreen mode
416
- prepareModeChange (options)
417
- {
418
- // Toggle the classes
419
- const changeClass = options.inFullscreen ? 'add' : 'remove';
420
- this.viewerState.outerObject.classList[changeClass]('diva-fullscreen');
421
- document.body.classList[changeClass]('diva-hide-scrollbar');
422
- this.settings.parentObject.classList[changeClass]('diva-full-width');
423
-
424
- // Adjust Diva's internal panel size, keeping the old values
425
- const storedHeight = this.settings.panelHeight;
426
- const storedWidth = this.settings.panelWidth;
427
- this.viewerState.viewport.invalidate();
428
-
429
- // If this isn't the original load, the offsets matter, and the position isn't being changed...
430
- if (!this.viewerState.loaded && !this.settings.inGrid && !('verticalOffset' in options))
431
- {
432
- //get the updated panel size
433
- const newHeight = this.settings.panelHeight;
434
- const newWidth = this.settings.panelWidth;
435
-
436
- //and re-center the new panel on the same point
437
- this.viewerState.verticalOffset += ((storedHeight - newHeight) / 2);
438
- this.viewerState.horizontalOffset += ((storedWidth - newWidth) / 2);
439
- }
440
-
441
- //turn on/off escape key listener
442
- if (options.inFullscreen)
443
- document.addEventListener('keyup', this.boundEscapeListener);
444
- else
445
- document.removeEventListener('keyup', this.boundEscapeListener);
446
- }
447
-
448
- // Update the view handler and the view rendering for the current view
449
- updateViewHandlerAndRendering ()
450
- {
451
- const Handler = this.settings.inGrid ? GridHandler : DocumentHandler;
452
-
453
- if (this.viewerState.viewHandler && !(this.viewerState.viewHandler instanceof Handler))
454
- {
455
- this.viewerState.viewHandler.destroy();
456
- this.viewerState.viewHandler = null;
457
- }
458
-
459
- if (!this.viewerState.viewHandler)
460
- this.viewerState.viewHandler = new Handler(this);
461
-
462
- if (!this.viewerState.renderer)
463
- this.initializeRenderer();
464
- }
465
-
466
- // TODO: This could probably be done upon ViewerCore initialization
467
- initializeRenderer ()
468
- {
469
- const compatErrors = Renderer.getCompatibilityErrors();
470
-
471
- if (compatErrors)
472
- {
473
- this.showError(compatErrors);
474
- }
475
- else
476
- {
477
- const options = {
478
- viewport: this.viewerState.viewport,
479
- outerElement: this.viewerState.outerElement,
480
- innerElement: this.viewerState.innerElement,
481
- settings: this.settings
482
- };
483
-
484
- const hooks = {
485
- onViewWillLoad: () =>
486
- {
487
- this.viewerState.viewHandler.onViewWillLoad();
488
- },
489
- onViewDidLoad: () =>
490
- {
491
- this.updatePageOverlays();
492
- this.viewerState.viewHandler.onViewDidLoad();
493
- },
494
- onViewDidUpdate: (pages, targetPage) =>
495
- {
496
- this.updatePageOverlays();
497
- this.viewerState.viewHandler.onViewDidUpdate(pages, targetPage);
498
- },
499
- onViewDidTransition: () =>
500
- {
501
- this.updatePageOverlays();
502
- },
503
- onPageWillLoad: (pageIndex) =>
504
- {
505
- this.publish('PageWillLoad', pageIndex);
506
- },
507
- onZoomLevelWillChange: (zoomLevel) =>
508
- {
509
- this.publish('ZoomLevelWillChange', zoomLevel);
510
- }
511
- };
512
-
513
- this.viewerState.renderer = new Renderer(options, hooks);
514
- }
515
- }
516
-
517
- getCurrentSourceProvider ()
518
- {
519
- if (this.settings.inGrid)
520
- {
521
- const gridSourceProvider = {
522
- getAllZoomLevelsForPage: (page) =>
523
- {
524
- return [gridSourceProvider.getBestZoomLevelForPage(page)];
525
- },
526
- getBestZoomLevelForPage: (page) =>
527
- {
528
- const url = this.settings.manifest.getPageImageURL(page.index, {
529
- width: page.dimensions.width
530
- });
531
-
532
- return {
533
- zoomLevel: 1, // FIXME
534
- rows: 1,
535
- cols: 1,
536
- tiles: [{
537
- url: url,
538
- zoomLevel: 1, // FIXME
539
- row: 0,
540
- col: 0,
541
- dimensions: page.dimensions,
542
- offset: {
543
- top: 0,
544
- left: 0
545
- }
546
- }]
547
- };
548
- }
549
- };
550
-
551
- return gridSourceProvider;
552
- }
553
-
554
- const tileDimensions = {
555
- width: this.settings.tileWidth,
556
- height: this.settings.tileHeight
557
- };
558
-
559
- return {
560
- getBestZoomLevelForPage: (page) =>
561
- {
562
- return this.settings.manifest.getPageImageTiles(page.index, Math.ceil(this.settings.zoomLevel), tileDimensions);
563
- },
564
- getAllZoomLevelsForPage: (page) =>
565
- {
566
- const levels = [];
567
- const levelCount = this.viewerState.manifest.maxZoom;
568
-
569
- for (let level=0; level <= levelCount; level++)
570
- {
571
- levels.push(this.settings.manifest.getPageImageTiles(page.index, level, tileDimensions));
572
- }
573
-
574
- levels.reverse();
575
-
576
- return levels;
577
- }
578
- };
579
- }
580
-
581
- getPadding ()
582
- {
583
- let topPadding, leftPadding;
584
- let docVPadding, docHPadding;
585
-
586
- if (this.settings.inGrid)
587
- {
588
- docVPadding = this.settings.fixedPadding;
589
- topPadding = leftPadding = docHPadding = 0;
590
- }
591
- else
592
- {
593
- topPadding = this.settings.verticallyOriented ? this.viewerState.verticalPadding : 0;
594
- leftPadding = this.settings.verticallyOriented ? 0 : this.viewerState.horizontalPadding;
595
-
596
- docVPadding = this.settings.verticallyOriented ? 0 : this.viewerState.verticalPadding;
597
- docHPadding = this.settings.verticallyOriented ? this.viewerState.horizontalPadding : 0;
598
- }
599
-
600
- return {
601
- document: {
602
- top: docVPadding,
603
- bottom: docVPadding,
604
- left: docHPadding,
605
- right: docHPadding
606
- },
607
- page: {
608
- top: topPadding,
609
- bottom: 0,
610
- left: leftPadding,
611
- right: 0
612
- }
613
- };
614
- }
615
-
616
- updatePageOverlays ()
617
- {
618
- this.viewerState.pageOverlays.updateOverlays(
619
- this.viewerState.renderer.getRenderedPages()
620
- );
621
- }
622
-
623
- // Called to handle any zoom level
624
- handleZoom (newZoomLevel, focalPoint)
625
- {
626
- // If the zoom level provided is invalid, return false
627
- if (!this.isValidOption('zoomLevel', newZoomLevel))
628
- return false;
629
-
630
- // While zooming, don't update scroll offsets based on the scaled version of diva-inner
631
- this.viewerState.viewportObject.removeEventListener('scroll', this.boundScrollFunction);
632
-
633
- // If no focal point was given, zoom on the center of the viewport
634
- if (!focalPoint)
635
- {
636
- const viewport = this.viewerState.viewport;
637
- const currentRegion = this.viewerState.renderer.layout.getPageRegion(this.settings.activePageIndex);
638
-
639
- focalPoint = {
640
- anchorPage: this.settings.activePageIndex,
641
- offset: {
642
- left: (viewport.width / 2) - (currentRegion.left - viewport.left),
643
- top: (viewport.height / 2) - (currentRegion.top - viewport.top)
644
- }
645
- };
646
- }
647
-
648
- const pageRegion = this.viewerState.renderer.layout.getPageRegion(focalPoint.anchorPage);
649
-
650
- // calculate distance from cursor coordinates to center of viewport
651
- const focalXToCenter = (pageRegion.left + focalPoint.offset.left) -
652
- (this.settings.viewport.left + (this.settings.viewport.width / 2));
653
- const focalYToCenter = (pageRegion.top + focalPoint.offset.top) -
654
- (this.settings.viewport.top + (this.settings.viewport.height / 2));
655
-
656
- const getPositionForZoomLevel = (zoomLevel, initZoom) =>
657
- {
658
- const zoomRatio = Math.pow(2, zoomLevel - initZoom);
659
-
660
- //TODO(jeromepl): Calculate position from page top left to viewport top left
661
- // calculate horizontal/verticalOffset: distance from viewport center to page upper left corner
662
- const horizontalOffset = (focalPoint.offset.left * zoomRatio) - focalXToCenter;
663
- const verticalOffset = (focalPoint.offset.top * zoomRatio) - focalYToCenter;
664
-
665
- return {
666
- zoomLevel: zoomLevel,
667
- anchorPage: focalPoint.anchorPage,
668
- verticalOffset: verticalOffset,
669
- horizontalOffset: horizontalOffset
670
- };
671
- };
672
-
673
- this.viewerState.options.zoomLevel = newZoomLevel;
674
- let initialZoomLevel = this.viewerState.oldZoomLevel;
675
- this.viewerState.oldZoomLevel = this.settings.zoomLevel;
676
- const endPosition = getPositionForZoomLevel(newZoomLevel, initialZoomLevel);
677
- this.viewerState.options.goDirectlyTo = endPosition.anchorPage;
678
- this.viewerState.verticalOffset = endPosition.verticalOffset;
679
- this.viewerState.horizontalOffset = endPosition.horizontalOffset;
680
-
681
- this.viewerState.renderer.transitionViewportPosition({
682
- duration: this.settings.zoomDuration,
683
- parameters: {
684
- zoomLevel: {
685
- from: initialZoomLevel,
686
- to: newZoomLevel
687
- }
688
- },
689
- getPosition: (parameters) =>
690
- {
691
- return getPositionForZoomLevel(parameters.zoomLevel, initialZoomLevel);
692
- },
693
- onEnd: (info) =>
694
- {
695
- this.viewerState.viewportObject.addEventListener('scroll', this.boundScrollFunction);
696
-
697
- if (info.interrupted)
698
- this.viewerState.oldZoomLevel = newZoomLevel;
699
- }
700
- });
701
-
702
- // Deactivate zoom buttons while zooming
703
- let zoomInButton = document.getElementById(this.settings.selector + 'zoom-in-button');
704
- let zoomOutButton = document.getElementById(this.settings.selector + 'zoom-out-button');
705
- zoomInButton.disabled = true;
706
- zoomOutButton.disabled = true;
707
- setTimeout(() =>
708
- {
709
- zoomInButton.disabled = false;
710
- zoomOutButton.disabled = false;
711
- }, this.settings.zoomDuration);
712
-
713
- // Send off the zoom level did change event.
714
- this.publish("ZoomLevelDidChange", newZoomLevel);
715
-
716
- return true;
717
- }
718
-
719
- /*
720
- Gets the Y-offset for a specific point on a specific page
721
- Acceptable values for "anchor":
722
- "top" (default) - will anchor top of the page to the top of the diva-outer element
723
- "bottom" - top, s/top/bottom
724
- "center" - will center the page on the diva element
725
- Returned value will be the distance from the center of the diva-outer element to the top of the current page for the specified anchor
726
- */
727
- getYOffset (pageIndex, anchor)
728
- {
729
- let pidx = (typeof(pageIndex) === "undefined" ? this.settings.activePageIndex : pageIndex);
730
-
731
- if (anchor === "center" || anchor === "centre") //how you can tell an American coded this
732
- {
733
- return parseInt(this.getPageData(pidx, "h") / 2, 10);
734
- }
735
- else if (anchor === "bottom")
736
- {
737
- return parseInt(this.getPageData(pidx, "h") - this.settings.panelHeight / 2, 10);
738
- }
739
- else
740
- {
741
- return parseInt(this.settings.panelHeight / 2, 10);
742
- }
743
- }
744
-
745
- //Same as getYOffset with "left" and "right" as acceptable values instead of "top" and "bottom"
746
- getXOffset (pageIndex, anchor)
747
- {
748
- let pidx = (typeof(pageIndex) === "undefined" ? this.settings.activePageIndex : pageIndex);
749
-
750
- if (anchor === "left")
751
- {
752
- return parseInt(this.settings.panelWidth / 2, 10);
753
- }
754
- else if (anchor === "right")
755
- {
756
- return parseInt(this.getPageData(pidx, "w") - this.settings.panelWidth / 2, 10);
757
- }
758
- else
759
- {
760
- return parseInt(this.getPageData(pidx, "w") / 2, 10);
761
- }
762
- }
763
-
764
- // updates panelHeight/panelWidth on resize
765
- updatePanelSize ()
766
- {
767
- this.viewerState.viewport.invalidate();
768
-
769
- // FIXME(wabain): This should really only be called after initial load
770
- if (this.viewerState.renderer)
771
- {
772
- this.updateOffsets();
773
- this.viewerState.renderer.goto(this.settings.activePageIndex, this.viewerState.verticalOffset, this.viewerState.horizontalOffset);
774
- }
775
-
776
- return true;
777
- }
778
-
779
- updateOffsets ()
780
- {
781
- const pageOffset = this.viewerState.renderer.layout.getPageToViewportCenterOffset(this.settings.activePageIndex, this.viewerState.viewport);
782
-
783
- if (pageOffset)
784
- {
785
- this.viewerState.horizontalOffset = pageOffset.x;
786
- this.viewerState.verticalOffset = pageOffset.y;
787
- }
788
- }
789
-
790
- // Bind mouse events (drag to scroll, double-click)
791
- bindMouseEvents ()
792
- {
793
- // Set drag scroll on the viewport object
794
- this.viewerState.viewportObject.classList.add('dragscroll');
795
-
796
- gestureEvents.onDoubleClick(this.viewerState.viewportObject, (event, coords) =>
797
- {
798
- debug('Double click at %s, %s', coords.left, coords.top);
799
- this.viewerState.viewHandler.onDoubleClick(event, coords);
800
- });
801
- }
802
-
803
- onResize ()
804
- {
805
- this.updatePanelSize();
806
- // Cancel any previously-set resize timeouts
807
- clearTimeout(this.viewerState.resizeTimer);
808
-
809
- this.viewerState.resizeTimer = setTimeout( () =>
810
- {
811
- const pageOffset = this.viewerState.renderer.layout.getPageToViewportCenterOffset(this.settings.activePageIndex, this.viewerState.viewport);
812
-
813
- if (pageOffset)
814
- {
815
- this.reloadViewer({
816
- goDirectlyTo: this.settings.activePageIndex,
817
- verticalOffset: pageOffset.y,
818
- horizontalOffset: pageOffset.x
819
- });
820
- }
821
- else
822
- {
823
- this.reloadViewer({
824
- goDirectlyTo: this.settings.activePageIndex
825
- });
826
- }
827
- }, 200);
828
- }
829
-
830
- // Bind touch and orientation change events
831
- bindTouchEvents ()
832
- {
833
- // Block the user from moving the window only if it's not integrated
834
- if (this.settings.blockMobileMove)
835
- {
836
- document.body.addEventListener('touchmove', (event) =>
837
- {
838
- const e = event.originalEvent;
839
- e.preventDefault();
840
-
841
- return false;
842
- });
843
- }
844
-
845
- // Touch events for swiping in the viewport to scroll pages
846
- // this.viewerState.viewportObject.addEventListener('scroll', this.scrollFunction.bind(this));
847
-
848
- gestureEvents.onPinch(this.viewerState.viewportObject, function (event, coords, start, end)
849
- {
850
- debug('Pinch %s at %s, %s', end - start, coords.left, coords.top);
851
- this.viewerState.viewHandler.onPinch(event, coords, start, end);
852
- });
853
-
854
- gestureEvents.onDoubleTap(this.viewerState.viewportObject, function (event, coords)
855
- {
856
- debug('Double tap at %s, %s', coords.left, coords.top);
857
- this.viewerState.viewHandler.onDoubleClick(event, coords);
858
- });
859
- }
860
-
861
- // Handle the scroll
862
- scrollFunction ()
863
- {
864
- const previousTopScroll = this.viewerState.viewport.top;
865
- const previousLeftScroll = this.viewerState.viewport.left;
866
-
867
- let direction;
868
-
869
- this.viewerState.viewport.invalidate();
870
-
871
- const newScrollTop = this.viewerState.viewport.top;
872
- const newScrollLeft = this.viewerState.viewport.left;
873
-
874
- if (this.settings.verticallyOriented || this.settings.inGrid)
875
- direction = newScrollTop - previousTopScroll;
876
- else
877
- direction = newScrollLeft - previousLeftScroll;
878
-
879
- this.viewerState.renderer.adjust();
880
-
881
- const primaryScroll = (this.settings.verticallyOriented || this.settings.inGrid) ? newScrollTop : newScrollLeft;
882
-
883
- this.publish("ViewerDidScroll", primaryScroll);
884
-
885
- if (direction > 0)
886
- {
887
- this.publish("ViewerDidScrollDown", primaryScroll);
888
- }
889
- else if (direction < 0)
890
- {
891
- this.publish("ViewerDidScrollUp", primaryScroll);
892
- }
893
-
894
- this.updateOffsets();
895
- }
896
-
897
- // Binds most of the event handlers (some more in createToolbar)
898
- handleEvents ()
899
- {
900
- // Change the cursor for dragging
901
- this.viewerState.innerObject.addEventListener('mousedown', () =>
902
- {
903
- this.viewerState.innerObject.classList.add('diva-grabbing');
904
- });
905
-
906
- this.viewerState.innerObject.addEventListener('mouseup', () =>
907
- {
908
- this.viewerState.innerObject.classList.remove('diva-grabbing');
909
- });
910
-
911
- this.bindMouseEvents();
912
- this.viewerState.viewportObject.addEventListener('scroll', this.boundScrollFunction);
913
-
914
- const upArrowKey = 38, downArrowKey = 40, leftArrowKey = 37, rightArrowKey = 39, spaceKey = 32, pageUpKey = 33, pageDownKey = 34, homeKey = 36, endKey = 35;
915
-
916
- // Catch the key presses in document
917
- document.addEventListener('keydown.diva', (event) =>
918
- {
919
- if (!this.viewerState.isActiveDiva)
920
- return true;
921
-
922
- // Space or page down - go to the next page
923
- if ((this.settings.enableSpaceScroll && !event.shiftKey && event.keyCode === spaceKey) || (this.settings.enableKeyScroll && event.keyCode === pageDownKey))
924
- {
925
- this.viewerState.viewport.top += this.settings.panelHeight;
926
- return false;
927
- }
928
- else if (!this.settings.enableSpaceScroll && event.keyCode === spaceKey)
929
- {
930
- event.preventDefault();
931
- }
932
-
933
- if (this.settings.enableKeyScroll)
934
- {
935
- // Don't steal keyboard shortcuts (metaKey = command [OS X], super [Win/Linux])
936
- if (event.shiftKey || event.ctrlKey || event.metaKey)
937
- return true;
938
-
939
- switch (event.keyCode)
940
- {
941
- case pageUpKey:
942
- // Page up - go to the previous page
943
- this.viewerState.viewport.top -= this.settings.panelHeight;
944
- return false;
945
-
946
- case upArrowKey:
947
- // Up arrow - scroll up
948
- this.viewerState.viewport.top -= this.settings.arrowScrollAmount;
949
- return false;
950
-
951
- case downArrowKey:
952
- // Down arrow - scroll down
953
- this.viewerState.viewport.top += this.settings.arrowScrollAmount;
954
- return false;
955
-
956
- case leftArrowKey:
957
- // Left arrow - scroll left
958
- this.viewerState.viewport.left -= this.settings.arrowScrollAmount;
959
- return false;
960
-
961
- case rightArrowKey:
962
- // Right arrow - scroll right
963
- this.viewerState.viewport.left += this.settings.arrowScrollAmount;
964
- return false;
965
-
966
- case homeKey:
967
- // Home key - go to the beginning of the document
968
- this.viewerState.viewport.top = 0;
969
- return false;
970
-
971
- case endKey:
972
- // End key - go to the end of the document
973
- // Count on the viewport coordinate value being normalized
974
- if (this.settings.verticallyOriented)
975
- this.viewerState.viewport.top = Infinity;
976
- else
977
- this.viewerState.viewport.left = Infinity;
978
-
979
- return false;
980
-
981
- default:
982
- return true;
983
- }
984
- }
985
- return true;
986
- });
987
-
988
- diva.Events.subscribe('ViewerDidTerminate', function()
989
- {
990
- document.removeEventListener('keydown.diva');
991
- }, this.settings.ID);
992
-
993
- // this.bindTouchEvents();
994
-
995
- // Handle window resizing events
996
- window.addEventListener('resize', this.onResize.bind(this), false);
997
-
998
- diva.Events.subscribe('ViewerDidTerminate', function()
999
- {
1000
- window.removeEventListener('resize', this.onResize, false);
1001
- }, this.settings.ID);
1002
-
1003
- // Handle orientation change separately
1004
- if ('onorientationchange' in window)
1005
- {
1006
- window.addEventListener('orientationchange', this.onResize, false);
1007
-
1008
- diva.Events.subscribe('ViewerDidTerminate', function()
1009
- {
1010
- window.removeEventListener('orientationchange', this.onResize, false);
1011
- }, this.settings.ID);
1012
- }
1013
-
1014
- diva.Events.subscribe('PanelSizeDidChange', this.updatePanelSize, this.settings.ID);
1015
-
1016
- // Clear page and resize timeouts when the viewer is destroyed
1017
- diva.Events.subscribe('ViewerDidTerminate', () =>
1018
- {
1019
- if (this.viewerState.renderer)
1020
- this.viewerState.renderer.destroy();
1021
-
1022
- clearTimeout(this.viewerState.resizeTimer);
1023
- }, this.settings.ID);
1024
- }
1025
-
1026
- initPlugins ()
1027
- {
1028
- if (!this.settings.hasOwnProperty('plugins'))
1029
- return null;
1030
-
1031
- this.viewerState.pluginInstances = this.settings.plugins.map( (plugin) =>
1032
- {
1033
- const p = new plugin(this);
1034
-
1035
- if (p.isPageTool)
1036
- this.viewerState.pageTools.push(p);
1037
-
1038
- return p;
1039
- });
1040
- }
1041
-
1042
- showThrobber ()
1043
- {
1044
- this.hideThrobber();
1045
-
1046
- this.viewerState.throbberTimeoutID = setTimeout( () =>
1047
- {
1048
- let thb = document.getElementById(this.settings.selector + 'throbber');
1049
- if (thb) thb.style.display = 'block';
1050
- }, this.settings.throbberTimeout);
1051
- }
1052
-
1053
- hideThrobber ()
1054
- {
1055
- // Clear the timeout, if it hasn't executed yet
1056
- clearTimeout(this.viewerState.throbberTimeoutID);
1057
-
1058
- let thb = document.getElementById(this.settings.selector + 'throbber');
1059
- // Hide the throbber if it has already executed
1060
- if (thb) thb.style.display = 'none';
1061
- }
1062
-
1063
- showError (message)
1064
- {
1065
- const errorElement = elt('div', this.elemAttrs('error'), [
1066
- elt('button', this.elemAttrs('error-close', {'aria-label': 'Close dialog'})),
1067
- elt('p',
1068
- elt('strong', 'Error')
1069
- ),
1070
- elt('div', message)
1071
- ]);
1072
-
1073
- this.viewerState.outerObject.appendChild(errorElement);
1074
-
1075
- //bind dialog close button
1076
- document.querySelector(this.settings.selector + 'error-close').addEventListener('click', () =>
1077
- {
1078
- errorElement.parentNode.removeChild(errorElement);
1079
- });
1080
- }
1081
-
1082
- setManifest (manifest, loadOptions)
1083
- {
1084
- this.viewerState.manifest = manifest;
1085
-
1086
- this.hideThrobber();
1087
-
1088
- // Convenience value
1089
- this.viewerState.numPages = this.settings.manifest.pages.length;
1090
-
1091
- this.optionsValidator.validate(this.viewerState.options);
1092
-
1093
- this.publish('NumberOfPagesDidChange', this.settings.numPages);
1094
-
1095
- // Calculate the horizontal and vertical inter-page padding based on the dimensions of the average zoom level
1096
- if (this.settings.adaptivePadding > 0)
1097
- {
1098
- const z = Math.floor((this.settings.minZoomLevel + this.settings.maxZoomLevel) / 2);
1099
- this.viewerState.horizontalPadding = parseInt(this.settings.manifest.getAverageWidth(z) * this.settings.adaptivePadding, 10);
1100
- this.viewerState.verticalPadding = parseInt(this.settings.manifest.getAverageHeight(z) * this.settings.adaptivePadding, 10);
1101
- }
1102
- else
1103
- {
1104
- // It's less than or equal to 0; use fixedPadding instead
1105
- this.viewerState.horizontalPadding = this.settings.fixedPadding;
1106
- this.viewerState.verticalPadding = this.settings.fixedPadding;
1107
- }
1108
-
1109
- // Make sure the vertical padding is at least 40, if plugin icons are enabled
1110
- if (this.viewerState.pageTools.length)
1111
- {
1112
- this.viewerState.verticalPadding = Math.max(40, this.viewerState.verticalPadding);
1113
- }
1114
-
1115
- // If we detect a viewingHint of 'paged' in the manifest or sequence, enable book view by default
1116
- if (this.settings.manifest.paged)
1117
- {
1118
- this.viewerState.options.inBookLayout = true;
1119
- }
1120
-
1121
- // Plugin setup hooks should be bound to the ObjectDidLoad event
1122
- this.publish('ObjectDidLoad', this.settings);
1123
-
1124
- // Adjust the document panel dimensions
1125
- this.updatePanelSize();
1126
-
1127
- let needsXCoord, needsYCoord;
1128
-
1129
- let anchoredVertically = false;
1130
- let anchoredHorizontally = false;
1131
-
1132
- // NB: `==` here will check both null and undefined
1133
- if (loadOptions.goDirectlyTo == null)
1134
- {
1135
- loadOptions.goDirectlyTo = this.settings.goDirectlyTo;
1136
- needsXCoord = needsYCoord = true;
1137
- }
1138
- else
1139
- {
1140
- needsXCoord = loadOptions.horizontalOffset == null || isNaN(loadOptions.horizontalOffset);
1141
- needsYCoord = loadOptions.verticalOffset == null || isNaN(loadOptions.verticalOffset);
1142
- }
1143
-
1144
- // Set default values for the horizontal and vertical offsets
1145
- if (needsXCoord)
1146
- {
1147
- // FIXME: What if inBookLayout/verticallyOriented is changed by loadOptions?
1148
- if (loadOptions.goDirectlyTo === 0 && this.settings.inBookLayout && this.settings.verticallyOriented)
1149
- {
1150
- // if in book layout, center the first opening by default
1151
- loadOptions.horizontalOffset = this.viewerState.horizontalPadding;
1152
- }
1153
- else
1154
- {
1155
- anchoredHorizontally = true;
1156
- loadOptions.horizontalOffset = this.getXOffset(loadOptions.goDirectlyTo, "center");
1157
- }
1158
- }
1159
-
1160
- if (needsYCoord)
1161
- {
1162
- anchoredVertically = true;
1163
- loadOptions.verticalOffset = this.getYOffset(loadOptions.goDirectlyTo, "top");
1164
- }
1165
-
1166
- this.reloadViewer(loadOptions);
1167
-
1168
- //prep dimensions one last time now that pages have loaded
1169
- this.updatePanelSize();
1170
-
1171
- if (this.settings.enableAutoTitle)
1172
- {
1173
- let title = document.getElementById(this.settings.selector + 'title');
1174
-
1175
- if (title)
1176
- {
1177
- title.innerHTML = this.settings.manifest.itemTitle;
1178
- }
1179
- else
1180
- {
1181
- this.settings.parentObject.insertBefore(
1182
- elt('div', this.elemAttrs('title'), [this.settings.manifest.itemTitle]),
1183
- this.settings.parentObject.firstChild
1184
- );
1185
- }
1186
- }
1187
-
1188
- // FIXME: This is a hack to ensure that the outerElement scrollbars are taken into account
1189
- if (this.settings.verticallyOriented)
1190
- this.viewerState.innerElement.style.minWidth = this.settings.panelWidth + 'px';
1191
- else
1192
- this.viewerState.innerElement.style.minHeight = this.settings.panelHeight + 'px';
1193
-
1194
- // FIXME: If the page was supposed to be positioned relative to the viewport we need to
1195
- // recalculate it to take into account the scrollbars
1196
- if (anchoredVertically || anchoredHorizontally)
1197
- {
1198
- if (anchoredVertically)
1199
- this.viewerState.verticalOffset = this.getYOffset(this.settings.activePageIndex, "top");
1200
-
1201
- if (anchoredHorizontally)
1202
- this.viewerState.horizontalOffset = this.getXOffset(this.settings.activePageIndex, "center");
1203
-
1204
- this.viewerState.renderer.goto(this.settings.activePageIndex, this.viewerState.verticalOffset, this.viewerState.horizontalOffset);
1205
- }
1206
-
1207
- // signal that everything should be set up and ready to go.
1208
- this.viewerState.loaded = true;
1209
-
1210
- this.publish("ViewerDidLoad", this.settings);
1211
- }
1212
-
1213
- publish (event)
1214
- {
1215
- const args = Array.prototype.slice.call(arguments, 1);
1216
- diva.Events.publish(event, args, this.publicInstance);
1217
- }
1218
-
1219
- getSettings ()
1220
- {
1221
- return this.settings;
1222
- }
1223
-
1224
- // Temporary accessor for the state of the viewer core
1225
- // TODO: Replace this with a more restricted view of whatever needs
1226
- // be exposed through settings for backwards compat
1227
- getInternalState ()
1228
- {
1229
- return this.viewerState;
1230
- }
1231
-
1232
- getPublicInstance ()
1233
- {
1234
- return this.publicInstance;
1235
- }
1236
-
1237
- getPageTools ()
1238
- {
1239
- return this.viewerState.pageTools;
1240
- }
1241
-
1242
- getCurrentLayout ()
1243
- {
1244
- return this.viewerState.renderer ? this.viewerState.renderer.layout : null;
1245
- }
1246
-
1247
- /** Get a copy of the current viewport dimensions */
1248
- getViewport ()
1249
- {
1250
- const viewport = this.viewerState.viewport;
1251
-
1252
- return {
1253
- top: viewport.top,
1254
- left: viewport.left,
1255
- bottom: viewport.bottom,
1256
- right: viewport.right,
1257
-
1258
- width: viewport.width,
1259
- height: viewport.height
1260
- };
1261
- }
1262
-
1263
- addPageOverlay (overlay)
1264
- {
1265
- this.viewerState.pageOverlays.addOverlay(overlay);
1266
- }
1267
-
1268
- removePageOverlay (overlay)
1269
- {
1270
- this.viewerState.pageOverlays.removeOverlay(overlay);
1271
- }
1272
-
1273
- getPageRegion (pageIndex, options)
1274
- {
1275
- const layout = this.viewerState.renderer.layout;
1276
- const region = layout.getPageRegion(pageIndex, options);
1277
-
1278
- if (options && options.incorporateViewport)
1279
- {
1280
- const secondaryDim = this.settings.verticallyOriented ? 'width' : 'height';
1281
-
1282
- if (this.viewerState.viewport[secondaryDim] > layout.dimensions[secondaryDim])
1283
- {
1284
- const docOffset = (this.viewerState.viewport[secondaryDim] - layout.dimensions[secondaryDim]) / 2;
1285
-
1286
- if (this.settings.verticallyOriented)
1287
- {
1288
- return {
1289
- top: region.top,
1290
- bottom: region.bottom,
1291
-
1292
- left: region.left + docOffset,
1293
- right: region.right + docOffset
1294
- };
1295
- }
1296
- else
1297
- {
1298
- return {
1299
- top: region.top + docOffset,
1300
- bottom: region.bottom + docOffset,
1301
-
1302
- left: region.left,
1303
- right: region.right
1304
- };
1305
- }
1306
- }
1307
- }
1308
-
1309
- return region;
1310
- }
1311
-
1312
- getPagePositionAtViewportOffset (coords)
1313
- {
1314
- const docCoords = {
1315
- left: coords.left + this.viewerState.viewport.left,
1316
- top: coords.top + this.viewerState.viewport.top
1317
- };
1318
-
1319
- const renderedPages = this.viewerState.renderer.getRenderedPages();
1320
- const pageCount = renderedPages.length;
1321
-
1322
- // Find the page on which the coords occur
1323
- for (let i=0; i < pageCount; i++)
1324
- {
1325
- const pageIndex = renderedPages[i];
1326
- const region = this.viewerState.renderer.layout.getPageRegion(pageIndex);
1327
-
1328
- if (region.left <= docCoords.left && region.right >= docCoords.left &&
1329
- region.top <= docCoords.top && region.bottom >= docCoords.top)
1330
- {
1331
- return {
1332
- anchorPage: pageIndex,
1333
- offset: {
1334
- left: docCoords.left - region.left,
1335
- top: docCoords.top - region.top
1336
- }
1337
- };
1338
- }
1339
- }
1340
-
1341
- // Fall back to current page
1342
- // FIXME: Would be better to use the closest page or something
1343
- const currentRegion = this.viewerState.renderer.layout.getPageRegion(this.settings.activePageIndex);
1344
-
1345
- return {
1346
- anchorPage: this.settings.activePageIndex,
1347
- offset: {
1348
- left: docCoords.left - currentRegion.left,
1349
- top: docCoords.top - currentRegion.top
1350
- }
1351
- };
1352
- }
1353
-
1354
- // setManifest (manifest, loadOptions)
1355
- // {
1356
- // setManifest(manifest, loadOptions || {});
1357
- // }
1358
-
1359
- /**
1360
- * Set the current page to the given index, firing VisiblePageDidChange
1361
- *
1362
- * @param pageIndex
1363
- */
1364
- setCurrentPages (activePage, visiblePages)
1365
- {
1366
- if (!arraysEqual(this.viewerState.currentPageIndices, visiblePages))
1367
- {
1368
- this.viewerState.currentPageIndices = visiblePages;
1369
- if (this.viewerState.activePageIndex !== activePage)
1370
- {
1371
- this.viewerState.activePageIndex = activePage;
1372
- this.publish("ActivePageDidChange", activePage);
1373
- }
1374
- this.publish("VisiblePageDidChange", visiblePages);
1375
-
1376
- // Publish an event if the page we're switching to has other images.
1377
- if (this.viewerState.manifest.pages[activePage].otherImages.length > 0)
1378
- this.publish('VisiblePageHasAlternateViews', activePage);
1379
- }
1380
- else if (this.viewerState.activePageIndex !== activePage)
1381
- {
1382
- this.viewerState.activePageIndex = activePage;
1383
- this.publish("ActivePageDidChange", activePage);
1384
- }
1385
-
1386
- function arraysEqual (a, b)
1387
- {
1388
- if (a.length !== b.length)
1389
- return false;
1390
-
1391
- for (let i = 0, len = a.length; i < len; i++)
1392
- {
1393
- if (a[i] !== b[i])
1394
- return false;
1395
- }
1396
- return true;
1397
- }
1398
- }
1399
-
1400
- getPageName (pageIndex)
1401
- {
1402
- return this.viewerState.manifest.pages[pageIndex].f;
1403
- }
1404
-
1405
- reload (newOptions)
1406
- {
1407
- this.reloadViewer(newOptions);
1408
- }
1409
-
1410
- zoom (zoomLevel, focalPoint)
1411
- {
1412
- return this.handleZoom(zoomLevel, focalPoint);
1413
- }
1414
-
1415
- enableScrollable ()
1416
- {
1417
- if (!this.viewerState.isScrollable)
1418
- {
1419
- this.bindMouseEvents();
1420
- this.enableDragScrollable();
1421
- this.viewerState.options.enableKeyScroll = this.viewerState.initialKeyScroll;
1422
- this.viewerState.options.enableSpaceScroll = this.viewerState.initialSpaceScroll;
1423
- this.viewerState.viewportElement.style.overflow = 'auto';
1424
- this.viewerState.isScrollable = true;
1425
- }
1426
- }
1427
-
1428
- enableDragScrollable ()
1429
- {
1430
- if (this.viewerState.viewportObject.hasAttribute('nochilddrag'))
1431
- this.viewerState.viewportObject.removeAttribute('nochilddrag');
1432
- }
1433
-
1434
- disableScrollable ()
1435
- {
1436
- if (this.viewerState.isScrollable)
1437
- {
1438
- // block dragging
1439
- this.disableDragScrollable();
1440
-
1441
- // block double-click zooming
1442
- this.viewerState.outerObject.dblclick = null;
1443
- this.viewerState.outerObject.contextmenu = null;
1444
-
1445
- // disable all other scrolling actions
1446
- this.viewerState.viewportElement.style.overflow = 'hidden';
1447
-
1448
- // block scrolling keys behavior, respecting initial scroll settings
1449
- this.viewerState.initialKeyScroll = this.settings.enableKeyScroll;
1450
- this.viewerState.initialSpaceScroll = this.settings.enableSpaceScroll;
1451
- this.viewerState.options.enableKeyScroll = false;
1452
- this.viewerState.options.enableSpaceScroll = false;
1453
-
1454
- this.viewerState.isScrollable = false;
1455
- }
1456
- }
1457
-
1458
- disableDragScrollable ()
1459
- {
1460
- if (!this.viewerState.viewportObject.hasAttribute('nochilddrag'))
1461
- this.viewerState.viewportObject.setAttribute('nochilddrag', "");
1462
- }
1463
-
1464
- // isValidOption (key, value)
1465
- // {
1466
- // return isValidOption(key, value);
1467
- // }
1468
-
1469
- // getXOffset (pageIndex, xAnchor)
1470
- // {
1471
- // return getXOffset(pageIndex, xAnchor);
1472
- // }
1473
-
1474
- // getYOffset (pageIndex, yAnchor)
1475
- // {
1476
- // return getYOffset(pageIndex, yAnchor);
1477
- // }
1478
-
1479
- // this.publish = publish;
1480
-
1481
- clear ()
1482
- {
1483
- this.clearViewer();
1484
- }
1485
-
1486
- setPendingManifestRequest (pendingManifestRequest)
1487
- {
1488
- this.viewerState.pendingManifestRequest = pendingManifestRequest;
1489
- }
1490
-
1491
- destroy ()
1492
- {
1493
- // Useful event to access elements in diva before they get destroyed. Used by the highlight plugin.
1494
- this.publish('ViewerWillTerminate', this.settings);
1495
-
1496
- // Cancel any pending request retrieving a manifest
1497
- if (this.settings.pendingManifestRequest)
1498
- this.settings.pendingManifestRequest.abort();
1499
-
1500
- // Removes the hide-scrollbar class from the body
1501
- document.body.removeClass('diva-hide-scrollbar');
1502
-
1503
- // Empty the parent container and remove any diva-related data
1504
- this.settings.parentObject.parent().empty().removeData('diva');
1505
-
1506
- // Remove any additional styling on the parent element
1507
- this.settings.parentObject.parent().removeAttr('style').removeAttr('class');
1508
-
1509
- this.publish('ViewerDidTerminate', this.settings);
1510
-
1511
- // Clear the Events cache
1512
- diva.Events.unsubscribeAll(this.settings.ID);
1513
- }
1514
- }