diva.js 6.0.1 → 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.
- package/.clang-format +7 -0
- package/.github/workflows/npm-publish.yml +45 -0
- package/LICENSE +55 -0
- package/Makefile +75 -0
- package/README.md +15 -108
- package/elm.json +32 -0
- package/package.json +12 -59
- package/review/elm.json +52 -0
- package/review/src/ReviewConfig.elm +87 -0
- package/scripts/elm-esm.sh +40 -0
- package/scripts/minify-css.mjs +31 -0
- package/src/Filters.elm +1044 -0
- package/src/Main.elm +1217 -0
- package/src/Model.elm +213 -0
- package/src/Msg.elm +59 -0
- package/src/Utilities.elm +46 -0
- package/src/View/CollectionExplorer.elm +172 -0
- package/src/View/Helpers.elm +86 -0
- package/src/View/HtmlRenderer.elm +136 -0
- package/src/View/Icons.elm +159 -0
- package/src/View/ManifestInfoModal.elm +363 -0
- package/src/View/PageViewModal.elm +1046 -0
- package/src/View/Sidebar.elm +786 -0
- package/src/View/Toolbar.elm +189 -0
- package/src/View.elm +244 -0
- package/src/diva.ts +802 -0
- package/src/filters.ts +1843 -0
- package/src/styles/app.css +328 -0
- package/src/styles/collection.css +75 -0
- package/src/styles/modal.css +388 -0
- package/src/styles/sidebar.css +215 -0
- package/src/styles/theme.css +39 -0
- package/src/styles/toolbar.css +154 -0
- package/src/viewer-element.ts +1307 -0
- package/testing/index.html +52 -0
- package/testing/testing.html +231 -0
- package/tsconfig.json +12 -0
- package/AUTHORS +0 -22
- package/_site/diva.iml +0 -11
- package/build/diva.css +0 -554
- package/build/diva.css.map +0 -1
- package/build/diva.js +0 -9
- package/build/diva.js.map +0 -1
- package/build/plugins/download.js +0 -2
- package/build/plugins/download.js.map +0 -1
- package/build/plugins/manipulation.js +0 -2
- package/build/plugins/manipulation.js.map +0 -1
- package/build/plugins/metadata.js +0 -2
- package/build/plugins/metadata.js.map +0 -1
- package/diva.iml +0 -11
- package/index.html +0 -28
- package/karma.conf.js +0 -87
- package/source/css/_mixins.scss +0 -43
- package/source/css/_variables.scss +0 -50
- package/source/css/_viewer.scss +0 -462
- package/source/css/diva.scss +0 -15
- package/source/css/plugins/_manipulation.scss +0 -228
- package/source/css/plugins/_metadata.scss +0 -31
- package/source/img/adjust.svg +0 -11
- package/source/img/book-view.svg +0 -6
- package/source/img/close.svg +0 -6
- package/source/img/download.svg +0 -6
- package/source/img/from-fullscreen.svg +0 -8
- package/source/img/grid-fewer.svg +0 -6
- package/source/img/grid-more.svg +0 -6
- package/source/img/grid-view.svg +0 -6
- package/source/img/link.svg +0 -6
- package/source/img/metadata.svg +0 -9
- package/source/img/page-view.svg +0 -6
- package/source/img/to-fullscreen.svg +0 -11
- package/source/img/zoom-in.svg +0 -6
- package/source/img/zoom-out.svg +0 -7
- package/source/js/composite-image.js +0 -174
- package/source/js/diva-global.js +0 -7
- package/source/js/diva.js +0 -1543
- package/source/js/document-handler.js +0 -180
- package/source/js/document-layout.js +0 -286
- package/source/js/exceptions.js +0 -26
- package/source/js/gesture-events.js +0 -190
- package/source/js/grid-handler.js +0 -122
- package/source/js/iiif-source-adapter.js +0 -63
- package/source/js/image-cache.js +0 -113
- package/source/js/image-manifest.js +0 -157
- package/source/js/image-request-handler.js +0 -76
- package/source/js/interpolate-animation.js +0 -122
- package/source/js/page-layouts/book-layout.js +0 -161
- package/source/js/page-layouts/grid-layout.js +0 -97
- package/source/js/page-layouts/index.js +0 -38
- package/source/js/page-layouts/page-dimensions.js +0 -9
- package/source/js/page-layouts/singles-layout.js +0 -27
- package/source/js/page-overlay-manager.js +0 -102
- package/source/js/page-tools-overlay.js +0 -95
- package/source/js/parse-iiif-manifest.js +0 -302
- package/source/js/plugins/_filters.js +0 -679
- package/source/js/plugins/download.js +0 -83
- package/source/js/plugins/manipulation.js +0 -837
- package/source/js/plugins/metadata.js +0 -190
- package/source/js/renderer.js +0 -584
- package/source/js/settings-view.js +0 -30
- package/source/js/tile-coverage-map.js +0 -25
- package/source/js/toolbar.js +0 -572
- package/source/js/utils/dragscroll.js +0 -106
- package/source/js/utils/elt.js +0 -94
- package/source/js/utils/events.js +0 -190
- package/source/js/utils/get-scrollbar-width.js +0 -29
- package/source/js/utils/hash-params.js +0 -86
- package/source/js/utils/parse-label-value.js +0 -34
- package/source/js/utils/vanilla.kinetic.js +0 -527
- package/source/js/validation-runner.js +0 -177
- package/source/js/viewer-core.js +0 -1505
- package/source/js/viewport.js +0 -143
- package/test/_setup.js +0 -13
- package/test/composite-image_test.js +0 -94
- package/test/diva_test.js +0 -43
- package/test/hash-params_test.js +0 -221
- package/test/image-cache_test.js +0 -106
- package/test/main.js +0 -6
- package/test/manifests/beromunsterManifest.json +0 -15514
- package/test/manifests/iiifv2.json +0 -11032
- package/test/manifests/iiifv2pages.json +0 -30437
- package/test/manifests/iiifv3.json +0 -10965
- package/test/navigation_test.js +0 -355
- package/test/parse-iiif-manifest_test.js +0 -68
- package/test/public_test.js +0 -881
- package/test/settings_test.js +0 -487
- package/test/utils/book-layout_test.js +0 -148
- package/test/utils/elt_test.js +0 -102
- package/test/utils/events_test.js +0 -245
- package/test/utils/hash-params_test.js +0 -79
- package/test/utils/parse-label-value_test.js +0 -45
- package/test/z_plugins_test.js +0 -180
- package/webpack.config.js +0 -58
- package/webpack.config.test.js +0 -45
package/source/js/renderer.js
DELETED
|
@@ -1,584 +0,0 @@
|
|
|
1
|
-
import { elt, setAttributes } from './utils/elt';
|
|
2
|
-
import CompositeImage from './composite-image';
|
|
3
|
-
import DocumentLayout from './document-layout';
|
|
4
|
-
import ImageCache from './image-cache';
|
|
5
|
-
import ImageRequestHandler from './image-request-handler';
|
|
6
|
-
import InterpolateAnimation from './interpolate-animation';
|
|
7
|
-
|
|
8
|
-
const REQUEST_DEBOUNCE_INTERVAL = 250;
|
|
9
|
-
|
|
10
|
-
export default class Renderer
|
|
11
|
-
{
|
|
12
|
-
constructor (options, hooks)
|
|
13
|
-
{
|
|
14
|
-
this._viewport = options.viewport;
|
|
15
|
-
this._outerElement = options.outerElement;
|
|
16
|
-
this._documentElement = options.innerElement;
|
|
17
|
-
this._settings = options.settings;
|
|
18
|
-
|
|
19
|
-
this._hooks = hooks || {};
|
|
20
|
-
|
|
21
|
-
this._canvas = elt('canvas', { class: 'diva-viewer-canvas' });
|
|
22
|
-
this._ctx = this._canvas.getContext('2d');
|
|
23
|
-
|
|
24
|
-
this.layout = null;
|
|
25
|
-
|
|
26
|
-
this._sourceResolver = null;
|
|
27
|
-
this._renderedPages = null;
|
|
28
|
-
this._config = null;
|
|
29
|
-
this._zoomLevel = null;
|
|
30
|
-
this._compositeImages = null;
|
|
31
|
-
this._renderedTiles = null;
|
|
32
|
-
this._animation = null;
|
|
33
|
-
|
|
34
|
-
// FIXME(wabain): What level should this be maintained at?
|
|
35
|
-
// Diva global?
|
|
36
|
-
this._cache = new ImageCache();
|
|
37
|
-
this._pendingRequests = {};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
static getCompatibilityErrors ()
|
|
41
|
-
{
|
|
42
|
-
if (typeof HTMLCanvasElement !== 'undefined')
|
|
43
|
-
{
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return [
|
|
48
|
-
'Your browser lacks support for the ', elt('pre', 'canvas'),
|
|
49
|
-
' element. Please upgrade your browser.'
|
|
50
|
-
];
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
load (config, viewportPosition, sourceResolver)
|
|
54
|
-
{
|
|
55
|
-
this._clearAnimation();
|
|
56
|
-
|
|
57
|
-
if (this._hooks.onViewWillLoad)
|
|
58
|
-
{
|
|
59
|
-
this._hooks.onViewWillLoad();
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
this._sourceResolver = sourceResolver;
|
|
63
|
-
this._config = config;
|
|
64
|
-
this._compositeImages = {};
|
|
65
|
-
this._setLayoutToZoomLevel(viewportPosition.zoomLevel);
|
|
66
|
-
|
|
67
|
-
// FIXME(wabain): Remove this when there's more confidence the check shouldn't be needed
|
|
68
|
-
if (!this.layout.getPageInfo(viewportPosition.anchorPage))
|
|
69
|
-
{
|
|
70
|
-
throw new Error('invalid page: ' + viewportPosition.anchorPage);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (this._canvas.width !== this._viewport.width || this._canvas.height !== this._viewport.height)
|
|
74
|
-
{
|
|
75
|
-
this._canvas.width = this._viewport.width;
|
|
76
|
-
this._canvas.height = this._viewport.height;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// FIXME: What hooks should be called here?
|
|
80
|
-
this.goto(viewportPosition.anchorPage, viewportPosition.verticalOffset, viewportPosition.horizontalOffset);
|
|
81
|
-
|
|
82
|
-
if (this._canvas.parentNode !== this._outerElement)
|
|
83
|
-
{
|
|
84
|
-
this._outerElement.insertBefore(this._canvas, this._outerElement.firstChild);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (this._hooks.onViewDidLoad)
|
|
88
|
-
{
|
|
89
|
-
this._hooks.onViewDidLoad();
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
_setViewportPosition (viewportPosition)
|
|
94
|
-
{
|
|
95
|
-
if (viewportPosition.zoomLevel !== this._zoomLevel)
|
|
96
|
-
{
|
|
97
|
-
if (this._zoomLevel === null)
|
|
98
|
-
{
|
|
99
|
-
throw new TypeError('The current view is not zoomable');
|
|
100
|
-
}
|
|
101
|
-
else if (viewportPosition.zoomLevel === null)
|
|
102
|
-
{
|
|
103
|
-
throw new TypeError('The current view requires a zoom level');
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
this._setLayoutToZoomLevel(viewportPosition.zoomLevel);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
this._goto(viewportPosition.anchorPage, viewportPosition.verticalOffset, viewportPosition.horizontalOffset);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
_setLayoutToZoomLevel (zoomLevel)
|
|
113
|
-
{
|
|
114
|
-
this.layout = new DocumentLayout(this._config, zoomLevel);
|
|
115
|
-
this._zoomLevel = zoomLevel;
|
|
116
|
-
|
|
117
|
-
setAttributes(this._documentElement, {
|
|
118
|
-
style: {
|
|
119
|
-
height: this.layout.dimensions.height + 'px',
|
|
120
|
-
width: this.layout.dimensions.width + 'px'
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
this._viewport.setInnerDimensions(this.layout.dimensions);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
adjust ()
|
|
128
|
-
{
|
|
129
|
-
this._clearAnimation();
|
|
130
|
-
|
|
131
|
-
this._render();
|
|
132
|
-
|
|
133
|
-
if (this._hooks.onViewDidUpdate)
|
|
134
|
-
{
|
|
135
|
-
this._hooks.onViewDidUpdate(this._renderedPages.slice(), null);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
_render ()
|
|
140
|
-
{
|
|
141
|
-
const newRenderedPages = [];
|
|
142
|
-
this.layout.pageGroups.forEach((group) =>
|
|
143
|
-
{
|
|
144
|
-
if (!this._viewport.intersectsRegion(group.region))
|
|
145
|
-
{
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const visiblePages = group.pages
|
|
150
|
-
.filter(function (page)
|
|
151
|
-
{
|
|
152
|
-
return this.isPageVisible(page.index);
|
|
153
|
-
}, this)
|
|
154
|
-
.map(page => page.index);
|
|
155
|
-
|
|
156
|
-
newRenderedPages.push.apply(newRenderedPages, visiblePages);
|
|
157
|
-
}, this);
|
|
158
|
-
|
|
159
|
-
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
|
|
160
|
-
this._paintOutline(newRenderedPages);
|
|
161
|
-
|
|
162
|
-
newRenderedPages.forEach((pageIndex) =>
|
|
163
|
-
{
|
|
164
|
-
if (!this._compositeImages[pageIndex])
|
|
165
|
-
{
|
|
166
|
-
const page = this.layout.getPageInfo(pageIndex);
|
|
167
|
-
const zoomLevels = this._sourceResolver.getAllZoomLevelsForPage(page);
|
|
168
|
-
const composite = new CompositeImage(zoomLevels);
|
|
169
|
-
composite.updateFromCache(this._cache);
|
|
170
|
-
this._compositeImages[pageIndex] = composite;
|
|
171
|
-
}
|
|
172
|
-
}, this);
|
|
173
|
-
|
|
174
|
-
this._initiateTileRequests(newRenderedPages);
|
|
175
|
-
|
|
176
|
-
const changes = findChanges(this._renderedPages || [], newRenderedPages);
|
|
177
|
-
|
|
178
|
-
changes.removed.forEach((pageIndex) =>
|
|
179
|
-
{
|
|
180
|
-
delete this._compositeImages[pageIndex];
|
|
181
|
-
}, this);
|
|
182
|
-
|
|
183
|
-
this._renderedPages = newRenderedPages;
|
|
184
|
-
this._paint();
|
|
185
|
-
|
|
186
|
-
if (this._hooks.onPageWillLoad)
|
|
187
|
-
{
|
|
188
|
-
changes.added.forEach((pageIndex) =>
|
|
189
|
-
{
|
|
190
|
-
this._hooks.onPageWillLoad(pageIndex);
|
|
191
|
-
}, this);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
_paint ()
|
|
196
|
-
{
|
|
197
|
-
const renderedTiles = [];
|
|
198
|
-
|
|
199
|
-
this._renderedPages.forEach( (pageIndex) =>
|
|
200
|
-
{
|
|
201
|
-
this._compositeImages[pageIndex].getTiles(this._zoomLevel).forEach((source) =>
|
|
202
|
-
{
|
|
203
|
-
const scaled = getScaledTileRecord(source, this._zoomLevel);
|
|
204
|
-
|
|
205
|
-
if (this._isTileVisible(pageIndex, scaled))
|
|
206
|
-
{
|
|
207
|
-
renderedTiles.push(source.url);
|
|
208
|
-
this._drawTile(pageIndex, scaled, this._cache.get(source.url));
|
|
209
|
-
}
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
const cache = this._cache;
|
|
214
|
-
|
|
215
|
-
const changes = findChanges(this._renderedTiles || [], renderedTiles);
|
|
216
|
-
|
|
217
|
-
changes.added.forEach(url =>
|
|
218
|
-
{
|
|
219
|
-
cache.acquire(url);
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
changes.removed.forEach(url =>
|
|
223
|
-
{
|
|
224
|
-
cache.release(url);
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
if (changes.removed)
|
|
228
|
-
{
|
|
229
|
-
// FIXME: Should only need to update the composite images
|
|
230
|
-
// for which tiles were removed
|
|
231
|
-
this._renderedPages.forEach((pageIndex) =>
|
|
232
|
-
{
|
|
233
|
-
this._compositeImages[pageIndex].updateFromCache(this._cache);
|
|
234
|
-
}, this);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
this._renderedTiles = renderedTiles;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Paint a page outline while the tiles are loading.
|
|
241
|
-
_paintOutline (pages)
|
|
242
|
-
{
|
|
243
|
-
pages.forEach( (pageIndex) =>
|
|
244
|
-
{
|
|
245
|
-
let pageInfo = this.layout.getPageInfo(pageIndex);
|
|
246
|
-
let pageOffset = this._getImageOffset(pageIndex);
|
|
247
|
-
|
|
248
|
-
// Ensure the document is drawn to the center of the viewport
|
|
249
|
-
let viewportPaddingX = Math.max(0, (this._viewport.width - this.layout.dimensions.width) / 2);
|
|
250
|
-
let viewportPaddingY = Math.max(0, (this._viewport.height - this.layout.dimensions.height) / 2);
|
|
251
|
-
|
|
252
|
-
let viewportOffsetX = pageOffset.left - this._viewport.left + viewportPaddingX;
|
|
253
|
-
let viewportOffsetY = pageOffset.top - this._viewport.top + viewportPaddingY;
|
|
254
|
-
|
|
255
|
-
let destXOffset = viewportOffsetX < 0 ? -viewportOffsetX : 0;
|
|
256
|
-
let destYOffset = viewportOffsetY < 0 ? -viewportOffsetY : 0;
|
|
257
|
-
|
|
258
|
-
let canvasX = Math.max(0, viewportOffsetX);
|
|
259
|
-
let canvasY = Math.max(0, viewportOffsetY);
|
|
260
|
-
|
|
261
|
-
let destWidth = pageInfo.dimensions.width - destXOffset;
|
|
262
|
-
let destHeight = pageInfo.dimensions.height - destYOffset;
|
|
263
|
-
|
|
264
|
-
this._ctx.strokeStyle = '#AAA';
|
|
265
|
-
// In order to get a 1px wide line using strokes, we need to start at a 'half pixel'
|
|
266
|
-
this._ctx.strokeRect(canvasX + 0.5, canvasY + 0.5, destWidth, destHeight);
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// This method should be sent all visible pages at once because it will initiate
|
|
271
|
-
// all image requests and cancel any remaining image requests. In the case that
|
|
272
|
-
// a request is ongoing and the tile is still visible in the viewport, the old request
|
|
273
|
-
// is kept active instead of restarting it. The image requests are given a timeout
|
|
274
|
-
// before loading in order to debounce them and have a small reaction time
|
|
275
|
-
// to cancel them and avoid useless requests.
|
|
276
|
-
_initiateTileRequests (pages)
|
|
277
|
-
{
|
|
278
|
-
// Only requests in this object are kept alive, since all others are not visible in the viewport
|
|
279
|
-
const newPendingRequests = {};
|
|
280
|
-
|
|
281
|
-
// Used later as a closure to initiate the image requests with the right source and pageIndex
|
|
282
|
-
const initiateRequest = (source, pageIndex) =>
|
|
283
|
-
{
|
|
284
|
-
const composite = this._compositeImages[pageIndex];
|
|
285
|
-
|
|
286
|
-
newPendingRequests[source.url] = new ImageRequestHandler({
|
|
287
|
-
url: source.url,
|
|
288
|
-
timeoutTime: REQUEST_DEBOUNCE_INTERVAL,
|
|
289
|
-
load: img =>
|
|
290
|
-
{
|
|
291
|
-
delete this._pendingRequests[source.url];
|
|
292
|
-
this._cache.put(source.url, img);
|
|
293
|
-
|
|
294
|
-
// Awkward way to check for updates
|
|
295
|
-
if (composite === this._compositeImages[pageIndex])
|
|
296
|
-
{
|
|
297
|
-
composite.updateWithLoadedUrls([source.url]);
|
|
298
|
-
|
|
299
|
-
if (this._isTileForSourceVisible(pageIndex, source))
|
|
300
|
-
{
|
|
301
|
-
this._paint();
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
else
|
|
305
|
-
{
|
|
306
|
-
if (this._isTileForSourceVisible(pageIndex, source))
|
|
307
|
-
this._paint();
|
|
308
|
-
}
|
|
309
|
-
},
|
|
310
|
-
error: () =>
|
|
311
|
-
{
|
|
312
|
-
// TODO: Could make a limited number of retries, etc.
|
|
313
|
-
delete this._pendingRequests[source.url];
|
|
314
|
-
}
|
|
315
|
-
});
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
for (let i = 0; i < pages.length; i++)
|
|
319
|
-
{
|
|
320
|
-
const pageIndex = pages[i];
|
|
321
|
-
const tiles = this._sourceResolver.getBestZoomLevelForPage(this.layout.getPageInfo(pageIndex)).tiles;
|
|
322
|
-
|
|
323
|
-
for (let j = 0; j < tiles.length; j++)
|
|
324
|
-
{
|
|
325
|
-
const source = tiles[j];
|
|
326
|
-
if (this._cache.has(source.url) || !this._isTileForSourceVisible(pageIndex, source))
|
|
327
|
-
{
|
|
328
|
-
continue;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
// Don't create a new request if the tile is already being loaded
|
|
332
|
-
if (this._pendingRequests[source.url])
|
|
333
|
-
{
|
|
334
|
-
newPendingRequests[source.url] = this._pendingRequests[source.url];
|
|
335
|
-
delete this._pendingRequests[source.url];
|
|
336
|
-
continue;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Use a closure since the load and error methods are going to be called later and
|
|
340
|
-
// we need to keep the right reference to the source and the page index
|
|
341
|
-
initiateRequest(source, pageIndex);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
for (const url in this._pendingRequests)
|
|
346
|
-
{
|
|
347
|
-
this._pendingRequests[url].abort();
|
|
348
|
-
}
|
|
349
|
-
this._pendingRequests = newPendingRequests;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
_drawTile (pageIndex, scaledTile, img)
|
|
353
|
-
{
|
|
354
|
-
let tileOffset = this._getTileToDocumentOffset(pageIndex, scaledTile);
|
|
355
|
-
|
|
356
|
-
// Ensure the document is drawn to the center of the viewport
|
|
357
|
-
let viewportPaddingX = Math.max(0, (this._viewport.width - this.layout.dimensions.width) / 2);
|
|
358
|
-
let viewportPaddingY = Math.max(0, (this._viewport.height - this.layout.dimensions.height) / 2);
|
|
359
|
-
|
|
360
|
-
let viewportOffsetX = tileOffset.left - this._viewport.left + viewportPaddingX;
|
|
361
|
-
let viewportOffsetY = tileOffset.top - this._viewport.top + viewportPaddingY;
|
|
362
|
-
|
|
363
|
-
let destXOffset = viewportOffsetX < 0 ? -viewportOffsetX : 0;
|
|
364
|
-
let destYOffset = viewportOffsetY < 0 ? -viewportOffsetY : 0;
|
|
365
|
-
|
|
366
|
-
let canvasX = Math.max(0, viewportOffsetX);
|
|
367
|
-
let canvasY = Math.max(0, viewportOffsetY);
|
|
368
|
-
|
|
369
|
-
let sourceXOffset = destXOffset / scaledTile.scaleRatio;
|
|
370
|
-
let sourceYOffset = destYOffset / scaledTile.scaleRatio;
|
|
371
|
-
|
|
372
|
-
// Ensure that the specified dimensions are no greater than the actual
|
|
373
|
-
// size of the image. Safari won't display the tile if they are.
|
|
374
|
-
let destImgWidth = Math.min(scaledTile.dimensions.width, img.width * scaledTile.scaleRatio) - destXOffset;
|
|
375
|
-
let destImgHeight = Math.min(scaledTile.dimensions.height, img.height * scaledTile.scaleRatio) - destYOffset;
|
|
376
|
-
|
|
377
|
-
let destWidth = Math.max(1, Math.round(destImgWidth));
|
|
378
|
-
let destHeight = Math.max(1, Math.round(destImgHeight));
|
|
379
|
-
|
|
380
|
-
let sourceWidth = destWidth / scaledTile.scaleRatio;
|
|
381
|
-
let sourceHeight = destHeight / scaledTile.scaleRatio;
|
|
382
|
-
|
|
383
|
-
this._ctx.drawImage(
|
|
384
|
-
img,
|
|
385
|
-
sourceXOffset, sourceYOffset,
|
|
386
|
-
sourceWidth, sourceHeight,
|
|
387
|
-
canvasX, canvasY,
|
|
388
|
-
destWidth, destHeight);
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
_isTileForSourceVisible (pageIndex, tileSource)
|
|
392
|
-
{
|
|
393
|
-
return this._isTileVisible(pageIndex, getScaledTileRecord(tileSource, this._zoomLevel));
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
_isTileVisible (pageIndex, scaledTile)
|
|
397
|
-
{
|
|
398
|
-
const tileOffset = this._getTileToDocumentOffset(pageIndex, scaledTile);
|
|
399
|
-
|
|
400
|
-
// FIXME(wabain): This check is insufficient during a zoom transition
|
|
401
|
-
return this._viewport.intersectsRegion({
|
|
402
|
-
top: tileOffset.top,
|
|
403
|
-
bottom: tileOffset.top + scaledTile.dimensions.height,
|
|
404
|
-
left: tileOffset.left,
|
|
405
|
-
right: tileOffset.left + scaledTile.dimensions.width
|
|
406
|
-
});
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
_getTileToDocumentOffset (pageIndex, scaledTile)
|
|
410
|
-
{
|
|
411
|
-
const imageOffset = this._getImageOffset(pageIndex);
|
|
412
|
-
|
|
413
|
-
return {
|
|
414
|
-
top: imageOffset.top + scaledTile.offset.top,
|
|
415
|
-
left: imageOffset.left + scaledTile.offset.left
|
|
416
|
-
};
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
_getImageOffset (pageIndex)
|
|
420
|
-
{
|
|
421
|
-
return this.layout.getPageOffset(pageIndex, {includePadding: true});
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// TODO: Update signature
|
|
425
|
-
goto (pageIndex, verticalOffset, horizontalOffset)
|
|
426
|
-
{
|
|
427
|
-
this._clearAnimation();
|
|
428
|
-
this._goto(pageIndex, verticalOffset, horizontalOffset);
|
|
429
|
-
|
|
430
|
-
if (this._hooks.onViewDidUpdate)
|
|
431
|
-
{
|
|
432
|
-
this._hooks.onViewDidUpdate(this._renderedPages.slice(), pageIndex);
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
_goto (pageIndex, verticalOffset, horizontalOffset)
|
|
437
|
-
{
|
|
438
|
-
// FIXME(wabain): Move this logic to the viewer
|
|
439
|
-
const pageOffset = this.layout.getPageOffset(pageIndex);
|
|
440
|
-
|
|
441
|
-
const desiredVerticalCenter = pageOffset.top + verticalOffset;
|
|
442
|
-
const top = desiredVerticalCenter - Math.round(this._viewport.height / 2);
|
|
443
|
-
|
|
444
|
-
const desiredHorizontalCenter = pageOffset.left + horizontalOffset;
|
|
445
|
-
const left = desiredHorizontalCenter - Math.round(this._viewport.width / 2);
|
|
446
|
-
|
|
447
|
-
this._viewport.top = top;
|
|
448
|
-
this._viewport.left = left;
|
|
449
|
-
|
|
450
|
-
this._render();
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
transitionViewportPosition (options)
|
|
454
|
-
{
|
|
455
|
-
this._clearAnimation();
|
|
456
|
-
|
|
457
|
-
const getPosition = options.getPosition;
|
|
458
|
-
const onViewDidTransition = this._hooks.onViewDidTransition;
|
|
459
|
-
|
|
460
|
-
this._animation = InterpolateAnimation.animate({
|
|
461
|
-
duration: options.duration,
|
|
462
|
-
parameters: options.parameters,
|
|
463
|
-
onUpdate: (values) =>
|
|
464
|
-
{
|
|
465
|
-
this._setViewportPosition(getPosition(values));
|
|
466
|
-
this._hooks.onZoomLevelWillChange(values.zoomLevel);
|
|
467
|
-
|
|
468
|
-
if (onViewDidTransition)
|
|
469
|
-
{
|
|
470
|
-
onViewDidTransition();
|
|
471
|
-
}
|
|
472
|
-
},
|
|
473
|
-
onEnd: (info) =>
|
|
474
|
-
{
|
|
475
|
-
if (options.onEnd)
|
|
476
|
-
{
|
|
477
|
-
options.onEnd(info);
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
if (this._hooks.onViewDidUpdate && !info.interrupted)
|
|
481
|
-
{
|
|
482
|
-
this._hooks.onViewDidUpdate(this._renderedPages.slice(), null);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
});
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
_clearAnimation ()
|
|
489
|
-
{
|
|
490
|
-
if (this._animation)
|
|
491
|
-
{
|
|
492
|
-
this._animation.cancel();
|
|
493
|
-
this._animation = null;
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
isPageVisible (pageIndex)
|
|
498
|
-
{
|
|
499
|
-
if (!this.layout)
|
|
500
|
-
{
|
|
501
|
-
return false;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
const page = this.layout.getPageInfo(pageIndex);
|
|
505
|
-
|
|
506
|
-
if (!page)
|
|
507
|
-
{
|
|
508
|
-
return false;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
return this._viewport.intersectsRegion(this.layout.getPageRegion(pageIndex));
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
getRenderedPages ()
|
|
515
|
-
{
|
|
516
|
-
return this._renderedPages.slice();
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
destroy ()
|
|
520
|
-
{
|
|
521
|
-
this._clearAnimation();
|
|
522
|
-
|
|
523
|
-
// FIXME(wabain): I don't know if we should actually do this
|
|
524
|
-
Object.keys(this._pendingRequests).forEach((req) =>
|
|
525
|
-
{
|
|
526
|
-
const handler = this._pendingRequests[req];
|
|
527
|
-
delete this._pendingRequests[req];
|
|
528
|
-
|
|
529
|
-
handler.abort();
|
|
530
|
-
}, this);
|
|
531
|
-
|
|
532
|
-
this._canvas.parentNode.removeChild(this._canvas);
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
function getScaledTileRecord (source, scaleFactor)
|
|
537
|
-
{
|
|
538
|
-
let scaleRatio;
|
|
539
|
-
|
|
540
|
-
if (scaleFactor === null)
|
|
541
|
-
{
|
|
542
|
-
scaleRatio = 1;
|
|
543
|
-
}
|
|
544
|
-
else
|
|
545
|
-
{
|
|
546
|
-
scaleRatio = Math.pow(2, scaleFactor - source.zoomLevel);
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
return {
|
|
550
|
-
sourceZoomLevel: source.zoomLevel,
|
|
551
|
-
scaleRatio: scaleRatio,
|
|
552
|
-
row: source.row,
|
|
553
|
-
col: source.col,
|
|
554
|
-
dimensions: {
|
|
555
|
-
width: source.dimensions.width * scaleRatio,
|
|
556
|
-
height: source.dimensions.height * scaleRatio
|
|
557
|
-
},
|
|
558
|
-
offset: {
|
|
559
|
-
left: source.offset.left * scaleRatio,
|
|
560
|
-
top: source.offset.top * scaleRatio
|
|
561
|
-
},
|
|
562
|
-
url: source.url
|
|
563
|
-
};
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
function findChanges (oldArray, newArray)
|
|
567
|
-
{
|
|
568
|
-
if (oldArray === newArray)
|
|
569
|
-
{
|
|
570
|
-
return {
|
|
571
|
-
added: [],
|
|
572
|
-
removed: []
|
|
573
|
-
};
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
const removed = oldArray.filter(oldEntry => newArray.indexOf(oldEntry) === -1);
|
|
577
|
-
|
|
578
|
-
const added = newArray.filter(newEntry => oldArray.indexOf(newEntry) === -1);
|
|
579
|
-
|
|
580
|
-
return {
|
|
581
|
-
added: added,
|
|
582
|
-
removed: removed
|
|
583
|
-
};
|
|
584
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
export default function createSettingsView(sources)
|
|
3
|
-
{
|
|
4
|
-
const obj = {};
|
|
5
|
-
|
|
6
|
-
sources.forEach( (source) =>
|
|
7
|
-
{
|
|
8
|
-
registerMixin(obj, source);
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
return obj;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function registerMixin(obj, mixin)
|
|
15
|
-
{
|
|
16
|
-
Object.keys(mixin).forEach( (key) =>
|
|
17
|
-
{
|
|
18
|
-
Object.defineProperty(obj, key, {
|
|
19
|
-
get: () =>
|
|
20
|
-
{
|
|
21
|
-
return mixin[key];
|
|
22
|
-
},
|
|
23
|
-
set: () =>
|
|
24
|
-
{
|
|
25
|
-
// TODO: Make everything strict mode so this isn't needed
|
|
26
|
-
throw new TypeError('Cannot set settings.' + key);
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
export default class TileCoverageMap
|
|
2
|
-
{
|
|
3
|
-
constructor (rows, cols)
|
|
4
|
-
{
|
|
5
|
-
this._rows = rows;
|
|
6
|
-
this._cols = cols;
|
|
7
|
-
this._map = new Array(rows).fill(null).map(() => new Array(cols).fill(false));
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
isLoaded (row, col)
|
|
11
|
-
{
|
|
12
|
-
// Return true for out of bounds tiles because they
|
|
13
|
-
// don't need to load. (Unfortunately this will also
|
|
14
|
-
// mask logical errors.)
|
|
15
|
-
if (row >= this._rows || col >= this._cols)
|
|
16
|
-
return true;
|
|
17
|
-
|
|
18
|
-
return this._map[row][col];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
set(row, col, value)
|
|
22
|
-
{
|
|
23
|
-
this._map[row][col] = value;
|
|
24
|
-
}
|
|
25
|
-
}
|