1/*! lightgallery - v1.3.9 - 2017-02-05 2* http://sachinchoolur.github.io/lightGallery/ 3* Copyright (c) 2017 Sachin N; Licensed GPLv3 */ 4 5(function (root, factory) { 6 if (typeof define === 'function' && define.amd) { 7 // AMD. Register as an anonymous module unless amdModuleId is set 8 define(['jquery'], function (a0) { 9 return (factory(a0)); 10 }); 11 } else if (typeof exports === 'object') { 12 // Node. Does not work with strict CommonJS, but 13 // only CommonJS-like environments that support module.exports, 14 // like Node. 15 module.exports = factory(require('jquery')); 16 } else { 17 factory(root["jQuery"]); 18 } 19}(this, function ($) { 20 21(function() { 22 'use strict'; 23 24 var defaults = { 25 26 mode: 'lg-slide', 27 28 // Ex : 'ease' 29 cssEasing: 'ease', 30 31 //'for jquery animation' 32 easing: 'linear', 33 speed: 600, 34 height: '100%', 35 width: '100%', 36 addClass: '', 37 startClass: 'lg-start-zoom', 38 backdropDuration: 150, 39 hideBarsDelay: 6000, 40 41 useLeft: false, 42 43 closable: true, 44 loop: true, 45 escKey: true, 46 keyPress: true, 47 controls: true, 48 slideEndAnimatoin: true, 49 hideControlOnEnd: false, 50 mousewheel: true, 51 52 getCaptionFromTitleOrAlt: true, 53 54 // .lg-item || '.lg-sub-html' 55 appendSubHtmlTo: '.lg-sub-html', 56 57 subHtmlSelectorRelative: false, 58 59 /** 60 * @desc number of preload slides 61 * will exicute only after the current slide is fully loaded. 62 * 63 * @ex you clicked on 4th image and if preload = 1 then 3rd slide and 5th 64 * slide will be loaded in the background after the 4th slide is fully loaded.. 65 * if preload is 2 then 2nd 3rd 5th 6th slides will be preloaded.. ... ... 66 * 67 */ 68 preload: 1, 69 showAfterLoad: true, 70 selector: '', 71 selectWithin: '', 72 nextHtml: '', 73 prevHtml: '', 74 75 // 0, 1 76 index: false, 77 78 iframeMaxWidth: '100%', 79 80 download: true, 81 counter: true, 82 appendCounterTo: '.lg-toolbar', 83 84 swipeThreshold: 50, 85 enableSwipe: true, 86 enableDrag: true, 87 88 dynamic: false, 89 dynamicEl: [], 90 galleryId: 1 91 }; 92 93 function Plugin(element, options) { 94 95 // Current lightGallery element 96 this.el = element; 97 98 // Current jquery element 99 this.$el = $(element); 100 101 // lightGallery settings 102 this.s = $.extend({}, defaults, options); 103 104 // When using dynamic mode, ensure dynamicEl is an array 105 if (this.s.dynamic && this.s.dynamicEl !== 'undefined' && this.s.dynamicEl.constructor === Array && !this.s.dynamicEl.length) { 106 throw ('When using dynamic mode, you must also define dynamicEl as an Array.'); 107 } 108 109 // lightGallery modules 110 this.modules = {}; 111 112 // false when lightgallery complete first slide; 113 this.lGalleryOn = false; 114 115 this.lgBusy = false; 116 117 // Timeout function for hiding controls; 118 this.hideBartimeout = false; 119 120 // To determine browser supports for touch events; 121 this.isTouch = ('ontouchstart' in document.documentElement); 122 123 // Disable hideControlOnEnd if sildeEndAnimation is true 124 if (this.s.slideEndAnimatoin) { 125 this.s.hideControlOnEnd = false; 126 } 127 128 // Gallery items 129 if (this.s.dynamic) { 130 this.$items = this.s.dynamicEl; 131 } else { 132 if (this.s.selector === 'this') { 133 this.$items = this.$el; 134 } else if (this.s.selector !== '') { 135 if (this.s.selectWithin) { 136 this.$items = $(this.s.selectWithin).find(this.s.selector); 137 } else { 138 this.$items = this.$el.find($(this.s.selector)); 139 } 140 } else { 141 this.$items = this.$el.children(); 142 } 143 } 144 145 // .lg-item 146 this.$slide = ''; 147 148 // .lg-outer 149 this.$outer = ''; 150 151 this.init(); 152 153 return this; 154 } 155 156 Plugin.prototype.init = function() { 157 158 var _this = this; 159 160 // s.preload should not be more than $item.length 161 if (_this.s.preload > _this.$items.length) { 162 _this.s.preload = _this.$items.length; 163 } 164 165 // if dynamic option is enabled execute immediately 166 var _hash = window.location.hash; 167 if (_hash.indexOf('lg=' + this.s.galleryId) > 0) { 168 169 _this.index = parseInt(_hash.split('&slide=')[1], 10); 170 171 $('body').addClass('lg-from-hash'); 172 if (!$('body').hasClass('lg-on')) { 173 setTimeout(function() { 174 _this.build(_this.index); 175 }); 176 177 $('body').addClass('lg-on'); 178 } 179 } 180 181 if (_this.s.dynamic) { 182 183 _this.$el.trigger('onBeforeOpen.lg'); 184 185 _this.index = _this.s.index || 0; 186 187 // prevent accidental double execution 188 if (!$('body').hasClass('lg-on')) { 189 setTimeout(function() { 190 _this.build(_this.index); 191 $('body').addClass('lg-on'); 192 }); 193 } 194 } else { 195 196 // Using different namespace for click because click event should not unbind if selector is same object('this') 197 _this.$items.on('click.lgcustom', function(event) { 198 199 // For IE8 200 try { 201 event.preventDefault(); 202 event.preventDefault(); 203 } catch (er) { 204 event.returnValue = false; 205 } 206 207 _this.$el.trigger('onBeforeOpen.lg'); 208 209 _this.index = _this.s.index || _this.$items.index(this); 210 211 // prevent accidental double execution 212 if (!$('body').hasClass('lg-on')) { 213 _this.build(_this.index); 214 $('body').addClass('lg-on'); 215 } 216 }); 217 } 218 219 }; 220 221 Plugin.prototype.build = function(index) { 222 223 var _this = this; 224 225 _this.structure(); 226 227 // module constructor 228 $.each($.fn.lightGallery.modules, function(key) { 229 _this.modules[key] = new $.fn.lightGallery.modules[key](_this.el); 230 }); 231 232 // initiate slide function 233 _this.slide(index, false, false, false); 234 235 if (_this.s.keyPress) { 236 _this.keyPress(); 237 } 238 239 if (_this.$items.length > 1) { 240 241 _this.arrow(); 242 243 setTimeout(function() { 244 _this.enableDrag(); 245 _this.enableSwipe(); 246 }, 50); 247 248 if (_this.s.mousewheel) { 249 _this.mousewheel(); 250 } 251 } 252 253 _this.counter(); 254 255 _this.closeGallery(); 256 257 _this.$el.trigger('onAfterOpen.lg'); 258 259 // Hide controllers if mouse doesn't move for some period 260 _this.$outer.on('mousemove.lg click.lg touchstart.lg', function() { 261 262 _this.$outer.removeClass('lg-hide-items'); 263 264 clearTimeout(_this.hideBartimeout); 265 266 // Timeout will be cleared on each slide movement also 267 _this.hideBartimeout = setTimeout(function() { 268 _this.$outer.addClass('lg-hide-items'); 269 }, _this.s.hideBarsDelay); 270 271 }); 272 273 _this.$outer.trigger('mousemove.lg'); 274 275 }; 276 277 Plugin.prototype.structure = function() { 278 var list = ''; 279 var controls = ''; 280 var i = 0; 281 var subHtmlCont = ''; 282 var template; 283 var _this = this; 284 285 $('body').append('<div class="lg-backdrop"></div>'); 286 $('.lg-backdrop').css('transition-duration', this.s.backdropDuration + 'ms'); 287 288 // Create gallery items 289 for (i = 0; i < this.$items.length; i++) { 290 list += '<div class="lg-item"></div>'; 291 } 292 293 // Create controlls 294 if (this.s.controls && this.$items.length > 1) { 295 controls = '<div class="lg-actions">' + 296 '<div class="lg-prev lg-icon">' + this.s.prevHtml + '</div>' + 297 '<div class="lg-next lg-icon">' + this.s.nextHtml + '</div>' + 298 '</div>'; 299 } 300 301 if (this.s.appendSubHtmlTo === '.lg-sub-html') { 302 subHtmlCont = '<div class="lg-sub-html"></div>'; 303 } 304 305 template = '<div class="lg-outer ' + this.s.addClass + ' ' + this.s.startClass + '">' + 306 '<div class="lg" style="width:' + this.s.width + '; height:' + this.s.height + '">' + 307 '<div class="lg-inner">' + list + '</div>' + 308 '<div class="lg-toolbar lg-group">' + 309 '<span class="lg-close lg-icon"></span>' + 310 '</div>' + 311 controls + 312 subHtmlCont + 313 '</div>' + 314 '</div>'; 315 316 $('body').append(template); 317 this.$outer = $('.lg-outer'); 318 this.$slide = this.$outer.find('.lg-item'); 319 320 if (this.s.useLeft) { 321 this.$outer.addClass('lg-use-left'); 322 323 // Set mode lg-slide if use left is true; 324 this.s.mode = 'lg-slide'; 325 } else { 326 this.$outer.addClass('lg-use-css3'); 327 } 328 329 // For fixed height gallery 330 _this.setTop(); 331 $(window).on('resize.lg orientationchange.lg', function() { 332 setTimeout(function() { 333 _this.setTop(); 334 }, 100); 335 }); 336 337 // add class lg-current to remove initial transition 338 this.$slide.eq(this.index).addClass('lg-current'); 339 340 // add Class for css support and transition mode 341 if (this.doCss()) { 342 this.$outer.addClass('lg-css3'); 343 } else { 344 this.$outer.addClass('lg-css'); 345 346 // Set speed 0 because no animation will happen if browser doesn't support css3 347 this.s.speed = 0; 348 } 349 350 this.$outer.addClass(this.s.mode); 351 352 if (this.s.enableDrag && this.$items.length > 1) { 353 this.$outer.addClass('lg-grab'); 354 } 355 356 if (this.s.showAfterLoad) { 357 this.$outer.addClass('lg-show-after-load'); 358 } 359 360 if (this.doCss()) { 361 var $inner = this.$outer.find('.lg-inner'); 362 $inner.css('transition-timing-function', this.s.cssEasing); 363 $inner.css('transition-duration', this.s.speed + 'ms'); 364 } 365 366 setTimeout(function() { 367 $('.lg-backdrop').addClass('in'); 368 }); 369 370 setTimeout(function() { 371 _this.$outer.addClass('lg-visible'); 372 }, this.s.backdropDuration); 373 374 if (this.s.download) { 375 this.$outer.find('.lg-toolbar').append('<a id="lg-download" target="_blank" download class="lg-download lg-icon"></a>'); 376 } 377 378 // Store the current scroll top value to scroll back after closing the gallery.. 379 this.prevScrollTop = $(window).scrollTop(); 380 381 }; 382 383 // For fixed height gallery 384 Plugin.prototype.setTop = function() { 385 if (this.s.height !== '100%') { 386 var wH = $(window).height(); 387 var top = (wH - parseInt(this.s.height, 10)) / 2; 388 var $lGallery = this.$outer.find('.lg'); 389 if (wH >= parseInt(this.s.height, 10)) { 390 $lGallery.css('top', top + 'px'); 391 } else { 392 $lGallery.css('top', '0px'); 393 } 394 } 395 }; 396 397 // Find css3 support 398 Plugin.prototype.doCss = function() { 399 // check for css animation support 400 var support = function() { 401 var transition = ['transition', 'MozTransition', 'WebkitTransition', 'OTransition', 'msTransition', 'KhtmlTransition']; 402 var root = document.documentElement; 403 var i = 0; 404 for (i = 0; i < transition.length; i++) { 405 if (transition[i] in root.style) { 406 return true; 407 } 408 } 409 }; 410 411 if (support()) { 412 return true; 413 } 414 415 return false; 416 }; 417 418 /** 419 * @desc Check the given src is video 420 * @param {String} src 421 * @return {Object} video type 422 * Ex:{ youtube : ["//www.youtube.com/watch?v=c0asJgSyxcY", "c0asJgSyxcY"] } 423 */ 424 Plugin.prototype.isVideo = function(src, index) { 425 426 var html; 427 if (this.s.dynamic) { 428 html = this.s.dynamicEl[index].html; 429 } else { 430 html = this.$items.eq(index).attr('data-html'); 431 } 432 433 if (!src && html) { 434 return { 435 html5: true 436 }; 437 } 438 439 var youtube = src.match(/\/\/(?:www\.)?youtu(?:\.be|be\.com)\/(?:watch\?v=|embed\/)?([a-z0-9\-\_\%]+)/i); 440 var vimeo = src.match(/\/\/(?:www\.)?vimeo.com\/([0-9a-z\-_]+)/i); 441 var dailymotion = src.match(/\/\/(?:www\.)?dai.ly\/([0-9a-z\-_]+)/i); 442 var vk = src.match(/\/\/(?:www\.)?(?:vk\.com|vkontakte\.ru)\/(?:video_ext\.php\?)(.*)/i); 443 444 if (youtube) { 445 return { 446 youtube: youtube 447 }; 448 } else if (vimeo) { 449 return { 450 vimeo: vimeo 451 }; 452 } else if (dailymotion) { 453 return { 454 dailymotion: dailymotion 455 }; 456 } else if (vk) { 457 return { 458 vk: vk 459 }; 460 } 461 }; 462 463 /** 464 * @desc Create image counter 465 * Ex: 1/10 466 */ 467 Plugin.prototype.counter = function() { 468 if (this.s.counter) { 469 $(this.s.appendCounterTo).append('<div id="lg-counter"><span id="lg-counter-current">' + (parseInt(this.index, 10) + 1) + '</span> / <span id="lg-counter-all">' + this.$items.length + '</span></div>'); 470 } 471 }; 472 473 /** 474 * @desc add sub-html into the slide 475 * @param {Number} index - index of the slide 476 */ 477 Plugin.prototype.addHtml = function(index) { 478 var subHtml = null; 479 var subHtmlUrl; 480 var $currentEle; 481 if (this.s.dynamic) { 482 if (this.s.dynamicEl[index].subHtmlUrl) { 483 subHtmlUrl = this.s.dynamicEl[index].subHtmlUrl; 484 } else { 485 subHtml = this.s.dynamicEl[index].subHtml; 486 } 487 } else { 488 $currentEle = this.$items.eq(index); 489 if ($currentEle.attr('data-sub-html-url')) { 490 subHtmlUrl = $currentEle.attr('data-sub-html-url'); 491 } else { 492 subHtml = $currentEle.attr('data-sub-html'); 493 if (this.s.getCaptionFromTitleOrAlt && !subHtml) { 494 subHtml = $currentEle.attr('title') || $currentEle.find('img').first().attr('alt'); 495 } 496 } 497 } 498 499 if (!subHtmlUrl) { 500 if (typeof subHtml !== 'undefined' && subHtml !== null) { 501 502 // get first letter of subhtml 503 // if first letter starts with . or # get the html form the jQuery object 504 var fL = subHtml.substring(0, 1); 505 if (fL === '.' || fL === '#') { 506 if (this.s.subHtmlSelectorRelative && !this.s.dynamic) { 507 subHtml = $currentEle.find(subHtml).html(); 508 } else { 509 subHtml = $(subHtml).html(); 510 } 511 } 512 } else { 513 subHtml = ''; 514 } 515 } 516 517 if (this.s.appendSubHtmlTo === '.lg-sub-html') { 518 519 if (subHtmlUrl) { 520 this.$outer.find(this.s.appendSubHtmlTo).load(subHtmlUrl); 521 } else { 522 this.$outer.find(this.s.appendSubHtmlTo).html(subHtml); 523 } 524 525 } else { 526 527 if (subHtmlUrl) { 528 this.$slide.eq(index).load(subHtmlUrl); 529 } else { 530 this.$slide.eq(index).append(subHtml); 531 } 532 } 533 534 // Add lg-empty-html class if title doesn't exist 535 if (typeof subHtml !== 'undefined' && subHtml !== null) { 536 if (subHtml === '') { 537 this.$outer.find(this.s.appendSubHtmlTo).addClass('lg-empty-html'); 538 } else { 539 this.$outer.find(this.s.appendSubHtmlTo).removeClass('lg-empty-html'); 540 } 541 } 542 543 this.$el.trigger('onAfterAppendSubHtml.lg', [index]); 544 }; 545 546 /** 547 * @desc Preload slides 548 * @param {Number} index - index of the slide 549 */ 550 Plugin.prototype.preload = function(index) { 551 var i = 1; 552 var j = 1; 553 for (i = 1; i <= this.s.preload; i++) { 554 if (i >= this.$items.length - index) { 555 break; 556 } 557 558 this.loadContent(index + i, false, 0); 559 } 560 561 for (j = 1; j <= this.s.preload; j++) { 562 if (index - j < 0) { 563 break; 564 } 565 566 this.loadContent(index - j, false, 0); 567 } 568 }; 569 570 /** 571 * @desc Load slide content into slide. 572 * @param {Number} index - index of the slide. 573 * @param {Boolean} rec - if true call loadcontent() function again. 574 * @param {Boolean} delay - delay for adding complete class. it is 0 except first time. 575 */ 576 Plugin.prototype.loadContent = function(index, rec, delay) { 577 578 var _this = this; 579 var _hasPoster = false; 580 var _$img; 581 var _src; 582 var _poster; 583 var _srcset; 584 var _sizes; 585 var _html; 586 var getResponsiveSrc = function(srcItms) { 587 var rsWidth = []; 588 var rsSrc = []; 589 for (var i = 0; i < srcItms.length; i++) { 590 var __src = srcItms[i].split(' '); 591 592 // Manage empty space 593 if (__src[0] === '') { 594 __src.splice(0, 1); 595 } 596 597 rsSrc.push(__src[0]); 598 rsWidth.push(__src[1]); 599 } 600 601 var wWidth = $(window).width(); 602 for (var j = 0; j < rsWidth.length; j++) { 603 if (parseInt(rsWidth[j], 10) > wWidth) { 604 _src = rsSrc[j]; 605 break; 606 } 607 } 608 }; 609 610 if (_this.s.dynamic) { 611 612 if (_this.s.dynamicEl[index].poster) { 613 _hasPoster = true; 614 _poster = _this.s.dynamicEl[index].poster; 615 } 616 617 _html = _this.s.dynamicEl[index].html; 618 _src = _this.s.dynamicEl[index].src; 619 620 if (_this.s.dynamicEl[index].responsive) { 621 var srcDyItms = _this.s.dynamicEl[index].responsive.split(','); 622 getResponsiveSrc(srcDyItms); 623 } 624 625 _srcset = _this.s.dynamicEl[index].srcset; 626 _sizes = _this.s.dynamicEl[index].sizes; 627 628 } else { 629 630 if (_this.$items.eq(index).attr('data-poster')) { 631 _hasPoster = true; 632 _poster = _this.$items.eq(index).attr('data-poster'); 633 } 634 635 _html = _this.$items.eq(index).attr('data-html'); 636 _src = _this.$items.eq(index).attr('href') || _this.$items.eq(index).attr('data-src'); 637 638 if (_this.$items.eq(index).attr('data-responsive')) { 639 var srcItms = _this.$items.eq(index).attr('data-responsive').split(','); 640 getResponsiveSrc(srcItms); 641 } 642 643 _srcset = _this.$items.eq(index).attr('data-srcset'); 644 _sizes = _this.$items.eq(index).attr('data-sizes'); 645 646 } 647 648 //if (_src || _srcset || _sizes || _poster) { 649 650 var iframe = false; 651 if (_this.s.dynamic) { 652 if (_this.s.dynamicEl[index].iframe) { 653 iframe = true; 654 } 655 } else { 656 if (_this.$items.eq(index).attr('data-iframe') === 'true') { 657 iframe = true; 658 } 659 } 660 661 var _isVideo = _this.isVideo(_src, index); 662 if (!_this.$slide.eq(index).hasClass('lg-loaded')) { 663 if (iframe) { 664 _this.$slide.eq(index).prepend('<div class="lg-video-cont" style="max-width:' + _this.s.iframeMaxWidth + '"><div class="lg-video"><iframe class="lg-object" frameborder="0" src="' + _src + '" allowfullscreen="true"></iframe></div></div>'); 665 } else if (_hasPoster) { 666 var videoClass = ''; 667 if (_isVideo && _isVideo.youtube) { 668 videoClass = 'lg-has-youtube'; 669 } else if (_isVideo && _isVideo.vimeo) { 670 videoClass = 'lg-has-vimeo'; 671 } else { 672 videoClass = 'lg-has-html5'; 673 } 674 675 _this.$slide.eq(index).prepend('<div class="lg-video-cont ' + videoClass + ' "><div class="lg-video"><span class="lg-video-play"></span><img class="lg-object lg-has-poster" src="' + _poster + '" /></div></div>'); 676 677 } else if (_isVideo) { 678 _this.$slide.eq(index).prepend('<div class="lg-video-cont "><div class="lg-video"></div></div>'); 679 _this.$el.trigger('hasVideo.lg', [index, _src, _html]); 680 } else { 681 _this.$slide.eq(index).prepend('<div class="lg-img-wrap"><img class="lg-object lg-image" src="' + _src + '" /></div>'); 682 } 683 684 _this.$el.trigger('onAferAppendSlide.lg', [index]); 685 686 _$img = _this.$slide.eq(index).find('.lg-object'); 687 if (_sizes) { 688 _$img.attr('sizes', _sizes); 689 } 690 691 if (_srcset) { 692 _$img.attr('srcset', _srcset); 693 try { 694 picturefill({ 695 elements: [_$img[0]] 696 }); 697 } catch (e) { 698 console.error('Make sure you have included Picturefill version 2'); 699 } 700 } 701 702 if (this.s.appendSubHtmlTo !== '.lg-sub-html') { 703 _this.addHtml(index); 704 } 705 706 _this.$slide.eq(index).addClass('lg-loaded'); 707 } 708 709 _this.$slide.eq(index).find('.lg-object').on('load.lg error.lg', function() { 710 711 // For first time add some delay for displaying the start animation. 712 var _speed = 0; 713 714 // Do not change the delay value because it is required for zoom plugin. 715 // If gallery opened from direct url (hash) speed value should be 0 716 if (delay && !$('body').hasClass('lg-from-hash')) { 717 _speed = delay; 718 } 719 720 setTimeout(function() { 721 _this.$slide.eq(index).addClass('lg-complete'); 722 _this.$el.trigger('onSlideItemLoad.lg', [index, delay || 0]); 723 }, _speed); 724 725 }); 726 727 // @todo check load state for html5 videos 728 if (_isVideo && _isVideo.html5 && !_hasPoster) { 729 _this.$slide.eq(index).addClass('lg-complete'); 730 } 731 732 if (rec === true) { 733 if (!_this.$slide.eq(index).hasClass('lg-complete')) { 734 _this.$slide.eq(index).find('.lg-object').on('load.lg error.lg', function() { 735 _this.preload(index); 736 }); 737 } else { 738 _this.preload(index); 739 } 740 } 741 742 //} 743 }; 744 745 /** 746 * @desc slide function for lightgallery 747 ** Slide() gets call on start 748 ** ** Set lg.on true once slide() function gets called. 749 ** Call loadContent() on slide() function inside setTimeout 750 ** ** On first slide we do not want any animation like slide of fade 751 ** ** So on first slide( if lg.on if false that is first slide) loadContent() should start loading immediately 752 ** ** Else loadContent() should wait for the transition to complete. 753 ** ** So set timeout s.speed + 50 754 <=> ** loadContent() will load slide content in to the particular slide 755 ** ** It has recursion (rec) parameter. if rec === true loadContent() will call preload() function. 756 ** ** preload will execute only when the previous slide is fully loaded (images iframe) 757 ** ** avoid simultaneous image load 758 <=> ** Preload() will check for s.preload value and call loadContent() again accoring to preload value 759 ** loadContent() <====> Preload(); 760 761 * @param {Number} index - index of the slide 762 * @param {Boolean} fromTouch - true if slide function called via touch event or mouse drag 763 * @param {Boolean} fromThumb - true if slide function called via thumbnail click 764 * @param {String} direction - Direction of the slide(next/prev) 765 */ 766 Plugin.prototype.slide = function(index, fromTouch, fromThumb, direction) { 767 768 var _prevIndex = this.$outer.find('.lg-current').index(); 769 var _this = this; 770 771 // Prevent if multiple call 772 // Required for hsh plugin 773 if (_this.lGalleryOn && (_prevIndex === index)) { 774 return; 775 } 776 777 var _length = this.$slide.length; 778 var _time = _this.lGalleryOn ? this.s.speed : 0; 779 780 if (!_this.lgBusy) { 781 782 if (this.s.download) { 783 var _src; 784 if (_this.s.dynamic) { 785 _src = _this.s.dynamicEl[index].downloadUrl !== false && (_this.s.dynamicEl[index].downloadUrl || _this.s.dynamicEl[index].src); 786 } else { 787 _src = _this.$items.eq(index).attr('data-download-url') !== 'false' && (_this.$items.eq(index).attr('data-download-url') || _this.$items.eq(index).attr('href') || _this.$items.eq(index).attr('data-src')); 788 789 } 790 791 if (_src) { 792 $('#lg-download').attr('href', _src); 793 _this.$outer.removeClass('lg-hide-download'); 794 } else { 795 _this.$outer.addClass('lg-hide-download'); 796 } 797 } 798 799 this.$el.trigger('onBeforeSlide.lg', [_prevIndex, index, fromTouch, fromThumb]); 800 801 _this.lgBusy = true; 802 803 clearTimeout(_this.hideBartimeout); 804 805 // Add title if this.s.appendSubHtmlTo === lg-sub-html 806 if (this.s.appendSubHtmlTo === '.lg-sub-html') { 807 808 // wait for slide animation to complete 809 setTimeout(function() { 810 _this.addHtml(index); 811 }, _time); 812 } 813 814 this.arrowDisable(index); 815 816 if (!direction) { 817 if (index < _prevIndex) { 818 direction = 'prev'; 819 } else if (index > _prevIndex) { 820 direction = 'next'; 821 } 822 } 823 824 if (!fromTouch) { 825 826 // remove all transitions 827 _this.$outer.addClass('lg-no-trans'); 828 829 this.$slide.removeClass('lg-prev-slide lg-next-slide'); 830 831 if (direction === 'prev') { 832 833 //prevslide 834 this.$slide.eq(index).addClass('lg-prev-slide'); 835 this.$slide.eq(_prevIndex).addClass('lg-next-slide'); 836 } else { 837 838 // next slide 839 this.$slide.eq(index).addClass('lg-next-slide'); 840 this.$slide.eq(_prevIndex).addClass('lg-prev-slide'); 841 } 842 843 // give 50 ms for browser to add/remove class 844 setTimeout(function() { 845 _this.$slide.removeClass('lg-current'); 846 847 //_this.$slide.eq(_prevIndex).removeClass('lg-current'); 848 _this.$slide.eq(index).addClass('lg-current'); 849 850 // reset all transitions 851 _this.$outer.removeClass('lg-no-trans'); 852 }, 50); 853 } else { 854 855 this.$slide.removeClass('lg-prev-slide lg-current lg-next-slide'); 856 var touchPrev; 857 var touchNext; 858 if (_length > 2) { 859 touchPrev = index - 1; 860 touchNext = index + 1; 861 862 if ((index === 0) && (_prevIndex === _length - 1)) { 863 864 // next slide 865 touchNext = 0; 866 touchPrev = _length - 1; 867 } else if ((index === _length - 1) && (_prevIndex === 0)) { 868 869 // prev slide 870 touchNext = 0; 871 touchPrev = _length - 1; 872 } 873 874 } else { 875 touchPrev = 0; 876 touchNext = 1; 877 } 878 879 if (direction === 'prev') { 880 _this.$slide.eq(touchNext).addClass('lg-next-slide'); 881 } else { 882 _this.$slide.eq(touchPrev).addClass('lg-prev-slide'); 883 } 884 885 _this.$slide.eq(index).addClass('lg-current'); 886 } 887 888 if (_this.lGalleryOn) { 889 setTimeout(function() { 890 _this.loadContent(index, true, 0); 891 }, this.s.speed + 50); 892 893 setTimeout(function() { 894 _this.lgBusy = false; 895 _this.$el.trigger('onAfterSlide.lg', [_prevIndex, index, fromTouch, fromThumb]); 896 }, this.s.speed); 897 898 } else { 899 _this.loadContent(index, true, _this.s.backdropDuration); 900 901 _this.lgBusy = false; 902 _this.$el.trigger('onAfterSlide.lg', [_prevIndex, index, fromTouch, fromThumb]); 903 } 904 905 _this.lGalleryOn = true; 906 907 if (this.s.counter) { 908 $('#lg-counter-current').text(index + 1); 909 } 910 911 } 912 913 }; 914 915 /** 916 * @desc Go to next slide 917 * @param {Boolean} fromTouch - true if slide function called via touch event 918 */ 919 Plugin.prototype.goToNextSlide = function(fromTouch) { 920 var _this = this; 921 var _loop = _this.s.loop; 922 if (fromTouch && _this.$slide.length < 3) { 923 _loop = false; 924 } 925 926 if (!_this.lgBusy) { 927 if ((_this.index + 1) < _this.$slide.length) { 928 _this.index++; 929 _this.$el.trigger('onBeforeNextSlide.lg', [_this.index]); 930 _this.slide(_this.index, fromTouch, false, 'next'); 931 } else { 932 if (_loop) { 933 _this.index = 0; 934 _this.$el.trigger('onBeforeNextSlide.lg', [_this.index]); 935 _this.slide(_this.index, fromTouch, false, 'next'); 936 } else if (_this.s.slideEndAnimatoin && !fromTouch) { 937 _this.$outer.addClass('lg-right-end'); 938 setTimeout(function() { 939 _this.$outer.removeClass('lg-right-end'); 940 }, 400); 941 } 942 } 943 } 944 }; 945 946 /** 947 * @desc Go to previous slide 948 * @param {Boolean} fromTouch - true if slide function called via touch event 949 */ 950 Plugin.prototype.goToPrevSlide = function(fromTouch) { 951 var _this = this; 952 var _loop = _this.s.loop; 953 if (fromTouch && _this.$slide.length < 3) { 954 _loop = false; 955 } 956 957 if (!_this.lgBusy) { 958 if (_this.index > 0) { 959 _this.index--; 960 _this.$el.trigger('onBeforePrevSlide.lg', [_this.index, fromTouch]); 961 _this.slide(_this.index, fromTouch, false, 'prev'); 962 } else { 963 if (_loop) { 964 _this.index = _this.$items.length - 1; 965 _this.$el.trigger('onBeforePrevSlide.lg', [_this.index, fromTouch]); 966 _this.slide(_this.index, fromTouch, false, 'prev'); 967 } else if (_this.s.slideEndAnimatoin && !fromTouch) { 968 _this.$outer.addClass('lg-left-end'); 969 setTimeout(function() { 970 _this.$outer.removeClass('lg-left-end'); 971 }, 400); 972 } 973 } 974 } 975 }; 976 977 Plugin.prototype.keyPress = function() { 978 var _this = this; 979 if (this.$items.length > 1) { 980 $(window).on('keyup.lg', function(e) { 981 if (_this.$items.length > 1) { 982 if (e.keyCode === 37) { 983 e.preventDefault(); 984 _this.goToPrevSlide(); 985 } 986 987 if (e.keyCode === 39) { 988 e.preventDefault(); 989 _this.goToNextSlide(); 990 } 991 } 992 }); 993 } 994 995 $(window).on('keydown.lg', function(e) { 996 if (_this.s.escKey === true && e.keyCode === 27) { 997 e.preventDefault(); 998 if (!_this.$outer.hasClass('lg-thumb-open')) { 999 _this.destroy(); 1000 } else { 1001 _this.$outer.removeClass('lg-thumb-open'); 1002 } 1003 } 1004 }); 1005 }; 1006 1007 Plugin.prototype.arrow = function() { 1008 var _this = this; 1009 this.$outer.find('.lg-prev').on('click.lg', function() { 1010 _this.goToPrevSlide(); 1011 }); 1012 1013 this.$outer.find('.lg-next').on('click.lg', function() { 1014 _this.goToNextSlide(); 1015 }); 1016 }; 1017 1018 Plugin.prototype.arrowDisable = function(index) { 1019 1020 // Disable arrows if s.hideControlOnEnd is true 1021 if (!this.s.loop && this.s.hideControlOnEnd) { 1022 if ((index + 1) < this.$slide.length) { 1023 this.$outer.find('.lg-next').removeAttr('disabled').removeClass('disabled'); 1024 } else { 1025 this.$outer.find('.lg-next').attr('disabled', 'disabled').addClass('disabled'); 1026 } 1027 1028 if (index > 0) { 1029 this.$outer.find('.lg-prev').removeAttr('disabled').removeClass('disabled'); 1030 } else { 1031 this.$outer.find('.lg-prev').attr('disabled', 'disabled').addClass('disabled'); 1032 } 1033 } 1034 }; 1035 1036 Plugin.prototype.setTranslate = function($el, xValue, yValue) { 1037 // jQuery supports Automatic CSS prefixing since jQuery 1.8.0 1038 if (this.s.useLeft) { 1039 $el.css('left', xValue); 1040 } else { 1041 $el.css({ 1042 transform: 'translate3d(' + (xValue) + 'px, ' + yValue + 'px, 0px)' 1043 }); 1044 } 1045 }; 1046 1047 Plugin.prototype.touchMove = function(startCoords, endCoords) { 1048 1049 var distance = endCoords - startCoords; 1050 1051 if (Math.abs(distance) > 15) { 1052 // reset opacity and transition duration 1053 this.$outer.addClass('lg-dragging'); 1054 1055 // move current slide 1056 this.setTranslate(this.$slide.eq(this.index), distance, 0); 1057 1058 // move next and prev slide with current slide 1059 this.setTranslate($('.lg-prev-slide'), -this.$slide.eq(this.index).width() + distance, 0); 1060 this.setTranslate($('.lg-next-slide'), this.$slide.eq(this.index).width() + distance, 0); 1061 } 1062 }; 1063 1064 Plugin.prototype.touchEnd = function(distance) { 1065 var _this = this; 1066 1067 // keep slide animation for any mode while dragg/swipe 1068 if (_this.s.mode !== 'lg-slide') { 1069 _this.$outer.addClass('lg-slide'); 1070 } 1071 1072 this.$slide.not('.lg-current, .lg-prev-slide, .lg-next-slide').css('opacity', '0'); 1073 1074 // set transition duration 1075 setTimeout(function() { 1076 _this.$outer.removeClass('lg-dragging'); 1077 if ((distance < 0) && (Math.abs(distance) > _this.s.swipeThreshold)) { 1078 _this.goToNextSlide(true); 1079 } else if ((distance > 0) && (Math.abs(distance) > _this.s.swipeThreshold)) { 1080 _this.goToPrevSlide(true); 1081 } else if (Math.abs(distance) < 5) { 1082 1083 // Trigger click if distance is less than 5 pix 1084 _this.$el.trigger('onSlideClick.lg'); 1085 } 1086 1087 _this.$slide.removeAttr('style'); 1088 }); 1089 1090 // remove slide class once drag/swipe is completed if mode is not slide 1091 setTimeout(function() { 1092 if (!_this.$outer.hasClass('lg-dragging') && _this.s.mode !== 'lg-slide') { 1093 _this.$outer.removeClass('lg-slide'); 1094 } 1095 }, _this.s.speed + 100); 1096 1097 }; 1098 1099 Plugin.prototype.enableSwipe = function() { 1100 var _this = this; 1101 var startCoords = 0; 1102 var endCoords = 0; 1103 var isMoved = false; 1104 1105 if (_this.s.enableSwipe && _this.isTouch && _this.doCss()) { 1106 1107 _this.$slide.on('touchstart.lg', function(e) { 1108 if (!_this.$outer.hasClass('lg-zoomed') && !_this.lgBusy) { 1109 e.preventDefault(); 1110 _this.manageSwipeClass(); 1111 startCoords = e.originalEvent.targetTouches[0].pageX; 1112 } 1113 }); 1114 1115 _this.$slide.on('touchmove.lg', function(e) { 1116 if (!_this.$outer.hasClass('lg-zoomed')) { 1117 e.preventDefault(); 1118 endCoords = e.originalEvent.targetTouches[0].pageX; 1119 _this.touchMove(startCoords, endCoords); 1120 isMoved = true; 1121 } 1122 }); 1123 1124 _this.$slide.on('touchend.lg', function() { 1125 if (!_this.$outer.hasClass('lg-zoomed')) { 1126 if (isMoved) { 1127 isMoved = false; 1128 _this.touchEnd(endCoords - startCoords); 1129 } else { 1130 _this.$el.trigger('onSlideClick.lg'); 1131 } 1132 } 1133 }); 1134 } 1135 1136 }; 1137 1138 Plugin.prototype.enableDrag = function() { 1139 var _this = this; 1140 var startCoords = 0; 1141 var endCoords = 0; 1142 var isDraging = false; 1143 var isMoved = false; 1144 if (_this.s.enableDrag && !_this.isTouch && _this.doCss()) { 1145 _this.$slide.on('mousedown.lg', function(e) { 1146 // execute only on .lg-object 1147 if (!_this.$outer.hasClass('lg-zoomed')) { 1148 if ($(e.target).hasClass('lg-object') || $(e.target).hasClass('lg-video-play')) { 1149 e.preventDefault(); 1150 1151 if (!_this.lgBusy) { 1152 _this.manageSwipeClass(); 1153 startCoords = e.pageX; 1154 isDraging = true; 1155 1156 // ** Fix for webkit cursor issue https://code.google.com/p/chromium/issues/detail?id=26723 1157 _this.$outer.scrollLeft += 1; 1158 _this.$outer.scrollLeft -= 1; 1159 1160 // * 1161 1162 _this.$outer.removeClass('lg-grab').addClass('lg-grabbing'); 1163 1164 _this.$el.trigger('onDragstart.lg'); 1165 } 1166 1167 } 1168 } 1169 }); 1170 1171 $(window).on('mousemove.lg', function(e) { 1172 if (isDraging) { 1173 isMoved = true; 1174 endCoords = e.pageX; 1175 _this.touchMove(startCoords, endCoords); 1176 _this.$el.trigger('onDragmove.lg'); 1177 } 1178 }); 1179 1180 $(window).on('mouseup.lg', function(e) { 1181 if (isMoved) { 1182 isMoved = false; 1183 _this.touchEnd(endCoords - startCoords); 1184 _this.$el.trigger('onDragend.lg'); 1185 } else if ($(e.target).hasClass('lg-object') || $(e.target).hasClass('lg-video-play')) { 1186 _this.$el.trigger('onSlideClick.lg'); 1187 } 1188 1189 // Prevent execution on click 1190 if (isDraging) { 1191 isDraging = false; 1192 _this.$outer.removeClass('lg-grabbing').addClass('lg-grab'); 1193 } 1194 }); 1195 1196 } 1197 }; 1198 1199 Plugin.prototype.manageSwipeClass = function() { 1200 var _touchNext = this.index + 1; 1201 var _touchPrev = this.index - 1; 1202 if (this.s.loop && this.$slide.length > 2) { 1203 if (this.index === 0) { 1204 _touchPrev = this.$slide.length - 1; 1205 } else if (this.index === this.$slide.length - 1) { 1206 _touchNext = 0; 1207 } 1208 } 1209 1210 this.$slide.removeClass('lg-next-slide lg-prev-slide'); 1211 if (_touchPrev > -1) { 1212 this.$slide.eq(_touchPrev).addClass('lg-prev-slide'); 1213 } 1214 1215 this.$slide.eq(_touchNext).addClass('lg-next-slide'); 1216 }; 1217 1218 Plugin.prototype.mousewheel = function() { 1219 var _this = this; 1220 _this.$outer.on('mousewheel.lg', function(e) { 1221 1222 if (!e.deltaY) { 1223 return; 1224 } 1225 1226 if (e.deltaY > 0) { 1227 _this.goToPrevSlide(); 1228 } else { 1229 _this.goToNextSlide(); 1230 } 1231 1232 e.preventDefault(); 1233 }); 1234 1235 }; 1236 1237 Plugin.prototype.closeGallery = function() { 1238 1239 var _this = this; 1240 var mousedown = false; 1241 this.$outer.find('.lg-close').on('click.lg', function() { 1242 _this.destroy(); 1243 }); 1244 1245 if (_this.s.closable) { 1246 1247 // If you drag the slide and release outside gallery gets close on chrome 1248 // for preventing this check mousedown and mouseup happened on .lg-item or lg-outer 1249 _this.$outer.on('mousedown.lg', function(e) { 1250 1251 if ($(e.target).is('.lg-outer') || $(e.target).is('.lg-item ') || $(e.target).is('.lg-img-wrap')) { 1252 mousedown = true; 1253 } else { 1254 mousedown = false; 1255 } 1256 1257 }); 1258 1259 _this.$outer.on('mouseup.lg', function(e) { 1260 1261 if ($(e.target).is('.lg-outer') || $(e.target).is('.lg-item ') || $(e.target).is('.lg-img-wrap') && mousedown) { 1262 if (!_this.$outer.hasClass('lg-dragging')) { 1263 _this.destroy(); 1264 } 1265 } 1266 1267 }); 1268 1269 } 1270 1271 }; 1272 1273 Plugin.prototype.destroy = function(d) { 1274 1275 var _this = this; 1276 1277 if (!d) { 1278 _this.$el.trigger('onBeforeClose.lg'); 1279 $(window).scrollTop(_this.prevScrollTop); 1280 } 1281 1282 1283 /** 1284 * if d is false or undefined destroy will only close the gallery 1285 * plugins instance remains with the element 1286 * 1287 * if d is true destroy will completely remove the plugin 1288 */ 1289 1290 if (d) { 1291 if (!_this.s.dynamic) { 1292 // only when not using dynamic mode is $items a jquery collection 1293 this.$items.off('click.lg click.lgcustom'); 1294 } 1295 1296 $.removeData(_this.el, 'lightGallery'); 1297 } 1298 1299 // Unbind all events added by lightGallery 1300 this.$el.off('.lg.tm'); 1301 1302 // Distroy all lightGallery modules 1303 $.each($.fn.lightGallery.modules, function(key) { 1304 if (_this.modules[key]) { 1305 _this.modules[key].destroy(); 1306 } 1307 }); 1308 1309 this.lGalleryOn = false; 1310 1311 clearTimeout(_this.hideBartimeout); 1312 this.hideBartimeout = false; 1313 $(window).off('.lg'); 1314 $('body').removeClass('lg-on lg-from-hash'); 1315 1316 if (_this.$outer) { 1317 _this.$outer.removeClass('lg-visible'); 1318 } 1319 1320 $('.lg-backdrop').removeClass('in'); 1321 1322 setTimeout(function() { 1323 if (_this.$outer) { 1324 _this.$outer.remove(); 1325 } 1326 1327 $('.lg-backdrop').remove(); 1328 1329 if (!d) { 1330 _this.$el.trigger('onCloseAfter.lg'); 1331 } 1332 1333 }, _this.s.backdropDuration + 50); 1334 }; 1335 1336 $.fn.lightGallery = function(options) { 1337 return this.each(function() { 1338 if (!$.data(this, 'lightGallery')) { 1339 $.data(this, 'lightGallery', new Plugin(this, options)); 1340 } else { 1341 try { 1342 $(this).data('lightGallery').init(); 1343 } catch (err) { 1344 console.error('lightGallery has not initiated properly'); 1345 } 1346 } 1347 }); 1348 }; 1349 1350 $.fn.lightGallery.modules = {}; 1351 1352})(); 1353 1354 1355})); 1356