1/*! lg-zoom - v1.0.4 - 2016-12-20 2* http://sachinchoolur.github.io/lightGallery 3* Copyright (c) 2016 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(jQuery); 18 } 19}(this, function ($) { 20 21(function() { 22 23 'use strict'; 24 25 var getUseLeft = function() { 26 var useLeft = false; 27 var isChrome = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./); 28 if (isChrome && parseInt(isChrome[2], 10) < 54) { 29 useLeft = true; 30 } 31 32 return useLeft; 33 }; 34 35 var defaults = { 36 scale: 1, 37 zoom: true, 38 actualSize: true, 39 enableZoomAfter: 300, 40 useLeftForZoom: getUseLeft() 41 }; 42 43 var Zoom = function(element) { 44 45 this.core = $(element).data('lightGallery'); 46 47 this.core.s = $.extend({}, defaults, this.core.s); 48 49 if (this.core.s.zoom && this.core.doCss()) { 50 this.init(); 51 52 // Store the zoomable timeout value just to clear it while closing 53 this.zoomabletimeout = false; 54 55 // Set the initial value center 56 this.pageX = $(window).width() / 2; 57 this.pageY = ($(window).height() / 2) + $(window).scrollTop(); 58 } 59 60 return this; 61 }; 62 63 Zoom.prototype.init = function() { 64 65 var _this = this; 66 var zoomIcons = '<span id="lg-zoom-in" class="lg-icon"></span><span id="lg-zoom-out" class="lg-icon"></span>'; 67 68 if (_this.core.s.actualSize) { 69 zoomIcons += '<span id="lg-actual-size" class="lg-icon"></span>'; 70 } 71 72 if (_this.core.s.useLeftForZoom) { 73 _this.core.$outer.addClass('lg-use-left-for-zoom'); 74 } else { 75 _this.core.$outer.addClass('lg-use-transition-for-zoom'); 76 } 77 78 this.core.$outer.find('.lg-toolbar').append(zoomIcons); 79 80 // Add zoomable class 81 _this.core.$el.on('onSlideItemLoad.lg.tm.zoom', function(event, index, delay) { 82 83 // delay will be 0 except first time 84 var _speed = _this.core.s.enableZoomAfter + delay; 85 86 // set _speed value 0 if gallery opened from direct url and if it is first slide 87 if ($('body').hasClass('lg-from-hash') && delay) { 88 89 // will execute only once 90 _speed = 0; 91 } else { 92 93 // Remove lg-from-hash to enable starting animation. 94 $('body').removeClass('lg-from-hash'); 95 } 96 97 _this.zoomabletimeout = setTimeout(function() { 98 _this.core.$slide.eq(index).addClass('lg-zoomable'); 99 }, _speed + 30); 100 }); 101 102 var scale = 1; 103 /** 104 * @desc Image zoom 105 * Translate the wrap and scale the image to get better user experience 106 * 107 * @param {String} scaleVal - Zoom decrement/increment value 108 */ 109 var zoom = function(scaleVal) { 110 111 var $image = _this.core.$outer.find('.lg-current .lg-image'); 112 var _x; 113 var _y; 114 115 // Find offset manually to avoid issue after zoom 116 var offsetX = ($(window).width() - $image.prop('offsetWidth')) / 2; 117 var offsetY = (($(window).height() - $image.prop('offsetHeight')) / 2) + $(window).scrollTop(); 118 119 _x = _this.pageX - offsetX; 120 _y = _this.pageY - offsetY; 121 122 var x = (scaleVal - 1) * (_x); 123 var y = (scaleVal - 1) * (_y); 124 125 $image.css('transform', 'scale3d(' + scaleVal + ', ' + scaleVal + ', 1)').attr('data-scale', scaleVal); 126 127 if (_this.core.s.useLeftForZoom) { 128 $image.parent().css({ 129 left: -x + 'px', 130 top: -y + 'px' 131 }).attr('data-x', x).attr('data-y', y); 132 } else { 133 $image.parent().css('transform', 'translate3d(-' + x + 'px, -' + y + 'px, 0)').attr('data-x', x).attr('data-y', y); 134 } 135 }; 136 137 var callScale = function() { 138 if (scale > 1) { 139 _this.core.$outer.addClass('lg-zoomed'); 140 } else { 141 _this.resetZoom(); 142 } 143 144 if (scale < 1) { 145 scale = 1; 146 } 147 148 zoom(scale); 149 }; 150 151 var actualSize = function(event, $image, index, fromIcon) { 152 var w = $image.prop('offsetWidth'); 153 var nw; 154 if (_this.core.s.dynamic) { 155 nw = _this.core.s.dynamicEl[index].width || $image[0].naturalWidth || w; 156 } else { 157 nw = _this.core.$items.eq(index).attr('data-width') || $image[0].naturalWidth || w; 158 } 159 160 var _scale; 161 162 if (_this.core.$outer.hasClass('lg-zoomed')) { 163 scale = 1; 164 } else { 165 if (nw > w) { 166 _scale = nw / w; 167 scale = _scale || 2; 168 } 169 } 170 171 if (fromIcon) { 172 _this.pageX = $(window).width() / 2; 173 _this.pageY = ($(window).height() / 2) + $(window).scrollTop(); 174 } else { 175 _this.pageX = event.pageX || event.originalEvent.targetTouches[0].pageX; 176 _this.pageY = event.pageY || event.originalEvent.targetTouches[0].pageY; 177 } 178 179 callScale(); 180 setTimeout(function() { 181 _this.core.$outer.removeClass('lg-grabbing').addClass('lg-grab'); 182 }, 10); 183 }; 184 185 var tapped = false; 186 187 // event triggered after appending slide content 188 _this.core.$el.on('onAferAppendSlide.lg.tm.zoom', function(event, index) { 189 190 // Get the current element 191 var $image = _this.core.$slide.eq(index).find('.lg-image'); 192 193 $image.on('dblclick', function(event) { 194 actualSize(event, $image, index); 195 }); 196 197 $image.on('touchstart', function(event) { 198 if (!tapped) { 199 tapped = setTimeout(function() { 200 tapped = null; 201 }, 300); 202 } else { 203 clearTimeout(tapped); 204 tapped = null; 205 actualSize(event, $image, index); 206 } 207 208 event.preventDefault(); 209 }); 210 211 }); 212 213 // Update zoom on resize and orientationchange 214 $(window).on('resize.lg.zoom scroll.lg.zoom orientationchange.lg.zoom', function() { 215 _this.pageX = $(window).width() / 2; 216 _this.pageY = ($(window).height() / 2) + $(window).scrollTop(); 217 zoom(scale); 218 }); 219 220 $('#lg-zoom-out').on('click.lg', function() { 221 if (_this.core.$outer.find('.lg-current .lg-image').length) { 222 scale -= _this.core.s.scale; 223 callScale(); 224 } 225 }); 226 227 $('#lg-zoom-in').on('click.lg', function() { 228 if (_this.core.$outer.find('.lg-current .lg-image').length) { 229 scale += _this.core.s.scale; 230 callScale(); 231 } 232 }); 233 234 $('#lg-actual-size').on('click.lg', function(event) { 235 actualSize(event, _this.core.$slide.eq(_this.core.index).find('.lg-image'), _this.core.index, true); 236 }); 237 238 // Reset zoom on slide change 239 _this.core.$el.on('onBeforeSlide.lg.tm', function() { 240 scale = 1; 241 _this.resetZoom(); 242 }); 243 244 // Drag option after zoom 245 if (!_this.core.isTouch) { 246 _this.zoomDrag(); 247 } 248 249 if (_this.core.isTouch) { 250 _this.zoomSwipe(); 251 } 252 253 }; 254 255 // Reset zoom effect 256 Zoom.prototype.resetZoom = function() { 257 this.core.$outer.removeClass('lg-zoomed'); 258 this.core.$slide.find('.lg-img-wrap').removeAttr('style data-x data-y'); 259 this.core.$slide.find('.lg-image').removeAttr('style data-scale'); 260 261 // Reset pagx pagy values to center 262 this.pageX = $(window).width() / 2; 263 this.pageY = ($(window).height() / 2) + $(window).scrollTop(); 264 }; 265 266 Zoom.prototype.zoomSwipe = function() { 267 var _this = this; 268 var startCoords = {}; 269 var endCoords = {}; 270 var isMoved = false; 271 272 // Allow x direction drag 273 var allowX = false; 274 275 // Allow Y direction drag 276 var allowY = false; 277 278 _this.core.$slide.on('touchstart.lg', function(e) { 279 280 if (_this.core.$outer.hasClass('lg-zoomed')) { 281 var $image = _this.core.$slide.eq(_this.core.index).find('.lg-object'); 282 283 allowY = $image.prop('offsetHeight') * $image.attr('data-scale') > _this.core.$outer.find('.lg').height(); 284 allowX = $image.prop('offsetWidth') * $image.attr('data-scale') > _this.core.$outer.find('.lg').width(); 285 if ((allowX || allowY)) { 286 e.preventDefault(); 287 startCoords = { 288 x: e.originalEvent.targetTouches[0].pageX, 289 y: e.originalEvent.targetTouches[0].pageY 290 }; 291 } 292 } 293 294 }); 295 296 _this.core.$slide.on('touchmove.lg', function(e) { 297 298 if (_this.core.$outer.hasClass('lg-zoomed')) { 299 300 var _$el = _this.core.$slide.eq(_this.core.index).find('.lg-img-wrap'); 301 var distanceX; 302 var distanceY; 303 304 e.preventDefault(); 305 isMoved = true; 306 307 endCoords = { 308 x: e.originalEvent.targetTouches[0].pageX, 309 y: e.originalEvent.targetTouches[0].pageY 310 }; 311 312 // reset opacity and transition duration 313 _this.core.$outer.addClass('lg-zoom-dragging'); 314 315 if (allowY) { 316 distanceY = (-Math.abs(_$el.attr('data-y'))) + (endCoords.y - startCoords.y); 317 } else { 318 distanceY = -Math.abs(_$el.attr('data-y')); 319 } 320 321 if (allowX) { 322 distanceX = (-Math.abs(_$el.attr('data-x'))) + (endCoords.x - startCoords.x); 323 } else { 324 distanceX = -Math.abs(_$el.attr('data-x')); 325 } 326 327 if ((Math.abs(endCoords.x - startCoords.x) > 15) || (Math.abs(endCoords.y - startCoords.y) > 15)) { 328 329 if (_this.core.s.useLeftForZoom) { 330 _$el.css({ 331 left: distanceX + 'px', 332 top: distanceY + 'px' 333 }); 334 } else { 335 _$el.css('transform', 'translate3d(' + distanceX + 'px, ' + distanceY + 'px, 0)'); 336 } 337 } 338 339 } 340 341 }); 342 343 _this.core.$slide.on('touchend.lg', function() { 344 if (_this.core.$outer.hasClass('lg-zoomed')) { 345 if (isMoved) { 346 isMoved = false; 347 _this.core.$outer.removeClass('lg-zoom-dragging'); 348 _this.touchendZoom(startCoords, endCoords, allowX, allowY); 349 350 } 351 } 352 }); 353 354 }; 355 356 Zoom.prototype.zoomDrag = function() { 357 358 var _this = this; 359 var startCoords = {}; 360 var endCoords = {}; 361 var isDraging = false; 362 var isMoved = false; 363 364 // Allow x direction drag 365 var allowX = false; 366 367 // Allow Y direction drag 368 var allowY = false; 369 370 _this.core.$slide.on('mousedown.lg.zoom', function(e) { 371 372 // execute only on .lg-object 373 var $image = _this.core.$slide.eq(_this.core.index).find('.lg-object'); 374 375 allowY = $image.prop('offsetHeight') * $image.attr('data-scale') > _this.core.$outer.find('.lg').height(); 376 allowX = $image.prop('offsetWidth') * $image.attr('data-scale') > _this.core.$outer.find('.lg').width(); 377 378 if (_this.core.$outer.hasClass('lg-zoomed')) { 379 if ($(e.target).hasClass('lg-object') && (allowX || allowY)) { 380 e.preventDefault(); 381 startCoords = { 382 x: e.pageX, 383 y: e.pageY 384 }; 385 386 isDraging = true; 387 388 // ** Fix for webkit cursor issue https://code.google.com/p/chromium/issues/detail?id=26723 389 _this.core.$outer.scrollLeft += 1; 390 _this.core.$outer.scrollLeft -= 1; 391 392 _this.core.$outer.removeClass('lg-grab').addClass('lg-grabbing'); 393 } 394 } 395 }); 396 397 $(window).on('mousemove.lg.zoom', function(e) { 398 if (isDraging) { 399 var _$el = _this.core.$slide.eq(_this.core.index).find('.lg-img-wrap'); 400 var distanceX; 401 var distanceY; 402 403 isMoved = true; 404 endCoords = { 405 x: e.pageX, 406 y: e.pageY 407 }; 408 409 // reset opacity and transition duration 410 _this.core.$outer.addClass('lg-zoom-dragging'); 411 412 if (allowY) { 413 distanceY = (-Math.abs(_$el.attr('data-y'))) + (endCoords.y - startCoords.y); 414 } else { 415 distanceY = -Math.abs(_$el.attr('data-y')); 416 } 417 418 if (allowX) { 419 distanceX = (-Math.abs(_$el.attr('data-x'))) + (endCoords.x - startCoords.x); 420 } else { 421 distanceX = -Math.abs(_$el.attr('data-x')); 422 } 423 424 if (_this.core.s.useLeftForZoom) { 425 _$el.css({ 426 left: distanceX + 'px', 427 top: distanceY + 'px' 428 }); 429 } else { 430 _$el.css('transform', 'translate3d(' + distanceX + 'px, ' + distanceY + 'px, 0)'); 431 } 432 } 433 }); 434 435 $(window).on('mouseup.lg.zoom', function(e) { 436 437 if (isDraging) { 438 isDraging = false; 439 _this.core.$outer.removeClass('lg-zoom-dragging'); 440 441 // Fix for chrome mouse move on click 442 if (isMoved && ((startCoords.x !== endCoords.x) || (startCoords.y !== endCoords.y))) { 443 endCoords = { 444 x: e.pageX, 445 y: e.pageY 446 }; 447 _this.touchendZoom(startCoords, endCoords, allowX, allowY); 448 449 } 450 451 isMoved = false; 452 } 453 454 _this.core.$outer.removeClass('lg-grabbing').addClass('lg-grab'); 455 456 }); 457 }; 458 459 Zoom.prototype.touchendZoom = function(startCoords, endCoords, allowX, allowY) { 460 461 var _this = this; 462 var _$el = _this.core.$slide.eq(_this.core.index).find('.lg-img-wrap'); 463 var $image = _this.core.$slide.eq(_this.core.index).find('.lg-object'); 464 var distanceX = (-Math.abs(_$el.attr('data-x'))) + (endCoords.x - startCoords.x); 465 var distanceY = (-Math.abs(_$el.attr('data-y'))) + (endCoords.y - startCoords.y); 466 var minY = (_this.core.$outer.find('.lg').height() - $image.prop('offsetHeight')) / 2; 467 var maxY = Math.abs(($image.prop('offsetHeight') * Math.abs($image.attr('data-scale'))) - _this.core.$outer.find('.lg').height() + minY); 468 var minX = (_this.core.$outer.find('.lg').width() - $image.prop('offsetWidth')) / 2; 469 var maxX = Math.abs(($image.prop('offsetWidth') * Math.abs($image.attr('data-scale'))) - _this.core.$outer.find('.lg').width() + minX); 470 471 if ((Math.abs(endCoords.x - startCoords.x) > 15) || (Math.abs(endCoords.y - startCoords.y) > 15)) { 472 if (allowY) { 473 if (distanceY <= -maxY) { 474 distanceY = -maxY; 475 } else if (distanceY >= -minY) { 476 distanceY = -minY; 477 } 478 } 479 480 if (allowX) { 481 if (distanceX <= -maxX) { 482 distanceX = -maxX; 483 } else if (distanceX >= -minX) { 484 distanceX = -minX; 485 } 486 } 487 488 if (allowY) { 489 _$el.attr('data-y', Math.abs(distanceY)); 490 } else { 491 distanceY = -Math.abs(_$el.attr('data-y')); 492 } 493 494 if (allowX) { 495 _$el.attr('data-x', Math.abs(distanceX)); 496 } else { 497 distanceX = -Math.abs(_$el.attr('data-x')); 498 } 499 500 if (_this.core.s.useLeftForZoom) { 501 _$el.css({ 502 left: distanceX + 'px', 503 top: distanceY + 'px' 504 }); 505 } else { 506 _$el.css('transform', 'translate3d(' + distanceX + 'px, ' + distanceY + 'px, 0)'); 507 } 508 509 } 510 }; 511 512 Zoom.prototype.destroy = function() { 513 514 var _this = this; 515 516 // Unbind all events added by lightGallery zoom plugin 517 _this.core.$el.off('.lg.zoom'); 518 $(window).off('.lg.zoom'); 519 _this.core.$slide.off('.lg.zoom'); 520 _this.core.$el.off('.lg.tm.zoom'); 521 _this.resetZoom(); 522 clearTimeout(_this.zoomabletimeout); 523 _this.zoomabletimeout = false; 524 }; 525 526 $.fn.lightGallery.modules.zoom = Zoom; 527 528})(); 529 530 531})); 532