View | Details | Raw Unified | Return to bug 28179
Collapse All | Expand All

(-)a/koha-tmpl/intranet-tmpl/lib/Chocolat/css/chocolat.css (+230 lines)
Line 0 Link Here
1
.chocolat-zoomable.chocolat-zoomed {
2
    cursor: zoom-out;
3
}
4
.chocolat-open {
5
    overflow: hidden;
6
}
7
.chocolat-overlay {
8
    transition: opacity 0.4s ease, visibility 0s 0.4s ease;
9
    height: 100%;
10
    width: 100%;
11
    position: fixed;
12
    left: 0;
13
    top: 0;
14
    z-index: 10;
15
    background-color: #fff;
16
    visibility: hidden;
17
    opacity: 0;
18
}
19
.chocolat-overlay.chocolat-visible {
20
    transition: opacity 0.4s, visibility 0s;
21
    visibility: visible;
22
    opacity: 0.8;
23
}
24
25
.chocolat-wrapper {
26
    transition: opacity 0.4s ease, visibility 0s 0.4s ease;
27
    width: 100%;
28
    height: 100%;
29
    position: fixed;
30
    opacity: 0;
31
    left: 0;
32
    top: 0;
33
    z-index: 16;
34
    color: #fff;
35
    visibility: hidden;
36
}
37
.chocolat-wrapper.chocolat-visible {
38
    transition: opacity 0.4s, visibility 0s;
39
    opacity: 1;
40
    visibility: visible;
41
}
42
43
.chocolat-zoomable .chocolat-img {
44
    cursor: zoom-in;
45
}
46
.chocolat-loader {
47
    transition: opacity 0.3s;
48
    height: 32px;
49
    width: 32px;
50
    position: absolute;
51
    left: 50%;
52
    top: 50%;
53
    margin-left: -16px;
54
    margin-top: -16px;
55
    z-index: 11;
56
    background: url(../images/loader.gif);
57
    opacity: 0;
58
}
59
.chocolat-loader.chocolat-visible {
60
    opacity: 1;
61
}
62
63
.chocolat-image-wrapper {
64
    position: fixed;
65
    width: 0px;
66
    height: 0px;
67
    left: 50%;
68
    top: 50%;
69
    z-index: 14;
70
    text-align: left;
71
    transform: translate(-50%, -50%);
72
}
73
74
.chocolat-image-wrapper .chocolat-img {
75
    position: absolute;
76
    width: 100%;
77
    height: 100%;
78
}
79
.chocolat-wrapper .chocolat-left {
80
    width: 50px;
81
    height: 100px;
82
    cursor: pointer;
83
    background: url(../images/left.png) 50% 50% no-repeat;
84
    z-index: 17;
85
    visibility: hidden;
86
}
87
88
.chocolat-layout {
89
    display: flex;
90
    flex-direction: column;
91
    position: absolute;
92
    top: 0;
93
    bottom: 0;
94
    left: 0;
95
    right: 0;
96
}
97
.chocolat-image-canvas {
98
    transition: opacity .2s;
99
    opacity: 0;
100
    flex-grow: 1;
101
    align-self: stretch;
102
}
103
.chocolat-image-canvas.chocolat-visible {
104
    opacity: 1;
105
}
106
.chocolat-center {
107
    flex-grow: 1;
108
    display: flex;
109
    justify-content: center;
110
    align-items: center;
111
    user-select: none;
112
}
113
114
.chocolat-wrapper .chocolat-right {
115
    width: 50px;
116
    height: 100px;
117
    cursor: pointer;
118
    background: url(../images/right.png) 50% 50% no-repeat;
119
    z-index: 17;
120
    visibility: hidden;
121
}
122
.chocolat-wrapper .chocolat-right.active {
123
    visibility: visible;
124
}
125
.chocolat-wrapper .chocolat-left.active {
126
    visibility: visible;
127
}
128
.chocolat-wrapper .chocolat-top {
129
    height: 50px;
130
    overflow: hidden;
131
    z-index: 17;
132
    flex-shrink: 0;
133
}
134
.chocolat-wrapper .chocolat-close {
135
    width: 50px;
136
    height: 50px;
137
    cursor: pointer;
138
    position: absolute;
139
    top: 0;
140
    right: 0;
141
    background: url(../images/close.png) 50% 50% no-repeat;
142
}
143
.chocolat-wrapper .chocolat-bottom {
144
    height: 40px;
145
    font-size: 12px;
146
    z-index: 17;
147
    padding-left: 15px;
148
    padding-right: 15px;
149
    background: rgba(0, 0, 0, 0.2);
150
    flex-shrink: 0;
151
    display: flex;
152
    align-items: center;
153
154
}
155
.chocolat-wrapper .chocolat-set-title {
156
    display: inline-block;
157
    padding-right: 15px;
158
    line-height: 1;
159
    border-right: 1px solid rgba(255, 255, 255, 0.3);
160
}
161
.chocolat-wrapper .chocolat-pagination {
162
    float: right;
163
    display: inline-block;
164
    padding-left: 15px;
165
    padding-right: 15px;
166
    margin-right: 15px;
167
    /*border-right: 1px solid rgba(255, 255, 255, 0.2);*/
168
}
169
.chocolat-wrapper .chocolat-fullscreen {
170
    width: 16px;
171
    height: 40px;
172
    background: url(../images/fullscreen.png) 50% 50% no-repeat;
173
    display: block;
174
    cursor: pointer;
175
    float: right;
176
}
177
.chocolat-wrapper .chocolat-description {
178
    display: inline-block;
179
    flex-grow: 1;
180
    text-align: left;
181
}
182
183
/* no container mode*/
184
body.chocolat-open > .chocolat-overlay {
185
    z-index: 15;
186
}
187
body.chocolat-open > .chocolat-loader {
188
    z-index: 15;
189
}
190
body.chocolat-open > .chocolat-image-wrapper {
191
    z-index: 17;
192
}
193
194
/* container mode*/
195
.chocolat-in-container .chocolat-wrapper,
196
.chocolat-in-container .chocolat-image-wrapper,
197
.chocolat-in-container .chocolat-overlay {
198
    position: absolute;
199
}
200
.chocolat-in-container {
201
    position: relative;
202
}
203
204
.chocolat-zoomable.chocolat-zooming-in .chocolat-image-wrapper,
205
.chocolat-zoomable.chocolat-zooming-out .chocolat-image-wrapper {
206
    transition: width .2s ease, height .2s ease;
207
}
208
.chocolat-zoomable.chocolat-zooming-in .chocolat-img,
209
.chocolat-zoomable.chocolat-zooming-out .chocolat-img {
210
    transition: margin .2s ease;
211
}
212
213
/* uncomment to hide controls when zoomed-in*/
214
/*
215
.chocolat-zoomable .chocolat-top,
216
.chocolat-zoomable .chocolat-bottom,
217
.chocolat-zoomable .chocolat-right,
218
.chocolat-zoomable .chocolat-left {
219
    transition: opacity .3s ease, visibility 0s .3s;
220
   opacity: 1;
221
}
222
223
.chocolat-zoomable.chocolat-zoomed .chocolat-top,
224
.chocolat-zoomable.chocolat-zoomed .chocolat-bottom,
225
.chocolat-zoomable.chocolat-zoomed .chocolat-right,
226
.chocolat-zoomable.chocolat-zoomed .chocolat-left {
227
    visibility: hidden;
228
    opacity: 0;
229
}
230
*/
(-)a/koha-tmpl/intranet-tmpl/lib/Chocolat/js/chocolat.js (+707 lines)
Line 0 Link Here
1
(function () {
2
    'use strict';
3
4
    let timerDebounce = undefined;
5
    function debounce(duration, callback) {
6
      clearTimeout(timerDebounce);
7
      timerDebounce = setTimeout(function () {
8
        callback();
9
      }, duration);
10
      return timerDebounce;
11
    }
12
    function transitionAsPromise(triggeringFunc, el) {
13
      return new Promise(resolve => {
14
        const handleTransitionEnd = () => {
15
          el.removeEventListener('transitionend', handleTransitionEnd);
16
          resolve();
17
        };
18
19
        el.addEventListener('transitionend', handleTransitionEnd);
20
        const classesBefore = el.getAttribute('class');
21
        const stylesBefore = el.getAttribute('style');
22
        triggeringFunc();
23
24
        if (classesBefore === el.getAttribute('class') && stylesBefore === el.getAttribute('style')) {
25
          handleTransitionEnd();
26
        }
27
28
        if (parseFloat(getComputedStyle(el)['transitionDuration']) === 0) {
29
          handleTransitionEnd();
30
        }
31
      });
32
    }
33
    function loadImage({
34
      src,
35
      srcset,
36
      sizes
37
    }) {
38
      const image = new Image();
39
      image.src = src;
40
41
      if (srcset) {
42
        image.srcset = srcset;
43
      }
44
45
      if (sizes) {
46
        image.sizes = sizes;
47
      }
48
49
      if ('decode' in image) {
50
        return new Promise((resolve, reject) => {
51
          image.decode().then(() => {
52
            resolve(image);
53
          }).catch(() => {
54
            reject(image);
55
          });
56
        });
57
      } else {
58
        return new Promise((resolve, reject) => {
59
          image.onload = resolve(image);
60
          image.onerror = reject(image);
61
        });
62
      }
63
    }
64
    function fit(options) {
65
      let height;
66
      let width;
67
      const {
68
        imgHeight,
69
        imgWidth,
70
        containerHeight,
71
        containerWidth,
72
        canvasWidth,
73
        canvasHeight,
74
        imageSize
75
      } = options;
76
      const canvasRatio = canvasHeight / canvasWidth;
77
      const containerRatio = containerHeight / containerWidth;
78
      const imgRatio = imgHeight / imgWidth;
79
80
      if (imageSize == 'cover') {
81
        if (imgRatio < containerRatio) {
82
          height = containerHeight;
83
          width = height / imgRatio;
84
        } else {
85
          width = containerWidth;
86
          height = width * imgRatio;
87
        }
88
      } else if (imageSize == 'native') {
89
        height = imgHeight;
90
        width = imgWidth;
91
      } else {
92
        if (imgRatio > canvasRatio) {
93
          height = canvasHeight;
94
          width = height / imgRatio;
95
        } else {
96
          width = canvasWidth;
97
          height = width * imgRatio;
98
        }
99
100
        if (imageSize === 'scale-down' && (width >= imgWidth || height >= imgHeight)) {
101
          width = imgWidth;
102
          height = imgHeight;
103
        }
104
      }
105
106
      return {
107
        height: height,
108
        width: width
109
      };
110
    }
111
    function openFullScreen(wrapper) {
112
      if (wrapper.requestFullscreen) {
113
        return wrapper.requestFullscreen();
114
      } else if (wrapper.webkitRequestFullscreen) {
115
        return wrapper.webkitRequestFullscreen();
116
      } else if (wrapper.msRequestFullscreen) {
117
        return wrapper.msRequestFullscreen();
118
      } else {
119
        return Promise.reject();
120
      }
121
    }
122
    function exitFullScreen() {
123
      if (document.exitFullscreen) {
124
        return document.exitFullscreen();
125
      } else if (document.webkitExitFullscreen) {
126
        return document.webkitExitFullscreen();
127
      } else if (document.msExitFullscreen) {
128
        return document.msExitFullscreen();
129
      } else {
130
        return Promise.reject();
131
      }
132
    }
133
134
    const defaults = {
135
      container: document.body,
136
      // window or element
137
      className: undefined,
138
      imageSize: 'scale-down',
139
      // 'scale-down', 'contain', 'cover' or 'native'
140
      fullScreen: false,
141
      loop: false,
142
      linkImages: true,
143
      setIndex: 0,
144
      firstImageIndex: 0,
145
      lastImageIndex: false,
146
      currentImageIndex: undefined,
147
      allowZoom: true,
148
      closeOnBackgroundClick: true,
149
      setTitle: function () {
150
        return '';
151
      },
152
      description: function () {
153
        return this.images[this.settings.currentImageIndex].title;
154
      },
155
      pagination: function () {
156
        const last = this.settings.lastImageIndex + 1;
157
        const position = this.settings.currentImageIndex + 1;
158
        return position + '/' + last;
159
      },
160
161
      afterInitialize() {},
162
163
      afterMarkup() {},
164
165
      afterImageLoad() {},
166
167
      afterClose() {},
168
169
      zoomedPaddingX: function (canvasWidth, imgWidth) {
170
        return 0;
171
      },
172
      zoomedPaddingY: function (canvasHeight, imgHeight) {
173
        return 0;
174
      }
175
    };
176
    class Chocolat {
177
      constructor(elements, settings) {
178
        this.settings = settings;
179
        this.elems = {};
180
        this.images = [];
181
        this.events = [];
182
        this.state = {
183
          fullScreenOpen: false,
184
          initialZoomState: null,
185
          initialized: false,
186
          timer: false,
187
          visible: false
188
        };
189
        this._cssClasses = ['chocolat-open', 'chocolat-in-container', 'chocolat-cover', 'chocolat-zoomable', 'chocolat-zoomed', 'chocolat-zooming-in', 'chocolat-zooming-out'];
190
191
        if (NodeList.prototype.isPrototypeOf(elements) || HTMLCollection.prototype.isPrototypeOf(elements)) {
192
          elements.forEach((el, i) => {
193
            this.images.push({
194
              title: el.getAttribute('title'),
195
              src: el.getAttribute('href'),
196
              srcset: el.getAttribute('data-srcset'),
197
              sizes: el.getAttribute('data-sizes')
198
            });
199
            this.off(el, 'click.chocolat');
200
            this.on(el, 'click.chocolat', e => {
201
              this.init(i);
202
              e.preventDefault();
203
            });
204
          });
205
        } else {
206
          this.images = elements;
207
        }
208
209
        if (this.settings.container instanceof Element || this.settings.container instanceof HTMLElement) {
210
          this.elems.container = this.settings.container;
211
        } else {
212
          this.elems.container = document.body;
213
        }
214
215
        this.api = {
216
          open: i => {
217
            i = parseInt(i) || 0;
218
            return this.init(i);
219
          },
220
          close: () => {
221
            return this.close();
222
          },
223
          next: () => {
224
            return this.change(1);
225
          },
226
          prev: () => {
227
            return this.change(-1);
228
          },
229
          goto: i => {
230
            return this.open(i);
231
          },
232
          current: () => {
233
            return this.settings.currentImageIndex;
234
          },
235
          position: () => {
236
            return this.position(this.elems.img);
237
          },
238
          destroy: () => {
239
            return this.destroy();
240
          },
241
          set: (property, value) => {
242
            this.settings[property] = value;
243
            return value;
244
          },
245
          get: property => {
246
            return this.settings[property];
247
          },
248
          getElem: name => {
249
            return this.elems[name];
250
          }
251
        };
252
      }
253
254
      init(i) {
255
        if (!this.state.initialized) {
256
          this.markup();
257
          this.attachListeners();
258
          this.settings.lastImageIndex = this.images.length - 1;
259
          this.state.initialized = true;
260
        }
261
262
        this.settings.afterInitialize.call(this);
263
        return this.load(i);
264
      }
265
266
      load(index) {
267
        if (!this.state.visible) {
268
          this.state.visible = true;
269
          setTimeout(() => {
270
            this.elems.overlay.classList.add('chocolat-visible');
271
            this.elems.wrapper.classList.add('chocolat-visible');
272
          }, 0);
273
          this.elems.container.classList.add('chocolat-open');
274
        }
275
276
        if (this.settings.fullScreen) {
277
          openFullScreen(this.elems.wrapper);
278
        }
279
280
        if (this.settings.currentImageIndex === index) {
281
          return Promise.resolve();
282
        }
283
284
        let loaderTimer = setTimeout(() => {
285
          this.elems.loader.classList.add('chocolat-visible');
286
        }, 1000);
287
        let fadeOutPromise;
288
        let image;
289
        let fadeOutTimer = setTimeout(() => {
290
          fadeOutTimer = undefined;
291
          fadeOutPromise = transitionAsPromise(() => {
292
            this.elems.imageCanvas.classList.remove('chocolat-visible');
293
          }, this.elems.imageCanvas);
294
        }, 80);
295
        return loadImage(this.images[index]).then(loadedImage => {
296
          image = loadedImage;
297
298
          if (fadeOutTimer) {
299
            clearTimeout(fadeOutTimer);
300
            return Promise.resolve();
301
          } else {
302
            return fadeOutPromise;
303
          }
304
        }).then(() => {
305
          const nextIndex = index + 1;
306
307
          if (this.images[nextIndex] != undefined) {
308
            loadImage(this.images[nextIndex]);
309
          }
310
311
          this.settings.currentImageIndex = index;
312
          this.elems.description.textContent = this.settings.description.call(this);
313
          this.elems.pagination.textContent = this.settings.pagination.call(this);
314
          this.arrows();
315
          return this.position(image).then(() => {
316
            this.elems.loader.classList.remove('chocolat-visible');
317
            clearTimeout(loaderTimer);
318
            return this.appear(image);
319
          });
320
        }).then(() => {
321
          this.elems.container.classList.toggle('chocolat-zoomable', this.zoomable(image, this.elems.wrapper));
322
          this.settings.afterImageLoad.call(this);
323
        });
324
      }
325
326
      position({
327
        naturalHeight,
328
        naturalWidth
329
      }) {
330
        const fitOptions = {
331
          imgHeight: naturalHeight,
332
          imgWidth: naturalWidth,
333
          containerHeight: this.elems.container.clientHeight,
334
          containerWidth: this.elems.container.clientWidth,
335
          canvasWidth: this.elems.imageCanvas.clientWidth,
336
          canvasHeight: this.elems.imageCanvas.clientHeight,
337
          imageSize: this.settings.imageSize
338
        };
339
        const {
340
          width,
341
          height
342
        } = fit(fitOptions);
343
        return transitionAsPromise(() => {
344
          Object.assign(this.elems.imageWrapper.style, {
345
            width: width + 'px',
346
            height: height + 'px'
347
          });
348
        }, this.elems.imageWrapper);
349
      }
350
351
      appear(image) {
352
        this.elems.imageWrapper.removeChild(this.elems.img);
353
        this.elems.img = image;
354
        this.elems.img.setAttribute('class', 'chocolat-img');
355
        this.elems.imageWrapper.appendChild(this.elems.img);
356
        const fadeInPromise = transitionAsPromise(() => {
357
          this.elems.imageCanvas.classList.add('chocolat-visible');
358
        }, this.elems.imageCanvas);
359
        return fadeInPromise;
360
      }
361
362
      change(step) {
363
        if (!this.state.visible) {
364
          return;
365
        }
366
367
        if (!this.settings.linkImages) {
368
          return;
369
        }
370
371
        this.zoomOut();
372
        const requestedImage = this.settings.currentImageIndex + parseInt(step);
373
374
        if (requestedImage > this.settings.lastImageIndex) {
375
          if (this.settings.loop) {
376
            return this.load(this.settings.firstImageIndex);
377
          }
378
        } else if (requestedImage < this.settings.firstImageIndex) {
379
          if (this.settings.loop) {
380
            return this.load(this.settings.lastImageIndex);
381
          }
382
        } else {
383
          return this.load(requestedImage);
384
        }
385
      }
386
387
      arrows() {
388
        if (this.settings.loop) {
389
          this.elems.left.classList.add('active');
390
          this.elems.right.classList.add('active');
391
        } else if (this.settings.linkImages) {
392
          this.elems.right.classList.toggle('active', this.settings.currentImageIndex !== this.settings.lastImageIndex);
393
          this.elems.left.classList.toggle('active', this.settings.currentImageIndex !== this.settings.firstImageIndex);
394
        } else {
395
          this.elems.left.classList.remove('active');
396
          this.elems.right.classList.remove('active');
397
        }
398
      }
399
400
      close() {
401
        if (this.state.fullScreenOpen) {
402
          exitFullScreen();
403
          return;
404
        }
405
406
        this.state.visible = false;
407
        const promiseOverlay = transitionAsPromise(() => {
408
          this.elems.overlay.classList.remove('chocolat-visible');
409
        }, this.elems.overlay);
410
        const promiseWrapper = transitionAsPromise(() => {
411
          this.elems.wrapper.classList.remove('chocolat-visible');
412
        }, this.elems.wrapper);
413
        return Promise.all([promiseOverlay, promiseWrapper]).then(() => {
414
          this.elems.container.classList.remove('chocolat-open');
415
          this.settings.afterClose.call(this);
416
        });
417
      }
418
419
      destroy() {
420
        for (let i = this.events.length - 1; i >= 0; i--) {
421
          const {
422
            element,
423
            eventName
424
          } = this.events[i];
425
          this.off(element, eventName);
426
        }
427
428
        if (!this.state.initialized) {
429
          return;
430
        }
431
432
        if (this.state.fullScreenOpen) {
433
          exitFullScreen();
434
        }
435
436
        this.settings.currentImageIndex = undefined;
437
        this.state.visible = false;
438
        this.state.initialized = false;
439
        this.elems.container.classList.remove(...this._cssClasses);
440
        this.elems.wrapper.parentNode.removeChild(this.elems.wrapper);
441
      }
442
443
      markup() {
444
        this.elems.container.classList.add('chocolat-open', this.settings.className);
445
446
        if (this.settings.imageSize == 'cover') {
447
          this.elems.container.classList.add('chocolat-cover');
448
        }
449
450
        if (this.elems.container !== document.body) {
451
          this.elems.container.classList.add('chocolat-in-container');
452
        }
453
454
        this.elems.wrapper = document.createElement('div');
455
        this.elems.wrapper.setAttribute('id', 'chocolat-content-' + this.settings.setIndex);
456
        this.elems.wrapper.setAttribute('class', 'chocolat-wrapper');
457
        this.elems.container.appendChild(this.elems.wrapper);
458
        this.elems.overlay = document.createElement('div');
459
        this.elems.overlay.setAttribute('class', 'chocolat-overlay');
460
        this.elems.wrapper.appendChild(this.elems.overlay);
461
        this.elems.loader = document.createElement('div');
462
        this.elems.loader.setAttribute('class', 'chocolat-loader');
463
        this.elems.wrapper.appendChild(this.elems.loader);
464
        this.elems.layout = document.createElement('div');
465
        this.elems.layout.setAttribute('class', 'chocolat-layout');
466
        this.elems.wrapper.appendChild(this.elems.layout);
467
        this.elems.top = document.createElement('div');
468
        this.elems.top.setAttribute('class', 'chocolat-top');
469
        this.elems.layout.appendChild(this.elems.top);
470
        this.elems.center = document.createElement('div');
471
        this.elems.center.setAttribute('class', 'chocolat-center');
472
        this.elems.layout.appendChild(this.elems.center);
473
        this.elems.left = document.createElement('div');
474
        this.elems.left.setAttribute('class', 'chocolat-left');
475
        this.elems.center.appendChild(this.elems.left);
476
        this.elems.imageCanvas = document.createElement('div');
477
        this.elems.imageCanvas.setAttribute('class', 'chocolat-image-canvas');
478
        this.elems.center.appendChild(this.elems.imageCanvas);
479
        this.elems.imageWrapper = document.createElement('div');
480
        this.elems.imageWrapper.setAttribute('class', 'chocolat-image-wrapper');
481
        this.elems.imageCanvas.appendChild(this.elems.imageWrapper);
482
        this.elems.img = document.createElement('img');
483
        this.elems.img.setAttribute('class', 'chocolat-img');
484
        this.elems.imageWrapper.appendChild(this.elems.img);
485
        this.elems.right = document.createElement('div');
486
        this.elems.right.setAttribute('class', 'chocolat-right');
487
        this.elems.center.appendChild(this.elems.right);
488
        this.elems.bottom = document.createElement('div');
489
        this.elems.bottom.setAttribute('class', 'chocolat-bottom');
490
        this.elems.layout.appendChild(this.elems.bottom);
491
        this.elems.close = document.createElement('span');
492
        this.elems.close.setAttribute('class', 'chocolat-close');
493
        this.elems.top.appendChild(this.elems.close);
494
        this.elems.description = document.createElement('span');
495
        this.elems.description.setAttribute('class', 'chocolat-description');
496
        this.elems.bottom.appendChild(this.elems.description);
497
        this.elems.pagination = document.createElement('span');
498
        this.elems.pagination.setAttribute('class', 'chocolat-pagination');
499
        this.elems.bottom.appendChild(this.elems.pagination);
500
        this.elems.setTitle = document.createElement('span');
501
        this.elems.setTitle.setAttribute('class', 'chocolat-set-title');
502
        this.elems.setTitle.textContent = this.settings.setTitle();
503
        this.elems.bottom.appendChild(this.elems.setTitle);
504
        this.elems.fullscreen = document.createElement('span');
505
        this.elems.fullscreen.setAttribute('class', 'chocolat-fullscreen');
506
        this.elems.bottom.appendChild(this.elems.fullscreen);
507
        this.settings.afterMarkup.call(this);
508
      }
509
510
      attachListeners() {
511
        this.off(document, 'keydown.chocolat');
512
        this.on(document, 'keydown.chocolat', e => {
513
          if (this.state.initialized) {
514
            if (e.keyCode == 37) {
515
              this.change(-1);
516
            } else if (e.keyCode == 39) {
517
              this.change(1);
518
            } else if (e.keyCode == 27) {
519
              this.close();
520
            }
521
          }
522
        });
523
        const right = this.elems.wrapper.querySelector('.chocolat-right');
524
        this.off(right, 'click.chocolat');
525
        this.on(right, 'click.chocolat', () => {
526
          this.change(+1);
527
        });
528
        const left = this.elems.wrapper.querySelector('.chocolat-left');
529
        this.off(left, 'click.chocolat');
530
        this.on(left, 'click.chocolat', () => {
531
          this.change(-1);
532
        });
533
        this.off(this.elems.close, 'click.chocolat');
534
        this.on(this.elems.close, 'click.chocolat', this.close.bind(this));
535
        this.off(this.elems.fullscreen, 'click.chocolat');
536
        this.on(this.elems.fullscreen, 'click.chocolat', () => {
537
          if (this.state.fullScreenOpen) {
538
            exitFullScreen();
539
            return;
540
          }
541
542
          openFullScreen(this.elems.wrapper);
543
        });
544
        this.off(document, 'fullscreenchange.chocolat');
545
        this.on(document, 'fullscreenchange.chocolat', () => {
546
          if (document.fullscreenElement || document.webkitCurrentFullScreenElement || document.webkitFullscreenElement) {
547
            this.state.fullScreenOpen = true;
548
          } else {
549
            this.state.fullScreenOpen = false;
550
          }
551
        });
552
        this.off(document, 'webkitfullscreenchange.chocolat');
553
        this.on(document, 'webkitfullscreenchange.chocolat', () => {
554
          if (document.fullscreenElement || document.webkitCurrentFullScreenElement || document.webkitFullscreenElement) {
555
            this.state.fullScreenOpen = true;
556
          } else {
557
            this.state.fullScreenOpen = false;
558
          }
559
        });
560
561
        if (this.settings.closeOnBackgroundClick) {
562
          this.off(this.elems.overlay, 'click.chocolat');
563
          this.on(this.elems.overlay, 'click.chocolat', this.close.bind(this));
564
        }
565
566
        this.off(this.elems.wrapper, 'click.chocolat');
567
        this.on(this.elems.wrapper, 'click.chocolat', () => {
568
          if (this.state.initialZoomState === null || !this.state.visible) {
569
            return;
570
          }
571
572
          this.elems.container.classList.add('chocolat-zooming-out');
573
          this.zoomOut().then(() => {
574
            this.elems.container.classList.remove('chocolat-zoomed');
575
            this.elems.container.classList.remove('chocolat-zooming-out');
576
          });
577
        });
578
        this.off(this.elems.imageWrapper, 'click.chocolat');
579
        this.on(this.elems.imageWrapper, 'click.chocolat', e => {
580
          if (this.state.initialZoomState === null && this.elems.container.classList.contains('chocolat-zoomable')) {
581
            e.stopPropagation();
582
            this.elems.container.classList.add('chocolat-zooming-in');
583
            this.zoomIn(e).then(() => {
584
              this.elems.container.classList.add('chocolat-zoomed');
585
              this.elems.container.classList.remove('chocolat-zooming-in');
586
            });
587
          }
588
        });
589
        this.on(this.elems.wrapper, 'mousemove.chocolat', e => {
590
          if (this.state.initialZoomState === null || !this.state.visible) {
591
            return;
592
          }
593
594
          const rect = this.elems.wrapper.getBoundingClientRect();
595
          const pos = {
596
            top: rect.top + window.scrollY,
597
            left: rect.left + window.scrollX
598
          };
599
          const height = this.elems.wrapper.clientHeight;
600
          const width = this.elems.wrapper.clientWidth;
601
          const imgWidth = this.elems.img.width;
602
          const imgHeight = this.elems.img.height;
603
          const coord = [e.pageX - width / 2 - pos.left, e.pageY - height / 2 - pos.top];
604
          let mvtX = 0;
605
606
          if (imgWidth > width) {
607
            const paddingX = this.settings.zoomedPaddingX(imgWidth, width);
608
            mvtX = coord[0] / (width / 2);
609
            mvtX = ((imgWidth - width) / 2 + paddingX) * mvtX;
610
          }
611
612
          let mvtY = 0;
613
614
          if (imgHeight > height) {
615
            const paddingY = this.settings.zoomedPaddingY(imgHeight, height);
616
            mvtY = coord[1] / (height / 2);
617
            mvtY = ((imgHeight - height) / 2 + paddingY) * mvtY;
618
          }
619
620
          this.elems.img.style.marginLeft = -mvtX + 'px';
621
          this.elems.img.style.marginTop = -mvtY + 'px';
622
        });
623
        this.on(window, 'resize.chocolat', e => {
624
          if (!this.state.initialized || !this.state.visible) {
625
            return;
626
          }
627
628
          debounce(50, () => {
629
            const fitOptions = {
630
              imgHeight: this.elems.img.naturalHeight,
631
              imgWidth: this.elems.img.naturalWidth,
632
              containerHeight: this.elems.wrapper.clientHeight,
633
              containerWidth: this.elems.wrapper.clientWidth,
634
              canvasWidth: this.elems.imageCanvas.clientWidth,
635
              canvasHeight: this.elems.imageCanvas.clientHeight,
636
              imageSize: this.settings.imageSize
637
            };
638
            const {
639
              width,
640
              height
641
            } = fit(fitOptions);
642
            this.position(this.elems.img).then(() => {
643
              this.elems.container.classList.toggle('chocolat-zoomable', this.zoomable(this.elems.img, this.elems.wrapper));
644
            });
645
          });
646
        });
647
      }
648
649
      zoomable(image, wrapper) {
650
        const wrapperWidth = wrapper.clientWidth;
651
        const wrapperHeight = wrapper.clientHeight;
652
        const isImageZoomable = this.settings.allowZoom && (image.naturalWidth > wrapperWidth || image.naturalHeight > wrapperHeight) ? true : false;
653
        const isImageStretched = image.clientWidth > image.naturalWidth || image.clientHeight > image.naturalHeight;
654
        return isImageZoomable && !isImageStretched;
655
      }
656
657
      zoomIn(e) {
658
        this.state.initialZoomState = this.settings.imageSize;
659
        this.settings.imageSize = 'native';
660
        return this.position(this.elems.img);
661
      }
662
663
      zoomOut(e) {
664
        this.settings.imageSize = this.state.initialZoomState || this.settings.imageSize;
665
        this.state.initialZoomState = null;
666
        this.elems.img.style.margin = 0;
667
        return this.position(this.elems.img);
668
      }
669
670
      on(element, eventName, cb) {
671
        // const eventName = this.settings.setIndex + '-' + eventName
672
        const length = this.events.push({
673
          element,
674
          eventName,
675
          cb
676
        });
677
        element.addEventListener(eventName.split('.')[0], this.events[length - 1].cb);
678
      }
679
680
      off(element, eventName) {
681
        // const eventName = this.settings.setIndex + '-' + eventName
682
        const index = this.events.findIndex(event => {
683
          return event.element === element && event.eventName === eventName;
684
        });
685
686
        if (this.events[index]) {
687
          element.removeEventListener(eventName.split('.')[0], this.events[index].cb);
688
          this.events.splice(index, 1);
689
        }
690
      }
691
692
    }
693
694
    const instances = [];
695
696
    window.Chocolat = function (elements, options) {
697
      const settings = Object.assign({}, defaults, {
698
        images: []
699
      }, options, {
700
        setIndex: instances.length
701
      });
702
      const instance = new Chocolat(elements, settings);
703
      instances.push(instance);
704
      return instance;
705
    };
706
707
}());
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/about.tt (-1 / +4 lines)
Lines 805-810 Link Here
805
805
806
            <h2>Moment.js</h2>
806
            <h2>Moment.js</h2>
807
            <p><a href="https://momentjs.com/">Moment.js</a> licensed under the <a href="https://github.com/moment/moment/blob/develop/LICENSE">MIT license</a>.</p>
807
            <p><a href="https://momentjs.com/">Moment.js</a> licensed under the <a href="https://github.com/moment/moment/blob/develop/LICENSE">MIT license</a>.</p>
808
809
            <h2>Chocolat.js</h2>
810
            <p><a href="http://chocolat.insipi.de">Chocolat.js</a> licensed under the <a href="https://github.com/nicolas-t/Chocolat/blob/master/LICENSE">GPL v3 license</a>.</p>
811
808
        </div>
812
        </div>
809
813
810
        <div id="translations">
814
        <div id="translations">
811
- 

Return to bug 28179