Commit | Line | Data |
---|---|---|
e368b357 AE |
1 | "use strict"; |
2 | ||
e471f286 | 3 | /** |
9a2a5bfc | 4 | * Enhanced image viewer for WCF. |
e471f286 | 5 | * |
e471f286 | 6 | * @author Alexander Ebert |
c839bd49 | 7 | * @copyright 2001-2018 WoltLab GmbH |
e471f286 AE |
8 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> |
9 | */ | |
10 | WCF.ImageViewer = Class.extend({ | |
11 | /** | |
9a2a5bfc AE |
12 | * trigger element to mimic a slideshow button |
13 | * @var jQuery | |
14 | */ | |
15 | _triggerElement: null, | |
16 | ||
17 | /** | |
18 | * Initializes the WCF.ImageViewer class. | |
e471f286 AE |
19 | */ |
20 | init: function() { | |
9a2a5bfc AE |
21 | this._triggerElement = $('<span class="wcfImageViewerTriggerElement" />').data('disableSlideshow', true).hide().appendTo(document.body); |
22 | this._triggerElement.wcfImageViewer({ | |
23 | enableSlideshow: 0, | |
24 | imageSelector: '.jsImageViewerEnabled', | |
25 | staticViewer: true | |
26 | }); | |
42d7d2cc AE |
27 | |
28 | WCF.DOMNodeInsertedHandler.addCallback('WCF.ImageViewer', $.proxy(this._domNodeInserted, this)); | |
29 | WCF.DOMNodeInsertedHandler.execute(); | |
cd000ad8 AE |
30 | }, |
31 | ||
32 | /** | |
33 | * Executes actions upon DOMNodeInserted events. | |
34 | */ | |
35 | _domNodeInserted: function() { | |
36 | this._initImageSizeCheck(); | |
9a2a5bfc | 37 | this._rebuildImageViewer(); |
cd000ad8 AE |
38 | }, |
39 | ||
40 | /** | |
9a2a5bfc | 41 | * Rebuilds the image viewer. |
cd000ad8 | 42 | */ |
9a2a5bfc | 43 | _rebuildImageViewer: function() { |
f9520da6 AE |
44 | var $links = $('a.jsImageViewer'); |
45 | if ($links.length) { | |
9a2a5bfc | 46 | $links.removeClass('jsImageViewer').addClass('jsImageViewerEnabled').click($.proxy(this._click, this)); |
f9520da6 | 47 | } |
e471f286 AE |
48 | }, |
49 | ||
50 | /** | |
9a2a5bfc AE |
51 | * Handles click on an image with image viewer support. |
52 | * | |
53 | * @param object event | |
e471f286 | 54 | */ |
9a2a5bfc | 55 | _click: function(event) { |
823532b0 AE |
56 | // ignore clicks while ctrl key is being pressed |
57 | if (event.ctrlKey) { | |
58 | return; | |
59 | } | |
60 | ||
9a2a5bfc AE |
61 | event.preventDefault(); |
62 | event.stopPropagation(); | |
b391396f MW |
63 | // skip if element is in a popover |
64 | if ($(event.currentTarget).closest('.popover').length) return; | |
9a2a5bfc AE |
65 | |
66 | this._triggerElement.wcfImageViewer('open', null, $(event.currentTarget).wcfIdentify()); | |
0d69ce6e MW |
67 | }, |
68 | ||
69 | /** | |
70 | * Initializes the image size check. | |
71 | */ | |
72 | _initImageSizeCheck: function() { | |
7961df36 MW |
73 | $('.jsResizeImage').each($.proxy(function(index, image) { |
74 | if (image.complete) this._checkImageSize({ currentTarget: image }); | |
75 | }, this)); | |
76 | ||
0d69ce6e MW |
77 | $('.jsResizeImage').on('load', $.proxy(this._checkImageSize, this)); |
78 | }, | |
79 | ||
80 | /** | |
81 | * Checks the image size. | |
82 | */ | |
83 | _checkImageSize: function(event) { | |
84 | var $image = $(event.currentTarget); | |
38b3a9d7 AE |
85 | if (!$image.is(':visible')) { |
86 | $image.off('load'); | |
87 | ||
88 | return; | |
89 | } | |
90 | ||
0d69ce6e | 91 | $image.removeClass('jsResizeImage'); |
0d69ce6e | 92 | |
309c94cf AE |
93 | // check if image falls within the signature, in that case ignore it |
94 | if ($image.closest('.messageSignature').length) { | |
95 | return; | |
96 | } | |
97 | ||
719046d7 AE |
98 | // setting img { max-width: 100% } causes the image to fit within boundaries, but does not reveal the original dimenions |
99 | var $imageObject = new Image(); | |
100 | $imageObject.src = $image.attr('src'); | |
101 | ||
510cccdd | 102 | var $maxWidth = $image.closest('div.messageText, div.messageTextPreview').width(); |
719046d7 | 103 | if ($maxWidth < $imageObject.width) { |
0d69ce6e | 104 | if (!$image.parents('a').length) { |
d35f16da | 105 | $image.wrap('<a href="' + $image.attr('src') + '" class="jsImageViewerEnabled embeddedImageLink" />'); |
efdaa080 | 106 | $image.parent().click($.proxy(this._click, this)); |
76b7aa02 MW |
107 | |
108 | if ($image.css('float') == 'right') { | |
109 | $image.parent().addClass('messageFloatObjectRight'); | |
110 | } | |
111 | else if ($image.css('float') == 'left') { | |
112 | $image.parent().addClass('messageFloatObjectLeft'); | |
113 | } | |
114 | $image[0].style.removeProperty('float'); | |
115 | $image[0].style.removeProperty('margin'); | |
0d69ce6e MW |
116 | } |
117 | } | |
29657d14 MW |
118 | else { |
119 | $image.removeClass('embeddedAttachmentLink'); | |
120 | } | |
e471f286 | 121 | } |
dd5df48e AE |
122 | }); |
123 | ||
124 | /** | |
125 | * Provides a focused image viewer for WCF. | |
126 | * | |
127 | * Usage: | |
128 | * $('.triggerElement').wcfImageViewer({ | |
129 | * shiftBy: 5, | |
130 | * | |
131 | * enableSlideshow: 1, | |
132 | * speed: 5, | |
133 | * | |
134 | * className: 'wcf\\data\\foo\\FooAction' | |
135 | * }); | |
136 | */ | |
137 | $.widget('ui.wcfImageViewer', { | |
138 | /** | |
139 | * active image index | |
140 | * @var integer | |
141 | */ | |
142 | _active: -1, | |
143 | ||
144 | /** | |
145 | * active image object id | |
146 | * @var integer | |
147 | */ | |
148 | _activeImage: null, | |
149 | ||
150 | /** | |
151 | * image viewer container object | |
152 | * @var jQuery | |
153 | */ | |
154 | _container: null, | |
155 | ||
156 | /** | |
157 | * initialization state | |
158 | * @var boolean | |
159 | */ | |
160 | _didInit: false, | |
161 | ||
d3d05fd9 AE |
162 | /** |
163 | * overrides slideshow settings unless explicitly enabled by user | |
164 | * @var boolean | |
165 | */ | |
166 | _disableSlideshow: false, | |
167 | ||
77404ea9 AE |
168 | /** |
169 | * event namespace used to distinguish event handlers using $.proxy | |
170 | * @var string | |
171 | */ | |
172 | _eventNamespace: '', | |
173 | ||
dd5df48e AE |
174 | /** |
175 | * list of available images | |
176 | * @var array<object> | |
177 | */ | |
178 | _images: [ ], | |
179 | ||
fa7549e1 AE |
180 | /** |
181 | * true if image viewer uses the mobile-optimized UI | |
182 | * @var boolean | |
183 | */ | |
184 | _isMobile: false, | |
185 | ||
dd5df48e AE |
186 | /** |
187 | * true if image viewer is open | |
188 | * @var boolean | |
189 | */ | |
190 | _isOpen: false, | |
191 | ||
192 | /** | |
193 | * number of total images | |
194 | * @var integer | |
195 | */ | |
196 | _items: -1, | |
197 | ||
198 | /** | |
199 | * maximum dimensions for enlarged view | |
200 | * @var object<integer> | |
201 | */ | |
202 | _maxDimensions: { | |
203 | height: 0, | |
204 | width: 0 | |
205 | }, | |
206 | ||
207 | /** | |
208 | * action proxy object | |
209 | * @var WCF.Action.Proxy | |
210 | */ | |
211 | _proxy: null, | |
212 | ||
213 | /** | |
214 | * true if slideshow is currently running | |
215 | * @var boolean | |
216 | */ | |
217 | _slideshowEnabled: false, | |
218 | ||
219 | /** | |
220 | * visible width of thumbnail container | |
221 | * @var integer | |
222 | */ | |
223 | _thumbnailContainerWidth: 0, | |
224 | ||
225 | /** | |
226 | * right margin of a thumbnail | |
227 | * @var integer | |
228 | */ | |
229 | _thumbnailMarginRight: 0, | |
230 | ||
231 | /** | |
232 | * left offset of thumbnail list | |
233 | * @var integer | |
234 | */ | |
235 | _thumbnailOffset: 0, | |
236 | ||
237 | /** | |
238 | * outer width of a thumbnail (includes margin) | |
239 | * @var integer | |
240 | */ | |
241 | _thumbnailWidth: 0, | |
242 | ||
243 | /** | |
244 | * slideshow timer object | |
245 | * @var WCF.PeriodicalExecuter | |
246 | */ | |
247 | _timer: null, | |
248 | ||
249 | /** | |
250 | * list of interface elements | |
251 | * @var object<jQuery> | |
252 | */ | |
253 | _ui: { | |
254 | buttonNext: null, | |
255 | buttonPrevious: null, | |
256 | header: null, | |
257 | image: null, | |
258 | imageContainer: null, | |
259 | imageList: null, | |
260 | slideshow: { | |
261 | container: null, | |
262 | enlarge: null, | |
263 | next: null, | |
264 | previous: null, | |
265 | toggle: null | |
266 | } | |
267 | }, | |
268 | ||
269 | /** | |
270 | * list of options parsed during init | |
271 | * @var object<mixed> | |
272 | */ | |
273 | options: { | |
274 | // navigation | |
275 | shiftBy: 5, // thumbnail slider control | |
276 | ||
277 | // slideshow | |
278 | enableSlideshow: 1, | |
279 | speed: 5, // time in seconds | |
280 | ||
281 | // ajax | |
fa7549e1 AE |
282 | className: '', // must be an instance of \wcf\data\IImageViewerAction |
283 | ||
284 | // alternative mode - static view | |
285 | imageSelector: '', | |
286 | staticViewer: false | |
dd5df48e AE |
287 | }, |
288 | ||
289 | /** | |
290 | * Creates a new wcfImageViewer instance. | |
291 | */ | |
292 | _create: function() { | |
293 | this._active = -1; | |
294 | this._activeImage = null; | |
295 | this._container = null; | |
296 | this._didInit = false; | |
d3d05fd9 | 297 | this._disableSlideshow = (this.element.data('disableSlideshow')); |
77404ea9 | 298 | this._eventNamespace = this.element.wcfIdentify(); |
dd5df48e | 299 | this._images = [ ]; |
fa7549e1 | 300 | this._isMobile = false; |
dd5df48e AE |
301 | this._isOpen = false; |
302 | this._items = -1; | |
303 | this._maxDimensions = { | |
304 | height: document.documentElement.clientHeight, | |
305 | width: document.documentElement.clientWidth | |
306 | }; | |
307 | this._proxy = new WCF.Action.Proxy({ | |
308 | success: $.proxy(this._success, this) | |
309 | }); | |
310 | this._slideshowEnabled = false; | |
311 | this._thumbnailContainerWidth = 0; | |
312 | this._thumbnailMarginRight = 0; | |
313 | this._thumbnailOffset = 0; | |
314 | this._thumbnaiLWidth = 0; | |
315 | this._timer = null; | |
316 | this._ui = { }; | |
317 | ||
318 | this.element.click($.proxy(this.open, this)); | |
ead50260 TD |
319 | |
320 | window.addEventListener('popstate', (function(event) { | |
67bcb900 | 321 | if (event.state != null && event.state.name === 'imageViewer') { |
ead50260 TD |
322 | if (event.state.container === this._eventNamespace) { |
323 | this.open(event); | |
324 | this.showImage(event.state.image); | |
325 | ||
326 | return; | |
327 | } | |
328 | } | |
329 | ||
330 | this.close(event); | |
331 | }).bind(this)); | |
dd5df48e AE |
332 | }, |
333 | ||
334 | /** | |
335 | * Opens the image viewer. | |
336 | * | |
c96906ac | 337 | * @param object event |
fa7549e1 | 338 | * @param string targetImageElementID |
dd5df48e AE |
339 | * @return boolean |
340 | */ | |
fa7549e1 | 341 | open: function(event, targetImageElementID) { |
c96906ac AE |
342 | if (event) event.preventDefault(); |
343 | ||
dd5df48e AE |
344 | if (this._isOpen) { |
345 | return false; | |
346 | } | |
347 | ||
ead50260 TD |
348 | // add history item for the image viewer |
349 | if (!event || event.type !== 'popstate') { | |
350 | window.history.pushState({ | |
351 | name: 'imageViewer' | |
352 | }, '', ''); | |
353 | } | |
354 | ||
fa7549e1 AE |
355 | if (this.options.staticViewer) { |
356 | var $images = this._getStaticImages(); | |
357 | this._initUI(); | |
358 | this._createThumbnails($images, true); | |
359 | this._render(true, undefined, targetImageElementID); | |
360 | ||
361 | this._isOpen = true; | |
362 | ||
363 | WCF.System.DisableScrolling.disable(); | |
9c31391d | 364 | WCF.System.DisableZoom.disable(); |
fa7549e1 AE |
365 | |
366 | // switch to fullscreen mode on smartphones | |
367 | if ($.browser.touch) { | |
368 | setTimeout($.proxy(function() { | |
369 | if (this._isMobile && !this._container.hasClass('maximized')) { | |
370 | this._toggleView(); | |
371 | } | |
372 | }, this), 500); | |
373 | } | |
dd5df48e AE |
374 | } |
375 | else { | |
fa7549e1 AE |
376 | if (this._images.length === 0) { |
377 | this._loadNextImages(true); | |
dd5df48e | 378 | } |
fa7549e1 AE |
379 | else { |
380 | this._render(false, this.element.data('targetImageID')); | |
381 | ||
382 | if (this._items > 1 && this._slideshowEnabled) { | |
383 | this.startSlideshow(); | |
384 | } | |
385 | ||
386 | this._isOpen = true; | |
1a6e8c52 | 387 | |
fa7549e1 | 388 | WCF.System.DisableScrolling.disable(); |
9c31391d | 389 | WCF.System.DisableZoom.disable(); |
fa7549e1 | 390 | } |
dd5df48e AE |
391 | } |
392 | ||
2bb74999 | 393 | this._bindListener(); |
77404ea9 | 394 | |
0c35c3e7 AE |
395 | document.documentElement.classList.add('pageOverlayActive'); |
396 | ||
dd5df48e AE |
397 | return true; |
398 | }, | |
399 | ||
400 | /** | |
401 | * Closes the image viewer. | |
402 | * | |
403 | * @return boolean | |
404 | */ | |
63c497a3 | 405 | close: function(event) { |
c96906ac AE |
406 | if (event) event.preventDefault(); |
407 | ||
ead50260 TD |
408 | // clear history item of the image viewer |
409 | if (!event || event.type !== 'popstate') { | |
410 | window.history.back(); | |
411 | return; | |
412 | } | |
413 | ||
dd5df48e AE |
414 | if (!this._isOpen) { |
415 | return false; | |
416 | } | |
417 | ||
418 | this._container.removeClass('open'); | |
419 | if (this._timer !== null) { | |
420 | this._timer.stop(); | |
421 | } | |
422 | ||
2bb74999 | 423 | this._unbindListener(); |
77404ea9 | 424 | |
dd5df48e AE |
425 | this._isOpen = false; |
426 | ||
ff34ee1f | 427 | WCF.System.DisableScrolling.enable(); |
9c31391d | 428 | WCF.System.DisableZoom.enable(); |
ff34ee1f | 429 | |
0c35c3e7 AE |
430 | document.documentElement.classList.remove('pageOverlayActive'); |
431 | ||
dd5df48e AE |
432 | return true; |
433 | }, | |
434 | ||
435 | /** | |
436 | * Enables the slideshow. | |
437 | * | |
438 | * @return boolean | |
439 | */ | |
440 | startSlideshow: function() { | |
d3d05fd9 | 441 | if (this._disableSlideshow || this._slideshowEnabled) { |
dd5df48e AE |
442 | return false; |
443 | } | |
444 | ||
ff34ee1f AE |
445 | if (this._timer === null) { |
446 | this._timer = new WCF.PeriodicalExecuter($.proxy(function() { | |
447 | var $index = this._active + 1; | |
448 | if ($index == this._items) { | |
449 | $index = 0; | |
450 | } | |
451 | ||
452 | this.showImage($index); | |
453 | }, this), this.options.speed * 1000); | |
454 | } | |
455 | else { | |
456 | this._timer.resume(); | |
457 | } | |
dd5df48e AE |
458 | |
459 | this._slideshowEnabled = true; | |
460 | ||
ca8bfa53 | 461 | this._ui.slideshow.toggle.children('span').removeClass('fa-play').addClass('fa-pause'); |
dd5df48e AE |
462 | |
463 | return true; | |
464 | }, | |
465 | ||
466 | /** | |
467 | * Disables the slideshow. | |
468 | * | |
ff34ee1f | 469 | * @param boolean disableSlideshow |
dd5df48e AE |
470 | * @return boolean |
471 | */ | |
ff34ee1f | 472 | stopSlideshow: function(disableSlideshow) { |
dd5df48e AE |
473 | if (!this._slideshowEnabled) { |
474 | return false; | |
475 | } | |
476 | ||
477 | this._timer.stop(); | |
4c6b5841 | 478 | if (disableSlideshow) { |
ca8bfa53 | 479 | this._ui.slideshow.toggle.children('span').removeClass('fa-pause').addClass('fa-play'); |
4c6b5841 | 480 | } |
dd5df48e AE |
481 | |
482 | this._slideshowEnabled = false; | |
483 | ||
484 | return true; | |
485 | }, | |
486 | ||
2bb74999 AE |
487 | /** |
488 | * Binds event listeners. | |
489 | */ | |
490 | _bindListener: function() { | |
491 | $(document).on('keydown.' + this._eventNamespace, $.proxy(this._keyDown, this)); | |
492 | $(window).on('resize.' + this._eventNamespace, $.proxy(this._renderImage, this)); | |
493 | }, | |
494 | ||
495 | /** | |
496 | * Unbinds event listeners. | |
497 | */ | |
498 | _unbindListener: function() { | |
499 | $(document).off('keydown.' + this._eventNamespace); | |
500 | $(window).off('resize.' + this._eventNamespace); | |
501 | }, | |
502 | ||
77404ea9 AE |
503 | /** |
504 | * Closes the slideshow on escape. | |
505 | * | |
506 | * @param object event | |
507 | * @return boolean | |
508 | */ | |
509 | _keyDown: function(event) { | |
510 | switch (event.which) { | |
511 | // close slideshow | |
512 | case $.ui.keyCode.ESCAPE: | |
513 | this.close(); | |
514 | break; | |
515 | ||
516 | // show previous image | |
517 | case $.ui.keyCode.LEFT: | |
518 | this._previousImage(); | |
519 | break; | |
520 | ||
521 | // show next image | |
522 | case $.ui.keyCode.RIGHT: | |
523 | this._nextImage(); | |
524 | break; | |
525 | ||
526 | // enable fullscreen mode | |
527 | case $.ui.keyCode.UP: | |
528 | if (!this._container.hasClass('maximized')) { | |
529 | this._toggleView(); | |
530 | } | |
531 | break; | |
532 | ||
533 | // disable fullscreen mode | |
534 | case $.ui.keyCode.DOWN: | |
535 | if (this._container.hasClass('maximized')) { | |
536 | this._toggleView(); | |
537 | } | |
538 | break; | |
539 | ||
540 | // jump to image page or full version | |
541 | case $.ui.keyCode.ENTER: | |
3166e052 | 542 | var $link = this._ui.header.find('h1 > a'); |
77404ea9 AE |
543 | if ($link.length == 1) { |
544 | // forward to image page | |
545 | window.location = $link.prop('href'); | |
546 | } | |
547 | else { | |
548 | // forward to full version | |
549 | this._ui.slideshow.full.trigger('click'); | |
550 | } | |
551 | break; | |
552 | ||
553 | // toggle play/pause (80 = [p]) | |
554 | case 80: | |
555 | this._ui.slideshow.toggle.trigger('click'); | |
556 | break; | |
c16cb4ba AE |
557 | |
558 | default: | |
559 | return true; | |
560 | break; | |
77404ea9 AE |
561 | } |
562 | ||
563 | return false; | |
564 | }, | |
565 | ||
dd5df48e AE |
566 | /** |
567 | * Renders the image viewer UI. | |
568 | * | |
569 | * @param boolean initialized | |
d3d05fd9 | 570 | * @param integer targetImageID |
fa7549e1 | 571 | * @param string targetImageElementID |
dd5df48e | 572 | */ |
fa7549e1 | 573 | _render: function(initialized, targetImageID, targetImageElementID) { |
dd5df48e | 574 | this._container.addClass('open'); |
04e1c42a | 575 | |
c96906ac | 576 | var $thumbnail = null; |
dd5df48e | 577 | if (initialized) { |
c96906ac | 578 | $thumbnail = this._ui.imageList.children('li:eq(0)'); |
dd5df48e AE |
579 | this._thumbnailMarginRight = parseInt($thumbnail.css('marginRight').replace(/px$/, '')) || 0; |
580 | this._thumbnailWidth = $thumbnail.outerWidth(true); | |
581 | this._thumbnailContainerWidth = this._ui.imageList.parent().innerWidth(); | |
582 | ||
fa7549e1 | 583 | if (this._items > 1 && this.options.enableSlideshow && !targetImageID && !targetImageElementID) { |
dd5df48e AE |
584 | this.startSlideshow(); |
585 | } | |
586 | } | |
587 | ||
c96906ac AE |
588 | if (targetImageID) { |
589 | this._ui.imageList.children('li').each($.proxy(function(index, item) { | |
590 | var $item = $(item); | |
591 | if ($item.data('objectID') == targetImageID) { | |
592 | $item.trigger('click'); | |
593 | this.moveToImage($item.data('index')); | |
594 | ||
595 | return false; | |
596 | } | |
597 | }, this)); | |
598 | } | |
e9b1b686 | 599 | else if (targetImageElementID) { |
fa7549e1 AE |
600 | var $i = 0; |
601 | $(this.options.imageSelector).each(function(index, element) { | |
602 | if ($(element).wcfIdentify() == targetImageElementID) { | |
603 | $i = index; | |
604 | ||
605 | return false; | |
606 | } | |
607 | }); | |
608 | ||
609 | var $item = this._ui.imageList.children('li:eq(' + $i + ')'); | |
7c76da5d AE |
610 | |
611 | // check if currently active image does not exist anymore | |
612 | if (this._active !== -1) { | |
613 | var $clear = false; | |
614 | if (this._active != $item.data('index')) { | |
615 | $clear = true; | |
616 | } | |
617 | ||
618 | if (this._ui.images[this._activeImage].prop('src') != this._images[this._active].image.url) { | |
619 | $clear = true; | |
620 | } | |
621 | ||
622 | if ($clear) { | |
623 | // reset active state | |
624 | this._active = -1; | |
625 | } | |
626 | } | |
627 | ||
fa7549e1 AE |
628 | $item.trigger('click'); |
629 | this.moveToImage($item.data('index')); | |
630 | } | |
c96906ac AE |
631 | else if ($thumbnail !== null) { |
632 | $thumbnail.trigger('click'); | |
633 | } | |
634 | ||
dd5df48e AE |
635 | this._toggleButtons(); |
636 | ||
637 | // check if there is enough space to load more thumbnails | |
638 | this._preload(); | |
639 | }, | |
640 | ||
641 | /** | |
642 | * Attempts to load the next images. | |
643 | */ | |
644 | _preload: function() { | |
645 | if (this._images.length < this._items) { | |
646 | var $thumbnailsWidth = this._images.length * this._thumbnailWidth; | |
647 | if ($thumbnailsWidth - this._thumbnailOffset < this._thumbnailContainerWidth) { | |
d3d05fd9 | 648 | this._loadNextImages(false); |
dd5df48e AE |
649 | } |
650 | } | |
651 | }, | |
652 | ||
653 | /** | |
654 | * Displays image on thumbnail click. | |
655 | * | |
656 | * @param object event | |
657 | */ | |
658 | _showImage: function(event) { | |
ff34ee1f | 659 | this.showImage($(event.currentTarget).data('index'), true); |
dd5df48e AE |
660 | }, |
661 | ||
662 | /** | |
663 | * Displays an image by index. | |
664 | * | |
665 | * @param integer index | |
ff34ee1f | 666 | * @param boolean disableSlideshow |
dd5df48e AE |
667 | * @return boolean |
668 | */ | |
ff34ee1f | 669 | showImage: function(index, disableSlideshow) { |
dd5df48e AE |
670 | if (this._active == index) { |
671 | return false; | |
672 | } | |
673 | ||
ff34ee1f AE |
674 | this.stopSlideshow(disableSlideshow || false); |
675 | ||
dd5df48e AE |
676 | // reset active marking |
677 | if (this._active != -1) { | |
678 | this._images[this._active].listItem.removeClass('active'); | |
679 | } | |
680 | ||
681 | this._active = index; | |
ead50260 TD |
682 | |
683 | // store latest image in history entry | |
684 | window.history.replaceState({ | |
685 | name: 'imageViewer', | |
686 | container: this._eventNamespace, | |
687 | image: this._active | |
688 | }, '', ''); | |
689 | ||
dd5df48e AE |
690 | var $image = this._images[index]; |
691 | ||
692 | this._ui.imageList.children('li').removeClass('active'); | |
693 | $image.listItem.addClass('active'); | |
694 | ||
695 | var $dimensions = this._ui.imageContainer.getDimensions('inner'); | |
ff34ee1f | 696 | var $newImageIndex = (this._activeImage ? 0 : 1); |
dd5df48e | 697 | |
ff34ee1f AE |
698 | if (this._activeImage !== null) { |
699 | this._ui.images[this._activeImage].removeClass('active'); | |
700 | } | |
701 | ||
702 | this._activeImage = $newImageIndex; | |
ff34ee1f | 703 | var $currentActiveImage = this._active; |
56d3c2de | 704 | this._ui.imageContainer.addClass('loading'); |
719046d7 AE |
705 | this._ui.images[$newImageIndex].off('load').prop('src', 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=='); // 1x1 pixel transparent gif |
706 | this._ui.images[$newImageIndex].on('load', $.proxy(function() { | |
ff34ee1f | 707 | this._imageOnLoad($currentActiveImage, $newImageIndex); |
56d3c2de AE |
708 | }, this)); |
709 | ||
710 | this._renderImage($newImageIndex, $image, $dimensions); | |
dd5df48e AE |
711 | |
712 | // user | |
fa7549e1 AE |
713 | if (!this.options.staticViewer) { |
714 | var $link = this._ui.header.find('> div > a').prop('href', $image.user.link).prop('title', $image.user.username); | |
715 | $link.children('img').prop('src', $image.user.avatarURL); | |
716 | } | |
dd5df48e AE |
717 | |
718 | // meta data | |
719 | var $title = WCF.String.escapeHTML($image.image.title); | |
1689207b | 720 | if ($image.image.link) $title = '<a href="' + $image.image.link + '">' + $title + '</a>'; |
3166e052 | 721 | this._ui.header.find('h1').html($title); |
dd5df48e | 722 | |
fa7549e1 AE |
723 | if (!this.options.staticViewer) { |
724 | var $seriesTitle = ($image.series && $image.series.title ? WCF.String.escapeHTML($image.series.title) : ''); | |
725 | if ($image.series.link) $seriesTitle = '<a href="' + $image.series.link + '">' + $seriesTitle + '</a>'; | |
3166e052 | 726 | this._ui.header.find('h2').html($seriesTitle); |
fa7549e1 | 727 | } |
dd5df48e | 728 | |
3166e052 | 729 | this._ui.header.find('h3').text(WCF.Language.get('wcf.imageViewer.seriesIndex').replace(/{x}/, $image.listItem.data('index') + 1).replace(/{y}/, this._items)); |
dd5df48e AE |
730 | |
731 | this._ui.slideshow.full.data('link', ($image.image.fullURL ? $image.image.fullURL : $image.image.url)); | |
732 | ||
733 | this.moveToImage($image.listItem.data('index')); | |
734 | ||
735 | this._toggleButtons(); | |
736 | ||
737 | return true; | |
738 | }, | |
739 | ||
785dac42 AE |
740 | /** |
741 | * Callback function for the image 'load' event. | |
742 | * | |
743 | * @param integer currentActiveImage | |
744 | * @param integer activeImageIndex | |
745 | */ | |
ff34ee1f AE |
746 | _imageOnLoad: function(currentActiveImage, activeImageIndex) { |
747 | // image did not load in time, ignore | |
748 | if (currentActiveImage != this._active) { | |
749 | return; | |
750 | } | |
751 | ||
752 | this._ui.imageContainer.removeClass('loading'); | |
753 | this._ui.images[activeImageIndex].addClass('active'); | |
4c6b5841 | 754 | |
fa7549e1 AE |
755 | if (this.options.staticViewer) { |
756 | this._renderImage(activeImageIndex, null); | |
757 | } | |
758 | ||
4c6b5841 | 759 | this.startSlideshow(); |
ff34ee1f AE |
760 | }, |
761 | ||
dd5df48e AE |
762 | /** |
763 | * Renders target image, leaving 'imageData' undefined will invoke the rendering process for the currently active image. | |
764 | * | |
765 | * @param integer targetIndex | |
766 | * @param object imageData | |
767 | * @param object containerDimensions | |
768 | */ | |
769 | _renderImage: function(targetIndex, imageData, containerDimensions) { | |
c3bae4f8 | 770 | var $checkForComplete = true; |
dd5df48e AE |
771 | if (!imageData) { |
772 | targetIndex = this._activeImage; | |
773 | imageData = this._images[this._active]; | |
774 | ||
775 | containerDimensions = { | |
fa7549e1 | 776 | height: $(window).height() - (this._container.hasClass('maximized') || this._container.hasClass('wcfImageViewerMobile') ? 0 : 200), |
dd5df48e AE |
777 | width: this._ui.imageContainer.innerWidth() |
778 | }; | |
c3bae4f8 AE |
779 | |
780 | $checkForComplete = false; | |
dd5df48e AE |
781 | } |
782 | ||
783 | // simulate padding | |
784 | containerDimensions.height -= 22; | |
785 | containerDimensions.width -= 20; | |
786 | ||
ce49a649 AE |
787 | var $image = this._ui.images[targetIndex]; |
788 | if ($image.prop('src') !== imageData.image.url) { | |
789 | // assigning the same exact source again breaks Internet Explorer 10 | |
790 | $image.prop('src', imageData.image.url); | |
791 | } | |
792 | ||
c3bae4f8 AE |
793 | if ($checkForComplete && $image[0].complete) { |
794 | $image.trigger('load'); | |
795 | } | |
fa7549e1 | 796 | |
edf4ac39 | 797 | if (this.options.staticViewer && !imageData.image.height && $image[0].complete) { |
92fbf517 F |
798 | // Firefox and Safari returns bogus values if attempting to read the real dimensions |
799 | if ($.browser.mozilla || $.browser.safari) { | |
bcc5af2b AE |
800 | var $img = new Image(); |
801 | $img.src = imageData.image.url; | |
802 | ||
2f638c83 AE |
803 | imageData.image.height = $img.height || $image[0].naturalHeight; |
804 | imageData.image.width = $img.width || $image[0].naturalWidth; | |
bcc5af2b AE |
805 | } |
806 | else { | |
807 | $image.css({ | |
808 | height: 'auto', | |
809 | width: 'auto' | |
810 | }); | |
811 | ||
812 | imageData.image.height = $image[0].height; | |
813 | imageData.image.width = $image[0].width; | |
814 | } | |
fa7549e1 | 815 | } |
dd5df48e AE |
816 | |
817 | var $height = imageData.image.height; | |
818 | var $width = imageData.image.width; | |
819 | var $ratio = 0.0; | |
820 | ||
821 | // check if image exceeds dimensions on the Y axis | |
822 | if ($height > containerDimensions.height) { | |
823 | $ratio = containerDimensions.height / $height; | |
824 | $height = containerDimensions.height; | |
825 | $width = Math.floor($width * $ratio); | |
826 | } | |
827 | ||
828 | // check if image exceeds dimensions on the X axis | |
829 | if ($width > containerDimensions.width) { | |
830 | $ratio = containerDimensions.width / $width; | |
831 | $width = containerDimensions.width; | |
832 | $height = Math.floor($height * $ratio); | |
833 | } | |
834 | ||
835 | var $left = Math.floor((containerDimensions.width - $width) / 2); | |
dd5df48e AE |
836 | this._ui.images[targetIndex].css({ |
837 | height: $height + 'px', | |
785dac42 | 838 | left: ($left + 10) + 'px', |
dd5df48e AE |
839 | marginTop: (Math.round($height / 2) * -1) + 'px', |
840 | width: $width + 'px' | |
841 | }); | |
842 | }, | |
843 | ||
844 | /** | |
1615fc2e | 845 | * Initializes the user interface. |
dd5df48e AE |
846 | * |
847 | * @return boolean | |
848 | */ | |
849 | _initUI: function() { | |
850 | if (this._didInit) { | |
851 | return false; | |
852 | } | |
853 | ||
854 | this._didInit = true; | |
855 | ||
fa7549e1 | 856 | this._container = $('<div class="wcfImageViewer' + (this.options.staticViewer ? ' wcfImageViewerStatic' : '') + '" />').appendTo(document.body); |
98df8a61 | 857 | var $imageContainer = $('<div><img /><img /></div>').appendTo(this._container); |
ca8bfa53 | 858 | var $imageList = $('<footer><span class="wcfImageViewerButtonPrevious icon fa-angle-double-left" /><div><ul /></div><span class="wcfImageViewerButtonNext icon fa-angle-double-right" /></footer>').appendTo(this._container); |
dd5df48e | 859 | var $slideshowContainer = $('<ul />').appendTo($imageContainer); |
ca8bfa53 MS |
860 | var $slideshowButtonPrevious = $('<li class="wcfImageViewerSlideshowButtonPrevious"><span class="icon icon48 fa-angle-left" /></li>').appendTo($slideshowContainer); |
861 | var $slideshowButtonToggle = $('<li class="wcfImageViewerSlideshowButtonToggle pointer"><span class="icon icon48 fa-play" /></li>').appendTo($slideshowContainer); | |
862 | var $slideshowButtonNext = $('<li class="wcfImageViewerSlideshowButtonNext"><span class="icon icon48 fa-angle-right" /></li>').appendTo($slideshowContainer); | |
863 | var $slideshowButtonEnlarge = $('<li class="wcfImageViewerSlideshowButtonEnlarge pointer jsTooltip" title="' + WCF.Language.get('wcf.imageViewer.button.enlarge') + '"><span class="icon icon48 fa-expand" /></li>').appendTo($slideshowContainer); | |
864 | var $slideshowButtonFull = $('<li class="wcfImageViewerSlideshowButtonFull pointer jsTooltip" title="' + WCF.Language.get('wcf.imageViewer.button.full') + '"><span class="icon icon48 fa-external-link" /></li>').appendTo($slideshowContainer); | |
dd5df48e AE |
865 | |
866 | this._ui = { | |
867 | buttonNext: $imageList.children('span.wcfImageViewerButtonNext'), | |
868 | buttonPrevious: $imageList.children('span.wcfImageViewerButtonPrevious'), | |
3166e052 | 869 | header: $('<header><div' + (this.options.staticViewer ? '>' : ' class="box64"><a class="jsTooltip"><img /></a>' ) + '<div><h1 /><h2 /><h3 /></div></div></header>').appendTo(this._container), |
dd5df48e AE |
870 | imageContainer: $imageContainer, |
871 | images: [ | |
872 | $imageContainer.children('img:eq(0)').on('webkitTransitionEnd transitionend msTransitionEnd oTransitionEnd', function() { $(this).removeClass('animateTransformation'); }), | |
873 | $imageContainer.children('img:eq(1)').on('webkitTransitionEnd transitionend msTransitionEnd oTransitionEnd', function() { $(this).removeClass('animateTransformation'); }) | |
874 | ], | |
875 | imageList: $imageList.find('> div > ul'), | |
876 | slideshow: { | |
877 | container: $slideshowContainer, | |
878 | enlarge: $slideshowButtonEnlarge, | |
879 | full: $slideshowButtonFull, | |
880 | next: $slideshowButtonNext, | |
881 | previous: $slideshowButtonPrevious, | |
882 | toggle: $slideshowButtonToggle | |
883 | } | |
884 | }; | |
885 | ||
886 | this._ui.buttonNext.click($.proxy(this._next, this)); | |
887 | this._ui.buttonPrevious.click($.proxy(this._previous, this)); | |
888 | ||
889 | $slideshowButtonNext.click($.proxy(this._nextImage, this)); | |
890 | $slideshowButtonPrevious.click($.proxy(this._previousImage, this)); | |
891 | $slideshowButtonEnlarge.click($.proxy(this._toggleView, this)); | |
892 | $slideshowButtonToggle.click($.proxy(function() { | |
dfa4a65b AE |
893 | if (this._items < 2) { |
894 | return; | |
895 | } | |
896 | ||
dd5df48e | 897 | if (this._slideshowEnabled) { |
ff34ee1f | 898 | this.stopSlideshow(true); |
dd5df48e AE |
899 | } |
900 | else { | |
d3d05fd9 | 901 | this._disableSlideshow = false; |
dd5df48e AE |
902 | this.startSlideshow(); |
903 | } | |
904 | }, this)); | |
905 | $slideshowButtonFull.click(function(event) { window.location = $(event.currentTarget).data('link'); }); | |
906 | ||
907 | // close button | |
ca8bfa53 | 908 | $('<span class="wcfImageViewerButtonClose icon icon48 fa-times pointer jsTooltip" title="' + WCF.Language.get('wcf.global.button.close') + '" />').appendTo(this._ui.header).click($.proxy(this.close, this)); |
dd5df48e | 909 | |
3aa68c56 AE |
910 | if (!$.browser.mobile) { |
911 | // clicking on the inner container should close the dialog, but it should not be available on mobile due to | |
912 | // the lack of precision causing accidental closing, the close button is big enough and easily reachable | |
913 | $imageContainer.click((function(event) { | |
914 | if (event.target === $imageContainer[0]) { | |
915 | this.close(); | |
916 | } | |
917 | }).bind(this)); | |
918 | } | |
919 | ||
fa7549e1 AE |
920 | WCF.DOMNodeInsertedHandler.execute(); |
921 | ||
431e4cb4 | 922 | enquire.register('(max-width: 767px)', { |
fa7549e1 AE |
923 | match: $.proxy(this._enableMobileView, this), |
924 | unmatch: $.proxy(this._disableMobileView, this) | |
925 | }); | |
926 | ||
dd5df48e AE |
927 | return true; |
928 | }, | |
929 | ||
fa7549e1 AE |
930 | /** |
931 | * Enables the mobile-optimized UI. | |
932 | */ | |
933 | _enableMobileView: function() { | |
934 | this._container.addClass('wcfImageViewerMobile'); | |
935 | ||
936 | var self = this; | |
937 | this._ui.imageContainer.swipe({ | |
938 | swipeLeft: function(event) { | |
939 | if (self._container.hasClass('maximized')) { | |
940 | self._nextImage(event); | |
941 | } | |
942 | }, | |
943 | swipeRight: function(event) { | |
944 | if (self._container.hasClass('maximized')) { | |
945 | self._previousImage(event); | |
946 | } | |
947 | }, | |
948 | tap: function(event, element) { | |
949 | // tap fires before click, prevent conflicts | |
950 | switch (element.tagName) { | |
951 | case 'DIV': | |
952 | case 'IMG': | |
953 | self._toggleView(); | |
954 | break; | |
955 | } | |
956 | } | |
957 | }); | |
958 | ||
959 | this._isMobile = true; | |
960 | }, | |
961 | ||
962 | /** | |
963 | * Disables the mobile-optimized UI. | |
964 | */ | |
965 | _disableMobileView: function() { | |
966 | this._container.removeClass('wcfImageViewerMobile'); | |
2a7e7ab9 | 967 | this._ui.imageContainer.swipe('destroy'); |
fa7549e1 AE |
968 | |
969 | this._isMobile = false; | |
970 | }, | |
971 | ||
dd5df48e AE |
972 | /** |
973 | * Toggles between normal and fullscreen view. | |
974 | */ | |
975 | _toggleView: function() { | |
976 | this._ui.images[this._activeImage].addClass('animateTransformation'); | |
977 | this._container.toggleClass('maximized'); | |
ca8bfa53 | 978 | this._ui.slideshow.enlarge.toggleClass('active').children('span').toggleClass('fa-expand').toggleClass('fa-compress'); |
dd5df48e AE |
979 | |
980 | this._renderImage(null, undefined, null); | |
981 | }, | |
982 | ||
983 | /** | |
984 | * Shifts the thumbnail list. | |
985 | * | |
986 | * @param object event | |
987 | * @param integer shiftBy | |
988 | */ | |
989 | _next: function(event, shiftBy) { | |
990 | if (this._ui.buttonNext.hasClass('pointer')) { | |
991 | if (shiftBy == undefined) { | |
ff34ee1f | 992 | this.stopSlideshow(true); |
dd5df48e AE |
993 | } |
994 | ||
995 | var $maximumOffset = Math.max((this._items * this._thumbnailWidth) - this._thumbnailContainerWidth - this._thumbnailMarginRight, 0); | |
996 | this._thumbnailOffset = Math.min(this._thumbnailOffset + (this._thumbnailWidth * (shiftBy ? shiftBy : this.options.shiftBy)), $maximumOffset); | |
997 | this._ui.imageList.css('marginLeft', (this._thumbnailOffset * -1)); | |
998 | } | |
999 | ||
1000 | this._preload(); | |
1001 | ||
1002 | this._toggleButtons(); | |
1003 | }, | |
1004 | ||
1005 | /** | |
1006 | * Unshifts the thumbnail list. | |
1007 | * | |
1008 | * @param object event | |
1009 | * @param integer shiftBy | |
1010 | */ | |
1011 | _previous: function(event, unshiftBy) { | |
1012 | if (this._ui.buttonPrevious.hasClass('pointer')) { | |
1013 | if (unshiftBy == undefined) { | |
ff34ee1f | 1014 | this.stopSlideshow(true); |
dd5df48e AE |
1015 | } |
1016 | ||
1017 | this._thumbnailOffset = Math.max(this._thumbnailOffset - (this._thumbnailWidth * (unshiftBy ? unshiftBy : this.options.shiftBy)), 0); | |
1018 | this._ui.imageList.css('marginLeft', (this._thumbnailOffset * -1)); | |
1019 | } | |
1020 | ||
1021 | this._toggleButtons(); | |
1022 | }, | |
1023 | ||
1024 | /** | |
1025 | * Displays the next image. | |
1026 | * | |
1027 | * @param object event | |
1028 | */ | |
1029 | _nextImage: function(event) { | |
1030 | if (this._ui.slideshow.next.hasClass('pointer')) { | |
77404ea9 AE |
1031 | this._disableSlideshow = true; |
1032 | ||
ff34ee1f | 1033 | this.stopSlideshow(true); |
dd5df48e | 1034 | this.showImage(this._active + 1); |
fa7549e1 AE |
1035 | |
1036 | if (event) { | |
1037 | event.preventDefault(); | |
1038 | event.stopPropagation(); | |
1039 | } | |
dd5df48e AE |
1040 | } |
1041 | }, | |
1042 | ||
1043 | /** | |
1044 | * Displays the previous image. | |
1045 | * | |
1046 | * @param object event | |
1047 | */ | |
1048 | _previousImage: function(event) { | |
1049 | if (this._ui.slideshow.previous.hasClass('pointer')) { | |
77404ea9 AE |
1050 | this._disableSlideshow = true; |
1051 | ||
ff34ee1f | 1052 | this.stopSlideshow(true); |
dd5df48e | 1053 | this.showImage(this._active - 1); |
fa7549e1 AE |
1054 | |
1055 | if (event) { | |
1056 | event.preventDefault(); | |
1057 | event.stopPropagation(); | |
1058 | } | |
dd5df48e AE |
1059 | } |
1060 | }, | |
1061 | ||
1062 | /** | |
1063 | * Moves thumbnail list to target thumbnail. | |
1064 | * | |
1065 | * @param integer seriesIndex | |
1066 | */ | |
1067 | moveToImage: function(seriesIndex) { | |
1068 | // calculate start and end of thumbnail | |
1069 | var $start = (seriesIndex - 3) * this._thumbnailWidth; | |
1070 | var $end = $start + (this._thumbnailWidth * 5); | |
1071 | ||
1072 | // calculate visible offsets | |
1073 | var $left = this._thumbnailOffset; | |
1074 | var $right = this._thumbnailOffset + this._thumbnailContainerWidth; | |
1075 | ||
1076 | // check if thumbnail is within boundaries | |
1077 | var $shouldMove = false; | |
1078 | if ($start < $left || $end > $right) { | |
1079 | $shouldMove = true; | |
1080 | } | |
1081 | ||
1082 | // try to shift until the thumbnail itself and the next/previous 2 thumbnails are visible | |
1083 | if ($shouldMove) { | |
1084 | var $shiftBy = 0; | |
1085 | ||
1086 | // unshift | |
1087 | if ($start < $left) { | |
1088 | while ($start < $left) { | |
1089 | $shiftBy++; | |
1090 | $left -= this._thumbnailWidth; | |
1091 | } | |
1092 | ||
1093 | this._previous(null, $shiftBy); | |
1094 | } | |
1095 | else { | |
1096 | // shift | |
1097 | while ($end > $right) { | |
1098 | $shiftBy++; | |
1099 | $right += this._thumbnailWidth; | |
1100 | } | |
1101 | ||
1102 | this._next(null, $shiftBy); | |
1103 | } | |
1104 | } | |
1105 | }, | |
1106 | ||
1107 | /** | |
1108 | * Toggles control buttons. | |
1109 | */ | |
1110 | _toggleButtons: function() { | |
1111 | // button 'previous' | |
1112 | if (this._thumbnailOffset > 0) { | |
1113 | this._ui.buttonPrevious.addClass('pointer'); | |
1114 | } | |
1115 | else { | |
1116 | this._ui.buttonPrevious.removeClass('pointer'); | |
1117 | } | |
1118 | ||
1119 | // button 'next' | |
1120 | var $maximumOffset = (this._images.length * this._thumbnailWidth) - this._thumbnailContainerWidth - this._thumbnailMarginRight; | |
1121 | if (this._thumbnailOffset >= $maximumOffset) { | |
1122 | this._ui.buttonNext.removeClass('pointer'); | |
1123 | } | |
1124 | else { | |
1125 | this._ui.buttonNext.addClass('pointer'); | |
1126 | } | |
1127 | ||
1128 | // slideshow controls | |
1129 | if (this._active > 0) { | |
1130 | this._ui.slideshow.previous.addClass('pointer'); | |
1131 | } | |
1132 | else { | |
1133 | this._ui.slideshow.previous.removeClass('pointer'); | |
1134 | } | |
1135 | ||
1136 | if (this._active + 1 < this._images.length) { | |
1137 | this._ui.slideshow.next.addClass('pointer'); | |
1138 | } | |
1139 | else { | |
1140 | this._ui.slideshow.next.removeClass('pointer'); | |
1141 | } | |
dfa4a65b AE |
1142 | |
1143 | if (this._items < 2) { | |
1144 | this._ui.slideshow.toggle.removeClass('pointer'); | |
1145 | } | |
c3bae4f8 AE |
1146 | else { |
1147 | this._ui.slideshow.toggle.addClass('pointer'); | |
1148 | } | |
dd5df48e AE |
1149 | }, |
1150 | ||
1151 | /** | |
1152 | * Inserts thumbnails. | |
1153 | * | |
1154 | * @param array<object> images | |
1155 | */ | |
1156 | _createThumbnails: function(images) { | |
fa7549e1 AE |
1157 | if (this.options.staticViewer) { |
1158 | this._images = [ ]; | |
1159 | this._ui.imageList.empty(); | |
1160 | } | |
1161 | ||
dd5df48e AE |
1162 | for (var $i = 0, $length = images.length; $i < $length; $i++) { |
1163 | var $image = images[$i]; | |
1164 | ||
ff34ee1f | 1165 | var $listItem = $('<li class="loading pointer"><img src="' + $image.thumbnail.url + '" /></li>').appendTo(this._ui.imageList); |
d3d05fd9 | 1166 | $listItem.data('index', this._images.length).data('objectID', $image.objectID).click($.proxy(this._showImage, this)); |
ff34ee1f AE |
1167 | var $img = $listItem.children('img'); |
1168 | if ($img.get(0).complete) { | |
1169 | // thumbnail is read from cache | |
1170 | $listItem.removeClass('loading'); | |
fa7549e1 AE |
1171 | |
1172 | // fix dimensions | |
1173 | if (this.options.staticViewer) { | |
1174 | this._fixThumbnailDimensions($img); | |
1175 | } | |
ff34ee1f AE |
1176 | } |
1177 | else { | |
fa7549e1 AE |
1178 | var self = this; |
1179 | $img.on('load', function() { | |
1180 | var $img = $(this); | |
1181 | $img.parent().removeClass('loading'); | |
1182 | ||
1183 | if (self.options.staticViewer) { | |
1184 | self._fixThumbnailDimensions($img); | |
1185 | } | |
1186 | }); | |
ff34ee1f | 1187 | } |
dd5df48e AE |
1188 | |
1189 | $image.listItem = $listItem; | |
1190 | this._images.push($image); | |
1191 | } | |
1192 | }, | |
1193 | ||
fa7549e1 AE |
1194 | /** |
1195 | * Fixes thumbnail dimensions within static mode. | |
1196 | * | |
1197 | * @param jQuery image | |
1198 | */ | |
1199 | _fixThumbnailDimensions: function(image) { | |
1200 | var $image = new Image(); | |
1201 | $image.src = image.prop('src'); | |
1202 | ||
1203 | var $height = $image.height; | |
1204 | var $width = $image.width; | |
1205 | ||
1206 | // quadratic, scale to 80x80 | |
1207 | if ($height == $width) { | |
1208 | $height = $width = 80; | |
1209 | } | |
1210 | else if ($height < $width) { | |
1211 | // landscape, use width as reference | |
1212 | var $scale = 80 / $width; | |
1213 | $width = 80; | |
1214 | $height *= $scale; | |
1215 | } | |
1216 | else { | |
1217 | // portrait, use height as reference | |
1218 | var $scale = 80 / $height; | |
1219 | $height = 80; | |
1220 | $width *= $scale; | |
1221 | } | |
1222 | ||
1223 | image.css({ | |
1224 | height: $height + 'px', | |
1225 | width: $width + 'px' | |
1226 | }); | |
1227 | }, | |
1228 | ||
dd5df48e AE |
1229 | /** |
1230 | * Loads the next images via AJAX. | |
d3d05fd9 AE |
1231 | * |
1232 | * @param boolean init | |
dd5df48e | 1233 | */ |
d3d05fd9 | 1234 | _loadNextImages: function(init) { |
dd5df48e AE |
1235 | this._proxy.setOption('data', { |
1236 | actionName: 'loadNextImages', | |
1237 | className: this.options.className, | |
1238 | interfaceName: 'wcf\\data\\IImageViewerAction', | |
1239 | objectIDs: [ this.element.data('objectID') ], | |
1240 | parameters: { | |
1241 | maximumHeight: this._maxDimensions.height, | |
1242 | maximumWidth: this._maxDimensions.width, | |
d3d05fd9 AE |
1243 | offset: this._images.length, |
1244 | targetImageID: (init && this.element.data('targetImageID') ? this.element.data('targetImageID') : 0) | |
dd5df48e AE |
1245 | } |
1246 | }); | |
ff34ee1f | 1247 | this._proxy.setOption('showLoadingOverlay', false); |
dd5df48e AE |
1248 | this._proxy.sendRequest(); |
1249 | }, | |
1250 | ||
fa7549e1 AE |
1251 | /** |
1252 | * Builds the list of static images and returns it. | |
1253 | * | |
1254 | * @return array<object> | |
1255 | */ | |
1256 | _getStaticImages: function() { | |
1257 | var $images = [ ]; | |
1258 | ||
1259 | $(this.options.imageSelector).each(function(index, link) { | |
1260 | var $link = $(link); | |
80d3787b | 1261 | var $thumbnail = $link.find('> img, .attachmentThumbnailImage > img').first(); |
bc8a4ed8 AE |
1262 | if (!$thumbnail.length) { |
1263 | $thumbnail = $link.parentsUntil('.formAttachmentList').last().find('.attachmentTinyThumbnail'); | |
1264 | } | |
fa7549e1 AE |
1265 | |
1266 | $images.push({ | |
1267 | image: { | |
e934d809 | 1268 | fullURL: $thumbnail.data('source') ? $thumbnail.data('source').replace(/\\\//g, '/') : $link.prop('href'), |
fa7549e1 AE |
1269 | link: '', |
1270 | title: $link.prop('title'), | |
7eff88e3 | 1271 | url: $link.prop('href') |
fa7549e1 AE |
1272 | }, |
1273 | series: null, | |
1274 | thumbnail: { | |
bc8a4ed8 | 1275 | url: $thumbnail.prop('src') |
fa7549e1 AE |
1276 | }, |
1277 | user: null | |
1278 | }); | |
1279 | }); | |
1280 | ||
1281 | this._items = $images.length; | |
1282 | ||
1283 | return $images; | |
1284 | }, | |
1285 | ||
dd5df48e AE |
1286 | /** |
1287 | * Handles successful AJAX requests. | |
1288 | * | |
1289 | * @param object data | |
1290 | * @param string textStatus | |
1291 | * @param jQuery jqXHR | |
1292 | */ | |
1293 | _success: function(data, textStatus, jqXHR) { | |
1294 | if (data.returnValues.items) { | |
1295 | this._items = data.returnValues.items; | |
1296 | } | |
1297 | ||
1298 | var $initialized = this._initUI(); | |
1299 | ||
1300 | this._createThumbnails(data.returnValues.images); | |
1301 | ||
d3d05fd9 AE |
1302 | var $targetImageID = (data.returnValues.targetImageID ? data.returnValues.targetImageID : 0); |
1303 | this._render($initialized, $targetImageID); | |
7cf89d4c MW |
1304 | |
1305 | if (!this._isOpen) { | |
1306 | this._isOpen = true; | |
1a6e8c52 | 1307 | |
7cf89d4c | 1308 | WCF.System.DisableScrolling.disable(); |
9c31391d | 1309 | WCF.System.DisableZoom.disable(); |
7cf89d4c | 1310 | } |
dd5df48e AE |
1311 | } |
1312 | }); |