1/* 2 By André Rinas, www.andrerinas.de 3 Available for use under the MIT License 4 1.13.0 5*/ 6;( function( $, window, document, undefined ) 7{ 8 'use strict'; 9 10$.fn.simpleLightbox = function( options ) 11{ 12 13 var options = $.extend({ 14 sourceAttr: 'href', 15 overlay: true, 16 spinner: true, 17 nav: true, 18 navText: ['‹', '›'], 19 captions: true, 20 captionDelay: 0, 21 captionSelector: 'img', 22 captionType: 'attr', 23 captionsData: 'title', 24 captionPosition: 'bottom', 25 captionClass: '', 26 close: true, 27 closeText: 'Ã', 28 swipeClose: true, 29 showCounter: true, 30 fileExt: 'png|jpg|jpeg|gif', 31 animationSlide: true, 32 animationSpeed: 250, 33 preloading: true, 34 enableKeyboard: true, 35 loop: true, 36 rel: false, 37 docClose: true, 38 swipeTolerance: 50, 39 className: 'simple-lightbox', 40 widthRatio: 0.8, 41 heightRatio: 0.9, 42 scaleImageToRatio: false, 43 disableRightClick: false, 44 disableScroll: true, 45 alertError: true, 46 alertErrorMessage: 'Image not found, next image will be loaded', 47 additionalHtml: false, 48 history: true 49 }, options); 50 51 // global variables 52 var touchDevice = ( 'ontouchstart' in window ), 53 pointerEnabled = window.navigator.pointerEnabled || window.navigator.msPointerEnabled, 54 touched = function( event ){ 55 if( touchDevice ) return true; 56 if( !pointerEnabled || typeof event === 'undefined' || typeof event.pointerType === 'undefined' ) return false; 57 if( typeof event.MSPOINTER_TYPE_MOUSE !== 'undefined' ) { 58 if( event.MSPOINTER_TYPE_MOUSE != event.pointerType ) return true; 59 } 60 else { 61 if( event.pointerType != 'mouse' ) return true; 62 } 63 return false; 64 }, 65 swipeDiff = 0, 66 swipeYDiff = 0, 67 curImg = $(), 68 transPrefix = function(){ 69 var s = document.body || document.documentElement; 70 s = s.style; 71 if( s.WebkitTransition === '' ) return '-webkit-'; 72 if( s.MozTransition === '' ) return '-moz-'; 73 if( s.OTransition === '' ) return '-o-'; 74 if( s.transition === '' ) return ''; 75 return false; 76 }, 77 opened = false, 78 loaded = [], 79 getRelated = function(rel, jqObj) { 80 var $related = jqObj.filter(function () { 81 return ($(this).attr('rel') === rel); 82 }); 83 return $related; 84 }, 85 objects = (options.rel && options.rel !== false) ? getRelated(options.rel, $(this)) : this, 86 transPrefix = transPrefix(), 87 globalScrollbarwidth = 0, 88 canTransisions = (transPrefix !== false) ? true : false, 89 supportsPushState = ('pushState' in history), 90 historyhasChanged = false, 91 historyUpdateTimeout, 92 winLoc = window.location, 93 getHash = function(){ 94 return winLoc.hash.substring(1); 95 }, 96 initialHash = getHash(), 97 updateHash = function(){ 98 var hash = getHash(), 99 newHash = 'pid='+(index+1); 100 var newURL = winLoc.href.split('#')[0] + '#' + newHash; 101 102 if(supportsPushState){ 103 history[historyhasChanged ? 'replaceState' : 'pushState']('', document.title, newURL); 104 }else { 105 if(historyhasChanged) { 106 winLoc.replace( newURL ); 107 } else { 108 winLoc.hash = newHash; 109 } 110 } 111 historyhasChanged = true; 112 }, 113 resetHash = function() { 114 if (supportsPushState) { 115 history.pushState('', document.title, winLoc.pathname + winLoc.search ); 116 } else { 117 winLoc.hash = ''; 118 } 119 clearTimeout(historyUpdateTimeout); 120 121 }, 122 updateURL = function(){ 123 if(!historyhasChanged) { 124 updateHash(); // first time 125 } else { 126 historyUpdateTimeout = setTimeout(updateHash, 800); 127 } 128 }, 129 prefix = 'simplelb', 130 overlay = $('<div>').addClass('sl-overlay'), 131 closeBtn = $('<button>').addClass('sl-close').html(options.closeText),
132 spinner = $('<div>').addClass('sl-spinner').html('<div></div>'), 133 nav = $('<div>').addClass('sl-navigation').html('<button class="sl-prev">'+options.navText[0]+'</button><button class="sl-next">'+options.navText[1]+'</button>'), 134 counter = $('<div>').addClass('sl-counter').html('<span class="sl-current"></span>/<span class="sl-total"></span>'), 135 animating = false, 136 index = 0, 137 caption = $('<div>').addClass('sl-caption '+options.captionClass+' pos-'+options.captionPosition), 138 image = $('<div>').addClass('sl-image'), 139 wrapper = $('<div>').addClass('sl-wrapper').addClass(options.className), 140 isValidLink = function( element ){ 141 if(!options.fileExt) return true; 142 var filEext = /\.([0-9a-z]+)(?=[?#])|(\.)(?:[\w]+)$/gmi; 143 var testExt = $( element ).attr( options.sourceAttr ).match(filEext); 144 return testExt && $( element ).prop( 'tagName' ).toLowerCase() == 'a' && ( new RegExp( '\.(' + options.fileExt + ')$', 'i' ) ).test( testExt ); 145 }, 146 setup = function(){ 147 if(options.close) closeBtn.appendTo(wrapper); 148 if(options.showCounter){ 149 if(objects.length > 1){ 150 counter.appendTo(wrapper); 151 counter.find('.sl-total').text(objects.length); 152 } 153 } 154 if(options.nav) nav.appendTo(wrapper); 155 if(options.spinner) spinner.appendTo(wrapper); 156 }, 157 openImage = function(elem){ 158 elem.trigger($.Event('show.simplelightbox')); 159 if(options.disableScroll) globalScrollbarwidth = handleScrollbar('hide'); 160 wrapper.appendTo('body'); 161 image.appendTo(wrapper); 162 if(options.overlay) overlay.appendTo($('body')); 163 animating = true; 164 index = objects.index(elem); 165 curImg = $( '<img/>' ) 166 .hide() 167 .attr('src', elem.attr(options.sourceAttr)); 168 if(loaded.indexOf(elem.attr(options.sourceAttr)) == -1){ 169 loaded.push(elem.attr(options.sourceAttr)); 170 } 171 image.html('').attr('style',''); 172 curImg.appendTo(image); 173 addEvents(); 174 overlay.fadeIn('fast'); 175 $('.sl-close').fadeIn('fast'); 176 spinner.show(); 177 nav.fadeIn('fast'); 178 $('.sl-wrapper .sl-counter .sl-current').text(index +1); 179 counter.fadeIn('fast'); 180 adjustImage(); 181 if(options.preloading) preload(); 182 setTimeout( function(){ elem.trigger($.Event('shown.simplelightbox')); } ,options.animationSpeed); 183 }, 184 adjustImage = function(dir){ 185 if(!curImg.length) return; 186 var tmpImage = new Image(), 187 windowWidth = $( window ).width() * options.widthRatio, 188 windowHeight = $( window ).height() * options.heightRatio; 189 tmpImage.src = curImg.attr( 'src' ); 190 191 $(tmpImage).on('error',function(ev){ 192 //no image was found 193 objects.eq(index).trigger($.Event('error.simplelightbox')); 194 animating = false; 195 opened = true; 196 spinner.hide(); 197 if(options.alertError){ 198 alert(options.alertErrorMessage); 199 } 200 if(dir == 1 || dir == -1){ 201 loadImage(dir); 202 } else { 203 loadImage(1); 204 } 205 return; 206 }); 207 208 209 tmpImage.onload = function() { 210 if (typeof dir !== 'undefined') { 211 objects.eq(index) 212 .trigger($.Event('changed.simplelightbox')) 213 .trigger($.Event( (dir===1?'nextDone':'prevDone')+'.simplelightbox')); 214 } 215 216 // history 217 if(options.history){ 218 updateURL(); 219 } 220 221 if(loaded.indexOf(curImg.attr( 'src' )) == -1){ 222 loaded.push(curImg.attr( 'src' )); 223 } 224 var imageWidth = tmpImage.width, 225 imageHeight = tmpImage.height; 226 227 if( options.scaleImageToRatio || imageWidth > windowWidth || imageHeight > windowHeight ){ 228 var ratio = imageWidth / imageHeight > windowWidth / windowHeight ? imageWidth / windowWidth : imageHeight / windowHeight; 229 imageWidth /= ratio; 230 imageHeight /= ratio; 231 } 232 233 $('.sl-image').css({ 234 'top': ( $( window ).height() - imageHeight ) / 2 + 'px', 235 'left': ( $( window ).width() - imageWidth - globalScrollbarwidth)/ 2 + 'px' 236 }); 237 spinner.hide(); 238 curImg 239 .css({ 240 'width': imageWidth + 'px', 241 'height': imageHeight + 'px' 242 }) 243 .fadeIn('fast'); 244 opened = true; 245 var cSel = (options.captionSelector == 'self') ? objects.eq(index) : objects.eq(index).find(options.captionSelector); 246 var captionText; 247 if(options.captionType == 'data'){ 248 captionText = cSel.data(options.captionsData); 249 } else if(options.captionType == 'text'){ 250 captionText = cSel.html(); 251 } else { 252 captionText = cSel.prop(options.captionsData); 253 } 254 255 if(!options.loop) { 256 if(index === 0){ $('.sl-prev').hide();} 257 if(index >= objects.length -1) {$('.sl-next').hide();} 258 if(index > 0){ $('.sl-prev').show(); } 259 if(index < objects.length -1){ $('.sl-next').show(); } 260 } 261 262 if(objects.length == 1) $('.sl-prev, .sl-next').hide(); 263 264 if(dir == 1 || dir == -1){ 265 var css = { 'opacity': 1.0 }; 266 if( options.animationSlide ) { 267 if( canTransisions ) { 268 slide(0, 100 * dir + 'px'); 269 setTimeout( function(){ slide( options.animationSpeed / 1000, 0 + 'px'); }, 50 ); 270 } 271 else { 272 css.left = parseInt( $('.sl-image').css( 'left' ) ) + 100 * dir + 'px'; 273 } 274 } 275 276 $('.sl-image').animate( css, options.animationSpeed, function(){ 277 animating = false;
278 setCaption(captionText); 279 }); 280 } else { 281 animating = false; 282 setCaption(captionText); 283 } 284 if(options.additionalHtml && $('.sl-additional-html').length === 0){ 285 $('<div>').html(options.additionalHtml).addClass('sl-additional-html').appendTo($('.sl-image')); 286 } 287 }; 288 }, 289 setCaption = function(captiontext){ 290 if(captiontext !== '' && typeof captiontext !== "undefined" && options.captions){ 291 caption.html(captiontext).hide().appendTo($('.sl-image')).delay(options.captionDelay).fadeIn('fast'); 292 } 293 }, 294 slide = function(speed, pos){ 295 var styles = {}; 296 styles[transPrefix + 'transform'] = 'translateX(' + pos + ')'; 297 styles[transPrefix + 'transition'] = transPrefix + 'transform ' + speed + 's linear'; 298 $('.sl-image').css(styles); 299 }, 300 addEvents = function(){ 301 // resize/responsive 302 $( window ).on( 'resize.'+prefix, adjustImage ); 303 304 // close lightbox on close btn 305 $( document ).on('click.'+prefix+ ' touchstart.'+prefix, '.sl-close', function(e){ 306 e.preventDefault(); 307 if(opened){ close();} 308 }); 309 310 if(options.history){ 311 setTimeout(function() { 312 $(window).on('hashchange.'+prefix,function(){ 313 if(opened){ 314 if(getHash() === initialHash) { 315 close(); 316 return; 317 } 318 } 319 }); 320 }, 40); 321 } 322 323 // nav-buttons 324 nav.on('click.'+prefix, 'button', function(e){ 325 e.preventDefault(); 326 swipeDiff = 0; 327 loadImage( $(this).hasClass('sl-next') ? 1 : -1 ); 328 }); 329 330 // touchcontrols 331 var swipeStart = 0, 332 swipeEnd = 0, 333 swipeYStart = 0, 334 swipeYEnd = 0, 335 mousedown = false, 336 imageLeft = 0; 337 338 image 339 .on( 'touchstart.'+prefix+' mousedown.'+prefix, function(e) 340 { 341 if(mousedown) return true; 342 if( canTransisions ) imageLeft = parseInt( image.css( 'left' ) ); 343 mousedown = true; 344 swipeDiff = 0; 345 swipeYDiff = 0; 346 swipeStart = e.originalEvent.pageX || e.originalEvent.touches[ 0 ].pageX; 347 swipeYStart = e.originalEvent.pageY || e.originalEvent.touches[ 0 ].pageY; 348 return false; 349 }) 350 .on( 'touchmove.'+prefix+' mousemove.'+prefix+' pointermove MSPointerMove', function(e) 351 { 352 if(!mousedown) return true; 353 e.preventDefault(); 354 swipeEnd = e.originalEvent.pageX || e.originalEvent.touches[ 0 ].pageX; 355 swipeYEnd = e.originalEvent.pageY || e.originalEvent.touches[ 0 ].pageY; 356 swipeDiff = swipeStart - swipeEnd; 357 swipeYDiff = swipeYStart - swipeYEnd; 358 if( options.animationSlide ) { 359 if( canTransisions ) slide( 0, -swipeDiff + 'px' ); 360 else image.css( 'left', imageLeft - swipeDiff + 'px' ); 361 } 362 }) 363 .on( 'touchend.'+prefix+' mouseup.'+prefix+' touchcancel.'+prefix+' mouseleave.'+prefix+' pointerup pointercancel MSPointerUp MSPointerCancel',function(e) 364 { 365 if(mousedown){ 366 mousedown = false; 367 var possibleDir = true; 368 if(!options.loop) { 369 if(index === 0 && swipeDiff < 0){ possibleDir = false; } 370 if(index >= objects.length -1 && swipeDiff > 0) { possibleDir = false; } 371 } 372 if( Math.abs( swipeDiff ) > options.swipeTolerance && possibleDir ) { 373 loadImage( swipeDiff > 0 ? 1 : -1 ); 374 } 375 else if( options.animationSlide ) 376 { 377 if( canTransisions ) slide( options.animationSpeed / 1000, 0 + 'px' ); 378 else image.animate({ 'left': imageLeft + 'px' }, options.animationSpeed / 2 ); 379 } 380 381 if( options.swipeClose && Math.abs(swipeYDiff) > 50 && Math.abs( swipeDiff ) < options.swipeTolerance) { 382 close(); 383 } 384 } 385 }); 386 }, 387 removeEvents = function(){ 388 nav.off('click', 'button'); 389 $( document ).off('click.'+prefix, '.sl-close'); 390 $( window ).off( 'resize.'+prefix); 391 $( window ).off( 'hashchange.'+prefix); 392 }, 393 preload = function(){ 394 var next = (index+1 < 0) ? objects.length -1: (index+1 >= objects.length -1) ? 0 : index+1, 395 prev = (index-1 < 0) ? objects.length -1: (index-1 >= objects.length -1) ? 0 : index-1; 396 $( '<img />' ).attr( 'src', objects.eq(next).attr( options.sourceAttr ) ).on('load', function(){ 397 if(loaded.indexOf($(this).attr('src')) == -1){ 398 loaded.push($(this).attr('src')); 399 } 400 objects.eq(index).trigger($.Event('nextImageLoaded.simplelightbox')); 401 }); 402 $( '<img />' ).attr( 'src', objects.eq(prev).attr( options.sourceAttr ) ).on('load', function(){ 403 if(loaded.indexOf($(this).attr('src')) == -1){ 404 loaded.push($(this).attr('src')); 405 } 406 objects.eq(index).trigger($.Event('prevImageLoaded.simplelightbox')); 407 }); 408 409 }, 410 loadImage = function(dir){ 411 objects.eq(index) 412 .trigger($.Event('change.simplelightbox')) 413 .trigger($.Event( (dir===1?'next':'prev')+'.simplelightbox')); 414 415 var newIndex = index + dir; 416 if(animating || (newIndex < 0 || newIndex >= objects.length) && options.loop === false ) return; 417 index = (newIndex < 0) ? objects.length -1: (newIndex > objects.length -1) ? 0 : newIndex; 418 $('.sl-wrapper .sl-counter .sl-current').text(index +1); 419 var css = { 'opacity': 0 }; 420 if( options.animationSlide ) { 421 if( canTransisions ) slide(options.animationSpeed / 1000, ( -100 * dir ) - swipeDiff + 'px'); 422 else css.left = parseInt( $('.sl-image').css( 'left' ) ) + -100 * dir + 'px'; 423 } 424 425 $('.sl-image').animate( css, options.animationSpeed, function(){ 426 setTimeout( function(){ 427 // fadeout old image 428 var elem = objects.eq(index); 429 curImg 430 .attr('src', elem.attr(options.sourceAttr)); 431 if(loaded.indexOf(elem.attr(options.sourceAttr)) == -1){ 432 spinner.show(); 433 } 434 $('.sl-caption').remove(); 435 adjustImage(dir); 436 if(options.preloading) preload(); 437 }, 100); 438 }); 439 }, 440 close = function(){ 441 if(animating) return; 442 var elem = objects.eq(index), 443 triggered = false;
444 445 elem.trigger($.Event('close.simplelightbox')); 446 if(options.history){ 447 resetHash(); 448 } 449 $('.sl-image img, .sl-overlay, .sl-close, .sl-navigation, .sl-image .sl-caption, .sl-counter').fadeOut('fast', function(){ 450 if(options.disableScroll) handleScrollbar('show'); 451 $('.sl-wrapper, .sl-overlay').remove(); 452 removeEvents(); 453 if(!triggered) elem.trigger($.Event('closed.simplelightbox')); 454 triggered = true; 455 }); 456 curImg = $(); 457 opened = false; 458 animating = false; 459 }, 460 handleScrollbar = function(type){ 461 var scrollbarWidth = 0; 462 if(type == 'hide'){ 463 var fullWindowWidth = window.innerWidth; 464 if (!fullWindowWidth) { 465 var documentElementRect = document.documentElement.getBoundingClientRect(); 466 fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left); 467 } 468 if(document.body.clientWidth < fullWindowWidth){ 469 var scrollDiv = document.createElement('div'), 470 padding = parseInt($('body').css('padding-right'),10); 471 scrollDiv.className = 'sl-scrollbar-measure'; 472 $('body').append(scrollDiv); 473 scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth; 474 $(document.body)[0].removeChild(scrollDiv); 475 $('body').data('padding',padding); 476 if(scrollbarWidth > 0){ 477 $('body').addClass('hidden-scroll').css({'padding-right':padding+scrollbarWidth}); 478 } 479 } 480 } else { 481 $('body').removeClass('hidden-scroll').css({'padding-right':$('body').data('padding')}); 482 } 483 return scrollbarWidth; 484 }; 485 486 // events 487 setup(); 488 489 // open lightbox 490 objects.on( 'click.'+prefix, function( e ){ 491 if(isValidLink(this)){ 492 e.preventDefault(); 493 if(animating) return false; 494 openImage($(this)); 495 } 496 }); 497 498 // close on click on doc 499 $( document ).on('click.'+prefix+ ' touchstart.'+prefix, function(e){ 500 if(opened){ 501 if((options.docClose && $(e.target).closest('.sl-image').length === 0 && $(e.target).closest('.sl-navigation').length === 0)){ 502 close(); 503 } 504 } 505 }); 506 507 // disable rightclick 508 if(options.disableRightClick){ 509 $( document ).on('contextmenu', '.sl-image img', function(e){ 510 return false; 511 }); 512 } 513 514 515 // keyboard-control 516 if( options.enableKeyboard ){ 517 $( document ).on( 'keyup.'+prefix, function( e ){ 518 swipeDiff = 0; 519 // keyboard control only if lightbox is open 520 if(opened){ 521 e.preventDefault(); 522 var key = e.keyCode; 523 if( key == 27 ) { 524 close(); 525 } 526 if( key == 37 || e.keyCode == 39 ) { 527 loadImage( e.keyCode == 39 ? 1 : -1 ); 528 } 529 } 530 }); 531 } 532 533 // Public methods 534 this.open = function(elem){ 535 elem = elem || $(this[0]); 536 openImage(elem); 537 }; 538 539 this.next = function(){ 540 loadImage( 1 ); 541 }; 542 543 this.prev = function(){ 544 loadImage( -1 ); 545 }; 546 547 this.close = function(){ 548 close(); 549 }; 550 551 this.destroy = function(){ 552 $( document ).off('click.'+prefix).off('keyup.'+prefix); 553 close(); 554 $('.sl-overlay, .sl-wrapper').remove(); 555 this.off('click'); 556 }; 557 558 this.refresh = function(){ 559 this.destroy(); 560 $(this).simpleLightbox(options); 561 }; 562 563 return this; 564 565}; 566})( jQuery, window, document );
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.