1/** 2 * Galleria v 1.2.9 2013-02-14 3 * http://galleria.io 4 * 5 * Licensed under the MIT license 6 * https://raw.github.com/aino/galleria/master/LICENSE 7 * 8 */ 9 10(function( $ ) { 11 12/*global jQuery, navigator, Galleria:true, Image */ 13 14// some references 15var undef, 16 window = this, 17 doc = window.document, 18 $doc = $( doc ), 19 $win = $( window ), 20 21// native prototypes 22 protoArray = Array.prototype, 23 24// internal constants 25 VERSION = 1.29, 26 DEBUG = true, 27 TIMEOUT = 30000, 28 DUMMY = false, 29 NAV = navigator.userAgent.toLowerCase(), 30 HASH = window.location.hash.replace(/#\//, ''), 31 F = function(){}, 32 FALSE = function() { return false; }, 33 IE = (function() { 34 35 var v = 3, 36 div = doc.createElement( 'div' ), 37 all = div.getElementsByTagName( 'i' ); 38 39 do { 40 div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->'; 41 } while ( all[0] ); 42 43 return v > 4 ? v : undef; 44 45 }() ), 46 DOM = function() { 47 return { 48 html: doc.documentElement, 49 body: doc.body, 50 head: doc.getElementsByTagName('head')[0], 51 title: doc.title 52 }; 53 }, 54 IFRAME = window.parent !== window.self, 55 56 // list of Galleria events 57 _eventlist = 'data ready thumbnail loadstart loadfinish image play pause progress ' + 58 'fullscreen_enter fullscreen_exit idle_enter idle_exit rescale ' + 59 'lightbox_open lightbox_close lightbox_image', 60 61 _events = (function() { 62 63 var evs = []; 64 65 $.each( _eventlist.split(' '), function( i, ev ) { 66 evs.push( ev ); 67 68 // legacy events 69 if ( /_/.test( ev ) ) { 70 evs.push( ev.replace( /_/g, '' ) ); 71 } 72 }); 73 74 return evs; 75 76 }()), 77 78 // legacy options 79 // allows the old my_setting syntax and converts it to camel case 80 81 _legacyOptions = function( options ) { 82 83 var n; 84 85 if ( typeof options !== 'object' ) { 86 87 // return whatever it was... 88 return options; 89 } 90 91 $.each( options, function( key, value ) { 92 if ( /^[a-z]+_/.test( key ) ) { 93 n = ''; 94 $.each( key.split('_'), function( i, k ) { 95 n += i > 0 ? k.substr( 0, 1 ).toUpperCase() + k.substr( 1 ) : k; 96 }); 97 options[ n ] = value; 98 delete options[ key ]; 99 } 100 }); 101 102 return options; 103 }, 104 105 _patchEvent = function( type ) { 106 107 // allow 'image' instead of Galleria.IMAGE 108 if ( $.inArray( type, _events ) > -1 ) { 109 return Galleria[ type.toUpperCase() ]; 110 } 111 112 return type; 113 }, 114 115 // video providers 116 _video = { 117 youtube: { 118 reg: /https?:\/\/(?:[a-zA_Z]{2,3}.)?(?:youtube\.com\/watch\?)((?:[\w\d\-\_\=]+&(?:amp;)?)*v(?:<[A-Z]+>)?=([0-9a-zA-Z\-\_]+))/i, 119 embed: function(id) { 120 return 'http://www.youtube.com/embed/'+id; 121 }, 122 getThumb: function( id, success, fail ) { 123 fail = fail || F; 124 $.getJSON(window.location.protocol+'//gdata.youtube.com/feeds/api/videos/' + id + '?v=2&alt=json-in-script&callback=?', function(data) { 125 try { 126 success( data.entry.media$group.media$thumbnail[0].url ); 127 } catch(e) { 128 fail(); 129 } 130 }).error(fail); 131 } 132 }, 133 vimeo: { 134 reg: /https?:\/\/(?:www\.)?(vimeo\.com)\/(?:hd#)?([0-9]+)/i, 135 embed: function(id) { 136 return 'http://player.vimeo.com/video/'+id; 137 }, 138 getThumb: function( id, success, fail ) { 139 fail = fail || F; 140 $.getJSON('http://vimeo.com/api/v2/video/' + id + '.json?callback=?', function(data) { 141 try { 142 success( data[0].thumbnail_medium ); 143 } catch(e) { 144 fail(); 145 } 146 }).error(fail); 147 } 148 }, 149 dailymotion: { 150 reg: /https?:\/\/(?:www\.)?(dailymotion\.com)\/video\/([^_]+)/, 151 embed: function(id) { 152 return 'http://www.dailymotion.com/embed/video/'+id; 153 }, 154 getThumb: function( id, success, fail ) { 155 fail = fail || F; 156 $.getJSON('https://api.dailymotion.com/video/'+id+'?fields=thumbnail_medium_url&callback=?', function(data) { 157 try { 158 success( data.thumbnail_medium_url ); 159 } catch(e) { 160 fail(); 161 } 162 }).error(fail); 163 } 164 } 165 }, 166 167 // utility for testing the video URL and getting the video ID 168 _videoTest = function( url ) { 169 var match; 170 for ( var v in _video ) { 171 match = url && url.match( _video[v].reg ); 172 if( match && match.length ) { 173 return { 174 id: match[2], 175 provider: v 176 }; 177 } 178 } 179 return false;
vendor: 8,885 bytes, lines 180-453
180 }, 181 182 // native fullscreen handler 183 _nativeFullscreen = { 184 185 support: (function() { 186 var html = DOM().html; 187 return !IFRAME && ( html.requestFullscreen || html.mozRequestFullScreen || html.webkitRequestFullScreen ); 188 }()), 189 190 callback: F, 191 192 enter: function( instance, callback, elem ) { 193 194 this.instance = instance; 195 196 this.callback = callback || F; 197 198 elem = elem || DOM().html; 199 if ( elem.requestFullscreen ) { 200 elem.requestFullscreen(); 201 } 202 else if ( elem.mozRequestFullScreen ) { 203 elem.mozRequestFullScreen(); 204 } 205 else if ( elem.webkitRequestFullScreen ) { 206 elem.webkitRequestFullScreen(); 207 } 208 }, 209 210 exit: function( callback ) { 211 212 this.callback = callback || F; 213 214 if ( doc.exitFullscreen ) { 215 doc.exitFullscreen(); 216 } 217 else if ( doc.mozCancelFullScreen ) { 218 doc.mozCancelFullScreen(); 219 } 220 else if ( doc.webkitCancelFullScreen ) { 221 doc.webkitCancelFullScreen(); 222 } 223 }, 224 225 instance: null, 226 227 listen: function() { 228 229 if ( !this.support ) { 230 return; 231 } 232 233 var handler = function() { 234 235 if ( !_nativeFullscreen.instance ) { 236 return; 237 } 238 var fs = _nativeFullscreen.instance._fullscreen; 239 240 if ( doc.fullscreen || doc.mozFullScreen || doc.webkitIsFullScreen ) { 241 fs._enter( _nativeFullscreen.callback ); 242 } else { 243 fs._exit( _nativeFullscreen.callback ); 244 } 245 }; 246 doc.addEventListener( 'fullscreenchange', handler, false ); 247 doc.addEventListener( 'mozfullscreenchange', handler, false ); 248 doc.addEventListener( 'webkitfullscreenchange', handler, false ); 249 } 250 }, 251 252 // the internal gallery holder 253 _galleries = [], 254 255 // the internal instance holder 256 _instances = [], 257 258 // flag for errors 259 _hasError = false, 260 261 // canvas holder 262 _canvas = false, 263 264 // instance pool, holds the galleries until themeLoad is triggered 265 _pool = [], 266 267 // themeLoad trigger 268 _themeLoad = function( theme ) { 269 270 Galleria.theme = theme; 271 272 // run the instances we have in the pool 273 $.each( _pool, function( i, instance ) { 274 if ( !instance._initialized ) { 275 instance._init.call( instance ); 276 } 277 }); 278 279 _pool = []; 280 }, 281 282 // the Utils singleton 283 Utils = (function() { 284 285 return { 286 287 // legacy support for clearTimer 288 clearTimer: function( id ) { 289 $.each( Galleria.get(), function() { 290 this.clearTimer( id ); 291 }); 292 }, 293 294 // legacy support for addTimer 295 addTimer: function( id ) { 296 $.each( Galleria.get(), function() { 297 this.addTimer( id ); 298 }); 299 }, 300 301 array : function( obj ) { 302 return protoArray.slice.call(obj, 0); 303 }, 304 305 create : function( className, nodeName ) { 306 nodeName = nodeName || 'div'; 307 var elem = doc.createElement( nodeName ); 308 elem.className = className; 309 return elem; 310 }, 311 312 removeFromArray : function( arr, elem ) { 313 $.each(arr, function(i, el) { 314 if ( el == elem ) { 315 arr.splice(i, 1); 316 return false; 317 } 318 }); 319 return arr; 320 }, 321 322 getScriptPath : function( src ) { 323 324 // the currently executing script is always the last 325 src = src || $('script:last').attr('src'); 326 var slices = src.split('/'); 327 328 if (slices.length == 1) { 329 return ''; 330 } 331 332 slices.pop(); 333 334 return slices.join('/') + '/'; 335 }, 336 337 // CSS3 transitions, added in 1.2.4 338 animate : (function() { 339 340 // detect transition 341 var transition = (function( style ) { 342 var props = 'transition WebkitTransition MozTransition OTransition'.split(' '), 343 i; 344 345 // disable css3 animations in opera until stable 346 if ( window.opera ) { 347 return false; 348 } 349 350 for ( i = 0; props[i]; i++ ) { 351 if ( typeof style[ props[ i ] ] !== 'undefined' ) { 352 return props[ i ]; 353 } 354 } 355 return false; 356 }(( doc.body || doc.documentElement).style )); 357 358 // map transitionend event 359 var endEvent = { 360 MozTransition: 'transitionend', 361 OTransition: 'oTransitionEnd', 362 WebkitTransition: 'webkitTransitionEnd', 363 transition: 'transitionend' 364 }[ transition ]; 365 366 // map bezier easing conversions 367 var easings = { 368 _default: [0.25, 0.1, 0.25, 1], 369 galleria: [0.645, 0.045, 0.355, 1], 370 galleriaIn: [0.55, 0.085, 0.68, 0.53], 371 galleriaOut: [0.25, 0.46, 0.45, 0.94], 372 ease: [0.25, 0, 0.25, 1], 373 linear: [0.25, 0.25, 0.75, 0.75], 374 'ease-in': [0.42, 0, 1, 1], 375 'ease-out': [0, 0, 0.58, 1], 376 'ease-in-out': [0.42, 0, 0.58, 1] 377 }; 378 379 // function for setting transition css for all browsers 380 var setStyle = function( elem, value, suffix ) { 381 var css = {}; 382 suffix = suffix || 'transition'; 383 $.each( 'webkit moz ms o'.split(' '), function() { 384 css[ '-' + this + '-' + suffix ] = value; 385 }); 386 elem.css( css ); 387 }; 388 389 // clear styles 390 var clearStyle = function( elem ) { 391 setStyle( elem, 'none', 'transition' ); 392 if ( Galleria.WEBKIT && Galleria.TOUCH ) { 393 setStyle( elem, 'translate3d(0,0,0)', 'transform' ); 394 if ( elem.data('revert') ) { 395 elem.css( elem.data('revert') ); 396 elem.data('revert', null); 397 } 398 } 399 }; 400 401 // various variables 402 var change, strings, easing, syntax, revert, form, css; 403 404 // the actual animation method 405 return function( elem, to, options ) { 406 407 // extend defaults 408 options = $.extend({ 409 duration: 400, 410 complete: F, 411 stop: false 412 }, options); 413 414 // cache jQuery instance 415 elem = $( elem ); 416 417 if ( !options.duration ) { 418 elem.css( to ); 419 options.complete.call( elem[0] ); 420 return; 421 } 422 423 // fallback to jQuery's animate if transition is not supported 424 if ( !transition ) { 425 elem.animate(to, options); 426 return; 427 } 428 429 // stop 430 if ( options.stop ) { 431 // clear the animation 432 elem.unbind( endEvent ); 433 clearStyle( elem ); 434 } 435 436 // see if there is a change 437 change = false; 438 $.each( to, function( key, val ) { 439 css = elem.css( key ); 440 if ( Utils.parseValue( css ) != Utils.parseValue( val ) ) { 441 change = true; 442 } 443 // also add computed styles for FF 444 elem.css( key, css ); 445 }); 446 if ( !change ) { 447 window.setTimeout( function() { 448 options.complete.call( elem[0] ); 449 }, options.duration ); 450 return; 451 } 452 453 // the css strings to be applied
454 strings = []; 455 456 // the easing bezier 457 easing = options.easing in easings ? easings[ options.easing ] : easings._default; 458 459 // the syntax 460 syntax = ' ' + options.duration + 'ms' + ' cubic-bezier(' + easing.join(',') + ')'; 461 462 // add a tiny timeout so that the browsers catches any css changes before animating 463 window.setTimeout( (function(elem, endEvent, to, syntax) { 464 return function() { 465 466 // attach the end event 467 elem.one(endEvent, (function( elem ) { 468 return function() { 469 470 // clear the animation 471 clearStyle(elem); 472 473 // run the complete method 474 options.complete.call(elem[0]); 475 }; 476 }( elem ))); 477 478 // do the webkit translate3d for better performance on iOS 479 if( Galleria.WEBKIT && Galleria.TOUCH ) { 480 481 revert = {}; 482 form = [0,0,0]; 483 484 $.each( ['left', 'top'], function(i, m) { 485 if ( m in to ) { 486 form[ i ] = ( Utils.parseValue( to[ m ] ) - Utils.parseValue(elem.css( m )) ) + 'px'; 487 revert[ m ] = to[ m ]; 488 delete to[ m ]; 489 } 490 }); 491 492 if ( form[0] || form[1]) { 493 494 elem.data('revert', revert); 495 496 strings.push('-webkit-transform' + syntax); 497 498 // 3d animate 499 setStyle( elem, 'translate3d(' + form.join(',') + ')', 'transform'); 500 } 501 } 502 503 // push the animation props 504 $.each(to, function( p, val ) { 505 strings.push(p + syntax); 506 }); 507 508 // set the animation styles 509 setStyle( elem, strings.join(',') ); 510 511 // animate 512 elem.css( to ); 513 514 }; 515 }(elem, endEvent, to, syntax)), 2); 516 }; 517 }()), 518 519 removeAlpha : function( elem ) { 520 if ( IE < 9 && elem ) { 521 522 var style = elem.style, 523 currentStyle = elem.currentStyle, 524 filter = currentStyle && currentStyle.filter || style.filter || ""; 525 526 if ( /alpha/.test( filter ) ) { 527 style.filter = filter.replace( /alpha\([^)]*\)/i, '' ); 528 } 529 } 530 }, 531 532 forceStyles : function( elem, styles ) { 533 elem = $(elem); 534 if ( elem.attr( 'style' ) ) { 535 elem.data( 'styles', elem.attr( 'style' ) ).removeAttr( 'style' ); 536 } 537 elem.css( styles ); 538 }, 539 540 revertStyles : function() { 541 $.each( Utils.array( arguments ), function( i, elem ) { 542 543 elem = $( elem ); 544 elem.removeAttr( 'style' ); 545 546 elem.attr('style',''); // "fixes" webkit bug 547 548 if ( elem.data( 'styles' ) ) { 549 elem.attr( 'style', elem.data('styles') ).data( 'styles', null ); 550 } 551 }); 552 }, 553 554 moveOut : function( elem ) { 555 Utils.forceStyles( elem, { 556 position: 'absolute', 557 left: -10000 558 }); 559 }, 560 561 moveIn : function() { 562 Utils.revertStyles.apply( Utils, Utils.array( arguments ) ); 563 }, 564 565 elem : function( elem ) { 566 if (elem instanceof $) { 567 return { 568 $: elem, 569 dom: elem[0] 570 }; 571 } else { 572 return { 573 $: $(elem), 574 dom: elem 575 }; 576 } 577 }, 578 579 hide : function( elem, speed, callback ) { 580 581 callback = callback || F; 582 583 var el = Utils.elem( elem ), 584 $elem = el.$; 585 586 elem = el.dom; 587 588 // save the value if not exist 589 if (! $elem.data('opacity') ) { 590 $elem.data('opacity', $elem.css('opacity') ); 591 } 592 593 // always hide 594 var style = { opacity: 0 }; 595 596 if (speed) { 597 598 var complete = IE < 9 && elem ? function() { 599 Utils.removeAlpha( elem ); 600 elem.style.visibility = 'hidden'; 601 callback.call( elem ); 602 } : callback; 603 604 Utils.animate( elem, style, { 605 duration: speed, 606 complete: complete, 607 stop: true 608 }); 609 } else { 610 if ( IE < 9 && elem ) { 611 Utils.removeAlpha( elem ); 612 elem.style.visibility = 'hidden'; 613 } else { 614 $elem.css( style ); 615 } 616 } 617 }, 618 619 show : function( elem, speed, callback ) { 620 621 callback = callback || F; 622 623 var el = Utils.elem( elem ), 624 $elem = el.$; 625 626 elem = el.dom; 627 628 // bring back saved opacity 629 var saved = parseFloat( $elem.data('opacity') ) || 1, 630 style = { opacity: saved }; 631 632 // animate or toggle 633 if (speed) { 634 635 if ( IE < 9 ) { 636 $elem.css('opacity', 0); 637 elem.style.visibility = 'visible'; 638 } 639 640 var complete = IE < 9 && elem ? function() { 641 if ( style.opacity == 1 ) { 642 Utils.removeAlpha( elem ); 643 } 644 callback.call( elem ); 645 } : callback; 646 647 Utils.animate( elem, style, { 648 duration: speed, 649 complete: complete, 650 stop: true 651 }); 652 } else { 653 if ( IE < 9 && style.opacity == 1 && elem ) { 654 Utils.removeAlpha( elem ); 655 elem.style.visibility = 'visible'; 656 } else { 657 $elem.css( style ); 658 } 659 } 660 }, 661 662 663 // enhanced click for mobile devices 664 // we bind a touchend and hijack any click event in the bubble 665 // then we execute the click directly and save it in a separate data object for later 666 optimizeTouch: (function() { 667 668 var node, 669 evs, 670 fakes, 671 travel, 672 evt = {}, 673 handler = function( e ) { 674 e.preventDefault(); 675 evt = $.extend({}, e, true); 676 }, 677 attach = function() { 678 this.evt = evt; 679 }, 680 fake = function() { 681 this.handler.call(node, this.evt); 682 }; 683 684 return function( elem ) { 685 686 $(elem).bind('touchend', function( e ) { 687 688 node = e.target; 689 travel = true; 690 691 while( node.parentNode && node != e.currentTarget && travel ) { 692 693 evs = $(node).data('events'); 694 fakes = $(node).data('fakes'); 695 696 if (evs && 'click' in evs) { 697 698 travel = false;
699 e.preventDefault(); 700 701 // fake the click and save the event object 702 $(node).click(handler).click(); 703 704 // remove the faked click 705 evs.click.pop(); 706 707 // attach the faked event 708 $.each( evs.click, attach); 709 710 // save the faked clicks in a new data object 711 $(node).data('fakes', evs.click); 712 713 // remove all clicks 714 delete evs.click; 715 716 } else if ( fakes ) { 717 718 travel = false; 719 e.preventDefault(); 720 721 // fake all clicks 722 $.each( fakes, fake ); 723 } 724 725 // bubble 726 node = node.parentNode; 727 } 728 }); 729 }; 730 }()), 731 732 wait : function(options) { 733 options = $.extend({ 734 until : FALSE, 735 success : F, 736 error : function() { Galleria.raise('Could not complete wait function.'); }, 737 timeout: 3000 738 }, options); 739 740 var start = Utils.timestamp(), 741 elapsed, 742 now, 743 fn = function() { 744 now = Utils.timestamp(); 745 elapsed = now - start; 746 if ( options.until( elapsed ) ) { 747 options.success(); 748 return false; 749 } 750 751 if (typeof options.timeout == 'number' && now >= start + options.timeout) { 752 options.error(); 753 return false; 754 } 755 window.setTimeout(fn, 10); 756 }; 757 758 window.setTimeout(fn, 10); 759 }, 760 761 toggleQuality : function( img, force ) { 762 763 if ( ( IE !== 7 && IE !== 8 ) || !img || img.nodeName.toUpperCase() != 'IMG' ) { 764 return; 765 } 766 767 if ( typeof force === 'undefined' ) { 768 force = img.style.msInterpolationMode === 'nearest-neighbor'; 769 } 770 771 img.style.msInterpolationMode = force ? 'bicubic' : 'nearest-neighbor'; 772 }, 773 774 insertStyleTag : function( styles, id ) { 775 776 if ( id && $( '#'+id ).length ) { 777 return; 778 } 779 780 var style = doc.createElement( 'style' ); 781 if ( id ) { 782 style.id = id; 783 } 784 785 DOM().head.appendChild( style ); 786 787 if ( style.styleSheet ) { // IE 788 style.styleSheet.cssText = styles; 789 } else { 790 var cssText = doc.createTextNode( styles ); 791 style.appendChild( cssText ); 792 } 793 }, 794 795 // a loadscript method that works for local scripts 796 loadScript: function( url, callback ) { 797 798 var done = false, 799 script = $('<scr'+'ipt>').attr({ 800 src: url, 801 async: true 802 }).get(0); 803 804 // Attach handlers for all browsers 805 script.onload = script.onreadystatechange = function() { 806 if ( !done && (!this.readyState || 807 this.readyState === 'loaded' || this.readyState === 'complete') ) { 808 809 done = true; 810 811 // Handle memory leak in IE 812 script.onload = script.onreadystatechange = null; 813 814 if (typeof callback === 'function') { 815 callback.call( this, this ); 816 } 817 } 818 }; 819 820 DOM().head.appendChild( script ); 821 }, 822
vendor: 45,474 bytes, lines 823-2249
823 // parse anything into a number 824 parseValue: function( val ) { 825 if (typeof val === 'number') { 826 return val; 827 } else if (typeof val === 'string') { 828 var arr = val.match(/\-?\d|\./g); 829 return arr && arr.constructor === Array ? arr.join('')*1 : 0; 830 } else { 831 return 0; 832 } 833 }, 834 835 // timestamp abstraction 836 timestamp: function() { 837 return new Date().getTime(); 838 }, 839 840 loadCSS : function( href, id, callback ) { 841 842 var link, 843 length; 844 845 // look for manual css 846 $('link[rel=stylesheet]').each(function() { 847 if ( new RegExp( href ).test( this.href ) ) { 848 link = this; 849 return false; 850 } 851 }); 852 853 if ( typeof id === 'function' ) { 854 callback = id; 855 id = undef; 856 } 857 858 callback = callback || F; // dirty 859 860 // if already present, return 861 if ( link ) { 862 callback.call( link, link ); 863 return link; 864 } 865 866 // save the length of stylesheets to check against 867 length = doc.styleSheets.length; 868 869 // check for existing id 870 if( $( '#' + id ).length ) { 871 872 $( '#' + id ).attr( 'href', href ); 873 length--; 874 875 } else { 876 link = $( '<link>' ).attr({ 877 rel: 'stylesheet', 878 href: href, 879 id: id 880 }).get(0); 881 882 var styles = $('link[rel="stylesheet"], style'); 883 if ( styles.length ) { 884 styles.get(0).parentNode.insertBefore( link, styles[0] ); 885 } else { 886 DOM().head.appendChild( link ); 887 } 888 889 if ( IE ) { 890 891 // IE has a limit of 31 stylesheets in one document 892 if( length >= 31 ) { 893 Galleria.raise( 'You have reached the browser stylesheet limit (31)', true ); 894 return; 895 } 896 } 897 } 898 899 if ( typeof callback === 'function' ) { 900 901 // First check for dummy element (new in 1.2.8) 902 var $loader = $('<s>').attr( 'id', 'galleria-loader' ).hide().appendTo( DOM().body ); 903 904 Utils.wait({ 905 until: function() { 906 return $loader.height() == 1; 907 }, 908 success: function() { 909 $loader.remove(); 910 callback.call( link, link ); 911 }, 912 error: function() { 913 $loader.remove(); 914 915 // If failed, tell the dev to download the latest theme 916 Galleria.raise( 'Theme CSS could not load after 20 sec. ' + ( Galleria.QUIRK ? 917 'Your browser is in Quirks Mode, please add a correct doctype.' : 918 'Please download the latest theme at http://galleria.io/customer/.' ), true ); 919 }, 920 timeout: 5000 921 }); 922 } 923 return link; 924 } 925 }; 926 }()), 927 928 // the transitions holder 929 _transitions = (function() { 930 931 var _slide = function(params, complete, fade, door) { 932 933 var easing = this.getOptions('easing'), 934 distance = this.getStageWidth(), 935 from = { left: distance * ( params.rewind ? -1 : 1 ) }, 936 to = { left: 0 }; 937 938 if ( fade ) { 939 from.opacity = 0; 940 to.opacity = 1; 941 } else { 942 from.opacity = 1; 943 } 944 945 $(params.next).css(from); 946 947 Utils.animate(params.next, to, { 948 duration: params.speed, 949 complete: (function( elems ) { 950 return function() { 951 complete(); 952 elems.css({ 953 left: 0 954 }); 955 }; 956 }( $( params.next ).add( params.prev ) )), 957 queue: false, 958 easing: easing 959 }); 960 961 if (door) { 962 params.rewind = !params.rewind; 963 } 964 965 if (params.prev) { 966 967 from = { left: 0 }; 968 to = { left: distance * ( params.rewind ? 1 : -1 ) }; 969 970 if ( fade ) { 971 from.opacity = 1; 972 to.opacity = 0; 973 } 974 975 $(params.prev).css(from); 976 Utils.animate(params.prev, to, { 977 duration: params.speed, 978 queue: false, 979 easing: easing, 980 complete: function() { 981 $(this).css('opacity', 0); 982 } 983 }); 984 } 985 }; 986 987 return { 988 989 active: false, 990 991 init: function( effect, params, complete ) { 992 if ( _transitions.effects.hasOwnProperty( effect ) ) { 993 _transitions.effects[ effect ].call( this, params, complete ); 994 } 995 }, 996 997 effects: { 998 999 fade: function(params, complete) { 1000 $(params.next).css({ 1001 opacity: 0, 1002 left: 0 1003 }); 1004 Utils.animate(params.next, { 1005 opacity: 1 1006 },{ 1007 duration: params.speed, 1008 complete: complete 1009 }); 1010 if (params.prev) { 1011 $(params.prev).css('opacity',1).show(); 1012 Utils.animate(params.prev, { 1013 opacity: 0 1014 },{ 1015 duration: params.speed 1016 }); 1017 } 1018 }, 1019 1020 flash: function(params, complete) { 1021 $(params.next).css({ 1022 opacity: 0, 1023 left: 0 1024 }); 1025 if (params.prev) { 1026 Utils.animate( params.prev, { 1027 opacity: 0 1028 },{ 1029 duration: params.speed/2, 1030 complete: function() { 1031 Utils.animate( params.next, { 1032 opacity:1 1033 },{ 1034 duration: params.speed, 1035 complete: complete 1036 }); 1037 } 1038 }); 1039 } else { 1040 Utils.animate( params.next, { 1041 opacity: 1 1042 },{ 1043 duration: params.speed, 1044 complete: complete 1045 }); 1046 } 1047 }, 1048 1049 pulse: function(params, complete) { 1050 if (params.prev) { 1051 $(params.prev).hide(); 1052 } 1053 $(params.next).css({ 1054 opacity: 0, 1055 left: 0 1056 }).show(); 1057 Utils.animate(params.next, { 1058 opacity:1 1059 },{ 1060 duration: params.speed, 1061 complete: complete 1062 }); 1063 }, 1064 1065 slide: function(params, complete) { 1066 _slide.apply( this, Utils.array( arguments ) ); 1067 }, 1068 1069 fadeslide: function(params, complete) { 1070 _slide.apply( this, Utils.array( arguments ).concat( [true] ) ); 1071 }, 1072 1073 doorslide: function(params, complete) { 1074 _slide.apply( this, Utils.array( arguments ).concat( [false, true] ) ); 1075 } 1076 } 1077 }; 1078 }()); 1079 1080_nativeFullscreen.listen(); 1081 1082/** 1083 The main Galleria class 1084 1085 @class 1086 @constructor 1087 1088 @example var gallery = new Galleria(); 1089 1090 @author http://aino.se 1091 1092 @requires jQuery 1093 1094*/ 1095 1096Galleria = function() { 1097 1098 var self = this; 1099 1100 // internal options 1101 this._options = {}; 1102 1103 // flag for controlling play/pause 1104 this._playing = false; 1105 1106 // internal interval for slideshow 1107 this._playtime = 5000; 1108 1109 // internal variable for the currently active image 1110 this._active = null; 1111 1112 // the internal queue, arrayified 1113 this._queue = { length: 0 }; 1114 1115 // the internal data array 1116 this._data = []; 1117 1118 // the internal dom collection 1119 this._dom = {}; 1120 1121 // the internal thumbnails array 1122 this._thumbnails = []; 1123 1124 // the internal layers array 1125 this._layers = []; 1126 1127 // internal init flag 1128 this._initialized = false; 1129 1130 // internal firstrun flag 1131 this._firstrun = false; 1132 1133 // global stagewidth/height 1134 this._stageWidth = 0; 1135 this._stageHeight = 0; 1136 1137 // target holder 1138 this._target = undef; 1139 1140 // bind hashes 1141 this._binds = []; 1142 1143 // instance id 1144 this._id = parseInt(Math.random()*10000, 10); 1145 1146 // add some elements 1147 var divs = 'container stage images image-nav image-nav-left image-nav-right ' + 1148 'info info-text info-title info-description ' + 1149 'thumbnails thumbnails-list thumbnails-container thumb-nav-left thumb-nav-right ' + 1150 'loader counter tooltip', 1151 spans = 'current total'; 1152 1153 $.each( divs.split(' '), function( i, elemId ) { 1154 self._dom[ elemId ] = Utils.create( 'galleria-' + elemId ); 1155 }); 1156 1157 $.each( spans.split(' '), function( i, elemId ) { 1158 self._dom[ elemId ] = Utils.create( 'galleria-' + elemId, 'span' ); 1159 }); 1160 1161 // the internal keyboard object 1162 // keeps reference of the keybinds and provides helper methods for binding keys 1163 var keyboard = this._keyboard = { 1164 1165 keys : { 1166 'UP': 38, 1167 'DOWN': 40, 1168 'LEFT': 37, 1169 'RIGHT': 39, 1170 'RETURN': 13, 1171 'ESCAPE': 27, 1172 'BACKSPACE': 8, 1173 'SPACE': 32 1174 }, 1175 1176 map : {}, 1177 1178 bound: false, 1179 1180 press: function(e) { 1181 var key = e.keyCode || e.which; 1182 if ( key in keyboard.map && typeof keyboard.map[key] === 'function' ) { 1183 keyboard.map[key].call(self, e); 1184 } 1185 }, 1186 1187 attach: function(map) { 1188 1189 var key, up; 1190 1191 for( key in map ) { 1192 if ( map.hasOwnProperty( key ) ) { 1193 up = key.toUpperCase(); 1194 if ( up in keyboard.keys ) { 1195 keyboard.map[ keyboard.keys[up] ] = map[key]; 1196 } else { 1197 keyboard.map[ up ] = map[key]; 1198 } 1199 } 1200 } 1201 if ( !keyboard.bound ) { 1202 keyboard.bound = true; 1203 $doc.bind('keydown', keyboard.press); 1204 } 1205 }, 1206 1207 detach: function() { 1208 keyboard.bound = false; 1209 keyboard.map = {}; 1210 $doc.unbind('keydown', keyboard.press); 1211 } 1212 }; 1213 1214 // internal controls for keeping track of active / inactive images 1215 var controls = this._controls = { 1216 1217 0: undef, 1218 1219 1: undef, 1220 1221 active : 0, 1222 1223 swap : function() { 1224 controls.active = controls.active ? 0 : 1; 1225 }, 1226 1227 getActive : function() { 1228 return controls[ controls.active ]; 1229 }, 1230 1231 getNext : function() { 1232 return controls[ 1 - controls.active ]; 1233 } 1234 }; 1235 1236 // internal carousel object 1237 var carousel = this._carousel = { 1238 1239 // shortcuts 1240 next: self.$('thumb-nav-right'), 1241 prev: self.$('thumb-nav-left'), 1242 1243 // cache the width 1244 width: 0, 1245 1246 // track the current position 1247 current: 0, 1248 1249 // cache max value 1250 max: 0, 1251 1252 // save all hooks for each width in an array 1253 hooks: [], 1254 1255 // update the carousel 1256 // you can run this method anytime, f.ex on window.resize 1257 update: function() { 1258 var w = 0, 1259 h = 0, 1260 hooks = [0]; 1261 1262 $.each( self._thumbnails, function( i, thumb ) { 1263 if ( thumb.ready ) { 1264 w += thumb.outerWidth || $( thumb.container ).outerWidth( true ); 1265 hooks[ i+1 ] = w; 1266 h = Math.max( h, thumb.outerHeight || $( thumb.container).outerHeight( true ) ); 1267 } 1268 }); 1269 1270 self.$( 'thumbnails' ).css({ 1271 width: w, 1272 height: h 1273 }); 1274 1275 carousel.max = w; 1276 carousel.hooks = hooks; 1277 carousel.width = self.$( 'thumbnails-list' ).width(); 1278 carousel.setClasses(); 1279 1280 self.$( 'thumbnails-container' ).toggleClass( 'galleria-carousel', w > carousel.width ); 1281 1282 // one extra calculation 1283 carousel.width = self.$( 'thumbnails-list' ).width(); 1284 1285 // todo: fix so the carousel moves to the left 1286 }, 1287 1288 bindControls: function() { 1289 1290 var i; 1291 1292 carousel.next.bind( 'click', function(e) { 1293 e.preventDefault(); 1294 1295 if ( self._options.carouselSteps === 'auto' ) { 1296 1297 for ( i = carousel.current; i < carousel.hooks.length; i++ ) { 1298 if ( carousel.hooks[i] - carousel.hooks[ carousel.current ] > carousel.width ) { 1299 carousel.set(i - 2); 1300 break; 1301 } 1302 } 1303 1304 } else { 1305 carousel.set( carousel.current + self._options.carouselSteps); 1306 } 1307 }); 1308 1309 carousel.prev.bind( 'click', function(e) { 1310 e.preventDefault(); 1311 1312 if ( self._options.carouselSteps === 'auto' ) { 1313 1314 for ( i = carousel.current; i >= 0; i-- ) { 1315 if ( carousel.hooks[ carousel.current ] - carousel.hooks[i] > carousel.width ) { 1316 carousel.set( i + 2 ); 1317 break; 1318 } else if ( i === 0 ) { 1319 carousel.set( 0 ); 1320 break; 1321 } 1322 } 1323 } else { 1324 carousel.set( carousel.current - self._options.carouselSteps ); 1325 } 1326 }); 1327 }, 1328 1329 // calculate and set positions 1330 set: function( i ) { 1331 i = Math.max( i, 0 ); 1332 while ( carousel.hooks[i - 1] + carousel.width >= carousel.max && i >= 0 ) { 1333 i--; 1334 } 1335 carousel.current = i; 1336 carousel.animate(); 1337 }, 1338 1339 // get the last position 1340 getLast: function(i) { 1341 return ( i || carousel.current ) - 1; 1342 }, 1343 1344 // follow the active image 1345 follow: function(i) { 1346 1347 //don't follow if position fits 1348 if ( i === 0 || i === carousel.hooks.length - 2 ) { 1349 carousel.set( i ); 1350 return; 1351 } 1352 1353 // calculate last position 1354 var last = carousel.current; 1355 while( carousel.hooks[last] - carousel.hooks[ carousel.current ] < 1356 carousel.width && last <= carousel.hooks.length ) { 1357 last ++; 1358 } 1359 1360 // set position 1361 if ( i - 1 < carousel.current ) { 1362 carousel.set( i - 1 ); 1363 } else if ( i + 2 > last) { 1364 carousel.set( i - last + carousel.current + 2 ); 1365 } 1366 }, 1367 1368 // helper for setting disabled classes 1369 setClasses: function() { 1370 carousel.prev.toggleClass( 'disabled', !carousel.current ); 1371 carousel.next.toggleClass( 'disabled', carousel.hooks[ carousel.current ] + carousel.width >= carousel.max ); 1372 }, 1373 1374 // the animation method 1375 animate: function(to) { 1376 carousel.setClasses(); 1377 var num = carousel.hooks[ carousel.current ] * -1; 1378 1379 if ( isNaN( num ) ) { 1380 return; 1381 } 1382 1383 Utils.animate(self.get( 'thumbnails' ), { 1384 left: num 1385 },{ 1386 duration: self._options.carouselSpeed, 1387 easing: self._options.easing, 1388 queue: false 1389 }); 1390 } 1391 }; 1392 1393 // tooltip control 1394 // added in 1.2 1395 var tooltip = this._tooltip = { 1396 1397 initialized : false, 1398 1399 open: false, 1400 1401 timer: 'tooltip' + self._id, 1402 1403 swapTimer: 'swap' + self._id, 1404 1405 init: function() { 1406 1407 tooltip.initialized = true; 1408 1409 var css = '.galleria-tooltip{padding:3px 8px;max-width:50%;background:#ffe;color:#000;z-index:3;position:absolute;font-size:11px;line-height:1.3;' + 1410 'opacity:0;box-shadow:0 0 2px rgba(0,0,0,.4);-moz-box-shadow:0 0 2px rgba(0,0,0,.4);-webkit-box-shadow:0 0 2px rgba(0,0,0,.4);}'; 1411 1412 Utils.insertStyleTag( css, 'galleria-tooltip' ); 1413 1414 self.$( 'tooltip' ).css({ 1415 opacity: 0.8, 1416 visibility: 'visible', 1417 display: 'none' 1418 }); 1419 1420 }, 1421 1422 // move handler 1423 move: function( e ) { 1424 var mouseX = self.getMousePosition(e).x, 1425 mouseY = self.getMousePosition(e).y, 1426 $elem = self.$( 'tooltip' ), 1427 x = mouseX, 1428 y = mouseY, 1429 height = $elem.outerHeight( true ) + 1, 1430 width = $elem.outerWidth( true ), 1431 limitY = height + 15; 1432 1433 var maxX = self.$( 'container' ).width() - width - 2, 1434 maxY = self.$( 'container' ).height() - height - 2; 1435 1436 if ( !isNaN(x) && !isNaN(y) ) { 1437 1438 x += 10; 1439 y -= ( height+8 ); 1440 1441 x = Math.max( 0, Math.min( maxX, x ) ); 1442 y = Math.max( 0, Math.min( maxY, y ) ); 1443 1444 if( mouseY < limitY ) { 1445 y = limitY; 1446 } 1447 1448 $elem.css({ left: x, top: y }); 1449 } 1450 }, 1451 1452 // bind elements to the tooltip 1453 // you can bind multiple elementIDs using { elemID : function } or { elemID : string } 1454 // you can also bind single DOM elements using bind(elem, string) 1455 bind: function( elem, value ) { 1456 1457 // todo: revise if alternative tooltip is needed for mobile devices 1458 if (Galleria.TOUCH) { 1459 return; 1460 } 1461 1462 if (! tooltip.initialized ) { 1463 tooltip.init(); 1464 } 1465 1466 var mouseout = function() { 1467 self.$( 'container' ).unbind( 'mousemove', tooltip.move ); 1468 self.clearTimer( tooltip.timer ); 1469 1470 self.$( 'tooltip' ).stop().animate({ 1471 opacity: 0 1472 }, 200, function() { 1473 1474 self.$( 'tooltip' ).hide(); 1475 1476 self.addTimer( tooltip.swapTimer, function() { 1477 tooltip.open = false; 1478 }, 1000); 1479 }); 1480 }; 1481 1482 var hover = function( elem, value) { 1483 1484 tooltip.define( elem, value ); 1485 1486 $( elem ).hover(function() { 1487 1488 self.clearTimer( tooltip.swapTimer ); 1489 self.$('container').unbind( 'mousemove', tooltip.move ).bind( 'mousemove', tooltip.move ).trigger( 'mousemove' ); 1490 tooltip.show( elem ); 1491 1492 self.addTimer( tooltip.timer, function() { 1493 self.$( 'tooltip' ).stop().show().animate({ 1494 opacity: 1 1495 }); 1496 tooltip.open = true; 1497 1498 }, tooltip.open ? 0 : 500); 1499 1500 }, mouseout).click(mouseout); 1501 }; 1502 1503 if ( typeof value === 'string' ) { 1504 hover( ( elem in self._dom ? self.get( elem ) : elem ), value ); 1505 } else { 1506 // asume elemID here 1507 $.each( elem, function( elemID, val ) { 1508 hover( self.get(elemID), val ); 1509 }); 1510 } 1511 }, 1512 1513 show: function( elem ) { 1514 1515 elem = $( elem in self._dom ? self.get(elem) : elem ); 1516 1517 var text = elem.data( 'tt' ), 1518 mouseup = function( e ) { 1519 1520 // attach a tiny settimeout to make sure the new tooltip is filled 1521 window.setTimeout( (function( ev ) { 1522 return function() { 1523 tooltip.move( ev ); 1524 }; 1525 }( e )), 10); 1526 1527 elem.unbind( 'mouseup', mouseup ); 1528 1529 }; 1530 1531 text = typeof text === 'function' ? text() : text; 1532 1533 if ( ! text ) { 1534 return; 1535 } 1536 1537 self.$( 'tooltip' ).html( text.replace(/\s/, ' ') ); 1538 1539 // trigger mousemove on mouseup in case of click 1540 elem.bind( 'mouseup', mouseup ); 1541 }, 1542 1543 define: function( elem, value ) { 1544 1545 // we store functions, not strings 1546 if (typeof value !== 'function') { 1547 var s = value; 1548 value = function() { 1549 return s; 1550 }; 1551 } 1552 1553 elem = $( elem in self._dom ? self.get(elem) : elem ).data('tt', value); 1554 1555 tooltip.show( elem ); 1556 1557 } 1558 }; 1559 1560 // internal fullscreen control 1561 var fullscreen = this._fullscreen = { 1562 1563 scrolled: 0, 1564 1565 crop: undef, 1566 1567 active: false, 1568 1569 keymap: self._keyboard.map, 1570 1571 parseCallback: function( callback, enter ) { 1572 1573 return _transitions.active ? function() { 1574 if ( typeof callback == 'function' ) { 1575 callback.call(self); 1576 } 1577 var active = self._controls.getActive(), 1578 next = self._controls.getNext(); 1579 1580 self._scaleImage( next ); 1581 self._scaleImage( active ); 1582 1583 if ( enter && self._options.trueFullscreen ) { 1584 // Firefox bug, revise later 1585 $( active.container ).add( next.container ).trigger( 'transitionend' ); 1586 } 1587 1588 } : callback; 1589 1590 }, 1591 1592 enter: function( callback ) { 1593 1594 callback = fullscreen.parseCallback( callback, true ); 1595 1596 if ( self._options.trueFullscreen && _nativeFullscreen.support ) { 1597 1598 // do some stuff prior animation for wmoother transitions 1599 1600 fullscreen.active = true; 1601 1602 Utils.forceStyles( self.get('container'), { 1603 width: '100%', 1604 height: '100%' 1605 }); 1606 1607 self.rescale(); 1608 1609 if ( Galleria.MAC ) { 1610 if ( Galleria.WEBKIT && !( Galleria.SAFARI && /version\/[1-5]/.test(NAV)) ) { 1611 self.$('container').css('opacity', 0).addClass('fullscreen'); 1612 window.setTimeout(function() { 1613 fullscreen.scale(); 1614 self.$('container').css('opacity', 1); 1615 }, 50); 1616 } else { 1617 self.$('stage').css('opacity', 0); 1618 window.setTimeout(function() { 1619 fullscreen.scale(); 1620 self.$('stage').css('opacity', 1); 1621 },4); 1622 } 1623 } else { 1624 self.$('container').addClass('fullscreen'); 1625 } 1626 1627 $win.resize( fullscreen.scale ); 1628 1629 _nativeFullscreen.enter( self, callback, self.get('container') ); 1630 1631 } else { 1632 1633 fullscreen.scrolled = $win.scrollTop(); 1634 window.scrollTo(0, 0); 1635 1636 fullscreen._enter( callback ); 1637 } 1638 1639 }, 1640 1641 _enter: function( callback ) { 1642 1643 fullscreen.active = true; 1644 1645 if ( IFRAME ) { 1646 1647 fullscreen.iframe = (function() { 1648 1649 var elem, 1650 refer = doc.referrer, 1651 test = doc.createElement('a'), 1652 loc = window.location; 1653 1654 test.href = refer; 1655 1656 if( test.protocol != loc.protocol || 1657 test.hostname != loc.hostname || 1658 test.port != loc.port ) { 1659 Galleria.raise('Parent fullscreen not available. Iframe protocol, domains and ports must match.'); 1660 return false; 1661 } 1662 1663 fullscreen.pd = window.parent.document; 1664 1665 $( fullscreen.pd ).find('iframe').each(function() { 1666 var idoc = this.contentDocument || this.contentWindow.document; 1667 if ( idoc === doc ) { 1668 elem = this; 1669 return false; 1670 } 1671 }); 1672 1673 return elem; 1674 }()); 1675 1676 } 1677 1678 // hide the image until rescale is complete 1679 Utils.hide( self.getActiveImage() ); 1680 1681 if ( IFRAME && fullscreen.iframe ) { 1682 fullscreen.iframe.scrolled = $( window.parent ).scrollTop(); 1683 window.parent.scrollTo(0, 0); 1684 } 1685 1686 var data = self.getData(), 1687 options = self._options, 1688 inBrowser = !self._options.trueFullscreen || !_nativeFullscreen.support, 1689 htmlbody = { 1690 height: '100%', 1691 overflow: 'hidden', 1692 margin:0, 1693 padding:0 1694 }; 1695 1696 if (inBrowser) { 1697 1698 self.$('container').addClass('fullscreen'); 1699 1700 // begin styleforce 1701 1702 Utils.forceStyles(self.get('container'), { 1703 position: 'fixed', 1704 top: 0, 1705 left: 0, 1706 width: '100%', 1707 height: '100%', 1708 zIndex: 10000 1709 }); 1710 Utils.forceStyles( DOM().html, htmlbody ); 1711 Utils.forceStyles( DOM().body, htmlbody ); 1712 } 1713 1714 if ( IFRAME && fullscreen.iframe ) { 1715 Utils.forceStyles( fullscreen.pd.documentElement, htmlbody ); 1716 Utils.forceStyles( fullscreen.pd.body, htmlbody ); 1717 Utils.forceStyles( fullscreen.iframe, $.extend( htmlbody, { 1718 width: '100%', 1719 height: '100%', 1720 top: 0, 1721 left: 0, 1722 position: 'fixed', 1723 zIndex: 10000, 1724 border: 'none' 1725 })); 1726 } 1727 1728 // temporarily attach some keys 1729 // save the old ones first in a cloned object 1730 fullscreen.keymap = $.extend({}, self._keyboard.map); 1731 1732 self.attachKeyboard({ 1733 escape: self.exitFullscreen, 1734 right: self.next, 1735 left: self.prev 1736 }); 1737 1738 // temporarily save the crop 1739 fullscreen.crop = options.imageCrop; 1740 1741 // set fullscreen options 1742 if ( options.fullscreenCrop != undef ) { 1743 options.imageCrop = options.fullscreenCrop; 1744 } 1745 1746 // swap to big image if it's different from the display image 1747 if ( data && data.big && data.image !== data.big ) { 1748 var big = new Galleria.Picture(), 1749 cached = big.isCached( data.big ), 1750 index = self.getIndex(), 1751 thumb = self._thumbnails[ index ]; 1752 1753 self.trigger( { 1754 type: Galleria.LOADSTART, 1755 cached: cached, 1756 rewind: false, 1757 index: index, 1758 imageTarget: self.getActiveImage(), 1759 thumbTarget: thumb, 1760 galleriaData: data 1761 }); 1762 1763 big.load( data.big, function( big ) { 1764 self._scaleImage( big, { 1765 complete: function( big ) { 1766 self.trigger({ 1767 type: Galleria.LOADFINISH, 1768 cached: cached, 1769 index: index, 1770 rewind: false, 1771 imageTarget: big.image, 1772 thumbTarget: thumb 1773 }); 1774 var image = self._controls.getActive().image; 1775 if ( image ) { 1776 $( image ).width( big.image.width ).height( big.image.height ) 1777 .attr( 'style', $( big.image ).attr('style') ) 1778 .attr( 'src', big.image.src ); 1779 } 1780 } 1781 }); 1782 }); 1783 } 1784 1785 // init the first rescale and attach callbacks 1786 1787 self.rescale(function() { 1788 1789 self.addTimer(false, function() { 1790 // show the image after 50 ms 1791 if ( inBrowser ) { 1792 Utils.show( self.getActiveImage() ); 1793 } 1794 1795 if (typeof callback === 'function') { 1796 callback.call( self ); 1797 } 1798 1799 }, 100); 1800 1801 self.trigger( Galleria.FULLSCREEN_ENTER ); 1802 }); 1803 1804 if ( !inBrowser ) { 1805 Utils.show( self.getActiveImage() ); 1806 } else { 1807 $win.resize( fullscreen.scale ); 1808 } 1809 1810 }, 1811 1812 scale : function() { 1813 self.rescale(); 1814 }, 1815 1816 exit: function( callback ) { 1817 1818 callback = fullscreen.parseCallback( callback ); 1819 1820 if ( self._options.trueFullscreen && _nativeFullscreen.support ) { 1821 _nativeFullscreen.exit( callback ); 1822 } else { 1823 fullscreen._exit( callback ); 1824 } 1825 }, 1826 1827 _exit: function( callback ) { 1828 1829 fullscreen.active = false; 1830 1831 var inBrowser = !self._options.trueFullscreen || !_nativeFullscreen.support; 1832 1833 self.$('container').removeClass( 'fullscreen' ); 1834 1835 if ( inBrowser ) { 1836 Utils.hide( self.getActiveImage() ); 1837 1838 // revert all styles 1839 Utils.revertStyles( self.get('container'), DOM().html, DOM().body ); 1840 1841 // scroll back 1842 window.scrollTo(0, fullscreen.scrolled); 1843 } 1844 1845 if ( IFRAME && fullscreen.iframe ) { 1846 Utils.revertStyles( fullscreen.pd.documentElement, fullscreen.pd.body, fullscreen.iframe ); 1847 if ( fullscreen.iframe.scrolled ) { 1848 window.parent.scrollTo(0, fullscreen.iframe.scrolled ); 1849 } 1850 } 1851 1852 // detach all keyboard events and apply the old keymap 1853 self.detachKeyboard(); 1854 self.attachKeyboard( fullscreen.keymap ); 1855 1856 // bring back cached options 1857 self._options.imageCrop = fullscreen.crop; 1858 1859 // return to original image 1860 var big = self.getData().big, 1861 image = self._controls.getActive().image; 1862 1863 if ( !self.getData().iframe && image && big && big == image.src ) { 1864 1865 window.setTimeout(function(src) { 1866 return function() { 1867 image.src = src; 1868 }; 1869 }( self.getData().image ), 1 ); 1870 1871 } 1872 1873 self.rescale(function() { 1874 self.addTimer(false, function() { 1875 1876 // show the image after 50 ms 1877 if ( inBrowser ) { 1878 Utils.show( self.getActiveImage() ); 1879 } 1880 1881 if ( typeof callback === 'function' ) { 1882 callback.call( self ); 1883 } 1884 1885 $win.trigger( 'resize' ); 1886 1887 }, 50); 1888 self.trigger( Galleria.FULLSCREEN_EXIT ); 1889 }); 1890 1891 $win.unbind('resize', fullscreen.scale); 1892 } 1893 }; 1894 1895 // the internal idle object for controlling idle states 1896 var idle = this._idle = { 1897 1898 trunk: [], 1899 1900 bound: false, 1901 1902 active: false, 1903 1904 add: function(elem, to, from, hide) { 1905 if (!elem) { 1906 return; 1907 } 1908 if (!idle.bound) { 1909 idle.addEvent(); 1910 } 1911 elem = $(elem); 1912 1913 if ( typeof from == 'boolean' ) { 1914 hide = from; 1915 from = {}; 1916 } 1917 1918 from = from || {}; 1919 1920 var extract = {}, 1921 style; 1922 1923 for ( style in to ) { 1924 if ( to.hasOwnProperty( style ) ) { 1925 extract[ style ] = elem.css( style ); 1926 } 1927 } 1928 1929 elem.data('idle', { 1930 from: $.extend( extract, from ), 1931 to: to, 1932 complete: true, 1933 busy: false 1934 }); 1935 1936 if ( !hide ) { 1937 idle.addTimer(); 1938 } else { 1939 elem.css( to ); 1940 } 1941 idle.trunk.push(elem); 1942 }, 1943 1944 remove: function(elem) { 1945 1946 elem = $(elem); 1947 1948 $.each(idle.trunk, function(i, el) { 1949 if ( el && el.length && !el.not(elem).length ) { 1950 elem.css( elem.data( 'idle' ).from ); 1951 idle.trunk.splice(i, 1); 1952 } 1953 }); 1954 1955 if (!idle.trunk.length) { 1956 idle.removeEvent(); 1957 self.clearTimer( idle.timer ); 1958 } 1959 }, 1960 1961 addEvent : function() { 1962 idle.bound = true; 1963 self.$('container').bind( 'mousemove click', idle.showAll ); 1964 if ( self._options.idleMode == 'hover' ) { 1965 self.$('container').bind( 'mouseleave', idle.hide ); 1966 } 1967 }, 1968 1969 removeEvent : function() { 1970 idle.bound = false; 1971 self.$('container').bind( 'mousemove click', idle.showAll ); 1972 if ( self._options.idleMode == 'hover' ) { 1973 self.$('container').unbind( 'mouseleave', idle.hide ); 1974 } 1975 }, 1976 1977 addTimer : function() { 1978 if( self._options.idleMode == 'hover' ) { 1979 return; 1980 } 1981 self.addTimer( 'idle', function() { 1982 idle.hide(); 1983 }, self._options.idleTime ); 1984 }, 1985 1986 hide : function() { 1987 1988 if ( !self._options.idleMode || self.getIndex() === false || self.getData().iframe ) { 1989 return; 1990 } 1991 1992 self.trigger( Galleria.IDLE_ENTER ); 1993 1994 var len = idle.trunk.length; 1995 1996 $.each( idle.trunk, function(i, elem) { 1997 1998 var data = elem.data('idle'); 1999 2000 if (! data) { 2001 return; 2002 } 2003 2004 elem.data('idle').complete = false; 2005 2006 Utils.animate( elem, data.to, { 2007 duration: self._options.idleSpeed, 2008 complete: function() { 2009 if ( i == len-1 ) { 2010 idle.active = false; 2011 } 2012 } 2013 }); 2014 }); 2015 }, 2016 2017 showAll : function() { 2018 2019 self.clearTimer( 'idle' ); 2020 2021 $.each( idle.trunk, function( i, elem ) { 2022 idle.show( elem ); 2023 }); 2024 }, 2025 2026 show: function(elem) { 2027 2028 var data = elem.data('idle'); 2029 2030 if ( !idle.active || ( !data.busy && !data.complete ) ) { 2031 2032 data.busy = true; 2033 2034 self.trigger( Galleria.IDLE_EXIT ); 2035 2036 self.clearTimer( 'idle' ); 2037 2038 Utils.animate( elem, data.from, { 2039 duration: self._options.idleSpeed/2, 2040 complete: function() { 2041 idle.active = true; 2042 $(elem).data('idle').busy = false; 2043 $(elem).data('idle').complete = true; 2044 } 2045 }); 2046 2047 } 2048 idle.addTimer(); 2049 } 2050 }; 2051 2052 // internal lightbox object 2053 // creates a predesigned lightbox for simple popups of images in galleria 2054 var lightbox = this._lightbox = { 2055 2056 width : 0, 2057 2058 height : 0, 2059 2060 initialized : false, 2061 2062 active : null, 2063 2064 image : null, 2065 2066 elems : {}, 2067 2068 keymap: false, 2069 2070 init : function() { 2071 2072 // trigger the event 2073 self.trigger( Galleria.LIGHTBOX_OPEN ); 2074 2075 if ( lightbox.initialized ) { 2076 return; 2077 } 2078 lightbox.initialized = true; 2079 2080 // create some elements to work with 2081 var elems = 'overlay box content shadow title info close prevholder prev nextholder next counter image', 2082 el = {}, 2083 op = self._options, 2084 css = '', 2085 abs = 'position:absolute;', 2086 prefix = 'lightbox-', 2087 cssMap = { 2088 overlay: 'position:fixed;display:none;opacity:'+op.overlayOpacity+';filter:alpha(opacity='+(op.overlayOpacity*100)+ 2089 ');top:0;left:0;width:100%;height:100%;background:'+op.overlayBackground+';z-index:99990', 2090 box: 'position:fixed;display:none;width:400px;height:400px;top:50%;left:50%;margin-top:-200px;margin-left:-200px;z-index:99991', 2091 shadow: abs+'background:#000;width:100%;height:100%;', 2092 content: abs+'background-color:#fff;top:10px;left:10px;right:10px;bottom:10px;overflow:hidden', 2093 info: abs+'bottom:10px;left:10px;right:10px;color:#444;font:11px/13px arial,sans-serif;height:13px', 2094 close: abs+'top:10px;right:10px;height:20px;width:20px;background:#fff;text-align:center;cursor:pointer;color:#444;font:16px/22px arial,sans-serif;z-index:99999', 2095 image: abs+'top:10px;left:10px;right:10px;bottom:30px;overflow:hidden;display:block;', 2096 prevholder: abs+'width:50%;top:0;bottom:40px;cursor:pointer;', 2097 nextholder: abs+'width:50%;top:0;bottom:40px;right:-1px;cursor:pointer;', 2098 prev: abs+'top:50%;margin-top:-20px;height:40px;width:30px;background:#fff;left:20px;display:none;text-align:center;color:#000;font:bold 16px/36px arial,sans-serif', 2099 next: abs+'top:50%;margin-top:-20px;height:40px;width:30px;background:#fff;right:20px;left:auto;display:none;font:bold 16px/36px arial,sans-serif;text-align:center;color:#000', 2100 title: 'float:left', 2101 counter: 'float:right;margin-left:8px;' 2102 }, 2103 hover = function(elem) { 2104 return elem.hover( 2105 function() { $(this).css( 'color', '#bbb' ); }, 2106 function() { $(this).css( 'color', '#444' ); } 2107 ); 2108 }, 2109 appends = {}; 2110 2111 // IE8 fix for IE's transparent background event "feature" 2112 if ( IE && IE > 7 ) { 2113 cssMap.nextholder += 'background:#000;filter:alpha(opacity=0);'; 2114 cssMap.prevholder += 'background:#000;filter:alpha(opacity=0);'; 2115 } 2116 2117 // create and insert CSS 2118 $.each(cssMap, function( key, value ) { 2119 css += '.galleria-'+prefix+key+'{'+value+'}'; 2120 }); 2121 2122 css += '.galleria-'+prefix+'box.iframe .galleria-'+prefix+'prevholder,'+ 2123 '.galleria-'+prefix+'box.iframe .galleria-'+prefix+'nextholder{'+ 2124 'width:100px;height:100px;top:50%;margin-top:-70px}'; 2125 2126 Utils.insertStyleTag( css, 'galleria-lightbox' ); 2127 2128 // create the elements 2129 $.each(elems.split(' '), function( i, elemId ) { 2130 self.addElement( 'lightbox-' + elemId ); 2131 el[ elemId ] = lightbox.elems[ elemId ] = self.get( 'lightbox-' + elemId ); 2132 }); 2133 2134 // initiate the image 2135 lightbox.image = new Galleria.Picture(); 2136 2137 // append the elements 2138 $.each({ 2139 box: 'shadow content close prevholder nextholder', 2140 info: 'title counter', 2141 content: 'info image', 2142 prevholder: 'prev', 2143 nextholder: 'next' 2144 }, function( key, val ) { 2145 var arr = []; 2146 $.each( val.split(' '), function( i, prop ) { 2147 arr.push( prefix + prop ); 2148 }); 2149 appends[ prefix+key ] = arr; 2150 }); 2151 2152 self.append( appends ); 2153 2154 $( el.image ).append( lightbox.image.container ); 2155 2156 $( DOM().body ).append( el.overlay, el.box ); 2157 2158 Utils.optimizeTouch( el.box ); 2159 2160 // add the prev/next nav and bind some controls 2161 2162 hover( $( el.close ).bind( 'click', lightbox.hide ).html('×') ); 2163 2164 $.each( ['Prev','Next'], function(i, dir) { 2165 2166 var $d = $( el[ dir.toLowerCase() ] ).html( /v/.test( dir ) ? '‹ ' : ' ›' ), 2167 $e = $( el[ dir.toLowerCase()+'holder'] ); 2168 2169 $e.bind( 'click', function() { 2170 lightbox[ 'show' + dir ](); 2171 }); 2172 2173 // IE7 and touch devices will simply show the nav 2174 if ( IE < 8 || Galleria.TOUCH ) { 2175 $d.show(); 2176 return; 2177 } 2178 2179 $e.hover( function() { 2180 $d.show(); 2181 }, function(e) { 2182 $d.stop().fadeOut( 200 ); 2183 }); 2184 2185 }); 2186 $( el.overlay ).bind( 'click', lightbox.hide ); 2187 2188 // the lightbox animation is slow on ipad 2189 if ( Galleria.IPAD ) { 2190 self._options.lightboxTransitionSpeed = 0; 2191 } 2192 2193 }, 2194 2195 rescale: function(event) { 2196 2197 // calculate 2198 var width = Math.min( $win.width()-40, lightbox.width ), 2199 height = Math.min( $win.height()-60, lightbox.height ), 2200 ratio = Math.min( width / lightbox.width, height / lightbox.height ), 2201 destWidth = Math.round( lightbox.width * ratio ) + 40, 2202 destHeight = Math.round( lightbox.height * ratio ) + 60, 2203 to = { 2204 width: destWidth, 2205 height: destHeight, 2206 'margin-top': Math.ceil( destHeight / 2 ) *- 1, 2207 'margin-left': Math.ceil( destWidth / 2 ) *- 1 2208 }; 2209 2210 // if rescale event, don't animate 2211 if ( event ) { 2212 $( lightbox.elems.box ).css( to ); 2213 } else { 2214 $( lightbox.elems.box ).animate( to, { 2215 duration: self._options.lightboxTransitionSpeed, 2216 easing: self._options.easing, 2217 complete: function() { 2218 var image = lightbox.image, 2219 speed = self._options.lightboxFadeSpeed; 2220 2221 self.trigger({ 2222 type: Galleria.LIGHTBOX_IMAGE, 2223 imageTarget: image.image 2224 }); 2225 2226 $( image.container ).show(); 2227 2228 $( image.image ).animate({ opacity: 1 }, speed); 2229 Utils.show( lightbox.elems.info, speed ); 2230 } 2231 }); 2232 } 2233 }, 2234 2235 hide: function() { 2236 2237 // remove the image 2238 lightbox.image.image = null; 2239 2240 $win.unbind('resize', lightbox.rescale); 2241 2242 $( lightbox.elems.box ).hide(); 2243 2244 Utils.hide( lightbox.elems.info ); 2245 2246 self.detachKeyboard(); 2247 self.attachKeyboard( lightbox.keymap ); 2248 2249 lightbox.keymap = false;
2250 2251 Utils.hide( lightbox.elems.overlay, 200, function() { 2252 $( this ).hide().css( 'opacity', self._options.overlayOpacity ); 2253 self.trigger( Galleria.LIGHTBOX_CLOSE ); 2254 }); 2255 }, 2256 2257 showNext: function() { 2258 lightbox.show( self.getNext( lightbox.active ) ); 2259 }, 2260 2261 showPrev: function() { 2262 lightbox.show( self.getPrev( lightbox.active ) ); 2263 }, 2264 2265 show: function(index) { 2266 2267 lightbox.active = index = typeof index === 'number' ? index : self.getIndex() || 0; 2268 2269 if ( !lightbox.initialized ) { 2270 lightbox.init(); 2271 } 2272 2273 // temporarily attach some keys 2274 // save the old ones first in a cloned object 2275 if ( !lightbox.keymap ) { 2276 2277 lightbox.keymap = $.extend({}, self._keyboard.map); 2278 2279 self.attachKeyboard({ 2280 escape: lightbox.hide, 2281 right: lightbox.showNext, 2282 left: lightbox.showPrev 2283 }); 2284 } 2285 2286 $win.unbind('resize', lightbox.rescale ); 2287 2288 var data = self.getData(index), 2289 total = self.getDataLength(), 2290 n = self.getNext( index ), 2291 ndata, p, i; 2292 2293 Utils.hide( lightbox.elems.info ); 2294 2295 try { 2296 for ( i = self._options.preload; i > 0; i-- ) { 2297 p = new Galleria.Picture(); 2298 ndata = self.getData( n ); 2299 p.preload( 'big' in ndata ? ndata.big : ndata.image ); 2300 n = self.getNext( n ); 2301 } 2302 } catch(e) {} 2303 2304 lightbox.image.isIframe = !!data.iframe; 2305 2306 $(lightbox.elems.box).toggleClass( 'iframe', !!data.iframe ); 2307 2308 lightbox.image.load( data.iframe || data.big || data.image, function( image ) { 2309 2310 if ( image.isIframe ) { 2311 2312 var cw = $(window).width(), 2313 ch = $(window).height(); 2314 2315 if ( self._options.maxVideoSize ) { 2316 var r = Math.min( self._options.maxVideoSize/cw, self._options.maxVideoSize/ch ); 2317 if ( r < 1 ) { 2318 cw *= r; 2319 ch *= r; 2320 } 2321 } 2322 lightbox.width = cw; 2323 lightbox.height = ch; 2324 2325 } else { 2326 lightbox.width = image.original.width; 2327 lightbox.height = image.original.height; 2328 } 2329 2330 $( image.image ).css({ 2331 width: image.isIframe ? '100%' : '100.1%', 2332 height: image.isIframe ? '100%' : '100.1%', 2333 top: 0, 2334 zIndex: 99998, 2335 opacity: 0, 2336 visibility: 'visible' 2337 }); 2338 2339 lightbox.elems.title.innerHTML = data.title || ''; 2340 lightbox.elems.counter.innerHTML = (index + 1) + ' / ' + total; 2341 $win.resize( lightbox.rescale ); 2342 lightbox.rescale(); 2343 }); 2344 2345 $( lightbox.elems.overlay ).show().css( 'visibility', 'visible' ); 2346 $( lightbox.elems.box ).show(); 2347 } 2348 }; 2349 2350 // the internal timeouts object 2351 // provides helper methods for controlling timeouts 2352 2353 var _timer = this._timer = { 2354 2355 trunk: {}, 2356 2357 add: function( id, fn, delay, loop ) { 2358 id = id || new Date().getTime(); 2359 loop = loop || false; 2360 this.clear( id ); 2361 if ( loop ) { 2362 var old = fn; 2363 fn = function() { 2364 old(); 2365 _timer.add( id, fn, delay ); 2366 }; 2367 } 2368 this.trunk[ id ] = window.setTimeout( fn, delay ); 2369 }, 2370 2371 clear: function( id ) { 2372 2373 var del = function( i ) { 2374 window.clearTimeout( this.trunk[ i ] ); 2375 delete this.trunk[ i ]; 2376 }, i; 2377 2378 if ( !!id && id in this.trunk ) { 2379 del.call( this, id ); 2380 2381 } else if ( typeof id === 'undefined' ) { 2382 for ( i in this.trunk ) { 2383 if ( this.trunk.hasOwnProperty( i ) ) { 2384 del.call( this, i ); 2385 } 2386 } 2387 } 2388 } 2389 }; 2390 2391 return this; 2392}; 2393 2394// end Galleria constructor 2395 2396Galleria.prototype = { 2397 2398 // bring back the constructor reference 2399 2400 constructor: Galleria, 2401 2402 /** 2403 Use this function to initialize the gallery and start loading. 2404 Should only be called once per instance. 2405 2406 @param {HTMLElement} target The target element 2407 @param {Object} options The gallery options 2408 2409 @returns Instance 2410 */ 2411 2412 init: function( target, options ) { 2413 2414 var self = this; 2415 2416 options = _legacyOptions( options ); 2417 2418 // save the original ingredients 2419 this._original = { 2420 target: target, 2421 options: options, 2422 data: null 2423 }; 2424 2425 // save the target here
2426 this._target = this._dom.target = target.nodeName ? target : $( target ).get(0); 2427 2428 // save the original content for destruction 2429 this._original.html = this._target.innerHTML; 2430 2431 // push the instance 2432 _instances.push( this ); 2433 2434 // raise error if no target is detected 2435 if ( !this._target ) { 2436 Galleria.raise('Target not found', true); 2437 return; 2438 } 2439 2440 // apply options 2441 this._options = { 2442 autoplay: false, 2443 carousel: true, 2444 carouselFollow: true, // legacy, deprecate at 1.3 2445 carouselSpeed: 400, 2446 carouselSteps: 'auto', 2447 clicknext: false, 2448 dailymotion: { 2449 foreground: '%23EEEEEE', 2450 highlight: '%235BCEC5', 2451 background: '%23222222', 2452 logo: 0, 2453 hideInfos: 1 2454 }, 2455 dataConfig : function( elem ) { return {}; }, 2456 dataSelector: 'img', 2457 dataSort: false, 2458 dataSource: this._target, 2459 debug: undef, 2460 dummy: undef, // 1.2.5 2461 easing: 'galleria', 2462 extend: function(options) {}, 2463 fullscreenCrop: undef, // 1.2.5 2464 fullscreenDoubleTap: true, // 1.2.4 toggles fullscreen on double-tap for touch devices 2465 fullscreenTransition: undef, // 1.2.6 2466 height: 0, 2467 idleMode: true, // 1.2.4 toggles idleMode 2468 idleTime: 3000, 2469 idleSpeed: 200, 2470 imageCrop: false, 2471 imageMargin: 0, 2472 imagePan: false, 2473 imagePanSmoothness: 12, 2474 imagePosition: '50%', 2475 imageTimeout: undef, // 1.2.5 2476 initialTransition: undef, // 1.2.4, replaces transitionInitial 2477 keepSource: false, 2478 layerFollow: true, // 1.2.5 2479 lightbox: false, // 1.2.3 2480 lightboxFadeSpeed: 200, 2481 lightboxTransitionSpeed: 200, 2482 linkSourceImages: true, 2483 maxScaleRatio: undef, 2484 maxVideoSize: undef, // 1.2.9 2485 minScaleRatio: undef, // deprecated in 1.2.9 2486 overlayOpacity: 0.85, 2487 overlayBackground: '#0b0b0b', 2488 pauseOnInteraction: true, 2489 popupLinks: false, 2490 preload: 2, 2491 queue: true, 2492 responsive: true, 2493 show: 0, 2494 showInfo: true, 2495 showCounter: true, 2496 showImagenav: true, 2497 swipe: true, // 1.2.4 2498 thumbCrop: true, 2499 thumbEventType: 'click', 2500 thumbFit: true, // legacy, deprecate at 1.3 2501 thumbMargin: 0, 2502 thumbQuality: 'auto', 2503 thumbDisplayOrder: true, // 1.2.8 2504 thumbnails: true, 2505 touchTransition: undef, // 1.2.6 2506 transition: 'fade', 2507 transitionInitial: undef, // legacy, deprecate in 1.3. Use initialTransition instead. 2508 transitionSpeed: 400, 2509 trueFullscreen: true, // 1.2.7 2510 useCanvas: false, // 1.2.4 2511 vimeo: { 2512 title: 0, 2513 byline: 0, 2514 portrait: 0, 2515 color: 'aaaaaa' 2516 }, 2517 wait: 5000, // 1.2.7 2518 width: 'auto', 2519 youtube: { 2520 modestbranding: 1, 2521 autohide: 1, 2522 color: 'white', 2523 hd: 1, 2524 rel: 0, 2525 showinfo: 0 2526 } 2527 }; 2528 2529 // legacy support for transitionInitial 2530 this._options.initialTransition = this._options.initialTransition || this._options.transitionInitial; 2531 2532 // turn off debug 2533 if ( options && options.debug === false ) { 2534 DEBUG = false; 2535 } 2536 2537 // set timeout 2538 if ( options && typeof options.imageTimeout === 'number' ) { 2539 TIMEOUT = options.imageTimeout; 2540 } 2541 2542 // set dummy 2543 if ( options && typeof options.dummy === 'string' ) { 2544 DUMMY = options.dummy; 2545 } 2546 2547 // hide all content 2548 $( this._target ).children().hide(); 2549
vendor: 4,455 bytes, lines 2550-2680
2550 // Warn for quirks mode 2551 if ( Galleria.QUIRK ) { 2552 Galleria.raise('Your page is in Quirks mode, Galleria may not render correctly. Please validate your HTML and add a correct doctype.'); 2553 } 2554 2555 // now we just have to wait for the theme... 2556 if ( typeof Galleria.theme === 'object' ) { 2557 this._init(); 2558 } else { 2559 // push the instance into the pool and run it when the theme is ready 2560 _pool.push( this ); 2561 } 2562 2563 return this; 2564 }, 2565 2566 // this method should only be called once per instance 2567 // for manipulation of data, use the .load method 2568 2569 _init: function() { 2570 2571 var self = this, 2572 options = this._options; 2573 2574 if ( this._initialized ) { 2575 Galleria.raise( 'Init failed: Gallery instance already initialized.' ); 2576 return this; 2577 } 2578 2579 this._initialized = true; 2580 2581 if ( !Galleria.theme ) { 2582 Galleria.raise( 'Init failed: No theme found.', true ); 2583 return this; 2584 } 2585 2586 // merge the theme & caller options 2587 $.extend( true, options, Galleria.theme.defaults, this._original.options, Galleria.configure.options ); 2588 2589 // check for canvas support 2590 (function( can ) { 2591 2592 if ( !( 'getContext' in can ) ) { 2593 can = null; 2594 return; 2595 } 2596 2597 _canvas = _canvas || { 2598 elem: can, 2599 context: can.getContext( '2d' ), 2600 cache: {}, 2601 length: 0 2602 }; 2603 2604 }( doc.createElement( 'canvas' ) ) ); 2605 2606 // bind the gallery to run when data is ready 2607 this.bind( Galleria.DATA, function() { 2608 2609 // save the new data 2610 this._original.data = this._data; 2611 2612 // lets show the counter here 2613 this.get('total').innerHTML = this.getDataLength(); 2614 2615 // cache the container 2616 var $container = this.$( 'container' ); 2617 2618 // set ratio if height is < 2 2619 if ( self._options.height < 2 ) { 2620 self._userRatio = self._ratio = self._options.height; 2621 } 2622 2623 // the gallery is ready, let's just wait for the css 2624 var num = { width: 0, height: 0 }; 2625 var testHeight = function() { 2626 return self.$( 'stage' ).height(); 2627 }; 2628 2629 // check container and thumbnail height 2630 Utils.wait({ 2631 until: function() { 2632 2633 // keep trying to get the value 2634 num = self._getWH(); 2635 $container.width( num.width ).height( num.height ); 2636 return testHeight() && num.width && num.height > 50; 2637 2638 }, 2639 success: function() { 2640 2641 self._width = num.width; 2642 self._height = num.height; 2643 self._ratio = self._ratio || num.height/num.width; 2644 2645 // for some strange reason, webkit needs a single setTimeout to play ball 2646 if ( Galleria.WEBKIT ) { 2647 window.setTimeout( function() { 2648 self._run(); 2649 }, 1); 2650 } else { 2651 self._run(); 2652 } 2653 }, 2654 error: function() { 2655 2656 // Height was probably not set, raise hard errors 2657 2658 if ( testHeight() ) { 2659 Galleria.raise('Could not extract sufficient width/height of the gallery container. Traced measures: width:' + num.width + 'px, height: ' + num.height + 'px.', true); 2660 } else { 2661 Galleria.raise('Could not extract a stage height from the CSS. Traced height: ' + testHeight() + 'px.', true); 2662 } 2663 }, 2664 timeout: typeof this._options.wait == 'number' ? this._options.wait : false 2665 }); 2666 }); 2667 2668 // build the gallery frame 2669 this.append({ 2670 'info-text' : 2671 ['info-title', 'info-description'], 2672 'info' : 2673 ['info-text'], 2674 'image-nav' : 2675 ['image-nav-right', 'image-nav-left'], 2676 'stage' : 2677 ['images', 'loader', 'counter', 'image-nav'], 2678 'thumbnails-list' : 2679 ['thumbnails'], 2680 'thumbnails-container' :
2681 ['thumb-nav-left', 'thumbnails-list', 'thumb-nav-right'], 2682 'container' : 2683 ['stage', 'thumbnails-container', 'info', 'tooltip'] 2684 }); 2685 2686 Utils.hide( this.$( 'counter' ).append( 2687 this.get( 'current' ), 2688 doc.createTextNode(' / '), 2689 this.get( 'total' ) 2690 ) ); 2691 2692 this.setCounter('–'); 2693 2694 Utils.hide( self.get('tooltip') ); 2695 2696 // add a notouch class on the container to prevent unwanted :hovers on touch devices 2697 this.$( 'container' ).addClass( Galleria.TOUCH ? 'touch' : 'notouch' ); 2698 2699 // add images to the controls 2700 $.each( new Array(2), function( i ) { 2701 2702 // create a new Picture instance 2703 var image = new Galleria.Picture(); 2704 2705 // apply some styles, create & prepend overlay 2706 $( image.container ).css({ 2707 position: 'absolute', 2708 top: 0, 2709 left: 0 2710 }).prepend( self._layers[i] = $( Utils.create('galleria-layer') ).css({ 2711 position: 'absolute', 2712 top:0, left:0, right:0, bottom:0, 2713 zIndex:2 2714 })[0] ); 2715 2716 // append the image 2717 self.$( 'images' ).append( image.container ); 2718 2719 // reload the controls 2720 self._controls[i] = image; 2721 2722 }); 2723 2724 // some forced generic styling 2725 this.$( 'images' ).css({ 2726 position: 'relative', 2727 top: 0, 2728 left: 0, 2729 width: '100%', 2730 height: '100%' 2731 }); 2732 2733 this.$( 'thumbnails, thumbnails-list' ).css({ 2734 overflow: 'hidden', 2735 position: 'relative' 2736 }); 2737 2738 // bind image navigation arrows 2739 this.$( 'image-nav-right, image-nav-left' ).bind( 'click', function(e) { 2740 2741 // tune the clicknext option 2742 if ( options.clicknext ) { 2743 e.stopPropagation(); 2744 } 2745 2746 // pause if options is set 2747 if ( options.pauseOnInteraction ) { 2748 self.pause(); 2749 } 2750 2751 // navigate 2752 var fn = /right/.test( this.className ) ? 'next' : 'prev'; 2753 self[ fn ](); 2754 2755 }); 2756 2757 // hide controls if chosen to 2758 $.each( ['info','counter','image-nav'], function( i, el ) { 2759 if ( options[ 'show' + el.substr(0,1).toUpperCase() + el.substr(1).replace(/-/,'') ] === false ) { 2760 Utils.moveOut( self.get( el.toLowerCase() ) ); 2761 } 2762 }); 2763 2764 // load up target content 2765 this.load(); 2766 2767 // now it's usually safe to remove the content 2768 // IE will never stop loading if we remove it, so let's keep it hidden for IE (it's usually fast enough anyway) 2769 if ( !options.keepSource && !IE ) { 2770 this._target.innerHTML = ''; 2771 } 2772 2773 // re-append the errors, if they happened before clearing 2774 if ( this.get( 'errors' ) ) { 2775 this.appendChild( 'target', 'errors' ); 2776 } 2777 2778 // append the gallery frame 2779 this.appendChild( 'target', 'container' ); 2780 2781 // parse the carousel on each thumb load 2782 if ( options.carousel ) { 2783 var count = 0, 2784 show = options.show; 2785 this.bind( Galleria.THUMBNAIL, function() { 2786 this.updateCarousel(); 2787 if ( ++count == this.getDataLength() && typeof show == 'number' && show > 0 ) { 2788 this._carousel.follow( show ); 2789 } 2790 }); 2791 } 2792 2793 // bind window resize for responsiveness 2794 if ( options.responsive ) { 2795 $win.bind( 'resize', function() { 2796 if ( !self.isFullscreen() ) { 2797 self.resize(); 2798 } 2799 }); 2800 } 2801 2802 // bind swipe gesture 2803 if ( options.swipe ) { 2804 2805 (function( images ) { 2806 2807 var swipeStart = [0,0], 2808 swipeStop = [0,0], 2809 limitX = 30, 2810 limitY = 100, 2811 multi = false, 2812 tid = 0, 2813 data, 2814 ev = { 2815 start: 'touchstart', 2816 move: 'touchmove', 2817 stop: 'touchend' 2818 }, 2819 getData = function(e) { 2820 return e.originalEvent.touches ? e.originalEvent.touches[0] : e; 2821 }, 2822 moveHandler = function( e ) { 2823 2824 if ( e.originalEvent.touches && e.originalEvent.touches.length > 1 ) { 2825 return; 2826 } 2827 2828 data = getData( e ); 2829 swipeStop = [ data.pageX, data.pageY ]; 2830 2831 if ( !swipeStart[0] ) { 2832 swipeStart = swipeStop; 2833 } 2834 2835 if ( Math.abs( swipeStart[0] - swipeStop[0] ) > 10 ) { 2836 e.preventDefault(); 2837 } 2838 }, 2839 upHandler = function( e ) { 2840 2841 images.unbind( ev.move, moveHandler ); 2842 2843 // if multitouch (possibly zooming), abort 2844 if ( ( e.originalEvent.touches && e.originalEvent.touches.length ) || multi ) { 2845 multi = !multi; 2846 return; 2847 } 2848 2849 if ( Utils.timestamp() - tid < 1000 && 2850 Math.abs( swipeStart[0] - swipeStop[0] ) > limitX && 2851 Math.abs( swipeStart[1] - swipeStop[1] ) < limitY ) { 2852 2853 e.preventDefault(); 2854 self[ swipeStart[0] > swipeStop[0] ? 'next' : 'prev' ](); 2855 } 2856 2857 swipeStart = swipeStop = [0,0]; 2858 }; 2859 2860 images.bind(ev.start, function(e) { 2861 2862 if ( e.originalEvent.touches && e.originalEvent.touches.length > 1 ) { 2863 return; 2864 } 2865 2866 data = getData(e); 2867 tid = Utils.timestamp(); 2868 swipeStart = swipeStop = [ data.pageX, data.pageY ]; 2869 images.bind(ev.move, moveHandler ).one(ev.stop, upHandler); 2870 2871 }); 2872 2873 }( self.$( 'images' ) )); 2874 2875 // double-tap/click fullscreen toggle 2876 2877 if ( options.fullscreenDoubleTap ) { 2878 2879 this.$( 'stage' ).bind( 'touchstart', (function() { 2880 var last, cx, cy, lx, ly, now, 2881 getData = function(e) { 2882 return e.originalEvent.touches ? e.originalEvent.touches[0] : e; 2883 }; 2884 return function(e) { 2885 now = Galleria.utils.timestamp(); 2886 cx = getData(e).pageX; 2887 cy = getData(e).pageY; 2888 if ( ( now - last < 500 ) && ( cx - lx < 20) && ( cy - ly < 20) ) { 2889 self.toggleFullscreen(); 2890 e.preventDefault(); 2891 self.$( 'stage' ).unbind( 'touchend', arguments.callee ); 2892 return; 2893 } 2894 last = now; 2895 lx = cx; 2896 ly = cy; 2897 }; 2898 }())); 2899 } 2900 2901 } 2902 2903 // optimize touch for container 2904 Utils.optimizeTouch( this.get( 'container' ) ); 2905 2906 // bind the ons 2907 $.each( Galleria.on.binds, function(i, bind) { 2908 // check if already bound 2909 if ( $.inArray( bind.hash, self._binds ) == -1 ) { 2910 self.bind( bind.type, bind.callback ); 2911 } 2912 }); 2913 2914 return this; 2915 }, 2916 2917 addTimer : function() { 2918 this._timer.add.apply( this._timer, Utils.array( arguments ) ); 2919 return this; 2920 }, 2921 2922 clearTimer : function() { 2923 this._timer.clear.apply( this._timer, Utils.array( arguments ) ); 2924 return this; 2925 }, 2926 2927 // parse width & height from CSS or options 2928 2929 _getWH : function() { 2930 2931 var $container = this.$( 'container' ), 2932 $target = this.$( 'target' ), 2933 self = this, 2934 num = {}, 2935 arr; 2936 2937 $.each(['width', 'height'], function( i, m ) { 2938 2939 // first check if options is set 2940 if ( self._options[ m ] && typeof self._options[ m ] === 'number') { 2941 num[ m ] = self._options[ m ]; 2942 } else { 2943 2944 arr = [ 2945 Utils.parseValue( $container.css( m ) ), // the container css height 2946 Utils.parseValue( $target.css( m ) ), // the target css height 2947 $container[ m ](), // the container jQuery method 2948 $target[ m ]() // the target jQuery method 2949 ]; 2950 2951 // if first time, include the min-width & min-height 2952 if ( !self[ '_'+m ] ) { 2953 arr.splice(arr.length, 2954 Utils.parseValue( $container.css( 'min-'+m ) ), 2955 Utils.parseValue( $target.css( 'min-'+m ) ) 2956 ); 2957 } 2958 2959 // else extract the measures from different sources and grab the highest value 2960 num[ m ] = Math.max.apply( Math, arr ); 2961 } 2962 }); 2963 2964 // allow setting a height ratio instead of exact value 2965 // useful when doing responsive galleries 2966 2967 if ( self._userRatio ) { 2968 num.height = num.width * self._userRatio; 2969 } 2970 2971 return num; 2972 }, 2973 2974 // Creates the thumbnails and carousel
2975 // can be used at any time, f.ex when the data object is manipulated 2976 // push is an optional argument with pushed images 2977 2978 _createThumbnails : function( push ) { 2979 2980 this.get( 'total' ).innerHTML = this.getDataLength(); 2981 2982 var src, 2983 thumb, 2984 data, 2985 special, 2986 2987 $container, 2988 2989 self = this, 2990 o = this._options, 2991 2992 i = push ? this._data.length - push.length : 0, 2993 chunk = i, 2994 2995 thumbchunk = [], 2996 loadindex = 0, 2997 2998 gif = IE < 8 ? 'http://upload.wikimedia.org/wikipedia/commons/c/c0/Blank.gif' : 2999 'data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw%3D%3D', 3000 3001 // get previously active thumbnail, if exists 3002 active = (function() { 3003 var a = self.$('thumbnails').find('.active'); 3004 if ( !a.length ) { 3005 return false; 3006 } 3007 return a.find('img').attr('src'); 3008 }()), 3009 3010 // cache the thumbnail option 3011 optval = typeof o.thumbnails === 'string' ? o.thumbnails.toLowerCase() : null, 3012 3013 // move some data into the instance 3014 // for some reason, jQuery cant handle css(property) when zooming in FF, breaking the gallery 3015 // so we resort to getComputedStyle for browsers who support it 3016 getStyle = function( prop ) { 3017 return doc.defaultView && doc.defaultView.getComputedStyle ? 3018 doc.defaultView.getComputedStyle( thumb.container, null )[ prop ] : 3019 $container.css( prop ); 3020 }, 3021 3022 fake = function(image, index, container) { 3023 return function() { 3024 $( container ).append( image ); 3025 self.trigger({ 3026 type: Galleria.THUMBNAIL, 3027 thumbTarget: image, 3028 index: index, 3029 galleriaData: self.getData( index ) 3030 }); 3031 }; 3032 }, 3033 3034 onThumbEvent = function( e ) { 3035 3036 // pause if option is set 3037 if ( o.pauseOnInteraction ) { 3038 self.pause(); 3039 } 3040 3041 // extract the index from the data 3042 var index = $( e.currentTarget ).data( 'index' ); 3043 if ( self.getIndex() !== index ) { 3044 self.show( index ); 3045 } 3046 3047 e.preventDefault(); 3048 }, 3049 3050 thumbComplete = function( thumb, callback ) { 3051 3052 $( thumb.container ).css( 'visibility', 'visible' ); 3053 self.trigger({ 3054 type: Galleria.THUMBNAIL, 3055 thumbTarget: thumb.image, 3056 index: thumb.data.order, 3057 galleriaData: self.getData( thumb.data.order ) 3058 }); 3059 3060 if ( typeof callback == 'function' ) { 3061 callback.call( self, thumb ); 3062 } 3063 }, 3064 3065 onThumbLoad = function( thumb, callback ) { 3066 3067 // scale when ready 3068 thumb.scale({ 3069 width: thumb.data.width, 3070 height: thumb.data.height, 3071 crop: o.thumbCrop, 3072 margin: o.thumbMargin, 3073 canvas: o.useCanvas, 3074 complete: function( thumb ) { 3075 3076 // shrink thumbnails to fit 3077 var top = ['left', 'top'], 3078 arr = ['Width', 'Height'], 3079 m, 3080 css, 3081 data = self.getData( thumb.index ), 3082 special = data.thumb.split(':'); 3083 3084 // calculate shrinked positions 3085 $.each(arr, function( i, measure ) { 3086 m = measure.toLowerCase(); 3087 if ( (o.thumbCrop !== true || o.thumbCrop === m ) && o.thumbFit ) { 3088 css = {}; 3089 css[ m ] = thumb[ m ]; 3090 $( thumb.container ).css( css ); 3091 css = {}; 3092 css[ top[ i ] ] = 0; 3093 $( thumb.image ).css( css ); 3094 } 3095 3096 // cache outer measures 3097 thumb[ 'outer' + measure ] = $( thumb.container )[ 'outer' + measure ]( true ); 3098 }); 3099 3100 // set high quality if downscale is moderate 3101 Utils.toggleQuality( thumb.image, 3102 o.thumbQuality === true || 3103 ( o.thumbQuality === 'auto' && thumb.original.width < thumb.width * 3 ) 3104 ); 3105 3106 // get "special" thumbs from provider 3107 if( data.iframe && special.length == 2 && special[0] in _video ) { 3108 3109 _video[ special[0] ].getThumb( special[1], (function(img) { 3110 return function(src) { 3111 img.src = src; 3112 thumbComplete( thumb, callback ); 3113 }; 3114 }( thumb.image ) )); 3115 3116 } else if ( o.thumbDisplayOrder && !thumb.lazy ) { 3117 3118 $.each( thumbchunk, function( i, th ) { 3119 if ( i === loadindex && th.ready && !th.displayed ) { 3120 3121 loadindex++; 3122 th.displayed = true; 3123 3124 thumbComplete( th, callback ); 3125 3126 return; 3127 } 3128 }); 3129 } else { 3130 thumbComplete( thumb, callback ); 3131 } 3132 } 3133 }); 3134 }; 3135 3136 if ( !push ) { 3137 this._thumbnails = []; 3138 this.$( 'thumbnails' ).empty(); 3139 } 3140 3141 // loop through data and create thumbnails 3142 for( ; this._data[ i ]; i++ ) { 3143 3144 data = this._data[ i ]; 3145 3146 // get source from thumb or image 3147 src = data.thumb || data.image; 3148 3149 if ( ( o.thumbnails === true || optval == 'lazy' ) && ( data.thumb || data.image ) ) { 3150 3151 // add a new Picture instance 3152 thumb = new Galleria.Picture(i); 3153 3154 // save the index 3155 thumb.index = i; 3156 3157 // flag displayed
3158 thumb.displayed = false; 3159 3160 // flag lazy 3161 thumb.lazy = false; 3162 3163 // flag video 3164 thumb.video = false; 3165 3166 // append the thumbnail 3167 this.$( 'thumbnails' ).append( thumb.container ); 3168 3169 // cache the container 3170 $container = $( thumb.container ); 3171 3172 // hide it 3173 $container.css( 'visibility', 'hidden' ); 3174 3175 thumb.data = { 3176 width : Utils.parseValue( getStyle( 'width' ) ), 3177 height : Utils.parseValue( getStyle( 'height' ) ), 3178 order : i, 3179 src : src 3180 }; 3181 3182 // grab & reset size for smoother thumbnail loads 3183 if ( o.thumbFit && o.thumbCrop !== true ) { 3184 $container.css( { width: 'auto', height: 'auto' } ); 3185 } else { 3186 $container.css( { width: thumb.data.width, height: thumb.data.height } ); 3187 } 3188 3189 // load the thumbnail 3190 special = src.split(':'); 3191 3192 if ( special.length == 2 && special[0] in _video ) { 3193 3194 thumb.video = true; 3195 thumb.ready = true; 3196 3197 thumb.load( gif, { 3198 height: thumb.data.height, 3199 width: thumb.data.height*1.25 3200 }, onThumbLoad); 3201 3202 } else if ( optval == 'lazy' ) { 3203 3204 $container.addClass( 'lazy' ); 3205 3206 thumb.lazy = true; 3207 3208 thumb.load( gif, { 3209 height: thumb.data.height, 3210 width: thumb.data.width 3211 }); 3212 3213 } else { 3214 thumb.load( src, onThumbLoad ); 3215 } 3216 3217 // preload all images here 3218 if ( o.preload === 'all' ) { 3219 thumb.preload( data.image ); 3220 } 3221 3222 // create empty spans if thumbnails is set to 'empty' 3223 } else if ( data.iframe || optval === 'empty' || optval === 'numbers' ) { 3224 3225 thumb = { 3226 container: Utils.create( 'galleria-image' ), 3227 image: Utils.create( 'img', 'span' ), 3228 ready: true 3229 }; 3230 3231 // create numbered thumbnails 3232 if ( optval === 'numbers' ) { 3233 $( thumb.image ).text( i + 1 ); 3234 } 3235 3236 if ( data.iframe ) { 3237 $( thumb.image ).addClass( 'iframe' ); 3238 } 3239 3240 this.$( 'thumbnails' ).append( thumb.container ); 3241 3242 // we need to "fake" a loading delay before we append and trigger 3243 // 50+ should be enough 3244 3245 window.setTimeout( ( fake )( thumb.image, i, thumb.container ), 50 + ( i*20 ) ); 3246 3247 // create null object to silent errors 3248 } else { 3249 thumb = { 3250 container: null, 3251 image: null 3252 }; 3253 } 3254 3255 // add events for thumbnails 3256 // you can control the event type using thumb_event_type 3257 // we'll add the same event to the source if it's kept 3258 3259 $( thumb.container ).add( o.keepSource && o.linkSourceImages ? data.original : null ) 3260 .data('index', i).bind( o.thumbEventType, onThumbEvent ) 3261 .data('thumbload', onThumbLoad); 3262 3263 if (active === src) { 3264 $( thumb.container ).addClass( 'active' ); 3265 } 3266 3267 this._thumbnails.push( thumb ); 3268 } 3269 3270 thumbchunk = this._thumbnails.slice( chunk ); 3271 3272 return this; 3273 }, 3274 3275 /** 3276 Lazy-loads thumbnails. 3277 You can call this method to load lazy thumbnails at run time 3278 3279 @param {Array|Number} index Index or array of indexes of thumbnails to be loaded 3280 @param {Function} complete Callback that is called when all lazy thumbnails have been loaded 3281 3282 @returns Instance 3283 */ 3284 3285 lazyLoad: function( index, complete ) { 3286 3287 var arr = index.constructor == Array ? index : [ index ], 3288 self = this, 3289 thumbnails = this.$( 'thumbnails' ).children().filter(function() { 3290 return $(this).data('lazy-src'); 3291 }), 3292 loaded = 0; 3293 3294 $.each( arr, function(i, ind) { 3295 3296 if ( ind > self._thumbnails.length - 1 ) { 3297 return; 3298 } 3299 3300 var thumb = self._thumbnails[ ind ], 3301 data = thumb.data, 3302 special = data.src.split(':'), 3303 callback = function() { 3304 if ( ++loaded == arr.length && typeof complete == 'function' ) { 3305 complete.call( self ); 3306 } 3307 }, 3308 thumbload = $( thumb.container ).data( 'thumbload' ); 3309 if ( thumb.video ) { 3310 thumbload.call( self, thumb, callback ); 3311 } else { 3312 thumb.load( data.src , function( thumb ) { 3313 thumbload.call( self, thumb, callback ); 3314 }); 3315 } 3316 }); 3317 3318 return this; 3319 3320 }, 3321 3322 /** 3323 Lazy-loads thumbnails in chunks. 3324 This method automatcally chops up the loading process of many thumbnails into chunks 3325 3326 @param {Number} size Size of each chunk to be loaded 3327 @param {Number} [delay] Delay between each loads 3328 3329 @returns Instance 3330 */ 3331 3332 lazyLoadChunks: function( size, delay ) { 3333 3334 var len = this.getDataLength(), 3335 i = 0, 3336 n = 0, 3337 arr = [], 3338 temp = [], 3339 self = this; 3340
vendor: 6,369 bytes, lines 3341-3546
3341 delay = delay || 0; 3342 3343 for( ; i<len; i++ ) { 3344 temp.push(i); 3345 if ( ++n == size || i == len-1 ) { 3346 arr.push( temp ); 3347 n = 0; 3348 temp = []; 3349 } 3350 } 3351 3352 var init = function( wait ) { 3353 var a = arr.shift(); 3354 if ( a ) { 3355 window.setTimeout(function() { 3356 self.lazyLoad(a, function() { 3357 init( true ); 3358 }); 3359 }, ( delay && wait ) ? delay : 0 ); 3360 } 3361 }; 3362 3363 init( false ); 3364 3365 return this; 3366 3367 }, 3368 3369 // the internal _run method should be called after loading data into galleria 3370 // makes sure the gallery has proper measurements before postrun & ready 3371 _run : function() { 3372 3373 var self = this; 3374 3375 self._createThumbnails(); 3376 3377 // make sure we have a stageHeight && stageWidth 3378 3379 Utils.wait({ 3380 3381 timeout: 10000, 3382 3383 until: function() { 3384 3385 // Opera crap 3386 if ( Galleria.OPERA ) { 3387 self.$( 'stage' ).css( 'display', 'inline-block' ); 3388 } 3389 3390 self._stageWidth = self.$( 'stage' ).width(); 3391 self._stageHeight = self.$( 'stage' ).height(); 3392 3393 return( self._stageWidth && 3394 self._stageHeight > 50 ); // what is an acceptable height? 3395 }, 3396 3397 success: function() { 3398 3399 // save the instance 3400 _galleries.push( self ); 3401 3402 // postrun some stuff after the gallery is ready 3403 3404 // show counter 3405 Utils.show( self.get('counter') ); 3406 3407 // bind carousel nav 3408 if ( self._options.carousel ) { 3409 self._carousel.bindControls(); 3410 } 3411 3412 // start autoplay 3413 if ( self._options.autoplay ) { 3414 3415 self.pause(); 3416 3417 if ( typeof self._options.autoplay === 'number' ) { 3418 self._playtime = self._options.autoplay; 3419 } 3420 3421 self._playing = true; 3422 } 3423 // if second load, just do the show and return 3424 if ( self._firstrun ) { 3425 3426 if ( self._options.autoplay ) { 3427 self.trigger( Galleria.PLAY ); 3428 } 3429 3430 if ( typeof self._options.show === 'number' ) { 3431 self.show( self._options.show ); 3432 } 3433 return; 3434 } 3435 3436 self._firstrun = true; 3437 3438 // initialize the History plugin 3439 if ( Galleria.History ) { 3440 3441 // bind the show method 3442 Galleria.History.change(function( value ) { 3443 3444 // if ID is NaN, the user pressed back from the first image 3445 // return to previous address 3446 if ( isNaN( value ) ) { 3447 window.history.go(-1); 3448 3449 // else show the image 3450 } else { 3451 self.show( value, undef, true ); 3452 } 3453 }); 3454 } 3455 3456 self.trigger( Galleria.READY ); 3457 3458 // call the theme init method 3459 Galleria.theme.init.call( self, self._options ); 3460 3461 // Trigger Galleria.ready 3462 $.each( Galleria.ready.callbacks, function(i ,fn) { 3463 if ( typeof fn == 'function' ) { 3464 fn.call( self, self._options ); 3465 } 3466 }); 3467 3468 // call the extend option 3469 self._options.extend.call( self, self._options ); 3470 3471 // show the initial image 3472 // first test for permalinks in history 3473 if ( /^[0-9]{1,4}$/.test( HASH ) && Galleria.History ) { 3474 self.show( HASH, undef, true ); 3475 3476 } else if( self._data[ self._options.show ] ) { 3477 self.show( self._options.show ); 3478 } 3479 3480 // play trigger 3481 if ( self._options.autoplay ) { 3482 self.trigger( Galleria.PLAY ); 3483 } 3484 }, 3485 3486 error: function() { 3487 Galleria.raise('Stage width or height is too small to show the gallery. Traced measures: width:' + self._stageWidth + 'px, height: ' + self._stageHeight + 'px.', true); 3488 } 3489 3490 }); 3491 }, 3492 3493 /** 3494 Loads data into the gallery. 3495 You can call this method on an existing gallery to reload the gallery with new data. 3496 3497 @param {Array|string} [source] Optional JSON array of data or selector of where to find data in the document. 3498 Defaults to the Galleria target or dataSource option. 3499 3500 @param {string} [selector] Optional element selector of what elements to parse. 3501 Defaults to 'img'. 3502 3503 @param {Function} [config] Optional function to modify the data extraction proceedure from the selector. 3504 See the dataConfig option for more information. 3505 3506 @returns Instance 3507 */ 3508 3509 load : function( source, selector, config ) { 3510 3511 var self = this, 3512 o = this._options; 3513 3514 // empty the data array 3515 this._data = []; 3516 3517 // empty the thumbnails 3518 this._thumbnails = []; 3519 this.$('thumbnails').empty(); 3520 3521 // shorten the arguments 3522 if ( typeof selector === 'function' ) { 3523 config = selector; 3524 selector = null; 3525 } 3526 3527 // use the source set by target 3528 source = source || o.dataSource; 3529 3530 // use selector set by option 3531 selector = selector || o.dataSelector; 3532 3533 // use the dataConfig set by option 3534 config = config || o.dataConfig; 3535 3536 // if source is a true object, make it into an array 3537 if( /^function Object/.test( source.constructor ) ) { 3538 source = [source]; 3539 } 3540 3541 // check if the data is an array already 3542 if ( source.constructor === Array ) { 3543 if ( this.validate( source ) ) { 3544 this._data = source; 3545 } else { 3546 Galleria.raise( 'Load failed: JSON Array not val
3546id.' ); 3547 } 3548 } else { 3549 3550 // add .video and .iframe to the selector (1.2.7) 3551 selector += ',.video,.iframe'; 3552 3553 // loop through images and set data 3554 $( source ).find( selector ).each( function( i, elem ) { 3555 3556 elem = $( elem ); 3557 var data = {}, 3558 parent = elem.parent(), 3559 href = parent.attr( 'href' ), 3560 rel = parent.attr( 'rel' ); 3561 3562 if( href && ( elem[0].nodeName == 'IMG' || elem.hasClass('video') ) && _videoTest( href ) ) { 3563 data.video = href; 3564 } else if( href && elem.hasClass('iframe') ) { 3565 data.iframe = href; 3566 } else { 3567 data.image = data.big = href; 3568 } 3569 3570 if ( rel ) { 3571 data.big = rel; 3572 } 3573 3574 // alternative extraction from HTML5 data attribute, added in 1.2.7 3575 $.each( 'big title description link layer'.split(' '), function( i, val ) { 3576 if ( elem.data(val) ) { 3577 data[ val ] = elem.data(val); 3578 } 3579 }); 3580 3581 // mix default extractions with the hrefs and config 3582 // and push it into the data array 3583 self._data.push( $.extend({ 3584 3585 title: elem.attr('title') || '', 3586 thumb: elem.attr('src'), 3587 image: elem.attr('src'), 3588 big: elem.attr('src'), 3589 description: elem.attr('alt') || '', 3590 link: elem.attr('longdesc'), 3591 original: elem.get(0) // saved as a reference 3592 3593 }, data, config( elem ) ) ); 3594 3595 }); 3596 } 3597 3598 if ( typeof o.dataSort == 'function' ) { 3599 protoArray.sort.call( this._data, o.dataSort ); 3600 } else if ( o.dataSort == 'random' ) { 3601 this._data.sort( function() { 3602 return Math.round(Math.random())-0.5; 3603 }); 3604 } 3605 3606 3607 3608 // trigger the DATA event and return 3609 if ( this.getDataLength() ) { 3610 this._parseData().trigger( Galleria.DATA ); 3611 } 3612 return this; 3613 3614 }, 3615 3616 // make sure the data works properly 3617 _parseData : function() { 3618 3619 var self = this, 3620 current; 3621 3622 $.each( this._data, function( i, data ) { 3623 3624 current = self._data[ i ]; 3625 3626 // copy image as thumb if no thumb exists 3627 if ( 'thumb' in data === false ) { 3628 current.thumb = data.image; 3629 } 3630 // copy image as big image if no biggie exists 3631 if ( !'big' in data ) { 3632 current.big = data.image; 3633 } 3634 // parse video 3635 if ( 'video' in data ) { 3636 var result = _videoTest( data.video ); 3637 3638 if ( result ) { 3639 current.iframe = _video[ result.provider ].embed( result.id ) + (function() { 3640 3641 // add options 3642 if ( typeof self._options[ result.provider ] == 'object' ) { 3643 var str = '?', arr = []; 3644 $.each( self._options[ result.provider ], function( key, val ) { 3645 arr.push( key + '=' + val ); 3646 }); 3647 3648 // small youtube specifics, perhaps move to _video later 3649 if ( result.provider == 'youtube' ) { 3650 arr = ['wmode=opaque'].concat(arr); 3651 } 3652 return str + arr.join('&'); 3653 } 3654 return ''; 3655 }()); 3656 delete current.video; 3657 if( !('thumb' in current) || !current.thumb ) { 3658 current.thumb = result.provider+':'+result.id; 3659 } 3660 } 3661 } 3662 }); 3663 3664 return this; 3665 }, 3666 3667 /** 3668 Destroy the Galleria instance and recover the original content 3669 3670 @example this.destroy(); 3671 3672 @returns Instance 3673 */ 3674 3675 destroy : function() { 3676 this.$( 'target' ).data( 'galleria', null ); 3677 this.$( 'container' ).unbind( 'galleria' ); 3678 this.get( 'target' ).innerHTML = this._original.html; 3679 this.clearTimer(); 3680 Utils.removeFromArray( _instances, this ); 3681 Utils.removeFromArray( _galleries, this ); 3682 return this; 3683 }, 3684 3685 /**
3686 Adds and/or removes images from the gallery 3687 Works just like Array.splice 3688 https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice 3689 3690 @example this.splice( 2, 4 ); // removes 4 images after the second image 3691 3692 @returns Instance 3693 */ 3694 3695 splice : function() { 3696 var self = this, 3697 args = Utils.array( arguments ); 3698 window.setTimeout(function() { 3699 protoArray.splice.apply( self._data, args ); 3700 self._parseData()._createThumbnails(); 3701 },2); 3702 return self; 3703 }, 3704 3705 /** 3706 Append images to the gallery 3707 Works just like Array.push 3708 https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/push 3709 3710 @example this.push({ image: 'image1.jpg' }); // appends the image to the gallery 3711 3712 @returns Instance 3713 */ 3714 3715 push : function() { 3716 var self = this, 3717 args = Utils.array( arguments ); 3718 3719 if ( args.length == 1 && args[0].constructor == Array ) { 3720 args = args[0]; 3721 } 3722 3723 window.setTimeout(function() { 3724 protoArray.push.apply( self._data, args ); 3725 self._parseData()._createThumbnails( args ); 3726 },2); 3727 return self; 3728 }, 3729 3730 _getActive: function() { 3731 return this._controls.getActive(); 3732 }, 3733 3734 validate : function( data ) { 3735 // todo: validate a custom data array 3736 return true; 3737 }, 3738 3739 /** 3740 Bind any event to Galleria 3741 3742 @param {string} type The Event type to listen for 3743 @param {Function} fn The function to execute when the event is triggered 3744 3745 @example this.bind( 'image', function() { Galleria.log('image shown') }); 3746 3747 @returns Instance 3748 */ 3749 3750 bind : function(type, fn) { 3751 3752 // allow 'image' instead of Galleria.IMAGE 3753 type = _patchEvent( type ); 3754 3755 this.$( 'container' ).bind( type, this.proxy(fn) ); 3756 return this; 3757 }, 3758 3759 /** 3760 Unbind any event to Galleria 3761 3762 @param {string} type The Event type to forget 3763 3764 @returns Instance 3765 */ 3766 3767 unbind : function(type) { 3768 3769 type = _patchEvent( type ); 3770 3771 this.$( 'container' ).unbind( type ); 3772 return this; 3773 }, 3774 3775 /** 3776 Manually trigger a Galleria event 3777 3778 @param {string} type The Event to trigger 3779 3780 @returns Instance 3781 */ 3782 3783 trigger : function( type ) { 3784 3785 type = typeof type === 'object' ? 3786 $.extend( type, { scope: this } ) : 3787 { type: _patchEvent( type ), scope: this }; 3788 3789 this.$( 'container' ).trigger( type ); 3790 3791 return this; 3792 }, 3793 3794 /** 3795 Assign an "idle state" to any element. 3796 The idle state will be applied after a certain amount of idle time 3797 Useful to hide f.ex navigation when the gallery is inactive 3798 3799 @param {HTMLElement|string} elem The Dom node or selector to apply the idle state to 3800 @param {Object} styles the CSS styles to apply when in idle mode 3801 @param {Object} [from] the CSS styles to apply when in normal 3802 @param {Boolean} [hide] set to true if you want to hide it first 3803 3804 @example addIdleState( this.get('image-nav'), { opacity: 0 }); 3805 @example addIdleState( '.galleria-image-nav', { top: -200 }, true); 3806 3807 @returns Instance 3808 */ 3809 3810 addIdleState: function( elem, styles, from, hide ) { 3811 this._idle.add.apply( this._idle, Utils.array( arguments ) ); 3812 return this; 3813 }, 3814 3815 /** 3816 Removes any idle state previously set using addIdleState() 3817 3818 @param {HTMLElement|string} elem The Dom node or selector to remove the idle state from. 3819 3820 @returns Instance 3821 */ 3822 3823 removeIdleState: function( elem ) { 3824 this._idle.remove.apply( this._idle, Utils.array( arguments ) ); 3825 return this; 3826 }, 3827 3828 /** 3829 Force Galleria to enter idle mode. 3830 3831 @returns Instance 3832 */ 3833 3834 enterIdleMode: function() { 3835 this._idle.hide(); 3836 return this; 3837 }, 3838 3839 /** 3840 Force Galleria to exit idle mode. 3841 3842 @returns Instance 3843 */ 3844 3845 exitIdleMode: function() { 3846 this._idle.showAll(); 3847 return this; 3848 }, 3849 3850 /** 3851 Enter FullScreen mode 3852 3853 @param {Function} callback the function to be executed when the fullscreen mode is fully applied. 3854 3855 @returns Instance 3856 */ 3857 3858 enterFullscreen: function( callback ) { 3859 this._fullscreen.enter.apply( this, Utils.array( arguments ) ); 3860 return this; 3861 }, 3862 3863 /** 3864 Exits FullScreen mode 3865 3866 @param {Function} callback the function to be executed when the fullscreen mode is fully applied. 3867 3868 @returns Instance 3869 */ 3870 3871 exitFullscreen: function( callback ) { 3872 this._fullscreen.exit.apply( this, Utils.array( arguments ) ); 3873 return this; 3874 }, 3875 3876 /** 3877 Toggle FullScreen mode 3878 3879 @param {Function} callback the function to be executed when the fullscreen mode is fully applied or removed. 3880 3881 @returns Instance 3882 */ 3883 3884 toggleFullscreen: function( callback ) { 3885 this._fullscreen[ this.isFullscreen() ? 'exit' : 'enter'].apply( this, Utils.array( arguments ) ); 3886 return this; 3887 }, 3888 3889 /** 3890 Adds a tooltip to any element. 3891 You can also call this method with an object as argument with elemID:value pairs to apply tooltips to (see examples) 3892 3893 @param {HTMLElement} elem The DOM Node to attach the event to 3894 @param {string|Function} value The tooltip message. Can also be a function that returns a string. 3895 3896 @example this.bindTooltip( this.get('thumbnails'), 'My thumbnails'); 3897 @example this.bindTooltip( this.get('thumbnails'), function() { return 'My thumbs' }); 3898 @example this.bindTooltip( { image_nav: 'Navigation' }); 3899 3900 @returns Instance 3901 */ 3902 3903 bindTooltip: function( elem, value ) { 3904 this._tooltip.bind.apply( this._tooltip, Utils.array(arguments) ); 3905 return this; 3906 }, 3907 3908 /** 3909 Note: this method is deprecated. Use refreshTooltip() instead. 3910 3911 Redefine a tooltip. 3912 Use this if you want to re-apply a tooltip value to an already bound tooltip element. 3913 3914 @param {HTMLElement} elem The DOM Node to attach the event to 3915 @param {string|Function} value The tooltip message. Can also be a function that returns a string. 3916 3917 @returns Instance 3918 */ 3919 3920 defineTooltip: function( elem, value ) { 3921 this._tooltip.define.apply( this._tooltip, Utils.array(arguments) ); 3922 return this; 3923 }, 3924 3925 /** 3926 Refresh a tooltip value. 3927 Use this if you want to change the tooltip value at runtime, f.ex if you have a play/pause toggle. 3928 3929 @param {HTMLElement} elem The DOM Node that has a tooltip that should be refreshed 3930 3931 @returns Instance 3932 */ 3933 3934 refreshTooltip: function( elem ) { 3935 this._tooltip.show.apply( this._tooltip, Utils.array(arguments) ); 3936 return this; 3937 }, 3938 3939 /** 3940 Open a pre-designed lightbox with the currently active image. 3941 You can control some visuals using gallery options. 3942 3943 @returns Instance 3944 */ 3945 3946 openLightbox: function() { 3947 this._lightbox.show.apply( this._lightbox, Utils.array( arguments ) ); 3948 return this; 3949 }, 3950 3951 /** 3952 Close the lightbox. 3953 3954 @returns Instance 3955 */ 3956 3957 closeLightbox: function() { 3958 this._lightbox.hide.apply( this._lightbox, Utils.array( arguments ) ); 3959 return this; 3960 }, 3961 3962 /** 3963 Get the currently active image element. 3964 3965 @returns {HTMLElement} The image element 3966 */ 3967 3968 getActiveImage: function() { 3969 return this._getActive().image || undef; 3970 }, 3971 3972 /** 3973 Get the currently active thumbnail element. 3974 3975 @returns {HTMLElement} The thumbnail element 3976 */ 3977 3978 getActiveThumb: function() { 3979 return this._thumbnails[ this._active ].image || undef; 3980 }, 3981 3982 /** 3983 Get the mouse position relative to the gallery container 3984 3985 @param e The mouse event 3986 3987 @example 3988 3989var gallery = this; 3990$(document).mousemove(function(e) { 3991 console.log( gallery.getMousePosition(e).x ); 3992}); 3993 3994 @returns {Object} Object with x & y of the relative mouse postion 3995 */ 3996 3997 getMousePosition : function(e) { 3998 return { 3999 x: e.pageX - this.$( 'container' ).offset().left, 4000 y: e.pageY - this.$( 'container' ).offset().top 4001 }; 4002 }, 4003 4004 /** 4005 Adds a panning effect to the image 4006 4007 @param [img] The optional image element. If not specified it takes the currently active image 4008 4009 @returns Instance 4010 */ 4011 4012 addPan : function( img ) { 4013 4014 if ( this._options.imageCrop === false ) { 4015 return; 4016 } 4017 4018 img = $( img || this.getActiveImage() ); 4019 4020 // define some variables and methods 4021 var self = this, 4022 x = img.width() / 2,
vendor: 8,869 bytes, lines 4023-4331
4023 y = img.height() / 2, 4024 destX = parseInt( img.css( 'left' ), 10 ), 4025 destY = parseInt( img.css( 'top' ), 10 ), 4026 curX = destX || 0, 4027 curY = destY || 0, 4028 distX = 0, 4029 distY = 0, 4030 active = false, 4031 ts = Utils.timestamp(), 4032 cache = 0, 4033 move = 0, 4034 4035 // positions the image 4036 position = function( dist, cur, pos ) { 4037 if ( dist > 0 ) { 4038 move = Math.round( Math.max( dist * -1, Math.min( 0, cur ) ) ); 4039 if ( cache !== move ) { 4040 4041 cache = move; 4042 4043 if ( IE === 8 ) { // scroll is faster for IE 4044 img.parent()[ 'scroll' + pos ]( move * -1 ); 4045 } else { 4046 var css = {}; 4047 css[ pos.toLowerCase() ] = move; 4048 img.css(css); 4049 } 4050 } 4051 } 4052 }, 4053 4054 // calculates mouse position after 50ms 4055 calculate = function(e) { 4056 if (Utils.timestamp() - ts < 50) { 4057 return; 4058 } 4059 active = true; 4060 x = self.getMousePosition(e).x; 4061 y = self.getMousePosition(e).y; 4062 }, 4063 4064 // the main loop to check 4065 loop = function(e) { 4066 4067 if (!active) { 4068 return; 4069 } 4070 4071 distX = img.width() - self._stageWidth; 4072 distY = img.height() - self._stageHeight; 4073 destX = x / self._stageWidth * distX * -1; 4074 destY = y / self._stageHeight * distY * -1; 4075 curX += ( destX - curX ) / self._options.imagePanSmoothness; 4076 curY += ( destY - curY ) / self._options.imagePanSmoothness; 4077 4078 position( distY, curY, 'Top' ); 4079 position( distX, curX, 'Left' ); 4080 4081 }; 4082 4083 // we need to use scroll in IE8 to speed things up 4084 if ( IE === 8 ) { 4085 4086 img.parent().scrollTop( curY * -1 ).scrollLeft( curX * -1 ); 4087 img.css({ 4088 top: 0, 4089 left: 0 4090 }); 4091 4092 } 4093 4094 // unbind and bind event 4095 this.$( 'stage' ).unbind( 'mousemove', calculate ).bind( 'mousemove', calculate ); 4096 4097 // loop the loop 4098 this.addTimer( 'pan' + self._id, loop, 50, true); 4099 4100 return this; 4101 }, 4102 4103 /** 4104 Brings the scope into any callback 4105 4106 @param fn The callback to bring the scope into 4107 @param [scope] Optional scope to bring 4108 4109 @example $('#fullscreen').click( this.proxy(function() { this.enterFullscreen(); }) ) 4110 4111 @returns {Function} Return the callback with the gallery scope 4112 */ 4113 4114 proxy : function( fn, scope ) { 4115 if ( typeof fn !== 'function' ) { 4116 return F; 4117 } 4118 scope = scope || this; 4119 return function() { 4120 return fn.apply( scope, Utils.array( arguments ) ); 4121 }; 4122 }, 4123 4124 /** 4125 Removes the panning effect set by addPan() 4126 4127 @returns Instance 4128 */ 4129 4130 removePan: function() { 4131 4132 // todo: doublecheck IE8 4133 4134 this.$( 'stage' ).unbind( 'mousemove' ); 4135 4136 this.clearTimer( 'pan' + this._id ); 4137 4138 return this; 4139 }, 4140 4141 /** 4142 Adds an element to the Galleria DOM array. 4143 When you add an element here, you can access it using element ID in many API calls 4144 4145 @param {string} id The element ID you wish to use. You can add many elements by adding more arguments. 4146 4147 @example addElement('mybutton'); 4148 @example addElement('mybutton','mylink'); 4149 4150 @returns Instance 4151 */ 4152 4153 addElement : function( id ) { 4154 4155 var dom = this._dom; 4156 4157 $.each( Utils.array(arguments), function( i, blueprint ) { 4158 dom[ blueprint ] = Utils.create( 'galleria-' + blueprint ); 4159 }); 4160 4161 return this; 4162 }, 4163 4164 /** 4165 Attach keyboard events to Galleria 4166 4167 @param {Object} map The map object of events. 4168 Possible keys are 'UP', 'DOWN', 'LEFT', 'RIGHT', 'RETURN', 'ESCAPE', 'BACKSPACE', and 'SPACE'. 4169 4170 @example 4171 4172this.attachKeyboard({ 4173 right: this.next, 4174 left: this.prev, 4175 up: function() { 4176 console.log( 'up key pressed' ) 4177 } 4178}); 4179 4180 @returns Instance 4181 */ 4182 4183 attachKeyboard : function( map ) { 4184 this._keyboard.attach.apply( this._keyboard, Utils.array( arguments ) ); 4185 return this; 4186 }, 4187 4188 /** 4189 Detach all keyboard events to Galleria 4190 4191 @returns Instance 4192 */ 4193 4194 detachKeyboard : function() { 4195 this._keyboard.detach.apply( this._keyboard, Utils.array( arguments ) ); 4196 return this; 4197 }, 4198 4199 /** 4200 Fast helper for appending galleria elements that you added using addElement() 4201 4202 @param {string} parentID The parent element ID where the element will be appended 4203 @param {string} childID the element ID that should be appended 4204 4205 @example this.addElement('myElement'); 4206 this.appendChild( 'info', 'myElement' ); 4207 4208 @returns Instance 4209 */ 4210 4211 appendChild : function( parentID, childID ) { 4212 this.$( parentID ).append( this.get( childID ) || childID ); 4213 return this; 4214 }, 4215 4216 /** 4217 Fast helper for prepending galleria elements that you added using addElement() 4218 4219 @param {string} parentID The parent element ID where the element will be prepended 4220 @param {string} childID the element ID that should be prepended 4221 4222 @example 4223 4224this.addElement('myElement'); 4225this.prependChild( 'info', 'myElement' ); 4226 4227 @returns Instance 4228 */ 4229 4230 prependChild : function( parentID, childID ) { 4231 this.$( parentID ).prepend( this.get( childID ) || childID ); 4232 return this; 4233 }, 4234 4235 /** 4236 Remove an element by blueprint 4237 4238 @param {string} elemID The element to be removed. 4239 You can remove multiple elements by adding arguments. 4240 4241 @returns Instance 4242 */ 4243 4244 remove : function( elemID ) { 4245 this.$( Utils.array( arguments ).join(',') ).remove(); 4246 return this; 4247 }, 4248 4249 // a fast helper for building dom structures 4250 // leave this out of the API for now 4251 4252 append : function( data ) { 4253 var i, j; 4254 for( i in data ) { 4255 if ( data.hasOwnProperty( i ) ) { 4256 if ( data[i].constructor === Array ) { 4257 for( j = 0; data[i][j]; j++ ) { 4258 this.appendChild( i, data[i][j] ); 4259 } 4260 } else { 4261 this.appendChild( i, data[i] ); 4262 } 4263 } 4264 } 4265 return this; 4266 }, 4267 4268 // an internal helper for scaling according to options 4269 _scaleImage : function( image, options ) { 4270 4271 image = image || this._controls.getActive(); 4272 4273 // janpub (JH) fix: 4274 // image might be unselected yet 4275 // e.g. when external logics rescales the gallery on window resize events 4276 if( !image ) { 4277 return; 4278 } 4279 4280 var self = this, 4281 4282 complete, 4283 4284 scaleLayer = function( img ) { 4285 $( img.container ).children(':first').css({ 4286 top: Math.max(0, Utils.parseValue( img.image.style.top )), 4287 left: Math.max(0, Utils.parseValue( img.image.style.left )), 4288 width: Utils.parseValue( img.image.width ), 4289 height: Utils.parseValue( img.image.height ) 4290 }); 4291 }; 4292 4293 options = $.extend({ 4294 width: this._stageWidth, 4295 height: this._stageHeight, 4296 crop: this._options.imageCrop, 4297 max: this._options.maxScaleRatio, 4298 min: this._options.minScaleRatio, 4299 margin: this._options.imageMargin, 4300 position: this._options.imagePosition, 4301 iframelimit: this._options.maxVideoSize 4302 }, options ); 4303 4304 if ( this._options.layerFollow && this._options.imageCrop !== true ) { 4305 4306 if ( typeof options.complete == 'function' ) { 4307 complete = options.complete; 4308 options.complete = function() { 4309 complete.call( image, image ); 4310 scaleLayer( image ); 4311 }; 4312 } else { 4313 options.complete = scaleLayer; 4314 } 4315 4316 } else { 4317 $( image.container ).children(':first').css({ top: 0, left: 0 }); 4318 } 4319 4320 image.scale( options ); 4321 return this; 4322 }, 4323 4324 /** 4325 Updates the carousel, 4326 useful if you resize the gallery and want to re-check if the carousel nav is needed. 4327 4328 @returns Instance 4329 */ 4330 4331 updateCarousel : function() {
4332 this._carousel.update(); 4333 return this; 4334 }, 4335 4336 /** 4337 Resize the entire gallery container 4338 4339 @param {Object} [measures] Optional object with width/height specified 4340 @param {Function} [complete] The callback to be called when the scaling is complete 4341 4342 @returns Instance 4343 */ 4344 4345 resize : function( measures, complete ) { 4346 4347 if ( typeof measures == 'function' ) { 4348 complete = measures; 4349 measures = undef; 4350 } 4351 4352 measures = $.extend( { width:0, height:0 }, measures ); 4353 4354 var self = this, 4355 $container = this.$( 'container' ); 4356 4357 $.each( measures, function( m, val ) { 4358 if ( !val ) { 4359 $container[ m ]( 'auto' ); 4360 measures[ m ] = self._getWH()[ m ]; 4361 } 4362 }); 4363 4364 $.each( measures, function( m, val ) { 4365 $container[ m ]( val ); 4366 }); 4367 4368 return this.rescale( complete ); 4369 4370 }, 4371 4372 /** 4373 Rescales the gallery 4374 4375 @param {number} width The target width 4376 @param {number} height The target height 4377 @param {Function} complete The callback to be called when the scaling is complete 4378 4379 @returns Instance 4380 */ 4381 4382 rescale : function( width, height, complete ) { 4383 4384 var self = this; 4385 4386 // allow rescale(fn) 4387 if ( typeof width === 'function' ) { 4388 complete = width; 4389 width = undef; 4390 } 4391 4392 var scale = function() { 4393 4394 // set stagewidth 4395 self._stageWidth = width || self.$( 'stage' ).width(); 4396 self._stageHeight = height || self.$( 'stage' ).height(); 4397 4398 // scale the active image 4399 self._scaleImage(); 4400 4401 if ( self._options.carousel ) { 4402 self.updateCarousel(); 4403 } 4404 4405 self.trigger( Galleria.RESCALE ); 4406 4407 if ( typeof complete === 'function' ) { 4408 complete.call( self ); 4409 } 4410 }; 4411 4412 scale.call( self ); 4413 4414 return this; 4415 }, 4416 4417 /** 4418 Refreshes the gallery. 4419 Useful if you change image options at runtime and want to apply the changes to the active image. 4420 4421 @returns Instance 4422 */ 4423 4424 refreshImage : function() { 4425 this._scaleImage(); 4426 if ( this._options.imagePan ) { 4427 this.addPan(); 4428 } 4429 return this; 4430 }, 4431 4432 /** 4433 Shows an image by index 4434 4435 @param {number|boolean} index The index to show 4436 @param {Boolean} rewind A boolean that should be true if you want the transition to go back 4437 4438 @returns Instance 4439 */ 4440 4441 show : function( index, rewind, _history ) { 4442 4443 // do nothing queue is long || index is false || queue is false and transition is in progress 4444 if ( this._queue.length > 3 || index === false || ( !this._options.queue && this._queue.stalled ) ) { 4445 return; 4446 } 4447 4448 index = Math.max( 0, Math.min( parseInt( index, 10 ), this.getDataLength() - 1 ) ); 4449 4450 rewind = typeof rewind !== 'undefined' ? !!rewind : index < this.getIndex(); 4451 4452 _history = _history || false; 4453 4454 // do the history thing and return 4455 if ( !_history && Galleria.History ) { 4456 Galleria.History.set( index.toString() ); 4457 return; 4458 } 4459 4460 this._active = index; 4461 4462 protoArray.push.call( this._queue, { 4463 index : index, 4464 rewind : rewind 4465 }); 4466 if ( !this._queue.stalled ) { 4467 this._show(); 4468 } 4469 4470 return this; 4471 }, 4472 4473 // the internal _show method does the actual showing 4474 _show : function() { 4475 4476 // shortcuts 4477 var self = this, 4478 queue = this._queue[ 0 ], 4479 data = this.getData( queue.index ); 4480 4481 if ( !data ) { 4482 return; 4483 } 4484 4485 var src = data.iframe || ( this.isFullscreen() && 'big' in data ? data.big : data.image ), // use big image if fullscreen mode 4486 active = this._controls.getActive(), 4487 next = this._controls.getNext(), 4488 cached = next.isCached( src ), 4489 thumb = this._thumbnails[ queue.index ], 4490 mousetrigger = function() { 4491 $( next.image ).trigger( 'mouseup' ); 4492 }; 4493 4494 // to be fired when loading & transition is complete: 4495 var complete = (function( data, next, active, queue, thumb ) { 4496 4497 return function() { 4498 4499 var win; 4500 4501 _transitions.active = false;
4502 4503 // optimize quality 4504 Utils.toggleQuality( next.image, self._options.imageQuality ); 4505 4506 // remove old layer 4507 self._layers[ self._controls.active ].innerHTML = ''; 4508 4509 // swap 4510 $( active.container ).css({ 4511 zIndex: 0, 4512 opacity: 0 4513 }).show(); 4514 4515 if( active.isIframe ) { 4516 $( active.container ).find( 'iframe' ).remove(); 4517 } 4518 4519 self.$('container').toggleClass('iframe', !!data.iframe); 4520 4521 $( next.container ).css({ 4522 zIndex: 1, 4523 left: 0, 4524 top: 0 4525 }).show(); 4526 4527 self._controls.swap(); 4528 4529 // add pan according to option 4530 if ( self._options.imagePan ) { 4531 self.addPan( next.image ); 4532 } 4533 4534 // make the image link or add lightbox 4535 // link takes precedence over lightbox if both are detected 4536 if ( data.link || self._options.lightbox || self._options.clicknext ) { 4537 4538 $( next.image ).css({ 4539 cursor: 'pointer' 4540 }).bind( 'mouseup', function( e ) { 4541 4542 // non-left click 4543 if ( typeof e.which == 'number' && e.which > 1 ) { 4544 return; 4545 } 4546 4547 // clicknext 4548 if ( self._options.clicknext && !Galleria.TOUCH ) { 4549 if ( self._options.pauseOnInteraction ) { 4550 self.pause(); 4551 } 4552 self.next(); 4553 return; 4554 } 4555 4556 // popup link 4557 if ( data.link ) { 4558 if ( self._options.popupLinks ) { 4559 win = window.open( data.link, '_blank' ); 4560 } else { 4561 window.location.href = data.link; 4562 } 4563 return; 4564 } 4565 4566 if ( self._options.lightbox ) { 4567 self.openLightbox(); 4568 } 4569 4570 }); 4571 } 4572 4573 // check if we are playing 4574 self._playCheck(); 4575 4576 // trigger IMAGE event 4577 self.trigger({ 4578 type: Galleria.IMAGE, 4579 index: queue.index, 4580 imageTarget: next.image, 4581 thumbTarget: thumb.image, 4582 galleriaData: data 4583 }); 4584 4585 // remove the queued image 4586 protoArray.shift.call( self._queue ); 4587 4588 // remove stalled 4589 self._queue.stalled = false; 4590 4591 // if we still have images in the queue, show it 4592 if ( self._queue.length ) { 4593 self._show(); 4594 } 4595 4596 }; 4597 }( data, next, active, queue, thumb )); 4598 4599 // let the carousel follow 4600 if ( this._options.carousel && this._options.carouselFollow ) { 4601 this._carousel.follow( queue.index ); 4602 } 4603 4604 // preload images 4605 if ( this._options.preload ) { 4606 4607 var p, i, 4608 n = this.getNext(), 4609 ndata; 4610 4611 try { 4612 for ( i = this._options.preload; i > 0; i-- ) { 4613 p = new Galleria.Picture(); 4614 ndata = self.getData( n ); 4615 p.preload( this.isFullscreen() && 'big' in ndata ? ndata.big : ndata.image ); 4616 n = self.getNext( n ); 4617 } 4618 } catch(e) {} 4619 } 4620 4621 // show the next image, just in case 4622 Utils.show( next.container ); 4623 4624 next.isIframe = !!data.iframe; 4625 4626 // add active classes 4627 $( self._thumbnails[ queue.index ].container ) 4628 .addClass( 'active' ) 4629 .siblings( '.active' ) 4630 .removeClass( 'active' ); 4631 4632 // trigger the LOADSTART event 4633 self.trigger( { 4634 type: Galleria.LOADSTART, 4635 cached: cached,
vendor: 11,716 bytes, lines 4636-5084
4636 index: queue.index, 4637 rewind: queue.rewind, 4638 imageTarget: next.image, 4639 thumbTarget: thumb.image, 4640 galleriaData: data 4641 }); 4642 4643 // stall the queue 4644 self._queue.stalled = true; 4645 4646 // begin loading the next image 4647 next.load( src, function( next ) { 4648 4649 // add layer HTML 4650 var layer = $( self._layers[ 1-self._controls.active ] ).html( data.layer || '' ).hide(); 4651 4652 self._scaleImage( next, { 4653 4654 complete: function( next ) { 4655 4656 // toggle low quality for IE 4657 if ( 'image' in active ) { 4658 Utils.toggleQuality( active.image, false ); 4659 } 4660 Utils.toggleQuality( next.image, false ); 4661 4662 // remove the image panning, if applied 4663 // TODO: rethink if this is necessary 4664 self.removePan(); 4665 4666 // set the captions and counter 4667 self.setInfo( queue.index ); 4668 self.setCounter( queue.index ); 4669 4670 // show the layer now 4671 if ( data.layer ) { 4672 layer.show(); 4673 // inherit click events set on image 4674 if ( data.link || self._options.lightbox || self._options.clicknext ) { 4675 layer.css( 'cursor', 'pointer' ).unbind( 'mouseup' ).mouseup( mousetrigger ); 4676 } 4677 } 4678 4679 var transition = self._options.transition; 4680 4681 // can JavaScript loop through objects in order? yes. 4682 $.each({ 4683 initial: active.image === null, 4684 touch: Galleria.TOUCH, 4685 fullscreen: self.isFullscreen() 4686 }, function( type, arg ) { 4687 if ( arg && self._options[ type + 'Transition' ] !== undef ) { 4688 transition = self._options[ type + 'Transition' ]; 4689 return false; 4690 } 4691 }); 4692 4693 // validate the transition 4694 if ( transition in _transitions.effects === false ) { 4695 complete(); 4696 } else { 4697 var params = { 4698 prev: active.container, 4699 next: next.container, 4700 rewind: queue.rewind, 4701 speed: self._options.transitionSpeed || 400 4702 }; 4703 4704 _transitions.active = true; 4705 4706 // call the transition function and send some stuff 4707 _transitions.init.call( self, transition, params, complete ); 4708 4709 } 4710 4711 // trigger the LOADFINISH event 4712 self.trigger({ 4713 type: Galleria.LOADFINISH, 4714 cached: cached, 4715 index: queue.index, 4716 rewind: queue.rewind, 4717 imageTarget: next.image, 4718 thumbTarget: self._thumbnails[ queue.index ].image, 4719 galleriaData: self.getData( queue.index ) 4720 }); 4721 4722 4723 } 4724 }); 4725 }); 4726 }, 4727 4728 /** 4729 Gets the next index 4730 4731 @param {number} [base] Optional starting point 4732 4733 @returns {number} the next index, or the first if you are at the first (looping) 4734 */ 4735 4736 getNext : function( base ) { 4737 base = typeof base === 'number' ? base : this.getIndex(); 4738 return base === this.getDataLength() - 1 ? 0 : base + 1; 4739 }, 4740 4741 /** 4742 Gets the previous index 4743 4744 @param {number} [base] Optional starting point 4745 4746 @returns {number} the previous index, or the last if you are at the first (looping) 4747 */ 4748 4749 getPrev : function( base ) { 4750 base = typeof base === 'number' ? base : this.getIndex(); 4751 return base === 0 ? this.getDataLength() - 1 : base - 1; 4752 }, 4753 4754 /** 4755 Shows the next image in line 4756 4757 @returns Instance 4758 */ 4759 4760 next : function() { 4761 if ( this.getDataLength() > 1 ) { 4762 this.show( this.getNext(), false ); 4763 } 4764 return this; 4765 }, 4766 4767 /** 4768 Shows the previous image in line 4769 4770 @returns Instance 4771 */ 4772 4773 prev : function() { 4774 if ( this.getDataLength() > 1 ) { 4775 this.show( this.getPrev(), true ); 4776 } 4777 return this; 4778 }, 4779 4780 /** 4781 Retrieve a DOM element by element ID 4782 4783 @param {string} elemId The delement ID to fetch 4784 4785 @returns {HTMLElement} The elements DOM node or null if not found. 4786 */ 4787 4788 get : function( elemId ) { 4789 return elemId in this._dom ? this._dom[ elemId ] : null; 4790 }, 4791 4792 /** 4793 Retrieve a data object 4794 4795 @param {number} index The data index to retrieve. 4796 If no index specified it will take the currently active image 4797 4798 @returns {Object} The data object 4799 */ 4800 4801 getData : function( index ) { 4802 return index in this._data ? 4803 this._data[ index ] : this._data[ this._active ]; 4804 }, 4805 4806 /** 4807 Retrieve the number of data items 4808 4809 @returns {number} The data length 4810 */ 4811 getDataLength : function() { 4812 return this._data.length; 4813 }, 4814 4815 /** 4816 Retrieve the currently active index 4817 4818 @returns {number|boolean} The active index or false if none found 4819 */ 4820 4821 getIndex : function() { 4822 return typeof this._active === 'number' ? this._active : false; 4823 }, 4824 4825 /** 4826 Retrieve the stage height 4827 4828 @returns {number} The stage height 4829 */ 4830 4831 getStageHeight : function() { 4832 return this._stageHeight; 4833 }, 4834 4835 /** 4836 Retrieve the stage width 4837 4838 @returns {number} The stage width 4839 */ 4840 4841 getStageWidth : function() { 4842 return this._stageWidth; 4843 }, 4844 4845 /** 4846 Retrieve the option 4847 4848 @param {string} key The option key to retrieve. If no key specified it will return all options in an object. 4849 4850 @returns option or options 4851 */ 4852 4853 getOptions : function( key ) { 4854 return typeof key === 'undefined' ? this._options : this._options[ key ]; 4855 }, 4856 4857 /** 4858 Set options to the instance. 4859 You can set options using a key & value argument or a single object argument (see examples) 4860 4861 @param {string} key The option key 4862 @param {string} value the the options value 4863 4864 @example setOptions( 'autoplay', true ) 4865 @example setOptions({ autoplay: true }); 4866 4867 @returns Instance 4868 */ 4869 4870 setOptions : function( key, value ) { 4871 if ( typeof key === 'object' ) { 4872 $.extend( this._options, key ); 4873 } else { 4874 this._options[ key ] = value; 4875 } 4876 return this; 4877 }, 4878 4879 /** 4880 Starts playing the slideshow 4881 4882 @param {number} delay Sets the slideshow interval in milliseconds. 4883 If you set it once, you can just call play() and get the same interval the next time. 4884 4885 @returns Instance 4886 */ 4887 4888 play : function( delay ) { 4889 4890 this._playing = true; 4891 4892 this._playtime = delay || this._playtime; 4893 4894 this._playCheck(); 4895 4896 this.trigger( Galleria.PLAY ); 4897 4898 return this; 4899 }, 4900 4901 /** 4902 Stops the slideshow if currently playing 4903 4904 @returns Instance 4905 */ 4906 4907 pause : function() { 4908 4909 this._playing = false; 4910 4911 this.trigger( Galleria.PAUSE ); 4912 4913 return this; 4914 }, 4915 4916 /** 4917 Toggle between play and pause events. 4918 4919 @param {number} delay Sets the slideshow interval in milliseconds. 4920 4921 @returns Instance 4922 */ 4923 4924 playToggle : function( delay ) { 4925 return ( this._playing ) ? this.pause() : this.play( delay ); 4926 }, 4927 4928 /** 4929 Checks if the gallery is currently playing 4930 4931 @returns {Boolean} 4932 */ 4933 4934 isPlaying : function() { 4935 return this._playing; 4936 }, 4937 4938 /** 4939 Checks if the gallery is currently in fullscreen mode 4940 4941 @returns {Boolean} 4942 */ 4943 4944 isFullscreen : function() { 4945 return this._fullscreen.active; 4946 }, 4947 4948 _playCheck : function() { 4949 var self = this, 4950 played = 0, 4951 interval = 20, 4952 now = Utils.timestamp(), 4953 timer_id = 'play' + this._id; 4954 4955 if ( this._playing ) { 4956 4957 this.clearTimer( timer_id ); 4958 4959 var fn = function() { 4960 4961 played = Utils.timestamp() - now; 4962 if ( played >= self._playtime && self._playing ) { 4963 self.clearTimer( timer_id ); 4964 self.next(); 4965 return; 4966 } 4967 if ( self._playing ) { 4968 4969 // trigger the PROGRESS event 4970 self.trigger({ 4971 type: Galleria.PROGRESS, 4972 percent: Math.ceil( played / self._playtime * 100 ), 4973 seconds: Math.floor( played / 1000 ), 4974 milliseconds: played 4975 }); 4976 4977 self.addTimer( timer_id, fn, interval ); 4978 } 4979 }; 4980 self.addTimer( timer_id, fn, interval ); 4981 } 4982 }, 4983 4984 /** 4985 Modify the slideshow delay 4986 4987 @param {number} delay the number of milliseconds between slides, 4988 4989 @returns Instance 4990 */ 4991 4992 setPlaytime: function( delay ) { 4993 this._playtime = delay; 4994 return this; 4995 }, 4996 4997 setIndex: function( val ) { 4998 this._active = val; 4999 return this; 5000 }, 5001 5002 /** 5003 Manually modify the counter 5004 5005 @param {number} [index] Optional data index to fectch, 5006 if no index found it assumes the currently active index 5007 5008 @returns Instance 5009 */ 5010 5011 setCounter: function( index ) { 5012 5013 if ( typeof index === 'number' ) { 5014 index++; 5015 } else if ( typeof index === 'undefined' ) { 5016 index = this.getIndex()+1; 5017 } 5018 5019 this.get( 'current' ).innerHTML = index; 5020 5021 if ( IE ) { // weird IE bug 5022 5023 var count = this.$( 'counter' ), 5024 opacity = count.css( 'opacity' ); 5025 5026 if ( parseInt( opacity, 10 ) === 1) { 5027 Utils.removeAlpha( count[0] ); 5028 } else { 5029 this.$( 'counter' ).css( 'opacity', opacity ); 5030 } 5031 5032 } 5033 5034 return this; 5035 }, 5036 5037 /** 5038 Manually set captions 5039 5040 @param {number} [index] Optional data index to fectch and apply as caption, 5041 if no index found it assumes the currently active index 5042 5043 @returns Instance 5044 */ 5045 5046 setInfo : function( index ) { 5047 5048 var self = this, 5049 data = this.getData( index ); 5050 5051 $.each( ['title','description'], function( i, type ) { 5052 5053 var elem = self.$( 'info-' + type ); 5054 5055 if ( !!data[type] ) { 5056 elem[ data[ type ].length ? 'show' : 'hide' ]().html( data[ type ] ); 5057 } else { 5058 elem.empty().hide(); 5059 } 5060 }); 5061 5062 return this; 5063 }, 5064 5065 /** 5066 Checks if the data contains any captions 5067 5068 @param {number} [index] Optional data index to fectch, 5069 if no index found it assumes the currently active index. 5070 5071 @returns {boolean} 5072 */ 5073 5074 hasInfo : function( index ) { 5075 5076 var check = 'title description'.split(' '), 5077 i; 5078 5079 for ( i = 0; check[i]; i++ ) { 5080 if ( !!this.getData( index )[ check[i] ] ) { 5081 return true; 5082 } 5083 } 5084 return false;
5085 5086 }, 5087 5088 jQuery : function( str ) { 5089 5090 var self = this, 5091 ret = []; 5092 5093 $.each( str.split(','), function( i, elemId ) { 5094 elemId = $.trim( elemId ); 5095 5096 if ( self.get( elemId ) ) { 5097 ret.push( elemId ); 5098 } 5099 }); 5100 5101 var jQ = $( self.get( ret.shift() ) ); 5102 5103 $.each( ret, function( i, elemId ) { 5104 jQ = jQ.add( self.get( elemId ) ); 5105 }); 5106 5107 return jQ; 5108 5109 }, 5110 5111 /** 5112 Converts element IDs into a jQuery collection 5113 You can call for multiple IDs separated with commas. 5114 5115 @param {string} str One or more element IDs (comma-separated) 5116 5117 @returns jQuery 5118 5119 @example this.$('info,container').hide(); 5120 */ 5121 5122 $ : function( str ) { 5123 return this.jQuery.apply( this, Utils.array( arguments ) ); 5124 } 5125 5126}; 5127 5128// End of Galleria prototype 5129 5130// Add events as static variables 5131$.each( _events, function( i, ev ) { 5132 5133 // legacy events 5134 var type = /_/.test( ev ) ? ev.replace( /_/g, '' ) : ev; 5135 5136 Galleria[ ev.toUpperCase() ] = 'galleria.'+type; 5137 5138} ); 5139 5140$.extend( Galleria, { 5141 5142 // Browser helpers 5143 IE9: IE === 9, 5144 IE8: IE === 8, 5145 IE7: IE === 7, 5146 IE6: IE === 6, 5147 IE: IE, 5148 WEBKIT: /webkit/.test( NAV ), 5149 CHROME: /chrome/.test( NAV ), 5150 SAFARI: /safari/.test( NAV ) && !(/chrome/.test( NAV )), 5151 QUIRK: ( IE && doc.compatMode && doc.compatMode === "BackCompat" ), 5152 MAC: /mac/.test( navigator.platform.toLowerCase() ), 5153 OPERA: !!window.opera, 5154 IPHONE: /iphone/.test( NAV ), 5155 IPAD: /ipad/.test( NAV ), 5156 ANDROID: /android/.test( NAV ), 5157 TOUCH: ('ontouchstart' in doc) 5158 5159}); 5160 5161// Galleria static methods 5162 5163/** 5164 Adds a theme that you can use for your Gallery 5165 5166 @param {Object} theme Object that should contain all your theme settings. 5167 <ul> 5168 <li>name - name of the theme</li> 5169 <li>author - name of the author</li> 5170 <li>css - css file name (not path)</li> 5171 <li>defaults - default options to apply, including theme-specific options</li> 5172 <li>init - the init function</li> 5173 </ul> 5174 5175 @returns {Object} theme 5176*/ 5177 5178Galleria.addTheme = function( theme ) { 5179 5180 // make sure we have a name 5181 if ( !theme.name ) { 5182 Galleria.raise('No theme name specified'); 5183 } 5184 5185 if ( typeof theme.defaults !== 'object' ) { 5186 theme.defaults = {}; 5187 } else { 5188 theme.defaults = _legacyOptions( theme.defaults ); 5189 } 5190 5191 var css = false, 5192 reg; 5193 5194 if ( typeof theme.css === 'string' ) { 5195 5196 // look for manually added CSS 5197 $('link').each(function( i, link ) { 5198 reg = new RegExp( theme.css ); 5199 if ( reg.test( link.href ) ) { 5200 5201 // we found the css 5202 css = true; 5203 5204 // the themeload trigger 5205 _themeLoad( theme ); 5206 5207 return false; 5208 } 5209 }); 5210 5211 // else look for the absolute path and load the CSS dynamic 5212 if ( !css ) { 5213 5214 $('script').each(function( i, script ) { 5215 5216 // look for the theme script 5217 reg = new RegExp( 'galleria\\.' + theme.name.toLowerCase() + '\\.' ); 5218 if( reg.test( script.src )) { 5219 5220 // we have a match 5221 css = script.src.replace(/[^\/]*$/, '') + theme.css; 5222 5223 window.setTimeout(function() { 5224 Utils.loadCSS( css, 'galleria-theme', function() { 5225 5226 // the themeload trigger 5227 _themeLoad( theme ); 5228 5229 }); 5230 }, 1); 5231 5232 } 5233 }); 5234 } 5235 5236 if ( !css ) { 5237 Galleria.raise('No theme CSS loaded'); 5238 } 5239 } else { 5240 5241 // pass 5242 _themeLoad( theme ); 5243 } 5244 return theme; 5245}; 5246 5247/** 5248 loadTheme loads a theme js file and attaches a load event to Galleria 5249 5250 @param {string} src The relative path to the theme source file 5251 5252 @param {Object} [options] Optional options you want to apply 5253 5254 @returns Galleria 5255*/ 5256 5257Galleria.loadTheme = function( src, options ) { 5258 5259 // Don't load if theme is already loaded 5260 if( $('script').filter(function() { return $(this).attr('src') == src; }).length ) { 5261 return; 5262 } 5263 5264 var loaded = false, 5265 err; 5266 5267 // start listening for the timeout onload 5268 $( window ).load( function() { 5269 if ( !loaded ) { 5270 // give it another 20 seconds 5271 err = window.setTimeout(function() { 5272 if ( !loaded && !Galleria.theme ) { 5273 Galleria.raise( "Galleria had problems loading theme at " + src + ". Please check theme path or load manually.", true ); 5274 } 5275 }, 20000); 5276 } 5277 }); 5278 5279 // first clear the current theme, if exists 5280 Galleria.unloadTheme(); 5281 5282 // load the theme 5283 Utils.loadScript( src, function() { 5284 loaded = true; 5285 window.clearTimeout( err ); 5286 }); 5287 5288 return Galleria; 5289}; 5290 5291/** 5292 unloadTheme unloads the Galleria theme and prepares for a new theme 5293 5294 @returns Galleria 5295*/ 5296 5297Galleria.unloadTheme = function() { 5298 5299 if ( typeof Galleria.theme == 'object' ) { 5300 5301 $('script').each(function( i, script ) { 5302 5303 if( new RegExp( 'galleria\\.' + Galleria.theme.name + '\\.' ).test( script.src ) ) { 5304 $( script ).remove(); 5305 } 5306 }); 5307 5308 Galleria.theme = undef; 5309 } 5310 5311 return Galleria; 5312}; 5313 5314/** 5315 Retrieves a Galleria instance. 5316 5317 @param {number} [index] Optional index to retrieve. 5318 If no index is supplied, the method will return all instances in an array.
vendor: 5,605 bytes, lines 5319-5559
5319 5320 @returns Instance or Array of instances 5321*/ 5322 5323Galleria.get = function( index ) { 5324 if ( !!_instances[ index ] ) { 5325 return _instances[ index ]; 5326 } else if ( typeof index !== 'number' ) { 5327 return _instances; 5328 } else { 5329 Galleria.raise('Gallery index ' + index + ' not found'); 5330 } 5331}; 5332 5333/** 5334 5335 Configure Galleria options via a static function. 5336 The options will be applied to all instances 5337 5338 @param {string|object} key The options to apply or a key 5339 5340 @param [value] If key is a string, this is the value 5341 5342 @returns Galleria 5343 5344*/ 5345 5346Galleria.configure = function( key, value ) { 5347 5348 var opts = {}; 5349 5350 if( typeof key == 'string' && value ) { 5351 opts[key] = value; 5352 key = opts; 5353 } else { 5354 $.extend( opts, key ); 5355 } 5356 5357 Galleria.configure.options = opts; 5358 5359 $.each( Galleria.get(), function(i, instance) { 5360 instance.setOptions( opts ); 5361 }); 5362 5363 return Galleria; 5364}; 5365 5366Galleria.configure.options = {}; 5367 5368/** 5369 5370 Bind a Galleria event to the gallery 5371 5372 @param {string} type A string representing the galleria event 5373 5374 @param {function} callback The function that should run when the event is triggered 5375 5376 @returns Galleria 5377 5378*/ 5379 5380Galleria.on = function( type, callback ) { 5381 if ( !type ) { 5382 return; 5383 } 5384 5385 callback = callback || F; 5386 5387 // hash the bind 5388 var hash = type + callback.toString().replace(/\s/g,'') + Utils.timestamp(); 5389 5390 // for existing instances 5391 $.each( Galleria.get(), function(i, instance) { 5392 instance._binds.push( hash ); 5393 instance.bind( type, callback ); 5394 }); 5395 5396 // for future instances 5397 Galleria.on.binds.push({ 5398 type: type, 5399 callback: callback, 5400 hash: hash 5401 }); 5402 5403 return Galleria; 5404}; 5405 5406Galleria.on.binds = []; 5407 5408/** 5409 5410 Run Galleria 5411 Alias for $(selector).galleria(options) 5412 5413 @param {string} selector A selector of element(s) to intialize galleria to 5414 5415 @param {object} options The options to apply 5416 5417 @returns Galleria 5418 5419*/ 5420 5421Galleria.run = function( selector, options ) { 5422 if ( $.isFunction( options ) ) { 5423 options = { extend: options }; 5424 } 5425 $( selector || '#galleria' ).galleria( options ); 5426 return Galleria; 5427}; 5428 5429/** 5430 Creates a transition to be used in your gallery 5431 5432 @param {string} name The name of the transition that you will use as an option 5433 5434 @param {Function} fn The function to be executed in the transition. 5435 The function contains two arguments, params and complete. 5436 Use the params Object to integrate the transition, and then call complete when you are done. 5437 5438 @returns Galleria 5439 5440*/ 5441 5442Galleria.addTransition = function( name, fn ) { 5443 _transitions.effects[name] = fn; 5444 return Galleria; 5445}; 5446 5447/** 5448 The Galleria utilites 5449*/ 5450 5451Galleria.utils = Utils; 5452 5453/** 5454 A helper metod for cross-browser logging. 5455 It uses the console log if available otherwise it falls back to alert 5456 5457 @example Galleria.log("hello", document.body, [1,2,3]); 5458*/ 5459 5460Galleria.log = function() { 5461 var args = Utils.array( arguments ); 5462 if( 'console' in window && 'log' in window.console ) { 5463 try { 5464 return window.console.log.apply( window.console, args ); 5465 } catch( e ) { 5466 $.each( args, function() { 5467 window.console.log(this); 5468 }); 5469 } 5470 } else { 5471 return window.alert( args.join('<br>') ); 5472 } 5473}; 5474 5475/** 5476 A ready method for adding callbacks when a gallery is ready 5477 Each method is call before the extend option for every instance 5478 5479 @param {function} callback The function to call 5480 5481 @returns Galleria 5482*/ 5483 5484Galleria.ready = function( fn ) { 5485 if ( typeof fn != 'function' ) { 5486 return Galleria; 5487 } 5488 $.each( _galleries, function( i, gallery ) { 5489 fn.call( gallery, gallery._options ); 5490 }); 5491 Galleria.ready.callbacks.push( fn ); 5492 return Galleria; 5493}; 5494 5495Galleria.ready.callbacks = []; 5496 5497/** 5498 Method for raising errors 5499 5500 @param {string} msg The message to throw 5501 5502 @param {boolean} [fatal] Set this to true to override debug settings and display a fatal error 5503*/ 5504 5505Galleria.raise = function( msg, fatal ) { 5506 5507 var type = fatal ? 'Fatal error' : 'Error', 5508 5509 self = this, 5510 5511 css = { 5512 color: '#fff', 5513 position: 'absolute', 5514 top: 0, 5515 left: 0, 5516 zIndex: 100000 5517 }, 5518 5519 echo = function( msg ) { 5520 5521 var html = '<div style="padding:4px;margin:0 0 2px;background:#' + 5522 ( fatal ? '811' : '222' ) + ';">' + 5523 ( fatal ? '<strong>' + type + ': </strong>' : '' ) + 5524 msg + '</div>'; 5525 5526 $.each( _instances, function() { 5527 5528 var cont = this.$( 'errors' ), 5529 target = this.$( 'target' ); 5530 5531 if ( !cont.length ) { 5532 5533 target.css( 'position', 'relative' ); 5534 5535 cont = this.addElement( 'errors' ).appendChild( 'target', 'errors' ).$( 'errors' ).css(css); 5536 } 5537 cont.append( html ); 5538 5539 }); 5540 5541 if ( !_instances.length ) { 5542 $('<div>').css( $.extend( css, { position: 'fixed' } ) ).append( html ).appendTo( DOM().body ); 5543 } 5544 }; 5545 5546 // if debug is on, display errors and throw exception if fatal 5547 if ( DEBUG ) { 5548 echo( msg ); 5549 if ( fatal ) { 5550 throw new Error(type + ': ' + msg); 5551 } 5552 5553 // else just echo a silent generic error if fatal 5554 } else if ( fatal ) { 5555 if ( _hasError ) { 5556 return; 5557 } 5558 _hasError = true; 5559 fatal = false;
5560 echo( 'Gallery could not load.' ); 5561 } 5562}; 5563 5564// Add the version 5565Galleria.version = VERSION; 5566 5567/** 5568 A method for checking what version of Galleria the user has installed and throws a readable error if the user needs to upgrade. 5569 Useful when building plugins that requires a certain version to function. 5570 5571 @param {number} version The minimum version required 5572 5573 @param {string} [msg] Optional message to display. If not specified, Galleria will throw a generic error. 5574 5575 @returns Galleria 5576*/ 5577 5578Galleria.requires = function( version, msg ) { 5579 msg = msg || 'You need to upgrade Galleria to version ' + version + ' to use one or more components.'; 5580 if ( Galleria.version < version ) { 5581 Galleria.raise(msg, true); 5582 } 5583 return Galleria; 5584}; 5585 5586/** 5587 Adds preload, cache, scale and crop functionality 5588 5589 @constructor 5590 5591 @requires jQuery 5592 5593 @param {number} [id] Optional id to keep track of instances 5594*/ 5595 5596Galleria.Picture = function( id ) { 5597 5598 // save the id 5599 this.id = id || null; 5600 5601 // the image should be null until loaded 5602 this.image = null; 5603 5604 // Create a new container 5605 this.container = Utils.create('galleria-image'); 5606 5607 // add container styles 5608 $( this.container ).css({ 5609 overflow: 'hidden', 5610 position: 'relative', // for IE Standards mode 5611 }); 5612 5613 // saves the original measurements 5614 this.original = { 5615 width: 0, 5616 height: 0 5617 }; 5618 5619 // flag when the image is ready 5620 this.ready = false; 5621 5622 // flag for iframe Picture 5623 this.isIframe = false; 5624 5625}; 5626 5627Galleria.Picture.prototype = { 5628 5629 // the inherited cache object 5630 cache: {}, 5631 5632 // show the image on stage 5633 show: function() { 5634 Utils.show( this.image ); 5635 }, 5636 5637 // hide the image 5638 hide: function() { 5639 Utils.moveOut( this.image ); 5640 }, 5641 5642 clear: function() { 5643 this.image = null; 5644 }, 5645 5646 /** 5647 Checks if an image is in cache 5648 5649 @param {string} src The image source path, ex '/path/to/img.jpg' 5650 5651 @returns {boolean} 5652 */ 5653 5654 isCached: function( src ) { 5655 return !!this.cache[src]; 5656 }, 5657 5658 /** 5659 Preloads an image into the cache 5660 5661 @param {string} src The image source path, ex '/path/to/img.jpg' 5662 5663 @returns Galleria.Picture 5664 */ 5665 5666 preload: function( src ) { 5667 $( new Image() ).load((function(src, cache) { 5668 return function() { 5669 cache[ src ] = src; 5670 }; 5671 }( src, this.cache ))).attr( 'src', src ); 5672 }, 5673 5674 /** 5675 Loads an image and call the callback when ready. 5676 Will also add the image to cache. 5677 5678 @param {string} src The image source path, ex '/path/to/img.jpg' 5679 @param {Object} [size] The forced size of the image, defined as an object { width: xx, height:xx } 5680 @param {Function} callback The function to be executed when the image is loaded & scaled 5681 5682 @returns The image container (jQuery object) 5683 */ 5684 5685 load: function(src, size, callback) { 5686 5687 if ( typeof size == 'function' ) { 5688 callback = size; 5689 size = null; 5690 } 5691 5692 if( this.isIframe ) { 5693 var id = 'if'+new Date().getTime(); 5694 5695 this.image = $('<iframe>', { 5696 src: src, 5697 frameborder: 0, 5698 id: id, 5699 allowfullscreen: true, 5700 css: { visibility: 'hidden' } 5701 })[0]; 5702 5703 $( this.container ).find( 'iframe,img' ).remove(); 5704 5705 this.container.appendChild( this.image ); 5706 5707 $('#'+id).load( (function( self, callback ) { 5708 return function() { 5709 window.setTimeout(function() { 5710 $( self.image ).css( 'visibility', 'visible' ); 5711 if( typeof callback == 'function' ) { 5712 callback.call( self, self ); 5713 } 5714 }, 10); 5715 }; 5716 }( this, callback ))); 5717 5718 return this.container; 5719 } 5720 5721 this.image = new Image(); 5722 5723 // IE8 opacity inherit bug 5724 if ( Galleria.IE8 ) { 5725 $( this.image ).css( 'filter', 'inherit' ); 5726 } 5727 5728 var i = 0, 5729 reload = false, 5730 resort = false, 5731 5732 // some jquery cache 5733 $container = $( this.container ), 5734 $image = $( this.image ), 5735 5736 onerror = function() { 5737 if ( !reload ) { 5738 reload = true; 5739 // reload the image with a timestamp 5740 window.setTimeout((function(image, src) { 5741 return function() { 5742 image.attr('src', src + '?' + Utils.timestamp() ); 5743 }; 5744 }( $(this), src )), 50); 5745 } else { 5746 // apply the dummy image if it exists 5747 if ( DUMMY ) { 5748 $( this ).attr( 'src', DUMMY ); 5749 } else { 5750 Galleria.raise('Image not found: ' + src); 5751 } 5752 } 5753 }, 5754 5755 // the onload method 5756 onload = (function( self, callback, src ) { 5757 5758 return function() { 5759 5760 var complete = function() { 5761 5762 $( this ).unbind( 'load' ); 5763 5764 // save the original size 5765 self.original = size || { 5766 height: this.height, 5767 width: this.width 5768 }; 5769 5770 self.container.appendChild( this ); 5771 5772 self.cache[ src ] = src; // will override old cache 5773 5774 if (typeof callback == 'function' ) { 5775 window.setTimeout(function() { 5776 callback.call( self, self ); 5777 },1); 5778 } 5779 }; 5780 5781 // Delay the callback to "fix" the Adblock Bug
5782 // http://code.google.com/p/adblockforchrome/issues/detail?id=3701 5783 if ( ( !this.width || !this.height ) ) { 5784 window.setTimeout( (function( img ) { 5785 return function() { 5786 if ( img.width && img.height ) { 5787 complete.call( img ); 5788 } else { 5789 // last resort, this should never happen but just in case it does... 5790 if ( !resort ) { 5791 $(new Image()).load( onload ).attr( 'src', img.src ); 5792 resort = true; 5793 } else { 5794 Galleria.raise('Could not extract width/height from image: ' + img.src + 5795 '. Traced measures: width:' + img.width + 'px, height: ' + img.height + 'px.'); 5796 } 5797 } 5798 }; 5799 }( this )), 2); 5800 } else { 5801 complete.call( this ); 5802 } 5803 }; 5804 }( this, callback, src )); 5805 5806 // remove any previous images 5807 $container.find( 'iframe,img' ).remove(); 5808 5809 // append the image 5810 $image.css({ display: 'block'}); 5811 5812 // hide it for now 5813 Utils.hide( this.image ); 5814 5815 // remove any max/min scaling 5816 $.each('minWidth minHeight maxWidth maxHeight'.split(' '), function(i, prop) { 5817 $image.css(prop, (/min/.test(prop) ? '0' : 'none')); 5818 }); 5819 5820 // begin load and insert in cache when done 5821 $image.load( onload ).error( onerror ).attr( 'src', src ); 5822 5823 // return the container 5824 return this.container; 5825 }, 5826 5827 /** 5828 Scales and crops the image 5829 5830 @param {Object} options The method takes an object with a number of options: 5831 5832 <ul> 5833 <li>width - width of the container</li> 5834 <li>height - height of the container</li> 5835 <li>min - minimum scale ratio</li> 5836 <li>max - maximum scale ratio</li> 5837 <li>margin - distance in pixels from the image border to the container</li> 5838 <li>complete - a callback that fires when scaling is complete</li> 5839 <li>position - positions the image, works like the css background-image property.</li> 5840 <li>crop - defines how to crop. Can be true, false, 'width' or 'height'</li> 5841 <li>canvas - set to true to try a canvas-based rescale</li> 5842 </ul> 5843 5844 @returns The image container object (jQuery) 5845 */ 5846 5847 scale: function( options ) { 5848 5849 var self = this; 5850 5851 // extend some defaults 5852 options = $.extend({ 5853 width: 0, 5854 height: 0, 5855 min: undef, 5856 max: undef, 5857 margin: 0, 5858 complete: F, 5859 position: 'center', 5860 crop: false, 5861 canvas: false, 5862 iframelimit: undef 5863 }, options); 5864 5865 if( this.isIframe ) { 5866 5867 var cw = options.width, 5868 ch = options.height, 5869 nw, nh; 5870 if ( options.iframelimit ) { 5871 var r = Math.min( options.iframelimit/cw, options.iframelimit/ch ); 5872 if ( r < 1 ) { 5873 nw = cw * r; 5874 nh = ch * r; 5875 5876 $( this.image ).css({ 5877 top: ch/2-nh/2, 5878 left: cw/2-nw/2, 5879 position: 'absolute' 5880 }); 5881 } else { 5882 $( this.image ).css({ 5883 top: 0, 5884 left: 0 5885 }); 5886 } 5887 } 5888 $( this.image ).width( nw || cw ).height( nh || ch ).removeAttr( 'width' ).removeAttr( 'height' ); 5889 $( this.container ).width( cw ).height( ch ); 5890 options.complete.call(self, self); 5891 try { 5892 if( this.image.contentWindow ) { 5893 $( this.image.contentWindow ).trigger('resize'); 5894 } 5895 } catch(e) {} 5896 return this.container; 5897 5898 } 5899 5900 // return the element if no image found 5901 if (!this.image) { 5902 return this.container; 5903 } 5904 5905 // store locale variables 5906 var width, 5907 height, 5908 $container = $( self.container ), 5909 data; 5910 5911 // wait for the width/height 5912 Utils.wait({ 5913 until: function() { 5914 width = options.width || 5915 $container.width() || 5916 Utils.parseValue( $container.css('width') ); 5917 5918 height = options.height || 5919 $container.height() || 5920 Utils.parseValue( $container.css('height') ); 5921 5922 return width && height; 5923 }, 5924 success: function() { 5925 5926 // calculate some cropping 5927 var newWidth = ( width - options.margin * 2 ) / self.original.width, 5928 newHeight = ( height - options.margin * 2 ) / self.original.height, 5929 min = Math.min( newWidth, newHeight ), 5930 max = Math.max( newWidth, newHeight ), 5931 cropMap = { 5932 'true' : max, 5933 'width' : newWidth, 5934 'height': newHeight, 5935 'false' : min, 5936 'landscape': self.original.width > self.original.height ? max : min, 5937 'portrait': self.original.width < self.original.height ? max : min 5938 }, 5939 ratio = cropMap[ options.crop.toString() ],
vendor: 5,563 bytes, lines 5940-6121
5940 canvasKey = ''; 5941 5942 // allow maxScaleRatio 5943 if ( options.max ) { 5944 ratio = Math.min( options.max, ratio ); 5945 } 5946 5947 // allow minScaleRatio 5948 if ( options.min ) { 5949 ratio = Math.max( options.min, ratio ); 5950 } 5951 5952 $.each( ['width','height'], function( i, m ) { 5953 $( self.image )[ m ]( self[ m ] = self.image[ m ] = Math.round( self.original[ m ] * ratio ) ); 5954 }); 5955 5956 $( self.container ).width( width ).height( height ); 5957 5958 if ( options.canvas && _canvas ) { 5959 5960 _canvas.elem.width = self.width; 5961 _canvas.elem.height = self.height; 5962 5963 canvasKey = self.image.src + ':' + self.width + 'x' + self.height; 5964 5965 self.image.src = _canvas.cache[ canvasKey ] || (function( key ) { 5966 5967 _canvas.context.drawImage(self.image, 0, 0, self.original.width*ratio, self.original.height*ratio); 5968 5969 try { 5970 5971 data = _canvas.elem.toDataURL(); 5972 _canvas.length += data.length; 5973 _canvas.cache[ key ] = data; 5974 return data; 5975 5976 } catch( e ) { 5977 return self.image.src; 5978 } 5979 5980 }( canvasKey ) ); 5981 5982 } 5983 5984 // calculate image_position 5985 var pos = {}, 5986 mix = {}, 5987 getPosition = function(value, measure, margin) { 5988 var result = 0; 5989 if (/\%/.test(value)) { 5990 var flt = parseInt( value, 10 ) / 100, 5991 m = self.image[ measure ] || $( self.image )[ measure ](); 5992 5993 result = Math.ceil( m * -1 * flt + margin * flt ); 5994 } else { 5995 result = Utils.parseValue( value ); 5996 } 5997 return result; 5998 }, 5999 positionMap = { 6000 'top': { top: 0 }, 6001 'left': { left: 0 }, 6002 'right': { left: '100%' }, 6003 'bottom': { top: '100%' } 6004 }; 6005 6006 $.each( options.position.toLowerCase().split(' '), function( i, value ) { 6007 if ( value === 'center' ) { 6008 value = '50%'; 6009 } 6010 pos[i ? 'top' : 'left'] = value; 6011 }); 6012 6013 $.each( pos, function( i, value ) { 6014 if ( positionMap.hasOwnProperty( value ) ) { 6015 $.extend( mix, positionMap[ value ] ); 6016 } 6017 }); 6018 6019 pos = pos.top ? $.extend( pos, mix ) : mix; 6020 6021 pos = $.extend({ 6022 top: '50%', 6023 left: '50%' 6024 }, pos); 6025 6026 // apply position 6027 $( self.image ).css({ 6028 position : 'absolute', 6029 top : getPosition(pos.top, 'height', height), 6030 left : getPosition(pos.left, 'width', width) 6031 }); 6032 6033 // show the image 6034 self.show(); 6035 6036 // flag ready and call the callback 6037 self.ready = true; 6038 options.complete.call( self, self ); 6039 6040 }, 6041 error: function() { 6042 Galleria.raise('Could not scale image: '+self.image.src); 6043 }, 6044 timeout: 1000 6045 }); 6046 return this; 6047 } 6048}; 6049 6050// our own easings 6051$.extend( $.easing, { 6052 6053 galleria: function (_, t, b, c, d) { 6054 if ((t/=d/2) < 1) { 6055 return c/2*t*t*t + b; 6056 } 6057 return c/2*((t-=2)*t*t + 2) + b; 6058 }, 6059 6060 galleriaIn: function (_, t, b, c, d) { 6061 return c*(t/=d)*t + b; 6062 }, 6063 6064 galleriaOut: function (_, t, b, c, d) { 6065 return -c *(t/=d)*(t-2) + b; 6066 } 6067 6068}); 6069 6070// the plugin initializer 6071$.fn.galleria = function( options ) { 6072 6073 var selector = this.selector; 6074 6075 // try domReady if element not found 6076 if ( !$(this).length ) { 6077 6078 $(function() { 6079 if ( $( selector ).length ) { 6080 6081 // if found on domReady, go ahead 6082 $( selector ).galleria( options ); 6083 6084 } else { 6085 6086 // if not, try fetching the element for 5 secs, then raise a warning. 6087 Galleria.utils.wait({ 6088 until: function() { 6089 return $( selector ).length; 6090 }, 6091 success: function() { 6092 $( selector ).galleria( options ); 6093 }, 6094 error: function() { 6095 Galleria.raise('Init failed: Galleria could not find the element "'+selector+'".'); 6096 }, 6097 timeout: 5000 6098 }); 6099 6100 } 6101 }); 6102 return this; 6103 } 6104 6105 return this.each(function() { 6106 6107 // destroy previous instance and prepare for new load 6108 if ( $.data(this, 'galleria') ) { 6109 $.data( this, 'galleria' ).destroy(); 6110 $( this ).find( '*' ).hide(); 6111 } 6112 6113 // load the new gallery 6114 $.data( this, 'galleria', new Galleria().init( this, options ) ); 6115 }); 6116 6117}; 6118 6119// phew 6120 6121}( jQuery ) );
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.