blueimp-gallery 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1176 @@
1
+ /*
2
+ * blueimp Gallery JS 2.7.1
3
+ * https://github.com/blueimp/Gallery
4
+ *
5
+ * Copyright 2013, Sebastian Tschan
6
+ * https://blueimp.net
7
+ *
8
+ * Swipe implementation based on
9
+ * https://github.com/bradbirdsall/Swipe
10
+ *
11
+ * Licensed under the MIT license:
12
+ * http://www.opensource.org/licenses/MIT
13
+ */
14
+
15
+ /*jslint regexp: true */
16
+ /*global define, window, document, DocumentTouch */
17
+
18
+ (function (factory) {
19
+ 'use strict';
20
+ if (typeof define === 'function' && define.amd) {
21
+ // Register as an anonymous AMD module:
22
+ define(['./blueimp-helper'], factory);
23
+ } else {
24
+ // Browser globals:
25
+ window.blueimp = window.blueimp || {};
26
+ window.blueimp.Gallery = factory(
27
+ window.blueimp.helper || window.jQuery
28
+ );
29
+ }
30
+ }(function ($) {
31
+ 'use strict';
32
+
33
+ function Gallery(list, options) {
34
+ if (!list || !list.length || document.body.style.maxHeight === undefined) {
35
+ // document.body.style.maxHeight is undefined on IE6 and lower
36
+ return false;
37
+ }
38
+ if (!this || this.options !== Gallery.prototype.options) {
39
+ // Called as function instead of as constructor,
40
+ // so we simply return a new instance:
41
+ return new Gallery(list, options);
42
+ }
43
+ this.list = list;
44
+ this.num = list.length;
45
+ this.initOptions(options);
46
+ this.initialize();
47
+ }
48
+
49
+ $.extend(Gallery.prototype, {
50
+
51
+ options: {
52
+ // The Id, element or querySelector of the gallery widget:
53
+ container: '#blueimp-gallery',
54
+ // The tag name, Id, element or querySelector of the slides container:
55
+ slidesContainer: 'div',
56
+ // The tag name, Id, element or querySelector of the title element:
57
+ titleElement: 'h3',
58
+ // The class to add when the gallery is visible:
59
+ displayClass: 'blueimp-gallery-display',
60
+ // The class to add when the gallery controls are visible:
61
+ controlsClass: 'blueimp-gallery-controls',
62
+ // The class to add when the gallery only displays one element:
63
+ singleClass: 'blueimp-gallery-single',
64
+ // The class to add when the left edge has been reached:
65
+ leftEdgeClass: 'blueimp-gallery-left',
66
+ // The class to add when the right edge has been reached:
67
+ rightEdgeClass: 'blueimp-gallery-right',
68
+ // The class to add when the automatic slideshow is active:
69
+ playingClass: 'blueimp-gallery-playing',
70
+ // The class for all slides:
71
+ slideClass: 'slide',
72
+ // The slide class for loading elements:
73
+ slideLoadingClass: 'slide-loading',
74
+ // The slide class for elements that failed to load:
75
+ slideErrorClass: 'slide-error',
76
+ // The class for the content element loaded into each slide:
77
+ slideContentClass: 'slide-content',
78
+ // The class for the "toggle" control:
79
+ toggleClass: 'toggle',
80
+ // The class for the "prev" control:
81
+ prevClass: 'prev',
82
+ // The class for the "next" control:
83
+ nextClass: 'next',
84
+ // The class for the "close" control:
85
+ closeClass: 'close',
86
+ // The class for the "play-pause" toggle control:
87
+ playPauseClass: 'play-pause',
88
+ // The list object property (or data attribute) with the object type:
89
+ typeProperty: 'type',
90
+ // The list object property (or data attribute) with the object title:
91
+ titleProperty: 'title',
92
+ // The list object property (or data attribute) with the object URL:
93
+ urlProperty: 'href',
94
+ // Defines if the gallery slides are cleared from the gallery modal,
95
+ // or reused for the next gallery initialization:
96
+ clearSlides: true,
97
+ // Defines if images should be stretched to fill the available space,
98
+ // while maintaining their aspect ratio (will only be enabled for browsers
99
+ // supporting background-size="contain", which excludes IE < 9):
100
+ stretchImages: false,
101
+ // Toggle the controls on pressing the Return key:
102
+ toggleControlsOnReturn: true,
103
+ // Toggle the automatic slideshow interval on pressing the Space key:
104
+ toggleSlideshowOnSpace: true,
105
+ // Navigate the gallery by pressing left and right on the keyboard:
106
+ enableKeyboardNavigation: true,
107
+ // Close the gallery on pressing the Esc key:
108
+ closeOnEscape: true,
109
+ // Close the gallery when clicking on an empty slide area:
110
+ closeOnSlideClick: true,
111
+ // Close the gallery by swiping up or down:
112
+ closeOnSwipeUpOrDown: true,
113
+ // Emulate touch events on mouse-pointer devices such as desktop browsers:
114
+ emulateTouchEvents: true,
115
+ // Hide the page scrollbars:
116
+ hidePageScrollbars: true,
117
+ // Stops any touches on the container from scrolling the page:
118
+ disableScroll: true,
119
+ // Carousel mode (shortcut for carousel specific options):
120
+ carousel: false,
121
+ // Allow continuous navigation, moving from last to first
122
+ // and from first to last slide:
123
+ continuous: true,
124
+ // Remove elements outside of the preload range from the DOM:
125
+ unloadElements: true,
126
+ // Start with the automatic slideshow:
127
+ startSlideshow: false,
128
+ // Delay in milliseconds between slides for the automatic slideshow:
129
+ slideshowInterval: 5000,
130
+ // The starting index as integer.
131
+ // Can also be an object of the given list,
132
+ // or an equal object with the same url property:
133
+ index: 0,
134
+ // The number of elements to load around the current index:
135
+ preloadRange: 2,
136
+ // The transition speed between slide changes in milliseconds:
137
+ transitionSpeed: 400,
138
+ // The transition speed for automatic slide changes, set to an integer
139
+ // greater 0 to override the default transition speed:
140
+ slideshowTransitionSpeed: undefined,
141
+ // The event object for which the default action will be canceled
142
+ // on Gallery initialization (e.g. the click event to open the Gallery):
143
+ event: undefined,
144
+ // Callback function executed when the Gallery is initialized.
145
+ // Is called with the gallery instance as "this" object:
146
+ onopen: undefined,
147
+ // Callback function executed on slide change.
148
+ // Is called with the gallery instance as "this" object and the
149
+ // current index and slide as arguments:
150
+ onslide: undefined,
151
+ // Callback function executed after the slide change transition.
152
+ // Is called with the gallery instance as "this" object and the
153
+ // current index and slide as arguments:
154
+ onslideend: undefined,
155
+ // Callback function executed on slide content load.
156
+ // Is called with the gallery instance as "this" object and the
157
+ // slide index and slide element as arguments:
158
+ onslidecomplete: undefined,
159
+ // Callback function executed when the Gallery is closed.
160
+ // Is called with the gallery instance as "this" object:
161
+ onclose: undefined
162
+ },
163
+
164
+ carouselOptions: {
165
+ hidePageScrollbars: false,
166
+ toggleControlsOnReturn: false,
167
+ toggleSlideshowOnSpace: false,
168
+ enableKeyboardNavigation: false,
169
+ closeOnEscape: false,
170
+ closeOnSlideClick: false,
171
+ closeOnSwipeUpOrDown: false,
172
+ disableScroll: false,
173
+ startSlideshow: true
174
+ },
175
+
176
+ // Detect touch, transition, transform and background-size support:
177
+ support: (function (element) {
178
+ var support = {
179
+ touch: window.ontouchstart !== undefined ||
180
+ (window.DocumentTouch && document instanceof DocumentTouch)
181
+ },
182
+ transitions = {
183
+ webkitTransition: {
184
+ end: 'webkitTransitionEnd',
185
+ prefix: '-webkit-'
186
+ },
187
+ MozTransition: {
188
+ end: 'transitionend',
189
+ prefix: '-moz-'
190
+ },
191
+ OTransition: {
192
+ end: 'otransitionend',
193
+ prefix: '-o-'
194
+ },
195
+ transition: {
196
+ end: 'transitionend',
197
+ prefix: ''
198
+ }
199
+ },
200
+ prop,
201
+ transition,
202
+ translateZ;
203
+ for (prop in transitions) {
204
+ if (transitions.hasOwnProperty(prop) &&
205
+ element.style[prop] !== undefined) {
206
+ transition = transitions[prop];
207
+ transition.name = prop;
208
+ support.transition = transition;
209
+ break;
210
+ }
211
+ }
212
+ document.body.appendChild(element);
213
+ if (transition) {
214
+ prop = transition.name.slice(0, -9) + 'ransform';
215
+ if (element.style[prop] !== undefined) {
216
+ element.style[prop] = 'translateZ(0)';
217
+ translateZ = window.getComputedStyle(element)
218
+ .getPropertyValue(transition.prefix + 'transform');
219
+ support.transform = {
220
+ prefix: transition.prefix,
221
+ name: prop,
222
+ translate: true,
223
+ translateZ: translateZ && translateZ !== 'none'
224
+ };
225
+ }
226
+ }
227
+ if (element.style.backgroundSize !== undefined) {
228
+ element.style.backgroundSize = 'contain';
229
+ support.backgroundSize = {
230
+ contain: window.getComputedStyle(element)
231
+ .getPropertyValue('background-size') === 'contain'
232
+ };
233
+ }
234
+ document.body.removeChild(element);
235
+ return support;
236
+ // Test element, has to be standard HTML and must not be hidden
237
+ // for the CSS3 transform translateZ test to be applicable:
238
+ }(document.createElement('div'))),
239
+
240
+ initialize: function () {
241
+ this.initStartIndex();
242
+ if (this.initWidget() === false) {
243
+ return false;
244
+ }
245
+ this.initEventListeners();
246
+ if (this.options.onopen) {
247
+ this.options.onopen.call(this);
248
+ }
249
+ // Load the slide at the given index:
250
+ this.onslide(this.index);
251
+ // Manually trigger the slideend event for the initial slide:
252
+ this.ontransitionend();
253
+ // Start the automatic slideshow if applicable:
254
+ if (this.options.startSlideshow) {
255
+ this.play();
256
+ }
257
+ },
258
+
259
+ slide: function (to, speed) {
260
+ window.clearTimeout(this.timeout);
261
+ var index = this.index,
262
+ direction,
263
+ natural_direction,
264
+ diff;
265
+ if (index === to || this.num === 1) {
266
+ return;
267
+ }
268
+ if (!speed) {
269
+ speed = this.options.transitionSpeed;
270
+ }
271
+ if (this.support.transition) {
272
+ if (!this.options.continuous) {
273
+ to = this.circle(to);
274
+ }
275
+ // 1: backward, -1: forward:
276
+ direction = Math.abs(index - to) / (index - to);
277
+ // Get the actual position of the slide:
278
+ if (this.options.continuous) {
279
+ natural_direction = direction;
280
+ direction = -this.positions[this.circle(to)] / this.slideWidth;
281
+ // If going forward but to < index, use to = slides.length + to
282
+ // If going backward but to > index, use to = -slides.length + to
283
+ if (direction !== natural_direction) {
284
+ to = -direction * this.num + to;
285
+ }
286
+ }
287
+ diff = Math.abs(index - to) - 1;
288
+ // Move all the slides between index and to in the right direction:
289
+ while (diff) {
290
+ diff -= 1;
291
+ this.move(
292
+ this.circle((to > index ? to : index) - diff - 1),
293
+ this.slideWidth * direction,
294
+ 0
295
+ );
296
+ }
297
+ to = this.circle(to);
298
+ this.move(index, this.slideWidth * direction, speed);
299
+ this.move(to, 0, speed);
300
+ if (this.options.continuous) {
301
+ this.move(
302
+ this.circle(to - direction),
303
+ -(this.slideWidth * direction),
304
+ 0
305
+ );
306
+ }
307
+ } else {
308
+ to = this.circle(to);
309
+ this.animate(index * -this.slideWidth, to * -this.slideWidth, speed);
310
+ }
311
+ this.onslide(to);
312
+ },
313
+
314
+ getIndex: function () {
315
+ return this.index;
316
+ },
317
+
318
+ getNumber: function () {
319
+ return this.num;
320
+ },
321
+
322
+ prev: function () {
323
+ if (this.options.continuous || this.index) {
324
+ this.slide(this.index - 1);
325
+ }
326
+ },
327
+
328
+ next: function () {
329
+ if (this.options.continuous || this.index < this.num - 1) {
330
+ this.slide(this.index + 1);
331
+ }
332
+ },
333
+
334
+ play: function (time) {
335
+ window.clearTimeout(this.timeout);
336
+ this.interval = time || this.options.slideshowInterval;
337
+ if (this.elements[this.index] > 1) {
338
+ this.timeout = this.setTimeout(
339
+ this.slide,
340
+ [this.index + 1, this.options.slideshowTransitionSpeed],
341
+ this.interval
342
+ );
343
+ }
344
+ this.container.addClass(this.options.playingClass);
345
+ },
346
+
347
+ pause: function () {
348
+ window.clearTimeout(this.timeout);
349
+ this.interval = null;
350
+ this.container.removeClass(this.options.playingClass);
351
+ },
352
+
353
+ add: function (list) {
354
+ var i;
355
+ this.list = this.list.concat(list);
356
+ this.num = this.list.length;
357
+ if (this.num > 2 && this.options.continuous === null) {
358
+ this.options.continuous = true;
359
+ this.container.removeClass(this.options.leftEdgeClass);
360
+ }
361
+ this.container
362
+ .removeClass(this.options.rightEdgeClass)
363
+ .removeClass(this.options.singleClass);
364
+ for (i = this.num - list.length; i < this.num; i += 1) {
365
+ this.addSlide(i);
366
+ this.positionSlide(i);
367
+ }
368
+ this.positions.length = this.num;
369
+ this.initSlides(true);
370
+ },
371
+
372
+ resetSlides: function () {
373
+ this.slidesContainer.empty();
374
+ this.slides = [];
375
+ },
376
+
377
+ close: function () {
378
+ var options = this.options;
379
+ this.destroyEventListeners();
380
+ // Cancel the slideshow:
381
+ this.pause();
382
+ this.container[0].style.display = 'none';
383
+ this.container
384
+ .removeClass(options.displayClass)
385
+ .removeClass(options.singleClass)
386
+ .removeClass(options.leftEdgeClass)
387
+ .removeClass(options.rightEdgeClass);
388
+ if (options.hidePageScrollbars) {
389
+ document.body.style.overflow = this.bodyOverflowStyle;
390
+ }
391
+ if (this.options.clearSlides) {
392
+ this.resetSlides();
393
+ }
394
+ if (this.options.onclose) {
395
+ this.options.onclose.call(this);
396
+ }
397
+ },
398
+
399
+ circle: function (index) {
400
+ // Always return a number inside of the slides index range:
401
+ return (this.num + (index % this.num)) % this.num;
402
+ },
403
+
404
+ move: function (index, dist, speed) {
405
+ this.translateX(index, dist, speed);
406
+ this.positions[index] = dist;
407
+ },
408
+
409
+ translate: function (index, x, y, speed) {
410
+ var style = this.slides[index].style,
411
+ transition = this.support.transition,
412
+ transform = this.support.transform;
413
+ style[transition.name + 'Duration'] = speed + 'ms';
414
+ style[transform.name] = 'translate(' + x + 'px, ' + y + 'px)' +
415
+ (transform.translateZ ? ' translateZ(0)' : '');
416
+ },
417
+
418
+ translateX: function (index, x, speed) {
419
+ this.translate(index, x, 0, speed);
420
+ },
421
+
422
+ translateY: function (index, y, speed) {
423
+ this.translate(index, 0, y, speed);
424
+ },
425
+
426
+ animate: function (from, to, speed) {
427
+ if (!speed) {
428
+ this.slidesContainer[0].style.left = to + 'px';
429
+ return;
430
+ }
431
+ var that = this,
432
+ start = new Date().getTime(),
433
+ timer = window.setInterval(function () {
434
+ var timeElap = new Date().getTime() - start;
435
+ if (timeElap > speed) {
436
+ that.slidesContainer[0].style.left = to + 'px';
437
+ that.ontransitionend();
438
+ window.clearInterval(timer);
439
+ return;
440
+ }
441
+ that.slidesContainer[0].style.left = (((to - from) *
442
+ (Math.floor((timeElap / speed) * 100) / 100)) +
443
+ from) + 'px';
444
+ }, 4);
445
+ },
446
+
447
+ preventDefault: function (event) {
448
+ if (event.preventDefault) {
449
+ event.preventDefault();
450
+ } else {
451
+ event.returnValue = false;
452
+ }
453
+ },
454
+
455
+ onresize: function () {
456
+ this.initSlides(true);
457
+ },
458
+
459
+ onmousedown: function (event) {
460
+ // Trigger on clicks of the left mouse button only:
461
+ if (event.which && event.which === 1) {
462
+ // Preventing the default mousedown action is required
463
+ // to make touch emulation work with Firefox:
464
+ event.preventDefault();
465
+ (event.originalEvent || event).touches = [{
466
+ pageX: event.pageX,
467
+ pageY: event.pageY
468
+ }];
469
+ this.ontouchstart(event);
470
+ }
471
+ },
472
+
473
+ onmousemove: function (event) {
474
+ if (this.touchStart) {
475
+ (event.originalEvent || event).touches = [{
476
+ pageX: event.pageX,
477
+ pageY: event.pageY
478
+ }];
479
+ this.ontouchmove(event);
480
+ }
481
+ },
482
+
483
+ onmouseup: function (event) {
484
+ if (this.touchStart) {
485
+ this.ontouchend(event);
486
+ delete this.touchStart;
487
+ }
488
+ },
489
+
490
+ onmouseout: function (event) {
491
+ if (this.touchStart) {
492
+ var target = event.target,
493
+ related = event.relatedTarget;
494
+ if (!related || (related !== target &&
495
+ !$.contains(target, related))) {
496
+ this.onmouseup(event);
497
+ }
498
+ }
499
+ },
500
+
501
+ ontouchstart: function (event) {
502
+ // jQuery doesn't copy touch event properties by default,
503
+ // so we have to access the originalEvent object:
504
+ var touches = (event.originalEvent || event).touches[0];
505
+ this.touchStart = {
506
+ // Remember the initial touch coordinates:
507
+ x: touches.pageX,
508
+ y: touches.pageY,
509
+ // Store the time to determine touch duration:
510
+ time: Date.now()
511
+ };
512
+ // Helper variable to detect scroll movement:
513
+ this.isScrolling = undefined;
514
+ // Reset delta values:
515
+ this.touchDelta = {};
516
+ },
517
+
518
+ ontouchmove: function (event) {
519
+ // jQuery doesn't copy touch event properties by default,
520
+ // so we have to access the originalEvent object:
521
+ var touches = (event.originalEvent || event).touches[0],
522
+ scale = (event.originalEvent || event).scale,
523
+ index = this.index,
524
+ touchDeltaX,
525
+ indices;
526
+ // Ensure this is a one touch swipe and not, e.g. a pinch:
527
+ if (touches.length > 1 || (scale && scale !== 1)) {
528
+ return;
529
+ }
530
+ if (this.options.disableScroll) {
531
+ event.preventDefault();
532
+ }
533
+ // Measure change in x and y coordinates:
534
+ this.touchDelta = {
535
+ x: touches.pageX - this.touchStart.x,
536
+ y: touches.pageY - this.touchStart.y
537
+ };
538
+ touchDeltaX = this.touchDelta.x;
539
+ // Detect if this is a vertical scroll movement (run only once per touch):
540
+ if (this.isScrolling === undefined) {
541
+ this.isScrolling = this.isScrolling ||
542
+ Math.abs(touchDeltaX) < Math.abs(this.touchDelta.y);
543
+ }
544
+ if (!this.isScrolling) {
545
+ // Always prevent horizontal scroll:
546
+ event.preventDefault();
547
+ // Stop the slideshow:
548
+ window.clearTimeout(this.timeout);
549
+ if (this.options.continuous) {
550
+ indices = [
551
+ this.circle(index + 1),
552
+ index,
553
+ this.circle(index - 1)
554
+ ];
555
+ } else {
556
+ // Increase resistance if first slide and sliding left
557
+ // or last slide and sliding right:
558
+ this.touchDelta.x = touchDeltaX =
559
+ touchDeltaX /
560
+ (((!index && touchDeltaX > 0) ||
561
+ (index === this.num - 1 && touchDeltaX < 0)) ?
562
+ (Math.abs(touchDeltaX) / this.slideWidth + 1) : 1);
563
+ indices = [index];
564
+ if (index) {
565
+ indices.push(index - 1);
566
+ }
567
+ if (index < this.num - 1) {
568
+ indices.unshift(index + 1);
569
+ }
570
+ }
571
+ while (indices.length) {
572
+ index = indices.pop();
573
+ this.translateX(index, touchDeltaX + this.positions[index], 0);
574
+ }
575
+ } else if (this.options.closeOnSwipeUpOrDown) {
576
+ this.translateY(index, this.touchDelta.y + this.positions[index], 0);
577
+ }
578
+ },
579
+
580
+ ontouchend: function () {
581
+ var index = this.index,
582
+ speed = this.options.transitionSpeed,
583
+ slideWidth = this.slideWidth,
584
+ isShortDuration = Number(Date.now() - this.touchStart.time) < 250,
585
+ // Determine if slide attempt triggers next/prev slide:
586
+ isValidSlide = (isShortDuration && Math.abs(this.touchDelta.x) > 20) ||
587
+ Math.abs(this.touchDelta.x) > slideWidth / 2,
588
+ // Determine if slide attempt is past start or end:
589
+ isPastBounds = (!index && this.touchDelta.x > 0)
590
+ || (index === this.num - 1 && this.touchDelta.x < 0),
591
+ isValidClose = !isValidSlide && this.options.closeOnSwipeUpOrDown &&
592
+ ((isShortDuration && Math.abs(this.touchDelta.y) > 20) ||
593
+ Math.abs(this.touchDelta.y) > this.slideHeight / 2),
594
+ direction,
595
+ indexForward,
596
+ indexBackward,
597
+ distanceForward,
598
+ distanceBackward;
599
+ if (this.options.continuous) {
600
+ isPastBounds = false;
601
+ }
602
+ // Determine direction of swipe (true: right, false: left):
603
+ direction = this.touchDelta.x < 0 ? -1 : 1;
604
+ if (!this.isScrolling) {
605
+ if (isValidSlide && !isPastBounds) {
606
+ indexForward = index + direction;
607
+ indexBackward = index - direction;
608
+ distanceForward = slideWidth * direction;
609
+ distanceBackward = -slideWidth * direction;
610
+ if (this.options.continuous) {
611
+ this.move(this.circle(indexForward), distanceForward, 0);
612
+ this.move(this.circle(index - 2 * direction), distanceBackward, 0);
613
+ } else if (indexForward >= 0 &&
614
+ indexForward < this.num) {
615
+ this.move(indexForward, distanceForward, 0);
616
+ }
617
+ this.move(index, this.positions[index] + distanceForward, speed);
618
+ this.move(
619
+ this.circle(indexBackward),
620
+ this.positions[this.circle(indexBackward)] + distanceForward,
621
+ speed
622
+ );
623
+ index = this.circle(indexBackward);
624
+ this.onslide(index);
625
+ } else {
626
+ // Move back into position
627
+ if (this.options.continuous) {
628
+ this.move(this.circle(index - 1), -slideWidth, speed);
629
+ this.move(index, 0, speed);
630
+ this.move(this.circle(index + 1), slideWidth, speed);
631
+ } else {
632
+ if (index) {
633
+ this.move(index - 1, -slideWidth, speed);
634
+ }
635
+ this.move(index, 0, speed);
636
+ if (index < this.num - 1) {
637
+ this.move(index + 1, slideWidth, speed);
638
+ }
639
+ }
640
+ }
641
+ } else {
642
+ if (isValidClose) {
643
+ this.close();
644
+ } else {
645
+ // Move back into position
646
+ this.translateY(index, 0, speed);
647
+ }
648
+ }
649
+ },
650
+
651
+ ontransitionend: function (event) {
652
+ var slide = this.slides[this.index];
653
+ if (!event || slide === event.target) {
654
+ if (this.interval) {
655
+ this.play();
656
+ }
657
+ this.setTimeout(
658
+ this.options.onslideend,
659
+ [this.index, slide]
660
+ );
661
+ }
662
+ },
663
+
664
+ oncomplete: function (event) {
665
+ var target = event.target || event.srcElement,
666
+ parent = target && target.parentNode,
667
+ index;
668
+ if (!target || !parent) {
669
+ return;
670
+ }
671
+ index = this.getNodeIndex(parent);
672
+ $(parent).removeClass(this.options.slideLoadingClass);
673
+ if (event.type === 'error') {
674
+ $(parent).addClass(this.options.slideErrorClass);
675
+ this.elements[index] = 3; // Fail
676
+ } else {
677
+ this.elements[index] = 2; // Done
678
+ }
679
+ // Fix for IE7's lack of support for percentage max-height:
680
+ if (target.clientHeight > this.container[0].clientHeight) {
681
+ target.style.maxHeight = this.container[0].clientHeight;
682
+ }
683
+ if (this.interval && this.slides[this.index] === parent) {
684
+ this.play();
685
+ }
686
+ this.setTimeout(
687
+ this.options.onslidecomplete,
688
+ [index, parent]
689
+ );
690
+ },
691
+
692
+ onload: function (event) {
693
+ this.oncomplete(event);
694
+ },
695
+
696
+ onerror: function (event) {
697
+ this.oncomplete(event);
698
+ },
699
+
700
+ onkeydown: function (event) {
701
+ switch (event.which || event.keyCode) {
702
+ case 13: // Return
703
+ if (this.options.toggleControlsOnReturn) {
704
+ this.preventDefault(event);
705
+ this.toggleControls();
706
+ }
707
+ break;
708
+ case 27: // Esc
709
+ if (this.options.closeOnEscape) {
710
+ this.close();
711
+ }
712
+ break;
713
+ case 32: // Space
714
+ if (this.options.toggleSlideshowOnSpace) {
715
+ this.preventDefault(event);
716
+ this.toggleSlideshow();
717
+ }
718
+ break;
719
+ case 37: // Left
720
+ if (this.options.enableKeyboardNavigation) {
721
+ this.preventDefault(event);
722
+ this.prev();
723
+ }
724
+ break;
725
+ case 39: // Right
726
+ if (this.options.enableKeyboardNavigation) {
727
+ this.preventDefault(event);
728
+ this.next();
729
+ }
730
+ break;
731
+ }
732
+ },
733
+
734
+ handleClick: function (event) {
735
+ var options = this.options,
736
+ target = event.target || event.srcElement,
737
+ parent = target.parentNode,
738
+ isTarget = function (className) {
739
+ return $(target).hasClass(className) ||
740
+ $(parent).hasClass(className);
741
+ };
742
+ if (parent === this.slidesContainer[0]) {
743
+ // Click on slide background
744
+ this.preventDefault(event);
745
+ if (options.closeOnSlideClick) {
746
+ this.close();
747
+ } else {
748
+ this.toggleControls();
749
+ }
750
+ } else if (parent.parentNode &&
751
+ parent.parentNode === this.slidesContainer[0]) {
752
+ // Click on displayed element
753
+ this.preventDefault(event);
754
+ this.toggleControls();
755
+ } else if (isTarget(options.toggleClass)) {
756
+ // Click on "toggle" control
757
+ this.preventDefault(event);
758
+ this.toggleControls();
759
+ } else if (isTarget(options.prevClass)) {
760
+ // Click on "prev" control
761
+ this.preventDefault(event);
762
+ this.prev();
763
+ } else if (isTarget(options.nextClass)) {
764
+ // Click on "next" control
765
+ this.preventDefault(event);
766
+ this.next();
767
+ } else if (isTarget(options.closeClass)) {
768
+ // Click on "close" control
769
+ this.preventDefault(event);
770
+ this.close();
771
+ } else if (isTarget(options.playPauseClass)) {
772
+ // Click on "play-pause" control
773
+ this.preventDefault(event);
774
+ this.toggleSlideshow();
775
+ }
776
+ },
777
+
778
+ onclick: function (event) {
779
+ if (this.options.emulateTouchEvents &&
780
+ this.touchDelta && (Math.abs(this.touchDelta.x) > 20 ||
781
+ Math.abs(this.touchDelta.y) > 20)) {
782
+ delete this.touchDelta;
783
+ return;
784
+ }
785
+ return this.handleClick(event);
786
+ },
787
+
788
+ updateEdgeClasses: function (index) {
789
+ if (!index) {
790
+ this.container.addClass(this.options.leftEdgeClass);
791
+ } else {
792
+ this.container.removeClass(this.options.leftEdgeClass);
793
+ }
794
+ if (index === this.num - 1) {
795
+ this.container.addClass(this.options.rightEdgeClass);
796
+ } else {
797
+ this.container.removeClass(this.options.rightEdgeClass);
798
+ }
799
+ },
800
+
801
+ handleSlide: function (index) {
802
+ if (!this.options.continuous) {
803
+ this.updateEdgeClasses(index);
804
+ }
805
+ this.loadElements(index);
806
+ if (this.options.unloadElements) {
807
+ this.unloadElements(index);
808
+ }
809
+ this.setTitle(index);
810
+ },
811
+
812
+ onslide: function (index) {
813
+ this.index = index;
814
+ this.handleSlide(index);
815
+ this.setTimeout(this.options.onslide, [index, this.slides[index]]);
816
+ },
817
+
818
+ setTitle: function (index) {
819
+ var text = this.slides[index].firstChild.title,
820
+ titleElement = this.titleElement;
821
+ if (titleElement.length) {
822
+ this.titleElement.empty();
823
+ if (text) {
824
+ titleElement[0].appendChild(document.createTextNode(text));
825
+ }
826
+ }
827
+ },
828
+
829
+ setTimeout: function (func, args, wait) {
830
+ var that = this;
831
+ return func && window.setTimeout(function () {
832
+ func.apply(that, args || []);
833
+ }, wait || 0);
834
+ },
835
+
836
+ imageFactory: function (obj, callback) {
837
+ var that = this,
838
+ img = this.imagePrototype.cloneNode(false),
839
+ url = obj,
840
+ contain = this.options.stretchImages &&
841
+ this.support.backgroundSize &&
842
+ this.support.backgroundSize.contain,
843
+ called,
844
+ element,
845
+ callbackWrapper = function (event) {
846
+ if (!called) {
847
+ event = {
848
+ type: event.type,
849
+ target: element
850
+ };
851
+ if (!element.parentNode) {
852
+ // Fix for IE7 firing the load event for
853
+ // cached images before the element could
854
+ // be added to the DOM:
855
+ return that.setTimeout(callbackWrapper, [event]);
856
+ }
857
+ called = true;
858
+ $(img).off('load error', callbackWrapper);
859
+ if (contain) {
860
+ if (event.type === 'load') {
861
+ element.style.background = 'url("' + url +
862
+ '") center no-repeat';
863
+ element.style.backgroundSize = 'contain';
864
+ }
865
+ }
866
+ callback(event);
867
+ }
868
+ },
869
+ title;
870
+ if (typeof url !== 'string') {
871
+ url = this.getItemProperty(obj, this.options.urlProperty);
872
+ title = this.getItemProperty(obj, this.options.titleProperty);
873
+ }
874
+ if (contain) {
875
+ element = this.elementPrototype.cloneNode(false);
876
+ } else {
877
+ element = img;
878
+ img.draggable = false;
879
+ }
880
+ if (title) {
881
+ element.title = title;
882
+ }
883
+ $(img).on('load error', callbackWrapper);
884
+ img.src = url;
885
+ return element;
886
+ },
887
+
888
+ createElement: function (obj, callback) {
889
+ var type = obj && this.getItemProperty(obj, this.options.typeProperty),
890
+ factory = (type && this[type.split('/')[0] + 'Factory']) ||
891
+ this.imageFactory,
892
+ element = obj && factory.call(this, obj, callback);
893
+ if (!element) {
894
+ element = this.elementPrototype.cloneNode(false);
895
+ this.setTimeout(callback, [{
896
+ type: 'error',
897
+ target: element
898
+ }]);
899
+ }
900
+ $(element).addClass(this.options.slideContentClass);
901
+ return element;
902
+ },
903
+
904
+ loadElement: function (index) {
905
+ if (!this.elements[index]) {
906
+ if (this.slides[index].firstChild) {
907
+ this.elements[index] = $(this.slides[index])
908
+ .hasClass(this.options.slideErrorClass) ? 3 : 2;
909
+ } else {
910
+ this.elements[index] = 1; // Loading
911
+ $(this.slides[index]).addClass(this.options.slideLoadingClass);
912
+ this.slides[index].appendChild(this.createElement(
913
+ this.list[index],
914
+ this.proxyListener
915
+ ));
916
+ }
917
+ }
918
+ },
919
+
920
+ loadElements: function (index) {
921
+ var limit = Math.min(this.num, this.options.preloadRange * 2 + 1),
922
+ j = index,
923
+ i;
924
+ for (i = 0; i < limit; i += 1) {
925
+ // First load the current slide element (0),
926
+ // then the next one (+1),
927
+ // then the previous one (-2),
928
+ // then the next after next (+2), etc.:
929
+ j += i * (i % 2 === 0 ? -1 : 1);
930
+ // Connect the ends of the list to load slide elements for
931
+ // continuous navigation:
932
+ j = this.circle(j);
933
+ this.loadElement(j);
934
+ }
935
+ },
936
+
937
+ unloadElements: function (index) {
938
+ var i,
939
+ slide,
940
+ diff;
941
+ for (i in this.elements) {
942
+ if (this.elements.hasOwnProperty(i)) {
943
+ diff = Math.abs(index - i);
944
+ if (diff > this.options.preloadRange &&
945
+ diff + this.options.preloadRange < this.num) {
946
+ slide = this.slides[i];
947
+ slide.removeChild(slide.firstChild);
948
+ delete this.elements[i];
949
+ }
950
+ }
951
+ }
952
+ },
953
+
954
+ addSlide: function (index) {
955
+ var slide = this.slidePrototype.cloneNode(false);
956
+ slide.setAttribute('data-index', index);
957
+ this.slidesContainer[0].appendChild(slide);
958
+ this.slides.push(slide);
959
+ },
960
+
961
+ positionSlide: function (index) {
962
+ var slide = this.slides[index];
963
+ slide.style.width = this.slideWidth + 'px';
964
+ if (this.support.transition) {
965
+ slide.style.left = (index * -this.slideWidth) + 'px';
966
+ this.move(index, this.index > index ? -this.slideWidth :
967
+ (this.index < index ? this.slideWidth : 0), 0);
968
+ }
969
+ },
970
+
971
+ initSlides: function (reload) {
972
+ var clearSlides,
973
+ i;
974
+ if (!reload) {
975
+ this.positions = [];
976
+ this.positions.length = this.num;
977
+ this.elements = {};
978
+ this.imagePrototype = document.createElement('img');
979
+ this.elementPrototype = document.createElement('div');
980
+ this.slidePrototype = document.createElement('div');
981
+ $(this.slidePrototype).addClass(this.options.slideClass);
982
+ this.slides = this.slidesContainer[0].children;
983
+ clearSlides = this.options.clearSlides ||
984
+ this.slides.length !== this.num;
985
+ }
986
+ this.slideWidth = this.container[0].offsetWidth;
987
+ this.slideHeight = this.container[0].offsetHeight;
988
+ this.slidesContainer[0].style.width =
989
+ (this.num * this.slideWidth) + 'px';
990
+ if (clearSlides) {
991
+ this.resetSlides();
992
+ }
993
+ for (i = 0; i < this.num; i += 1) {
994
+ if (clearSlides) {
995
+ this.addSlide(i);
996
+ }
997
+ this.positionSlide(i);
998
+ }
999
+ // Reposition the slides before and after the given index:
1000
+ if (this.options.continuous && this.support.transition) {
1001
+ this.move(this.circle(this.index - 1), -this.slideWidth, 0);
1002
+ this.move(this.circle(this.index + 1), this.slideWidth, 0);
1003
+ }
1004
+ if (!this.support.transition) {
1005
+ this.slidesContainer[0].style.left =
1006
+ (this.index * -this.slideWidth) + 'px';
1007
+ }
1008
+ },
1009
+
1010
+ toggleControls: function () {
1011
+ var controlsClass = this.options.controlsClass;
1012
+ if (this.container.hasClass(controlsClass)) {
1013
+ this.container.removeClass(controlsClass);
1014
+ } else {
1015
+ this.container.addClass(controlsClass);
1016
+ }
1017
+ },
1018
+
1019
+ toggleSlideshow: function () {
1020
+ if (!this.interval) {
1021
+ this.play();
1022
+ } else {
1023
+ this.pause();
1024
+ }
1025
+ },
1026
+
1027
+ getNodeIndex: function (element) {
1028
+ return parseInt(element.getAttribute('data-index'), 10);
1029
+ },
1030
+
1031
+ getNestedProperty: function (obj, property) {
1032
+ property.replace(
1033
+ // Matches native JavaScript notation in a String,
1034
+ // e.g. '["doubleQuoteProp"].dotProp[2]'
1035
+ /\[(?:'([^']+)'|"([^"]+)"|(\d+))\]|(?:(?:^|\.)([^\.\[]+))/g,
1036
+ function (str, singleQuoteProp, doubleQuoteProp, arrayIndex, dotProp) {
1037
+ var prop = dotProp || singleQuoteProp || doubleQuoteProp ||
1038
+ (arrayIndex && parseInt(arrayIndex, 10));
1039
+ if (str && obj) {
1040
+ obj = obj[prop];
1041
+ }
1042
+ }
1043
+ );
1044
+ return obj;
1045
+ },
1046
+
1047
+ getItemProperty: function (obj, property) {
1048
+ return obj[property] || (obj.getAttribute &&
1049
+ obj.getAttribute('data-' + property)) ||
1050
+ this.getNestedProperty(obj, property);
1051
+ },
1052
+
1053
+ initStartIndex: function () {
1054
+ var index = this.options.index,
1055
+ urlProperty = this.options.urlProperty,
1056
+ i;
1057
+ // Check if the index is given as a list object:
1058
+ if (index && typeof index !== 'number') {
1059
+ for (i = 0; i < this.num; i += 1) {
1060
+ if (this.list[i] === index ||
1061
+ this.getItemProperty(this.list[i], urlProperty) ===
1062
+ this.getItemProperty(index, urlProperty)) {
1063
+ index = i;
1064
+ break;
1065
+ }
1066
+ }
1067
+ }
1068
+ // Make sure the index is in the list range:
1069
+ this.index = this.circle(parseInt(index, 10) || 0);
1070
+ },
1071
+
1072
+ initEventListeners: function () {
1073
+ var that = this,
1074
+ slidesContainer = this.slidesContainer,
1075
+ proxyListener = function (event) {
1076
+ var type = that.support.transition &&
1077
+ that.support.transition.end === event.type ?
1078
+ 'transitionend' : event.type;
1079
+ that['on' + type](event);
1080
+ };
1081
+ $(window).on('resize', proxyListener);
1082
+ $(document.body).on('keydown', proxyListener);
1083
+ this.container.on('click', proxyListener);
1084
+ if (this.support.touch) {
1085
+ slidesContainer
1086
+ .on('touchstart touchmove touchend', proxyListener);
1087
+ } else if (this.options.emulateTouchEvents &&
1088
+ this.support.transition) {
1089
+ slidesContainer
1090
+ .on('mousedown mousemove mouseup mouseout', proxyListener);
1091
+ }
1092
+ if (this.support.transition) {
1093
+ slidesContainer.on(
1094
+ this.support.transition.end,
1095
+ proxyListener
1096
+ );
1097
+ }
1098
+ this.proxyListener = proxyListener;
1099
+ },
1100
+
1101
+ destroyEventListeners: function () {
1102
+ var slidesContainer = this.slidesContainer,
1103
+ proxyListener = this.proxyListener;
1104
+ $(window).off('resize', proxyListener);
1105
+ $(document.body).off('keydown', proxyListener);
1106
+ this.container.off('click', proxyListener);
1107
+ if (this.support.touch) {
1108
+ slidesContainer
1109
+ .off('touchstart touchmove touchend', proxyListener);
1110
+ } else if (this.options.emulateTouchEvents &&
1111
+ this.support.transition) {
1112
+ slidesContainer
1113
+ .off('mousedown mousemove mouseup mouseout', proxyListener);
1114
+ }
1115
+ if (this.support.transition) {
1116
+ slidesContainer.off(
1117
+ this.support.transition.end,
1118
+ proxyListener
1119
+ );
1120
+ }
1121
+ },
1122
+
1123
+ initWidget: function () {
1124
+ this.container = $(this.options.container);
1125
+ if (!this.container.length) {
1126
+ return false;
1127
+ }
1128
+ this.slidesContainer = this.container.find(
1129
+ this.options.slidesContainer
1130
+ );
1131
+ if (!this.slidesContainer.length) {
1132
+ return false;
1133
+ }
1134
+ this.titleElement = this.container.find(
1135
+ this.options.titleElement
1136
+ );
1137
+ if (this.options.hidePageScrollbars) {
1138
+ // Hide the page scrollbars:
1139
+ this.bodyOverflowStyle = document.body.style.overflow;
1140
+ document.body.style.overflow = 'hidden';
1141
+ }
1142
+ if (this.num === 1) {
1143
+ this.container.addClass(this.options.singleClass);
1144
+ }
1145
+ this.container[0].style.display = 'block';
1146
+ this.initSlides();
1147
+ this.container.addClass(this.options.displayClass);
1148
+ },
1149
+
1150
+ initOptions: function (options) {
1151
+ // Create a copy of the prototype options:
1152
+ this.options = $.extend({}, this.options);
1153
+ // Check if carousel mode is enabled:
1154
+ if ((options && options.carousel) ||
1155
+ (this.options.carousel && (!options || options.carousel !== false))) {
1156
+ $.extend(this.options, this.carouselOptions);
1157
+ }
1158
+ // Override any given options:
1159
+ $.extend(this.options, options);
1160
+ if (this.num < 3) {
1161
+ // 1 or 2 slides cannot be displayed continuous,
1162
+ // remember the original option by setting to null instead of false:
1163
+ this.options.continuous = this.options.continuous ? null : false;
1164
+ }
1165
+ if (!this.support.transition) {
1166
+ this.options.emulateTouchEvents = false;
1167
+ }
1168
+ if (this.options.event) {
1169
+ this.preventDefault(this.options.event);
1170
+ }
1171
+ }
1172
+
1173
+ });
1174
+
1175
+ return Gallery;
1176
+ }));