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.
Files changed (133) 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 -108
  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/_site/diva.iml +0 -11
  40. package/build/diva.css +0 -554
  41. package/build/diva.css.map +0 -1
  42. package/build/diva.js +0 -9
  43. package/build/diva.js.map +0 -1
  44. package/build/plugins/download.js +0 -2
  45. package/build/plugins/download.js.map +0 -1
  46. package/build/plugins/manipulation.js +0 -2
  47. package/build/plugins/manipulation.js.map +0 -1
  48. package/build/plugins/metadata.js +0 -2
  49. package/build/plugins/metadata.js.map +0 -1
  50. package/diva.iml +0 -11
  51. package/index.html +0 -28
  52. package/karma.conf.js +0 -87
  53. package/source/css/_mixins.scss +0 -43
  54. package/source/css/_variables.scss +0 -50
  55. package/source/css/_viewer.scss +0 -462
  56. package/source/css/diva.scss +0 -15
  57. package/source/css/plugins/_manipulation.scss +0 -228
  58. package/source/css/plugins/_metadata.scss +0 -31
  59. package/source/img/adjust.svg +0 -11
  60. package/source/img/book-view.svg +0 -6
  61. package/source/img/close.svg +0 -6
  62. package/source/img/download.svg +0 -6
  63. package/source/img/from-fullscreen.svg +0 -8
  64. package/source/img/grid-fewer.svg +0 -6
  65. package/source/img/grid-more.svg +0 -6
  66. package/source/img/grid-view.svg +0 -6
  67. package/source/img/link.svg +0 -6
  68. package/source/img/metadata.svg +0 -9
  69. package/source/img/page-view.svg +0 -6
  70. package/source/img/to-fullscreen.svg +0 -11
  71. package/source/img/zoom-in.svg +0 -6
  72. package/source/img/zoom-out.svg +0 -7
  73. package/source/js/composite-image.js +0 -174
  74. package/source/js/diva-global.js +0 -7
  75. package/source/js/diva.js +0 -1543
  76. package/source/js/document-handler.js +0 -180
  77. package/source/js/document-layout.js +0 -286
  78. package/source/js/exceptions.js +0 -26
  79. package/source/js/gesture-events.js +0 -190
  80. package/source/js/grid-handler.js +0 -122
  81. package/source/js/iiif-source-adapter.js +0 -63
  82. package/source/js/image-cache.js +0 -113
  83. package/source/js/image-manifest.js +0 -157
  84. package/source/js/image-request-handler.js +0 -76
  85. package/source/js/interpolate-animation.js +0 -122
  86. package/source/js/page-layouts/book-layout.js +0 -161
  87. package/source/js/page-layouts/grid-layout.js +0 -97
  88. package/source/js/page-layouts/index.js +0 -38
  89. package/source/js/page-layouts/page-dimensions.js +0 -9
  90. package/source/js/page-layouts/singles-layout.js +0 -27
  91. package/source/js/page-overlay-manager.js +0 -102
  92. package/source/js/page-tools-overlay.js +0 -95
  93. package/source/js/parse-iiif-manifest.js +0 -302
  94. package/source/js/plugins/_filters.js +0 -679
  95. package/source/js/plugins/download.js +0 -83
  96. package/source/js/plugins/manipulation.js +0 -837
  97. package/source/js/plugins/metadata.js +0 -190
  98. package/source/js/renderer.js +0 -584
  99. package/source/js/settings-view.js +0 -30
  100. package/source/js/tile-coverage-map.js +0 -25
  101. package/source/js/toolbar.js +0 -572
  102. package/source/js/utils/dragscroll.js +0 -106
  103. package/source/js/utils/elt.js +0 -94
  104. package/source/js/utils/events.js +0 -190
  105. package/source/js/utils/get-scrollbar-width.js +0 -29
  106. package/source/js/utils/hash-params.js +0 -86
  107. package/source/js/utils/parse-label-value.js +0 -34
  108. package/source/js/utils/vanilla.kinetic.js +0 -527
  109. package/source/js/validation-runner.js +0 -177
  110. package/source/js/viewer-core.js +0 -1505
  111. package/source/js/viewport.js +0 -143
  112. package/test/_setup.js +0 -13
  113. package/test/composite-image_test.js +0 -94
  114. package/test/diva_test.js +0 -43
  115. package/test/hash-params_test.js +0 -221
  116. package/test/image-cache_test.js +0 -106
  117. package/test/main.js +0 -6
  118. package/test/manifests/beromunsterManifest.json +0 -15514
  119. package/test/manifests/iiifv2.json +0 -11032
  120. package/test/manifests/iiifv2pages.json +0 -30437
  121. package/test/manifests/iiifv3.json +0 -10965
  122. package/test/navigation_test.js +0 -355
  123. package/test/parse-iiif-manifest_test.js +0 -68
  124. package/test/public_test.js +0 -881
  125. package/test/settings_test.js +0 -487
  126. package/test/utils/book-layout_test.js +0 -148
  127. package/test/utils/elt_test.js +0 -102
  128. package/test/utils/events_test.js +0 -245
  129. package/test/utils/hash-params_test.js +0 -79
  130. package/test/utils/parse-label-value_test.js +0 -45
  131. package/test/z_plugins_test.js +0 -180
  132. package/webpack.config.js +0 -58
  133. package/webpack.config.test.js +0 -45
@@ -1,837 +0,0 @@
1
- import {
2
- grayscale,
3
- saturation,
4
- vibrance,
5
- brightness,
6
- contrast,
7
- hue,
8
- gamma,
9
- ccRed,
10
- ccGreen,
11
- ccBlue,
12
- invert,
13
- threshold,
14
- sharpen,
15
- resetFilters
16
- } from "./_filters";
17
- import gestureEvents from '../gesture-events';
18
-
19
- /**
20
- * Returns a function, that, as long as it continues to be invoked, will not
21
- be triggered. The function will be called after it stops being called for
22
- N milliseconds. If `immediate` is passed, trigger the function on the
23
- leading edge, instead of the trailing.
24
- */
25
- function debounce (func, wait, immediate)
26
- {
27
- let timeout;
28
- return function ()
29
- {
30
- let context = this, args = arguments;
31
- let later = function ()
32
- {
33
- timeout = null;
34
- if (!immediate)
35
- {
36
- func.apply(context, args);
37
- }
38
- };
39
- let callNow = immediate && !timeout;
40
- clearTimeout(timeout);
41
- timeout = setTimeout(later, wait);
42
- if (callNow)
43
- {
44
- func.apply(context, args);
45
- }
46
- };
47
- }
48
-
49
- /**
50
- * A Diva.js plugin that allows users to manipulate images by adjusting their
51
- * brightness, contrast, and other parameters.
52
- **/
53
- export default class ManipulationPlugin
54
- {
55
- constructor (core)
56
- {
57
- this._core = core;
58
- this.pageToolsIcon = this.createIcon();
59
- this._backdrop = null;
60
- this._page = null;
61
- this._mainImage = null;
62
- this._canvas = null;
63
-
64
- // store the data for the original main image so that we
65
- // do the processing on that, not on a previously-processed image.
66
- this._originalData = null;
67
-
68
- // zoom stuff
69
- this.maxZoom = 4;
70
- this.minZoom = 1;
71
- this.zoom = 1;
72
-
73
- this.rotate = 0;
74
-
75
- // mirror stuff, 1 for not mirrored, -1 for mirrored
76
- this.mirrorHorizontal = 1;
77
- this.mirrorVertical = 1;
78
-
79
- this.boundEscapeListener = this.escapeListener.bind(this);
80
-
81
- // url of currently loaded image
82
- this.currentImageURL = null;
83
- }
84
-
85
- handleClick (event, settings, publicInstance, pageIndex)
86
- {
87
- document.body.style.overflow = 'hidden';
88
- this._backdrop = document.createElement('div');
89
- this._backdrop.classList.add('manipulation-fullscreen');
90
-
91
- this._sidebar = document.createElement('div');
92
- this._sidebar.classList.add('manipulation-sidebar');
93
-
94
- this._mainArea = document.createElement('div');
95
- this._mainArea.classList.add('manipulation-main-area');
96
-
97
- // enable scroll by dragging
98
- this._mainArea.classList.add('dragscroll');
99
- this._mainArea.addEventListener('mousedown', () => { this._mainArea.classList.add('grabbing'); });
100
- this._mainArea.addEventListener('mouseup', () => { this._mainArea.classList.remove('grabbing'); });
101
-
102
- // add double click zoom handler
103
- gestureEvents.onDoubleClick(this._mainArea, this.handleDblClick.bind(this));
104
-
105
- this._tools = document.createElement('div');
106
- this._tools.classList.add('manipulation-tools');
107
-
108
- this._backdrop.appendChild(this._sidebar);
109
- this._backdrop.appendChild(this._mainArea);
110
- this._backdrop.appendChild(this._tools);
111
-
112
- this._core.parentObject.appendChild(this._backdrop);
113
- document.addEventListener('keyup', this.boundEscapeListener);
114
-
115
- this._page = settings.manifest.pages[pageIndex];
116
-
117
- this._canvas = document.createElement('canvas');
118
- this._ctx = this._canvas.getContext('2d');
119
- this._mainArea.appendChild(this._canvas);
120
-
121
- this._initializeSidebar();
122
- this._initializeTools();
123
-
124
- window.resetDragscroll();
125
- this._loadImageInMainArea(event, this._page.url);
126
-
127
- // hide toolbar if fullscreen
128
- if (settings.inFullscreen)
129
- {
130
- document.getElementById(settings.selector + 'tools').style.display = 'none';
131
- }
132
-
133
- // handle smaller viewport
134
- if (window.innerWidth <= 1000)
135
- {
136
- this._mainArea.classList.remove('manipulation-main-area');
137
- this._mainArea.classList.add('manipulation-main-area-mobile');
138
- this._sidebar.classList.remove('manipulation-sidebar');
139
- this._sidebar.classList.add('manipulation-sidebar-mobile');
140
- this._tools.classList.remove('manipulation-tools');
141
- this._tools.classList.add('manipulation-tools-mobile');
142
-
143
- // add hamburger menus
144
- let burger = document.createElement('div');
145
- burger.classList.add('burger-menu');
146
- let s1 = document.createElement('div');
147
- let s2 = document.createElement('div');
148
- let s3 = document.createElement('div');
149
- s1.classList.add('stripe');
150
- s2.classList.add('stripe');
151
- s3.classList.add('stripe');
152
-
153
- burger.appendChild(s1);
154
- burger.appendChild(s2);
155
- burger.appendChild(s3);
156
-
157
- this.burgerClicked = false;
158
-
159
- burger.onclick = () => {
160
- if (this.burgerClicked)
161
- {
162
- this._sidebar.style.display = 'none';
163
- this._tools.style.display = 'none';
164
- this._mainArea.style.display = 'block';
165
- }
166
- else
167
- {
168
- this._sidebar.style.display = 'block';
169
- this._tools.style.display = 'block';
170
- this._mainArea.style.display = 'none';
171
- }
172
- this.burgerClicked = !this.burgerClicked;
173
- };
174
-
175
- this._backdrop.appendChild(burger);
176
- }
177
- }
178
-
179
- handleDblClick (event)
180
- {
181
- let newZoom = event.ctrlKey ? this.zoom - 1 : this.zoom + 1;
182
- if (newZoom < this.minZoom || newZoom > this.maxZoom)
183
- return;
184
-
185
- // update slider
186
- let slider = document.getElementById('zoom-slider');
187
- slider.value = newZoom;
188
-
189
- this.handleZoom(event, newZoom, true);
190
- }
191
-
192
- /*
193
- * Returns an SVG object representing the icon for the project. Implemented in code
194
- * here so that the entire Diva object can be passed to the client with no external
195
- * dependencies.
196
- **/
197
- createIcon ()
198
- {
199
- const manipulationIcon = document.createElement('div');
200
- manipulationIcon.classList.add('diva-manipulation-icon');
201
-
202
- let root = document.createElementNS("http://www.w3.org/2000/svg", "svg");
203
- root.setAttribute("x", "0px");
204
- root.setAttribute("y", "0px");
205
- root.setAttribute("viewBox", "0 0 25 25");
206
- root.id = `${this._core.settings.selector}manipulation-icon`;
207
-
208
- let g = document.createElementNS("http://www.w3.org/2000/svg", "g");
209
- g.id = `${this._core.settings.selector}manipulation-icon-glyph`;
210
- g.setAttribute("transform", "matrix(1, 0, 0, 1, -11.5, -11.5)");
211
- g.setAttribute("class", "diva-pagetool-icon");
212
-
213
- let path1 = document.createElementNS("http://www.w3.org/2000/svg", "path");
214
- path1.setAttribute("d", "M27,21h-1v-9h-3v9h-1c-0.55,0-1,0.45-1,1v3c0,0.55,0.45,1,1,1h1h3h1c0.55,0,1-0.45,1-1v-3C28,21.45,27.55,21,27,21z M27,24h-5v-0.5h5V24z");
215
-
216
- let path2 = document.createElementNS("http://www.w3.org/2000/svg", "path");
217
- path2.setAttribute("d", "M35,16h-1v-4h-3v4h-1c-0.55,0-1,0.45-1,1v3c0,0.55,0.45,1,1,1h1h3h1c0.55,0,1-0.45,1-1v-3C36,16.45,35.55,16,35,16z M35,19h-5v-0.5h5V19z");
218
-
219
- let path3 = document.createElementNS("http://www.w3.org/2000/svg", "path");
220
- path3.setAttribute("d", "M19,26h-1V12h-3v14h-1c-0.55,0-1,0.45-1,1v3c0,0.55,0.45,1,1,1h1h3h1c0.55,0,1-0.45,1-1v-3C20,26.45,19.55,26,19,26zM19,29h-5v-0.5h5V29z");
221
-
222
- let rect1 = document.createElementNS("http://www.w3.org/2000/svg", "rect");
223
- rect1.setAttribute('x', '23');
224
- rect1.setAttribute('y', '27');
225
- rect1.setAttribute('width', '3');
226
- rect1.setAttribute('height', '9');
227
-
228
- let rect2 = document.createElementNS("http://www.w3.org/2000/svg", "rect");
229
- rect2.setAttribute('x', '31');
230
- rect2.setAttribute('y', '22');
231
- rect2.setAttribute('width', '3');
232
- rect2.setAttribute('height', '14');
233
-
234
- let rect3 = document.createElementNS("http://www.w3.org/2000/svg", "rect");
235
- rect3.setAttribute('x', '15');
236
- rect3.setAttribute('y', '32');
237
- rect3.setAttribute('width', '3');
238
- rect3.setAttribute('height', '4');
239
-
240
- g.appendChild(path1);
241
- g.appendChild(path2);
242
- g.appendChild(rect1);
243
- g.appendChild(path3);
244
- g.appendChild(rect2);
245
- g.appendChild(rect3);
246
- root.appendChild(g);
247
-
248
- manipulationIcon.appendChild(root);
249
-
250
- return manipulationIcon;
251
- }
252
-
253
- escapeListener (event)
254
- {
255
- if (event.keyCode === 27)
256
- {
257
- document.removeEventListener('keyup', this.boundEscapeListener);
258
- document.body.style.overflow = 'auto';
259
- this._core.parentObject.removeChild(this._backdrop);
260
-
261
- // show toolbar
262
- document.getElementById(`${this._core.settings.selector}tools`).style.display = 'block';
263
- }
264
- }
265
-
266
- _initializeSidebar ()
267
- {
268
- // 150px wide images for the sidebar.
269
- let thumbnailSize = "150";
270
- let mainPageSidebarImageURL = `${this._page.url}full/${thumbnailSize},/0/default.jpg`;
271
-
272
- let otherImageURLs = this._page.otherImages.map((img) =>
273
- {
274
- return `${img.url}full/${thumbnailSize},/0/default.jpg`;
275
- });
276
-
277
- let primaryImgDiv = document.createElement('div');
278
- primaryImgDiv.classList.add('manipulation-sidebar-primary-image');
279
-
280
- let primaryImg = document.createElement('img');
281
- primaryImg.setAttribute('src', mainPageSidebarImageURL);
282
-
283
- let primaryImgLabel = document.createElement('div');
284
- primaryImgLabel.textContent = this._page.il;
285
-
286
- primaryImgDiv.appendChild(primaryImg);
287
- primaryImgDiv.appendChild(primaryImgLabel);
288
-
289
- this._sidebar.appendChild(primaryImgDiv);
290
-
291
- primaryImgDiv.addEventListener('click', () =>
292
- {
293
- this._loadImageInMainArea.call(this, event, this._page.url);
294
- });
295
-
296
- otherImageURLs.map((url, idx) =>
297
- {
298
- let othDiv = document.createElement('div');
299
- othDiv.classList.add('manipulation-sidebar-secondary-image');
300
-
301
- let othImg = document.createElement('img');
302
- othImg.setAttribute('src', url);
303
-
304
- let othImgLabel = document.createElement('div');
305
- othImgLabel.textContent = this._page.otherImages[idx].il;
306
-
307
- othDiv.appendChild(othImg);
308
- othDiv.appendChild(othImgLabel);
309
-
310
- this._sidebar.appendChild(othDiv);
311
-
312
- othDiv.addEventListener('click', () => this._loadImageInMainArea.call(this, event, this._page.otherImages[idx].url));
313
- });
314
- }
315
-
316
- _initializeTools ()
317
- {
318
- // Close button
319
- let closeButton = document.createElement('button');
320
-
321
- closeButton.innerHTML = '&#10006';
322
- closeButton.classList.add('close-button');
323
- closeButton.setAttribute('style', 'display: absolute; top: 1em; right: 1em;');
324
-
325
- closeButton.onclick = () =>
326
- {
327
- document.body.style.overflow = 'auto';
328
- this._core.parentObject.removeChild(this._backdrop);
329
- document.getElementById(this._core.settings.selector + 'tools').style.display = 'block';
330
- };
331
-
332
- // Header title
333
- let header = document.createElement('h2');
334
-
335
- header.setAttribute('style', 'margin-bottom: 0.3em;');
336
- header.classList.add('manipulation-tools-text');
337
- header.innerText = 'Image tools';
338
-
339
- // Zoom tool
340
- let zoomDiv = document.createElement('div');
341
- let zoomAdjust = document.createElement('input');
342
- let zoomText = document.createTextNode('Zoom');
343
-
344
- zoomDiv.classList.add('manipulation-tools-text');
345
- zoomAdjust.setAttribute('type', 'range');
346
- zoomAdjust.setAttribute('max', this.maxZoom);
347
- zoomAdjust.setAttribute('min', this.minZoom);
348
- zoomAdjust.setAttribute('value', this.zoom);
349
- zoomAdjust.id = 'zoom-slider';
350
- zoomDiv.addEventListener('change', debounce((e) => this.handleZoom(e, e.target.value, true), 250));
351
- zoomDiv.appendChild(zoomAdjust);
352
- zoomDiv.appendChild(zoomText);
353
-
354
- // Rotation tool
355
- let rotateDiv = document.createElement('div');
356
- let rotateAdjust = document.createElement('input');
357
- let rotateText = document.createTextNode('Rotation');
358
-
359
- rotateDiv.classList.add('manipulation-tools-text');
360
- rotateAdjust.setAttribute('type', 'range');
361
- rotateAdjust.setAttribute('max', 359);
362
- rotateAdjust.setAttribute('min', 0);
363
- rotateAdjust.setAttribute('value', 0);
364
-
365
- rotateDiv.addEventListener('input', (e) => { this.handleTransform(e, null, e.target.value); });
366
- rotateDiv.appendChild(rotateAdjust);
367
- rotateDiv.appendChild(rotateText);
368
-
369
- // Mirror tool
370
- let mirrorDiv = document.createElement('div');
371
- let verticalMirrorButton = document.createElement('button');
372
- verticalMirrorButton.id = 'vertical-mirror-button';
373
- let horizontalMirrorButton = document.createElement('button');
374
- horizontalMirrorButton.id = 'horizontal-mirror-button';
375
-
376
- verticalMirrorButton.textContent = "Mirror Vertically";
377
- horizontalMirrorButton.textContent = "Mirror Horizontally";
378
- verticalMirrorButton.addEventListener('click', (e) => this.handleTransform(e, 'vertical', this.rotate));
379
- horizontalMirrorButton.addEventListener('click', (e) => this.handleTransform(e, 'horizontal', this.rotate));
380
- mirrorDiv.appendChild(verticalMirrorButton);
381
- mirrorDiv.appendChild(horizontalMirrorButton);
382
-
383
- // Filters title
384
- let filtersTitle = document.createElement('div');
385
- filtersTitle.setAttribute('style', 'margin: 1em 0;');
386
-
387
- let titleText = document.createElement('h3');
388
- titleText.setAttribute('style', 'margin: 0;');
389
- titleText.classList.add('manipulation-tools-text');
390
- titleText.innerText = 'Filters';
391
-
392
- // Selection options (color filters or threshold)
393
- let select = document.createElement('select');
394
- select.id = 'filter-select';
395
- select.style.backgroundColor = 'white';
396
-
397
- let colorFilters = document.createElement('option');
398
- colorFilters.value = 'colours';
399
- colorFilters.innerText = 'Color Filters';
400
-
401
- let otherFilters = document.createElement('option');
402
- otherFilters.value = 'threshold';
403
- otherFilters.innerText = 'Threshold';
404
-
405
- select.addEventListener('change', switchVisibleFilters);
406
- select.appendChild(colorFilters);
407
- select.appendChild(otherFilters);
408
- filtersTitle.appendChild(titleText);
409
- filtersTitle.appendChild(select);
410
-
411
- // Grayscale filter
412
- let bwDiv = document.createElement('div');
413
- bwDiv.classList.add('color-filters');
414
- let blackWhiteButton = document.createElement('button');
415
- blackWhiteButton.textContent = "Grayscale";
416
- blackWhiteButton.addEventListener('click', (e) => this._applyTransformationToImageData(e, grayscale));
417
- bwDiv.appendChild(blackWhiteButton);
418
-
419
- // Saturation filter
420
- let saturationDiv = document.createElement('div');
421
- saturationDiv.classList.add('color-filters');
422
- saturationDiv.classList.add('manipulation-tools-text');
423
- let saturationAdjust = document.createElement('input');
424
- let saturationText = document.createTextNode('Saturation');
425
- saturationAdjust.setAttribute('type', 'range');
426
- saturationAdjust.setAttribute('max', 100);
427
- saturationAdjust.setAttribute('min', -100);
428
- saturationAdjust.setAttribute('value', 0);
429
-
430
- saturationAdjust.addEventListener('change', debounce((e) => this._applyTransformationToImageData(e, saturation, e.target.value), 250));
431
- saturationDiv.appendChild(saturationAdjust);
432
- saturationDiv.appendChild(saturationText);
433
-
434
- // Vibrance filter
435
- let vibDiv = document.createElement('div');
436
- vibDiv.classList.add('color-filters');
437
- vibDiv.classList.add('manipulation-tools-text');
438
- let vibranceAdjust = document.createElement('input');
439
- let vibranceText = document.createTextNode('Vibrance');
440
- vibranceAdjust.setAttribute('type', 'range');
441
- vibranceAdjust.setAttribute('max', 100);
442
- vibranceAdjust.setAttribute('min', -100);
443
- vibranceAdjust.setAttribute('value', 0);
444
-
445
- vibranceAdjust.addEventListener('change', debounce((e) => this._applyTransformationToImageData(e, vibrance, e.target.value), 250));
446
- vibDiv.appendChild(vibranceAdjust);
447
- vibDiv.appendChild(vibranceText);
448
-
449
- // Brightness filter
450
- let brightDiv = document.createElement('div');
451
- brightDiv.classList.add('color-filters');
452
- brightDiv.classList.add('manipulation-tools-text');
453
- let brightnessAdjust = document.createElement('input');
454
- let brightnessText = document.createTextNode('Brightness');
455
- brightnessAdjust.setAttribute('type', 'range');
456
- brightnessAdjust.setAttribute('max', 100);
457
- brightnessAdjust.setAttribute('min', -100);
458
- brightnessAdjust.setAttribute('value', 0);
459
-
460
- brightnessAdjust.addEventListener('change', debounce((e) => this._applyTransformationToImageData(e, brightness, e.target.value), 250));
461
- brightDiv.appendChild(brightnessAdjust);
462
- brightDiv.appendChild(brightnessText);
463
-
464
- // Contrast filter
465
- let contrastDiv = document.createElement('div');
466
- contrastDiv.classList.add('color-filters');
467
- contrastDiv.classList.add('manipulation-tools-text');
468
- let contrastAdjust = document.createElement('input');
469
- let contrastText = document.createTextNode('Contrast');
470
- contrastAdjust.setAttribute('type', 'range');
471
- contrastAdjust.setAttribute('max', 100);
472
- contrastAdjust.setAttribute('min', -100);
473
- contrastAdjust.setAttribute('value', 0);
474
-
475
- contrastAdjust.addEventListener('change', debounce((e) => this._applyTransformationToImageData(e, contrast, e.target.value), 250));
476
- contrastDiv.appendChild(contrastAdjust);
477
- contrastDiv.appendChild(contrastText);
478
-
479
- // Invert colours filter
480
- let invDiv = document.createElement('div');
481
- invDiv.classList.add('color-filters');
482
- let invertButton = document.createElement('button');
483
- invertButton.textContent = "Invert Colours";
484
- invertButton.addEventListener('click', (e) => this._applyTransformationToImageData(e, invert));
485
- invDiv.appendChild(invertButton);
486
-
487
- // Sharpness filter
488
- let sharpDiv = document.createElement('div');
489
- sharpDiv.classList.add('color-filters');
490
- sharpDiv.classList.add('manipulation-tools-text');
491
- let sharpenAdjust = document.createElement('input');
492
- let sharpenText = document.createTextNode('Sharpness');
493
- sharpenAdjust.setAttribute('type', 'range');
494
- sharpenAdjust.setAttribute('max', 100);
495
- sharpenAdjust.setAttribute('min', 0);
496
- sharpenAdjust.setAttribute('value', 0);
497
-
498
- sharpenAdjust.addEventListener('change', debounce((e) => this._applyTransformationToImageData(e, sharpen, e.target.value), 250));
499
- sharpDiv.appendChild(sharpenAdjust);
500
- sharpDiv.appendChild(sharpenText);
501
-
502
- // Hue filter
503
- let hueDiv = document.createElement('div');
504
- hueDiv.classList.add('color-filters');
505
- hueDiv.classList.add('manipulation-tools-text');
506
- let hueAdjust = document.createElement('input');
507
- let hueText = document.createTextNode('Hue');
508
- hueAdjust.setAttribute('type', 'range');
509
- hueAdjust.setAttribute('max', 100);
510
- hueAdjust.setAttribute('min', 0);
511
- hueAdjust.setAttribute('value', 0);
512
-
513
- hueAdjust.addEventListener('change', debounce((e) => this._applyTransformationToImageData(e, hue, e.target.value), 250));
514
- hueDiv.appendChild(hueAdjust);
515
- hueDiv.appendChild(hueText);
516
-
517
- // Gamma filter
518
- let gammaDiv = document.createElement('div');
519
- gammaDiv.classList.add('color-filters');
520
- gammaDiv.classList.add('manipulation-tools-text');
521
- let gammaAdjust = document.createElement('input');
522
- let gammaText = document.createTextNode('Gamma');
523
- gammaAdjust.setAttribute('type', 'range');
524
- gammaAdjust.setAttribute('max', 300);
525
- gammaAdjust.setAttribute('min', -100);
526
- gammaAdjust.setAttribute('value', 0);
527
-
528
- gammaAdjust.addEventListener('change', debounce((e) => this._applyTransformationToImageData(e, gamma, e.target.value), 250));
529
- gammaDiv.appendChild(gammaAdjust);
530
- gammaDiv.appendChild(gammaText);
531
-
532
- // Colour channel RGB slides
533
- let ccRedDiv = document.createElement('div');
534
- ccRedDiv.classList.add('color-filters');
535
- ccRedDiv.classList.add('manipulation-tools-text');
536
- let ccRedAdjust = document.createElement('input');
537
- let ccRedText = document.createTextNode('CC Red');
538
- ccRedAdjust.setAttribute('type', 'range');
539
- ccRedAdjust.setAttribute('max', 100);
540
- ccRedAdjust.setAttribute('min', -100);
541
- ccRedAdjust.setAttribute('value', 0);
542
-
543
- let ccGreenDiv = document.createElement('div');
544
- ccGreenDiv.classList.add('color-filters');
545
- ccGreenDiv.classList.add('manipulation-tools-text');
546
- let ccGreenAdjust = document.createElement('input');
547
- let ccGreenText = document.createTextNode('CC Green');
548
- ccGreenAdjust.setAttribute('type', 'range');
549
- ccGreenAdjust.setAttribute('max', 100);
550
- ccGreenAdjust.setAttribute('min', -100);
551
- ccGreenAdjust.setAttribute('value', 0);
552
-
553
- let ccBlueDiv = document.createElement('div');
554
- ccBlueDiv.classList.add('color-filters');
555
- ccBlueDiv.classList.add('manipulation-tools-text');
556
- let ccBlueAdjust = document.createElement('input');
557
- let ccBlueText = document.createTextNode('CC Blue');
558
- ccBlueAdjust.setAttribute('type', 'range');
559
- ccBlueAdjust.setAttribute('max', 100);
560
- ccBlueAdjust.setAttribute('min', -100);
561
- ccBlueAdjust.setAttribute('value', 0);
562
-
563
- ccRedAdjust.addEventListener('change', debounce((e) => this._applyTransformationToImageData(e, ccRed, e.target.value), 250));
564
- ccGreenAdjust.addEventListener('change', debounce((e) => this._applyTransformationToImageData(e, ccGreen, e.target.value), 250));
565
- ccBlueAdjust.addEventListener('change', debounce((e) => this._applyTransformationToImageData(e, ccBlue, e.target.value), 250));
566
-
567
- ccRedDiv.appendChild(ccRedAdjust);
568
- ccRedDiv.appendChild(ccRedText);
569
- ccGreenDiv.appendChild(ccGreenAdjust);
570
- ccGreenDiv.appendChild(ccGreenText);
571
- ccBlueDiv.appendChild(ccBlueAdjust);
572
- ccBlueDiv.appendChild(ccBlueText);
573
-
574
- // Threshold filter
575
- let threshDiv = document.createElement('div');
576
- threshDiv.style.display = 'none';
577
- let thresholdAdjust = document.createElement('input');
578
- let thresholdText = document.createTextNode('Threshold');
579
- threshDiv.classList.add('manipulation-tools-text');
580
- thresholdAdjust.setAttribute('type', 'range');
581
- thresholdAdjust.setAttribute('max', 255);
582
- thresholdAdjust.setAttribute('min', 64);
583
- thresholdAdjust.setAttribute('value', 0);
584
-
585
- thresholdAdjust.addEventListener('change', debounce((e) => this._applyTransformationToImageData(e, threshold, e.target.value), 250));
586
- threshDiv.appendChild(thresholdAdjust);
587
- threshDiv.appendChild(thresholdText);
588
-
589
- // Reset button
590
- let resetButton = document.createElement('button');
591
- resetButton.setAttribute('style', 'margin-top: 1em;');
592
- let buttonText = document.createTextNode('Reset');
593
- resetButton.appendChild(buttonText);
594
- resetButton.onclick = (e) => { this._loadImageInMainArea(e, this.currentImageURL); };
595
-
596
- // Log to keep track of the order of filter application
597
- let filterLog = document.createElement('div');
598
- filterLog.classList.add('manipulation-tools-text');
599
- filterLog.innerHTML = "<h3> Filter Application Order <h3>";
600
- filterLog.id = 'filter-log';
601
-
602
- this._tools.appendChild(closeButton);
603
- this._tools.appendChild(header);
604
- this._tools.appendChild(zoomDiv);
605
- this._tools.appendChild(rotateDiv);
606
- this._tools.appendChild(mirrorDiv);
607
- this._tools.appendChild(filtersTitle);
608
- this._tools.appendChild(bwDiv);
609
- this._tools.appendChild(invDiv);
610
- this._tools.appendChild(saturationDiv);
611
- this._tools.appendChild(vibDiv);
612
- this._tools.appendChild(brightDiv);
613
- this._tools.appendChild(contrastDiv);
614
- this._tools.appendChild(sharpDiv);
615
- this._tools.appendChild(hueDiv);
616
- this._tools.appendChild(gammaDiv);
617
- this._tools.appendChild(ccRedDiv);
618
- this._tools.appendChild(ccGreenDiv);
619
- this._tools.appendChild(ccBlueDiv);
620
- this._tools.appendChild(threshDiv);
621
- this._tools.appendChild(resetButton);
622
- this._tools.appendChild(filterLog);
623
-
624
- this._tools.setAttribute('style', 'padding: 0 1em;');
625
-
626
- function switchVisibleFilters ()
627
- {
628
- let filters = document.getElementsByClassName('color-filters');
629
-
630
- if (this.value === 'threshold')
631
- {
632
- for (let i = 0, len = filters.length; i < len; i++)
633
- {
634
- filters[i].style.display = 'none';
635
- }
636
-
637
- threshDiv.style.display = 'block';
638
- }
639
- else
640
- {
641
- for (let i = 0, len = filters.length; i < len; i++)
642
- {
643
- filters[i].style.display = 'block';
644
- }
645
-
646
- threshDiv.style.display = 'none';
647
- }
648
- }
649
- }
650
-
651
- _resetSliders ()
652
- {
653
- // check if element is a slider, if so then reset
654
- for (let i = 0, len = this._tools.children.length; i < len; i++)
655
- {
656
- let tool = this._tools.children[i].children[0];
657
- if (tool && tool.type === 'range')
658
- tool.value = 0;
659
- }
660
-
661
- document.getElementById('filter-log').innerHTML = "<h3> Filter Application Order <h3>";
662
-
663
- // reset counters
664
- this.zoom = 1;
665
- this.rotate = 0;
666
-
667
- // reset mirror
668
- this.mirrorHorizontal = 1;
669
- this.mirrorVertical = 1;
670
- this.handleTransform(null, null, this.rotate);
671
-
672
- resetFilters();
673
- }
674
-
675
- _loadImageInMainArea (event, imageURL)
676
- {
677
- this.currentImageURL = imageURL; // for resetting
678
-
679
- let url = `${imageURL}full/full/0/default.jpg`;
680
-
681
- this._mainImage = new Image();
682
- this._mainImage.crossOrigin = "anonymous";
683
-
684
- this._mainImage.addEventListener('load', () =>
685
- {
686
- // Determine the size of the (square) canvas based on the hypoteneuse
687
- this._canvas.size = Math.sqrt(this._mainImage.width * this._mainImage.width + this._mainImage.height * this._mainImage.height);
688
- this._canvas.width = this._canvas.size;
689
- this._canvas.height = this._canvas.size;
690
- this._canvas.cornerX = (this._canvas.size - this._mainImage.width) / 2;
691
- this._canvas.cornerY = (this._canvas.size - this._mainImage.height) / 2;
692
-
693
- this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
694
- this._ctx.drawImage(this._mainImage, this._canvas.cornerX, this._canvas.cornerY, this._mainImage.width, this._mainImage.height);
695
- this._originalData = this._ctx.getImageData(this._canvas.cornerX, this._canvas.cornerY, this._mainImage.width, this._mainImage.height);
696
- this._alteredData = this._originalData;
697
-
698
- // to preserve pre-zoom dimensions
699
- this.dims = {
700
- w: this._canvas.width,
701
- h: this._canvas.height
702
- };
703
-
704
- // clean up the image data since it's been painted to the canvas
705
- this._mainImage = null;
706
-
707
- // center the viewport
708
- this.centerView();
709
- });
710
-
711
- this._mainImage.src = url;
712
-
713
- this._resetSliders();
714
- }
715
-
716
- _applyTransformationToImageData (event, func, value)
717
- {
718
- let cw = this._canvas.width;
719
- let ch = this._canvas.height;
720
- let adjustment;
721
-
722
- if (value)
723
- {
724
- adjustment = parseInt(value, 10);
725
- }
726
-
727
- let newData = func(this._originalData, adjustment);
728
- this._alteredData = newData;
729
-
730
- this._ctx.clearRect(0, 0, cw, ch);
731
- this._ctx.putImageData(newData, this._canvas.cornerX, this._canvas.cornerY);
732
-
733
- // necessary to reset the current zoom level (since ImageData gets altered at zoom 1)
734
- this.handleZoom(event, this.zoom, false);
735
- }
736
-
737
- handleZoom (event, value, recenter)
738
- {
739
- let scale = value * 0.5 + 0.5;
740
-
741
- let w = this.dims.w;
742
- let h = this.dims.h;
743
-
744
- // temp canvas for drawing at original zoom level
745
- let tempCanvas = document.createElement('canvas');
746
- let tempCtx = tempCanvas.getContext('2d');
747
- tempCanvas.width = w;
748
- tempCanvas.height = h;
749
- tempCtx.putImageData(this._alteredData, this._canvas.cornerX, this._canvas.cornerY);
750
-
751
- this._canvas.width = w * scale;
752
- this._canvas.height = h * scale;
753
- this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
754
- this._ctx.scale(scale, scale);
755
- this._ctx.drawImage(tempCanvas, 0, 0);
756
-
757
- // determine if zooming in or not
758
- let zoomingIn = value > this.zoom ? true : false;
759
-
760
- this.zoom = parseInt(value, 10);
761
-
762
- if (recenter)
763
- {
764
- let rect = event.target.getBoundingClientRect();
765
- let x = event.clientX - rect.left;
766
- let y = event.clientY - rect.top;
767
-
768
- if (!zoomingIn)
769
- {
770
- // x & y are in terms of pre-zoom-out dimensions, so scale down accordingly
771
- let scaleOut = (this.zoom * 0.5 + 0.5) / ((this.zoom + 1) * 0.5 + 0.5);
772
- x *= scaleOut;
773
- y *= scaleOut;
774
- }
775
-
776
- this.centerView(x, y, zoomingIn);
777
- }
778
- }
779
-
780
- centerView (x, y, zoomingIn)
781
- {
782
- let view = document.getElementsByClassName('manipulation-main-area')[0];
783
- if (!view)
784
- view = document.getElementsByClassName('manipulation-main-area-mobile')[0];
785
-
786
- if (zoomingIn)
787
- {
788
- // x & y are in terms of pre-zoom-in dimensions, so scale up accordingly
789
- let scaleIn = (this.zoom * 0.5 + 0.5) / ((this.zoom - 1) * 0.5 + 0.5);
790
- x *= scaleIn;
791
- y *= scaleIn;
792
- }
793
-
794
- // distance from center
795
- let center = this._canvas.height / 2;
796
- let distY = y - center;
797
- let distX = x - center;
798
-
799
- let h = this._canvas.height;
800
- let w = this._canvas.width;
801
-
802
- let topCentered = (h - view.clientHeight) / 2;
803
- let leftCentered = (w - view.clientWidth) / 2;
804
-
805
- let top = y ? topCentered + distY : topCentered;
806
- let left = x ? leftCentered + distX : leftCentered;
807
-
808
- view.scrollTop = top;
809
- view.scrollLeft = left;
810
- }
811
-
812
- handleTransform (event, type, value)
813
- {
814
- let canvas = document.getElementsByClassName('manipulation-main-area')[0].children[0];
815
-
816
- if (type === 'vertical')
817
- this.mirrorVertical *= -1;
818
- else if (type === 'horizontal')
819
- this.mirrorHorizontal *= -1;
820
-
821
- canvas.style.transform = "scale("+this.mirrorHorizontal+","+this.mirrorVertical+") rotate("+value+"deg)";
822
-
823
- this.rotate = value;
824
- }
825
- }
826
-
827
- ManipulationPlugin.prototype.pluginName = "manipulation";
828
- ManipulationPlugin.prototype.isPageTool = true;
829
-
830
- /**
831
- * Make this plugin available in the global context
832
- * as part of the 'Diva' namespace.
833
- **/
834
- (function (global)
835
- {
836
- global.Diva.ManipulationPlugin = ManipulationPlugin;
837
- })(window);