vendor: 60,459 bytes, lines 1-1453
1/*! 2 * @preserve nanogallery2 - javascript photo / video gallery and lightbox 3 * Homepage: http://nanogallery2.nanostudio.org 4 * Sources: https://github.com/nanostudio-org/nanogallery2 5 * 6 * License: GPLv3 and commercial licence 7 * 8 * Requirements: 9 * - jQuery (http://www.jquery.com) - version >= 1.7.1 10 * 11 * Embeded components: 12 * - shifty (https://github.com/jeremyckahn/shifty) 13 * - imagesloaded (https://github.com/desandro/imagesloaded) 14 * - hammer.js (http://hammerjs.github.io/) 15 * - screenfull.js (https://github.com/sindresorhus/screenfull.js) 16 * Tools: 17 * - webfont generated with http://fontello.com - mainly based on Font Awesome Copyright (C) 2012 by Dave Gandy (http://fontawesome.io/) 18 * - ICO online converter: https://iconverticons.com/online/ 19 */ 20 21 22 23// ########################################### 24// ##### nanogallery2 as a JQUERY PLUGIN ##### 25// ########################################### 26 27 28 29// Expose plugin as an AMD module if AMD loader is present: 30(function (factory) { 31 "use strict"; 32 if (typeof define === 'function' && define.amd) { 33 // AMD. Register as an anonymous module. 34 // define('nanogallery2', ['jquery'], factory); 35 define(['jquery'], factory); 36 } else if (typeof exports === 'object' && typeof require === 'function') { 37 // Browserify 38 factory(require('jquery')); 39 } else { 40 // Browser globals 41 factory(jQuery); 42 } 43}(function ($) { 44// ;(function ($) { 45 "use strict"; 46 47 //##### TOOLS/HELPERS #### 48 49 // Convert color to RGB/RGBA 50 function ColorHelperToRGB( color ) { 51 var obj = document.getElementById('ngyColorHelperToRGB'); 52 if (obj === null) { 53 obj = document.createElement('div'); 54 obj.id = "ngyColorHelperToRGB"; 55 obj.style.cssText = 'display: none; color:' + color + ';'; 56 document.body.appendChild(obj); 57 } 58 59 var rgb = getComputedStyle(obj).color; 60 61 // to get HEX value: 62 // var rgb = getComputedStyle(obj).color.match(/\d+/g); 63 // var r = parseInt(rgb[0]).toString(16); 64 // var g = parseInt(rgb[1]).toString(16); 65 // var b = parseInt(rgb[2]).toString(16); 66 // var hex = '#' + r + g + b; 67 68 return rgb; 69 } 70 71 72 // ##### helper for color handling 73 // - normalise RGB/RGBA/HEX format 74 // - lighten/darken color 75 // Inspired by: 76 // https://stackoverflow.com/questions/5560248/programmatically-lighten-or-darken-a-hex-color-or-rgb-and-blend-colors 77 // http://www.pimptrizkit.com/?t=20%20Shades 78 function ShadeBlendConvert (p, from, to) { 79 var rgba=''; 80 if( from.toUpperCase().substring(0,5) == 'RGBA(' ) { 81 rgba='a'; 82 from='rgb('+from.substring(5); 83 } 84 85 if(typeof(p)!="number"||p<-1||p>1||typeof(from)!="string"||(from[0]!='r'&&from[0]!='#')||(typeof(to)!="string"&&typeof(to)!="undefined"))return null; 86 //if(!this.sbcRip)this.sbcRip=function(d){ 87 function sbcRip(d){ 88 var l=d.length,RGB=new Object(); 89 if(l>9){ 90 d=d.split(","); 91 if(d.length<3||d.length>4)return null; 92 RGB[0]=i(d[0].slice(4)),RGB[1]=i(d[1]),RGB[2]=i(d[2]),RGB[3]=d[3]?parseFloat(d[3]):-1; 93 }else{ 94 if(l==8||l==6||l<4)return null; 95 if(l<6)d="#"+d[1]+d[1]+d[2]+d[2]+d[3]+d[3]+(l>4?d[4]+""+d[4]:""); 96 d=i(d.slice(1),16),RGB[0]=d>>16&255,RGB[1]=d>>8&255,RGB[2]=d&255,RGB[3]=l==9||l==5?r(((d>>24&255)/255)*10000)/10000:-1; 97 } 98 return RGB; 99 } 100 var i=parseInt,r=Math.round,h=from.length>9,h=typeof(to)=="string"?to.length>9?true:to=="c"?!h:false:h,b=p<0,p=b?p*-1:p,to=to&&to!="c"?to:b?"#000000":"#FFFFFF",f=sbcRip(from),t=sbcRip(to); 101 if(!f||!t)return null; 102 if(h)return "rgb"+rgba+"("+r((t[0]-f[0])*p+f[0])+","+r((t[1]-f[1])*p+f[1])+","+r((t[2]-f[2])*p+f[2])+(f[3]<0&&t[3]<0?")":","+(f[3]>-1&&t[3]>-1?r(((t[3]-f[3])*p+f[3])*10000)/10000:t[3]<0?f[3]:t[3])+")"); 103 else return "#"+(0x100000000+(f[3]>-1&&t[3]>-1?r(((t[3]-f[3])*p+f[3])*255):t[3]>-1?r(t[3]*255):f[3]>-1?r(f[3]*255):255)*0x1000000+r((t[0]-f[0])*p+f[0])*0x10000+r((t[1]-f[1])*p+f[1])*0x100+r((t[2]-f[2])*p+f[2])).toString(16).slice(f[3]>-1||t[3]>-1?1:3); 104 } 105 106 107 // ##### clone a javascript object 108 function cloneJSObject( obj ) { 109 if (obj === null || typeof obj !== 'object') { 110 return obj; 111 } 112 113 var temp = obj.constructor(); // give temp the original obj's constructor 114 for (var key in obj) { 115 temp[key] = cloneJSObject(obj[key]); 116 } 117 return temp; 118 } 119 120 // get viewport coordinates and size 121 function getViewport() { 122 var $win = jQuery(window); 123 return { 124 l: $win.scrollLeft(), 125 t: $win.scrollTop(), 126 w: $win.width(), 127 h: $win.height() 128 } 129 } 130 131 132 // Check if element is in viewport 133 // avoid if possible (performance issue) 134 /* 135 function inViewport( $elt, threshold ) { 136 var wp = getViewport(), 137 eltOS = $elt.offset(), 138 th = $elt.outerHeight(true), 139 tw = $elt.outerWidth(true); 140 if( eltOS.top >= (wp.t - threshold) 141 && (eltOS.top + th) <= (wp.t + wp.h + threshold) 142 && eltOS.left >= (wp.l - threshold) 143 && (eltOS.left + tw) <= (wp.l + wp.w + threshold) ) { 144 return true; 145 } 146 else { 147 return false; 148 } 149 } 150 */ 151 152 153 // Check if whole element is in ViewPort 154 // avoid if possible (performance issue) 155 function inViewportVert( $elt, threshold ) { 156 var wp = getViewport(), 157 eltOS = $elt.offset(), 158 th = $elt.outerHeight(true); 159 //var tw=$elt.outerWidth(true); 160 161 if( wp.t == 0 && (eltOS.top) <= (wp.t + wp.h ) ) { return true; } 162 163 if( eltOS.top >= wp.t && (eltOS.top + th) <= (wp.t + wp.h - threshold) ) { 164 return true; 165 } 166 else { 167 return false; 168 } 169 } 170 // Check if top of the element is in ViewPort 171 function topInViewportVert( $elt, threshold ) { 172 var wp = getViewport(), 173 eltOS = $elt.offset(); 174 175 if( eltOS.top >= wp.t && eltOS.top <= (wp.t + wp.h - threshold) ) { 176 return true; 177 } 178 else { 179 return false; 180 } 181 } 182 183 184 // set z-index to display 2 elements on top of all others 185 // function set2ElementsOnTop( start, elt1, elt2 ) { 186 // var highest_index = 0; 187 // if( start=='' ) { start= '*'; } 188 // jQuery(start).each(function() { 189 // var cur = parseInt(jQuery(this).css('z-index')); 190 // highest_index = cur > highest_index ? cur : highest_index; 191 // }); 192 // highest_index++; 193 // jQuery(elt2).css('z-index',highest_index+1); 194 // jQuery(elt1).css('z-index',highest_index); 195 // } 196 197 // set z-index to display element on top of all others 198 function setElementOnTop( start, elt ) { 199 var highest_index = 0; 200 if( start == '' ) { start = '*'; } 201 jQuery(start).each(function() { 202 var cur = parseInt(jQuery(this).css('z-index')); 203 highest_index = cur > highest_index ? cur : highest_index; 204 }); 205 highest_index++; 206 jQuery(elt).css('z-index',highest_index); 207 } 208 209 // return the real type of the object 210 var toType = function( obj ) { 211 // by Angus Croll - http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/ 212 return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase() 213 }; 214 215 216 $.nanogallery2 = function (elt, options) { 217 // To avoid scope issues, use '_this' instead of 'this' 218 // to reference this class from internal events and functions. 219 var _this = this; 220 221 // Access to jQuery and DOM versions of element 222 _this.$e = jQuery(elt); 223 _this.e = elt; 224 225 // Add a reverse reference to the DOM object 226 _this.$e.data('nanogallery2data', _this); 227 228 _this.init = function () { 229 230 // define these global objects only once per HTML page 231 if (typeof window.NGY2Item === 'undefined') { 232 233 window.NGY2Tools = (function () { 234 235 function NGY2Tools() { 236 var nextId = 1; // private static --> all instances 237 } 238 239 // check album name - albumList/blockList/allowList 240 NGY2Tools.FilterAlbumName = function( title, ID ) { 241 var s = title.toUpperCase(); 242 if( this.albumList.length > 0 ) { 243 for( var j=0; j < this.albumList.length; j++) { 244 if( s === this.albumList[j].toUpperCase() || ID === this.albumList[j] ) { 245 return true; 246 } 247 } 248 } 249 else { 250 var found = false; 251 if( this.allowList !== null ) { 252 //allowList : authorize only album cointaining one of the specified keyword in the title 253 for( var j = 0; j < this.allowList.length; j++) { 254 if( s.indexOf(this.allowList[j]) !== -1 ) { 255 found = true; 256 } 257 } 258 if( !found ) { return false; } 259 } 260 261 262 if( this.blockList !== null ) { 263 //blockList : ignore album cointaining one of the specified keyword in the title 264 for( var j = 0; j < this.blockList.length; j++) { 265 if( s.indexOf(this.blockList[j]) !== -1 ) { 266 return false; 267 } 268 } 269 } 270 return true; 271 } 272 }; 273 274 275 /** @function nanoAlert */ 276 /* Display an alert message in a specific element */ 277 NGY2Tools.NanoAlert = function(context, msg, verbose) { 278 NGY2Tools.NanoConsoleLog.call(context, msg); 279 if( context.$E.conConsole != null ) { 280 context.$E.conConsole.css({visibility: 'visible', minHeight: '100px'}); 281 if( verbose == false ) { 282 context.$E.conConsole.append('<p>' + msg + '</p>'); 283 } 284 else { 285 context.$E.conConsole.append('<p>nanogallery2: '+ msg + ' [' + context.baseEltID + ']</p>'); 286 } 287 } 288 }; 289 290 291 /** @function NanoConsoleLog */ 292 /* write message to the browser console */ 293 NGY2Tools.NanoConsoleLog = function(context, msg) { 294 if (window.console) { console.log('nanogallery2: ' + msg + ' [' + context.baseEltID + ']'); } 295 // debugger; 296 }; 297 298 299 /** @function PreloaderDisplay() */ 300 /* Display/hide preloader */ 301 NGY2Tools.PreloaderDisplay = function(display) { 302 if( display === true ) { 303 // loading bar at the top of the gallery 304 this.$E.conLoadingB.removeClass('nanoGalleryLBarOff').addClass('nanoGalleryLBar'); 305 // spinner over album thumbnail 306 if( this.GOM.albumIdxLoading != undefined && this.GOM.albumIdxLoading != -1 ) { 307 let item = this.I[this.GOM.albumIdxLoading]; 308 item.$Elts['.nGY2TnImg'].addClass('nGY2GThumbnailLoaderDisplayed'); 309 } 310 } 311 else { 312 // loading bar at the top of the gallery 313 this.$E.conLoadingB.removeClass('nanoGalleryLBar').addClass('nanoGalleryLBarOff'); 314 // spinner over album thumbnail 315 if( this.GOM.albumIdxLoading != undefined && this.GOM.albumIdxLoading != -1 ) { 316 let item = this.I[this.GOM.albumIdxLoading]; 317 item.$Elts['.nGY2TnImg'].removeClass('nGY2GThumbnailLoaderDisplayed'); 318 } 319 } 320 }; 321 322 // Scrambles the elements of an array 323 //+ Jonas Raoni Soares Silva 324 //@ http://jsfromhell.com/array/shuffle [v1.0] 325 NGY2Tools.AreaShuffle = function (o) { 326 for (var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x); 327 return o; 328 }; 329 330 /** @function GetImageTitleFromURL() */ 331 /* retrieve filemane */ 332 NGY2Tools.GetImageTitleFromURL = function( imageURL ) { 333 if( this.O.thumbnailLabel.get('title') == '%filename' ) { 334 return (imageURL.split('/').pop()).replace('_',' '); 335 } 336 337 if( this.O.thumbnailLabel.get('title') == '%filenameNoExt' ) { 338 var s=imageURL.split('/').pop(); 339 return (s.split('.').shift()).replace('_',' '); 340 } 341 // return imageURL; 342 return ''; 343 }; 344 345 346 /** @function AlbumPostProcess() */ 347 /* post process one album based on plugin general parameters --> sorting/maxItems*/ 348 NGY2Tools.AlbumPostProcess = function(albumID) { 349 350 // this function can probably be optimized.... 351 352 var sortOrder = this.gallerySorting[this.GOM.curNavLevel]; 353 var maxItems = this.galleryMaxItems[this.GOM.curNavLevel]; 354 355 if( sortOrder != '' || maxItems > 0 ) { 356 357 // copy album's items to a new array 358 var currentAlbum = this.I.filter( function( obj ) { 359 return( obj.albumID == albumID && obj.kind != 'albumUp' ); 360 }); 361 362 // sorting options 363 switch( sortOrder ) { 364 case 'RANDOM': 365 currentAlbum = NGY2Tools.AreaShuffle(currentAlbum); 366 break; 367 case 'REVERSED': 368 currentAlbum = currentAlbum.reverse(); 369 break; 370 case 'TITLEASC': 371 currentAlbum.sort(function (a, b) { 372 return( (a.title.toUpperCase() < b.title.toUpperCase()) ? -1 : ((a.title.toUpperCase() > b.title.toUpperCase()) ? 1 : 0) ); 373 }); 374 break; 375 case 'TITLEDESC': 376 currentAlbum.sort(function (a, b) { 377 return( (a.title.toUpperCase() > b.title.toUpperCase()) ? -1 : ((a.title.toUpperCase() < b.title.toUpperCase()) ? 1 : 0) ); 378 }); 379 break; 380 } 381 382 // max Items 383 if( maxItems > 0 && currentAlbum.length > maxItems ) { 384 currentAlbum.splice(maxItems - 1, currentAlbum.length-maxItems ); 385 } 386 387 // remove the albums's items from the global items array 388 this.I.ngy2removeIf( function( obj ) { 389 return( obj.albumID == albumID && obj.kind != 'albumUp' ); 390 }); 391 392 // add the sorted items back to the album 393 this.I.push.apply(this.I, currentAlbum); 394 395 } 396 }; 397 398 return NGY2Tools; 399 })(); 400 401 // ==================== 402 // ===== NGY2Item ===== 403 // ==================== 404 window.NGY2Item = (function() { 405 var nextId = 1; // private static --> all instances 406 407 // constructor 408 function NGY2Item( itemID ) { 409 //window.NGY2Item = function( itemID ) { 410 var ID = 0; // private 411 412 // public (this instance only) 413 if( itemID === undefined || itemID === null ) { 414 ID = nextId++; 415 } 416 else { 417 ID = itemID; 418 } 419 this.GetID = function () { return ID; }; 420 421 // public 422 this.kind = ''; // 'image', 'album' or 'albumUp' 423 this.mediaKind = 'img'; // 'img', 'iframe', 'video' 424 this.mediaMarkup = ''; 425 this.G = null; // pointer to global instance 426 this.title = ''; // image title 427 this.description = ''; // image description 428 this.albumID = 0; // ID of the parent album 429 this.src = ''; // full sized image URL 430 this.width = 0; // image width 431 this.height = 0; // image height 432 this.destinationURL = ''; // thumbnail destination URL --> open URL instead of displaying image 433 this.downloadURL = ''; // thumbnail download URL --> specify the image for download button 434 this.author = ''; // image/album author 435 this.left = 0; // store position to animate from old to new 436 this.top = 0; 437 this.width = 0; // store size to avoid setting width/height if not required 438 this.height = 0; 439 this.resizedContentWidth= 0; // store size of content (image) to avoid setting width/height if not required 440 this.resizedContentHeight= 0; 441 this.thumbs = { // URLs and sizes for user defined 442 url: { l1: { xs: '', sm:'', me: '', la: '', xl: '' }, lN: { xs: '', sm: '', me: '', la:'', xl: '' } }, 443 width: { l1: { xs: 0, sm: 0, me: 0, la: 0 , xl: 0 }, lN: { xs: 0 , sm: 0, me: 0, la: 0, xl: 0 } }, 444 height: { l1: { xs: 0, sm: 0, me: 0, la: 0 , xl: 0 }, lN: { xs: 0, sm: 0, me: 0, la: 0, xl: 0 } } 445 }; 446 this.thumbnailImgRevealed = false; // thumbnail image already revealed 447 this.imageDominantColors = null; // base64 GIF 448 this.imageDominantColor = null; // HEX RGB 449 this.featured = false; // featured element 450 this.flickrThumbSizes = {}; // store URLs for all available thumbnail sizes (flickr) 451 this.picasaThumbs = null; // store URLs and sizes 452 this.hovered = false; // is the thumbnail currently hovered? 453 this.hoverInitDone = false; 454 this.contentIsLoaded = false; // album: are items already loaded? 455 this.contentLength = 0; // album: number of items (real number of items in memory) 456 this.numberItems = 0; // album: number of items (value returned by data source) 457 this.mediaNumber = 0; // media number in the album 458 this.mediaCounter = 0; // number of medias in an album 459 this.eltTransform = []; // store the CSS transformations 460 this.eltFilter = []; // store the CSS filters 461 this.eltEffect = []; // store data about hover effects animations 462 this.paginationLastPage = 0; // for albums 463 this.paginationLastWidth = 0; // for albums 464 this.customData = {}; 465 this.selected = false; 466 this.imageWidth = 0; // image natural (real) width 467 this.imageHeight = 0; // image natural (real) height 468 this.$elt = null; // pointer to the corresponding DOM element 469 this.$Elts = []; // cached pointers to the thumbnail content -> to avoid jQuery().find() 470 this.tags = []; // list of tags of the current item 471 this.albumTagList = []; // list of all the tags of the items contained in the current album 472 this.albumTagListSel = []; // list of currently selected tags (only for albums) 473 this.exif = { exposure: '', flash: '', focallength: '', fstop: '', iso: '', model: '', time: '', location: ''}; 474 this.deleted = false; // item is deleted -> do not display anymore 475 this.rotationAngle = 0; // image display rotation angle 476 } 477 478 // public static 479 480 NGY2Item.Get = function( instance, ID ) { 481 var l = instance.I.length; 482 for( var i = 0; i < l; i++ ) { 483 if( instance.I[i].GetID() == ID ) { 484 return instance.I[i]; 485 } 486 } 487 return null; 488 }; 489 490 NGY2Item.GetIdx = function( instance, ID ) { 491 var l = instance.I.length; 492 for( var i = 0; i < l; i++ ) { 493 if( instance.I[i].GetID() == ID ) { 494 return i; 495 } 496 } 497 return -1; 498 }; 499 500 // create new item (image, album or albumUp) 501 NGY2Item.New = function( instance, title, description, ID, albumID, kind, tags ) { 502 var album = NGY2Item.Get( instance, albumID ); 503 504 // title translation 505 if( instance.O.titleTranslationMap !== null ) { 506 let obj = instance.O.titleTranslationMap.find(o => o.title === title); 507 if( obj !== undefined ) { 508 title = obj.replace; 509 } 510 } 511 512 513 if( albumID != -1 && albumID != 0 && title !='image gallery by nanogallery2 [build]' ) { 514 if( instance.O.thumbnailLevelUp && album.getContentLength(false) == 0 && instance.O.album == '' ) { 515 // add navigation thumbnail (album up) 516 let item = new NGY2Item('0'); 517 instance.I.push( item ); 518 album.contentLength += 1; 519 item.title = 'UP'; 520 item.albumID = albumID; 521 item.kind = 'albumUp'; 522 item.G = instance; 523 524 jQuery.extend( true, item.thumbs.width, instance.tn.defaultSize.width); 525 jQuery.extend( true, item.thumbs.height, instance.tn.defaultSize.height); 526 } 527 } 528 529 var item = NGY2Item.Get(instance, ID); 530 if( item === null ){ 531 // create a new item (otherwise, just update the existing one) 532 item = new NGY2Item(ID); 533 instance.I.push(item); 534 if( albumID != -1 && title !='image gallery by nanogallery2 [build]' ) { 535 album.contentLength+=1; 536 } 537 } 538 item.G = instance; 539 540 item.albumID = albumID; 541 item.kind = kind; 542 if( kind == 'image' ) { 543 album.mediaCounter += 1; 544 item.mediaNumber = album.mediaCounter; 545 } 546 547 // check keyword to find features images/albums 548 var kw = instance.O.thumbnailFeaturedKeyword; 549 if( kw != '' ) { 550 // check if item featured based on a keyword in the title or in the description 551 kw = kw.toUpperCase(); 552 var p = title.toUpperCase().indexOf(kw); 553 if( p > -1) { 554 item.featured = true; 555 // remove keyword case unsensitive 556 title = title.substring(0, p) + title.substring(p+kw.length, title.length); 557 } 558 p = description.toUpperCase().indexOf(kw); 559 if( p > -1) { 560 item.featured=true; 561 // remove keyword case unsensitive 562 description=description.substring(0, p) + description.substring(p + kw.length, description.length); 563 } 564 } 565 566 // TAGS 567 // if( instance.galleryFilterTags.Get() != false ) { 568 // if( instance.galleryFilterTags.Get() == true ) { 569 // if( tags != '' && tags != undefined ) { 570 // use set tags 571 // item.setTags(tags.split(' ')); 572 // } 573 // } 574 // else { 575 // extract tags starting with # (in title) 576 if( typeof instance.galleryFilterTags.Get() == 'string' ) { 577 switch( instance.galleryFilterTags.Get().toUpperCase() ) { 578 case 'TITLE': { 579 let re = /(?:^|\W)#(\w+)(?!\w)/g, match, matches = []; 580 // let tags = ""; 581 while (match = re.exec(title)) { 582 matches.push(match[1].replace(/^\s*|\s*$/, '')); //trim trailing/leading whitespace 583 } 584 item.setTags(matches); //tags; 585 title = title.split('#').join(''); //replaceall 586 break; 587 } 588 case 'DESCRIPTION': { 589 let re = /(?:^|\W)#(\w+)(?!\w)/g, match2, matches2 = []; 590 // let tags = ""; 591 while (match2 = re.exec(description)) { 592 matches2.push(match2[1].replace(/^\s*|\s*$/, '')); //trim trailing/leading whitespace 593 } 594 item.setTags(matches2); //tags; 595 description = description.split('#').join(''); //replaceall 596 break; 597 } 598 } 599 } 600 else { 601 if( tags != '' && tags != undefined ) { 602 // use set tags 603 item.setTags(tags.split(' ')); 604 } 605 } 606 // } 607 // } 608 609 // set (maybe modified) fields title and description 610 item.title = escapeHtml(instance, title); 611 item.description = escapeHtml(instance, description); 612 return item; 613 }; 614 615 616 // removes logically current item 617 NGY2Item.prototype.delete = function( ) { 618 this.deleted = true; 619 620 // update content length of parent album 621 this.G.I[NGY2Item.GetIdx(this.G, this.albumID)].contentLength--; 622 this.G.I[NGY2Item.GetIdx(this.G, this.albumID)].numberItems--; 623 624 // check if in GOM and removes it 625 var nbTn = this.G.GOM.items.length; 626 var ID = this.GetID(); 627 var foundIdx = -1; 628 var foundGOMidx = -1; 629 for( var i = 0; i < nbTn ; i++ ) { 630 var curTn = this.G.GOM.items[i]; 631 var item=this.G.I[curTn.thumbnailIdx]; 632 if( item.GetID() == ID ) { 633 // FOUND 634 if( !curTn.neverDisplayed ) { 635 foundIdx = curTn.thumbnailIdx; 636 foundGOMidx = i; 637 } 638 } 639 else { 640 if( foundIdx != -1 ) { 641 if( !curTn.neverDisplayed ) { 642 // update index value 643 item.$getElt('.nGY2GThumbnail').data('index', i-1); 644 item.$getElt('.nGY2GThumbnailImg').data('index', i-1); 645 } 646 } 647 } 648 } 649 if( foundIdx != -1 ) { 650 // delete item in GOM and delete thumbnail 651 var G = this.G; 652 if( this.selected == true ) { 653 this.selected = false; 654 G.GOM.nbSelected--; // update the global counter 655 } 656 if( G.I[foundIdx].$elt !== null ) { 657 G.I[foundIdx].$elt.remove(); // delete thumbnail DOM object 658 } 659 G.GOM.items.splice(foundGOMidx, 1); // delete in GOM 660 if( G.GOM.lastDisplayedIdx != -1 ) { 661 G.GOM.lastDisplayedIdx -= 1; 662 } 663 } 664 665 // TODO: update media-number of the other item in the same album 666 } 667 668 NGY2Item.prototype.addToGOM = function( ) { 669 // retrieve index 670 var ID = this.GetID(); 671 var l = this.G.I.length; 672 for( var idx = 0; idx < l; idx++ ) { 673 var item = this.G.I[idx]; 674 if( item.GetID() == ID ) { 675 var w = item.thumbImg().width; 676 var h = item.thumbImg().height; 677 // set default size if required 678 if( h == 0 ) { 679 h = this.G.tn.defaultSize.getHeight(); 680 } 681 if( w == 0 ) { 682 w = this.G.tn.defaultSize.getWidth(); 683 } 684 // add to GOM -> will be displayed on next refresh/resize 685 var tn = new this.G.GOM.GTn(idx, w, h); 686 this.G.GOM.items.push(tn); 687 break; 688 } 689 } 690 691 } 692 693 694 // function to avoid XSS issue - Cross Site Scripting 695 // original: https://github.com/janl/mustache.js/blob/master/mustache.js#L55 696 var entityMap = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '/': '/', '`': '`', '=': '=' }; 697 function escapeHtml (instance, string) { 698 if( instance.O.allowHTMLinData == true ) { 699 return string; 700 } 701 else { 702 return String(string).replace(/[&<>"'`=\/]/g, function fromEntityMap (s) { 703 return entityMap[s]; 704 }); 705 } 706 } 707 708 709 NGY2Item.get_nextId = function () { 710 return nextId; 711 }; 712 713 //=== public (shared across instances) 714 715 //--- cached sub elements 716 NGY2Item.prototype.$getElt = function( elt, forceRefresh ) { 717 if( this.$elt == null ) { return null; } 718 if( this.$Elts[elt] !== undefined && !forceRefresh == true ) { 719 return this.$Elts[elt]; 720 } 721 else { 722 if( elt == '.nGY2GThumbnail' ) { 723 this.$Elts[elt]=this.$elt; 724 } 725 else { 726 this.$Elts[elt]=this.$elt.find(elt); 727 } 728 return this.$Elts[elt]; 729 } 730 }; 731 732 // remove one element (in DOM and in cache) 733 NGY2Item.prototype.removeElt = function( elt ) { 734 if( this.$elt == null ) { return; } 735 if( this.$Elts[elt] == undefined) { return; } 736 this.$Elts[elt].remove(); 737 var index = this.$Elts.indexOf(elt); 738 this.$Elts.splice(index, 1); 739 }; 740 741 //--- returns the album containing the item 742 NGY2Item.prototype.album = function() { 743 return this.G.I[NGY2Item.GetIdx(this.G, this.albumID)]; 744 }; 745 746 //--- viewer - transition can be disabled per media kind - returns true if current media supports transition (swipe) 747 NGY2Item.prototype.mediaTransition = function( ) { 748 if( this.G.O.viewerTransitionMediaKind.indexOf( this.mediaKind ) > -1 ) { 749 return true; 750 } 751 return false; 752 }; 753 754 //--- set one image (url and size) 755 NGY2Item.prototype.imageSet = function( src, w, h ) { 756 this.src = src; 757 this.width = w; 758 this.height = h; 759 }; 760 761 //--- set one thumbnail (url and size) - screenSize and level are optional 762 NGY2Item.prototype.thumbSet = function( src, w, h, screenSize, level ) { 763 var lst=['xs','sm','me','la','xl']; 764 if( typeof screenSize === 'undefined' || screenSize == '' || screenSize == null ) { 765 for( var i=0; i< lst.length; i++ ) { 766 if( typeof level === 'undefined' || level == '' ) { 767 this.thumbs.url.l1[lst[i]]=src; 768 this.thumbs.height.l1[lst[i]]=h; 769 this.thumbs.width.l1[lst[i]]=w; 770 this.thumbs.url.lN[lst[i]]=src; 771 this.thumbs.height.lN[lst[i]]=h; 772 this.thumbs.width.lN[lst[i]]=w; 773 } 774 else { 775 this.thumbs.url[level][lst[i]]=src; 776 this.thumbs.height[level][lst[i]]=h; 777 this.thumbs.width[level][lst[i]]=w; 778 } 779 } 780 } 781 else { 782 if( typeof level === 'undefined' || level == '' || level == null ) { 783 this.thumbs.url.l1[screenSize]=src; 784 this.thumbs.height.l1[screenSize]=h; 785 this.thumbs.width.l1[screenSize]=w; 786 this.thumbs.url.lN[screenSize]=src; 787 this.thumbs.height.lN[screenSize]=h; 788 this.thumbs.width.lN[screenSize]=w; 789 } 790 else { 791 this.thumbs.url[level][screenSize]=src; 792 this.thumbs.height[level][screenSize]=h; 793 this.thumbs.width[level][screenSize]=w; 794 } 795 } 796 797 for( var i=0; i< lst.length; i++ ) { 798 this.thumbs.height.l1[lst[i]]=h; 799 } 800 for( var i=0; i< lst.length; i++ ) { 801 if( this.G.tn.settings.height.lN[lst[i]] == this.G.tn.settings.getH() && this.G.tn.settings.width.l1[lst[i]] == this.G.tn.settings.getW() ) { 802 this.thumbs.height.lN[lst[i]]=h; 803 } 804 } 805 }; 806 807 //--- set thumbnail image real height for current level/resolution, and for all others level/resolutions having the same settings 808 NGY2Item.prototype.thumbSetImgHeight = function( h ) { 809 var lst=['xs','sm','me','la','xl']; 810 for( var i=0; i< lst.length; i++ ) { 811 if( this.G.tn.settings.height.l1[lst[i]] == this.G.tn.settings.getH() && this.G.tn.settings.width.l1[lst[i]] == this.G.tn.settings.getW() ) { 812 this.thumbs.height.l1[lst[i]]=h; 813 } 814 } 815 for( var i=0; i< lst.length; i++ ) { 816 if( this.G.tn.settings.height.lN[lst[i]] == this.G.tn.settings.getH() && this.G.tn.settings.width.l1[lst[i]] == this.G.tn.settings.getW() ) { 817 this.thumbs.height.lN[lst[i]]=h; 818 } 819 } 820 }; 821 822 //--- set thumbnail image real width for current level/resolution, and for all others level/resolutions having the same settings 823 NGY2Item.prototype.thumbSetImgWidth = function( w ) { 824 var lst=['xs','sm','me','la','xl']; 825 for( var i=0; i< lst.length; i++ ) { 826 if( this.G.tn.settings.height.l1[lst[i]] == this.G.tn.settings.getH() && this.G.tn.settings.width.l1[lst[i]] == this.G.tn.settings.getW() ) { 827 this.thumbs.width.l1[lst[i]]=w; 828 } 829 } 830 for( var i=0; i< lst.length; i++ ) { 831 if( this.G.tn.settings.height.lN[lst[i]] == this.G.tn.settings.getH() && this.G.tn.settings.width.l1[lst[i]] == this.G.tn.settings.getW() ) { 832 this.thumbs.width.lN[lst[i]]=w; 833 } 834 } 835 }; 836 837 //--- Returns Thumbnail image (depending of the screen resolution) 838 NGY2Item.prototype.thumbImg = function () { 839 var tnImg = { src: '', width: 0, height: 0 }; 840 841 if( this.title == 'image gallery by nanogallery2 [build]' ) { 842 tnImg.src = this.G.emptyGif; 843 tnImg.url = this.G.emptyGif; 844 return tnImg; 845 } 846 tnImg.src = this.thumbs.url[this.G.GOM.curNavLevel][this.G.GOM.curWidth]; 847 tnImg.width = this.thumbs.width[this.G.GOM.curNavLevel][this.G.GOM.curWidth]; 848 tnImg.height = this.thumbs.height[this.G.GOM.curNavLevel][this.G.GOM.curWidth]; 849 return tnImg; 850 }; 851 852 //--- Set tags to items and add these tags to the album 853 NGY2Item.prototype.setTags = function( tags ) { 854 if( tags.length > 0 ) { 855 this.tags = tags; 856 var lstTags = this.album().albumTagList; 857 for( var i = 0; i < tags.length; i++ ) { 858 var tfound = false; 859 for( var j = 0; j < lstTags.length; j++ ) { 860 if( tags[i].toUpperCase() == lstTags[j].toUpperCase() ) { 861 tfound = true; 862 } 863 } 864 if( tfound == false) { 865 this.album().albumTagList.push(tags[i]) 866 // this.album().albumTagListSel.push(tags[i]) 867 } 868 } 869 } 870 }; 871 872 //--- check if 1 of current item's tags is selected (tag filter) 873 NGY2Item.prototype.checkTagFilter = function() { 874 if( this.G.galleryFilterTags.Get() != false && this.album().albumTagList.length > 0 ) { 875 if( this.G.O.thumbnailLevelUp && this.kind == 'albumUp' ) { 876 return true; 877 } 878 var found = false; 879 var lstTags = this.album().albumTagListSel; 880 if( lstTags.length == 0 ) { 881 // no tag is selected -> display all items 882 return true; 883 } 884 for( var i = 0; i < this.tags.length; i++ ) { 885 for( var j = 0; j < lstTags.length; j++ ) { 886 if( this.tags[i].toUpperCase() == lstTags[j].toUpperCase() ) { 887 found = true; 888 break; 889 } 890 } 891 } 892 return found; 893 } 894 else 895 return true; 896 }; 897 898 //--- check if 1 of current item's tags is found using API search 899 NGY2Item.prototype.isSearchTagFound = function() { 900 if( this.G.GOM.albumSearchTags == '' ) { return true; } 901 if( this.G.O.thumbnailLevelUp && this.kind == 'albumUp' ) { return true; } 902 903 //var lstTags=this.album().albumTagListSel; 904 for( var i = 0; i < this.tags.length; i++ ) { 905 if( this.tags[i].toUpperCase().indexOf( this.G.GOM.albumSearchTags ) >= 0 ) { 906 return true; 907 } 908 } 909 return false; 910 }; 911 912 //--- set the URL of the media to display in the viewer 913 //--- markup is defined for images 914 NGY2Item.prototype.setMediaURL = function( url, mediaKind ) { 915 this.src = url; 916 this.mediaKind = mediaKind; 917 if( mediaKind == 'img' ) { 918 this.mediaMarkup = '<img class="nGY2ViewerMedia" src="' + url + '" alt=" " itemprop="contentURL" draggable="false">'; 919 } 920 }; 921 922 923 //--- check if current item should be displayed 924 NGY2Item.prototype.isToDisplay = function( albumID ) { 925 return this.albumID == albumID && this.checkTagFilter() && this.isSearchFound() && this.isSearchTagFound() && this.deleted == false; 926 }; 927 928 929 930 //--- returns the number of items of the current album 931 //--- count using tags filter 932 NGY2Item.prototype.getContentLength = function( filterTags ) { 933 if( filterTags == false || this.albumTagList.length == 0 || this.G.galleryFilterTags.Get() == false ) { 934 return this.contentLength; 935 } 936 else { 937 var l = this.G.I.length; 938 var cnt = 0; 939 var albumID = this.GetID(); 940 for( var idx = 0; idx < l; idx++ ) { 941 var item = this.G.I[idx]; 942 if( item.isToDisplay(albumID) ) { 943 cnt++; 944 } 945 } 946 return cnt; 947 } 948 }; 949 950 NGY2Item.prototype.isSearchFound = function() { 951 if( this.G.GOM.albumSearch != '' ) { 952 if( this.title.toUpperCase().indexOf( this.G.GOM.albumSearch ) == -1 ) { 953 return false; 954 } 955 } 956 return true; 957 } 958 959 960 //--- for future use... 961 NGY2Item.prototype.responsiveURL = function () { 962 var url = ''; 963 switch(this.G.O.kind) { 964 case '': 965 url = this.src; 966 break; 967 case 'flickr': 968 url = this.src; 969 break; 970 case 'picasa': 971 case 'google': 972 case 'google2': 973 default: 974 url = this.src; 975 break; 976 } 977 return url; 978 }; 979 980 981 //--- Reveal the thumbnail image with animation on opacity 982 NGY2Item.prototype.ThumbnailImageReveal = function () { 983 984 if( this.thumbnailImgRevealed == false ) { 985 this.thumbnailImgRevealed = true; 986 new NGTweenable().tween({ 987 from: { opacity: 0 }, 988 to: { opacity: 1 }, 989 attachment: { item: this }, 990 delay: 30, 991 duration: 400, 992 easing: 'easeOutQuart', 993 step: function (state, att) { 994 var $e=att.item.$getElt('.nGY2TnImg'); 995 if( $e != null ) { 996 $e.css( state ); 997 } 998 } 999 }); 1000 } 1001 }; 1002 1003 1004 // In case of thumbnails with stacks - apply a percent to a value which include a unit 1005 function ValueApplyPercent( str, percent ) { 1006 str=String(str); 1007 if( str === '0' || percent == 1 ) { return str; } 1008 var n = Number(str.replace(/[a-zA-Z]/g, '')); 1009 var ar = str.match(/([^\-0-9\.]+)/g); 1010 var a = ''; 1011 if( ar != null && ar.length > 0 ) { 1012 a = ar.join(); 1013 } 1014 1015 if( isNaN(n) || n == 0 ) { 1016 return str; 1017 } 1018 1019 n = n * percent; 1020 return n + a; 1021 } 1022 1023 //--- 2D/3D CSS transform - apply the cached value to element 1024 NGY2Item.prototype.CSSTransformApply = function ( eltClass ) { 1025 var obj = this.eltTransform[eltClass]; 1026 1027 if( eltClass == '.nGY2GThumbnail' ) { 1028 // thumbnail 1029 var nbStacks = obj.$elt.length-1; 1030 var pTranslateX = 1; 1031 var pTranslateY = 1; 1032 var pTranslateZ = 1; 1033 var pTranslate = 1; 1034 var pRotateX = 1; 1035 var pRotateY = 1; 1036 var pRotateZ = 1; 1037 var pRotate = 1; 1038 var pScale = 1; 1039 for( var n = nbStacks; n >= 0; n-- ) { 1040 // units must be given with 1041 var v = 'translateX(' + ValueApplyPercent(obj.translateX,pTranslateX) + ') translateY(' + ValueApplyPercent(obj.translateY,pTranslateY) + ') translateZ(' + ValueApplyPercent(obj.translateZ,pTranslateZ) + ') scale(' + ValueApplyPercent(obj.scale,pScale) + ') translate(' + ValueApplyPercent(obj.translate,pTranslate) + ')'; 1042 if( !(this.G.IE <= 9) && !this.G.isGingerbread ) { 1043 v += ' rotateX(' + ValueApplyPercent(obj.rotateX,pRotateX) + ') rotateY(' + ValueApplyPercent(obj.rotateY,pRotateY) + ') rotateZ(' + ValueApplyPercent(obj.rotateZ,pRotateZ) + ') rotate(' + ValueApplyPercent(obj.rotate,pRotate) + ')'; 1044 } 1045 else { 1046 v += ' rotate(' + ValueApplyPercent(obj.rotateZ,pRotateZ) + ')'; 1047 } 1048 obj.$elt[n].style[this.G.CSStransformName] = v; 1049 1050 if( nbStacks > 0 ) { 1051 // apply a percent to the stack elements 1052 pTranslateX -= this.G.tn.opt.Get('stacksTranslateX'); 1053 pTranslateY -= this.G.tn.opt.Get('stacksTranslateY'); 1054 pTranslateZ -= this.G.tn.opt.Get('stacksTranslateZ'); 1055 pRotateX -= this.G.tn.opt.Get('stacksRotateX'); 1056 pRotateY -= this.G.tn.opt.Get('stacksRotateY'); 1057 pRotateZ -= this.G.tn.opt.Get('stacksRotateZ'); 1058 pScale -= this.G.tn.opt.Get('stacksScale'); 1059 } 1060 } 1061 } 1062 else { 1063 // thumbnail sub element 1064 if( obj.$elt != null ) { 1065 for( var n = 0; n < obj.$elt.length; n++ ) { 1066 if( obj.$elt[n] != undefined ) { 1067 // units must be given with 1068 var v = 'translateX(' + obj.translateX + ') translateY(' + obj.translateY + ') translateZ(' + obj.translateZ + ') scale(' + obj.scale + ') translate(' + obj.translate + ')'; 1069 if( !(this.G.IE <= 9) && !this.G.isGingerbread ) { 1070 v += ' rotateX(' + obj.rotateX + ') rotateY(' + obj.rotateY + ') rotateZ(' + obj.rotateZ + ') rotate(' + obj.rotate + ')'; 1071 } 1072 else { 1073 v += ' rotate(' + obj.rotateZ + ')'; 1074 } 1075 obj.$elt[n].style[this.G.CSStransformName] = v; 1076 } 1077 } 1078 } 1079 } 1080 }; 1081 1082 //--- 2D/3D CSS transform - set a value in cache 1083 NGY2Item.prototype.CSSTransformSet = function ( eltClass, transform, value, forceRefresh ) { 1084 if( this.eltTransform[eltClass] == undefined ) { 1085 this.eltTransform[eltClass] = { translateX: 0, translateY: 0, translateZ: 0, rotateX: 0, rotateY: 0, rotateZ: 0, scale: 1, translate: '0px,0px', rotate: 0 }; 1086 this.eltTransform[eltClass].$elt = this.$getElt(eltClass); 1087 } 1088 this.eltTransform[eltClass][transform] = value; 1089 if( forceRefresh === true ) { 1090 this.eltTransform[eltClass].$elt = this.$getElt(eltClass, true); 1091 } 1092 }; 1093 1094 //--- CSS Filters - apply the cached value to element 1095 NGY2Item.prototype.CSSFilterApply = function ( eltClass ) { 1096 var obj = this.eltFilter[eltClass]; 1097 var v = 'blur(' + obj.blur + ') brightness(' + obj.brightness + ') grayscale(' + obj.grayscale + ') sepia(' + obj.sepia + ') contrast(' + obj.contrast + ') opacity(' + obj.opacity + ') saturate(' + obj.saturate + ')'; 1098 if( obj.$elt != null ) { 1099 for( var n = 0; n < obj.$elt.length; n++ ) { 1100 if( obj.$elt[n] != undefined ) { 1101 obj.$elt[n].style.WebkitFilter = v; 1102 obj.$elt[n].style.filter = v; 1103 } 1104 } 1105 } 1106 }; 1107 1108 //--- CSS Filters - set a value in cache 1109 NGY2Item.prototype.CSSFilterSet = function ( eltClass, filter, value, forceRefresh ) { 1110 if( this.eltFilter[eltClass] == undefined ) { 1111 this.eltFilter[eltClass] = { blur: 0, brightness: '100%', grayscale: '0%', sepia: '0%', contrast: '100%', opacity: '100%', saturate: '100%' }; 1112 this.eltFilter[eltClass].$elt = this.$getElt(eltClass); 1113 } 1114 this.eltFilter[eltClass][filter] = value; 1115 if( forceRefresh === true ) { 1116 this.eltTransform[eltClass].$elt = this.$getElt(eltClass, true); 1117 } 1118 }; 1119 1120 //--- thumbnail hover animation 1121 NGY2Item.prototype.animate = function ( effect, delay, hoverIn ) { 1122 if( this.$getElt() == null ) { return; } 1123 1124 var context = {}; 1125 context.G = this.G; 1126 context.item = this; 1127 context.effect = effect; 1128 context.hoverIn = hoverIn; 1129 context.cssKind = ''; 1130 if( hoverIn ) { 1131 // HOVER IN 1132 1133 if( this.eltEffect[effect.element] == undefined ) { 1134 this.eltEffect[effect.element] = []; 1135 } 1136 if( this.eltEffect[effect.element][effect.type] == undefined ) { 1137 this.eltEffect[effect.element][effect.type] = { initialValue: 0, lastValue: 0 }; 1138 } 1139 if( effect.firstKeyframe ) { 1140 // store initial and current value -> for use in the back animation 1141 this.eltEffect[effect.element][effect.type] = { initialValue: effect.from, lastValue: effect.from}; 1142 } 1143 1144 context.animeFrom = effect.from; 1145 context.animeTo = effect.to; 1146 context.animeDuration = parseInt(effect.duration); 1147 context.animeDelay = 30 + parseInt(effect.delay + delay); // 30ms is a default delay to avoid conflict with other initializations 1148 context.animeEasing = effect.easing; 1149 } 1150 else { 1151 // HOVER OUT 1152 // if( effect.firstKeyframe ) { 1153 context.animeFrom = this.eltEffect[effect.element][effect.type].lastValue; 1154 context.animeTo = this.eltEffect[effect.element][effect.type].initialValue; 1155 // context.animeTo=effect.from; 1156 // } 1157 // else { 1158 // // context.animeFrom=effect.from; 1159 // context.animeFrom = this.eltEffect[effect.element][effect.type].lastValue; 1160 // context.animeTo = this.eltEffect[effect.element][effect.type].initialValue; 1161 // //context.animeTo=effect.to; 1162 1163 // } 1164 1165 context.animeDuration = parseInt(effect.durationBack); 1166 context.animeDelay = 30 + parseInt(effect.delayBack + delay); // 30ms is a default delay to avoid conflict with other initializations 1167 context.animeEasing = effect.easingBack; 1168 } 1169 1170 1171 // detect if animation on CSS transform 1172 var transform=['translateX', 'translateY', 'translateZ', 'scale', 'rotateX', 'rotateY', 'rotateZ']; 1173 for( var i = 0; i < transform.length; i++ ) { 1174 if( effect.type == transform[i] ) { 1175 context.cssKind = 'transform'; 1176 break; 1177 } 1178 } 1179 1180 // detect if animation on CSS filter 1181 var filter=['blur', 'brightness', 'grayscale', 'sepia', 'contrast', 'opacity', 'saturate']; 1182 for( var i = 0; i < filter.length; i++ ) { 1183 if( effect.type == filter[i] ) { 1184 context.cssKind = 'filter'; 1185 break; 1186 } 1187 } 1188 // handle some special cases 1189 if( hoverIn && effect.element == '.nGY2GThumbnail' && ( effect.type == 'scale' || effect.type == 'rotateX') ) { 1190 this.G.GOM.lastZIndex++; 1191 this.$getElt(effect.element).css('z-index', this.G.GOM.lastZIndex); 1192 // setElementOnTop(this.G.$E.base, this.$getElt(effect.element) ); 1193 } 1194 1195 // animation 1196 var tweenable = new NGTweenable(); 1197 context.tweenable=tweenable; 1198 tweenable.tween({ 1199 attachment: context, 1200 from: { 'v': context.animeFrom }, 1201 to: { 'v': context.animeTo }, 1202 duration: context.animeDuration, //parseInt(effect.duration), 1203 delay: context.animeDelay, //parseInt(effect.delay), 1204 easing: context.animeEasing, //'easeOutQuart', 1205 1206 step: function (state, att) { 1207 if( att.item.$getElt() == null ) { 1208 // the thumbnail may be destroyed since the start of the animation 1209 att.tweenable.stop(false); 1210 // att.tweenable.dispose(); 1211 return; 1212 } 1213 if( att.hoverIn && !att.item.hovered ) { 1214 // thumbnail no more hovered 1215 att.tweenable.stop(false); 1216 // att.tweenable.dispose(); 1217 return; 1218 } 1219 1220 if( att.G.VOM.viewerDisplayed ) { 1221 att.tweenable.stop(false); 1222 // att.tweenable.dispose(); 1223 return; 1224 } 1225 1226 // test if in delay phase 1227 if( state.v == att.animeFrom ) { return; } 1228 1229 switch( att.cssKind ) { 1230 case 'transform': 1231 // window.ng_draf( function() { 1232 att.item.CSSTransformSet(att.effect.element, att.effect.type, state.v); 1233 att.item.CSSTransformApply( att.effect.element ); 1234 // }); 1235 break; 1236 case 'filter': 1237 // window.ng_draf( function() { 1238 att.item.CSSFilterSet(att.effect.element, att.effect.type, state.v); 1239 att.item.CSSFilterApply( att.effect.element ); 1240 // }); 1241 break; 1242 default: 1243 var v=state.v; 1244 if( state.v.substring(0,4) == 'rgb(' || state.v.substring(0,5) == 'rgba(' ) { 1245 // to remove values after the dot (not supported by RGB/RGBA) 1246 // v=ngtinycolor(state.v).toRgbString(); 1247 v = ShadeBlendConvert(0, v); 1248 } 1249 // window.ng_draf( function() { 1250 att.item.$getElt( att.effect.element ).css( att.effect.type, v ); 1251 // }); 1252 break; 1253 } 1254 if( hoverIn ) { 1255 // store value for back animation 1256 att.item.eltEffect[att.effect.element][att.effect.type].lastValue = state.v; 1257 } 1258 }, 1259 1260 finish: function (state, att) { 1261 if( hoverIn ) { 1262 // store value for back animation 1263 att.item.eltEffect[att.effect.element][att.effect.type].lastValue = state.v; 1264 } 1265 1266 if( att.item.$getElt() == null ) { 1267 // the thumbnail may be destroyed since the start of the animation 1268 return; 1269 } 1270 if( att.hoverIn && !att.item.hovered ) { 1271 // thumbnail no more hovered 1272 return; 1273 } 1274 1275 if( att.G.VOM.viewerDisplayed ) { 1276 return; 1277 } 1278 1279 // window.ng_draf( function() { 1280 switch( att.cssKind ) { 1281 case 'transform': 1282 att.item.CSSTransformSet(att.effect.element, att.effect.type, att.animeTo); 1283 att.item.CSSTransformApply(att.effect.element); 1284 break; 1285 case 'filter': 1286 att.item.CSSFilterSet(att.effect.element, att.effect.type, att.animeTo); 1287 att.item.CSSFilterApply(att.effect.element); 1288 break; 1289 default: 1290 att.item.$getElt(att.effect.element).css(att.effect.type, att.animeTo); 1291 break; 1292 } 1293 // }); 1294 } 1295 }); 1296 }; 1297 1298 return NGY2Item; 1299 })(); 1300 1301 } 1302 1303 _this.options = jQuery.extend(true, {}, jQuery.nanogallery2.defaultOptions, options); 1304 // Initialization code 1305 _this.nG2 = null; 1306 _this.nG2 = new nanoGALLERY2(); 1307 _this.nG2.initiateGallery2(_this.e, _this.options ); 1308 1309 }; 1310 1311 // PUBLIC EXPOSED METHODS 1312 _this.test = function() { 1313 //alert('test'); 1314 // console.dir(_this.nG.G.I.length); 1315 // console.dir(_this.nG); 1316 // debugger; 1317 //privateTest(); 1318 } 1319 1320 1321 // Run initializer 1322 _this.init(); 1323 }; 1324 1325 jQuery.nanogallery2.defaultOptions = { 1326 kind : '', 1327 userID : '', 1328 photoset : '', 1329 album: '', 1330 blockList : 'scrapbook|profil|auto backup', 1331 tagBlockList: '', 1332 allowList : '', 1333 albumList : '', 1334 albumList2 : null, 1335 RTL : false, 1336 flickrSkipOriginal : true, 1337 flickrAPIKey: '', 1338 breadcrumbAutoHideTopLevel : true, 1339 displayBreadcrumb : true, 1340 breadcrumbOnlyCurrentLevel : true, 1341 breadcrumbHideIcons : true, 1342 theme : 'nGY2', 1343 galleryTheme : 'dark', 1344 viewerTheme : 'dark', 1345 items : null, 1346 itemsBaseURL : '', 1347 thumbnailSelectable : false, 1348 dataProvider: '', 1349 allowHTMLinData: false, 1350 locationHash : true, 1351 slideshowDelay : 3000, 1352 slideshowAutoStart : false, 1353 1354 debugMode: false, 1355 1356 titleTranslationMap: null, 1357 galleryDisplayMoreStep : 2, 1358 galleryDisplayMode : 'fullContent', 1359 galleryL1DisplayMode : null, 1360 galleryPaginationMode : 'rectangles', // 'dots', 'rectangles', 'numbers' 1361 galleryPaginationTopButtons : true, 1362 galleryMaxRows : 2, 1363 galleryL1MaxRows : null, 1364 galleryLastRowFull: false, 1365 galleryL1LastRowFull: null, 1366 galleryLayoutEngine : 'default', 1367 paginationSwipe: true, 1368 paginationVisiblePages : 10, 1369 galleryFilterTags : false, // possible values: false, true, 'title', 'description' 1370 galleryL1FilterTags : null, // possible values: false, true, 'title', 'description' 1371 galleryFilterTagsMode : 'single', 1372 galleryL1FilterTagsMode : null, 1373 galleryMaxItems : 0, // maximum number of items per album --> only flickr, google2, nano_photos_provider2 1374 galleryL1MaxItems : null, // maximum number of items per gallery page --> only flickr, google2, nano_photos_provider2 1375 gallerySorting : '', 1376 galleryL1Sorting : null, 1377 galleryDisplayTransition : 'none', 1378 galleryL1DisplayTransition : null, 1379 galleryDisplayTransitionDuration : 1000, 1380 galleryL1DisplayTransitionDuration : null, 1381 galleryResizeAnimation : false, 1382 galleryRenderDelay : 10, 1383 1384 thumbnailCrop : true, 1385 thumbnailL1Crop : null, 1386 thumbnailCropScaleFactor : 1.5, 1387 thumbnailLevelUp : false, 1388 thumbnailAlignment : 'fillWidth', 1389 thumbnailWidth : 300, 1390 thumbnailL1Width : null, 1391 thumbnailHeight : 200, 1392 thumbnailL1Height : null, 1393 thumbnailBaseGridHeight : 0, 1394 thumbnailL1BaseGridHeight : null, 1395 thumbnailGutterWidth : 2, 1396 thumbnailL1GutterWidth : null, 1397 thumbnailGutterHeight : 2, 1398 thumbnailL1GutterHeight : null, 1399 thumbnailBorderVertical : 2, 1400 thumbnailL1BorderVertical : null, 1401 thumbnailBorderHorizontal : 2, 1402 thumbnailL1BorderHorizontal : null, 1403 thumbnailFeaturedKeyword : '*featured', 1404 thumbnailAlbumDisplayImage : false, 1405 thumbnailHoverEffect2 : 'toolsAppear', 1406 thumbnailBuildInit2 : '', 1407 thumbnailStacks : 0, 1408 thumbnailL1Stacks : null, 1409 thumbnailStacksTranslateX : 0, 1410 thumbnailL1StacksTranslateX : null, 1411 thumbnailStacksTranslateY : 0, 1412 thumbnailL1StacksTranslateY : null, 1413 thumbnailStacksTranslateZ : 0, 1414 thumbnailL1StacksTranslateZ : null, 1415 thumbnailStacksRotateX : 0, 1416 thumbnailL1StacksRotateX : null, 1417 thumbnailStacksRotateY : 0, 1418 thumbnailL1StacksRotateY : null, 1419 thumbnailStacksRotateZ : 0, 1420 thumbnailL1StacksRotateZ : null, 1421 thumbnailStacksScale : 0, 1422 thumbnailL1StacksScale : null, 1423 thumbnailDisplayOutsideScreen: true, 1424 thumbnailWaitImageLoaded: true, 1425 thumbnailSliderDelay: 2000, 1426 galleryBuildInit2 : '', 1427 portable : false, 1428 eventsDebounceDelay: 10, 1429 1430 touchAnimation : false, 1431 touchAnimationL1 : undefined, 1432 touchAutoOpenDelay : 0, 1433 1434 thumbnailLabel : { 1435 position : 'overImage', 1436 align: 'center', 1437 valign: 'bottom', 1438 display : true, 1439 displayDescription : false, 1440 titleMaxLength : 0, 1441 titleMultiLine : false, 1442 descriptionMaxLength : 0, 1443 descriptionMultiLine : false, 1444 hideIcons : true, 1445 title : '' 1446 }, 1447 1448 thumbnailToolbarImage : { topLeft: 'select', topRight : 'featured' }, 1449 thumbnailToolbarAlbum : { topLeft: 'select', topRight : 'counter' }, 1450 thumbnailDisplayOrder : '', 1451 thumbnailL1DisplayOrder : null, 1452 thumbnailDisplayInterval : 15, 1453 thumbnailL1DisplayInterval : null,
1454 thumbnailDisplayTransition : 'fadeIn', 1455 thumbnailL1DisplayTransition : null, 1456 thumbnailDisplayTransitionEasing : 'easeOutQuart', 1457 thumbnailL1DisplayTransitionEasing : null, 1458 thumbnailDisplayTransitionDuration: 240, 1459 thumbnailL1DisplayTransitionDuration: null, 1460 thumbnailOpenInLightox : true, 1461 thumbnailOpenOriginal : false, 1462 1463 lightboxStandalone: false, 1464 viewer : 'internal', 1465 viewerFullscreen: false, 1466 imageTransition : 'swipe2', 1467 viewerTransitionMediaKind : 'img', 1468 viewerZoom : true, 1469 viewerImageDisplay : '', 1470 openOnStart : '', 1471 viewerHideToolsDelay : 4000, 1472 viewerToolbar : { 1473 display : false, 1474 position : 'bottom', 1475 fullWidth : false, 1476 align : 'center', 1477 autoMinimize : 0, 1478 standard : 'minimizeButton,label', 1479 minimized : 'minimizeButton,label,infoButton,shareButton,fullscreenButton' 1480 }, 1481 viewerTools : { 1482 topLeft : 'pageCounter,playPauseButton', 1483 topRight : 'rotateLeft,rotateRight,fullscreenButton,closeButton' 1484 }, 1485 viewerGallery: 'bottomOverMedia', 1486 viewerGalleryTWidth: 40, 1487 viewerGalleryTHeight: 40, 1488 1489 breakpointSizeSM : 480, 1490 breakpointSizeME : 992, 1491 breakpointSizeLA : 1200, 1492 breakpointSizeXL : 1800, 1493 1494 fnThumbnailInit : null, 1495 fnThumbnailHoverInit : null, 1496 fnThumbnailHover : null, 1497 fnThumbnailHoverOut : null, 1498 fnThumbnailDisplayEffect : null, 1499 fnViewerInfo : null, 1500 fnImgToolbarCustInit : null, 1501 fnImgToolbarCustDisplay : null, 1502 fnImgToolbarCustClick : null, 1503 fnProcessData : null, 1504 fnThumbnailSelection : null, 1505 fnGalleryRenderStart : null, 1506 fnGalleryRenderEnd : null, 1507 fnGalleryObjectModelBuilt : null, 1508 fnGalleryLayoutApplied : null, 1509 fnThumbnailClicked : null, 1510 fnShoppingCartUpdated : null, 1511 fnThumbnailToolCustAction : null, 1512 fnThumbnailOpen : null, 1513 fnImgDisplayed : null, 1514 fnPopupMediaInfo : null, 1515 1516 i18n : { 1517 'breadcrumbHome' : 'Galleries', 'breadcrumbHome_FR' : 'Galeries', 1518 'thumbnailImageTitle' : '', 'thumbnailAlbumTitle' : '', 1519 'thumbnailImageDescription' : '', 'thumbnailAlbumDescription' : '', 1520 'infoBoxPhoto' : 'Photo', 'infoBoxDate' : 'Date', 'infoBoxAlbum' : 'Album', 'infoBoxDimensions' : 'Dimensions', 'infoBoxFilename' : 'Filename', 'infoBoxFileSize' : 'File size', 'infoBoxCamera' : 'Camera', 'infoBoxFocalLength' : 'Focal length', 'infoBoxExposure' : 'Exposure', 'infoBoxFNumber' : 'F Number', 'infoBoxISO' : 'ISO', 'infoBoxMake' : 'Make', 'infoBoxFlash' : 'Flash', 'infoBoxViews' : 'Views', 'infoBoxComments' : 'Comments' 1521 }, 1522 icons : { 1523 // example for font awesome: <i style="color:#eee;" class="fa fa-search-plus"></i> 1524 thumbnailAlbum: '<i class="nGY2Icon-folder-empty"></i>', 1525 thumbnailImage: '<i class="nGY2Icon-picture"></i>', 1526 breadcrumbAlbum: '<i class="nGY2Icon-folder-empty"></i>', 1527 breadcrumbHome: '<i class="nGY2Icon-home"></i>', 1528 breadcrumbSeparator: '<i class="nGY2Icon-left-open"></i>', 1529 breadcrumbSeparatorRtl: '<i class="nGY2Icon-right-open"></i>', 1530 navigationFilterSelected: '<i style="color:#fff;" class="nGY2Icon-ok"></i>', 1531 navigationFilterUnselected: '<i style="color:#ddd;opacity:0.3;" class="nGY2Icon-circle-empty"></i>', 1532 navigationFilterSelectedAll: '<i class="nGY2Icon-ccw"></i>', 1533 navigationPaginationPrevious: '<i class="nGY2Icon-ngy2_chevron-left"></i>', 1534 navigationPaginationNext: '<i class="nGY2Icon-ngy2_chevron-right"></i>', 1535 thumbnailSelected: '<i style="color:#bff;" class="nGY2Icon-ok-circled"></i>', 1536 thumbnailUnselected: '<i style="color:#bff;" class="nGY2Icon-circle-empty"></i>', 1537 thumbnailFeatured: '<i style="color:#dd5;" class="nGY2Icon-star"></i>', 1538 thumbnailCounter: '<i class="nGY2Icon-picture"></i>', 1539 thumbnailShare: '<i class="nGY2Icon-ngy2_share2"></i>', 1540 thumbnailDownload: '<i class="nGY2Icon-ngy2_download2"></i>', 1541 thumbnailInfo: '<i class="nGY2Icon-ngy2_info2"></i>', 1542 thumbnailShoppingcart: '<i class="nGY2Icon-basket"></i>',
vendor: 203,093 bytes, lines 1543-6930
1543 thumbnailDisplay: '<i class="nGY2Icon-resize-full"></i>', 1544 thumbnailCustomTool1: 'T1', 1545 thumbnailCustomTool2: 'T2', 1546 thumbnailCustomTool3: 'T3', 1547 thumbnailCustomTool4: 'T4', 1548 thumbnailCustomTool5: 'T5', 1549 thumbnailCustomTool6: 'T6', 1550 thumbnailCustomTool7: 'T7', 1551 thumbnailCustomTool8: 'T8', 1552 thumbnailCustomTool9: 'T9', 1553 thumbnailCustomTool10: 'T10', 1554 thumbnailAlbumUp: '<i style="font-size: 3em;" class="nGY2Icon-ngy2_chevron_up2"></i>', 1555 paginationNext: '<i class="nGY2Icon-right-open"></i>', 1556 paginationPrevious: '<i class="nGY2Icon-left-open"></i>', 1557 galleryMoreButton: '<i class="nGY2Icon-picture"></i> <i class="nGY2Icon-right-open"></i>', 1558 buttonClose: '<i class="nGY2Icon-ngy2_close2"></i>', 1559 viewerPrevious: '<i class="nGY2Icon-ngy2_chevron-left"></i>', 1560 viewerNext: '<i class="nGY2Icon-ngy2_chevron-right"></i>', 1561 viewerImgPrevious: '<i class="nGY2Icon-ngy2_chevron_left3"></i>', 1562 viewerImgNext: '<i class="nGY2Icon-ngy2_chevron_right3"></i>', 1563 viewerDownload: '<i class="nGY2Icon-ngy2_download2"></i>', 1564 viewerToolbarMin: '<i class="nGY2Icon-ellipsis-vert"></i>', 1565 viewerToolbarStd: '<i class="nGY2Icon-menu"></i>', 1566 viewerPlay: '<i class="nGY2Icon-play"></i>', 1567 viewerPause: '<i class="nGY2Icon-pause"></i>', 1568 viewerFullscreenOn: '<i class="nGY2Icon-resize-full"></i>', 1569 viewerFullscreenOff: '<i class="nGY2Icon-resize-small"></i>', 1570 viewerZoomIn: '<i class="nGY2Icon-ngy2_zoom_in2"></i>', 1571 viewerZoomOut: '<i class="nGY2Icon-ngy2_zoom_out2"></i>', 1572 viewerLinkOriginal: '<i class="nGY2Icon-ngy2_external2"></i>', 1573 viewerInfo: '<i class="nGY2Icon-ngy2_info2"></i>', 1574 viewerShare: '<i class="nGY2Icon-ngy2_share2"></i>', 1575 viewerRotateLeft: '<i class="nGY2Icon-ccw"></i>', 1576 viewerRotateRight: '<i class="nGY2Icon-cw"></i>', 1577 viewerShoppingcart: '<i class="nGY2Icon-basket"></i>', 1578 user: '<i class="nGY2Icon-user"></i>', 1579 location: '<i class="nGY2Icon-location"></i>', 1580 picture: '<i class="nGY2Icon-picture"></i>', 1581 config: '<i class="nGY2Icon-wrench"></i>', 1582 shareFacebook: '<i style="color:#3b5998;" class="nGY2Icon-facebook-squared"></i>', 1583 shareTwitter: '<i style="color:#00aced;" class="nGY2Icon-twitter-squared"></i>', 1584 // shareGooglePlus: '<i style="color:#dd4b39;" class="nGY2Icon-gplus-squared"></i>', 1585 shareTumblr: '<i style="color:#32506d;" class="nGY2Icon-tumblr-squared"></i>', 1586 sharePinterest: '<i style="color:#cb2027;" class="nGY2Icon-pinterest-squared"></i>', 1587 shareVK: '<i style="color:#3b5998;" class="nGY2Icon-vkontakte"></i>', 1588 shareMail: '<i style="color:#555;" class="nGY2Icon-mail-alt"></i>', 1589 viewerCustomTool1: 'T1', 1590 viewerCustomTool2: 'T2', 1591 viewerCustomTool3: 'T3', 1592 viewerCustomTool4: 'T4', 1593 viewerCustomTool5: 'T5', 1594 viewerCustomTool6: 'T6', 1595 viewerCustomTool7: 'T7', 1596 viewerCustomTool8: 'T8', 1597 viewerCustomTool9: 'T9', 1598 viewerCustomTool10: 'T10' 1599 } 1600 }; 1601 1602 jQuery.fn.nanogallery2 = function (args, option, value) { 1603 1604 if( typeof jQuery(this).data('nanogallery2data') === 'undefined'){ 1605 if( args == 'destroy' ) { 1606 // command to destroy but no instance yet --> exit 1607 return; 1608 } 1609 1610 return this.each( function(){ 1611 (new jQuery.nanogallery2(this, args)); 1612 }); 1613 } 1614 else { 1615 // no options --> 1616 // This function breaks the chain, but provides some API methods 1617 var nG2 = $(this).data('nanogallery2data').nG2; 1618 1619 // Lightbox standalone 1620 // (Another click on an already opened media) 1621 if( args !== undefined && args.lightboxStandalone === true ) { 1622 // items exist already (G.I is populated) -> just open the lightbox again 1623 nG2.LightboxReOpen(); 1624 return; 1625 } 1626 1627 switch(args){ 1628 case 'displayItem': 1629 nG2.DisplayItem(option); 1630 break; 1631 1632 case 'search': 1633 return( nG2.Search(option)); 1634 break; 1635 1636 case 'search2': 1637 return nG2.Search2(option, value); 1638 break; 1639 1640 case 'search2Execute': 1641 return nG2.Search2Execute(); 1642 break; 1643 1644 case 'refresh': 1645 nG2.Refresh(); 1646 break; 1647 1648 case 'resize': 1649 nG2.Resize(); 1650 break; 1651 1652 case 'instance': 1653 return nG2; 1654 break; 1655 1656 case 'data': 1657 nG2.data= { 1658 items: nG2.I, 1659 gallery: nG2.GOM, 1660 lightbox: nG2.VOM, 1661 shoppingcart: nG2.shoppingCart 1662 }; 1663 return nG2.data; 1664 break; 1665 1666 case 'reload': 1667 nG2.ReloadAlbum(); 1668 return $(this); 1669 break; 1670 1671 case 'itemsSelectedGet': 1672 return nG2.ItemsSelectedGet(); 1673 break; 1674 1675 case 'itemsSetSelectedValue': 1676 nG2.ItemsSetSelectedValue(option, value); 1677 break; 1678 1679 case 'option': 1680 if(typeof value === 'undefined'){ 1681 return nG2.Get(option); 1682 }else{ 1683 nG2.Set(option,value); 1684 if( option == 'demoViewportWidth' ) { 1685 // force resize event -> for demo purposes 1686 $(window).trigger('resize'); 1687 } 1688 } 1689 break; 1690 1691 case 'destroy': 1692 nG2.Destroy(); 1693 $(this).removeData('nanogallery2data'); 1694 break; 1695 1696 case 'shoppingCartGet': 1697 // returns the content of the shoppingcart 1698 return nG2.shoppingCart; 1699 break; 1700 1701 case 'shoppingCartUpdate': 1702 // parameters : 1703 // - option = item's ID 1704 // - value = new quantity 1705 1706 if( typeof value === 'undefined' || typeof option === 'undefined' ){ 1707 return false; 1708 } 1709 1710 var item_ID = option; 1711 var new_qty = value; 1712 1713 for( var i=0; i < nG2.shoppingCart.length; i++) { 1714 if( nG2.shoppingCart[i].ID == item_ID ) { 1715 1716 // updates counter 1717 nG2.shoppingCart[i].qty = new_qty; 1718 1719 let item = nG2.I[nG2.shoppingCart[i].idx]; 1720 1721 // updates thumbnail 1722 nG2.ThumbnailToolbarOneCartUpdate( item ); 1723 1724 if( new_qty == 0 ) { 1725 // removes item from shoppingcart 1726 nG2.shoppingCart.splice(i, 1); 1727 } 1728 1729 var fu = nG2.O.fnShoppingCartUpdated; 1730 if( fu !== null ) { 1731 typeof fu == 'function' ? fu(nG2.shoppingCart, item, 'api') : window[fu](nG2.shoppingCart, item, 'api'); 1732 } 1733 1734 break; 1735 } 1736 } 1737 1738 return nG2.shoppingCart; 1739 break; 1740 1741 case 'shoppingCartRemove': 1742 // parameters : 1743 // - option = item's ID 1744 if( typeof option === 'undefined' ){ 1745 return false; 1746 } 1747 var ID = option; 1748 for( var i=0; i < nG2.shoppingCart.length; i++) { 1749 if( nG2.shoppingCart[i].ID == ID ) { 1750 1751 var item = nG2.I[nG2.shoppingCart[i].idx]; 1752 1753 // updates thumbnail 1754 nG2.shoppingCart[i].qty = 0; 1755 nG2.ThumbnailToolbarOneCartUpdate( item ); 1756 1757 // removes item from shoppingcart 1758 nG2.shoppingCart.splice(i, 1); 1759 1760 1761 var fu = nG2.O.fnShoppingCartUpdated; 1762 if( fu !== null ) { 1763 typeof fu == 'function' ? fu(nG2.shoppingCart, item, 'api') : window[fu](nG2.shoppingCart, item, 'api'); 1764 } 1765 1766 break; 1767 } 1768 } 1769 1770 return nG2.shoppingCart; 1771 break; 1772 1773 case 'closeViewer': 1774 nG2.CloseViewer(); 1775 break; 1776 case 'minimizeToolbar': 1777 nG2.MinimizeToolbar(); 1778 break; 1779 case 'maximizeToolbar': 1780 nG2.MaximizeToolbar(); 1781 break; 1782 case 'paginationPreviousPage': 1783 nG2.PaginationPreviousPage(); 1784 break; 1785 case 'paginationNextPage': 1786 nG2.paginationNextPage(); 1787 break; 1788 case 'paginationGotoPage': 1789 nG2.PaginationGotoPage( option ); 1790 break; 1791 case 'paginationCountPages': 1792 nG2.PaginationCountPages(); 1793 break; 1794 1795 } 1796 return $(this); 1797 1798 } 1799 }; 1800 1801 1802 // ############################### 1803 // ##### nanogallery2 script ##### 1804 // ############################### 1805 1806 /** @function nanoGALLERY2 */ 1807 function nanoGALLERY2() { 1808 "use strict"; 1809 1810 /** 1811 * Force reload the current album, if provided by Json 1812 */ 1813 this.LightboxReOpen = function(){ 1814 LightboxStandaloneDisplay(); 1815 } 1816 1817 /** 1818 * Force reload the current album, if provided by Json 1819 */ 1820 this.ReloadAlbum = function(){ 1821 if( G.O.kind === '' ) { 1822 throw 'Not supported for this content source:' + G.O.kind; 1823 } 1824 1825 var albumIdx = G.GOM.albumIdx; 1826 if( albumIdx == -1 ) { 1827 throw ('Current album not found.'); 1828 } 1829 1830 var albumID = G.I[albumIdx].GetID(); 1831 1832 // unselect everything & remove link to album (=logical delete) 1833 var l = G.I.length; 1834 for( var i = 0; i < l ; i++ ) { 1835 var item = G.I[i]; 1836 if( item.albumID == albumID ) { 1837 item.selected = false; 1838 } 1839 } 1840 1841 G.I[albumIdx].contentIsLoaded = false; 1842 1843 DisplayAlbum('-1', albumID); 1844 }; 1845 1846 /** 1847 * Set one or several items selected/unselected 1848 * @param {array} items 1849 */ 1850 this.ItemsSetSelectedValue = function(items, value){ 1851 var l = items.length; 1852 for( var j = 0; j < l ; j++) { 1853 ThumbnailSelectionSet(items[j], value); 1854 } 1855 }; 1856 1857 /** 1858 * Returns an array of selected items 1859 * @returns {Array} 1860 */ 1861 this.ItemsSelectedGet = function(){ 1862 var selectedItems = []; 1863 var l = G.I.length; 1864 for( var i = 0; i < l ; i++ ) { 1865 if( G.I[i].selected == true ) { 1866 selectedItems.push(G.I[i]); 1867 } 1868 } 1869 return selectedItems; 1870 }; 1871 1872 /** 1873 * Returns the value of an option 1874 * @param {string} option 1875 * @returns {nanoGALLERY.G.O} 1876 */ 1877 this.Get = function(option){ 1878 return G.O[option]; 1879 }; 1880 1881 /** 1882 * Set a new value for a defined option 1883 * @param {string} option 1884 */ 1885 this.Set = function(option, value){ 1886 G.O[option] = value; 1887 switch( option ) { 1888 case 'thumbnailSelectable': 1889 ThumbnailSelectionClear(); 1890 // refresh the displayed gallery 1891 GalleryRender( G.GOM.albumIdx ); 1892 break; 1893 } 1894 }; 1895 1896 /** 1897 * Refresh the current gallery 1898 */ 1899 this.Refresh = function() { 1900 // Refresh the displayed gallery 1901 GalleryRender( G.GOM.albumIdx ); 1902 }; 1903 /** 1904 * Resize the current gallery 1905 */ 1906 this.Resize = function() { 1907 // resize the displayed gallery 1908 GalleryResize(); 1909 }; 1910 1911 /** 1912 * display one item (image or gallery) 1913 * itemID syntax: 1914 * - albumID --> display one album 1915 * - albumID/imageID --> display one image 1916 */ 1917 this.DisplayItem = function( itemID ) { 1918 var IDs=parseIDs( itemID ); 1919 if( IDs.imageID != '0' ) { 1920 DisplayPhoto( IDs.imageID, IDs.albumID ); 1921 } 1922 else { 1923 DisplayAlbum( '-1', IDs.albumID ); 1924 } 1925 }; 1926 1927 this.ThumbnailToolbarOneCartUpdate = function ( item ) { 1928 ThumbnailBuildToolbarOneCartUpdate( item ); 1929 } 1930 1931 1932 1933 var CountItemsToDisplay = function( gIdx ) { 1934 if( G.I[gIdx] == undefined ) { return 0; } 1935 var albumID = G.I[gIdx].GetID(); 1936 var l = G.I.length; 1937 var cnt = 0; 1938 for( var idx = 0; idx < l; idx++ ) { 1939 var item = G.I[idx]; 1940 if( item.isToDisplay(albumID) ) { 1941 cnt++; 1942 } 1943 } 1944 return cnt; 1945 } 1946 /** 1947 * Search in the displayed gallery (in thumbnails title) 1948 */ 1949 this.Search = function( search ) { 1950 G.GOM.albumSearch = search.toUpperCase(); 1951 var gIdx = G.GOM.albumIdx; 1952 GalleryRender( G.GOM.albumIdx ); 1953 return CountItemsToDisplay( gIdx ); 1954 }; 1955 1956 /** 1957 * Search2 in title and tags - set search values 1958 */ 1959 this.Search2 = function( searchTitle, searchTags ) { 1960 if( searchTitle != undefined && searchTitle != null ) { 1961 G.GOM.albumSearch = searchTitle.toUpperCase().trim(); 1962 } 1963 else { 1964 G.GOM.albumSearch = ''; 1965 } 1966 1967 if( searchTags != null && searchTags != undefined ) { 1968 G.GOM.albumSearchTags = searchTags.toUpperCase().trim(); 1969 } 1970 else { 1971 G.GOM.albumSearchTags = ''; 1972 } 1973 return CountItemsToDisplay( G.GOM.albumIdx ); 1974 }; 1975 1976 /** 1977 * Search2 - execute the search on title and tags 1978 */ 1979 this.Search2Execute = function() { 1980 var gIdx = G.GOM.albumIdx; 1981 GalleryRender( G.GOM.albumIdx ); 1982 return CountItemsToDisplay( gIdx ); 1983 }; 1984 1985 1986 /** 1987 * Destroy the current gallery 1988 */ 1989 this.Destroy = function(){ 1990 1991 if( G.GOM.hammertime != null ) { 1992 G.GOM.hammertime.destroy(); 1993 G.GOM.hammertime = null; 1994 } 1995 1996 if( G.VOM.hammertime != null ) { 1997 G.VOM.hammertime.destroy(); 1998 G.VOM.hammertime = null; 1999 } 2000 2001 // color scheme 2002 $('#ngycs_' + G.baseEltID).remove() 2003 2004 G.GOM.items = []; 2005 NGY2Item.New( G, G.i18nTranslations.breadcrumbHome, '', '0', '-1', 'album' ); 2006 G.GOM.navigationBar.$newContent = null; 2007 G.$E.base.empty(); 2008 G.$E.base.removeData(); 2009 if( G.O.locationHash ) { 2010 jQuery(window).off('hashchange.nanogallery2.' + G.baseEltID); 2011 } 2012 2013 jQuery(window).off('resize.nanogallery2.' + G.baseEltID); 2014 jQuery(window).off('orientationChange.nanogallery2.' + G.baseEltID); 2015 jQuery(window).off('scroll.nanogallery2.' + G.baseEltID); 2016 if( G.$E.scrollableParent !== null ) { 2017 G.$E.scrollableParent.off('scroll.nanogallery2.' + G.baseEltID); 2018 } 2019 G.GOM.firstDisplay = true; 2020 }; 2021 2022 /** 2023 * CloseViewer - close the media viewer 2024 */ 2025 this.CloseViewer = function() { 2026 LightboxClose(null); 2027 return false; 2028 }; 2029 2030 /** 2031 * MinimizeToolbar - display the minimized lightbox main toolbar 2032 */ 2033 this.MinimizeToolbar = function() { 2034 ViewerToolbarForVisibilityMin(); 2035 return false; 2036 }; 2037 2038 /** 2039 * MaximizeToolbar - display the maximized/standard lightbox main toolbar 2040 */ 2041 this.MaximizeToolbar = function() { 2042 ViewerToolbarForVisibilityStd(); 2043 return false; 2044 }; 2045 2046 /** 2047 * PaginationPreviousPage - gallery paginate to previous page 2048 */ 2049 this.PaginationPreviousPage = function() { 2050 paginationPreviousPage(); 2051 return false; 2052 }; 2053 2054 2055 /** 2056 * PaginationNextPage - gallery paginate to next page 2057 */ 2058 this.PaginationNextPage = function() { 2059 paginationNextPage(); 2060 return false; 2061 }; 2062 2063 2064 /** 2065 * PaginationGotoPage - gallery paginate to specific page 2066 */ 2067 this.PaginationGotoPage = function( page ) { 2068 // var aIdx = G.$E.conPagin.data('galleryIdx'); 2069 if( page > 1 ) { page--; } 2070 G.GOM.pagination.currentPage = page; 2071 2072 // scroll to top of gallery if not displayed 2073 G.GOM.ScrollToTop(); 2074 2075 GalleryDisplayPart1(); 2076 GalleryDisplayPart2( true ); 2077 return false; 2078 }; 2079 2080 /** 2081 * PaginationCountPages - gallery pagination - returns the number of pages 2082 */ 2083 this.PaginationCountPages = function() { 2084 if( G.GOM.items.length == 0 ) { return 0; } // no thumbnail to display 2085 2086 var nbPages = Math.ceil((G.GOM.items[G.GOM.items.length - 1].row + 1) / G.galleryMaxRows.Get()); 2087 return nbPages; 2088 }; 2089 2090 /** 2091 * PaginationCountPages - gallery pagination - returns the number of pages 2092 */ 2093 2094 2095 2096 // throttle() 2097 // author: underscore.js - http://underscorejs.org/docs/underscore.html 2098 // Returns a function, that, when invoked, will only be triggered at most once during a given window of time. 2099 // Normally, the throttled function will run as much as it can, without ever going more than once per wait duration; 2100 // but if youÂ’d like to disable the execution on the leading edge, pass {leading: false}. 2101 // To disable execution on the trailing edge, ditto. 2102 var throttle = function(func, wait, options) { 2103 var context, args, result; 2104 var timeout = null; 2105 var previous = 0; 2106 if (!options) options = {}; 2107 var later = function() { 2108 previous = options.leading === false ? 0 : new Date().getTime(); 2109 timeout = null; 2110 result = func.apply(context, args); 2111 if (!timeout) context = args = null; 2112 }; 2113 return function() { 2114 var now = new Date().getTime(); 2115 if (!previous && options.leading === false) previous = now; 2116 var remaining = wait - (now - previous); 2117 context = this; 2118 args = arguments; 2119 if (remaining <= 0 || remaining > wait) { 2120 if (timeout) { 2121 clearTimeout(timeout); 2122 timeout = null; 2123 } 2124 previous = now; 2125 result = func.apply(context, args); 2126 if (!timeout) context = args = null; 2127 } else if (!timeout && options.trailing !== false) { 2128 timeout = setTimeout(later, remaining); 2129 } 2130 return result; 2131 }; 2132 }; 2133 2134 2135 // DEBOUNCE 2136 // author: John Hann - http://unscriptable.com/2009/03/20/debouncing-javascript-methods/ 2137 // execAsap - false means executing at the end of the detection period 2138 var debounce = function (func, threshold, execAsap) { 2139 var timeout; 2140 return function debounced () { 2141 var obj = this, args = arguments; 2142 function delayed () { 2143 if (!execAsap) 2144 func.apply(obj, args); 2145 timeout = null; 2146 }; 2147 2148 if (timeout) 2149 clearTimeout(timeout); 2150 // clearRequestTimeout(timeout); 2151 else if (execAsap) 2152 func.apply(obj, args); 2153 timeout = setTimeout(delayed, threshold || 100); 2154 // timeout = requestTimeout(delayed, threshold || 100); 2155 }; 2156 } 2157 2158 2159 // Double requestAnimationFrame 2160 window.ng_draf = function (cb) { 2161 return requestAnimationFrame(function() { 2162 window.requestAnimationFrame(cb) 2163 }) 2164 } 2165 2166 // REQUESTTIMEOUT - replace SETTIMEOUT - https://gist.github.com/joelambert/1002116 2167 /** 2168 * Behaves the same as setTimeout except uses requestAnimationFrame() where possible for better performance 2169 * @param {function} fn The callback function 2170 * @param {int} delay The delay in milliseconds 2171 */ 2172 2173 window.requestTimeout = function(fn, delay) { 2174 if( !window.requestAnimationFrame && 2175 !window.webkitRequestAnimationFrame && 2176 !(window.mozRequestAnimationFrame && window.mozCancelRequestAnimationFrame) && // Firefox 5 ships without cancel support 2177 !window.oRequestAnimationFrame && 2178 !window.msRequestAnimationFrame) 2179 return window.setTimeout(fn, delay); 2180 2181 var start = new Date().getTime(), 2182 handle = new Object(); 2183 2184 function loop(){ 2185 var current = new Date().getTime(), 2186 delta = current - start; 2187 // delta = delay; 2188 2189 delta >= delay ? fn.call() : handle.value = requestAnimFrame(loop); 2190 }; 2191 2192 handle.value = requestAnimFrame(loop); 2193 return handle; 2194 }; 2195 2196 2197 // requestAnimationFrame() shim by Paul Irish 2198 // http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 2199 window.requestAnimFrame = (function() { 2200 return window.requestAnimationFrame || 2201 window.webkitRequestAnimationFrame || 2202 window.mozRequestAnimationFrame || 2203 window.oRequestAnimationFrame || 2204 window.msRequestAnimationFrame || 2205 function(/* function */ callback, /* DOMElement */ element){ 2206 window.setTimeout(callback, 1000 / 60); 2207 }; 2208 })(); 2209 2210 2211 // CLEARREQUESTTIMEOUT - to replace CLEARTIMEOUT - https://gist.github.com/joelambert/1002116 2212 /** 2213 * Behaves the same as clearTimeout except uses cancelRequestAnimationFrame() where possible for better performance 2214 * @param {int|object} fn The callback function 2215 */ 2216 window.clearRequestTimeout = function(handle) { 2217 window.cancelAnimationFrame ? window.cancelAnimationFrame(handle.value) : 2218 window.webkitCancelAnimationFrame ? window.webkitCancelAnimationFrame(handle.value) : 2219 window.webkitCancelRequestAnimationFrame ? window.webkitCancelRequestAnimationFrame(handle.value) : /* Support for legacy API */ 2220 window.mozCancelRequestAnimationFrame ? window.mozCancelRequestAnimationFrame(handle.value) : 2221 window.oCancelRequestAnimationFrame ? window.oCancelRequestAnimationFrame(handle.value) : 2222 window.msCancelRequestAnimationFrame ? window.msCancelRequestAnimationFrame(handle.value) : 2223 clearTimeout(handle); 2224 }; 2225 2226 2227 2228 /* 2229 ** Global data for this nanogallery2 instance 2230 **/ 2231 var G=this; 2232 G.I = []; // gallery items 2233 G.Id = []; // gallery items 2234 G.O = null; // user options 2235 G.baseEltID = null; // ID of the base element 2236 G.$E = { 2237 base: null, // base element 2238 conTnParent: null, // $g_containerThumbnailsParent 2239 conLoadingB: null, // loading bar - nanoGalleryLBarOff 2240 conConsole: null, // console for error messages 2241 conNavigationBar: null, // gallery navigation bar 2242 conTnBottom: null, // container on the bottom of the gallery 2243 scrollableParent: null // first scrollable parent container 2244 }; 2245 G.shoppingCart = []; 2246 G.layout = { // Layout informations 2247 internal : true, 2248 engine : '', 2249 support : { rows: false }, 2250 prerequisite : { imageSize: false }, 2251 SetEngine: function() { 2252 2253 if( G.layout.internal ) { 2254 if( G.tn.settings.width[G.GOM.curNavLevel][G.GOM.curWidth] == 'auto' || G.tn.settings.width[G.GOM.curNavLevel][G.GOM.curWidth] == '' ) { 2255 // do not use getH() / getW() here! 2256 G.layout.engine = 'JUSTIFIED'; 2257 G.layout.support.rows = true; 2258 G.layout.prerequisite.imageSize = true; 2259 return; 2260 } 2261 if( G.tn.settings.height[G.GOM.curNavLevel][G.GOM.curWidth] == 'auto' || G.tn.settings.height[G.GOM.curNavLevel][G.GOM.curWidth] == '' ) { 2262 // do not use getH() / getW() here! 2263 G.layout.engine = 'CASCADING'; 2264 G.layout.support.rows = false; 2265 G.layout.prerequisite.imageSize = true; 2266 return; 2267 } 2268 2269 if( G.tn.settings.getMosaic() != null ) { 2270 G.layout.engine = 'MOSAIC'; 2271 G.layout.support.rows = true; 2272 G.layout.prerequisite.imageSize = false; 2273 return; 2274 } 2275 2276 G.layout.engine = 'GRID'; 2277 G.layout.support.rows=true; 2278 // if( G.tn.opt.Get('crop') === true ) { 2279 // G.layout.prerequisite.imageSize = true; 2280 // } 2281 // else { 2282 G.layout.prerequisite.imageSize = false; 2283 // } 2284 } 2285 } 2286 }; 2287 G.galleryResizeEventEnabled = false; 2288 G.galleryMaxRows = { l1: 0, lN: 0, 2289 Get: function() { 2290 return G.galleryMaxRows[G.GOM.curNavLevel]; 2291 } 2292 }; 2293 G.galleryMaxItems = { l1: 0, lN: 0, 2294 Get: function() { 2295 return G.galleryMaxItems[G.GOM.curNavLevel]; 2296 } 2297 }; 2298 G.galleryFilterTags = { l1: 0, lN: 0, 2299 Get: function() { 2300 return G.galleryFilterTags[G.GOM.curNavLevel]; 2301 } 2302 }; 2303 G.galleryFilterTagsMode = { l1: 0, lN: 0, 2304 Get: function() { 2305 return G.galleryFilterTagsMode[G.GOM.curNavLevel]; 2306 } 2307 }; 2308 G.galleryDisplayMode = { l1: 'FULLCONTENT', lN: 'FULLCONTENT', 2309 Get: function() { 2310 return G.galleryDisplayMode[G.GOM.curNavLevel]; 2311 } 2312 }; 2313 G.galleryLastRowFull = { l1: false, lN: false, 2314 Get: function() { 2315 return G.galleryLastRowFull[G.GOM.curNavLevel]; 2316 } 2317 }; 2318 G.gallerySorting = { l1: '', lN: '', 2319 Get: function() { 2320 return G.gallerySorting[G.GOM.curNavLevel]; 2321 } 2322 }; 2323 G.galleryDisplayTransition = { l1: 'none', lN: 'none', 2324 Get: function() { 2325 return G.galleryDisplayTransition[G.GOM.curNavLevel]; 2326 } 2327 }; 2328 G.galleryDisplayTransitionDuration = { l1: 500, lN: 500, 2329 Get: function() { 2330 return G.galleryDisplayTransitionDuration[G.GOM.curNavLevel]; 2331 } 2332 }; 2333 G.$currentTouchedThumbnail = null; 2334 2335 2336 2337 2338 // ##### GENERAL THUMBNAILS PROPERTIES --> 2339 G.tn = { 2340 // levell specific options 2341 opt: { 2342 l1: { crop: true, stacks: 0, stacksTranslateX: 0, stacksTranslateY: 0, stacksTranslateZ: 0, stacksRotateX: 0, stacksRotateY: 0, stacksRotateZ: 0, stacksScale: 0, borderHorizontal: 0, borderVertical: 0, baseGridHeight: 0, displayTransition: 'FADEIN', displayTransitionStartVal: 0, displayTransitionEasing: 'easeOutQuart', displayTransitionDuration: 240, displayInterval: 15 }, 2343 lN: { crop: true, stacks: 0, stacksTranslateX: 0, stacksTranslateY: 0, stacksTranslateZ: 0, stacksRotateX: 0, stacksRotateY: 0, stacksRotateZ: 0, stacksScale: 0, borderHorizontal: 0, borderVertical: 0, baseGridHeight: 0, displayTransition: 'FADEIN', displayTransitionStartVal: 0, displayTransitionEasing: 'easeOutQuart', displayTransitionDuration: 240, displayInterval: 15 }, 2344 Get: function(opt) { 2345 return G.tn.opt[G.GOM.curNavLevel][opt]; 2346 } 2347 }, 2348 scale: 1, // image scale depending of the hover effect 2349 labelHeight: { // in case label on bottom, otherwise always=0 2350 l1: 0, lN: 0, 2351 get: function() { 2352 return G.tn.labelHeight[G.GOM.curNavLevel]; 2353 } 2354 }, 2355 defaultSize: { // default thumbnail size 2356 // label height is not included 2357 width: { l1 : { xs:0, sm:0, me:0, la:0, xl:0 }, lN : { xs:0, sm:0, me:0, la:0, xl:0 } }, 2358 height: { l1 : { xs:0, sm:0, me:0, la:0, xl:0 }, lN : { xs:0, sm:0, me:0, la:0, xl:0 } }, 2359 getWidth: function() { 2360 return G.tn.defaultSize.width[G.GOM.curNavLevel][G.GOM.curWidth]; 2361 }, 2362 getOuterWidth: function() { // width including border 2363 G.tn.borderWidth = G.tn.opt.Get('borderHorizontal'); 2364 G.tn.borderHeight = G.tn.opt.Get('borderVertical'); 2365 var w = G.tn.defaultSize.width[G.GOM.curNavLevel][G.GOM.curWidth] + G.tn.opt.Get('borderHorizontal') * 2; 2366 if( G.O.thumbnailLabel.get('position') == 'right' || G.O.thumbnailLabel.get('position') == 'left' ) { 2367 w += G.tn.defaultSize.width[G.GOM.curNavLevel][G.GOM.curWidth]; 2368 } 2369 return w; 2370 }, 2371 getHeight: function() { 2372 return G.tn.defaultSize.height[G.GOM.curNavLevel][G.GOM.curWidth]; 2373 }, 2374 getOuterHeight: function() { // height, border included 2375 return G.tn.defaultSize.height[G.GOM.curNavLevel][G.GOM.curWidth]+G.tn.opt.Get('borderVertical')*2; 2376 } 2377 }, 2378 settings: { // user defined width/height of the image to display depending on the screen size 2379 width: { l1 : { xs:0, sm:0, me:0, la:0, xl:0, xsc:'u', smc:'u', mec:'u', lac:'u', xlc:'u' }, 2380 lN : { xs:0, sm:0, me:0, la:0, xl:0, xsc:'u', smc:'u', mec:'u', lac:'u', xlc:'u' } }, 2381 height: { l1 : { xs:0, sm:0, me:0, la:0, xl:0, xsc:'u', smc:'u', mec:'u', lac:'u', xlc:'u' }, 2382 lN : { xs:0, sm:0, me:0, la:0, xl:0, xsc:'u', smc:'u', mec:'u', lac:'u', xlc:'u' } }, 2383 getH: function(l, w) { 2384 var cl = (l == undefined ? G.GOM.curNavLevel : l); 2385 var cw = (w == undefined ? G.GOM.curWidth : w); 2386 if( G.layout.engine == 'MOSAIC' ) { 2387 return this.height[cl][cw] * this.mosaic[cl+'Factor']['h'][cw]; 2388 } 2389 else { 2390 return this.height[cl][cw]; 2391 } 2392 }, 2393 getW: function(l, w) { 2394 var cl = (l == undefined ? G.GOM.curNavLevel : l); 2395 var cw = (w == undefined ? G.GOM.curWidth : w); 2396 if( G.layout.engine == 'MOSAIC' ) { 2397 return this.width[cl][cw] * this.mosaic[cl+'Factor']['w'][cw]; 2398 } 2399 else { 2400 return this.width[cl][cw]; 2401 // return G.tn.settings.width[G.GOM.curNavLevel][G.GOM.curWidth]; 2402 } 2403 }, 2404 mosaic: { l1 : { xs: null, sm: null, me: null, la: null, xl: null }, 2405 lN : { xs: null, sm: null, me: null, la: null, xl: null }, 2406 l1Factor : { h :{ xs: 1, sm: 1, me: 1, la: 1, xl: 1 }, w :{ xs: 1, sm: 1, me: 1, la: 1, xl: 1 }}, 2407 lNFactor : { h :{ xs: 1, sm: 1, me: 1, la: 1, xl: 1 }, w :{ xs: 1, sm: 1, me: 1, la: 1, xl: 1 }} 2408 }, 2409 getMosaic: function() { 2410 return this.mosaic[G.GOM.curNavLevel][G.GOM.curWidth]; 2411 }, 2412 mosaicCalcFactor: function(l, w) { 2413 // retrieve max size multiplicator 2414 var maxW = 1; 2415 var maxH = 1; 2416 for( var n = 0; n < G.tn.settings.mosaic[l][w].length; n++ ) { 2417 maxW = Math.max(maxW, this.mosaic[l][w][n]['w']); 2418 maxH = Math.max(maxH, this.mosaic[l][w][n]['h']); 2419 } 2420 this.mosaic[l + 'Factor']['h'][w] = maxH; 2421 this.mosaic[l + 'Factor']['w'][w] = maxW; 2422 }, 2423 gutterHeight: { l1 : { xs:0, sm:0, me:0, la:0, xl:0 }, lN : { xs:0, sm:0, me:0, la:0, xl:0 } }, 2424 gutterWidth: { l1 : { xs:0, sm:0, me:0, la:0, xl:0 }, lN : { xs:0, sm:0, me:0, la:0, xl:0 } }, 2425 GetResponsive: function( setting ) { 2426 return this[setting][G.GOM.curNavLevel][G.GOM.curWidth]; 2427 } 2428 }, 2429 // thumbnail hover effects 2430 hoverEffects : { 2431 std : [], 2432 level1: [], 2433 get: function() { 2434 if( G.GOM.curNavLevel == 'l1' && this.level1.length !== 0 ) { 2435 return this.level1; 2436 } 2437 else { 2438 return this.std; 2439 } 2440 } 2441 }, 2442 // thumbnail init 2443 buildInit : { 2444 std : [], 2445 level1: [], 2446 get: function() { 2447 if( G.GOM.curNavLevel == 'l1' && this.level1.length !== 0 ) { 2448 return this.level1; 2449 } 2450 else { 2451 return this.std; 2452 } 2453 } 2454 }, 2455 // thumbnail toolbars 2456 toolbar: { 2457 album : { topLeft : '', topRight: '', bottomLeft: '', bottomRight: '' }, 2458 image : { topLeft : '', topRight: '', bottomLeft: '', bottomRight: '' }, 2459 albumUp : { topLeft : '', topRight: '', bottomLeft: '', bottomRight: '' }, 2460 get: function( item ) { 2461 return this[item.kind]; 2462 }, 2463 }, 2464 style: { 2465 // inline CSS 2466 l1 : { label: '', title: '', desc: '' }, 2467 lN : { label: '', title: '', desc: '' }, 2468 getTitle : function() { 2469 return ('style="' + this[G.GOM.curNavLevel].title + '"'); 2470 }, 2471 getDesc : function() { 2472 return ('style="' + this[G.GOM.curNavLevel].desc + '"'); 2473 }, 2474 getLabel: function() { 2475 var s='style="'+ this[G.GOM.curNavLevel].label; 2476 s+= (G.O.RTL ? '"direction:RTL;"' :''); 2477 s+='"'; 2478 return s; 2479 } 2480 } 2481 }; 2482 G.scrollTimeOut = 0; 2483 G.i18nTranslations = {'paginationPrevious':'Previous', 'paginationNext':'Next', 'breadcrumbHome':'List of Albums', 'thumbnailImageTitle':'', 'thumbnailAlbumTitle':'', 'thumbnailImageDescription':'', 'thumbnailAlbumDescription':'' }; 2484 G.emptyGif = 'data:image/gif;base64,R0lGODlhEAAQAIAAAP///////yH5BAEKAAEALAAAAAAQABAAAAIOjI+py+0Po5y02ouzPgUAOw=='; 2485 G.CSStransformName = FirstSupportedPropertyName(["transform", "msTransform", "MozTransform", "WebkitTransform", "OTransform"]); 2486 // G.CSSfilterName = FirstSupportedPropertyName(["filter", "WebkitFilter"]); 2487 G.CSStransformStyle = FirstSupportedPropertyName(["transformStyle", "msTransformStyle", "MozTransformStyle", "WebkitTransformStyle", "OTransformStyle"]); 2488 G.CSSperspective = FirstSupportedPropertyName(["perspective", "msPerspective", "MozPerspective", "WebkitPerspective", "OPerspective"]); 2489 G.CSSbackfaceVisibilityName = FirstSupportedPropertyName(["backfaceVisibility", "msBackfaceVisibility", "MozBackfaceVisibility", "WebkitBackfaceVisibility", "OBackfaceVisibility"]); 2490 G.CSStransitionName = FirstSupportedPropertyName(["transition", "msTransition", "MozTransition", "WebkitTransition", "OTransition"]); 2491 G.CSSanimationName = FirstSupportedPropertyName(["animation", "msAnimation", "MozAnimation", "WebkitAnimation", "OAnimation"]); 2492 G.GalleryResizeThrottled = throttle(GalleryResize, 15, {leading: false}); 2493 2494 G.blockList = null; // album names - block list 2495 G.allowList = null; // album names - allow list 2496 G.albumList = []; // album list 2497 G.locationHashLastUsed = ''; 2498 G.custGlobals = {}; 2499 G.touchAutoOpenDelayTimerID = 0; 2500 G.i18nLang = ''; 2501 G.timeLastTouchStart = 0; 2502 G.custGlobals = {}; 2503 G.markupOrApiProcessed = false; 2504 2505 //------------------------ 2506 //--- Gallery Object Model 2507 G.GOM = { 2508 albumIdx : -1, // index (in G.I) of the currently displayed album 2509 clipArea : { top: 0, height: 0 }, // area of the GOM to display on screen 2510 displayArea : { width: 0 , height: 0 }, // size of the GOM area (=used area, not available area) 2511 displayAreaLast : { width: 0 , height: 0 }, // previous size of the GOM area 2512 displayedMoreSteps : 0, // current number of displayed steps (moreButton mode) 2513 items: [], // current items of the GOMS 2514 $imgPreloader: [], 2515 thumbnails2Display: [], 2516 itemsDisplayed : 0, // number of currently displayed thumbnails 2517 firstDisplay : true, 2518 firstDisplayTime : 0, // in conjunction with galleryRenderDelay 2519 navigationBar : { // content of the navigation bar (for breadcrumb, filter tags and next/previous pagination) 2520 displayed: false, 2521 $newContent: '' 2522 }, 2523 cache : { // cached data 2524 viewport: null, 2525 containerOffset: null, 2526 areaWidth: 100 // available area width 2527 }, 2528 nbSelected : 0, // number of selected items 2529 pagination : { currentPage: 0 }, // pagination data 2530 panThreshold: 60, // threshold value (in pixels) to block horizontal pan/swipe 2531 panYOnly: false, // threshold value reach -> definitively block horizontal pan until end of pan 2532 lastFullRow : -1, // number of the last row without holes 2533 lastDisplayedIdx: -1, // used to display the counter of not displayed items 2534 displayInterval : { from: 0, len: 0 }, 2535 hammertime: null, 2536 curNavLevel: 'l1', // current navigation level (l1 or LN) 2537 curWidth: 'me', 2538 albumSearch: '', // current search string -> title (used to filter the thumbnails on screen) 2539 albumSearchTags: '', // current search string -> tags 2540 lastZIndex: 0, // used to put a thumbnail on top of all others (for exemple for scale hover effect) 2541 lastRandomValue: 0, 2542 slider : { // slider on last thumbnail 2543 hostIdx: -1, // idx of the thumbnail hosting the slider 2544 hostItem: null, // item hosting the slider 2545 currentIdx: 0, // idx of the current displayed item 2546 nextIdx: 0, // idx of the next item to display in the slider 2547 timerID: 0, 2548 tween: null // tranistion tween instance 2549 }, 2550 NGY2Item: function( idx ) { // returns a NGY2Item or null if it does not exist 2551 if( G.GOM.items[idx] == undefined || G.GOM.items[idx] == null ) { return null; } 2552 var i = G.GOM.items[idx].thumbnailIdx; 2553 return G.I[i]; 2554 }, 2555 // One GOM item (thumbnail) 2556 // function GTn(index, width, height) { 2557 GTn: function(index, width, height) { 2558 this.thumbnailIdx = index; 2559 this.width = 0; // thumbnail width 2560 this.height = 0; // thumbnail height 2561 this.top = 0; // position: top 2562 this.left = 0; // position: left 2563 this.row = 0; // position: row number 2564 this.imageWidth = width; // image width 2565 this.imageHeight = height; // image height 2566 this.resizedContentWidth = 0; 2567 this.resizedContentHeight = 0; 2568 this.displayed = false; 2569 this.neverDisplayed = true; 2570 this.inDisplayArea = false; 2571 }, 2572 // Position the top of the gallery to make it visible, if not displayed 2573 ScrollToTop: function() { 2574 2575 if( G.GOM.firstDisplay ) { 2576 // do no scroll to top on first display 2577 return; 2578 } 2579 2580 if( G.$E.scrollableParent === null && !topInViewportVert(G.$E.base, 20) ) { 2581 // $('html, body').animate({scrollTop: G.$E.base.offset().top}, 200); 2582 G.$E.base.get(0).scrollIntoView(); 2583 } 2584 2585 if( G.$E.scrollableParent !== null ) { 2586 // gallery in a scrollable container: check if we have to scroll up so that the top of the gallery is visible 2587 // vertical position of the scrollbar 2588 var scrollTop = G.$E.scrollableParent.scrollTop(); 2589 // top of the gallery relative to the top of the scrollable container 2590 var dist = Math.abs(G.$E.scrollableParent.offset().top - G.$E.base.offset().top - scrollTop); 2591 if( scrollTop > dist ) { 2592 window.ng_draf( function() { 2593 // we need a little delay before setting the new scrollbar (but why?....) 2594 G.$E.scrollableParent.scrollTop(dist); 2595 }); 2596 } 2597 } 2598 } 2599 }; 2600 2601 2602 //------------------------ 2603 //--- Viewer Object Model 2604 2605 G.VOM = { 2606 viewerDisplayed: false, // is the viewer currently displayed 2607 viewerIsFullscreen: false, // viewer in fullscreen mode 2608 infoDisplayed: false, // is the info box displayed 2609 toolbarsDisplayed: true, // the toolbars are displayed 2610 toolsHide: null, 2611 zoom : { 2612 posX: 0, // position to center zoom in/out 2613 posY: 0, 2614 userFactor: 1, // user zoom factor (applied to the baseZoom factor) 2615 isZooming: false 2616 }, 2617 padding: { H: 0, V: 0 }, // padding for the image 2618 window: { lastWidth: 0, lastHeight: 0 }, 2619 $viewer: null, 2620 $toolbar: null, // viewerToolbar 2621 $toolbarTL: null, // viewer toolbar on top left 2622 $toolbarTR: null, // viewer toolbar on top right 2623 2624 toolbarMode: 'std', // current toolbar mode (standard, minimized) 2625 playSlideshow : false, // slide show mode status 2626 playSlideshowTimerID: 0, // slideshow mode time 2627 slideshowDelay: 3000, // slideshow mode - delay before next image 2628 albumID: -1, 2629 viewerMediaIsChanged: false, // media display is currently modified 2630 items: [], // current list of images to be managed by the viewer 2631 2632 panMode: 'off', // if panning, which element -> media, gallery, or zoom - if not -> off 2633 2634 $baseCont: null, // lightbox container 2635 $content: null, // pointer to the 3 media in the viewer 2636 content: { 2637 previous : { 2638 vIdx: -1, 2639 $media: null, 2640 NGY2Item: function() { 2641 return G.I[ G.VOM.items[G.VOM.content.previous.vIdx].ngy2ItemIdx ]; 2642 } 2643 }, 2644 current : { 2645 vIdx: -1, 2646 $media: null, 2647 NGY2Item: function() { 2648 return G.I[ G.VOM.items[G.VOM.content.current.vIdx].ngy2ItemIdx ]; 2649 } 2650 }, 2651 next : { 2652 vIdx: -1, 2653 $media: null, 2654 NGY2Item: function() { 2655 return G.I[ G.VOM.items[G.VOM.content.next.vIdx].ngy2ItemIdx ]; 2656 } 2657 } 2658 }, 2659 IdxNext: function() { 2660 var n = 0; 2661 // if( G.VOM.currItemIdx <= (G.VOM.items.length-1) ) { 2662 if( G.VOM.content.current.vIdx < (G.VOM.items.length-1) ) { 2663 n = G.VOM.content.current.vIdx + 1; 2664 } 2665 return n; 2666 }, 2667 IdxPrevious: function() { 2668 var n = G.VOM.content.current.vIdx - 1; 2669 if( G.VOM.content.current.vIdx == 0 ) { 2670 n = G.VOM.items.length - 1; 2671 } 2672 return n; 2673 }, 2674 2675 gallery: { 2676 $elt: null, // Base container 2677 $tmbCont: null, // Thumbnail container 2678 gwidth: 0, // thumbnail container width (all thumbnails) 2679 vwidth: 0, // visible width of the gallery (just for the visible thumbnails) 2680 oneTmbWidth: 0, 2681 firstDisplay: true, 2682 posX: 0, 2683 SetThumbnailActive() { 2684 if( G.O.viewerGallery == 'none' ) { return; } 2685 this.$tmbCont.children().removeClass('activeVThumbnail'); 2686 this.$tmbCont.children().eq( G.VOM.content.current.vIdx ).addClass('activeVThumbnail'); 2687 this.firstDisplay = false; 2688 }, 2689 Resize: function() { 2690 if( G.O.viewerGallery == 'none' ) { return; } 2691 2692 if( !this.firstDisplay ) { 2693 var viewerW = G.VOM.$viewer.width(); 2694 2695 // Center base element 2696 var maxTmb = Math.trunc(viewerW / this.oneTmbWidth); // max thumbnail that can be displayed 2697 this.vwidth = maxTmb * this.oneTmbWidth; 2698 this.$elt.css({ width: this.vwidth, left: (viewerW - this.vwidth)/2 }); 2699 2700 // Set the position the thumbnails container (if there's no enough space for all thumbnails) 2701 if( G.VOM.items.length >= maxTmb ) { 2702 var tmbPos = this.oneTmbWidth * G.VOM.content.current.vIdx; // left position of the selected thumbnail 2703 2704 if( (tmbPos + this.posX) < this.vwidth ) { 2705 if( tmbPos + this.posX < 0 ) { 2706 this.posX = -tmbPos; 2707 } 2708 } 2709 else { 2710 if( tmbPos + this.posX >= this.vwidth ) { 2711 this.posX = this.vwidth - (tmbPos + this.oneTmbWidth) 2712 } 2713 } 2714 } 2715 2716 this.PanGallery(0); 2717 } 2718 else { 2719 // first display of the gallery -> opacity transition 2720 new NGTweenable().tween({ 2721 from: { opacity: 0 }, 2722 to: { opacity: 1 }, 2723 easing: 'easeInOutSine', 2724 duration: 1000, 2725 step: function (state) { 2726 // G.VOM.gallery.$elt.css( state ); 2727 }, 2728 finish: function (state) { 2729 // G.VOM.gallery.$elt.css({ opacity: 1}); 2730 } 2731 }); 2732 2733 } 2734 }, 2735 PanGallery: function( panX ){ 2736 2737 // all thumbnails are visible -> center the base element 2738 if( this.gwidth < G.VOM.$viewer.width() ) { // this.oneTmbWidth 2739 this.posX = (G.VOM.$viewer.width() - this.gwidth) / 2; 2740 panX = 0; // block pan 2741 } 2742 2743 // if( this.posX > (this.vwidth - this.oneTmbWidth) ) { 2744 if( this.posX > (this.vwidth - this.oneTmbWidth) ) { 2745 // gallery is outside of the screen, right side 2746 this.posX = this.vwidth - this.oneTmbWidth; 2747 } 2748 if( (this.posX+this.gwidth) < this.oneTmbWidth ) { 2749 // gallery is outside of the screen, left side 2750 this.posX = -this.gwidth + this.oneTmbWidth; 2751 } 2752 2753 this.$tmbCont.css( G.CSStransformName , 'translateX(' + (this.posX + panX) + 'px)'); 2754 }, 2755 PanGalleryEnd: function( velocity ) { // velocity = pixels/millisecond 2756 2757 var d = velocity * 100; // distance 2758 new NGTweenable().tween({ 2759 from: { pan: G.VOM.gallery.posX }, 2760 to: { pan: G.VOM.gallery.posX + d }, 2761 easing: 'easeOutQuad', 2762 duration: 500, 2763 step: function (state) { 2764 G.VOM.gallery.posX = state.pan; 2765 G.VOM.gallery.PanGallery( 0 ); 2766 } 2767 }); 2768 } 2769 2770 }, 2771 hammertime: null, // hammer.js manager 2772 swipePosX: 0, // current horizontal swip position 2773 panPosX: 0, // position for manual pan 2774 panPosY: 0, 2775 panThreshold: 60, // threshold value (in pixels) to block vertical pan 2776 panXOnly: false, // threshold value reach -> definitively block vertical pan until end of pan 2777 singletapTime: 0, 2778 viewerTheme: '', 2779 timeImgChanged: 0, 2780 ImageLoader: { 2781 // fires a callback when image size is know (during download) 2782 // inspired by ROB - http://stackoverflow.com/users/226507/rob 2783 maxChecks: 1000, 2784 list: [], 2785 intervalHandle : null, 2786 2787 loadImage : function (callback, ngitem) { 2788 if( ngitem.mediaKind != 'img' ) { return; } // ignore - only for images 2789 var img = new Image (); 2790 img.src = ngitem.responsiveURL(); 2791 if (img.width && img.height) { 2792 callback (img.width, img.height, ngitem, 0); 2793 } 2794 else { 2795 var obj = {image: img, url: ngitem.responsiveURL(), ngitem: ngitem, callback: callback, checks: 1}; 2796 var i; 2797 for (i=0; i < this.list.length; i++) { 2798 if (this.list[i] == null) 2799 break; 2800 } 2801 this.list[i] = obj; 2802 if (!this.intervalHandle) 2803 this.intervalHandle = setInterval(this.interval, 50); 2804 } 2805 }, 2806 2807 // called by setInterval 2808 interval : function () { 2809 var count = 0; 2810 var list = G.VOM.ImageLoader.list, item; 2811 for (var i=0; i<list.length; i++) { 2812 item = list[i]; 2813 if (item != null) { 2814 if (item.image.width && item.image.height) { 2815 G.VOM.ImageLoader.list[i] = null; 2816 item.callback (item.image.width, item.image.height, item.ngitem, item.checks); 2817 } 2818 else if (item.checks > G.VOM.ImageLoader.maxChecks) { 2819 G.VOM.ImageLoader.list[i] = null; 2820 item.callback (0, 0, item.ngitem, item.checks); 2821 } 2822 else { 2823 count++; 2824 item.checks++; 2825 } 2826 } 2827 } 2828 if (count == 0) { 2829 G.VOM.ImageLoader.list = []; 2830 clearInterval (G.VOM.ImageLoader.intervalHandle); 2831 delete G.VOM.ImageLoader.intervalHandle; 2832 } 2833 } 2834 } 2835 } 2836 // One VOM item (image) 2837 function VImg( index ) { 2838 this.$e = null; 2839 this.ngy2ItemIdx = index; 2840 this.mediaNumber = G.VOM.items.length + 1; 2841 this.posX = 0; // to center the element 2842 this.posY = 0; 2843 } 2844 2845 2846 //------------------------ 2847 //--- popup 2848 G.popup = { 2849 isDisplayed: false, 2850 $elt: null, 2851 close: function() { 2852 if( this.$elt != null ) { 2853 var tweenable = new NGTweenable(); 2854 tweenable.tween({ 2855 from: { opacity:1 }, 2856 to: { opacity:0 }, 2857 attachment: { t: this }, 2858 easing: 'easeInOutSine', 2859 duration: 100, 2860 step: function (state, att) { 2861 if( att.t.$elt != null ) { 2862 att.t.$elt.css('opacity',state.opacity); 2863 } 2864 }, 2865 finish: function (state, att) { 2866 if( att.t.$elt != null ) { 2867 att.t.$elt.remove(); 2868 att.t.$elt=null; 2869 } 2870 att.t.isDisplayed=false; 2871 } 2872 }); 2873 } 2874 } 2875 } 2876 2877 2878 // Color schemes - Gallery 2879 // Gradient generator: https://www.grabient.com/ 2880 G.galleryTheme_dark = { 2881 navigationBar : { background: 'none', borderTop: '', borderBottom: '', borderRight: '', borderLeft: '' }, 2882 navigationBreadcrumb : { background: '#111', color: '#fff', colorHover: '#ccc', borderRadius: '4px' }, 2883 navigationFilter : { color: '#ddd', background: '#111', colorSelected: '#fff', backgroundSelected: '#111', borderRadius: '4px' }, 2884 navigationPagination : { background: '#111', color: '#fff', colorHover: '#ccc', borderRadius: '4px' }, 2885 thumbnail : { background: '#444', backgroundImage: 'linear-gradient(315deg, #111 0%, #445 90%)', borderColor: '#000', borderRadius: '0px', labelOpacity : 1, labelBackground: 'rgba(34, 34, 34, 0)', titleColor: '#fff', titleBgColor: 'transparent', titleShadow: '', descriptionColor: '#ccc', descriptionBgColor: 'transparent', descriptionShadow: '', stackBackground: '#aaa' }, 2886 thumbnailIcon : { padding: '5px', color: '#fff', shadow:'' }, 2887 pagination : { background: '#181818', backgroundSelected: '#666', color: '#fff', borderRadius: '2px', shapeBorder: '3px solid #666', shapeColor: '#444', shapeSelectedColor: '#aaa'} 2888 }; 2889 2890 G.galleryTheme_light = { 2891 navigationBar : { background: 'none', borderTop: '', borderBottom: '', borderRight: '', borderLeft: '' }, 2892 navigationBreadcrumb : { background: '#eee', color: '#000', colorHover: '#333', borderRadius: '4px' }, 2893 navigationFilter : { background: '#eee', color: '#222', colorSelected: '#000', backgroundSelected: '#eee', borderRadius: '4px' }, 2894 navigationPagination : { background: '#eee', color: '#000', colorHover: '#333', borderRadius: '4px' }, 2895 thumbnail : { background: '#444', backgroundImage: 'linear-gradient(315deg, #111 0%, #445 90%)', borderColor: '#000', labelOpacity : 1, labelBackground: 'rgba(34, 34, 34, 0)', titleColor: '#fff', titleBgColor: 'transparent', titleShadow: '', descriptionColor: '#ccc', descriptionBgColor: 'transparent', descriptionShadow: '', stackBackground: '#888' }, 2896 thumbnailIcon : { padding: '5px', color: '#fff' }, 2897 pagination : { background: '#eee', backgroundSelected: '#aaa', color: '#000', borderRadius: '2px', shapeBorder: '3px solid #666', shapeColor: '#444', shapeSelectedColor: '#aaa'} 2898 }; 2899 2900 // Color schemes - lightbox 2901 G.viewerTheme_dark = { 2902 background: '#000', 2903 barBackground: 'rgba(4, 4, 4, 0.2)', 2904 barBorder: '0px solid #111', 2905 barColor: '#fff', 2906 barDescriptionColor: '#ccc' 2907 }; 2908 G.viewerTheme_light = { 2909 background: '#f8f8f8', 2910 barBackground: 'rgba(4, 4, 4, 0.7)', 2911 barBorder: '0px solid #111', 2912 barColor: '#fff', 2913 barDescriptionColor: '#ccc' 2914 }; 2915 2916 2917 2918 // shortcut with G context to NGY2TOOLS 2919 // var NanoAlert = NGY2Tools.NanoAlert.bind(G); 2920 // var NanoConsoleLog = NGY2Tools.NanoConsoleLog.bind(G); 2921 var NanoAlert = NGY2Tools.NanoAlert; 2922 var NanoConsoleLog = NGY2Tools.NanoConsoleLog; 2923 2924 2925 /** @function initiateGallery2 */ 2926 this.initiateGallery2 = function( element, params ) { 2927 2928 // GLOBAL OPTIONS 2929 G.O = params; 2930 // Base element 2931 G.$E.base = jQuery(element); 2932 G.baseEltID = G.$E.base.attr('id'); 2933 if( G.baseEltID == undefined ) { 2934 // set a default ID to the root container 2935 var base_id = 'my_nanogallery'; 2936 var c = ''; 2937 var f = true; 2938 while( f ) { 2939 if (document.getElementById(base_id + c)) { 2940 // ID already exists 2941 if( c == '' ) { 2942 c = 1; 2943 } 2944 else { 2945 c++; 2946 } 2947 } 2948 else { 2949 f = false; 2950 G.baseEltID = 'my_nanogallery' + c; 2951 } 2952 } 2953 G.$E.base.attr('id', G.baseEltID) 2954 } 2955 G.O.$markup = []; 2956 2957 DefineVariables(); 2958 SetPolyFills(); 2959 BuildSkeleton(); 2960 G.GOM.firstDisplayTime = Date.now(); 2961 2962 SetGlobalEvents(); 2963 2964 // check if only one specific album will be used 2965 if( !G.O.lightboxStandalone ) { 2966 var albumToDisplay = G.O.album; 2967 if( albumToDisplay == '' && G.O.photoset != '' ) { 2968 albumToDisplay = G.O.photoset; 2969 G.O.album = G.O.photoset; 2970 } 2971 if( albumToDisplay != '' ) { 2972 G.O.displayBreadcrumb = false; // no breadcrumb since only 1 album 2973 if( albumToDisplay.toUpperCase() != 'NONE' ) { 2974 // open a public album 2975 if( G.O.kind == "nano_photos_provider2") { 2976 if( albumToDisplay == decodeURIComponent(albumToDisplay)) { 2977 // album ID must be encoded 2978 albumToDisplay = encodeURIComponent(albumToDisplay); 2979 G.O.album = albumToDisplay; 2980 } 2981 } 2982 NGY2Item.New( G, '', '', albumToDisplay, '-1', 'album' ); 2983 if( !ProcessLocationHash() ) { 2984 DisplayAlbum('-1', albumToDisplay); 2985 } 2986 return; 2987 } 2988 } 2989 } 2990 2991 // use full content 2992 // add base album 2993 NGY2Item.New( G, G.i18nTranslations.breadcrumbHome, '', '0', '-1', 'album' ); 2994 2995 processStartOptions(); 2996 2997 } 2998 2999 3000 /** @function processStartOptions */ 3001 function processStartOptions() { 3002 // open image or album 3003 // 1. load hidden albums 3004 // 2. check if location hash set (deep linking) 3005 // 3. check openOnStart parameter 3006 // 4. open root album (ID=-1) 3007 3008 // hidden/private albums are loaded on plugin start (Picasa) --> no more available in Google Photos 3009 // if( G.albumListHidden.length > 0 ) { 3010 // jQuery.nanogallery2['data_'+G.O.kind](G, 'GetHiddenAlbums', G.albumListHidden, processStartOptionsPart2); 3011 // return; 3012 //} 3013 3014 if( !ProcessLocationHash() ) { 3015 processStartOptionsPart2(); 3016 } 3017 } 3018 3019 /** @function processStartOptionsPart2 */ 3020 function processStartOptionsPart2() { 3021 3022 // Check location hash + start parameters -> determine what to do on start 3023 if( G.O.lightboxStandalone ) { 3024 LightboxStandaloneFindContent(); 3025 } 3026 else { 3027 // openOnStart parameter 3028 if( G.O.openOnStart != '' ) { 3029 var IDs = parseIDs(G.O.openOnStart); 3030 if( IDs.imageID != '0' ) { 3031 DisplayPhoto(IDs.imageID, IDs.albumID); 3032 } 3033 else { 3034 DisplayAlbum('-1', IDs.albumID); 3035 } 3036 } 3037 else { 3038 // open root album (ID = -1) 3039 DisplayAlbum('-1', 0); 3040 } 3041 } 3042 } 3043 3044 3045 // Lightbox standaone -> retrieve the items to display 3046 // Each item needs at least a thumbnail image and a big image 3047 // ONLY IMAGES SUPPORTED 3048 function LightboxStandaloneFindContent() { 3049 3050 G.GOM.curNavLevel = 'l1'; 3051 3052 if( G.O.items == null ) { 3053 // retrieve all element having "data-nanogallery2-lightbox" and from the same group if defined 3054 var elts = jQuery('[data-nanogallery2-Lightbox'); 3055 // element group 3056 var g = G.$E.base[0].dataset.nanogallery2Lgroup; 3057 3058 GetContentMarkup( elts, g ); 3059 } 3060 else { 3061 // Content defined in the starting parameters 3062 GetContentApiObject(); 3063 } 3064 3065 LightboxStandaloneDisplay(); 3066 3067 } 3068 3069 3070 // Populate G.VOM.items + open the lightbox 3071 function LightboxStandaloneDisplay() { 3072 3073 G.VOM.items = []; 3074 G.VOM.albumID = '0'; 3075 G.GOM.curNavLevel = 'l1'; 3076 var vcnt = 0; 3077 3078 var elt = G.$E.base[0].attributes; 3079 var thumbsrc = ''; 3080 // src attribute (img element) 3081 if( elt.hasOwnProperty('src') ) { 3082 thumbsrc = elt['src'].nodeValue; 3083 } 3084 // data-ngthumb attribute 3085 if( thumbsrc == '' && elt.hasOwnProperty('data-ngthumb') ) { 3086 thumbsrc = elt['data-ngthumb'].nodeValue; 3087 } 3088 3089 var displayIdx = undefined; 3090 for( var idx = 0; idx < G.I.length; idx++ ) { 3091 if( G.I[idx].kind == 'image' ) { 3092 var vimg = new VImg(idx); 3093 G.VOM.items.push(vimg); 3094 3095 if( G.I[idx].thumbImg().src == thumbsrc ) { 3096 // same thumbnail URL 3097 displayIdx = vcnt; 3098 } 3099 vcnt++; 3100 } 3101 3102 } 3103 if( G.VOM.items.length > 0 ) { 3104 LightboxOpen( displayIdx ); 3105 } 3106 else { 3107 NanoConsoleLog(G, 'No content for Lightbox standalone.'); 3108 } 3109 } 3110 3111 3112 3113 // Parse string to extract albumID and imageID (format albumID/imageID) 3114 function parseIDs( IDs ) { 3115 var r = { albumID: '0', imageID: '0' }; 3116 3117 var t = IDs.split('/'); 3118 if( t.length > 0 ) { 3119 r.albumID = t[0]; 3120 if( t.length > 1 ) { 3121 r.imageID = t[1]; 3122 } 3123 } 3124 return r; 3125 } 3126 3127 3128 /** @function DisplayAlbum */ 3129 function DisplayAlbum( imageID, albumID ) { 3130 // close viewer if already displayed 3131 if( G.VOM.viewerDisplayed ) { 3132 LightboxClose(null); 3133 } 3134 3135 // set current navigation level (l1 or lN) 3136 var albumIdx = NGY2Item.GetIdx(G, albumID); 3137 G.GOM.curNavLevel = 'lN'; 3138 if( albumIdx == 0 ) { 3139 G.GOM.curNavLevel = 'l1'; 3140 } 3141 G.layout.SetEngine(); 3142 G.galleryResizeEventEnabled = false; 3143 3144 if( albumIdx == -1 ) { 3145 NGY2Item.New( G, '', '', albumID, '0', 'album' ); // create empty album 3146 albumIdx = G.I.length - 1; 3147 } 3148 3149 if( !G.I[albumIdx].contentIsLoaded ) { 3150 // get content of the album if not already loaded 3151 AlbumGetContent( albumID, DisplayAlbum, imageID, albumID ); 3152 return; 3153 } 3154 3155 ThumbnailSelectionClear(); 3156 3157 G.GOM.pagination.currentPage = 0; 3158 SetLocationHash( albumID, '' ); 3159 GalleryRender( albumIdx ); 3160 3161 } 3162 3163 3164 //----- manage the bottom area of the gallery -> "pagination" or "more button" 3165 function GalleryBottomManage() { 3166 3167 switch( G.galleryDisplayMode.Get() ) { 3168 case 'PAGINATION': 3169 if( G.layout.support.rows && G.galleryMaxRows.Get() > 0 ) { 3170 ManagePagination(); 3171 } 3172 break; 3173 case 'MOREBUTTON': 3174 G.$E.conTnBottom.off('click'); 3175 var nb = G.GOM.items.length-G.GOM.itemsDisplayed; 3176 if( nb == 0 ) { 3177 G.$E.conTnBottom.empty(); 3178 } 3179 else { 3180 G.$E.conTnBottom.html('<div class="nGY2GalleryMoreButton"><div class="nGY2GalleryMoreButtonAnnotation">+'+nb+' ' + G.O.icons.galleryMoreButton +'</div></div>'); 3181 G.$E.conTnBottom.on('click', function(e) { 3182 G.GOM.displayedMoreSteps++; 3183 GalleryResize(); 3184 }); 3185 } 3186 break; 3187 case 'FULLCONTENT': 3188 default: 3189 break; 3190 } 3191 } 3192 3193 3194 // add one album/folder to the breadcrumb 3195 function breadcrumbAdd( albumIdx ) { 3196 3197 var ic=''; 3198 if( !G.O.breadcrumbHideIcons ) { 3199 ic=G.O.icons.breadcrumbAlbum; 3200 if( albumIdx == 0 ) { 3201 ic=G.O.icons.breadcrumbHome; 3202 } 3203 } 3204 var $newDiv =jQuery('<div class="oneItem">'+ic + G.I[albumIdx].title+'</div>').appendTo(G.GOM.navigationBar.$newContent.find('.nGY2Breadcrumb')); 3205 if( G.O.breadcrumbOnlyCurrentLevel ) { 3206 // link to parent folder (only 1 level is displayed in the breadcrumb) 3207 if( albumIdx == 0 ) { 3208 // no parent level -> stay on current one 3209 jQuery($newDiv).data('albumID','0'); 3210 } 3211 else { 3212 jQuery($newDiv).data('albumID',G.I[albumIdx].albumID); 3213 } 3214 } 3215 else { 3216 // link to current folder 3217 jQuery($newDiv).data('albumID',G.I[albumIdx].GetID()); 3218 } 3219 $newDiv.click(function() { 3220 var cAlbumID = jQuery(this).data('albumID'); 3221 DisplayAlbum('-1', cAlbumID); 3222 return; 3223 }); 3224 } 3225 3226 // add one separator to breadcrumb 3227 function breadcrumbAddSeparator( lastAlbumID ) { 3228 var $newSep=jQuery('<div class="oneItem">'+(G.O.RTL ? G.O.icons.breadcrumbSeparatorRtl : G.O.icons.breadcrumbSeparator)+'</div>').appendTo(G.GOM.navigationBar.$newContent.find('.nGY2Breadcrumb')); 3229 jQuery($newSep).data('albumIdx',lastAlbumID); 3230 $newSep.click(function() { 3231 var sepAlbumIdx=jQuery(this).data('albumIdx'); 3232 DisplayAlbum('-1', G.I[sepAlbumIdx].GetID()); 3233 return; 3234 }); 3235 } 3236 3237 3238 3239 // Manage the gallery toolbar (breadcrumb + tag filter + pagination next/previous) 3240 function GalleryNavigationBar( albumIdx ) { 3241 3242 // Title + background image 3243 // var bgImage=''; 3244 // var l=G.I.length; 3245 // var albumID = G.I[albumIdx].GetID(); 3246 // for( var idx=0; idx<l ; idx++) { 3247 // var item=G.I[idx]; 3248 // if( item.kind == 'image' && item.isToDisplay(albumID) ) { 3249 // bgImage='<div id="pipo" class="pipo" style="height: 150px; width:100%; background-image: url("' + item.responsiveURL() + '"); background-size: cover; background-position: center center; filter:blur(2px)">pipo</div>'; 3250 // break; 3251 // } 3252 // } 3253 3254 //console.log(bgImage); 3255 3256 // new navigation bar items are not build in the DOM, but in memory 3257 G.GOM.navigationBar.$newContent=jQuery('<div class="nGY2Navigationbar"></div>'); 3258 //G.GOM.navigationBar.$newContent = jQuery(bgImage ); 3259 //console.log(G.GOM.navigationBar.$newContent); 3260 3261 //-- manage breadcrumb 3262 if( G.O.displayBreadcrumb == true && !G.O.thumbnailAlbumDisplayImage) { 3263 // retrieve new folder level 3264 var newLevel = 0, 3265 lstItems=[]; 3266 if( albumIdx != 0 ) { 3267 var l=G.I.length, 3268 parentID=0; 3269 3270 lstItems.push(albumIdx); 3271 var curIdx=albumIdx; 3272 newLevel++; 3273 3274 while( G.I[curIdx].albumID != 0 && G.I[curIdx].albumID != -1) { 3275 for(var i=1; i < l; i++ ) { 3276 if( G.I[i].GetID() == G.I[curIdx].albumID ) { 3277 curIdx=i; 3278 lstItems.push(curIdx); 3279 newLevel++; 3280 break; 3281 } 3282 } 3283 } 3284 } 3285 3286 // build breadcrumb 3287 if( !(G.O.breadcrumbAutoHideTopLevel && newLevel == 0) ) { 3288 BreadcrumbBuild( lstItems ); 3289 } 3290 } 3291 3292 3293 //-- manage and build tag filters 3294 if( G.galleryFilterTags.Get() != false ) { 3295 var nTags = G.I[albumIdx].albumTagList.length; 3296 if( nTags > 0 ) { 3297 for(var i = 0; i < nTags; i++ ) { 3298 var s = G.I[albumIdx].albumTagList[i]; 3299 var ic = G.O.icons.navigationFilterUnselected; 3300 var tagClass = 'Unselected'; 3301 if( jQuery.inArray(s, G.I[albumIdx].albumTagListSel) >= 0 ) { 3302 tagClass = 'Selected'; 3303 ic = G.O.icons.navigationFilterSelected; 3304 } 3305 3306 var $newTag = jQuery('<div class="nGY2NavigationbarItem nGY2NavFilter' + tagClass + '">'+ ic +' '+ s +'</div>').appendTo(G.GOM.navigationBar.$newContent); 3307 3308 $newTag.click(function() { 3309 3310 var $this = jQuery(this); 3311 var tag = $this.text().replace(/^\s*|\s*$/, ''); //trim trailing/leading whitespace 3312 3313 if( G.galleryFilterTagsMode.Get() == 'single' ) { 3314 // single TAG selection 3315 G.I[albumIdx].albumTagListSel = []; 3316 G.I[albumIdx].albumTagListSel.push(tag); 3317 } 3318 else { 3319 // multiple selection of TAGS 3320 // if( $this.hasClass('oneTagUnselected') ){ 3321 if( $this.hasClass('nGY2NavFilterUnselected') ){ 3322 G.I[albumIdx].albumTagListSel.push(tag); 3323 } 3324 else { 3325 var tidx=jQuery.inArray(tag,G.I[albumIdx].albumTagListSel); 3326 if( tidx != -1 ) { 3327 G.I[albumIdx].albumTagListSel.splice(tidx,1); 3328 } 3329 } 3330 $this.toggleClass('nGY2NavFilters-oneTagUnselected nGY2NavFilters-oneTagSelected'); 3331 } 3332 3333 DisplayAlbum('-1', G.I[albumIdx].GetID()); 3334 }); 3335 } 3336 3337 // clear/reset TAGS selection 3338 var $newClearFilter=jQuery('<div class="nGY2NavigationbarItem nGY2NavFilterSelectAll">'+ G.O.icons.navigationFilterSelectedAll +'</div>').appendTo(G.GOM.navigationBar.$newContent); 3339 $newClearFilter.click(function() { 3340 // var nTags = G.I[albumIdx].albumTagList.length; 3341 G.I[albumIdx].albumTagListSel = []; 3342 // for(var i = 0; i < nTags; i++ ) { 3343 // var s = G.I[albumIdx].albumTagList[i]; 3344 // G.I[albumIdx].albumTagListSel.push(s); 3345 // } 3346 DisplayAlbum('-1', G.I[albumIdx].GetID()); 3347 }); 3348 } 3349 } 3350 3351 // --- Gallery pagination next/previous 3352 if( G.galleryDisplayMode.Get() == "PAGINATION" && G.O.galleryPaginationTopButtons ) { 3353 if( G.layout.support.rows && G.galleryMaxRows.Get() > 0 ) { 3354 // ManagePagination( G.GOM.albumIdx ); 3355 var $newTagPrev = jQuery('<div class="nGY2NavigationbarItem nGY2NavPagination">'+G.O.icons.navigationPaginationPrevious+'</div>').appendTo(G.GOM.navigationBar.$newContent); 3356 $newTagPrev.click(function() { 3357 paginationPreviousPage(); 3358 }); 3359 var $newTagNext = jQuery('<div class="nGY2NavigationbarItem nGY2NavPagination">'+G.O.icons.navigationPaginationNext+'</div>').appendTo(G.GOM.navigationBar.$newContent); 3360 $newTagNext.click(function() { 3361 paginationNextPage(); 3362 }); 3363 } 3364 } 3365 3366 } 3367 3368 function BreadcrumbBuild(lstItems) { 3369 3370 // console.log(G.GOM.navigationBar.$newContent); 3371 jQuery('<div class="nGY2NavigationbarItem nGY2Breadcrumb"></div>').appendTo(G.GOM.navigationBar.$newContent); 3372 // console.log(G.GOM.navigationBar.$newContent); 3373 3374 if( G.O.breadcrumbOnlyCurrentLevel ) { 3375 // display only 1 separator and the current folder level 3376 if( lstItems.length == 0 ) { 3377 breadcrumbAdd(0); 3378 } 3379 else { 3380 // var last=lstItems.length-1; 3381 if( lstItems.length == 1 ) { 3382 breadcrumbAddSeparator(0); // root level 3383 } 3384 else { 3385 breadcrumbAddSeparator(lstItems[0]); 3386 } 3387 breadcrumbAdd(lstItems[0]); 3388 } 3389 } 3390 else { 3391 // display the full breadcrum (full folder levels including root level) 3392 breadcrumbAdd(0); 3393 if( lstItems.length > 0 ) { 3394 breadcrumbAddSeparator(0); 3395 for(var i=lstItems.length-1; i>=0 ; i-- ) { 3396 breadcrumbAdd(lstItems[i]); 3397 if( i > 0 ) { 3398 breadcrumbAddSeparator(lstItems[i-1]); 3399 } 3400 } 3401 } 3402 } 3403 3404 } 3405 3406 3407 // Display gallery pagination 3408 function ManagePagination() { 3409 3410 G.$E.conTnBottom.css('opacity', 0); 3411 G.$E.conTnBottom.children().remove(); 3412 3413 if( G.GOM.items.length == 0 ) { return; } // no thumbnail to display 3414 3415 // calculate the number of pages 3416 var nbPages = Math.ceil((G.GOM.items[G.GOM.items.length - 1].row + 1)/G.galleryMaxRows.Get()); 3417 3418 // only one page -> do not display pagination 3419 if( nbPages == 1 ) { return; } 3420 3421 // check if current page still exists (for example after a resize) 3422 if( G.GOM.pagination.currentPage > (nbPages-1) ) { 3423 G.GOM.pagination.currentPage = nbPages-1; 3424 } 3425 3426 GalleryRenderGetInterval(); 3427 // nothing to display --> exit 3428 if( G.GOM.displayInterval.len == 0 ) { return; } 3429 3430 // display "previous" 3431 if( G.O.galleryPaginationMode == 'NUMBERS' && G.GOM.pagination.currentPage > 0 ) { 3432 var $eltPrev = jQuery('<div class="nGY2PaginationPrev">'+G.O.icons.paginationPrevious+'</div>').appendTo(G.$E.conTnBottom); 3433 $eltPrev.click(function(e) { 3434 paginationPreviousPage(); 3435 }); 3436 } 3437 3438 var firstPage = 0; 3439 var lastPage = nbPages; 3440 if( G.O.galleryPaginationMode != 'NUMBERS' ) { 3441 // no 'previous'/'next' and no max number of pagination items 3442 firstPage = 0; 3443 } 3444 else { 3445 // display pagination numbers and previous/next 3446 // var vp = G.O.paginationVisiblePages; 3447 var numberOfPagesToDisplay = G.O.paginationVisiblePages; 3448 if( numberOfPagesToDisplay >= nbPages ) { 3449 firstPage = 0; 3450 } 3451 else { 3452 // we have more pages than we want to display 3453 var nbBeforeAfter = 0; 3454 if( isOdd(numberOfPagesToDisplay) ) { 3455 nbBeforeAfter = (numberOfPagesToDisplay + 1) / 2; 3456 } 3457 else { 3458 nbBeforeAfter = numberOfPagesToDisplay / 2; 3459 } 3460 3461 if( G.GOM.pagination.currentPage < nbBeforeAfter ) { 3462 firstPage = 0; 3463 lastPage = numberOfPagesToDisplay - 1; 3464 if( lastPage > nbPages ) { 3465 lastPage = nbPages - 1; 3466 } 3467 } 3468 else { 3469 firstPage = G.GOM.pagination.currentPage - nbBeforeAfter; 3470 lastPage = firstPage + numberOfPagesToDisplay; 3471 if( lastPage > nbPages ) { 3472 lastPage = nbPages - 1; 3473 } 3474 } 3475 3476 if( (lastPage - firstPage) < numberOfPagesToDisplay ) { 3477 firstPage = lastPage - numberOfPagesToDisplay; 3478 if( firstPage < 0 ) { 3479 firstPage = 0; 3480 } 3481 } 3482 3483 } 3484 } 3485 3486 // render pagination items 3487 for(var i = firstPage; i < lastPage; i++ ) { 3488 var c = ''; 3489 var p = ''; 3490 3491 switch( G.O.galleryPaginationMode ) { 3492 case 'NUMBERS': 3493 c = 'nGY2paginationItem'; 3494 p = i + 1; 3495 break; 3496 case 'DOTS': 3497 c = 'nGY2paginationDot'; 3498 break; 3499 case 'RECTANGLES': 3500 c = 'nGY2paginationRectangle'; 3501 break; 3502 } 3503 if( i == G.GOM.pagination.currentPage ) { 3504 c += 'CurrentPage'; 3505 } 3506 3507 var elt$ = jQuery('<div class="' + c + '">' + p + '</div>').appendTo(G.$E.conTnBottom); 3508 elt$.data('pageNumber', i ); 3509 elt$.click( function(e) { 3510 G.GOM.pagination.currentPage = jQuery(this).data('pageNumber'); 3511 TriggerCustomEvent('pageChanged'); 3512 3513 // scroll to top of gallery if not displayed 3514 G.GOM.ScrollToTop(); 3515 3516 GalleryDisplayPart1(); 3517 GalleryDisplayPart2( true ); 3518 }); 3519 3520 } 3521 3522 // display "next" 3523 if( G.O.galleryPaginationMode == 'NUMBERS' && (G.GOM.pagination.currentPage + 1) < nbPages ) { 3524 var $eltNext = jQuery('<div class="nGY2PaginationNext">' + G.O.icons.paginationNext + '</div>').appendTo(G.$E.conTnBottom); 3525 $eltNext.click( function(e) { 3526 paginationNextPage(); 3527 }); 3528 } 3529 3530 G.$E.conTnBottom.css('opacity', 1); 3531 3532 } 3533 function isOdd(num) { return (num % 2) == 1;} 3534 3535 // pagination - next page 3536 function paginationNextPage() { 3537 // var aIdx = G.GOM.albumIdx; 3538 var n1 = 0; 3539 ThumbnailHoverOutAll(); 3540 3541 // pagination - max lines per page mode 3542 if( G.galleryMaxRows.Get() > 0 ) { 3543 // number of pages 3544 n1 = (G.GOM.items[G.GOM.items.length - 1].row + 1) / G.galleryMaxRows.Get(); 3545 } 3546 var n2 = Math.ceil(n1); 3547 var pn = G.GOM.pagination.currentPage; 3548 if( pn < (n2-1) ) { 3549 pn++; 3550 } 3551 else { 3552 pn = 0; 3553 } 3554 3555 G.GOM.pagination.currentPage = pn; 3556 TriggerCustomEvent('pageChanged'); 3557 3558 // scroll to top of gallery if not displayed 3559 G.GOM.ScrollToTop(); 3560 3561 GalleryDisplayPart1(); 3562 GalleryDisplayPart2( true ); 3563 } 3564 3565 // pagination - previous page 3566 function paginationPreviousPage() { 3567 // var aIdx=G.$E.conTnBottom.data('galleryIdx'), 3568 // var aIdx = G.GOM.albumIdx; 3569 var n1 = 0; 3570 3571 ThumbnailHoverOutAll(); 3572 3573 // pagination - max lines per page mode 3574 if( G.galleryMaxRows.Get() > 0 ) { 3575 // number of pages 3576 n1 = (G.GOM.items[G.GOM.items.length - 1].row + 1) / G.galleryMaxRows.Get(); 3577 } 3578 var n2 = Math.ceil(n1); 3579 3580 // var pn=G.$E.conTnBottom.data('currentPageNumber'); 3581 var pn = G.GOM.pagination.currentPage; 3582 if( pn > 0 ) { 3583 pn--; 3584 } 3585 else { 3586 pn = n2 - 1; 3587 } 3588 3589 G.GOM.pagination.currentPage = pn; 3590 TriggerCustomEvent('pageChanged'); 3591 3592 // scroll to top of gallery if not displayed 3593 G.GOM.ScrollToTop(); 3594 3595 GalleryDisplayPart1(); 3596 GalleryDisplayPart2( true ); 3597 } 3598 3599 // retrieve the from/to intervall for gallery thumbnail render 3600 function GalleryRenderGetInterval() { 3601 G.GOM.displayInterval.from = 0; 3602 G.GOM.displayInterval.len = G.I.length; 3603 3604 switch( G.galleryDisplayMode.Get() ) { 3605 case 'PAGINATION': 3606 if( G.layout.support.rows ) { 3607 let nbTn = G.GOM.items.length; 3608 var firstRow = G.GOM.pagination.currentPage * G.galleryMaxRows.Get(); 3609 var lastRow = firstRow + G.galleryMaxRows.Get(); 3610 var firstTn = -1; 3611 G.GOM.displayInterval.len = 0; 3612 for( var i = 0; i < nbTn ; i++ ) { 3613 let curTn = G.GOM.items[i]; 3614 if( curTn.row >= firstRow && curTn.row < lastRow ) { 3615 if( firstTn == -1 ) { 3616 G.GOM.displayInterval.from = i; 3617 firstTn = i; 3618 } 3619 G.GOM.displayInterval.len++; 3620 } 3621 } 3622 } 3623 break; 3624 case 'MOREBUTTON': 3625 if( G.layout.support.rows ) { 3626 let nbTn = G.GOM.items.length; 3627 let lastRow = G.O.galleryDisplayMoreStep * (G.GOM.displayedMoreSteps+1); 3628 G.GOM.displayInterval.len = 0; 3629 for( var i = 0; i < nbTn ; i++ ) { 3630 let curTn = G.GOM.items[i]; 3631 if( curTn.row < lastRow ) { 3632 G.GOM.displayInterval.len++; 3633 } 3634 } 3635 } 3636 break; 3637 case 'ROWS': 3638 if( G.layout.support.rows ) { 3639 let nbTn = G.GOM.items.length; 3640 let lastRow = G.galleryMaxRows.Get(); 3641 if( G.galleryLastRowFull.Get() && G.GOM.lastFullRow != -1 ) { 3642 if( lastRow > (G.GOM.lastFullRow + 1) ) { 3643 lastRow = G.GOM.lastFullRow + 1; 3644 } 3645 } 3646 G.GOM.displayInterval.len = 0; 3647 for( var i = 0; i < nbTn ; i++ ) { 3648 let curTn = G.GOM.items[i]; 3649 if( curTn.row < lastRow ) { 3650 G.GOM.displayInterval.len++; 3651 } 3652 } 3653 } 3654 break; 3655 default: 3656 case 'FULLCONTENT': 3657 if( G.layout.support.rows && G.galleryLastRowFull.Get() && G.GOM.lastFullRow != -1 ) { 3658 let nbTn = G.GOM.items.length; 3659 let lastRow = G.GOM.lastFullRow + 1; 3660 G.GOM.displayInterval.len = 0; 3661 for( var i = 0; i < nbTn ; i++ ) { 3662 let curTn = G.GOM.items[i]; 3663 if( curTn.row < lastRow ) { 3664 G.GOM.displayInterval.len++; 3665 } 3666 } 3667 } 3668 break; 3669 } 3670 } 3671 3672 3673 // RENDER THE GALLERY 3674 function GalleryRender( albumIdx ) { 3675 TriggerCustomEvent('galleryRenderStart'); 3676 clearTimeout(G.GOM.slider.timerID); 3677 G.GOM.slider.hostIdx = -1; // disabled slider on thumbnail 3678 3679 var fu=G.O.fnGalleryRenderStart; 3680 if( fu !== null ) { 3681 // typeof fu == 'function' ? fu(albumIdx) : window[fu](albumIdx); 3682 typeof fu == 'function' ? fu( G.I[G.GOM.albumIdx] ) : window[fu]( G.I[G.GOM.albumIdx] ); 3683 } 3684 3685 G.layout.SetEngine(); 3686 G.galleryResizeEventEnabled = false; 3687 G.GOM.albumIdx = -1; 3688 G.GOM.lastDisplayedIdx = -1; 3689 3690 // pagination 3691 if( G.$E.conTnBottom !== undefined ) { 3692 // G.$E.conTnBottom.children().remove(); 3693 G.$E.conTnBottom.empty(); 3694 } 3695 3696 // navigation toolbar (breadcrumb + tag filters + pagination next/previous) 3697 GalleryNavigationBar(albumIdx); 3698 3699 if( G.GOM.firstDisplay ) { 3700 // first gallery display 3701 G.GOM.firstDisplay = false; 3702 var d = Date.now()-G.GOM.firstDisplayTime; 3703 if( d < G.O.galleryRenderDelay ) { 3704 // display after defined delay 3705 // setTimeout( function() { GalleryRenderPart1( albumIdx )}, G.O.galleryRenderDelay-d ); 3706 requestTimeout( function() { GalleryRenderPart1( albumIdx )}, G.O.galleryRenderDelay-d ); 3707 } 3708 else { 3709 GalleryRenderPart1( albumIdx ); 3710 } 3711 G.O.galleryRenderDelay = 0; 3712 3713 } 3714 else { 3715 var hideNavigationBar = false; 3716 if( G.GOM.navigationBar.$newContent.children().length == 0 ) { 3717 hideNavigationBar = true; 3718 } 3719 3720 // hide everything 3721 var tweenable = new NGTweenable(); 3722 tweenable.tween({ 3723 from: { 'opacity': 1 }, 3724 to: { 'opacity': 0 }, 3725 duration: 300, 3726 easing: 'easeInQuart', 3727 attachment: { h: hideNavigationBar }, 3728 step: function (state, att) { 3729 G.$E.conTnParent.css({'opacity': state.opacity }); 3730 if( att.h ) { 3731 G.$E.conNavigationBar.css({ 'opacity': state.opacity }); 3732 } 3733 }, 3734 finish: function (state, att) { 3735 if( att.h ) { 3736 G.$E.conNavigationBar.css({ 'opacity': 0, 'display': 'none' }); 3737 } 3738 // scroll to top of the gallery if needed 3739 3740 G.GOM.ScrollToTop(); 3741 3742 GalleryRenderPart1( albumIdx ); 3743 } 3744 }); 3745 } 3746 } 3747 3748 3749 function GalleryRenderPart1( albumIdx ) { 3750 // display new navigation bar 3751 var oldN = G.$E.conNavigationBar.children().length; 3752 G.$E.conNavigationBar.empty(); 3753 G.GOM.navigationBar.$newContent.children().clone(true,true).appendTo(G.$E.conNavigationBar); 3754 // G.GOM.navigationBar.$newContent.appendTo(G.$E.conNavigationBar); 3755 if( G.$E.conNavigationBar.children().length > 0 && oldN == 0 ) { 3756 G.$E.conNavigationBar.css({ 'opacity': 0, 'display': 'block' }); 3757 var tweenable = new NGTweenable(); 3758 tweenable.tween({ 3759 from: { opacity: 0 }, 3760 to: { opacity: 1 }, 3761 duration: 200, 3762 easing: 'easeInQuart', 3763 step: function (state) { 3764 // window.ng_draf( function() { 3765 G.$E.conNavigationBar.css( state ); 3766 // }); 3767 }, 3768 finish: function (state) { 3769 // window.ng_draf( function() { 3770 G.$E.conNavigationBar.css({ 'opacity': 1 }); 3771 // display gallery 3772 // GalleryRenderPart2( albumIdx ); 3773 // setTimeout(function(){ GalleryRenderPart2(albumIdx) }, 60); 3774 requestTimeout(function(){ GalleryRenderPart2(albumIdx) }, 20); 3775 // }); 3776 } 3777 }); 3778 } 3779 else { 3780 requestTimeout(function(){ GalleryRenderPart2(albumIdx) }, 20); 3781 } 3782 3783 } 3784 3785 // Gallery render part 2 -> remove all existing thumbnails 3786 function GalleryRenderPart2(albumIdx) { 3787 3788 G.GOM.lastZIndex = parseInt(G.$E.base.css('z-index')); 3789 if( isNaN(G.GOM.lastZIndex) ) { 3790 G.GOM.lastZIndex=0; 3791 } 3792 G.$E.conTnParent.css({ 'opacity': 0 }); 3793 G.$E.conTn.off().empty(); 3794 var l = G.I.length; 3795 for( var i = 0; i < l ; i++ ) { 3796 // reset each item 3797 var item = G.I[i]; 3798 item.hovered = false; 3799 item.$elt = null; 3800 item.$Elts = []; 3801 item.eltTransform = []; 3802 item.eltFilter = []; 3803 item.width = 0; 3804 item.height = 0; 3805 item.left = 0; 3806 item.top = 0; 3807 item.resizedContentWidth = 0; 3808 item.resizedContentHeight = 0; 3809 item.thumbnailImgRevealed = false; 3810 } 3811 3812 if( G.CSStransformName == null ) { 3813 G.$E.conTn.css('left', '0px'); 3814 } 3815 else { 3816 // G.$E.conTn.css( G.CSStransformName, 'translateX(0px)'); 3817 G.$E.conTn.css( G.CSStransformName, 'none'); 3818 } 3819 3820 // setTimeout(function(){ GalleryRenderPart3(albumIdx) }, 60); 3821 requestTimeout(function(){ GalleryRenderPart3(albumIdx) }, 20); 3822 // GalleryRenderPart3(albumIdx); 3823 3824 } 3825 3826 // Gallery render part 3 -> start building the new gallery 3827 function GalleryRenderPart3(albumIdx) { 3828 var d = new Date(); 3829 3830 G.$E.conTnParent.css( 'opacity', 1); 3831 3832 G.GOM.items = []; 3833 G.GOM.displayedMoreSteps = 0; 3834 // retrieve label height 3835 if( G.O.thumbnailLabel.get('position') == 'onBottom' ) { 3836 // retrieve height each time because size can change depending on thumbnail's settings 3837 G.tn.labelHeight[G.GOM.curNavLevel] = ThumbnailGetLabelHeight(); 3838 } 3839 else { 3840 G.tn.labelHeight[G.GOM.curNavLevel] = 0; 3841 } 3842 G.GOM.albumIdx=albumIdx; 3843 3844 TriggerCustomEvent('galleryRenderEnd'); 3845 var fu=G.O.fnGalleryRenderEnd; 3846 if( fu !== null ) { 3847 // typeof fu == 'function' ? fu(albumIdx) : window[fu](albumIdx); 3848 typeof fu == 'function' ? fu(G.I[G.GOM.albumIdx] ) : window[fu](G.I[G.GOM.albumIdx] ); 3849 } 3850 3851 // Step 1: populate GOM 3852 if( GalleryPopulateGOM() ) { 3853 // step 2: calculate layout 3854 GallerySetLayout(); 3855 3856 // step 3: display whole gallery 3857 GalleryAppear(); 3858 3859 // step 4: display thumbnails 3860 GalleryDisplayPart1(); 3861 requestTimeout(function(){ GalleryDisplayPart2( false ) }, 20); 3862 } 3863 else { 3864 // 3865 G.galleryResizeEventEnabled = true; 3866 } 3867 3868 if( G.O.debugMode ) { console.log('GalleryRenderPart3: '+ (new Date()-d)); } 3869 3870 } 3871 3872 3873 // Resize the gallery 3874 function GalleryResize() { 3875 var d = new Date(); 3876 G.galleryResizeEventEnabled = false; 3877 // G.GOM.cache.areaWidth=G.$E.conTnParent.width(); 3878 if( GallerySetLayout() == false ) { 3879 G.galleryResizeEventEnabled = true; 3880 if( G.O.debugMode ) { console.log('GalleryResize1: '+ (new Date()-d)); } 3881 return; 3882 } 3883 if( G.O.debugMode ) { console.log('GalleryResizeSetLayout: '+ (new Date()-d)); } 3884 3885 GalleryDisplayPart1(); 3886 GalleryDisplayPart2( false ); 3887 3888 if( G.O.debugMode ) { console.log('GalleryResizeFull: '+ (new Date()-d)); } 3889 } 3890 3891 3892 3893 // copy items (album content) to GOM 3894 // returns: 3895 // true: thumbnail image size is needed for the layout, but not set -> retrieve the sizes and display gallery 3896 function GalleryPopulateGOM() { 3897 3898 var preloadImages = ''; 3899 var albumID = G.I[G.GOM.albumIdx].GetID(); 3900 var l = G.I.length; 3901 var cnt = 0; 3902 3903 for( var idx = 0; idx < l; idx++ ) { 3904 var item = G.I[idx]; 3905 // check album 3906 if( item.isToDisplay(albumID) ) { 3907 var w = item.thumbImg().width; 3908 var h = item.thumbImg().height; 3909 // if unknown image size and layout is not grid --> we need to retrieve the size of the images 3910 if( G.layout.prerequisite.imageSize && ( w == 0 || h == 0) ) { 3911 preloadImages += '<img src="'+item.thumbImg().src+'" data-idx="'+cnt+'" data-albumidx="'+G.GOM.albumIdx+'">'; 3912 } 3913 3914 // set default size if required 3915 if( h == 0 ) { 3916 h = G.tn.defaultSize.getHeight(); 3917 } 3918 if( w == 0 ) { 3919 w = G.tn.defaultSize.getWidth(); 3920 } 3921 var tn = new G.GOM.GTn(idx, w, h); 3922 G.GOM.items.push(tn); 3923 cnt++; 3924 } 3925 } 3926 3927 TriggerCustomEvent('galleryObjectModelBuilt'); 3928 var fu = G.O.fnGalleryObjectModelBuilt; 3929 if( fu !== null ) { 3930 typeof fu == 'function' ? fu() : window[fu](); 3931 } 3932 3933 if( preloadImages != '' ) { 3934 // preload images to retrieve their size and then resize the gallery (=GallerySetLayout()+ GalleryDisplay()) 3935 var $newImg = jQuery(preloadImages); 3936 var gi_imgLoad = ngimagesLoaded( $newImg ); 3937 $newImg = null; 3938 gi_imgLoad.on( 'progress', function( instance, image ) { 3939 3940 if( image.isLoaded ) { 3941 var idx = image.img.getAttribute('data-idx'); 3942 var albumIdx = image.img.getAttribute('data-albumidx'); 3943 if( albumIdx == G.GOM.albumIdx ) { 3944 // ignore event if not on current album 3945 var curTn = G.GOM.items[idx]; 3946 curTn.imageWidth = image.img.naturalWidth; 3947 curTn.imageHeight = image.img.naturalHeight; 3948 var item = G.I[curTn.thumbnailIdx]; 3949 item.thumbs.width[G.GOM.curNavLevel][G.GOM.curWidth] = curTn.imageWidth; 3950 item.thumbs.height[G.GOM.curNavLevel][G.GOM.curWidth] = curTn.imageHeight; 3951 3952 // resize the gallery 3953 G.GalleryResizeThrottled(); 3954 3955 // set the retrieved size to all levels with same configuration 3956 var object = item.thumbs.width.l1; 3957 for (let property in object) { 3958 if (object.hasOwnProperty(property)) { 3959 if( property != G.GOM.curWidth ) { 3960 if( G.tn.settings.width.l1[property] == G.tn.settings.getW() && G.tn.settings.height.l1[property] == G.tn.settings.getH() ) { 3961 item.thumbs.width.l1[property] = curTn.imageWidth; 3962 item.thumbs.height.l1[property] = curTn.imageHeight; 3963 } 3964 } 3965 } 3966 } 3967 object = item.thumbs.width.lN; 3968 for (let property in object) { 3969 if (object.hasOwnProperty(property)) { 3970 if( property != G.GOM.curWidth ) { 3971 if( G.tn.settings.width.lN[property] == G.tn.settings.getW() && G.tn.settings.height.lN[property] == G.tn.settings.getH() ) { 3972 item.thumbs.width.lN[property] = curTn.imageWidth; 3973 item.thumbs.height.lN[property] = curTn.imageHeight; 3974 } 3975 } 3976 } 3977 } 3978 } 3979 } 3980 }); 3981 G.galleryResizeEventEnabled = true; 3982 return false; 3983 } 3984 else { 3985 return true; 3986 } 3987 3988 } 3989 3990 //----- Calculate the layout of the thumbnails for the full gallery 3991 function GallerySetLayout() { 3992 var r = true; 3993 // width of the available area 3994 G.GOM.cache.areaWidth = G.$E.conTnParent.width(); 3995 G.GOM.displayArea = { width:0, height:0 }; 3996 3997 switch( G.layout.engine ) { 3998 case 'JUSTIFIED': 3999 r = GallerySetLayoutWidthtAuto(); 4000 break; 4001 case 'CASCADING': 4002 r = GallerySetLayoutHeightAuto(); 4003 break; 4004 case 'MOSAIC': 4005 r = GallerySetLayoutMosaic(); 4006 break; 4007 case 'GRID': 4008 default: 4009 r = GallerySetLayoutGrid(); 4010 break; 4011 } 4012 4013 TriggerCustomEvent('galleryLayoutApplied'); 4014 var fu = G.O.fnGalleryLayoutApplied; 4015 if( fu !== null ) { 4016 typeof fu == 'function' ? fu() : window[fu](); 4017 } 4018 return r; 4019 4020 } 4021 4022 4023 //----- CASCADING LAYOUT 4024 function GallerySetLayoutHeightAuto() { 4025 var curCol = 0, 4026 areaWidth = G.GOM.cache.areaWidth, 4027 curRow = 0, 4028 colHeight = [], 4029 maxCol = NbThumbnailsPerRow(areaWidth), 4030 gutterWidth = 0, 4031 gutterHeight = G.tn.settings.GetResponsive('gutterHeight'); 4032 var w = 0; 4033 var scaleFactor = 1; 4034 var tnWidth = G.tn.defaultSize.getOuterWidth(); 4035 var nbTn = G.GOM.items.length; 4036 var curPosY = 0; 4037 4038 if( G.O.thumbnailAlignment == 'justified' ) { 4039 maxCol = Math.min(maxCol, nbTn); 4040 gutterWidth = ( maxCol == 1 ? 0 : (areaWidth - (maxCol * tnWidth) ) / (maxCol - 1) ); 4041 } 4042 else { 4043 gutterWidth = G.tn.settings.GetResponsive('gutterWidth'); 4044 } 4045 4046 4047 var borderWidth = G.tn.opt.Get('borderHorizontal') * 2; 4048 var borderHeight = G.tn.opt.Get('borderVertical') * 2; 4049 4050 G.GOM.lastFullRow=-1; // feature disabled 4051 4052 // Retrieve the real used width of the area (the evaluation is based on the content of the first line) 4053 if( G.O.thumbnailAlignment == 'fillWidth' ) { 4054 // fillWidth --> evaluate scale factor and number of columns 4055 var totalGutterWidth = (maxCol - 1) * gutterWidth; 4056 scaleFactor = (areaWidth - totalGutterWidth) / (maxCol * tnWidth); 4057 if( scaleFactor > 1 ) { 4058 maxCol++; // add one column and re-evaluate the scale factor 4059 } 4060 totalGutterWidth = (maxCol - 1) * gutterWidth; 4061 scaleFactor = Math.min( (areaWidth - totalGutterWidth) / (maxCol*tnWidth), 1); // no upscale 4062 } 4063 4064 4065 tnWidth = Math.round( tnWidth * scaleFactor); 4066 var contentWidth = tnWidth - borderWidth; 4067 4068 // loop to position the thumbnails, and set their size 4069 var baseHeight = Math.round( G.tn.opt.Get('baseGridHeight') * scaleFactor ); 4070 for( var i = 0; i < nbTn ; i++ ) { 4071 var curTn = G.GOM.items[i]; 4072 if( curTn.deleted == true ) { break; } // item is logically deleted 4073 if( curTn.imageHeight > 0 && curTn.imageWidth > 0 ) { 4074 var curPosX = 0, 4075 curPosY = 0; 4076 var imageRatio = curTn.imageHeight / curTn.imageWidth; 4077 // curTn.resizedContentWidth = tnWidth - borderWidth; 4078 curTn.resizedContentWidth = contentWidth; 4079 curTn.resizedContentHeight = curTn.resizedContentWidth * imageRatio; 4080 if( baseHeight > 0 ) { 4081 // grid based vertical position 4082 var t = Math.max( Math.trunc(curTn.resizedContentHeight/baseHeight), 1) ; 4083 curTn.resizedContentHeight = baseHeight * t + ((t-1)*(borderHeight+gutterHeight)); 4084 } 4085 4086 curTn.height = curTn.resizedContentHeight + borderHeight + G.tn.labelHeight.get(); 4087 curTn.width = tnWidth; 4088 curTn.row = 0; 4089 4090 if( curRow == 0 ) { 4091 // first row 4092 curPosX = curCol * (tnWidth + gutterWidth); 4093 colHeight[curCol] = curTn.height + gutterHeight; 4094 4095 curCol++; 4096 if( curCol >= maxCol ) { 4097 curCol = 0; 4098 curRow++; 4099 } 4100 } 4101 else { 4102 var c=0, 4103 minColHeight=colHeight[0]; 4104 for( var j = 1; j < maxCol; j++) { 4105 if( (colHeight[j] + 5) < minColHeight ) { // +5 --> threshold 4106 minColHeight = colHeight[j]; 4107 c = j; 4108 //break; 4109 } 4110 } 4111 curPosY = colHeight[c]; 4112 curPosX = c * (tnWidth + gutterWidth); 4113 colHeight[c] = curPosY + curTn.height + gutterHeight; 4114 } 4115 4116 var x = curPosX; 4117 if( G.O.RTL) { 4118 x= w - curPosX - tnWidth; 4119 } 4120 4121 curTn.left = x; 4122 curTn.top = curPosY; 4123 } 4124 } 4125 4126 G.GOM.displayArea.width= maxCol * (tnWidth + gutterWidth) - gutterWidth; 4127 return true; 4128 } 4129 4130 4131 //----- JUSTIFIED LAYOUT 4132 function GallerySetLayoutWidthtAuto() { 4133 var curWidth = 0, 4134 areaWidth = G.GOM.cache.areaWidth, 4135 lastPosX = 0, 4136 curPosY = 0, 4137 rowLastItem = [], 4138 rowNum = 0, 4139 rowHeight = [], 4140 bNewRow = false, 4141 cnt = 0, 4142 gutterWidth = G.tn.settings.GetResponsive('gutterWidth'), 4143 gutterHeight = G.tn.settings.GetResponsive('gutterHeight'); 4144 // by grief-of-these-days 4145 var maxRowHeightVertical = 0; // max height of a row with vertical thumbs 4146 var maxRowHeightHorizontal = 0; // max height of a row with horizontal thumbs 4147 var rowHasVertical = false; // current row has vertical thumbs 4148 var rowHasHorizontal = false; // current row has horizontal thumbs 4149 4150 var tnHeight = G.tn.defaultSize.getOuterHeight(); 4151 var borderWidth = G.tn.opt.Get('borderHorizontal') * 2; 4152 var borderHeight = G.tn.opt.Get('borderVertical') * 2; 4153 var nbTnInCurrRow = 1; 4154 var nbTn = G.GOM.items.length; 4155 4156 // first loop --> retrieve each row image height 4157 for( var i = 0; i < nbTn ; i++ ) { 4158 let curTn = G.GOM.items[i]; 4159 if( curTn.deleted == true ) { break; } // item is logically deleted 4160 if( curTn.imageWidth > 0 ) { 4161 let imageRatio = curTn.imageWidth / curTn.imageHeight; 4162 let imageWidth = Math.floor( tnHeight * imageRatio ); 4163 4164 if( bNewRow ) { 4165 bNewRow = false; 4166 rowNum++; 4167 curWidth = 0; 4168 rowHasVertical = false; 4169 rowHasHorizontal = false; 4170 nbTnInCurrRow = 1; 4171 } 4172 // by grief-of-these-days 4173 if( curTn.imageHeight > curTn.imageWidth ) { 4174 rowHasVertical = true; 4175 } 4176 else { 4177 rowHasHorizontal = true; 4178 } 4179 4180 if( (curWidth + gutterWidth + imageWidth) < (areaWidth - (nbTnInCurrRow * borderWidth)) ) { 4181 // enough place left in the current row 4182 curWidth += imageWidth + gutterWidth; 4183 rowHeight[rowNum] = tnHeight; 4184 4185 // prevent incomplete row from being heigher than the previous ones. 4186 // by grief-of-these-days 4187 var rowHeightLimit = Math.max(rowHasVertical ? maxRowHeightVertical : 0, rowHasHorizontal ? maxRowHeightHorizontal : 0); 4188 if( rowHeightLimit > 0 ) { 4189 rowHeight[rowNum] = Math.min(rowHeight[rowNum], rowHeightLimit); 4190 } 4191 4192 rowLastItem[rowNum] = i; 4193 } 4194 else { 4195 // new row after current item --> we need to adujet the row height to have enough space for the current thumbnail 4196 curWidth += gutterWidth+imageWidth; 4197 let ratio = (areaWidth - nbTnInCurrRow * borderWidth) / curWidth; 4198 let rH = Math.floor(tnHeight * ratio); 4199 rowHeight[rowNum] = rH; 4200 4201 // save the max row height for each thumb orientation. 4202 // by grief-of-these-days 4203 if( rowHasVertical ) { 4204 maxRowHeightVertical = Math.max( maxRowHeightVertical, rH ); 4205 } 4206 if( rowHasHorizontal ) { 4207 maxRowHeightHorizontal = Math.max( maxRowHeightHorizontal, rH ); 4208 } 4209 4210 rowLastItem[rowNum] = i; 4211 bNewRow = true; 4212 } 4213 cnt++; 4214 nbTnInCurrRow++; 4215 } 4216 } 4217 4218 rowNum = 0; 4219 curPosY = 0; 4220 lastPosX = 0; 4221 cnt = 0; 4222 4223 G.GOM.lastFullRow = 0; // display at leat 1 row (even if not full) 4224 4225 // second loop --> calculate each thumbnail size 4226 for( var i = 0; i < nbTn ; i++ ) { 4227 let curTn = G.GOM.items[i]; 4228 if( curTn.imageWidth > 0 ) { 4229 let imageRatio = curTn.imageWidth / curTn.imageHeight; 4230 let imageWidth = Math.floor( imageRatio * rowHeight[rowNum] ); // border is already NOT included 4231 4232 if( i == rowLastItem[rowNum] ) { 4233 // row last item --> adjust image width because of rounding problems 4234 if( rowLastItem.length != (rowNum+1) ) { 4235 // last item in current row -> use the full remaining width 4236 imageWidth = areaWidth - lastPosX - borderWidth; 4237 } 4238 else { 4239 // very last item (on the last row) 4240 if( (lastPosX + gutterWidth + imageWidth + borderWidth ) > areaWidth ) { 4241 // reduce size if image is wider as the remaining space 4242 imageWidth = areaWidth - lastPosX - borderWidth; 4243 } 4244 } 4245 } 4246 4247 let rh = parseInt( rowHeight[rowNum] ); 4248 imageWidth = parseInt( imageWidth ); 4249 4250 // thumbnail image size 4251 curTn.resizedContentWidth = imageWidth; 4252 curTn.resizedContentHeight = rh; 4253 // thumbnail position and size 4254 curTn.width = imageWidth + borderWidth; 4255 curTn.height= rh + G.tn.labelHeight.get() + borderHeight; 4256 curTn.row = rowNum; 4257 4258 curTn.top = curPosY; 4259 let x = lastPosX; 4260 if( G.O.RTL) { 4261 x = areaWidth - lastPosX - curTn.width ; 4262 } 4263 curTn.left = x; 4264 4265 lastPosX += curTn.width + gutterWidth; 4266 4267 if( i == rowLastItem[rowNum] ) { 4268 // start a new row 4269 curPosY += curTn.height + gutterHeight; 4270 G.GOM.lastFullRow = rowNum - 1; 4271 rowNum++; 4272 lastPosX = 0; 4273 } 4274 cnt++; 4275 } 4276 else { 4277 return false; 4278 } 4279 } 4280 4281 // hover effect on gallery (vs on thumbnail) --> experimental / not used 4282 if( false ) { 4283 var newTop = 0; 4284 if( typeof GOMidx !== 'undefined' ) { 4285 if( G.GOM.albumIdx != -1 ) { 4286 var hoveredTn = G.GOM.items[GOMidx]; 4287 4288 // hovered thumbnail 4289 hoveredTn.width += 40; 4290 hoveredTn.height += 40; 4291 4292 for( var i = 0; i < nbTn ; i++ ) { 4293 var curTn = G.GOM.items[i]; 4294 if( curTn.imageWidth > 0 ) { 4295 if( curTn.row == hoveredTn.row ) { 4296 // hovered row 4297 newTop = 40; 4298 if( hoveredTn.thumbnailIdx != curTn.thumbnailIdx ) { 4299 // not hovered thumbnail 4300 curTn.top += 30; 4301 curTn.width -= 20; 4302 curTn.height -= 20; 4303 } 4304 } 4305 else { 4306 // not hovered row 4307 if( curTn.row == 0 ) { 4308 // first row 4309 } 4310 else { 4311 curTn.top += newTop; 4312 } 4313 } 4314 } 4315 } 4316 } 4317 } 4318 } 4319 4320 G.GOM.displayArea.width = areaWidth; 4321 return true; 4322 } 4323 4324 4325 //----- MOSAIC LAYOUT 4326 // Grid using a user defined pattern layout 4327 // With this layout, a pattern definition is handeld a row 4328 function GallerySetLayoutMosaic() { 4329 var areaWidth = G.GOM.cache.areaWidth; 4330 var gutterHeight = G.tn.settings.GetResponsive('gutterHeight'); 4331 var gutterWidth = G.tn.settings.GetResponsive('gutterWidth'); 4332 var borderWidth = G.tn.opt.Get('borderHorizontal') * 2; 4333 var borderHeight = G.tn.opt.Get('borderVertical') * 2; 4334 4335 var nbTn = G.GOM.items.length; 4336 var row = 0; 4337 var h = 0; 4338 var n = 0; 4339 4340 4341 // first loop: evaluate the gallery width based on the first row 4342 var nbCols = 0; 4343 var maxW = 0; 4344 let mosaicPattern = G.tn.settings.getMosaic(); 4345 for( var i = 0; i < nbTn ; i++ ) { 4346 let curPatternElt = mosaicPattern[n]; 4347 4348 var cLeft = (curPatternElt.c - 1) * G.tn.defaultSize.getOuterWidth() + (curPatternElt.c - 1) * gutterWidth; 4349 var cWidth = curPatternElt.w * G.tn.defaultSize.getOuterWidth() + (curPatternElt.w - 1) * gutterWidth; 4350 4351 maxW = Math.max(maxW, cLeft + cWidth ); 4352 4353 nbCols = Math.max(nbCols, (curPatternElt.c - 1) + curPatternElt.w ); 4354 4355 n++; 4356 if( n >= mosaicPattern.length ) { 4357 // end of pattern 4358 break; 4359 } 4360 } 4361 var totalGutterWidth = (nbCols - 1) * gutterWidth; 4362 var scaleFactor = Math.min( (areaWidth - totalGutterWidth ) / ( maxW - totalGutterWidth ), 1); 4363 4364 // second loop: position all the thumbnails based on the layout pattern 4365 row = 0; 4366 n = 0; 4367 // let mosaicPattern = G.tn.settings.getMosaic(); 4368 for( var i = 0; i < nbTn ; i++ ) { 4369 let curTn = G.GOM.items[i]; 4370 let curPatternElt = mosaicPattern[n]; 4371 4372 curTn.top = Math.round((curPatternElt.r - 1) * G.tn.defaultSize.getOuterHeight()*scaleFactor) + (curPatternElt.r - 1) * gutterHeight + row * h + (G.tn.labelHeight.get()*(curPatternElt.r-1)) ; 4373 if( row > 0 ) { 4374 curTn.top += gutterHeight; 4375 } 4376 4377 curTn.left = (curPatternElt.c - 1) * Math.round(G.tn.defaultSize.getOuterWidth()*scaleFactor) + (curPatternElt.c - 1) * gutterWidth; 4378 4379 curTn.height = Math.round(curPatternElt.h * G.tn.defaultSize.getOuterHeight() * scaleFactor) + (curPatternElt.h - 1) * gutterHeight + (G.tn.labelHeight.get() * curPatternElt.h); 4380 curTn.resizedContentHeight = curTn.height - G.tn.labelHeight.get() - borderHeight; 4381 4382 curTn.width = Math.round(curPatternElt.w * G.tn.defaultSize.getOuterWidth()*scaleFactor) + (curPatternElt.w - 1) * gutterWidth; 4383 curTn.resizedContentWidth = curTn.width - borderWidth ; 4384 4385 curTn.row = row; 4386 if( row == 0 ) { 4387 h=Math.max(h, curTn.top + curTn.height); 4388 } 4389 4390 n++; 4391 if( n >= mosaicPattern.length ) { 4392 // end pattern -> new line 4393 n = 0; 4394 row++; 4395 } 4396 } 4397 4398 G.GOM.displayArea.width = (maxW - totalGutterWidth) * scaleFactor + totalGutterWidth; 4399 return true; 4400 } 4401 4402 4403 4404 // --- GRID LAYOUT 4405 function GallerySetLayoutGrid() { 4406 var curPosX= 0, 4407 curPosY= 0, 4408 areaWidth= G.GOM.cache.areaWidth, 4409 gutterWidth= 0, 4410 gutterHeight= G.tn.settings.GetResponsive('gutterHeight'), 4411 maxCol= NbThumbnailsPerRow(areaWidth), 4412 w= 0, 4413 cols= [], 4414 curCol= 0, 4415 newAreaWidth = areaWidth, 4416 tnWidth= G.tn.defaultSize.getOuterWidth(); 4417 var scaleFactor = 1; 4418 var nbTn= G.GOM.items.length; 4419 var borderWidth = G.tn.opt.Get('borderHorizontal') * 2; 4420 var borderHeight = G.tn.opt.Get('borderVertical') * 2; 4421 4422 // retrieve gutter width 4423 if( G.O.thumbnailAlignment == 'justified' ) { 4424 maxCol = Math.min( maxCol, nbTn); 4425 gutterWidth = (maxCol==1 ? 0 : (areaWidth-(maxCol*tnWidth))/(maxCol-1)); 4426 } 4427 else { 4428 gutterWidth = G.tn.settings.GetResponsive('gutterWidth'); 4429 } 4430 4431 // first loop to retrieve the real used width of the area (the evaluation is based on the content of the first line) 4432 // Retrieve the real used width of the area (the evaluation is based on the content of the first line) 4433 if( G.O.RTL || G.O.thumbnailAlignment == 'fillWidth' ) { 4434 // scaled --> evaluate scale factor and number of columns 4435 var totalGutterWidth = (maxCol-1) * gutterWidth; 4436 scaleFactor = (areaWidth - totalGutterWidth) / (maxCol*tnWidth); 4437 if( scaleFactor > 1 ) { 4438 maxCol++; // add one column and re-evaluate the scale factor 4439 } 4440 totalGutterWidth = (maxCol-1) * gutterWidth; 4441 scaleFactor = Math.min( (areaWidth - totalGutterWidth) / (maxCol*tnWidth), 1); // no upscale 4442 newAreaWidth = (maxCol*tnWidth) + totalGutterWidth; 4443 } 4444 4445 4446 G.GOM.lastFullRow = 0 ; // display at leat 1 row (even if not full) 4447 // var lastPosY = 0; 4448 var row = 0; 4449 4450 tnWidth = Math.round(tnWidth * scaleFactor); 4451 var contentWidth = tnWidth - borderWidth; 4452 var tnHeight = Math.round(G.tn.defaultSize.getOuterHeight() * scaleFactor) + G.tn.labelHeight.get(); 4453 var contentHeight = Math.round( G.tn.defaultSize.getOuterHeight() * scaleFactor) - borderHeight; 4454 4455 // loop to position and to set size of all thumbnails 4456 for( var i = 0; i < nbTn ; i++ ) { 4457 if( curPosY == 0 ) { 4458 curPosX = curCol * (tnWidth + gutterWidth) 4459 cols[curCol] = curPosX; 4460 w = curPosX + tnWidth; 4461 } 4462 else { 4463 curPosX = cols[curCol]; 4464 } 4465 4466 var x = curPosX; 4467 if( G.O.RTL ) { 4468 x = parseInt(newAreaWidth) - curPosX - tnWidth; 4469 } 4470 4471 // MANDATORY : set thumbnail position AND size 4472 var curTn=G.GOM.items[i]; 4473 curTn.top = curPosY; 4474 curTn.left = x; 4475 curTn.height = tnHeight; 4476 curTn.width = tnWidth; 4477 // image size 4478 if( G.O.thumbnailAlignment == 'fillWidth' ) { 4479 curTn.resizedContentWidth = contentWidth; 4480 curTn.resizedContentHeight = contentHeight; 4481 } 4482 curTn.row = row; 4483 // lastPosY = curPosY; 4484 4485 curCol++; 4486 if( curCol >= maxCol ){ 4487 // new line 4488 curCol = 0; 4489 curPosY += tnHeight + gutterHeight; 4490 G.GOM.lastFullRow = row; 4491 row++; 4492 } 4493 } 4494 G.GOM.displayArea.width = w; 4495 4496 return true; 4497 } 4498 4499 4500 4501 4502 //----- Display the thumbnails according to the calculated layout 4503 function GalleryDisplayPart1() { 4504 if( G.CSStransformName == null ) { 4505 G.$E.conTn.css( 'left' , '0px'); 4506 } 4507 else { 4508 G.$E.conTn.css( G.CSStransformName , 'none'); 4509 } 4510 // CacheViewport(); 4511 } 4512 4513 function CacheViewport() { 4514 G.GOM.cache.viewport = getViewport(); 4515 // G.GOM.cache.areaWidth = G.$E.conTnParent.width(); 4516 G.GOM.cache.areaWidth = G.$E.base.width(); 4517 4518 // position of the gallery container 4519 // we use the position of the loadingbar because : 4520 // - the gallery may be wrong positioned due to one display animation currently running 4521 // - the loadingbar is never animated and positioned just before the gallery container 4522 //G.GOM.cache.containerOffset = G.$E.conTnParent.offset(); 4523 if( !G.O.lightboxStandalone ) { 4524 G.GOM.cache.containerOffset = G.$E.conLoadingB.offset(); 4525 } 4526 } 4527 4528 4529 4530 function GalleryDisplayPart2( forceTransition ) { 4531 CacheViewport(); 4532 4533 var nbTn = G.GOM.items.length; 4534 G.GOM.itemsDisplayed = 0; 4535 var threshold = 50; 4536 var cnt = 0; // counter for delay between each thumbnail display 4537 4538 4539 GalleryRenderGetInterval(); 4540 4541 for( var i = 0; i < nbTn ; i++ ) { 4542 let curTn = G.GOM.items[i]; 4543 if( i >= G.GOM.displayInterval.from && cnt < G.GOM.displayInterval.len ) { 4544 curTn.inDisplayArea = true; 4545 if( forceTransition ) { 4546 curTn.neverDisplayed = true; 4547 } 4548 G.GOM.itemsDisplayed++; 4549 cnt++; 4550 } 4551 else{ 4552 curTn.inDisplayArea = false; 4553 } 4554 } 4555 4556 // bottom of the gallery (pagination, more button...) 4557 GalleryBottomManage(); 4558 4559 var tnToDisplay = []; 4560 var tnToReDisplay = []; 4561 4562 CacheViewport(); 4563 G.GOM.clipArea.top = -1; 4564 cnt = 0 ; 4565 var lastTnIdx = -1; 4566 G.GOM.clipArea.height = 0; 4567 // NOTE: loop always the whole GOM.items --> in case an already displayed thumbnail needs to be removed 4568 for( var i = 0; i < nbTn ; i++ ) { 4569 let curTn = G.GOM.items[i]; 4570 if( curTn.inDisplayArea ) { 4571 if( G.GOM.clipArea.top == -1 ) { 4572 G.GOM.clipArea.top = curTn.top; 4573 } 4574 if( (curTn.top - G.GOM.clipArea.top) <= -1 ) { 4575 // with mosaic layout, the first thumbnail may not give the top position 4576 G.GOM.clipArea.top = curTn.top; 4577 } 4578 4579 G.GOM.clipArea.height = Math.max( G.GOM.clipArea.height, curTn.top-G.GOM.clipArea.top + curTn.height); 4580 4581 if( curTn.neverDisplayed ) { 4582 // thumbnail is not displayed -> check if in viewport to display or not 4583 var top = G.GOM.cache.containerOffset.top + (curTn.top - G.GOM.clipArea.top); 4584 // var left=containerOffset.left+curTn.left; 4585 if( (top + curTn.height) >= (G.GOM.cache.viewport.t - threshold) && top <= (G.GOM.cache.viewport.t + G.GOM.cache.viewport.h + threshold) ) { 4586 // build thumbnail 4587 let item = G.I[curTn.thumbnailIdx]; 4588 if( item.$elt == null ) { 4589 // ThumbnailBuild( item, curTn.thumbnailIdx, i, (i+1) == nbTn ); 4590 ThumbnailBuild( item, curTn.thumbnailIdx, i ); 4591 } 4592 tnToDisplay.push({idx:i, delay:cnt, top: curTn.top, left: curTn.left}); 4593 cnt++; 4594 } 4595 } 4596 else { 4597 tnToReDisplay.push({idx: i, delay: 0, top: curTn.top, left: curTn.left}); 4598 } 4599 // G.GOM.itemsDisplayed++; 4600 lastTnIdx = i; 4601 } 4602 else { 4603 curTn.displayed = false; 4604 let item = G.I[curTn.thumbnailIdx]; 4605 if( item.$elt != null ){ 4606 item.$elt.css({ opacity: 0, display: 'none' }); 4607 } 4608 } 4609 } 4610 4611 var areaWidth = G.$E.conTnParent.width(); 4612 4613 // set gallery area really used size 4614 // if( G.GOM.displayArea.width != G.GOM.displayAreaLast.width || G.GOM.displayArea.height != G.GOM.displayAreaLast.height ) { 4615 if( G.GOM.displayArea.width != G.GOM.displayAreaLast.width || G.GOM.clipArea.height != G.GOM.displayAreaLast.height ) { 4616 G.$E.conTn.width( G.GOM.displayArea.width ).height( G.GOM.clipArea.height ); 4617 G.GOM.displayAreaLast.width = G.GOM.displayArea.width; 4618 G.GOM.displayAreaLast.height = G.GOM.clipArea.height; 4619 // G.GOM.displayAreaLast.height=G.GOM.displayArea.height-G.GOM.clipArea.top; 4620 } 4621 4622 if( areaWidth != G.$E.conTnParent.width() ) { 4623 // gallery area width changed since layout calculation (for example when a scrollbar appeared) 4624 // so we need re-calculate the layout before displaying the thumbnails 4625 G.GOM.cache.areaWidth = G.$E.conTnParent.width(); 4626 GallerySetLayout(); 4627 GalleryDisplayPart1(); 4628 GalleryDisplayPart2( forceTransition ); 4629 return; 4630 } 4631 4632 // counter of not displayed images (is displayed on the last thumbnail) 4633 if( G.layout.support.rows ) { 4634 if( G.galleryDisplayMode.Get() == 'ROWS' || (G.galleryDisplayMode.Get() == 'FULLCONTENT' && G.galleryLastRowFull.Get() && G.GOM.lastFullRow != -1) ){ 4635 if( lastTnIdx < (nbTn - 1) ) { 4636 G.GOM.lastDisplayedIdxNew = lastTnIdx; 4637 } 4638 else { 4639 G.GOM.lastDisplayedIdxNew =- 1; 4640 } 4641 // remove last displayed counter 4642 if( G.GOM.lastDisplayedIdx != -1 ) { 4643 let item = G.I[G.GOM.items[G.GOM.lastDisplayedIdx].thumbnailIdx]; 4644 item.$getElt('.nGY2GThumbnailIconsFullThumbnail').html(''); 4645 } 4646 } 4647 } 4648 4649 4650 // batch set position (and display animation) to all thumbnails 4651 // first display newly built thumbnails 4652 4653 G.GOM.thumbnails2Display=[]; 4654 4655 var duration = ThumbnailPreparePosition( tnToDisplay ); 4656 ThumbnailPreparePosition( tnToReDisplay ); 4657 4658 ThumbnailDisplayAnimBatch(); 4659 4660 if( G.tn.opt.Get('displayTransition') == 'NONE' ) { 4661 G.galleryResizeEventEnabled = true; 4662 // GalleryThumbnailSliderBuildAndStart(); // image slider on last displayed thumbnail 4663 TriggerCustomEvent('galleryDisplayed'); 4664 } 4665 else { 4666 // setTimeout(function() { 4667 requestTimeout( function() { 4668 // change value after the end of the display transistion of the newly built thumbnails 4669 G.galleryResizeEventEnabled = true; 4670 // GalleryThumbnailSliderBuildAndStart(); // image slider on last displayed thumbnail 4671 TriggerCustomEvent('galleryDisplayed'); 4672 // }, nbBuild * G.tn.opt.Get('displayInterval')); 4673 }, duration * G.tn.opt.Get('displayInterval')); 4674 } 4675 4676 } 4677 4678 4679 function ThumbnailPreparePosition( lstThumb ) { 4680 4681 var nbBuild = lstThumb.length; 4682 if( nbBuild == 0 ) { return 0; } 4683 4684 4685 var displayOrder = G.tn.opt.Get('displayOrder'); 4686 4687 if( displayOrder == 'random' ) { 4688 NGY2Tools.AreaShuffle( lstThumb ); 4689 } 4690 else { 4691 if( displayOrder == 'rowByRow' && !( G.layout.engine == 'JUSTIFIED' || G.layout.engine == 'GRID' )) { 4692 displayOrder = ''; 4693 } 4694 if( (displayOrder == 'colFromRight' || displayOrder == 'colFromLeft' ) && !(G.layout.engine == 'CASCADING' || G.layout.engine == 'GRID' )) { 4695 displayOrder = ''; 4696 } 4697 } 4698 4699 4700 // DISPLAY COLUMN BY COLUMN 4701 if( displayOrder == 'colFromRight' || displayOrder == 'colFromLeft' ) { 4702 var tab = []; 4703 var cols = []; 4704 for( var i = 0; i < nbBuild ; i++ ) { 4705 if( tab[lstThumb[i].left] == undefined ) { 4706 tab[lstThumb[i].left] = []; 4707 cols.push( lstThumb[i].left ); 4708 } 4709 tab[lstThumb[i].left].push( lstThumb[i].idx ) 4710 } 4711 if( displayOrder == 'colFromRight' ) { 4712 cols = cols.reverse(); 4713 } 4714 for( var i = 0; i < cols.length; i++ ) { 4715 var col = cols[i]; 4716 for( var j = 0; j < tab[col].length; j++ ) { 4717 ThumbnailSetPosition( tab[col][j], i); 4718 } 4719 } 4720 return(i); 4721 } 4722 4723 4724 // STANDARD DISPLAY OR ROW BY ROW 4725 var d = 0; 4726 var top = lstThumb[0].top; 4727 for( var i = 0; i < nbBuild ; i++ ) { 4728 // ThumbnailSetPosition(tnToDisplay[i].idx, tnToDisplay[i].delay+10); 4729 // ThumbnailSetPosition(tnToDisplay[i].idx, i); 4730 4731 if( displayOrder == 'rowByRow' ) { 4732 // DISPLAY ROW BY ROW 4733 if( lstThumb[i].top > top ) { 4734 d++; 4735 top = lstThumb[i].top; 4736 } 4737 } 4738 else { 4739 d++; 4740 } 4741 ThumbnailSetPosition(lstThumb[i].idx, d); 4742 } 4743 return(d); 4744 4745 } 4746 4747 // Thumbnail: set the new position 4748 function ThumbnailSetPosition( GOMidx, cnt ) { 4749 var newTop= 0; 4750 var curTn= G.GOM.items[GOMidx]; 4751 var idx= G.GOM.items[GOMidx].thumbnailIdx; 4752 var item= G.I[idx]; 4753 4754 if( curTn.neverDisplayed ) { 4755 // thumbnail is built but has never been displayed (=first display) 4756 var top = curTn.top - G.GOM.clipArea.top; 4757 if( G.tn.opt.Get('stacks') > 0 ) { 4758 // we have stacks -> do not display them here. They will be displayed at the end of the display animation 4759 item.$elt.last().css({ display: 'block'}); 4760 item.$elt.css({ top: top , left: curTn.left }); 4761 } 4762 else { 4763 item.$elt.css({ display: 'block', top: top , left: curTn.left }); 4764 } 4765 newTop=top; 4766 4767 // display the image of the thumbnail when fully loaded 4768 if( G.O.thumbnailWaitImageLoaded === true ) { 4769 var gi_imgLoad = ngimagesLoaded( item.$getElt('.nGY2TnImg2') ); 4770 gi_imgLoad.on( 'progress', function( instance, image ) { 4771 if( image.isLoaded ) { 4772 var albumIdx = image.img.getAttribute('data-albumidx'); 4773 if( albumIdx == G.GOM.albumIdx ) { 4774 // ignore event if not on current album 4775 var idx = image.img.getAttribute('data-idx'); 4776 G.I[idx].ThumbnailImageReveal(); 4777 } 4778 } 4779 }); 4780 } 4781 // display the thumbnail 4782 ThumbnailAppear(GOMidx, cnt); 4783 4784 curTn.displayed = true; 4785 curTn.neverDisplayed = false; 4786 } 4787 else { 4788 var topOld = G.GOM.cache.containerOffset.top + item.top; 4789 var top = G.GOM.cache.containerOffset.top + (curTn.top - G.GOM.clipArea.top); 4790 newTop = curTn.top - G.GOM.clipArea.top; 4791 var vp = G.GOM.cache.viewport; 4792 if( G.O.thumbnailDisplayOutsideScreen || ( ( (topOld + curTn.height) >= (vp.t - vp.h) && topOld <= (vp.t + vp.h * 4) ) || 4793 ( (top + curTn.height) >= (vp.t - vp.h) && top <= (vp.t + vp.h * 4) ) ) ) { 4794 // thumbnail positioned in enlarged viewport (viewport + 4 x viewport height) (v1.5: changed from 2 to 4) 4795 if( curTn.displayed ) { 4796 // thumbnail is displayed 4797 if( item.top != curTn.top || item.left != curTn.left ) { 4798 // set position 4799 if( G.O.galleryResizeAnimation == true ) { 4800 // with transition 4801 var tweenable = new NGTweenable(); 4802 tweenable.tween({ 4803 from: { top: item.top, left: item.left, height: item.height, width: item.width }, 4804 to: { top: newTop, left: curTn.left, height: curTn.height, width: curTn.width }, 4805 attachment: { $e: item.$elt }, 4806 duration: 100, 4807 delay: cnt * G.tn.opt.Get('displayInterval') / 5, 4808 // easing: 'easeInOutQuad', 4809 easing: 'easeOutQuart', 4810 step: function (state, att) { 4811 // window.ng_draf( function() { 4812 att.$e.css(state); 4813 // }); 4814 }, 4815 finish: function (state, att) { 4816 var _this=this; 4817 // window.ng_draf( function() { 4818 _this.dispose(); 4819 // }); 4820 } 4821 }); 4822 } 4823 else { 4824 // set position without transition 4825 // item.$elt.css({ top: curTn.top , left: curTn.left }); 4826 item.$elt.css({ top: newTop , left: curTn.left }); 4827 } 4828 } 4829 } 4830 else { 4831 // re-display thumbnail 4832 curTn.displayed = true; 4833 // item.$elt.css({ display: 'block', top: curTn.top , left: curTn.left, opacity:1 }); 4834 item.$elt.css({ display: 'block', top: newTop, left: curTn.left, opacity: 1 }); 4835 ThumbnailAppearFinish(item); 4836 } 4837 } 4838 else { 4839 // undisplay thumbnail if not in viewport+margin --> performance gain 4840 curTn.displayed = false; 4841 item.$elt.css({ display: 'none'}); 4842 } 4843 } 4844 item.left = curTn.left; 4845 item.top = newTop; 4846 4847 // set new size if changed 4848 if( item.width != curTn.width || item.height != curTn.height ) { 4849 item.$elt.css({ width: curTn.width , height: curTn.height }); 4850 item.width = curTn.width; 4851 item.height = curTn.height; 4852 4853 // if( curTn.resizedContentWidth > 0 ) { 4854 // resize also the content (=image) 4855 if( item.resizedContentWidth != curTn.resizedContentWidth || item.resizedContentHeight != curTn.resizedContentHeight ) { 4856 if( item.kind == 'albumUp' ) { 4857 // item.$getElt('.nGY2GThumbnailAlbumUp').css({'height': curTn.resizedContentHeight, 'width': curTn.resizedContentWidth}); 4858 } 4859 else { 4860 item.$getElt('.nGY2GThumbnailImage').css({'height': curTn.resizedContentHeight, 'width': curTn.resizedContentWidth}); 4861 4862 if( G.layout.engine == 'JUSTIFIED' ) { 4863 item.$getElt('.nGY2GThumbnailImg').css({'height': curTn.resizedContentHeight, 'width': curTn.resizedContentWidth}); 4864 } 4865 } 4866 item.resizedContentWidth = curTn.resizedContentWidth; 4867 item.resizedContentHeight = curTn.resizedContentHeight; 4868 } 4869 } 4870 4871 4872 // add counter of remaining (not displayed) images 4873 if( G.GOM.lastDisplayedIdxNew == GOMidx && G.layout.support.rows ) { 4874 if( (G.galleryDisplayMode.Get() == 'ROWS' && G.galleryMaxRows.Get() > 0) || (G.galleryDisplayMode.Get() == 'FULLCONTENT' && G.galleryLastRowFull.Get() && G.GOM.lastFullRow != -1) ){ 4875 // number of items 4876 var nb = G.GOM.items.length - GOMidx - 1; 4877 if( item.albumID != '0' && G.O.thumbnailLevelUp ) { 4878 nb--; 4879 } 4880 4881 if( nb > 0 ) { 4882 // display counter 4883 if( G.O.thumbnailOpenInLightox || G.O.thumbnailSliderDelay > 0 ) { 4884 item.$getElt('.nGY2GThumbnailIconsFullThumbnail').html( '+' + nb); 4885 } 4886 4887 // if( G.layout.engine == 'GRID' && G.GOM.slider.hostItem != G.GOM.NGY2Item(GOMidx) ) { 4888 // image slider on last displayed thumbnail 4889 if( G.O.thumbnailLabel.get('position') != 'right' && G.O.thumbnailLabel.get('position') != 'left' ) { 4890 if( G.GOM.slider.hostItem != G.GOM.NGY2Item(GOMidx) ) { 4891 4892 // set current slider back to initial content 4893 GalleryThumbnailSliderSetContent( G.GOM.slider.hostItem ); 4894 // new slider 4895 G.GOM.slider.hostIdx = GOMidx; 4896 G.GOM.slider.hostItem = G.GOM.NGY2Item(GOMidx); 4897 G.GOM.slider.nextIdx = GOMidx; 4898 G.GOM.slider.currentIdx = GOMidx; 4899 GalleryThumbnailSliderBuildAndStart(); // image slider on last displayed thumbnail 4900 // GalleryThumbnailSliderSetNextContent(); 4901 } 4902 } 4903 } 4904 else { 4905 // reset slider content to initial content because all thumbnails are displayed 4906 GalleryThumbnailSliderSetContent( G.GOM.slider.hostItem ); 4907 G.GOM.slider.hostIdx = -1; 4908 } 4909 4910 G.GOM.lastDisplayedIdx = GOMidx; 4911 } 4912 } 4913 4914 } 4915 4916 // --------------------- 4917 // replace image on last thumbnails with not displayed ones (mode ROWS or FULLCONTENT with galleryLastRowFull enabled) 4918 // function GalleryLastThumbnailSlideImage() { 4919 function GalleryThumbnailSliderBuildAndStart() { 4920 4921 if( G.O.thumbnailSliderDelay == 0 || G.GOM.slider.hostIdx == -1 ) { 4922 return; 4923 } 4924 clearTimeout(G.GOM.slider.timerID); 4925 4926 var item = G.GOM.slider.hostItem; 4927 4928 // dupplicate image layer -> for the next image 4929 if( item.$getElt('.nGY2TnImgNext').length == 0 ) { 4930 item.$getElt('.nGY2TnImg').clone().removeClass('nGY2TnImg').addClass('nGY2TnImgNext').insertAfter(item.$getElt('.nGY2TnImg')); 4931 item.$getElt('.nGY2TnImgBack').clone().removeClass('nGY2TnImgBack').addClass('nGY2TnImgBackNext').insertAfter(item.$getElt('.nGY2TnImg', true)); 4932 item.$getElt('.nGY2GThumbnailImage', true); // important -> refresh the cache 4933 item.$getElt('.nGY2GThumbnailImg', true); // important -> refresh the cache 4934 } 4935 4936 item.CSSTransformSet('.nGY2TnImgNext', 'translateX', '100%', true); 4937 item.CSSTransformApply( '.nGY2TnImgNext' ); 4938 item.CSSTransformSet('.nGY2TnImgBackNext', 'translateX', '100%', true); 4939 item.CSSTransformApply( '.nGY2TnImgBackNext' ); 4940 4941 GalleryThumbnailSliderSetNextContent(); 4942 4943 // clearTimeout(G.GOM.slider.timerID); 4944 // G.GOM.slider.timerID = setTimeout(function(){ GalleryThumbnailSliderStartTransition() }, G.O.thumbnailSliderDelay); 4945 G.GOM.slider.timerID = requestTimeout(function(){ GalleryThumbnailSliderStartTransition() }, G.O.thumbnailSliderDelay); 4946 } 4947 4948 4949 function GalleryThumbnailSliderSetNextContent() { 4950 4951 G.GOM.slider.nextIdx++; 4952 if( G.GOM.slider.nextIdx >= G.GOM.items.length ) { 4953 G.GOM.slider.nextIdx = G.GOM.slider.hostIdx; 4954 } 4955 4956 // new image 4957 var newItem = G.GOM.NGY2Item(G.GOM.slider.nextIdx); 4958 // var imgBlurred = G.emptyGif; 4959 var bgImg = "url('" + G.emptyGif + "')"; 4960 if( newItem.imageDominantColors != null ) { 4961 // imgBlurred = newItem.imageDominantColors; 4962 bgImg = "url('" + newItem.imageDominantColors + "')"; 4963 } 4964 G.GOM.slider.hostItem.$getElt('.nGY2TnImgBackNext', true).css({'background-image': bgImg, opacity: 1 }); 4965 G.GOM.slider.hostItem.$getElt('.nGY2TnImgNext', true).css({ 'background-image': "url('" + newItem.thumbImg().src + "')", opacity: 1 }); 4966 G.GOM.slider.hostItem.$getElt('.nGY2TnImgNext .nGY2GThumbnailImg', true).attr('src', newItem.thumbImg().src ); 4967 4968 4969 } 4970 4971 // thumbnail slider - transition from one image to the next one 4972 function GalleryThumbnailSliderStartTransition() { 4973 4974 if( G.GOM.slider.hostItem.$getElt() != null ) { 4975 4976 // slider transition 4977 var tweenable = new NGTweenable(); 4978 G.GOM.slider.tween = tweenable; 4979 tweenable.tween({ 4980 from: { 'left': 100 }, 4981 to: { 'left': 0 }, 4982 duration: 800, 4983 delay: 0, 4984 // easing: 'easeInOutQuad', 4985 easing: 'easeOutQuart', 4986 4987 step: function (state) { 4988 if( G.GOM.slider.hostItem.$getElt() == null ) { 4989 // the thumbnail may have been destroyed since the start of the animation 4990 G.GOM.slider.tween.stop(false); 4991 return; 4992 } 4993 4994 // window.ng_draf( function() { 4995 // slide current content 4996 G.GOM.slider.hostItem.CSSTransformSet('.nGY2TnImgBack', 'translateX', -(100 - state.left) + '%'); 4997 G.GOM.slider.hostItem.CSSTransformApply( '.nGY2TnImgBack' ); 4998 G.GOM.slider.hostItem.CSSTransformSet('.nGY2TnImg', 'translateX', -(100 - state.left) + '%'); 4999 G.GOM.slider.hostItem.CSSTransformApply( '.nGY2TnImg' ); 5000 5001 // slide new content 5002 G.GOM.slider.hostItem.CSSTransformSet('.nGY2TnImgBackNext', 'translateX', state.left + '%'); 5003 G.GOM.slider.hostItem.CSSTransformApply( '.nGY2TnImgBackNext' ); 5004 G.GOM.slider.hostItem.CSSTransformSet('.nGY2TnImgNext', 'translateX', state.left + '%'); 5005 G.GOM.slider.hostItem.CSSTransformApply( '.nGY2TnImgNext' ); 5006 // }); 5007 5008 5009 }, 5010 finish: function (state) { 5011 if( G.GOM.slider.hostItem.$getElt() == null ) { 5012 // the thumbnail may be destroyed since the start of the animation 5013 return; 5014 } 5015 5016 if( G.GOM.NGY2Item(G.GOM.slider.nextIdx) == null ) { return; } // item does not exist anymore 5017 5018 // window.ng_draf( function() { 5019 // set new content as current content 5020 GalleryThumbnailSliderSetContent( G.GOM.NGY2Item(G.GOM.slider.nextIdx) ); 5021 G.GOM.slider.currentIdx = G.GOM.slider.nextIdx; 5022 GalleryThumbnailSliderSetNextContent(); 5023 5024 clearTimeout(G.GOM.slider.timerID); 5025 // G.GOM.slider.timerID=setTimeout(function(){ GalleryThumbnailSliderStartTransition() }, G.O.thumbnailSliderDelay); 5026 G.GOM.slider.timerID = requestTimeout(function(){ GalleryThumbnailSliderStartTransition() }, G.O.thumbnailSliderDelay); 5027 // }); 5028 } 5029 }); 5030 } 5031 } 5032 5033 // set main content of the thumbnail hosting the slider 5034 // hide the elements for the next content of the slider 5035 function GalleryThumbnailSliderSetContent( ngy2itemContent ) { 5036 if( G.GOM.slider.hostIdx == -1 ) { return; } 5037 5038 if( G.GOM.slider.tween != null ) { 5039 if( G.GOM.slider.tween._isTweening == true ) { 5040 G.GOM.slider.tween.stop(false); 5041 } 5042 } 5043 5044 var bgImg = "url('" + G.emptyGif + "')"; 5045 if( ngy2itemContent.imageDominantColors != null ) { 5046 bgImg = "url('" + ngy2itemContent.imageDominantColors + "')"; 5047 } 5048 G.GOM.slider.hostItem.$getElt('.nGY2TnImgBack').css('background-image', bgImg); 5049 G.GOM.slider.hostItem.$getElt('.nGY2TnImg').css('background-image', "url('" + ngy2itemContent.thumbImg().src + "')" ); 5050 G.GOM.slider.hostItem.$getElt('.nGY2TnImg .nGY2GThumbnailImg').attr('src', ngy2itemContent.thumbImg().src ); 5051 5052 G.GOM.slider.hostItem.CSSTransformSet('.nGY2TnImgBack', 'translateX', '0'); 5053 G.GOM.slider.hostItem.CSSTransformApply( '.nGY2TnImgBack' ); 5054 G.GOM.slider.hostItem.CSSTransformSet('.nGY2TnImg', 'translateX', '0'); 5055 G.GOM.slider.hostItem.CSSTransformApply( '.nGY2TnImg' ); 5056 5057 // place the containers for the next image slider outside of the thumbnail (=hidden) 5058 G.GOM.slider.hostItem.CSSTransformSet('.nGY2TnImgBackNext', 'translateX', '100%', true); 5059 G.GOM.slider.hostItem.CSSTransformApply( '.nGY2TnImgBackNext' ); 5060 G.GOM.slider.hostItem.CSSTransformSet('.nGY2TnImgNext', 'translateX', '100%', true); 5061 G.GOM.slider.hostItem.CSSTransformApply( '.nGY2TnImgNext' ); 5062 5063 // set new title and description 5064 if( G.O.thumbnailLabel.get('display') == true ) { 5065 var icons = G.O.icons.thumbnailAlbum; 5066 if( ngy2itemContent.kind != 'album' ) { 5067 icons = G.O.icons.thumbnailImage; 5068 } 5069 G.GOM.slider.hostItem.$getElt('.nGY2GThumbnailTitle').html(icons + getThumbnailTitle(ngy2itemContent)); 5070 G.GOM.slider.hostItem.$getElt('.nGY2GThumbnailDescription').html(icons + getTumbnailDescription(ngy2itemContent)); 5071 } 5072 } 5073 5074 5075 5076 // Compute the height of the label part of a thumbnail (title+description, both single line) 5077 function ThumbnailGetLabelHeight() { 5078 var newElt = [], 5079 newEltIdx = 0; 5080 5081 // if( G.O.thumbnailLabel.get('display') == false && G.tn.toolbar.getWidth(item) <= 0 ) { 5082 if( G.O.thumbnailLabel.get('display') == false ) { 5083 return 0; 5084 } 5085 5086 // var desc=''; 5087 // if( G.O.thumbnailLabel.get('displayDescription') == true ) { 5088 // desc = 'aAzZjJ'; 5089 // } 5090 5091 // visibility set to hidden 5092 newElt[newEltIdx++] = '<div class="nGY2GThumbnail ' + G.O.theme + '" style="display:block;visibility:hidden;position:absolute;top:-9999px;left:-9999px;" ><div class="nGY2GThumbnailSub">'; 5093 if( G.O.thumbnailLabel.get('display') == true ) { 5094 // Labels: title and description 5095 newElt[newEltIdx++] = ' <div class="nGY2GThumbnailLabel" '+ G.tn.style.getLabel() +'>'; 5096 newElt[newEltIdx++] = ' <div class="nGY2GThumbnailAlbumTitle" '+G.tn.style.getTitle()+'>aAzZjJ</div>'; 5097 if( G.O.thumbnailLabel.get('displayDescription') == true ) { 5098 newElt[newEltIdx++] = ' <div class="nGY2GThumbnailDescription" '+G.tn.style.getDesc()+'>'+'aAzZjJ'+'</div>'; 5099 } 5100 newElt[newEltIdx++] = ' </div>'; 5101 } 5102 5103 newElt[newEltIdx++] = '</div></div>'; 5104 5105 var $newDiv = jQuery(newElt.join('')).appendTo(G.$E.conTn); 5106 var h = $newDiv.find('.nGY2GThumbnailLabel').outerHeight(true); 5107 $newDiv.remove(); 5108 5109 return h; 5110 } 5111 5112 function ThumbnailBuildStacks( bgColor ) { 5113 var ns=G.tn.opt.Get('stacks'); 5114 if( ns == 0 ) { return ''; } 5115 5116 var s=''; 5117 for( var i=0; i<ns; i++ ) { 5118 s='<div class="nGY2GThumbnailStack " style="display:none;'+bgColor+'"></div>'+s; 5119 } 5120 return s; 5121 } 5122 5123 //----- Build one UP thumbnail (=navigation thumbnail) 5124 function ThumbnailBuildAlbumpUp( item, GOMidx ) { 5125 // function ThumbnailBuildAlbumpUp( item, idx, GOMidx ) { 5126 var newElt = [], 5127 newEltIdx = 0; 5128 5129 var mp = ''; 5130 if( G.O.thumbnailOpenInLightox === false ) { 5131 mp = 'cursor:default;' 5132 } 5133 5134 newElt[newEltIdx++] = ThumbnailBuildStacks('') + '<div class="nGY2GThumbnail" style="display:none;opacity:0;' + mp + '" >'; 5135 newElt[newEltIdx++] = ' <div class="nGY2GThumbnailSub">'; 5136 5137 var h=G.tn.defaultSize.getHeight(), 5138 w=G.tn.defaultSize.getWidth(); 5139 5140 newElt[newEltIdx++] = ' <div class="nGY2GThumbnailImage" style="width:'+w+'px;height:'+h+'px;"><img class="nGY2GThumbnailImg" src="'+G.emptyGif+'" alt="" style="max-width:'+w+'px;max-height:'+h+'px;" ></div>'; 5141 // newElt[newEltIdx++] = ' <div class="nGY2GThumbnailAlbumUp" style="width:'+w+'px;height:'+h+'px;">'+G.O.icons.thumbnailAlbumUp+'</div>'; 5142 newElt[newEltIdx++] = ' <div class="nGY2GThumbnailAlbumUp" >'+G.O.icons.thumbnailAlbumUp+'</div>'; 5143 newElt[newEltIdx++] = ' </div>'; 5144 newElt[newEltIdx++] = '</div>'; 5145 5146 var $newDiv = jQuery(newElt.join('')).appendTo(G.$E.conTn); //.animate({ opacity: 1},1000, 'swing'); //.show('slow'); //.fadeIn('slow').slideDown('slow'); 5147 5148 item.$elt = $newDiv; 5149 $newDiv.data('index', GOMidx); 5150 item.$getElt('.nGY2GThumbnailImg').data('index', GOMidx); 5151 5152 return; 5153 } 5154 5155 5156 //----- Build one thumbnail 5157 function ThumbnailBuild( item, idx, GOMidx ) { 5158 // function ThumbnailBuild( item, idx, GOMidx, lastOne ) { 5159 item.eltTransform = []; 5160 item.eltFilter = []; 5161 item.hoverInitDone = false; 5162 item.$Elts = []; 5163 5164 if( item.kind == 'albumUp' ) { 5165 ThumbnailBuildAlbumpUp( item, GOMidx); 5166 return; 5167 } 5168 5169 var newElt = [], 5170 newEltIdx = 0; 5171 5172 var mp = ''; 5173 if( G.O.thumbnailOpenInLightox === false ) { 5174 mp = 'cursor:default;' 5175 } 5176 5177 // var src = encodeURI(item.thumbImg().src), 5178 5179 var src = (item.thumbImg().src).replace(/'/g, "%27"), // replace single quote with %27 5180 sTitle = getThumbnailTitle(item); 5181 5182 // image background -> visible during image download 5183 var bg = ''; 5184 var bgImg = "background-image: url('" + G.emptyGif + "');"; 5185 if( item.imageDominantColors != null ) { 5186 // dominant colorS (blurred preview image) 5187 bgImg = "background-image: url('" + item.imageDominantColors + "');"; 5188 } 5189 else { 5190 // dominant color -> background color 5191 if( item.imageDominantColor != null ) { 5192 bg = 'background-color:' + item.imageDominantColor + ';'; 5193 } 5194 else { 5195 bgImg = ''; 5196 } 5197 } 5198 5199 var op = 'opacity:1;'; 5200 if( G.O.thumbnailWaitImageLoaded == true ) { 5201 op = 'opacity:0;'; 5202 } 5203 5204 // ##### thumbnail containers (with stacks) 5205 newElt[newEltIdx++] = ThumbnailBuildStacks(bg) + '<div class="nGY2GThumbnail nGY2GThumbnail_'+G.GOM.curNavLevel+'" style="display:none;opacity:0;' + mp + '"><div class="nGY2GThumbnailSub ' + ( G.O.thumbnailSelectable && item.selected ? "nGY2GThumbnailSubSelected" : "" ) + '">'; 5206 5207 5208 // image size 5209 var w = G.tn.settings.getW(); 5210 var h = G.tn.settings.getH(); 5211 if( G.tn.settings.getMosaic() !== null ) { 5212 // mosaic layout -> 5213 w = G.GOM.items[GOMidx].width; 5214 h = G.GOM.items[GOMidx].height; 5215 } 5216 5217 var bgSize = 'contain'; 5218 if( G.tn.opt.Get('crop') ) { 5219 bgSize = 'cover'; // thumbnail image will be cropped to fit in the thumbnail (no black border) 5220 } 5221 5222 // ##### layer for image background (color, dominant color, blurred preview) 5223 var s1 = "position: absolute; top: 0px; left: 0px; width:" + w + "px; height:" + h + "px;"+ bg + bgImg + " background-position: center center; background-repeat: no-repeat; background-size:" + bgSize + "; overflow: hidden;"; 5224 newElt[newEltIdx++]='<div class="nGY2GThumbnailImage nGY2TnImgBack" style="' + s1 + '"></div>'; 5225 5226 // #### layer for image 5227 // for url in CSS: single backslashes are replaced by double backslashes 5228 var s2 = op + "position: absolute; top: 0px; left: 0px; width:" + w + "px; height:" + h + "px; background-image: url('" + src.replace(/\\/g, '\\\\') + "'); background-position: center center; background-repeat: no-repeat; background-size:" + bgSize + "; overflow: hidden;"; 5229 newElt[newEltIdx++]='<div class="nGY2GThumbnailImage nGY2TnImg" style="' + s2 + '">'; 5230 newElt[newEltIdx++]=' <img class="nGY2GThumbnailImg nGY2TnImg2" src="' + src + '" alt="' + sTitle + '" style="opacity:0;" data-idx="' + idx + '" data-albumidx="' + G.GOM.albumIdx + '" >'; 5231 newElt[newEltIdx++]='</div>'; 5232 5233 // ##### layer for user customization purposes 5234 newElt[newEltIdx++]='<div class="nGY2GThumbnailCustomLayer"></div>'; 5235 5236 // ##### layer for labels (title + description and their icons) 5237 if( G.O.thumbnailLabel.get('display') == true ) { 5238 // Labels: title and description 5239 newElt[newEltIdx++]= ' <div class="nGY2GThumbnailLabel" '+ G.tn.style.getLabel(item) + '>'; 5240 if( item.kind == 'album' ) { 5241 // album kind 5242 newElt[newEltIdx++]= ' <div class="nGY2GThumbnailTitle nGY2GThumbnailAlbumTitle" ' + G.tn.style.getTitle() + '>' + G.O.icons.thumbnailAlbum + sTitle + '</div>'; 5243 } 5244 else { 5245 // image/media kind 5246 newElt[newEltIdx++]= ' <div class="nGY2GThumbnailTitle nGY2GThumbnailImageTitle" ' + G.tn.style.getTitle() + '>' + G.O.icons.thumbnailImage + sTitle + '</div>'; 5247 } 5248 newElt[newEltIdx++]= ' <div class="nGY2GThumbnailDescription" ' + G.tn.style.getDesc() + '>' + getTumbnailDescription(item) + '</div>'; 5249 newElt[newEltIdx++]= ' </div>'; 5250 } 5251 5252 // ##### layer for tools 5253 // newElt[newEltIdx++] = ThumbnailBuildTools(item, lastOne); 5254 newElt[newEltIdx++] = ThumbnailBuildTools(item); 5255 5256 // close containers 5257 newElt[newEltIdx++]='</div></div>'; 5258 5259 var $newDiv =jQuery(newElt.join('')).appendTo(G.$E.conTn); 5260 5261 item.$elt=$newDiv; 5262 $newDiv.data('index',GOMidx); 5263 item.$getElt('.nGY2GThumbnailImg').data('index',GOMidx); 5264 5265 // Custom init function 5266 var fu=G.O.fnThumbnailInit; 5267 if( fu !== null ) { 5268 typeof fu == 'function' ? fu($newDiv, item, GOMidx) : window[fu]($newDiv, item, GOMidx); 5269 } 5270 5271 if( item.title != 'image gallery by nanogallery2 [build]' ) { 5272 ThumbnailOverInit(GOMidx); 5273 } 5274 5275 return ; 5276 } 5277 5278 5279 // Thumbnail layer for tools (toolbars and counter) 5280 function ThumbnailBuildTools( item ) { 5281 5282 // toolbars 5283 var tb = ThumbnailBuildToolbarOne(item, 'topLeft') + ThumbnailBuildToolbarOne(item, 'topRight') + ThumbnailBuildToolbarOne(item, 'bottomLeft') + ThumbnailBuildToolbarOne(item, 'bottomRight'); 5284 5285 // counter of not displayed images 5286 tb += '<div class="nGY2GThumbnailIconsFullThumbnail"></div>'; 5287 5288 return tb; 5289 } 5290 5291 function ThumbnailBuildToolbarOne( item, position ) { 5292 var toolbar = ''; 5293 var tb = G.tn.toolbar.get(item); 5294 var width = { xs:0, sm:1, me:2, la:3, xl:4 }; 5295 var cnt = 0; 5296 5297 if( tb[position] != '' ) { 5298 var pos='top: 0; right: 0; text-align: right;'; // 'topRight' and default 5299 switch( position ) { 5300 case 'topLeft': 5301 pos = 'top: 0; left: 0; text-align: left;'; 5302 break; 5303 case 'bottomRight': 5304 pos = 'bottom: 0; right: 0; text-align: right;'; 5305 break; 5306 case 'bottomLeft': 5307 pos = 'bottom: 0; left: 0; text-align: left;'; 5308 break; 5309 } 5310 5311 toolbar += ' <ul class="nGY2GThumbnailIcons" style="' + pos + '">'; 5312 5313 var icons = tb[position].split(','); 5314 var nb = icons.length; 5315 for( var i = 0; i < nb; i++ ) { 5316 var icon = icons[i].replace(/^\s*|\s*$/, ''); //trim trailing/leading whitespace 5317 5318 var minWidth = icon.substring(0,2).toLowerCase(); 5319 var tIcon = icon; 5320 var display = true; 5321 if( /xs|sm|me|la|xl/i.test(minWidth) ) { 5322 // check visbility (depending on screen width) 5323 if( width[minWidth] > width[G.GOM.curWidth] ) { 5324 display = false; 5325 } 5326 tIcon = icon.substring(2); 5327 } 5328 5329 if( display ) { 5330 var sp=(i+1<nb ? ' ' :''); 5331 switch( tIcon ) { 5332 case 'COUNTER': 5333 if( item.kind == 'album' ) { 5334 toolbar += ' <li class="nGY2GThumbnailIcon" data-ngy2action="">'; 5335 toolbar += ' <div class="nGY2GThumbnailIconImageCounter"></div>'; 5336 toolbar += ' <div class="nGY2GThumbnailIconText">' + G.O.icons.thumbnailCounter+Math.max((item.getContentLength(false)),item.numberItems) + sp + '</div>'; 5337 toolbar += ' </li>'; 5338 cnt++; 5339 } 5340 break; 5341 case 'COUNTER2': 5342 if( item.kind == 'album' ) { 5343 toolbar += ' <li class="nGY2GThumbnailIcon" data-ngy2action="">'; 5344 toolbar += ' <div class="nGY2GThumbnailIconTextBadge">' + G.O.icons.thumbnailCounter+Math.max((item.getContentLength(false)),item.numberItems) + sp + '</div>'; 5345 toolbar += ' </li>'; 5346 cnt++; 5347 } 5348 break; 5349 case 'SHARE': 5350 toolbar += ' <li class="nGY2GThumbnailIcon" data-ngy2action="' + tIcon + '">'; 5351 toolbar += ' <div>' + G.O.icons.thumbnailShare + '</div>'; 5352 toolbar += ' </li>'; 5353 cnt++; 5354 break; 5355 case 'DOWNLOAD': 5356 toolbar += ' <li class="nGY2GThumbnailIcon" data-ngy2action="' + tIcon + '">'; 5357 toolbar += ' <div>' + G.O.icons.thumbnailDownload + '</div>'; 5358 toolbar += ' </li>'; 5359 cnt++; 5360 break; 5361 case 'INFO': 5362 toolbar += ' <li class="nGY2GThumbnailIcon" data-ngy2action="' + tIcon + '">'; 5363 toolbar += ' <div>' + G.O.icons.thumbnailInfo + '</div>'; 5364 toolbar += ' </li>'; 5365 cnt++; 5366 break; 5367 case 'SHOPPINGCART': 5368 toolbar += ' <li class="nGY2GThumbnailIcon" data-ngy2action="' + tIcon + '">'; 5369 // toolbar += ' <div>' + G.O.icons.thumbnailShoppingcart + '</div>'; 5370 toolbar += ThumbnailBuildToolbarOneCart( item ); 5371 5372 toolbar += ' </li>'; 5373 cnt++; 5374 break; 5375 case 'DISPLAY': 5376 toolbar += ' <li class="nGY2GThumbnailIcon" data-ngy2action="DISPLAY">'; 5377 toolbar += ' <div class="nGY2GThumbnailIconImageShare">' + G.O.icons.thumbnailDisplay + '</div>'; 5378 toolbar += ' </li>'; 5379 cnt++; 5380 break; 5381 case 'CUSTOM1': 5382 case 'CUSTOM2': 5383 case 'CUSTOM3': 5384 case 'CUSTOM4': 5385 case 'CUSTOM5': 5386 case 'CUSTOM6': 5387 case 'CUSTOM7': 5388 case 'CUSTOM8': 5389 case 'CUSTOM9': 5390 case 'CUSTOM10': 5391 var cust = tIcon.replace('CUSTOM', ''); 5392 toolbar += ' <li class="nGY2GThumbnailIcon" data-ngy2action="' + tIcon.toLowerCase() + '">'; 5393 toolbar += ' <div class="nGY2GThumbnailIconImageShare">' + G.O.icons['thumbnailCustomTool' + cust] + '</div>'; 5394 toolbar += ' </li>'; 5395 cnt++; 5396 break; 5397 case 'FEATURED': 5398 if( item.featured === true ) { 5399 toolbar += ' <li class="nGY2GThumbnailIcon" data-ngy2action="">'; 5400 toolbar += ' <div class="nGY2GThumbnailIconImageFeatured">' + G.O.icons.thumbnailFeatured + '</div>'; 5401 toolbar += ' </li>'; 5402 cnt++; 5403 } 5404 break; 5405 case 'SELECT': 5406 if( G.O.thumbnailSelectable == true ) { 5407 toolbar += ' <li class="nGY2GThumbnailIcon" data-ngy2action="TOGGLESELECT">'; 5408 if( item.selected === true ) { 5409 toolbar += ' <div class="nGY2GThumbnailIconImageSelect nGY2ThumbnailSelected">' + G.O.icons.thumbnailSelected + '</div>'; 5410 } 5411 else { 5412 toolbar += ' <div class="nGY2GThumbnailIconImageSelect nGY2ThumbnailUnselected">' + G.O.icons.thumbnailUnselected + '</div>'; 5413 } 5414 toolbar += ' </li>'; 5415 cnt++; 5416 } 5417 break; 5418 } 5419 } 5420 } 5421 toolbar += ' </ul>'; 5422 } 5423 5424 if( cnt > 0 ) { 5425 return toolbar; 5426 } 5427 else { 5428 return ''; 5429 } 5430 } 5431 5432 // CART ICON AND COUNTER 5433 function ThumbnailBuildToolbarOneCart( item ) { 5434 var q = 0; 5435 5436 var id = item.GetID() 5437 for( var i=0; i<G.shoppingCart.length; i++ ) { 5438 if( G.I[G.shoppingCart[i].idx].GetID() == id ) { 5439 q = G.shoppingCart[i].qty; 5440 } 5441 } 5442 if( q == 0 ) { 5443 q = ''; 5444 } 5445 5446 return ' <div>' + G.O.icons.thumbnailShoppingcart + q + '</div>'; 5447 } 5448 function ThumbnailBuildToolbarOneCartUpdate( item ) { 5449 var $e = item.$elt; 5450 5451 if( $e != null ) { 5452 var $q = $e.find('*[data-ngy2action="SHOPPINGCART"]'); 5453 if( $q !== undefined ) { 5454 $q.html( ThumbnailBuildToolbarOneCart( item ) ); 5455 } 5456 } 5457 } 5458 5459 function getThumbnailTitle( item ) { 5460 5461 var sTitle = item.title; 5462 if( G.O.thumbnailLabel.get('display') == true ) { 5463 if( sTitle === undefined || sTitle.length == 0 ) { sTitle = ' '; } 5464 5465 if( G.i18nTranslations.thumbnailImageTitle != '' ) { 5466 sTitle = G.i18nTranslations.thumbnailImageTitle; 5467 } 5468 var ml = G.O.thumbnailLabel.get('titleMaxLength'); 5469 if( ml > 3 && sTitle.length > ml ){ 5470 sTitle = sTitle.substring(0, ml) + '...'; 5471 } 5472 } 5473 5474 return sTitle; 5475 } 5476 5477 function getTumbnailDescription( item ) { 5478 var sDesc = ''; 5479 if( G.O.thumbnailLabel.get('displayDescription') == true ) { 5480 if( item.kind == 'album' ) { 5481 if( G.i18nTranslations.thumbnailImageDescription != '' ) { 5482 sDesc = G.i18nTranslations.thumbnailAlbumDescription; 5483 } 5484 else { 5485 sDesc = item.description; 5486 } 5487 } 5488 else { 5489 if( G.i18nTranslations.thumbnailImageDescription != '' ) { 5490 sDesc = G.i18nTranslations.thumbnailImageDescription; 5491 } 5492 else { 5493 sDesc = item.description; 5494 } 5495 } 5496 var ml = G.O.thumbnailLabel.get('descriptionMaxLength'); 5497 if( ml > 3 && sDesc.length > ml ){ 5498 sDesc = sDesc.substring(0, ml) + '...'; 5499 } 5500 if( sDesc.length == 0 ) { 5501 sDesc = ' '; 5502 } 5503 } 5504 5505 return sDesc; 5506 } 5507 5508 5509 5510 // Retrieve the maximum number of thumbnails that fits in one row 5511 function NbThumbnailsPerRow( areaWidth ) { 5512 var tnW = G.tn.defaultSize.getOuterWidth(); 5513 5514 var nbMaxTn = 0; 5515 if( G.O.thumbnailAlignment == 'justified' ) { 5516 nbMaxTn = Math.floor((areaWidth)/(tnW)); 5517 } 5518 else { 5519 nbMaxTn = Math.floor((areaWidth + G.tn.settings.GetResponsive('gutterWidth'))/(tnW + G.tn.settings.GetResponsive('gutterWidth'))); 5520 } 5521 5522 if( G.O.maxItemsPerLine >0 && nbMaxTn > G.O.maxItemsPerLine ) { 5523 nbMaxTn = G.O.maxItemsPerLine; 5524 } 5525 5526 if( nbMaxTn < 1 ) { nbMaxTn = 1; } 5527 5528 return nbMaxTn 5529 } 5530 5531 // Thumbnail display animation 5532 function ThumbnailAppear( n, cnt ) { 5533 var curTn = G.GOM.items[n]; 5534 var item = G.I[curTn.thumbnailIdx]; 5535 5536 5537 if( G.tn.opt.Get('displayTransition') == 'NONE' ) { 5538 item.$elt.css({ opacity: 1 }); 5539 ThumbnailAppearFinish( item ); 5540 } 5541 else { 5542 if( item.$elt == null ) { return; } 5543 var top = G.GOM.cache.containerOffset.top + ( curTn.top - G.GOM.clipArea.top ); 5544 var vp = G.GOM.cache.viewport; 5545 if( (top + (curTn.top - G.GOM.clipArea.top)) >= (vp.t - 50) && top <= (vp.t + vp.h + 50) ) { 5546 // display animation only if in the current viewport 5547 var delay = cnt * G.tn.opt.Get('displayInterval'); 5548 if( G.tn.opt.Get('displayTransition') == 'CUSTOM' ) { 5549 if( G.GOM.curNavLevel == 'lN' ) { 5550 G.O.fnThumbnailDisplayEffect(item.$elt, item, n, delay); 5551 } 5552 else { 5553 G.O.fnThumbnailL1DisplayEffect(item.$elt, item, n, delay); 5554 } 5555 } 5556 else { 5557 G.GOM.thumbnails2Display.push({itm: item, d: delay}); 5558 // ThumbnailDisplayAnim2(item, delay); 5559 } 5560 return; 5561 } 5562 else { 5563 item.$elt.css({ opacity: 1 }); 5564 ThumbnailAppearFinish(item); 5565 } 5566 } 5567 } 5568 5569 5570 // displays thumbnail stacks at the end of the display animation 5571 function ThumbnailAppearFinish( item ) { 5572 5573 // add stacks 5574 var ns = G.tn.opt.Get('stacks'); 5575 if( ns > 0 ) { 5576 // display stacks 5577 item.$elt.css({ display: 'block'}); 5578 var o = 0.9; 5579 // set stack opacity 5580 for( var i = ns-1; i>=0; i-- ) { 5581 item.$elt.eq(i).css('opacity', o); 5582 o = o - 0.2; 5583 } 5584 5585 } 5586 } 5587 5588 5589 function ThumbnailDisplayAnim2( item, delay ) { 5590 function randomIntFromInterval(min,max) { 5591 return Math.floor(Math.random()*(max-min+1)+min); 5592 } 5593 var oFrom = {}; 5594 var oTo = {}; 5595 5596 switch (G.tn.opt.Get('displayTransition')) { 5597 case 'RANDOMSCALE': { 5598 var scales = [0.95, 1, 1.05, 1.1]; 5599 var zi = [1, 2, 3, 4]; 5600 5601 var r = randomIntFromInterval(0,3); 5602 while( r == G.GOM.lastRandomValue ) { 5603 r = randomIntFromInterval(0,3); 5604 } 5605 G.GOM.lastRandomValue = r; 5606 let f = scales[r]; 5607 // item.$elt.css({ 'z-index': G.GOM.lastZIndex+zi[r], 'box-shadow': '-1px 2px 5px 1px rgba(0, 0, 0, 0.7)' }); 5608 item.$elt.css({ 'z-index': G.GOM.lastZIndex+zi[r], 'box-shadow': '0px 0px 5px 3px rgba(0,0,0,0.74)' }); 5609 5610 oFrom = { scale: 0.5, opacity:0 }; 5611 oTo = { scale: f, opacity:1 }; 5612 break; 5613 } 5614 5615 case 'SCALEUP': { 5616 let f = G.tn.opt.Get('displayTransitionStartVal'); 5617 if( f == 0 ) { f = 0.6; } // default value 5618 oFrom = { scale: f, opacity: 0 }; 5619 oTo = { scale: 1, opacity: 1 }; 5620 break; 5621 } 5622 5623 case 'SCALEDOWN': { 5624 let f = G.tn.opt.Get('displayTransitionStartVal'); 5625 if( f == 0 ) { f=1.3; } // default value 5626 oFrom = { scale: f, opacity: 0 }; 5627 oTo = { scale: 1, opacity: 1 }; 5628 break; 5629 } 5630 case 'SLIDEUP': { 5631 let f = G.tn.opt.Get('displayTransitionStartVal'); 5632 if( f == 0 ) { f=50; } // default value 5633 oFrom = { opacity: 0, translateY: f }; 5634 oTo = { opacity: 1, translateY: 0 }; 5635 break; 5636 } 5637 case 'SLIDEDOWN': { 5638 let f=G.tn.opt.Get('displayTransitionStartVal'); 5639 if( f == 0 ) { f=-50; } // default value 5640 oFrom = { opacity: 0, translateY: f }; 5641 oTo = { opacity: 1, translateY: 0 }; 5642 break; 5643 } 5644 case 'FLIPUP': { 5645 let f=G.tn.opt.Get('displayTransitionStartVal'); 5646 if( f == 0 ) { f=100; } // default value 5647 oFrom = { opacity: 0, translateY: f, rotateX: 45 }; 5648 oTo = { opacity: 1, translateY: 0, rotateX: 0 }; 5649 break; 5650 } 5651 case 'FLIPDOWN': { 5652 let f=G.tn.opt.Get('displayTransitionStartVal'); 5653 if( f == 0 ) { f=-100; } // default value 5654 oFrom = { opacity: 0, translateY: f, rotateX: -45 }; 5655 oTo = { opacity: 1, translateY: 0, rotateX: 0 }; 5656 break; 5657 } 5658 case 'SLIDEUP2': { 5659 let f = G.tn.opt.Get('displayTransitionStartVal'); 5660 if( f == 0 ) { f=100; } // default value 5661 oFrom = { opacity: 0, translateY: f, rotateY: 40 }; 5662 oTo = { opacity: 1, translateY: 0, rotateY: 0 }; 5663 break; 5664 } 5665 case 'IMAGESLIDEUP': { 5666 // let f = G.tn.opt.Get('displayTransitionStartVal'); 5667 // if( f == 0 ) { f=100; } // default value 5668 oFrom = { opacity: 0, top: '100%' }; 5669 oTo = { opacity: 1, top: '0%' }; 5670 break; 5671 } 5672 case 'SLIDEDOWN2': { 5673 let f=G.tn.opt.Get('displayTransitionStartVal'); 5674 if( f == 0 ) { f=-100; } // default value 5675 oFrom = { opacity: 0, translateY: f, rotateY: 40 }; 5676 oTo = { opacity: 1, translateY: 0, rotateY: 0 }; 5677 break; 5678 } 5679 case 'SLIDERIGHT': { 5680 let f=G.tn.opt.Get('displayTransitionStartVal'); 5681 if( f == 0 ) { f=-150; } // default value 5682 oFrom = { opacity: 0, translateX: f }; 5683 oTo = { opacity: 1, translateX: 0 }; 5684 break; 5685 } 5686 case 'SLIDELEFT': { 5687 let f=G.tn.opt.Get('displayTransitionStartVal'); 5688 if( f == 0 ) { f=150; } // default value 5689 oFrom = { opacity: 0, translateX: f }; 5690 oTo = { opacity: 1, translateX: 0 }; 5691 break; 5692 } 5693 case 'FADEIN': 5694 oFrom = { opacity: 0 }; 5695 oTo = { opacity: 1 }; 5696 break; 5697 5698 5699 } 5700 5701 var tweenable = new NGTweenable(); 5702 tweenable.tween({ 5703 from: oFrom, 5704 to: oTo, 5705 attachment: { $e:item.$elt, item: item, tw: tweenable }, 5706 delay: delay, 5707 duration: G.tn.opt.Get('displayTransitionDuration'), 5708 easing: G.tn.opt.Get('displayTransitionEasing'), 5709 step: function (state, att) { 5710 window.requestAnimationFrame( function() { 5711 if( att.item.$elt === null ) { // the thumbnail may have been destroyed since the start of the animation 5712 att.tw.stop(false); 5713 return; 5714 } 5715 switch (G.tn.opt.Get('displayTransition')) { 5716 case 'RANDOMSCALE': 5717 att.$e.css( G.CSStransformName , 'scale(' + state.scale + ')').css('opacity', state.opacity); 5718 break; 5719 case 'SCALEUP': 5720 att.$e.css( G.CSStransformName , 'scale('+state.scale+')').css('opacity',state.opacity); 5721 break; 5722 case 'SCALEDOWN': 5723 att.item.$elt.last().css('opacity', state.opacity); 5724 att.item.CSSTransformSet('.nGY2GThumbnail', 'scale', state.scale); 5725 att.item.CSSTransformApply('.nGY2GThumbnail'); 5726 break; 5727 case 'SLIDEUP': 5728 att.item.$elt.css('opacity', state.opacity); 5729 att.item.CSSTransformSet('.nGY2GThumbnail', 'translate', '0px, '+state.translateY + 'px'); 5730 att.item.CSSTransformApply('.nGY2GThumbnail'); 5731 break; 5732 case 'SLIDEDOWN': 5733 att.item.$elt.css('opacity', state.opacity); 5734 att.item.CSSTransformSet('.nGY2GThumbnail', 'translate', '0px,'+state.translateY+'px'); 5735 att.item.CSSTransformApply('.nGY2GThumbnail'); 5736 break; 5737 case 'FLIPUP': 5738 att.item.CSSTransformSet('.nGY2GThumbnail', 'translate', '0px,'+state.translateY+'px'); 5739 att.item.CSSTransformSet('.nGY2GThumbnail', 'rotateX', state.rotateX+'deg'); 5740 att.item.$elt.css('opacity', state.opacity); 5741 att.item.CSSTransformApply('.nGY2GThumbnail'); 5742 break; 5743 case 'FLIPDOWN': 5744 att.item.$elt.css('opacity', state.opacity); 5745 att.item.CSSTransformSet('.nGY2GThumbnail', 'translate', '0px,' + state.translateY + 'px'); 5746 att.item.CSSTransformSet('.nGY2GThumbnail', 'rotateX', state.rotateX + 'deg'); 5747 att.item.CSSTransformApply('.nGY2GThumbnail'); 5748 break; 5749 case 'SLIDEUP2': 5750 att.item.$elt.css('opacity', state.opacity); 5751 att.item.CSSTransformSet('.nGY2GThumbnail', 'translate', '0px,' + state.translateY + 'px'); 5752 att.item.CSSTransformSet('.nGY2GThumbnail', 'rotateY', state.rotateY + 'deg'); 5753 att.item.CSSTransformApply('.nGY2GThumbnail'); 5754 break; 5755 case 'IMAGESLIDEUP': 5756 att.item.$elt.css('opacity', state.opacity); 5757 att.item.$Elts['.nGY2GThumbnailImage'].css('top', state.top); 5758 break; 5759 case 'SLIDEDOWN2': 5760 att.item.$elt.css('opacity', state.opacity); 5761 att.item.CSSTransformSet('.nGY2GThumbnail', 'translate', '0px, ' + state.translateY + 'px'); 5762 att.item.CSSTransformSet('.nGY2GThumbnail', 'rotateY', state.rotateY + 'deg'); 5763 att.item.CSSTransformApply('.nGY2GThumbnail'); 5764 break; 5765 case 'SLIDERIGHT': 5766 att.item.$elt.css('opacity', state.opacity); 5767 att.item.CSSTransformSet('.nGY2GThumbnail', 'translate', state.translateX + 'px, 0px'); 5768 att.item.CSSTransformApply('.nGY2GThumbnail'); 5769 break; 5770 case 'SLIDELEFT': 5771 att.item.CSSTransformSet('.nGY2GThumbnail', 'translate', state.translateX + 'px, 0px'); 5772 att.item.$elt.css('opacity', state.opacity); 5773 att.item.CSSTransformApply('.nGY2GThumbnail'); 5774 break; 5775 case 'FADEIN': 5776 att.$e.css(state); 5777 break; 5778 } 5779 }); 5780 // att.$e.css( G.CSStransformName , 'scale('+state.scale+')').css('opacity',state.opacity); 5781 }, 5782 finish: function (state, att) { 5783 window.requestAnimationFrame( function() { 5784 if( att.item.$elt === null ) { return; } 5785 5786 switch (G.tn.opt.Get('displayTransition')) { 5787 case 'RANDOMSCALE': 5788 att.$e.css( G.CSStransformName , 'scale('+state.scale+')').css('opacity', ''); 5789 break; 5790 case 'SCALEUP': 5791 att.$e.css( G.CSStransformName , '').css('opacity', ''); 5792 break; 5793 case 'SCALEDOWN': 5794 att.item.$elt.last().css('opacity', ''); 5795 att.item.CSSTransformSet('.nGY2GThumbnail', 'scale', state.scale); 5796 att.item.CSSTransformApply('.nGY2GThumbnail'); 5797 break; 5798 case 'IMAGESLIDEUP': 5799 att.item.$elt.css('opacity', ''); 5800 att.item.$Elts['.nGY2GThumbnailImage'].css('top', 0); 5801 break; 5802 case 'SLIDEDOWN2': 5803 att.item.$elt.css('opacity', ''); 5804 att.item.CSSTransformApply('.nGY2GThumbnail'); 5805 break; 5806 default : 5807 // case 'SLIDEUP': 5808 // case 'SLIDEDOWN': 5809 // case 'FLIPUP': 5810 // case 'FLIPDOWN': 5811 // case 'SLIDEUP2': 5812 // case 'SLIDERIGHT': 5813 // case 'SLIDELEFT': 5814 // case 'FADEIN': 5815 att.item.$elt.css('opacity', ''); 5816 } 5817 ThumbnailAppearFinish(att.item); 5818 }); 5819 5820 } 5821 }); 5822 5823 } 5824 5825 // batch display thumbnails with animation 5826 function ThumbnailDisplayAnimBatch() { 5827 5828 G.GOM.thumbnails2Display.forEach( function(one) { 5829 ThumbnailDisplayAnim2(one.itm, one.d); 5830 }); 5831 G.GOM.thumbnails2Display=[]; 5832 } 5833 5834 5835 5836 // ###################################### 5837 // Gallery display animation 5838 function GalleryAppear() { 5839 5840 var d=G.galleryDisplayTransitionDuration.Get(); 5841 switch( G.galleryDisplayTransition.Get() ){ 5842 case 'ROTATEX': 5843 G.$E.base.css({ perspective: '1000px', 'perspective-origin': '50% 0%' }); 5844 new NGTweenable().tween({ 5845 from: { r: 50 }, 5846 to: { r: 0 }, 5847 attachment: { orgIdx: G.GOM.albumIdx }, 5848 duration: d, 5849 easing: 'easeOutCirc', 5850 step: function (state, att) { 5851 if( att.orgIdx == G.GOM.albumIdx ) { 5852 // window.ng_draf( function() { 5853 G.$E.conTnParent.css( G.CSStransformName , 'rotateX(' + state.r + 'deg)'); 5854 // }); 5855 } 5856 } 5857 }); 5858 break; 5859 case 'SLIDEUP': 5860 G.$E.conTnParent.css({ opacity: 0 }); 5861 new NGTweenable().tween({ 5862 from: { y: 200, o: 0 }, 5863 to: { y: 0, o: 1 }, 5864 attachment: { orgIdx: G.GOM.albumIdx }, 5865 duration: d, 5866 easing: 'easeOutCirc', 5867 step: function (state, att) { 5868 if( att.orgIdx == G.GOM.albumIdx ) { 5869 // window.ng_draf( function() { 5870 G.$E.conTnParent.css( G.CSStransformName , 'translate( 0px, '+state.y + 'px)').css('opacity', state.o); 5871 // }); 5872 } 5873 } 5874 }); 5875 break; 5876 case 'NONE': 5877 default: 5878 break; 5879 } 5880 5881 5882 } 5883 5884 // ###################################### 5885 // ##### THUMBNAIL HOVER MANAGEMENT ##### 5886 // ###################################### 5887 5888 function ThumbnailOverInit( GOMidx ) { 5889 // Over init in 2 step: 5890 // 1) init with thumbnailBuildInit2 parameter 5891 // 2) init with the hover effect parameter 5892 5893 5894 var curTn = G.GOM.items[GOMidx]; 5895 var item = G.I[curTn.thumbnailIdx]; 5896 5897 if( item.$elt == null ) { return; } // zombie 5898 5899 var fu = G.O.fnThumbnailHoverInit; 5900 if( fu !== null ) { 5901 typeof fu == 'function' ? fu($e, item, GOMidx) : window[fu]($e, item, GOMidx); 5902 } 5903 5904 // build initialization 5905 var inits = G.tn.buildInit.get(); 5906 for( var j = 0; j < inits.length; j++) { 5907 switch( inits[j].property ) { 5908 // CSS Transform 5909 case 'scale': 5910 case 'rotateX': 5911 case 'rotateY': 5912 case 'rotateZ': 5913 case 'translateX': 5914 case 'translateY': 5915 case 'translateZ': 5916 item.CSSTransformSet(inits[j].element, inits[j].property, inits[j].value); 5917 item.CSSTransformApply(inits[j].element); 5918 break; 5919 // CSS filter 5920 case 'blur': 5921 case 'brightness': 5922 case 'grayscale': 5923 case 'sepia': 5924 case 'contrast': 5925 case 'opacity': 5926 case 'saturate': 5927 item.CSSFilterSet(inits[j].element, inits[j].property, inits[j].value); 5928 item.CSSFilterApply(inits[j].element); 5929 break; 5930 default: 5931 var $t=item.$getElt(inits[j].element); 5932 $t.css( inits[j].property, inits[j].value ); 5933 break; 5934 } 5935 } 5936 5937 // hover 5938 var effects = G.tn.hoverEffects.get(); 5939 for( var j = 0; j < effects.length; j++) { 5940 if( effects[j].firstKeyframe === true ) { 5941 switch( effects[j].type ) { 5942 case 'scale': 5943 case 'rotateX': 5944 case 'rotateY': 5945 case 'rotateZ': 5946 case 'translateX': 5947 case 'translateY': 5948 case 'translateZ': 5949 item.CSSTransformSet(effects[j].element, effects[j].type, effects[j].from); 5950 item.CSSTransformApply(effects[j].element); 5951 break; 5952 case 'blur': 5953 case 'brightness': 5954 case 'grayscale': 5955 case 'sepia': 5956 case 'contrast': 5957 case 'opacity': 5958 case 'saturate': 5959 item.CSSFilterSet(effects[j].element, effects[j].type, effects[j].from); 5960 item.CSSFilterApply(effects[j].element); 5961 break; 5962 default: 5963 var $t = item.$getElt(effects[j].element); 5964 $t.css( effects[j].type, effects[j].from ); 5965 break; 5966 5967 } 5968 } 5969 } 5970 item.hoverInitDone=true; 5971 } 5972 5973 function ThumbnailHoverReInitAll() { 5974 if( G.GOM.albumIdx == -1 ) { return; }; 5975 var l = G.GOM.items.length; 5976 for( var i = 0; i < l ; i++ ) { 5977 ThumbnailOverInit(i); 5978 // G.GOM.items[i].hovered=false; 5979 G.I[G.GOM.items[i].thumbnailIdx].hovered = false; 5980 } 5981 } 5982 5983 5984 function ThumbnailHover( GOMidx ) { 5985 if( G.GOM.albumIdx == -1 || !G.galleryResizeEventEnabled ) { return; }; 5986 if( G.GOM.slider.hostIdx == GOMidx ) { 5987 // slider hosted on thumbnail -> no hover effect 5988 return; 5989 } 5990 var curTn = G.GOM.items[GOMidx]; 5991 var item = G.I[curTn.thumbnailIdx]; 5992 if( item.kind == 'albumUp' || item.$elt == null ) { return; } 5993 5994 item.hovered = true; 5995 5996 var fu = G.O.fnThumbnailHover; 5997 if( fu !== null ) { 5998 typeof fu == 'function' ? fu(item.$elt, item, GOMidx) : window[fu](item.$elt, item, GOMidx); 5999 } 6000 var effects = G.tn.hoverEffects.get(); 6001 6002 try { 6003 for( var j = 0; j < effects.length; j++) { 6004 if( effects[j].hoverin === true ) { 6005 //item.animate( effects[j], j*10, true ); 6006 item.animate( effects[j], 0, true ); 6007 } 6008 } 6009 // effects on whole layout 6010 // GalleryResize( GOMidx ); 6011 } 6012 catch (e) { 6013 NanoAlert(G, 'error on hover: ' + e.message ); 6014 } 6015 6016 } 6017 6018 function ThumbnailHoverOutAll() { 6019 if( G.GOM.albumIdx == -1 ) { return; }; 6020 var l = G.GOM.items.length; 6021 for( var i = 0; i < l ; i++ ) { 6022 if( G.GOM.items[i].inDisplayArea ) { 6023 ThumbnailHoverOut(i); 6024 } 6025 else { 6026 G.I[G.GOM.items[i].thumbnailIdx].hovered = false; 6027 } 6028 } 6029 } 6030 6031 6032 function ThumbnailHoverOut( GOMidx ) { 6033 if( G.GOM.albumIdx == -1 || !G.galleryResizeEventEnabled ) { return; } 6034 6035 if( G.GOM.slider.hostIdx == GOMidx ) { 6036 // slider on thumbnail -> no hover effect 6037 return; 6038 } 6039 6040 var curTn = G.GOM.items[GOMidx]; 6041 var item = G.I[curTn.thumbnailIdx]; 6042 if( item.kind == 'albumUp' || !item.hovered ) { return; } 6043 item.hovered = false; 6044 if( item.$elt == null ) { return; } 6045 6046 var fu = G.O.fnThumbnailHoverOut; 6047 if( fu !== null ) { 6048 typeof fu == 'function' ? fu(item.$elt, item, GOMidx) : window[fu](item.$elt, item, GOMidx); 6049 } 6050 6051 var effects = G.tn.hoverEffects.get(); 6052 try { 6053 for( var j = 0; j < effects.length; j++) { 6054 if( effects[j].hoverout === true ) { 6055 // item.animate( effects[j], j*10, false ); 6056 item.animate( effects[j], 0, false ); 6057 } 6058 } 6059 // effects on whole layout 6060 // GalleryResize( ); 6061 } 6062 catch (e) { 6063 NanoAlert(G, 'error on hoverOut: ' + e.message ); 6064 } 6065 6066 } 6067 6068 6069 /** @function DisplayPhoto */ 6070 function DisplayPhoto( imageID, albumID ) { 6071 6072 if( G.O.debugMode ) { console.log('#DisplayPhoto : '+ albumID +'-'+ imageID); } 6073 var albumIdx = NGY2Item.GetIdx(G, albumID); 6074 if( albumIdx == 0 ) { 6075 G.GOM.curNavLevel = 'l1'; 6076 } 6077 else { 6078 G.GOM.curNavLevel = 'lN'; 6079 } 6080 6081 if( albumIdx == -1 ) { 6082 // get content of album on root level 6083 if( G.O.kind != '' ) { 6084 // do not add album if Markup or Javascript data 6085 NGY2Item.New( G, '', '', albumID, '0', 'album' ); // create empty album 6086 // albumIdx = G.I.length - 1; 6087 } 6088 } 6089 6090 var ngy2ItemIdx = NGY2Item.GetIdx(G, imageID); 6091 if( ngy2ItemIdx == -1 ) { 6092 // get content of the album 6093 AlbumGetContent( albumID, DisplayPhoto, imageID, albumID ); 6094 return; 6095 } 6096 6097 if( G.O.debugMode ) { console.log('#DisplayPhoto : '+ ngy2ItemIdx); } 6098 6099 DisplayPhotoIdx(ngy2ItemIdx); 6100 6101 } 6102 6103 // function AlbumGetContent( albumIdx, fnToCall ) { 6104 function AlbumGetContent( albumID, fnToCall, fnParam1, fnParam2 ) { 6105 // var url=''; 6106 // var kind='image'; 6107 // var albumIdx=NGY2Item.GetIdx(G, albumID); 6108 // var photoIdx=NGY2Item.GetIdx(G, photoID); 6109 6110 switch( G.O.kind ) { 6111 // MARKUP / API 6112 case '': 6113 AlbumGetMarkupOrApi(fnToCall, fnParam1, fnParam2); 6114 break; 6115 // JSON, Flickr, Picasa, ... 6116 default: 6117 jQuery.nanogallery2['data_'+G.O.kind](G, 'AlbumGetContent', albumID, fnToCall, fnParam1, fnParam2 ); 6118 } 6119 6120 } 6121 6122 var mediaList = { 6123 youtube : { 6124 getID: function( url ) { 6125 // https://stackoverflow.com/questions/10591547/how-to-get-youtube-video-id-from-url 6126 var s = url.match( /(?:https?:\/{2})?(?:w{3}\.)?youtu(?:be)?\.(?:com|be)(?:\/watch\?v=|\/)([^\s&]+)/ ); 6127 return s != null ? s[1] : null; 6128 }, 6129 thumbUrl: function( id ) { 6130 return 'https://img.youtube.com/vi/' + id + '/hqdefault.jpg'; 6131 }, 6132 url: function( id ) { 6133 return 'https://www.youtube.com/embed/' + id; 6134 }, 6135 markup: function( id ) { 6136 // return '<iframe class="nGY2ViewerMedia" src="https://www.youtube.com/embed/' + id + '?rel=0" frameborder="0" gesture="media" allowfullscreen></iframe>'; 6137 return '<iframe class="nGY2ViewerMedia" src="https://www.youtube.com/embed/' + id + '?rel=0" frameborder="0" allow="autoplay" allowfullscreen></iframe>'; 6138 }, 6139 kind: 'iframe' 6140 }, 6141 vimeo : { 6142 getID: function( url ) { 6143 // https://stackoverflow.com/questions/2916544/parsing-a-vimeo-id-using-javascript 6144 // var s = url.match( /^.*(vimeo\.com\/)((channels\/[A-z]+\/)|(groups\/[A-z]+\/videos\/))?([0-9]+)/ ); 6145 var s = url.match( /(http|https)?:\/\/(www\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/([^\/]*)\/videos\/|)(\d+)(?:|\/\?)/ ); 6146 return s != null ? s[4] : null; 6147 }, 6148 url: function( id ) { 6149 return 'https://player.vimeo.com/video/' + id; 6150 }, 6151 markup: function( id ) { 6152 // return '<iframe class="nGY2ViewerMedia" src="https://player.vimeo.com/video/' + id + '?rel=0" frameborder="0" gesture="media" allowfullscreen></iframe>'; 6153 // return '<iframe class="nGY2ViewerMedia" src="https://player.vimeo.com/video/' + id + '?rel=0" frameborder="0" allow="autoplay; fullscreen" allowfullscreen></iframe>'; 6154 return '<iframe class="nGY2ViewerMedia" src="https://player.vimeo.com/video/' + id + '" frameborder="0" allow="autoplay; fullscreen" allowfullscreen></iframe>'; 6155 }, 6156 kind: 'iframe' 6157 }, 6158 dailymotion : { 6159 getID: function( url ) { 6160 // https://stackoverflow.com/questions/12387389/how-to-parse-dailymotion-video-url-in-javascript 6161 var m = url.match(/^.+dailymotion.com\/(video|hub)\/([^_]+)[^#]*(#video=([^_&]+))?/); 6162 if (m !== null) { 6163 if(m[4] !== undefined) { 6164 return m[4]; 6165 } 6166 return m[2]; 6167 } 6168 return null; 6169 }, 6170 thumbUrl: function( id ) { 6171 return 'https://www.dailymotion.com/thumbnail/video/' + id; 6172 }, 6173 url: function( id ) { 6174 return 'https://www.dailymotion.com/embed/video/' + id; 6175 }, 6176 markup: function( id ) { 6177 // return '<iframe class="nGY2ViewerMedia" src="https://www.dailymotion.com/embed/video/' + id + '?rel=0" frameborder="0" gesture="media" allowfullscreen></iframe>'; 6178 return '<iframe class="nGY2ViewerMedia" src="https://www.dailymotion.com/embed/video/' + id + '?rel=0" frameborder="0" allow="autoplay" allowfullscreen></iframe>'; 6179 }, 6180 kind: 'iframe' 6181 }, 6182 selfhosted : { 6183 // SELF-HOSTED VIDEOS 6184 getID: function( url ) { 6185 // In order to leave things as is, I used ID to identify the extension 6186 // https://stackoverflow.com/questions/6997262/how-to-pull-url-file-extension-out-of-url-string-using-javascript 6187 // Make sure the method used for verifying the extension matches the kind of url your selfhosted video has 6188 var extension = url.split('.').pop().toLowerCase(); 6189 6190 // supported extensions 6191 var s = ( extension === 'mp4' || extension === 'webm' || extension === 'ogv' || extension === '3gp' ) ? extension : null ; 6192 return s; 6193 }, 6194 markup: function( url ) { 6195 // return '<video controls class="nGY2ViewerMedia"><source src="${id.src}" type="video/${id.type}" preload="auto">Your browser does not support the video tag (HTML 5).</video>'; 6196 var extension = url.split('.').pop(); 6197 return '<video controls class="nGY2ViewerMedia"><source src="'+ url +'" type="video/'+ extension +'" preload="auto">Your browser does not support the video tag (HTML 5).</video>'; 6198 }, 6199 kind: 'video', 6200 selfhosted : true 6201 } 6202 }; 6203 6204 function AlbumGetMarkupOrApi ( fnToCall, fnParam1, fnParam2 ) { 6205 6206 if( G.markupOrApiProcessed === true ) { 6207 // already processed (maybe location hash to unknow reference) -> display root album 6208 DisplayAlbum('-1', 0); 6209 return; 6210 } 6211 6212 if( G.O.items !== undefined && G.O.items !== null ) { 6213 // data defined as an object in an option parameter 6214 GetContentApiObject(); 6215 } 6216 else { 6217 if( G.O.$markup.length > 0 ) { 6218 // data defined as markup (href elements) 6219 GetContentMarkup( G.O.$markup ); 6220 G.O.$markup=[] ; 6221 } 6222 else { 6223 NanoConsoleLog(G, 'error: no media to process.'); 6224 return; 6225 } 6226 } 6227 6228 G.markupOrApiProcessed = true; 6229 if( fnToCall !== null && fnToCall !== undefined) { 6230 fnToCall( fnParam1, fnParam2, null ); 6231 } 6232 } 6233 6234 function StartsWithProtocol ( path ) { 6235 if( path == undefined ) { return false; } 6236 // if( path == null ) { return false; } 6237 6238 var pattern = /^((http|https|ftp|ftps|file):\/\/)/; 6239 if( !pattern.test(path) ) { 6240 // not a full URL 6241 return false; 6242 } 6243 return true; 6244 } 6245 6246 function GetContentApiObject() { 6247 var foundAlbumID=false; 6248 var nbTitles = 0; 6249 var AlbumPostProcess = NGY2Tools.AlbumPostProcess.bind(G); 6250 6251 G.I[0].contentIsLoaded = true; 6252 6253 jQuery.each(G.O.items, function(i,item){ 6254 6255 var title = ''; 6256 title = GetI18nItem(item, 'title'); 6257 if( title === undefined ) { title=''; } 6258 6259 var src=''; 6260 if( item['src'+RetrieveCurWidth().toUpperCase()] !== undefined ) { 6261 src = item['src'+RetrieveCurWidth().toUpperCase()]; 6262 } 6263 else { 6264 src = item.src; 6265 } 6266 if( !StartsWithProtocol(src) ) { 6267 src = G.O.itemsBaseURL + src; 6268 } 6269 6270 var thumbsrc = ''; 6271 if( item.srct !== undefined && item.srct.length > 0 ) { 6272 thumbsrc = item.srct; 6273 if( !StartsWithProtocol(thumbsrc) ) { 6274 thumbsrc = G.O.itemsBaseURL + thumbsrc; 6275 } 6276 } 6277 else { 6278 thumbsrc = src; 6279 } 6280 6281 if( G.O.thumbnailLabel.get('title') != '' ) { 6282 title = GetImageTitle(src); 6283 } 6284 6285 var description=''; //' '; 6286 description=GetI18nItem(item,'description'); 6287 if( description === undefined ) { description=''; } 6288 //if( toType(item.description) == 'string' ) { 6289 // description=item.description; 6290 //} 6291 6292 var tags = GetI18nItem(item, 'tags'); 6293 if( tags === undefined ) { tags=''; } 6294 6295 var albumID = 0; 6296 if( item.albumID !== undefined ) { 6297 albumID=item.albumID; 6298 foundAlbumID = true; 6299 } 6300 var ID = null; 6301 if( item.ID !== undefined ) { 6302 ID = item.ID; 6303 } 6304 var kind = 'image'; 6305 if( item.kind !== undefined && item.kind.length > 0 ) { 6306 kind = item.kind; 6307 } 6308 6309 var newItem=NGY2Item.New( G, title, description, ID, albumID, kind, tags ); 6310 if( title != '' ) { 6311 nbTitles++; 6312 } 6313 6314 // media source url - img is the default media kind 6315 newItem.setMediaURL( src, 'img'); 6316 6317 // manage media kinds other than IMG 6318 jQuery.each(mediaList, function ( n, media ) { 6319 var id = media.getID(src); 6320 if( id != null ) { 6321 if( thumbsrc == src && typeof media.thumbUrl == 'function' ) { 6322 thumbsrc = media.thumbUrl(id); 6323 } 6324 if( typeof media.url == 'function' ) { src = media.url(id); } 6325 newItem.mediaKind = media.kind; 6326 newItem.mediaMarkup = ( media.selfhosted ) ? media.markup( src ) : media.markup(id); 6327 return false; 6328 } 6329 }); 6330 6331 // image size 6332 if( item.imageWidth !== undefined ) { newItem.imageWidth = item.width; } 6333 if( item.imageHeight !== undefined ) { newItem.imageHeight = item.height; } 6334 6335 // THUMBNAILS 6336 6337 // thumbnail image size 6338 var tw = item.imgtWidth !== undefined ? item.imgtWidth : 0; 6339 var th = item.imgtHeight !== undefined ? item.imgtHeight : 0; 6340 6341 // default thumbnail URL and size 6342 newItem.thumbs = { 6343 url: { l1 : { xs: thumbsrc, sm: thumbsrc, me: thumbsrc, la: thumbsrc, xl: thumbsrc }, lN : { xs: thumbsrc, sm: thumbsrc, me: thumbsrc, la: thumbsrc, xl: thumbsrc } }, 6344 width: { l1 : { xs: tw, sm: tw, me: tw, la: tw, xl: tw }, lN : { xs: tw, sm: tw, me: tw, la: tw, xl: tw } }, 6345 height: { l1 : { xs: th, sm: th, me: th, la: th, xl: th }, lN : { xs: th, sm: th, me: th, la: th, xl: th } } 6346 }; 6347 6348 // default media type -> IMG 6349 if( newItem.mediaKind == 'img' ) { 6350 6351 // responsive thumbnails URL and size 6352 var lst=['xs', 'sm', 'me', 'la', 'xl']; 6353 for( var i=0; i< lst.length; i++ ) { 6354 // url 6355 var turl = item['srct' + lst[i].toUpperCase()]; 6356 if( turl !== undefined ) { 6357 if( !StartsWithProtocol(turl) ) { 6358 turl = G.O.itemsBaseURL + turl; 6359 } 6360 newItem.url.l1[lst[i]] = turl; 6361 newItem.url.lN[lst[i]] = turl; 6362 } 6363 // width 6364 var tw = item['imgt' + lst[i].toUpperCase() + 'Width']; 6365 if( tw != undefined ) { 6366 newItem.width.l1[lst[i]] = parseInt(tw); 6367 newItem.width.lN[lst[i]] = parseInt(tw); 6368 } 6369 // height 6370 var th = item['imgt' + lst[i].toUpperCase() + 'Height']; 6371 if( th != undefined ) { 6372 newItem.height.l1[lst[i]] = parseInt(th); 6373 newItem.height.lN[lst[i]] = parseInt(th); 6374 } 6375 } 6376 } 6377 6378 // dominant colors (needs to be a base64 gif) 6379 if( item.imageDominantColors !== undefined ) { 6380 newItem.imageDominantColors = item.imageDominantColors; 6381 } 6382 // dominant color (rgb hex) 6383 if( item.imageDominantColor !== undefined ) { 6384 newItem.imageDominantColor = item.imageDominantColor; 6385 } 6386 6387 // dest url 6388 if( item.destURL !== undefined && item.destURL.length>0 ) { 6389 newItem.destinationURL = item.destURL; 6390 } 6391 6392 // download image url 6393 if( item.downloadURL !== undefined && item.downloadURL.length>0 ) { 6394 newItem.downloadURL = item.downloadURL; 6395 } 6396 6397 // EXIF DATA 6398 // Exif - model 6399 if( item.exifModel !== undefined ) { newItem.exif.model = item.exifModel; } 6400 // Exif - flash 6401 if( item.exifFlash !== undefined ) { newItem.exif.flash = item.exifFlash; } 6402 // Exif - focallength 6403 if( item.exifFocalLength !== undefined ) { newItem.exif.focallength = item.exifFocalLength; } 6404 // Exif - fstop 6405 if( item.exifFStop !== undefined ) { newItem.exif.fstop = item.exifFStop; } 6406 // Exif - exposure 6407 if( item.exifExposure !== undefined ) { newItem.exif.exposure = item.exifExposure; } 6408 // Exif - time 6409 if( item.exifIso !== undefined ) { newItem.exif.iso = item.exifIso; } 6410 // Exif - iso 6411 if( item.exifTime !== undefined ) { newItem.exif.time = item.exifTime; } 6412 // Exif - location 6413 if( item.exifLocation !== undefined ) { newItem.exif.location = item.exifLocation; } 6414 6415 6416 // custom data 6417 if( item.customData !== null ) { 6418 newItem.customData = cloneJSObject( item.customData ); 6419 } 6420 6421 newItem.contentIsLoaded = true; 6422 6423 var fu = G.O.fnProcessData; 6424 if( fu !== null ) { 6425 typeof fu == 'function' ? fu(newItem, 'api', item) : window[fu](newItem, 'api', item); 6426 } 6427 6428 AlbumPostProcess(albumID); 6429 }); 6430 6431 // if( foundAlbumID ) { G.O.displayBreadcrumb=true; } 6432 if( nbTitles == 0 ) { G.O.thumbnailLabel.display=false; } 6433 6434 } 6435 6436 6437 // Returns the text of the DOM element (without children) 6438 // Because jQuery().text() returns the text of all children 6439 function ElementGetText( element ) { 6440 6441 var text = ''; 6442 if( element.childNodes[0] !== undefined ) { 6443 if( element.childNodes[0].nodeValue !== null && element.childNodes[0].nodeValue !== undefined ) { 6444 text = element.childNodes[0].nodeValue.trim(); 6445 } 6446 } 6447 return text; 6448 } 6449 6450 // Extract items from the jQuery elements 6451 function GetContentMarkup( $elements, group ) { 6452 var foundAlbumID = false; 6453 var nbTitles = 0; 6454 var AlbumPostProcess = NGY2Tools.AlbumPostProcess.bind(G); 6455 var GetImageTitleFromURL = NGY2Tools.GetImageTitleFromURL.bind(G); 6456 6457 G.I[0].contentIsLoaded = true; 6458 6459 jQuery.each($elements, function(i, item){ 6460 6461 // compare to group defined on the element that has been clicked (lightbox standalone) 6462 if( item.dataset.nanogallery2Lgroup != group ) { return; } 6463 6464 // ignore element <SCRIPT> 6465 if( item.nodeName == 'SCRIPT' ) { return; } 6466 6467 // create dictionnary with all data attribute name in lowercase (to be case unsensitive) 6468 var data = { 6469 // all possible data attributes with some default values 6470 'data-ngdesc': '', // item description 6471 'data-ngid': null, // ID 6472 'data-ngkind': 'image', // kind (image, album, albumup) 6473 'data-ngtags': null, // tags 6474 'data-ngdest': '', // destination URL 6475 'data-ngthumbimgwidth': 0, // thumbnail width 6476 'data-ngthumbimgheight': 0, // thumbnail height 6477 'data-ngimagewidth': 0, // image width 6478 'data-ngimageheight': 0, // image height 6479 'data-ngimagedominantcolors': null, // image dominant colors 6480 'data-ngimagedominantcolor': null, // image dominant colors 6481 'data-ngexifmodel': '', // EXIF data 6482 'data-ngexifflash': '', 6483 'data-ngexiffocallength': '', 6484 'data-ngexiffstop': '', 6485 'data-ngexifexposure': '', 6486 'data-ngexifiso': '', 6487 'data-ngexiftime': '', 6488 'data-ngexiflocation': '', 6489 'data-ngsrc': '', 6490 'alt': '' 6491 }; 6492 6493 // Extract data attributes from main item 6494 [].forEach.call( item.attributes, function(attr) { 6495 data[attr.name.toLowerCase()] = attr.value.trim(); 6496 }); 6497 6498 var title = ElementGetText(item); 6499 if( title == '' && data.alt != '') { 6500 // no title -> check ALT attribute of main element 6501 title = data['alt']; 6502 } 6503 6504 // Complete with data attributes from all children 6505 jQuery.each($(item).children(), function(i, sub_item){ 6506 6507 // title may be set on a child element 6508 if( title == '' ) { 6509 title = ElementGetText(sub_item); 6510 } 6511 6512 [].forEach.call( sub_item.attributes, function(attr) { 6513 data[attr.name.toLowerCase()] = attr.value.trim(); 6514 }); 6515 6516 if( title == '' && data.alt != '') { 6517 // no title -> check ALT attribute of sub element 6518 title = data['alt']; 6519 } 6520 6521 }); 6522 6523 // BIG IMAGE URL 6524 // responsive image URL 6525 var src = '', 6526 st = RetrieveCurWidth().toUpperCase(); 6527 if( data.hasOwnProperty('data-ngsrc'+st) ) { 6528 src = data['data-ngsrc'+st]; 6529 } 6530 // image URL from data-ngsrc attribute 6531 // if( src == '' ) { 6532 // src = data['data-ngsrc']; 6533 // } 6534 // image URL from href attribute (a element) 6535 // if( src == '' ) { 6536 // src = data['href']; 6537 // } 6538 src = src || data['data-ngsrc'] || data['href']; 6539 if( src !== undefined && !StartsWithProtocol(src) ) { // do not add the base URL if src starts with a protocol (http, https...) 6540 src = G.O.itemsBaseURL + src; 6541 } 6542 6543 // THUMBNAIL IMAGE 6544 var thumbsrc = ''; 6545 // src attribute (img element) 6546 if( data.hasOwnProperty('src') ) { 6547 thumbsrc = data['src']; 6548 } 6549 // data-ngthumb attribute 6550 if( thumbsrc == '' && data.hasOwnProperty('data-ngthumb') ) { 6551 thumbsrc = data['data-ngthumb']; 6552 } 6553 if( thumbsrc == '' ) { 6554 thumbsrc = src; // no thumbnail image URL -> use big image URL 6555 } 6556 if( thumbsrc !== undefined && !StartsWithProtocol(thumbsrc) ) { 6557 thumbsrc = G.O.itemsBaseURL + thumbsrc; 6558 } 6559 6560 // ignore if no media URL at all 6561 if( src === undefined && thumbsrc === undefined ) { return; } 6562 6563 //newObj.description=jQuery(item).attr('data-ngdesc'); 6564 var description = data['data-ngdesc']; 6565 var ID = data['id'] || data['data-ngid']; 6566 // if( ID == undefined ) { 6567 // ID = data['data-ngid']; 6568 // } 6569 var kind = data['data-ngkind']; 6570 var tags = data['data-ngtags']; 6571 6572 var albumID = '0'; 6573 if( data.hasOwnProperty('data-ngalbumid') ) { 6574 albumID = data['data-ngalbumid']; 6575 foundAlbumID = true; 6576 } 6577 6578 // var title = jQuery(item).text(); 6579 var title_from_url = GetImageTitleFromURL( src ); 6580 if( title_from_url != '' ) { 6581 title = title_from_url; 6582 } 6583 6584 var newItem = NGY2Item.New( G, title, description, ID, albumID, kind, tags ); 6585 if( title != '' ) { 6586 nbTitles++; 6587 } 6588 6589 // media source url - img is the default media kind 6590 newItem.setMediaURL( src, 'img'); 6591 6592 // manage media kinds other than IMG 6593 // newItem.mediaKind = 'img'; 6594 jQuery.each(mediaList, function ( n, media ) { 6595 var id = media.getID(src); 6596 if( id != null ) { 6597 if( thumbsrc == src && typeof media.thumbUrl == 'function' ) { 6598 thumbsrc = media.thumbUrl(id); 6599 } 6600 if( typeof media.url == 'function' ) { src = media.url(id); } 6601 newItem.mediaKind = media.kind; 6602 newItem.mediaMarkup = ( media.selfhosted ) ? media.markup( src ) : media.markup(id); 6603 return false; 6604 } 6605 }); 6606 6607 6608 // Big image size 6609 newItem.imageWidth = parseInt( data['data-ngimagewidth'] ); 6610 newItem.imageHeight = parseInt( data['data-ngimageheight'] ); 6611 6612 // default thumbnail image URL and size 6613 var tw = parseInt(data['data-ngthumbimgwidth']); 6614 var th = parseInt(data['data-ngthumbimgheight']); 6615 newItem.thumbs = { 6616 url: { l1 : { xs: thumbsrc, sm: thumbsrc, me: thumbsrc, la: thumbsrc, xl: thumbsrc }, lN : { xs: thumbsrc, sm: thumbsrc, me: thumbsrc, la: thumbsrc, xl: thumbsrc } }, 6617 width: { l1 : { xs: tw, sm: tw, me: tw, la: tw, xl: tw }, lN : { xs: tw, sm: tw, me: tw, la: tw, xl: tw } }, 6618 height: { l1 : { xs: th, sm: th, me: th, la: th, xl: th }, lN : { xs: th, sm: th, me: th, la: th, xl: th } } 6619 }; 6620 6621 // Media type -> IMG 6622 if( newItem.mediaKind == 'img' ) { 6623 6624 // responsive thumbnails URL and size 6625 var lst = ['xs', 'sm', 'me', 'la', 'xl']; 6626 for( var i = 0; i < lst.length; i++ ) { 6627 // url 6628 if( data.hasOwnProperty('data-ngthumb' + lst[i]) ) { 6629 var turl=data['data-ngthumb' + lst[i]]; 6630 if( !StartsWithProtocol(turl) ) { 6631 turl = G.O.itemsBaseURL + turl; 6632 } 6633 newItem.url.l1[lst[i]] = turl; 6634 newItem.url.lN[lst[i]] = turl; 6635 } 6636 6637 // width 6638 if( data.hasOwnProperty('data-ngthumb' + lst[i] + 'width') ) { 6639 var tw=parseInt(data['data-ngthumb' + lst[i] + 'width']); 6640 newItem.width.l1[lst[i]] = tw; 6641 newItem.width.lN[lst[i]] = tw; 6642 } 6643 // height 6644 if( data.hasOwnProperty('data-ngthumb' + lst[i] + 'height') ) { 6645 var th=parseInt('data-ngthumb' + lst[i] + 'height'); 6646 newItem.height.l1[lst[i]] = th; 6647 newItem.height.lN[lst[i]] = th; 6648 } 6649 } 6650 } 6651 6652 6653 // dominant colorS (needs to be a base64 gif) 6654 newItem.imageDominantColors = data['data-ngimagedominantcolors']; 6655 // dominant color (rgb hex) 6656 newItem.imageDominantColor = data['data-ngimagedominantcolors']; 6657 6658 newItem.destinationURL = data['data-ngdest']; 6659 newItem.downloadURL = data['data-ngdownloadurl']; 6660 6661 // Exif - model 6662 newItem.exif.model=data['data-ngexifmodel']; 6663 // Exif - flash 6664 newItem.exif.flash=data['data-ngexifflash']; 6665 // Exif - focallength 6666 newItem.exif.focallength=data['data-ngexiffocallength']; 6667 // Exif - fstop 6668 newItem.exif.fstop=data['data-ngexiffstop']; 6669 // Exif - exposure 6670 newItem.exif.exposure=data['data-ngexifexposure']; 6671 // Exif - iso 6672 newItem.exif.iso=data['data-ngexifiso']; 6673 // Exif - time 6674 newItem.exif.time=data['data-ngexiftime']; 6675 // Exif - location 6676 newItem.exif.location=data['data-ngexiflocation']; 6677 6678 newItem.contentIsLoaded = true; 6679 6680 // custom data 6681 if( jQuery(item).data('customdata') !== undefined ) { 6682 newItem.customData = cloneJSObject(jQuery(item).data('customdata')); 6683 } 6684 // custom data 6685 if( jQuery(item).data('ngcustomdata') !== undefined ) { 6686 newItem.customData = cloneJSObject(jQuery(item).data('ngcustomdata')); 6687 } 6688 6689 var fu=G.O.fnProcessData; 6690 if( fu !== null ) { 6691 typeof fu == 'function' ? fu(newItem, 'markup', item) : window[fu](newItem, 'markup', item); 6692 } 6693 6694 AlbumPostProcess(albumID); 6695 6696 }); 6697 6698 // if( foundAlbumID ) { G.O.displayBreadcrumb=true; } 6699 if( nbTitles == 0 ) { G.O.thumbnailLabel.display = false; } 6700 6701 } 6702 6703 6704 // ################################ 6705 // ##### DEFINE VARIABLES ##### 6706 // ################################ 6707 6708 6709 /** @function DefineVariables */ 6710 function DefineVariables() { 6711 6712 // change 'picasa' to 'google' for compatibility reason 6713 if( G.O.kind.toUpperCase() == 'PICASA' || G.O.kind.toUpperCase() == 'GOOGLE') { 6714 G.O.kind='google2'; 6715 } 6716 6717 // management of screen width 6718 G.GOM.cache.viewport = getViewport(); 6719 G.GOM.curWidth = RetrieveCurWidth(); 6720 6721 // tumbnail toolbar 6722 jQuery.extend(true, G.tn.toolbar.image, G.O.thumbnailToolbarImage ); 6723 jQuery.extend(true, G.tn.toolbar.album, G.O.thumbnailToolbarAlbum ); 6724 var t = ['image', 'album']; 6725 var pos= ['topLeft', 'topRight', 'bottomLeft', 'bottomRight'] 6726 for( var i=0; i < t.length ; i++ ) { 6727 for( var j=0; j < pos.length ; j++ ) { 6728 G.tn.toolbar[t[i]][pos[j]] = G.tn.toolbar[t[i]][pos[j]].toUpperCase(); 6729 } 6730 } 6731 6732 // convert label settings 6733 if( G.O.thumbnailLabel.position == 'overImageOnBottom' ) { 6734 G.O.thumbnailLabel.valign = 'bottom'; 6735 G.O.thumbnailLabel.position = 'overImage'; 6736 } 6737 if( G.O.thumbnailLabel.position == 'overImageOnMiddle' ) { 6738 G.O.thumbnailLabel.valign = 'middle'; 6739 G.O.thumbnailLabel.position = 'overImage'; 6740 } 6741 if( G.O.thumbnailLabel.position == 'overImageOnTop' ) { 6742 G.O.thumbnailLabel.valign = 'top'; 6743 G.O.thumbnailLabel.position = 'overImage'; 6744 } 6745 if( G.O.thumbnailL1Label !== undefined && G.O.thumbnailL1Label.position !== undefined ) { 6746 if( G.O.thumbnailL1Label.position == 'overImageOnBottom' ) { 6747 G.O.thumbnailL1Label.valign = 'bottom'; 6748 G.O.thumbnailL1Label.position = 'overImage'; 6749 } 6750 if( G.O.thumbnailL1Label.position == 'overImageOnMiddle' ) { 6751 G.O.thumbnailL1Label.valign = 'middle'; 6752 G.O.thumbnailL1Label.position = 'overImage'; 6753 } 6754 if( G.O.thumbnailL1Label.position == 'overImageOnTop' ) { 6755 G.O.thumbnailL1Label.valign = 'top'; 6756 G.O.thumbnailL1Label.position = 'overImage'; 6757 } 6758 } 6759 6760 // thumbnails label - level dependant settings 6761 G.O.thumbnailLabel.get = function( opt ) { 6762 if( G.GOM.curNavLevel == 'l1' && G.O.thumbnailL1Label !== undefined && G.O.thumbnailL1Label[opt] !== undefined ) { 6763 return G.O.thumbnailL1Label[opt]; 6764 } 6765 else { 6766 return G.O.thumbnailLabel[opt]; 6767 } 6768 }; 6769 G.O.thumbnailLabel.set = function( opt, value ) { 6770 if( G.GOM.curNavLevel == 'l1' && G.O.thumbnailL1Label !== undefined && G.O.thumbnailL1Label[opt] !== undefined ) { 6771 G.O.thumbnailL1Label[opt]=value; 6772 } 6773 else { 6774 G.O.thumbnailLabel[opt]=value; 6775 } 6776 }; 6777 6778 if( G.O.blockList != '' ) { G.blockList = G.O.blockList.toUpperCase().split('|'); } 6779 if( G.O.allowList != '' ) { G.allowList = G.O.allowList.toUpperCase().split('|'); } 6780 6781 if( G.O.albumList2 !== undefined && G.O.albumList2 !== null && G.O.albumList2.constructor === Array ) { 6782 var l=G.O.albumList2.length; 6783 for(var i=0; i< l; i++ ) { 6784 G.albumList.push(G.O.albumList2[i]); 6785 } 6786 // G.albumList=G.O.albumList.toUpperCase().split('|'); 6787 } 6788 if( G.O.albumList2 !== undefined && typeof G.O.albumList2 == 'string' ) { 6789 G.albumList.push(G.O.albumList2); 6790 } 6791 6792 6793 // thumbnail image crop 6794 G.tn.opt.lN.crop = G.O.thumbnailCrop; 6795 G.tn.opt.l1.crop = G.O.thumbnailL1Crop != null ? G.O.thumbnailL1Crop : G.O.thumbnailCrop; 6796 6797 6798 function ThumbnailOpt( lN, l1, opt) { 6799 G.tn.opt.lN[opt] = G.O[lN]; 6800 G.tn.opt.l1[opt] = G.O[lN]; 6801 if( toType(G.O[l1]) == 'number' ) { 6802 G.tn.opt.l1[opt] = G.O[l1]; 6803 } 6804 } 6805 // thumbnail stacks 6806 ThumbnailOpt('thumbnailStacks', 'thumbnailL1Stacks', 'stacks'); 6807 // thumbnail stacks translate X 6808 ThumbnailOpt('thumbnailStacksTranslateX', 'thumbnailL1StacksTranslateX', 'stacksTranslateX'); 6809 // thumbnail stacks translate Y 6810 ThumbnailOpt('thumbnailStacksTranslateY', 'thumbnailL1StacksTranslateY', 'stacksTranslateY'); 6811 // thumbnail stacks translate Z 6812 ThumbnailOpt('thumbnailStacksTranslateZ', 'thumbnailL1StacksTranslateZ', 'stacksTranslateZ'); 6813 // thumbnail stacks rotate X 6814 ThumbnailOpt('thumbnailStacksRotateX', 'thumbnailL1StacksRotateX', 'stacksRotateX'); 6815 // thumbnail stacks rotate Y 6816 ThumbnailOpt('thumbnailStacksRotateY', 'thumbnailL1StacksRotateY', 'stacksRotateY'); 6817 // thumbnail stacks rotate Z 6818 ThumbnailOpt('thumbnailStacksRotateZ', 'thumbnailL1StacksRotateZ', 'stacksRotateZ'); 6819 // thumbnail stacks scale 6820 ThumbnailOpt('thumbnailStacksScale', 'thumbnailL1StacksScale', 'stacksScale'); 6821 // thumbnail gutter width 6822 // ThumbnailOpt('thumbnailGutterWidth', 'thumbnailL1GutterWidth', 'gutterWidth'); 6823 // thumbnail gutter height 6824 // ThumbnailOpt('thumbnailGutterHeight', 'thumbnailL1GutterHeight', 'gutterHeight'); 6825 // thumbnail border horizontal 6826 ThumbnailOpt('thumbnailBorderHorizontal', 'thumbnailL1BorderHorizontal', 'borderHorizontal'); 6827 // thumbnail border vertical 6828 ThumbnailOpt('thumbnailBorderVertical', 'thumbnailL1BorderVertical', 'borderVertical'); 6829 // thumbnail grid base height (for cascading layout) 6830 ThumbnailOpt('thumbnailBaseGridHeight', 'thumbnailL1BaseGridHeight', 'baseGridHeight'); 6831 6832 6833 // Set same value to all widths 6834 function ResponsiveSetSize( setting, level, v ) { 6835 G.tn.settings[setting][level]['xs'] = v; 6836 G.tn.settings[setting][level]['sm'] = v; 6837 G.tn.settings[setting][level]['me'] = v; 6838 G.tn.settings[setting][level]['la'] = v; 6839 G.tn.settings[setting][level]['xl'] = v; 6840 } 6841 6842 // Get and evaluate responsive values from one option 6843 // Responsive is with syntax: n XSn1 SMn2 MEn3 LAn4 XLn5 (where n is the default value) 6844 // Value 'auto' is accepted for all options, but is handeld only for thumbnail width/height 6845 function ResponsiveOption( option, setting, level ) { 6846 var v = G.O[option]; 6847 6848 if( v === undefined || v === null ) { return; } 6849 6850 // if( toType(v) == 'number' ) { 6851 if( toType(v) == 'number' || v.indexOf(' ') == -1 ) { 6852 // set value for all widths 6853 var vn = 'auto'; 6854 if( v != 'auto' ) { vn = parseInt(v); } 6855 ResponsiveSetSize( setting, level, vn ); 6856 } 6857 else { 6858 var sp = v.split(' '); 6859 if( sp.length > 0 && +sp[0] === +sp[0] ) { // check if sp[0] is a number 6860 // first value is the default size for all widths 6861 var vn = 'auto'; 6862 if( sp[0] != 'auto' ) { vn = parseInt(sp[0]); } 6863 ResponsiveSetSize( setting, level, vn ); 6864 } 6865 for( var i = 1; i < sp.length; i++ ) { 6866 if( /^xs|sm|me|la|xl/i.test( sp[i] ) ) { // regex: i ignores the case and ^ means "starts with" 6867 var wi = sp[i].substring(0, 2).toLowerCase(); 6868 var va = sp[i].substring(2); 6869 var vn = 'auto'; 6870 if( va != 'auto' ) { vn = parseInt(va); } 6871 G.tn.settings[setting][level][wi] = vn; 6872 } 6873 } 6874 } 6875 } 6876 6877 ResponsiveOption('thumbnailGutterWidth', 'gutterWidth', 'lN'); 6878 ResponsiveOption('thumbnailGutterWidth', 'gutterWidth', 'l1'); // set default values for first level 6879 ResponsiveOption('thumbnailL1GutterWidth', 'gutterWidth', 'l1'); 6880 ResponsiveOption('thumbnailGutterHeight', 'gutterHeight', 'lN'); 6881 ResponsiveOption('thumbnailGutterHeight', 'gutterHeight', 'l1'); // set default values for first level 6882 ResponsiveOption('thumbnailL1GutterHeight', 'gutterHeight', 'l1'); 6883 6884 // gallery display mode 6885 G.galleryDisplayMode.lN = G.O.galleryDisplayMode.toUpperCase(); 6886 G.galleryDisplayMode.l1 = G.O.galleryL1DisplayMode != null ? G.O.galleryL1DisplayMode.toUpperCase() : G.O.galleryDisplayMode.toUpperCase(); 6887 6888 // gallery maximum number of lines of thumbnails 6889 G.galleryMaxRows.lN = G.O.galleryMaxRows; 6890 G.galleryMaxRows.l1 = toType(G.O.galleryL1MaxRows) == 'number' ? G.O.galleryL1MaxRows : G.O.galleryMaxRows; 6891 6892 // gallery last row full 6893 G.galleryLastRowFull.lN = G.O.galleryLastRowFull; 6894 G.galleryLastRowFull.l1 = G.O.galleryL1LastRowFull != null ? G.O.galleryL1LastRowFull : G.O.galleryLastRowFull; 6895 6896 // gallery sorting 6897 G.gallerySorting.lN = G.O.gallerySorting.toUpperCase(); 6898 G.gallerySorting.l1 = G.O.galleryL1Sorting != null ? G.O.galleryL1Sorting.toUpperCase() : G.gallerySorting.lN; 6899 6900 // gallery display transition 6901 G.galleryDisplayTransition.lN = G.O.galleryDisplayTransition.toUpperCase(); 6902 G.galleryDisplayTransition.l1 = G.O.galleryL1DisplayTransition != null ? G.O.galleryL1DisplayTransition.toUpperCase() : G.galleryDisplayTransition.lN; 6903 6904 // gallery display transition duration 6905 G.galleryDisplayTransitionDuration.lN = G.O.galleryDisplayTransitionDuration; 6906 G.galleryDisplayTransitionDuration.l1 = G.O.galleryL1DisplayTransitionDuration != null ? G.O.galleryL1DisplayTransitionDuration : G.galleryDisplayTransitionDuration.lN; 6907 6908 // gallery max items per album (not for inline/api defined items) 6909 G.galleryMaxItems.lN = G.O.galleryMaxItems; 6910 G.galleryMaxItems.l1 = toType(G.O.galleryL1MaxItems) == 'number' ? G.O.galleryL1MaxItems : G.O.galleryMaxItems; 6911 6912 // gallery filter tags 6913 G.galleryFilterTags.lN = G.O.galleryFilterTags; 6914 G.galleryFilterTags.l1 = G.O.galleryL1FilterTags != null ? G.O.galleryL1FilterTags : G.O.galleryFilterTags; 6915 6916 // gallery filter tags mode 6917 G.galleryFilterTagsMode.lN = G.O.galleryFilterTagsMode; 6918 G.galleryFilterTagsMode.l1 = G.O.galleryL1FilterTagsMode != null ? G.O.galleryL1FilterTagsMode : G.O.galleryFilterTagsMode; 6919 6920 // gallery pagination 6921 G.O.galleryPaginationMode = G.O.galleryPaginationMode.toUpperCase(); 6922 6923 if( toType(G.O.slideshowDelay) == 'number' && G.O.slideshowDelay >= 2000 ) { 6924 G.VOM.slideshowDelay = G.O.slideshowDelay; 6925 } 6926 else { 6927 NanoConsoleLog(G, 'Parameter "slideshowDelay" must be an integer >= 2000 ms.'); 6928 } 6929 6930 // gallery display transition
6931 if( typeof G.O.thumbnailDisplayTransition == 'boolean' ) { 6932 if( G.O.thumbnailDisplayTransition === true ) { 6933 G.tn.opt.lN.displayTransition = 'FADEIN'; 6934 G.tn.opt.l1.displayTransition = 'FADEIN'; 6935 } 6936 else { 6937 G.tn.opt.lN.displayTransition = 'NONE'; 6938 G.tn.opt.l1.displayTransition = 'NONE'; 6939 } 6940 } 6941 6942 if( G.O.fnThumbnailDisplayEffect !== '' ) { 6943 G.tn.opt.lN.displayTransition = 'CUSTOM'; 6944 G.tn.opt.l1.displayTransition = 'CUSTOM'; 6945 } 6946 if( G.O.fnThumbnailL1DisplayEffect !== '' ) { 6947 G.tn.opt.l1.displayTransition = 'CUSTOM'; 6948 } 6949 6950 6951 // thumbnail display transition easing 6952 // set default easing 6953 ThumbnailOpt('thumbnailDisplayTransitionEasing', 'thumbnailL1DisplayTransitionEasing', 'displayTransitionEasing'); 6954 // parse thumbnail display transition 6955 function thumbnailDisplayTransitionParse( cfg, level ) { 6956 if( typeof cfg == 'string' ) { 6957 var st=cfg.split('_'); 6958 if( st.length == 1 ) {
vendor: 130,618 bytes, lines 6959-10296
6959 G.tn.opt[level]['displayTransition'] = cfg.toUpperCase(); 6960 } 6961 if( st.length == 2 ) { 6962 G.tn.opt[level]['displayTransition'] = st[0].toUpperCase(); 6963 G.tn.opt[level]['displayTransitionStartVal'] = Number(st[1]); 6964 } 6965 if( st.length == 3 ) { 6966 G.tn.opt[level]['displayTransition'] = st[0].toUpperCase(); 6967 G.tn.opt[level]['displayTransitionStartVal'] = Number(st[1]); 6968 G.tn.opt[level]['displayTransitionEasing'] = st[2]; 6969 } 6970 } 6971 } 6972 thumbnailDisplayTransitionParse( G.O.thumbnailDisplayTransition, 'lN'); 6973 thumbnailDisplayTransitionParse( G.O.thumbnailDisplayTransition, 'l1'); 6974 thumbnailDisplayTransitionParse( G.O.thumbnailL1DisplayTransition, 'l1'); 6975 6976 6977 // thumbnail display transition duration 6978 ThumbnailOpt('thumbnailDisplayTransitionDuration', 'thumbnailL1DisplayTransitionDuration', 'displayTransitionDuration'); 6979 // thumbnail display transition interval duration 6980 ThumbnailOpt('thumbnailDisplayInterval', 'thumbnailL1DisplayInterval', 'displayInterval'); 6981 // thumbnail display order 6982 ThumbnailOpt('thumbnailDisplayOrder', 'thumbnailL1DisplayOrder', 'displayOrder'); 6983 6984 6985 // resolution breakpoints --> convert old syntax to new one 6986 if( G.O.thumbnailSizeSM !== undefined ) { G.O.breakpointSizeSM = G.O.thumbnailSizeSM; } 6987 if( G.O.thumbnailSizeME !== undefined ) { G.O.breakpointSizeME = G.O.thumbnailSizeME; } 6988 if( G.O.thumbnailSizeLA !== undefined ) { G.O.breakpointSizeLA = G.O.thumbnailSizeLA; } 6989 if( G.O.thumbnailSizeXL !== undefined ) { G.O.breakpointSizeXL = G.O.thumbnailSizeXL; } 6990 6991 // THUMBNAIL BUILD INIT 6992 //level 1 6993 if( G.O.thumbnailL1BuildInit2 !== undefined ) { 6994 var t1 = G.O.thumbnailL1BuildInit2.split('|'); 6995 for( var i = 0; i < t1.length; i++ ) { 6996 var o1 = t1[i].trim().split('_'); 6997 if( o1.length == 3 ) { 6998 var i1 = NewTBuildInit(); 6999 i1.element = ThumbnailOverEffectsGetCSSElement(o1[0], ''); 7000 i1.property = o1[1]; 7001 i1.value = o1[2]; 7002 G.tn.buildInit.level1.push(i1); 7003 } 7004 } 7005 } 7006 //level N 7007 if( G.O.thumbnailBuildInit2 !== undefined ) { 7008 var t1 = G.O.thumbnailBuildInit2.split('|'); 7009 for( var i = 0; i < t1.length; i++ ) { 7010 var o1 = t1[i].trim().split('_'); 7011 if( o1.length == 3 ) { 7012 var i1 = NewTBuildInit(); 7013 i1.element = ThumbnailOverEffectsGetCSSElement(o1[0], ''); 7014 i1.property = o1[1]; 7015 i1.value = o1[2]; 7016 G.tn.buildInit.std.push(i1); 7017 } 7018 } 7019 } 7020 7021 7022 // THUMBNAIL HOVER EFFETCS 7023 7024 // thumbnails hover effects - Level1 7025 var tL1HE = G.O.thumbnailL1HoverEffect2; 7026 if( tL1HE !== undefined ) { 7027 switch( toType(tL1HE) ) { 7028 case 'string': { 7029 let tmp = tL1HE.split('|'); 7030 for(var i = 0; i < tmp.length; i++) { 7031 let oDef = NewTHoverEffect(); 7032 oDef = ThumbnailHoverEffectExtract( tmp[i].trim(), oDef ); 7033 if( oDef != null ) { 7034 G.tn.hoverEffects.level1.push(oDef); 7035 } 7036 } 7037 break; 7038 } 7039 case 'object': { 7040 let oDef = NewTHoverEffect(); 7041 oDef = jQuery.extend(oDef,tL1HE); 7042 oDef = ThumbnailHoverEffectExtract( oDef.name, oDef ); 7043 if( oDef != null ) { 7044 G.tn.hoverEffects.level1.push(oDef); 7045 } 7046 break; 7047 } 7048 case 'array': { 7049 for(var i = 0; i < tL1HE.length; i++) { 7050 let oDef = NewTHoverEffect(); 7051 oDef = jQuery.extend(oDef,tL1HE[i]); 7052 oDef = ThumbnailHoverEffectExtract( oDef.name, oDef ); 7053 if( oDef != null ) { 7054 G.tn.hoverEffects.level1.push(oDef); 7055 } 7056 } 7057 break; 7058 } 7059 case 'null': 7060 break; 7061 default: 7062 NanoAlert(G, 'incorrect parameter for "thumbnailL1HoverEffect2".'); 7063 } 7064 } 7065 G.tn.hoverEffects.level1 = ThumbnailOverEffectsPreset(G.tn.hoverEffects.level1); 7066 7067 // thumbnails hover effects - other levels 7068 var tHE = G.O.thumbnailHoverEffect2; 7069 switch( toType(tHE) ) { 7070 case 'string': { 7071 let tmp = tHE.split('|'); 7072 for(var i = 0; i < tmp.length; i++) { 7073 let oDef = NewTHoverEffect(); 7074 oDef = ThumbnailHoverEffectExtract( tmp[i].trim(), oDef ); 7075 if( oDef != null ) { 7076 G.tn.hoverEffects.std.push(oDef); 7077 } 7078 } 7079 break; 7080 } 7081 case 'object': { 7082 let oDef = NewTHoverEffect(); 7083 oDef = jQuery.extend(oDef, tHE); 7084 oDef = ThumbnailHoverEffectExtract( oDef.name, oDef ); 7085 if( oDef != null ) { 7086 G.tn.hoverEffects.std.push(oDef); 7087 } 7088 break; 7089 } 7090 case 'array': { 7091 for(var i = 0; i < tHE.length; i++) { 7092 let oDef = NewTHoverEffect(); 7093 oDef = jQuery.extend(oDef,tHE[i]); 7094 oDef = ThumbnailHoverEffectExtract( oDef.name, oDef ); 7095 if( oDef!= null ) { 7096 G.tn.hoverEffects.std.push(oDef); 7097 } 7098 } 7099 break; 7100 } 7101 case 'null': 7102 break; 7103 default: 7104 NanoAlert(G, 'incorrect parameter for "thumbnailHoverEffect2".'); 7105 } 7106 G.tn.hoverEffects.std = ThumbnailOverEffectsPreset(G.tn.hoverEffects.std); 7107 7108 7109 if( G.O.touchAnimationL1 == undefined ) { 7110 G.O.touchAnimationL1 = G.O.touchAnimation; 7111 } 7112 7113 // disable thumbnail touch animation when no hover effect defined 7114 if( G.tn.hoverEffects.std.length == 0 ) { 7115 if( G.tn.hoverEffects.level1.length == 0 ) { 7116 G.O.touchAnimationL1 = false; 7117 } 7118 G.O.touchAnimation = false; 7119 } 7120 7121 7122 // thumbnail sizes 7123 if( G.O.thumbnailHeight == 0 || G.O.thumbnailHeight == '' ) { G.O.thumbnailHeight = 'auto'; } 7124 if( G.O.thumbnailWidth == 0 || G.O.thumbnailWidth == '' ) { G.O.thumbnailWidth = 'auto'; } 7125 if( G.O.thumbnailL1Height == 0 || G.O.thumbnailL1Height == '' ) { G.O.thumbnailL1Height = 'auto'; } 7126 if( G.O.thumbnailL1Width == 0 || G.O.thumbnailL1Width == '' ) { G.O.thumbnailL1Width = 'auto'; } 7127 7128 // RETRIEVE ALL THUMBNAIL SIZES 7129 // ThumbnailSizes( 'thumbnailWidth', false, 'width'); 7130 // ThumbnailSizes( 'thumbnailL1Width', true, 'width'); 7131 // ThumbnailSizes( 'thumbnailHeight', false, 'height'); 7132 // ThumbnailSizes( 'thumbnailL1Height', true, 'height'); 7133 ResponsiveOption('thumbnailWidth', 'width', 'lN'); 7134 ResponsiveOption('thumbnailWidth', 'width', 'l1'); 7135 ResponsiveOption('thumbnailL1Width', 'width', 'l1'); 7136 ResponsiveOption('thumbnailHeight', 'height', 'lN'); 7137 ResponsiveOption('thumbnailHeight', 'height', 'l1'); 7138 ResponsiveOption('thumbnailL1Height', 'height', 'l1'); 7139 7140 7141 G.O.thumbnailLabelHeight = parseInt(G.O.thumbnailLabelHeight); 7142 7143 7144 // retrieve all mosaic layout patterns 7145 // default pattern 7146 if( G.O.galleryMosaic != undefined ) { 7147 // clone object 7148 G.tn.settings.mosaic.l1.xs = JSON.parse(JSON.stringify(G.O.galleryMosaic)); 7149 G.tn.settings.mosaic.l1.sm = JSON.parse(JSON.stringify(G.O.galleryMosaic)); 7150 G.tn.settings.mosaic.l1.me = JSON.parse(JSON.stringify(G.O.galleryMosaic)); 7151 G.tn.settings.mosaic.l1.la = JSON.parse(JSON.stringify(G.O.galleryMosaic)); 7152 G.tn.settings.mosaic.l1.xl = JSON.parse(JSON.stringify(G.O.galleryMosaic)); 7153 G.tn.settings.mosaic.lN.xs = JSON.parse(JSON.stringify(G.O.galleryMosaic)); 7154 G.tn.settings.mosaic.lN.sm = JSON.parse(JSON.stringify(G.O.galleryMosaic)); 7155 G.tn.settings.mosaic.lN.me = JSON.parse(JSON.stringify(G.O.galleryMosaic)); 7156 G.tn.settings.mosaic.lN.la = JSON.parse(JSON.stringify(G.O.galleryMosaic)); 7157 G.tn.settings.mosaic.lN.xl = JSON.parse(JSON.stringify(G.O.galleryMosaic)); 7158 G.tn.settings.mosaicCalcFactor('l1', 'xs'); 7159 G.tn.settings.mosaicCalcFactor('l1', 'sm'); 7160 G.tn.settings.mosaicCalcFactor('l1', 'me'); 7161 G.tn.settings.mosaicCalcFactor('l1', 'la'); 7162 G.tn.settings.mosaicCalcFactor('l1', 'xl'); 7163 G.tn.settings.mosaicCalcFactor('lN', 'xs'); 7164 G.tn.settings.mosaicCalcFactor('lN', 'sm'); 7165 G.tn.settings.mosaicCalcFactor('lN', 'me'); 7166 G.tn.settings.mosaicCalcFactor('lN', 'la'); 7167 G.tn.settings.mosaicCalcFactor('lN', 'xl'); 7168 } 7169 if( G.O.galleryL1Mosaic != undefined ) { 7170 // default L1 pattern 7171 G.tn.settings.mosaic.l1.xs = JSON.parse(JSON.stringify(G.O.galleryL1Mosaic)); 7172 G.tn.settings.mosaic.l1.sm = JSON.parse(JSON.stringify(G.O.galleryL1Mosaic)); 7173 G.tn.settings.mosaic.l1.me = JSON.parse(JSON.stringify(G.O.galleryL1Mosaic)); 7174 G.tn.settings.mosaic.l1.la = JSON.parse(JSON.stringify(G.O.galleryL1Mosaic)); 7175 G.tn.settings.mosaic.l1.xl = JSON.parse(JSON.stringify(G.O.galleryL1Mosaic)); 7176 G.tn.settings.mosaicCalcFactor('l1', 'xs'); 7177 G.tn.settings.mosaicCalcFactor('l1', 'sm'); 7178 G.tn.settings.mosaicCalcFactor('l1', 'me'); 7179 G.tn.settings.mosaicCalcFactor('l1', 'la'); 7180 G.tn.settings.mosaicCalcFactor('l1', 'xl'); 7181 } 7182 7183 var lst=['xs','sm','me','la','xl']; 7184 // retrieve responsive mosaic definition for levels l1 & lN 7185 for( var w = 0; w < lst.length; w++ ) { 7186 if( G.O['galleryMosaic' + lst[w].toUpperCase()] != undefined ) { 7187 G.tn.settings.mosaic.lN[lst[w]] = JSON.parse(JSON.stringify( G.O['galleryMosaic' + lst[w].toUpperCase()] )); 7188 G.tn.settings.mosaic.l1[lst[w]] = JSON.parse(JSON.stringify( G.O['galleryMosaic' + lst[w].toUpperCase()] )); 7189 G.tn.settings.mosaicCalcFactor('lN',lst[w]); 7190 G.tn.settings.mosaicCalcFactor('l1', lst[w]); 7191 } 7192 } 7193 // retrieve responsive mosaic definition for level l1 7194 for( var w = 0; w < lst.length; w++ ) { 7195 if( G.O['galleryL1Mosaic' + lst[w].toUpperCase()] != undefined ) { 7196 G.tn.settings.mosaic.l1[lst[w]] = JSON.parse(JSON.stringify( G.O['galleryL1Mosaic' + lst[w].toUpperCase()] )); 7197 G.tn.settings.mosaicCalcFactor('l1', lst[w]); 7198 } 7199 } 7200 7201 G.O.imageTransition = G.O.imageTransition.toUpperCase(); 7202 7203 G.layout.SetEngine(); 7204 7205 // init plugins 7206 switch( G.O.kind ) { 7207 // MARKUP / API 7208 case '': 7209 break; 7210 // JSON, Flickr, Picasa, ... 7211 default: 7212 jQuery.nanogallery2['data_' + G.O.kind](G, 'Init' ); 7213 } 7214 7215 } 7216 7217 // HOVER EFFECTS 7218 function ThumbnailHoverEffectExtract( name, effect) { 7219 var easings = [ 'easeInQuad', 'easeOutQuad', 'easeInOutQuad', 'easeInCubic', 'easeOutCubic', 'easeInOutCubic', 'easeInQuart', 'easeOutQuart', 'easeInOutQuart', 'easeInQuint', 'easeOutQuint', 'easeInOutQuint', 'easeInSine', 'easeOutSine', 'easeInOutSine', 'easeInExpo', 'easeOutExpo', 'easeInOutExpo', 'easeInCirc', 'easeOutCirc', 'easeInOutCirc', 'easeOutBounce', 'easeInBack', 'easeOutBack', 'easeInOutBack', 'elastic', 'bounce']; 7220 7221 var sp = name.split('_'); 7222 if( sp.length >= 4 ) { 7223 // var oDef=NewTHoverEffect(); 7224 effect.name = ''; 7225 effect.type = sp[1]; 7226 effect.from = sp[2]; 7227 effect.to = sp[3]; 7228 if( sp.length >= 5 ) { 7229 // effect.duration=sp[4]; 7230 7231 for( var n = 4; n < sp.length; n++ ) { 7232 var v = sp[n]; 7233 7234 // check if an easing name 7235 var foundEasing = false; 7236 for( var e = 0; e < easings.length; e++) { 7237 if( v == easings[e] ) { 7238 foundEasing = true; 7239 effect.easing = v; 7240 break; 7241 } 7242 } 7243 if( foundEasing === true ) { 7244 continue; 7245 } 7246 7247 v = v.toUpperCase(); 7248 7249 if( v == 'HOVERIN' ) { 7250 effect.hoverout = false; 7251 continue; 7252 } 7253 if( v == 'HOVEROUT' ) { 7254 effect.hoverin = false; 7255 continue; 7256 } 7257 7258 if( v == 'KEYFRAME' ) { 7259 effect.firstKeyframe = false; 7260 continue; 7261 } 7262 7263 var num = parseInt(v.replace(/[^0-9\.]/g, ''), 10); // extract a number if one exists 7264 7265 if( num > 0 ) { 7266 // the string contains a numbers > 0 7267 if( v.indexOf('DURATION') >= 0 ) { 7268 effect.duration = num; 7269 continue; 7270 } 7271 if( v.indexOf('DURATIONBACK') >= 0 ) { 7272 effect.durationBack = num; 7273 continue; 7274 } 7275 if( v.indexOf('DELAY') >= 0 ) { 7276 effect.delay = num; 7277 continue; 7278 } 7279 if( v.indexOf('DELAYBACK') >= 0 ) { 7280 effect.delayBack = num; 7281 continue; 7282 } 7283 7284 // no parameter name found -> default is duration 7285 effect.duration = num; 7286 } 7287 } 7288 } 7289 effect.element = ThumbnailOverEffectsGetCSSElement(sp[0], effect.type); 7290 7291 } 7292 else { 7293 effect.name = name; 7294 // NanoAlert(G, 'incorrect parameter for "thumbnailHoverEffect": ' + name); 7295 // return null; 7296 } 7297 return effect; 7298 } 7299 7300 7301 function ThumbnailOverEffectsGetCSSElement( element, property ) { 7302 7303 var elts = { 7304 'image': '.nGY2GThumbnailImage', 7305 'thumbnail': '.nGY2GThumbnail', 7306 'label': '.nGY2GThumbnailLabel', 7307 'title': '.nGY2GThumbnailTitle', 7308 'description': '.nGY2GThumbnailDescription', 7309 'tools': '.nGY2GThumbnailIcons', 7310 'customlayer': '.nGY2GThumbnailCustomLayer', 7311 'default': 'nGY2GThumbnailImage' 7312 }; 7313 return (elts[element] || elts['default']); 7314 7315 7316 } 7317 7318 // convert preset hover effects (nanoGALLERY) to new ones (nanogallery2) 7319 function ThumbnailOverEffectsPreset( effects ) { 7320 7321 // COMPATIBILITY WITH nanoGALLERY 7322 // OK: 7323 // 'borderLighter', 'borderDarker', 'scale120', 'labelAppear', 'labelAppear75', 'labelOpacity50', 'scaleLabelOverImage' 7324 // 'overScale', 'overScaleOutside', 'descriptionAppear' 7325 // 'slideUp', 'slideDown', 'slideRight', 'slideLeft' 7326 // 'imageScale150', 'imageScaleIn80', 'imageScale150Outside', 'imageSlideUp', 'imageSlideDown', 'imageSlideRight', 'imageSlideLeft' 7327 // 'labelSlideUpTop', 'labelSlideUp', 'labelSlideDown', 'descriptionSlideUp' 7328 // KO: 7329 // 'labelSplit4', 'labelSplitVert', 'labelAppearSplit4', 'labelAppearSplitVert' 7330 // TODO: 7331 // 'rotateCornerBL', 'rotateCornerBR', 'imageSplit4', 'imageSplitVert', 'imageRotateCornerBL', 'imageRotateCornerBR', 'imageFlipHorizontal', 'imageFlipVertical' 7332 7333 7334 7335 var newEffects=[]; 7336 for( var i=0; i< effects.length; i++ ) { 7337 switch( effects[i].name.toUpperCase() ) { 7338 case 'BORDERLIGHTER': { 7339 let rgb = ColorHelperToRGB(GalleryThemeGetCurrent().thumbnail.borderColor); 7340 let name = 'thumbnail_borderColor_'+rgb+'_'+ShadeBlendConvert(0.5, rgb ); 7341 newEffects.push(ThumbnailHoverEffectExtract(name, effects[i])); 7342 break; 7343 } 7344 case 'BORDERDARKER': { 7345 let rgb = ColorHelperToRGB(GalleryThemeGetCurrent().thumbnail.borderColor); 7346 let name = 'thumbnail_borderColor_'+rgb+'_'+ShadeBlendConvert(-0.5, rgb ); 7347 newEffects.push(ThumbnailHoverEffectExtract(name, effects[i])); 7348 break; 7349 } 7350 case 'SCALE120': 7351 newEffects.push(ThumbnailHoverEffectExtract('thumbnail_scale_1.00_1.20', effects[i])); 7352 break; 7353 case 'LABELAPPEAR': 7354 case 'LABELAPPEAR75': 7355 newEffects.push(ThumbnailHoverEffectExtract('label_opacity_0.00_1.00', effects[i])); 7356 break; 7357 case 'TOOLSAPPEAR': 7358 newEffects.push(ThumbnailHoverEffectExtract('tools_opacity_0_1', effects[i])); 7359 break; 7360 case 'TOOLSSLIDEDOWN': 7361 newEffects.push(ThumbnailHoverEffectExtract('tools_translateY_-100%_0%', effects[i])); 7362 break; 7363 case 'TOOLSSLIDEUP': 7364 newEffects.push(ThumbnailHoverEffectExtract('tools_translateY_100%_0%', effects[i])); 7365 break; 7366 case 'LABELOPACITY50': 7367 newEffects.push(ThumbnailHoverEffectExtract('label_opacity_1.00_0.50', effects[i])); 7368 break; 7369 case 'LABELSLIDEUPTOP': 7370 case 'LABELSLIDEUP': 7371 newEffects.push(ThumbnailHoverEffectExtract('label_translateY_100%_0%', effects[i])); 7372 newEffects.push(ThumbnailHoverEffectExtract('label_translateY_100%_0%', effects[i])); 7373 break; 7374 case 'LABELSLIDEDOWN': 7375 newEffects.push(ThumbnailHoverEffectExtract('label_translateY_-100%_0%', effects[i])); 7376 break; 7377 case 'SCALELABELOVERIMAGE': 7378 newEffects.push(ThumbnailHoverEffectExtract('label_scale_0.00_1.00', effects[i])); 7379 var n = cloneJSObject(effects[i]); 7380 newEffects.push(ThumbnailHoverEffectExtract('image_scale_1.00_0.00', n)); 7381 break; 7382 case 'OVERSCALE': 7383 case 'OVERSCALEOUTSIDE': 7384 //var name = 'label_scale_0_100'; 7385 newEffects.push(ThumbnailHoverEffectExtract('label_scale_2.00_1.00', effects[i])); 7386 var n = cloneJSObject(effects[i]); 7387 newEffects.push(ThumbnailHoverEffectExtract('label_opacity_0.00_1.00', n)); 7388 n = cloneJSObject(effects[i]); 7389 newEffects.push(ThumbnailHoverEffectExtract('image_scale_1.00_0.00', n)); 7390 n = cloneJSObject(effects[i]); 7391 newEffects.push(ThumbnailHoverEffectExtract('image_opacity_1.00_0.00', n)); 7392 break; 7393 case 'DESCRIPTIONAPPEAR': 7394 newEffects.push(ThumbnailHoverEffectExtract('description_opacity_0_1', effects[i])); 7395 break; 7396 case 'SLIDERIGHT': 7397 newEffects.push(ThumbnailHoverEffectExtract('image_translateX_0%_100%', effects[i])); 7398 newEffects.push(ThumbnailHoverEffectExtract('label_translateX_-100%_0%', cloneJSObject(effects[i]))); 7399 break; 7400 case 'SLIDELEFT': 7401 newEffects.push(ThumbnailHoverEffectExtract('image_translateX_0%_-100%', effects[i])); 7402 newEffects.push(ThumbnailHoverEffectExtract('label_translateX_100%_0%', cloneJSObject(effects[i]))); 7403 break; 7404 case 'SLIDEUP': 7405 newEffects.push(ThumbnailHoverEffectExtract('image_translateY_0%_-100%', effects[i])); 7406 newEffects.push(ThumbnailHoverEffectExtract('label_translateY_100%_0%', cloneJSObject(effects[i]))); 7407 break; 7408 case 'SLIDEDOWN': 7409 newEffects.push(ThumbnailHoverEffectExtract('image_translateY_0%_100%', effects[i])); 7410 newEffects.push(ThumbnailHoverEffectExtract('label_translateY_-100%_0%', cloneJSObject(effects[i]))); 7411 break; 7412 case 'IMAGESCALE150': 7413 case 'IMAGESCALE150OUTSIDE': 7414 newEffects.push(ThumbnailHoverEffectExtract('image_scale_1.00_1.50', effects[i])); 7415 break; 7416 case 'IMAGESCALEIN80': 7417 newEffects.push(ThumbnailHoverEffectExtract('image_scale_1.20_1.00', effects[i])); 7418 break; 7419 case 'IMAGESLIDERIGHT': 7420 newEffects.push(ThumbnailHoverEffectExtract('image_translateX_0%_100%', effects[i])); 7421 break; 7422 case 'IMAGESLIDELEFT': 7423 newEffects.push(ThumbnailHoverEffectExtract('image_translateX_0%_-100%', effects[i])); 7424 break; 7425 case 'IMAGESLIDEUP': 7426 newEffects.push(ThumbnailHoverEffectExtract('image_translateY_0%_-100%', effects[i])); 7427 break; 7428 case 'IMAGESLIDEDOWN': 7429 newEffects.push(ThumbnailHoverEffectExtract('image_translateY_0%_100%', effects[i])); 7430 break; 7431 case 'LABELSLIDEUPDOWN': 7432 newEffects.push(ThumbnailHoverEffectExtract('label_translateY_0%_100%', effects[i])); 7433 break; 7434 case 'DESCRIPTIONSLIDEUP': 7435 newEffects.push(ThumbnailHoverEffectExtract('description_translateY_110%_0%', effects[i])); 7436 break; 7437 7438 case 'IMAGEBLURON': 7439 newEffects.push(ThumbnailHoverEffectExtract('image_blur_2.00px_0.00px', effects[i])); 7440 break; 7441 case 'IMAGEBLUROFF': 7442 newEffects.push(ThumbnailHoverEffectExtract('image_blur_0.00px_2.00px', effects[i])); 7443 break; 7444 case 'IMAGEGRAYON': 7445 newEffects.push(ThumbnailHoverEffectExtract('image_grayscale_0%_100%', effects[i])); 7446 break; 7447 case 'IMAGEGRAYOFF': 7448 newEffects.push(ThumbnailHoverEffectExtract('image_grayscale_100%_0%', effects[i])); 7449 break; 7450 case 'IMAGESEPIAON': 7451 newEffects.push(ThumbnailHoverEffectExtract('image_sepia_100%_1%', effects[i])); 7452 break; 7453 case 'IMAGESEPIAOFF': 7454 newEffects.push(ThumbnailHoverEffectExtract('image_sepia_1%_100%', effects[i])); 7455 break; 7456 7457 default: 7458 newEffects.push(effects[i]); 7459 break; 7460 } 7461 } 7462 7463 return newEffects; 7464 } 7465 7466 7467 // Thumbnail hover effect definition 7468 function NewTHoverEffect() { 7469 var oDef={ 7470 name: '', 7471 element: '', // element class 7472 type: '', 7473 from: '', // start value 7474 to: '', // end value 7475 hoverin: true, 7476 hoverout: true, 7477 firstKeyframe: true, 7478 delay: 0, 7479 delayBack: 0, 7480 duration: 400, 7481 durationBack: 300, 7482 easing: 'easeOutQuart', 7483 easingBack: 'easeOutQuart', 7484 animParam: null 7485 }; 7486 return oDef; 7487 } 7488 7489 function NewTBuildInit() { 7490 // to set CSS properties 7491 var oDef={ element: '', property: '', value: '' }; 7492 return oDef; 7493 } 7494 7495 7496 function ThumbnailStyle( cfg, level) { 7497 7498 switch( cfg.position ){ 7499 case 'onBottom' : 7500 G.tn.style[level]['label'] = 'bottom:0; '; 7501 break; 7502 case 'right' : 7503 switch( cfg.valign ) { 7504 case 'top': 7505 G.tn.style[level]['label'] = 'top:0; position:absolute; left: 50%;'; 7506 break; 7507 case 'middle': 7508 G.tn.style[level]['label'] = 'top:0; bottom:0; left: 50%;'; 7509 G.tn.style[level]['title'] = 'position:absolute; bottom:50%;'; 7510 G.tn.style[level]['desc'] = 'position:absolute; top:50%;'; 7511 break; 7512 case 'bottom': 7513 default: 7514 G.tn.style[level].label = 'bottom:0; position:absolute; left: 50%;'; 7515 G.tn.style[level].title = 'position:absolute;bottom:0;'; 7516 break; 7517 } 7518 break; 7519 case 'custom': 7520 break; 7521 default: 7522 case 'overImage' : 7523 switch( cfg.valign ) { 7524 case 'top': 7525 G.tn.style[level]['label'] = 'top:0; position:absolute;'; 7526 break; 7527 case 'middle': 7528 G.tn.style[level]['label'] = 'top:0; bottom:0;'; 7529 G.tn.style[level]['title'] = 'position:absolute; bottom:50%;'; 7530 G.tn.style[level]['desc'] = 'position:absolute; top:50%;'; 7531 break; 7532 case 'bottom': 7533 default: 7534 // G.O.thumbnailLabel.position = 'overImageOnBottom'; 7535 G.tn.style[level].label = 'bottom:0; position:absolute;'; 7536 break; 7537 } 7538 7539 // case 'overImageOnTop' : 7540 // G.tn.style[level]['label'] = 'top:0; position:absolute;'; 7541 // break; 7542 // case 'overImageOnMiddle' : 7543 // G.tn.style[level]['label'] = 'top:0; bottom:0;'; 7544 // G.tn.style[level]['title'] = 'position:absolute; bottom:50%;'; 7545 // G.tn.style[level]['desc'] = 'position:absolute; top:50%;'; 7546 // break; 7547 // case 'right' : 7548 // case 'custom' : 7549 // break; 7550 // case 'overImageOnBottom' : 7551 // default : 7552 // G.O.thumbnailLabel.position = 'overImageOnBottom'; 7553 // G.tn.style[level].label = 'bottom:0; position:absolute;'; 7554 // break; 7555 } 7556 7557 // if( G.layout.engine != 'CASCADING' ) { 7558 if( cfg.position != 'onBottom' ) { 7559 // multi-line 7560 if( cfg.titleMultiLine ) { 7561 G.tn.style[level]['title'] += 'white-space:normal;'; 7562 } 7563 if( cfg.descriptionMultiLine ) { 7564 G.tn.style[level]['desc'] += 'white-space:normal;'; 7565 } 7566 } 7567 7568 // horizontal alignement 7569 switch( cfg.align ) { 7570 case 'right': 7571 G.tn.style[level].label += 'text-align:right;'; 7572 break; 7573 case 'left': 7574 G.tn.style[level].label += 'text-align:left;'; 7575 break; 7576 default: 7577 G.tn.style[level].label += 'text-align:center;'; 7578 break; 7579 } 7580 7581 7582 if( cfg.titleFontSize != undefined && cfg.titleFontSize != '' ) { 7583 G.tn.style[level].title += 'font-size:' + cfg.titleFontSize + ';'; 7584 } 7585 if( cfg.descriptionFontSize != undefined && cfg.descriptionFontSize != '' ) { 7586 G.tn.style[level].desc += 'font-size:' + cfg.descriptionFontSize + ';'; 7587 } 7588 7589 if( cfg.displayDescription == false ) { 7590 G.tn.style[level].desc += 'display:none;'; 7591 } 7592 } 7593 7594 7595 // cache some thumbnail settings 7596 function ThumbnailDefCaches() { 7597 // thumbnail content CSS styles 7598 7599 // settings for level L1 and LN 7600 ThumbnailStyle( G.O.thumbnailLabel, 'lN'); 7601 if( G.O.thumbnailL1Label !== undefined ) { 7602 ThumbnailStyle( G.O.thumbnailL1Label, 'l1'); 7603 } 7604 else { 7605 ThumbnailStyle( G.O.thumbnailLabel, 'l1'); 7606 } 7607 7608 if( G.O.thumbnailL1Label && G.O.thumbnailL1Label.display ) { 7609 // settings for level L1 7610 ThumbnailStyle( G.O.thumbnailL1Label, 'l1'); 7611 } 7612 7613 7614 // default thumbnail sizes levels l1 and lN 7615 var lst=['xs','sm','me','la','xl']; 7616 for( var i = 0; i < lst.length; i++ ) { 7617 var w = G.tn.settings.width.lN[lst[i]]; 7618 if( w != 'auto' ) { 7619 G.tn.defaultSize.width.lN[lst[i]] = w; 7620 G.tn.defaultSize.width.l1[lst[i]] = w; 7621 } 7622 else { 7623 var h = G.tn.settings.height.lN[lst[i]]; 7624 G.tn.defaultSize.width.lN[lst[i]] = h; // dynamic width --> set height value as default for the width 7625 G.tn.defaultSize.width.l1[lst[i]] = h; // dynamic width --> set height value as default 7626 } 7627 } 7628 for( var i = 0; i < lst.length; i++ ) { 7629 var h = G.tn.settings.height.lN[lst[i]]; 7630 if( h != 'auto' ) { 7631 // grid or justified layout 7632 G.tn.defaultSize.height.lN[lst[i]] = h; //+G.tn.labelHeight.get(); 7633 G.tn.defaultSize.height.l1[lst[i]] = h; //+G.tn.labelHeight.get(); 7634 } 7635 else { 7636 var w = G.tn.settings.width.lN[lst[i]]; 7637 G.tn.defaultSize.height.lN[lst[i]] = w; // dynamic height --> set width value as default for the height 7638 G.tn.defaultSize.height.l1[lst[i]] = w; // dynamic height --> set width value as default 7639 } 7640 } 7641 7642 // default thumbnail sizes levels l1 7643 for( var i = 0; i < lst.length; i++ ) { 7644 var w = G.tn.settings.width.l1[lst[i]]; 7645 if( w != 'auto' ) { 7646 G.tn.defaultSize.width.l1[lst[i]] = w; 7647 } 7648 else { 7649 var h = G.tn.settings.height.l1[lst[i]]; 7650 G.tn.defaultSize.width.l1[lst[i]] = h; // dynamic width --> set height value as default 7651 } 7652 } 7653 for( var i = 0; i < lst.length; i++ ) { 7654 var h = G.tn.settings.height.l1[lst[i]]; 7655 if( h != 'auto' ) { 7656 // grid or justified layout 7657 G.tn.defaultSize.height.l1[lst[i]] = h; //+G.tn.labelHeight.get(); 7658 } 7659 else { 7660 var w = G.tn.settings.width.l1[lst[i]]; 7661 G.tn.defaultSize.height.l1[lst[i]]= w ; // dynamic height --> set width value as default 7662 } 7663 } 7664 7665 } 7666 7667 7668 // 7669 function GalleryThemeGetCurrent() { 7670 7671 var cs=null; 7672 switch(toType(G.O.galleryTheme)) { 7673 case 'object': // user custom color scheme object 7674 cs = G.galleryTheme_dark; // default color scheme 7675 jQuery.extend(true,cs,G.O.galleryTheme); 7676 break; 7677 case 'string': // name of an internal defined color scheme 7678 switch( G.O.galleryTheme ) { 7679 case 'light': 7680 cs = G.galleryTheme_light; 7681 break; 7682 case 'default': 7683 case 'dark': 7684 case 'none': 7685 default: 7686 cs = G.galleryTheme_dark; 7687 } 7688 break; 7689 default: 7690 cs = G.galleryTheme_dark; 7691 } 7692 return cs; 7693 } 7694 7695 // ##### BREADCRUMB/THUMBNAIL COLOR SCHEME ##### 7696 function SetGalleryTheme() { 7697 7698 if( typeof G.O.colorScheme !== 'undefined' ) { 7699 G.O.galleryTheme = G.O.colorScheme; 7700 } 7701 7702 var cs = null; 7703 var galleryTheme = ''; 7704 switch(toType(G.O.galleryTheme)) { 7705 case 'object': // user custom color scheme object 7706 cs = G.galleryTheme_dark; // default color scheme 7707 jQuery.extend(true,cs,G.O.galleryTheme); 7708 galleryTheme='nanogallery_gallerytheme_custom_' + G.baseEltID; 7709 break; 7710 case 'string': // name of an internal defined color scheme 7711 switch( G.O.galleryTheme ) { 7712 case 'light': 7713 cs = G.galleryTheme_light; 7714 galleryTheme='nanogallery_gallerytheme_light_' + G.baseEltID; 7715 break; 7716 case 'default': 7717 case 'dark': 7718 case 'none': 7719 default: 7720 cs = G.galleryTheme_dark; 7721 galleryTheme='nanogallery_gallerytheme_dark_' + G.baseEltID; 7722 } 7723 break; 7724 default: 7725 NanoAlert(G, 'Error in galleryTheme parameter.'); 7726 return; 7727 } 7728 7729 //var s1='.nanogallery_theme_'+G.O.theme+' '; 7730 var s1='.' + galleryTheme + ' '; 7731 7732 // navigation bar 7733 var c = cs.navigationBar; 7734 var s=s1+'.nGY2Navigationbar { background:'+c.background+'; }'+'\n'; 7735 if( c.border !== undefined && c.border !== '' ) { s+=s1+'.nGY2Navigationbar { border:'+c.border+'; }'+'\n'; } 7736 if( c.borderTop !== undefined && c.borderTop !== '' ) { s+=s1+'.nGY2Navigationbar { border-top:'+c.borderTop+'; }'+'\n'; } 7737 if( c.borderBottom !== undefined && c.borderBottom !== '' ) { s+=s1+'.nGY2Navigationbar { border-bottom:'+c.borderBottom+'; }'+'\n'; } 7738 if( c.borderRight !== undefined && c.borderRight !== '' ) { s+=s1+'.nGY2Navigationbar { border-right:'+c.borderRight+'; }'+'\n'; } 7739 if( c.borderLeft !== undefined && c.borderLeft !== '' ) { s+=s1+'.nGY2Navigationbar { border-left:'+c.borderLeft+'; }'+'\n'; } 7740 7741 // navigation bar - breadcrumb 7742 var c = cs.navigationBreadcrumb; 7743 s+=s1+'.nGY2Breadcrumb { background:'+c.background+'; border-radius:'+c.borderRadius+'; }'+'\n'; 7744 s+=s1+'.nGY2Breadcrumb .oneItem { color:'+c.color+'; }'+'\n'; 7745 s+=s1+'.nGY2Breadcrumb .oneItem:hover { color:'+c.colorHover+'; }'+'\n'; 7746 7747 // navigation bar - tag filter 7748 var c = cs.navigationFilter; 7749 s+=s1+'.nGY2NavFilterUnselected { color:'+c.color+'; background:'+c.background+'; border-radius:'+c.borderRadius+'; }'+'\n'; 7750 s+=s1+'.nGY2NavFilterSelected { color:'+c.colorSelected+'; background:'+c.backgroundSelected+'; border-radius:'+c.borderRadius+'; }'+'\n'; 7751 s+=s1+'.nGY2NavFilterSelectAll { color:'+c.colorSelected+'; background:'+c.background+'; border-radius:'+c.borderRadius+'; }'+'\n'; 7752 7753 // navigation bar - pagination next/previous 7754 var c = cs.navigationPagination; 7755 s+=s1+'.nGY2NavPagination { color:'+c.color+'; background:'+c.background+'; border-radius:'+c.borderRadius+'; }'+'\n'; 7756 s+=s1+'.nGY2NavPagination:hover { color:'+c.colorHover+'; }'+'\n'; 7757 7758 // thumbnails 7759 var c = cs.thumbnail; 7760 // s+=s1+'.nGY2GThumbnail { border-radius: '+c.borderRadius+'; background:'+c.background+'; border-color:'+c.borderColor+'; border-top-width:'+G.tn.opt.Get('borderVertical')+'px; border-right-width:'+G.tn.opt.Get('borderHorizontal')+'px; border-bottom-width:'+G.tn.opt.Get('borderVertical')+'px; border-left-width:'+G.tn.opt.Get('borderHorizontal')+'px;}'+'\n'; 7761 s+=s1+'.nGY2GThumbnail { border-radius: '+c.borderRadius+'; background:'+c.background+'; border-color:'+c.borderColor+'; }'+'\n'; 7762 s+=s1+'.nGY2GThumbnail_l1 { border-top-width:'+G.tn.opt.l1.borderVertical+'px; border-right-width:'+G.tn.opt.l1.borderHorizontal+'px; border-bottom-width:'+G.tn.opt.l1.borderVertical+'px; border-left-width:'+G.tn.opt.l1.borderHorizontal+'px;}'+'\n'; 7763 s+=s1+'.nGY2GThumbnail_lN { border-top-width:'+G.tn.opt.lN.borderVertical+'px; border-right-width:'+G.tn.opt.lN.borderHorizontal+'px; border-bottom-width:'+G.tn.opt.lN.borderVertical+'px; border-left-width:'+G.tn.opt.lN.borderHorizontal+'px;}'+'\n'; 7764 s+=s1+'.nGY2GThumbnailStack { background:'+c.stackBackground+'; }'+'\n'; 7765 // s+=s1+'.nGY2GThumbnailImage { background:'+cs.thumbnail.background+'; background-image:'+cs.thumbnail.backgroundImage+'; }'+'\n'; 7766 s+=s1+'.nGY2TnImgBack { background:'+c.background+'; background-image:'+c.backgroundImage+'; }'+'\n'; 7767 s+=s1+'.nGY2GThumbnailAlbumUp { background:'+c.background+'; background-image:'+c.backgroundImage+'; color:'+cs.thumbnail.titleColor+'; }'+'\n'; 7768 s+=s1+'.nGY2GThumbnailIconsFullThumbnail { color:'+c.titleColor+'; }\n'; 7769 s+=s1+'.nGY2GThumbnailLabel { background:'+c.labelBackground+'; opacity:'+c.labelOpacity+'; }'+'\n'; 7770 s+=s1+'.nGY2GThumbnailImageTitle { color:'+c.titleColor+'; background-color:'+c.titleBgColor+'; '+(c.titleShadow =='' ? '': 'Text-Shadow:'+c.titleShadow+';')+' }'+'\n'; 7771 s+=s1+'.nGY2GThumbnailAlbumTitle { color:'+c.titleColor+'; background-color:'+c.titleBgColor+'; '+(c.titleShadow =='' ? '': 'Text-Shadow:'+c.titleShadow+';')+' }'+'\n'; 7772 s+=s1+'.nGY2GThumbnailDescription { color:'+c.descriptionColor+'; background-color:'+c.descriptionBgColor+'; '+(c.descriptionShadow =='' ? '': 'Text-Shadow:'+c.descriptionShadow+';')+' }'+'\n'; 7773 7774 // thumbnails - icons 7775 var c = cs.thumbnailIcon; 7776 s+=s1+'.nGY2GThumbnailIcons { padding:'+c.padding+'; }\n'; 7777 s+=s1+'.nGY2GThumbnailIcon { color:'+c.color+'; '+(c.shadow =='' ? '': 'Text-Shadow:'+c.shadow+';')+' }\n'; 7778 s+=s1+'.nGY2GThumbnailIconTextBadge { background-color:'+c.color+'; }\n'; 7779 7780 // gallery pagination -> dot/rectangle based 7781 var c = cs.pagination; 7782 if( G.O.galleryPaginationMode != 'NUMBERS' ) { 7783 s+=s1+'.nGY2paginationDot { border:'+c.shapeBorder+'; background:'+c.shapeColor+';}\n'; 7784 s+=s1+'.nGY2paginationDotCurrentPage { border:'+c.shapeBorder+'; background:'+c.shapeSelectedColor+';}\n'; 7785 s+=s1+'.nGY2paginationRectangle { border:'+c.shapeBorder+'; background:'+c.shapeColor+';}\n'; 7786 s+=s1+'.nGY2paginationRectangleCurrentPage { border:'+c.shapeBorder+'; background:'+c.shapeSelectedColor+';}\n'; 7787 } else { 7788 s+=s1+'.nGY2paginationItem { background:'+c.background+'; color:'+c.color+'; border-radius:'+c.borderRadius+'; }\n'; 7789 s+=s1+'.nGY2paginationItemCurrentPage { background:'+c.background+'; color:'+c.color+'; border-radius:'+c.borderRadius+'; }\n'; 7790 s+=s1+'.nGY2PaginationPrev { background:'+c.background+'; color:'+c.color+'; border-radius:'+c.borderRadius+'; }\n'; 7791 s+=s1+'.nGY2PaginationNext { background:'+c.background+'; color:'+c.color+'; border-radius:'+c.borderRadius+'; }\n'; 7792 s+=s1+'.nGY2paginationItemCurrentPage { background:'+c.backgroundSelected+'; }\n'; 7793 } 7794 7795 // gallery more button 7796 var c = cs.thumbnail; 7797 // s+=s1+'.nGY2GalleryMoreButtonAnnotation { background:'+c.background+'; border-color:'+c.borderColor+'; border-top-width:'+G.O.thumbnailBorderVertical+'px; border-right-width:'+G.O.thumbnailBorderHorizontal+'px; border-bottom-width:'+G.O.thumbnailBorderVertical+'px; border-left-width:'+G.O.thumbnailBorderHorizontal+'px;}\n'; 7798 s+=s1+'.nGY2GalleryMoreButtonAnnotation { background:'+c.background+'; border-color:'+c.borderColor+'; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px;}\n'; 7799 s+=s1+'.nGY2GalleryMoreButtonAnnotation { color:'+c.titleColor+'; '+(c.titleShadow =='' ? '': 'Text-Shadow:'+c.titleShadow)+'; }\n'; 7800 7801 jQuery('head').append('<style id="ngycs_'+G.baseEltID+'">'+s+'</style>'); 7802 G.$E.base.addClass(galleryTheme); 7803 7804 }; 7805 7806 // ##### VIEWER COLOR SCHEME ##### 7807 function SetViewerTheme( ) { 7808 7809 if( G.VOM.viewerTheme != '' ) { 7810 G.VOM.$baseCont.addClass(G.VOM.viewerTheme); 7811 return; 7812 } 7813 7814 if( typeof G.O.colorSchemeViewer !== 'undefined' ) { 7815 G.O.viewerTheme = G.O.colorSchemeViewer; 7816 } 7817 7818 var cs=null; 7819 switch(toType(G.O.viewerTheme)) { 7820 case 'object': // user custom color scheme object 7821 cs = G.viewerTheme_dark; 7822 jQuery.extend(true, cs, G.O.viewerTheme); 7823 G.VOM.viewerTheme = 'nanogallery_viewertheme_custom_' + G.baseEltID; 7824 break; 7825 case 'string': // name of an internal defined color scheme 7826 switch( G.O.viewerTheme ) { 7827 case 'none': 7828 return; 7829 break; 7830 case 'light': 7831 cs = G.viewerTheme_light; 7832 G.VOM.viewerTheme = 'nanogallery_viewertheme_light_' + G.baseEltID; 7833 break; 7834 case 'dark': 7835 case 'default': 7836 cs = G.viewerTheme_dark; 7837 G.VOM.viewerTheme = 'nanogallery_viewertheme_dark_' + G.baseEltID; 7838 break; 7839 } 7840 break; 7841 default: 7842 NanoAlert(G, 'Error in viewerTheme parameter.'); 7843 return; 7844 } 7845 7846 var s1 = '.' + G.VOM.viewerTheme + ' '; 7847 var s = s1 + '.nGY2Viewer { background:' + cs.background + '; }'+'\n'; 7848 s += s1 + '.nGY2Viewer .toolbarBackground { background:' + cs.barBackground + '; }'+'\n'; 7849 s += s1 + '.nGY2Viewer .toolbar { border:' + cs.barBorder + '; color:' + cs.barColor + '; }'+'\n'; 7850 s += s1 + '.nGY2Viewer .toolbar .previousButton:after { color:' + cs.barColor + '; }'+'\n'; 7851 s += s1 + '.nGY2Viewer .toolbar .nextButton:after { color:' + cs.barColor + '; }'+'\n'; 7852 s += s1 + '.nGY2Viewer .toolbar .closeButton:after { color:' + cs.barColor + '; }'+'\n'; 7853 s += s1 + '.nGY2Viewer .toolbar .label .title { color:' + cs.barColor + '; }'+'\n'; 7854 s += s1 + '.nGY2Viewer .toolbar .label .description { color:' + cs.barDescriptionColor + '; }'+'\n'; 7855 jQuery('head').append('<style>' + s + '</style>'); 7856 G.VOM.$baseCont.addClass(G.VOM.viewerTheme); 7857 }; 7858 7859 7860 7861 /** @function SetPolyFills */ 7862 function SetPolyFills() { 7863 7864 // POLYFILL FOR BIND function --> for older Safari mobile 7865 // found on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Compatibility 7866 if (!Function.prototype.bind) { 7867 Function.prototype.bind = function (oThis) { 7868 if (typeof this !== "function") { 7869 // closest thing possible to the ECMAScript 5 7870 // internal IsCallable function 7871 throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); 7872 } 7873 7874 var aArgs = Array.prototype.slice.call(arguments, 1), 7875 fToBind = this, 7876 fNOP = function () {}, 7877 fBound = function () { 7878 return fToBind.apply(this instanceof fNOP && oThis 7879 ? this 7880 : oThis, 7881 aArgs.concat(Array.prototype.slice.call(arguments))); 7882 }; 7883 7884 fNOP.prototype = this.prototype; 7885 fBound.prototype = new fNOP(); 7886 7887 return fBound; 7888 }; 7889 } 7890 7891 // requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel 7892 // http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 7893 // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating 7894 // MIT license 7895 (function() { 7896 var lastTime = 0; 7897 var vendors = ['ms', 'moz', 'webkit', 'o']; 7898 for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { 7899 window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; 7900 window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame']; 7901 } 7902 if (!window.requestAnimationFrame) 7903 window.requestAnimationFrame = function(callback, element) { 7904 var currTime = new Date().getTime(); 7905 var timeToCall = Math.max(0, 16 - (currTime - lastTime)); 7906 var id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall); 7907 lastTime = currTime + timeToCall; 7908 return id; 7909 }; 7910 7911 if (!window.cancelAnimationFrame) 7912 window.cancelAnimationFrame = function(id) { 7913 clearTimeout(id); 7914 }; 7915 }()); 7916 7917 // array.ngy2removeIf -> removes items from array base on a function's result 7918 Array.prototype.ngy2removeIf = function(callback) { 7919 var i = this.length; 7920 while (i--) { 7921 if (callback(this[i], i)) { 7922 this.splice(i, 1); 7923 } 7924 } 7925 }; 7926 7927 // IE11 for startsWith 7928 // thanks to @lichtamberg - https://github.com/lichtamberg 7929 if (!String.prototype.startsWith) { 7930 String.prototype.startsWith = function(searchString, position) { 7931 position = position || 0; 7932 return this.indexOf(searchString, position) === position; 7933 }; 7934 } 7935 7936 } 7937 7938 7939 // Gallery clicked or toolbar touched -> retrieve & execute action 7940 function GalleryClicked(e) { 7941 7942 var r = GalleryEventRetrieveElementl(e, false); 7943 7944 if( r.GOMidx == -1 ) { return 'exit'; } 7945 7946 var idx = G.GOM.items[r.GOMidx].thumbnailIdx; 7947 if( G.GOM.slider.hostIdx == r.GOMidx ) { 7948 idx = G.GOM.items[G.GOM.slider.currentIdx].thumbnailIdx; 7949 } 7950 switch( r.action ) { 7951 case 'OPEN': 7952 ThumbnailOpen(idx, false); 7953 return 'exit'; 7954 break; 7955 case 'DISPLAY': 7956 // used the display icon (ignore if selection mode) 7957 ThumbnailOpen(idx, true); 7958 return 'exit'; 7959 break; 7960 case 'TOGGLESELECT': 7961 ThumbnailSelectionToggle(idx); 7962 return 'exit'; 7963 break; 7964 case 'SHARE': 7965 PopupShare(idx); 7966 return 'exit'; 7967 break; 7968 case 'DOWNLOAD': 7969 DownloadImage(idx); 7970 return 'exit'; 7971 break; 7972 case 'INFO': 7973 ItemDisplayInfo(G.I[idx]); 7974 return 'exit'; 7975 break; 7976 case 'SHOPPINGCART': 7977 AddToCart(idx, 'gallery'); 7978 return 'exit'; 7979 break; 7980 default: 7981 // all other actions (custom1..10, or anything else) 7982 var fu = G.O.fnThumbnailToolCustAction; 7983 if( fu !== null ) { 7984 typeof fu == 'function' ? fu(r.action, G.I[idx]) : window[fu](r.action, G.I[idx]); 7985 } 7986 break; 7987 } 7988 } 7989 7990 // Download an image 7991 function DownloadImage(idx) { 7992 if( G.I[idx].mediaKind != 'img' ) { return; } 7993 7994 7995 var url = G.I[idx].src; 7996 7997 if( G.I[idx].downloadURL != undefined && G.I[idx].downloadURL != '' ) { 7998 url = G.I[idx].downloadURL; 7999 } 8000 8001 var a = document.createElement('a'); 8002 a.href = url; 8003 // a.download = url.split('.').pop(); 8004 a.download = url.split('/').pop(); 8005 a.target = '_blank'; 8006 a.style.display = 'none'; 8007 document.body.appendChild(a); 8008 a.click(); 8009 document.body.removeChild(a); 8010 8011 } 8012 8013 // add one image to the shopping cart 8014 function AddToCart( idx, source ) { 8015 // increment quantity if already in shopping cart 8016 var found=false; 8017 for( var i=0; i<G.shoppingCart.length; i++ ) { 8018 if( G.shoppingCart[i].idx == idx ) { 8019 G.shoppingCart[i].qty++; 8020 ThumbnailBuildToolbarOneCartUpdate( G.I[idx] ); 8021 8022 var fu = G.O.fnShoppingCartUpdated; 8023 if( fu !== null ) { 8024 typeof fu == 'function' ? fu(G.shoppingCart, G.I[idx], source) : window[fu](G.shoppingCart, G.I[idx], source); 8025 } 8026 TriggerCustomEvent('shoppingCartUpdated'); 8027 return; 8028 } 8029 } 8030 8031 // add to shopping cart 8032 if( !found) { 8033 G.shoppingCart.push( { idx:idx, ID:G.I[idx].GetID(), qty:1} ); 8034 ThumbnailBuildToolbarOneCartUpdate(G.I[idx]); 8035 8036 var fu=G.O.fnShoppingCartUpdated; 8037 if( fu !== null ) { 8038 typeof fu == 'function' ? fu(G.shoppingCart, G.I[idx], source) : window[fu](G.shoppingCart, G.I[idx], source); 8039 } 8040 TriggerCustomEvent('shoppingCartUpdated'); 8041 } 8042 } 8043 8044 8045 // All thumbnails are set to unselected 8046 function ThumbnailSelectionClear() { 8047 G.GOM.nbSelected = 0; 8048 for( var i = 0, nbTn = G.GOM.items.length; i < nbTn ; i++ ) { 8049 var item = G .I[G.GOM.items[i].thumbnailIdx]; 8050 if( item.selected ) { 8051 item.selected = false; 8052 var fu = G.O.fnThumbnailSelection; 8053 if( fu !== null ) { 8054 typeof fu == 'function' ? fu(item.$elt, item, G.I) : window[fu](item.$elt, item, G.I); 8055 } 8056 } 8057 item.selected = false; 8058 } 8059 } 8060 8061 function ThumbnailSelectionToggle( idx ){ 8062 var item = G.I[idx]; 8063 if( item.selected === true ) { 8064 ThumbnailSelectionSet(item, false); 8065 G.GOM.nbSelected--; 8066 TriggerCustomEvent('itemUnSelected'); 8067 } 8068 else { 8069 ThumbnailSelectionSet(item, true); 8070 G.GOM.nbSelected++; 8071 TriggerCustomEvent('itemSelected'); 8072 } 8073 } 8074 8075 8076 // this replaces ThumbnailSelection() 8077 function ThumbnailSelectionSet(item, selected ){ 8078 8079 item.selected = selected; 8080 8081 ThumbnailSelectionSetIcon( item ); 8082 8083 // called when the selection status of an item changed 8084 var fu=G.O.fnThumbnailSelection; 8085 if( fu !== null ) { 8086 typeof fu == 'function' ? fu(item.$elt, item, G.I) : window[fu](item.$elt, item, G.I); 8087 } 8088 8089 } 8090 8091 function ThumbnailSelectionSetIcon( item ) { 8092 if( item.$elt == null ) { 8093 // thumbnail is not built 8094 return; 8095 } 8096 var $sub = item.$getElt('.nGY2GThumbnail'); 8097 var $icon = item.$getElt('.nGY2GThumbnailIconImageSelect'); 8098 if( item.selected === true) { 8099 $sub.addClass('nGY2GThumbnailSubSelected'); 8100 $icon.addClass('nGY2ThumbnailSelected'); 8101 $icon.removeClass('nGY2ThumbnailUnselected'); 8102 $icon.html(G.O.icons.thumbnailSelected); 8103 } 8104 else { 8105 $sub.removeClass('nGY2GThumbnailSubSelected'); 8106 $icon.removeClass('nGY2ThumbnailSelected'); 8107 $icon.addClass('nGY2ThumbnailUnselected'); 8108 $icon.html(G.O.icons.thumbnailUnselected); 8109 } 8110 } 8111 8112 8113 // display a modal popup for sharing image/album 8114 function PopupShare(idx) { 8115 8116 // SEE SAMPLES: https://gist.github.com/chrisjlee/5196139 8117 // https://github.com/Julienh/Sharrre 8118 8119 var item=G.I[idx]; 8120 8121 var currentURL=document.location.protocol + '//' + document.location.hostname + document.location.pathname; 8122 var newLocationHash = '#nanogallery/' + G.baseEltID + '/'; 8123 if( item.kind == 'image' ) { 8124 newLocationHash += item.albumID + '/' + item.GetID(); 8125 } 8126 else { 8127 newLocationHash += item.GetID(); 8128 } 8129 8130 var content = '<br><br>'; 8131 content += '<div class="nGY2PopupOneItem" style="text-align:center;" data-share="facebook">' + G.O.icons.shareFacebook + '</div>'; 8132 content += '<div class="nGY2PopupOneItem" style="text-align:center;" data-share="pinterest">' + G.O.icons.sharePinterest + '</div>'; 8133 content += '<div class="nGY2PopupOneItem" style="text-align:center;" data-share="tumblr">' + G.O.icons.shareTumblr + '</div>'; 8134 content += '<div class="nGY2PopupOneItem" style="text-align:center;" data-share="twitter">' + G.O.icons.shareTwitter + '</div>'; 8135 // content += '<div class="nGY2PopupOneItem" style="text-align:center;" data-share="googleplus">' + G.O.icons.shareGooglePlus + '</div>'; 8136 content += '<div class="nGY2PopupOneItem" style="text-align:center;" data-share="vk">' + G.O.icons.shareVK + '</div>'; 8137 content += '<div class="nGY2PopupOneItem" style="text-align:center;" data-share="mail">' + G.O.icons.shareMail + '</div>'; 8138 content += '<div class="nGY2PopupOneItem" style="text-align:center;"></div>'; 8139 content += '<input class="nGY2PopupOneItemText" readonly type="text" value="' + currentURL+newLocationHash + '" style="width:100%;text-align:center;">'; 8140 content += '<br>'; 8141 8142 currentURL = encodeURIComponent(document.location.protocol + '//' + document.location.hostname + document.location.pathname + newLocationHash); 8143 8144 var currentTitle = item.title; 8145 var currentTn = item.thumbImg().src; 8146 8147 8148 Popup('nanogallery2 - share to:', content, 'Center'); 8149 8150 G.popup.$elt.find('.nGY2PopupOneItem').on('click', function(e) { 8151 e.stopPropagation(); 8152 8153 var shareURL = ''; 8154 var found = true; 8155 switch(jQuery(this).attr('data-share').toUpperCase()) { 8156 case 'FACEBOOK': 8157 // <a name="fb_share" type="button" href="http://www.facebook.com/sharer.php?u={$url}&media={$imgPath}&description={$desc}" class="joinFB">Share Your Advertise</a> 8158 //window.open("https://www.facebook.com/sharer.php?u="+currentURL,"","height=368,width=600,left=100,top=100,menubar=0"); 8159 shareURL = 'https://www.facebook.com/sharer.php?u=' + currentURL; 8160 break; 8161 case 'VK': 8162 shareURL = 'http://vk.com/share.php?url=' + currentURL; 8163 break; 8164 case 'GOOGLEPLUS': 8165 shareURL = "https://plus.google.com/share?url=" + currentURL; 8166 break; 8167 case 'TWITTER': 8168 // shareURL="https://twitter.com/share?url="+currentURL+"&text="+currentTitle; 8169 shareURL = 'https://twitter.com/intent/tweet?text=' + currentTitle + 'url=' + currentURL; 8170 break; 8171 case 'PINTEREST': 8172 // shareURL='https://pinterest.com/pin/create/bookmarklet/?media='+currentTn+'&url='+currentURL+'&description='+currentTitle; 8173 shareURL = 'https://pinterest.com/pin/create/button/?media=' + currentTn + '&url=' + currentURL + '&description=' + currentTitle; 8174 break; 8175 case 'TUMBLR': 8176 //shareURL='https://www.tumblr.com/widgets/share/tool/preview?caption=<strong>'+currentTitle+'</strong>&tags=nanogallery2&url='+currentURL+'&shareSource=legacy&posttype=photo&content='+currentTn+'&clickthroughUrl='+currentURL; 8177 shareURL = 'http://www.tumblr.com/share/link?url=' + currentURL + '&name=' + currentTitle; 8178 break; 8179 case 'MAIL': 8180 shareURL = 'mailto:?subject=' + currentTitle + '&body=' + currentURL; 8181 break; 8182 default: 8183 found = false; 8184 break; 8185 } 8186 8187 if( found ) { 8188 window.open(shareURL, "" , "height=550,width=500,left=100,top=100,menubar=0" ); 8189 G.popup.close(); 8190 // $popup.remove(); 8191 } 8192 8193 }); 8194 } 8195 8196 // build a modal popup 8197 function Popup(title, content, align) { 8198 var pp = '<div class="nGY2Popup" style="opacity:0;"><div class="nGY2PopupContent' + align + '">'; 8199 pp += '<div class="nGY2PopupCloseButton" style="font-size:0.9em;">' + G.O.icons.buttonClose + '</div>'; 8200 pp += '<div class="nGY2PopupTitle">' + title + '</div>'; 8201 pp += content; 8202 pp += '</div></div>'; 8203 8204 G.popup.$elt = jQuery(pp).appendTo('body'); 8205 setElementOnTop( G.VOM.$viewer, G.popup.$elt); 8206 8207 G.popup.isDisplayed = true; 8208 8209 var tweenable = new NGTweenable(); 8210 tweenable.tween({ 8211 from: { o: 0, y: 100 }, 8212 to: { o: 1, y: 0 }, 8213 easing: 'easeInOutSine', 8214 duration: 250, 8215 step: function (state, att) { 8216 G.popup.$elt[0].style.opacity = state.o; 8217 G.popup.$elt[0].style[G.CSStransformName] = 'translateY(' + (state.y) + 'px)'; 8218 } 8219 }); 8220 8221 G.popup.$elt.find('.nGY2PopupCloseButton').on('click', function(e) { 8222 e.stopPropagation(); 8223 G.popup.close(); 8224 }); 8225 8226 } 8227 8228 8229 function GalleryMouseEnter(e) { 8230 if( !G.VOM.viewerDisplayed && G.GOM.albumIdx != -1 ) { 8231 var r = GalleryEventRetrieveElementl(e, true); 8232 // if( r.action == 'OPEN' && r.GOMidx != -1 ) { 8233 if( r.GOMidx != -1 ) { 8234 // var target = e.target || e.srcElement; 8235 // if( target.getAttribute('class') != 'nGY2GThumbnail' ) { return; } 8236 ThumbnailHover(r.GOMidx); 8237 } 8238 } 8239 } 8240 8241 function GalleryMouseLeave(e) { 8242 if( !G.VOM.viewerDisplayed && G.GOM.albumIdx != -1 ) { 8243 var r = GalleryEventRetrieveElementl(e, true); 8244 if( r.GOMidx != -1 ) { 8245 // var target = e.target || e.srcElement; 8246 // if( target.getAttribute('class') != 'nGY2GThumbnail' ) { return; } 8247 ThumbnailHoverOut(r.GOMidx); 8248 } 8249 } 8250 } 8251 8252 function GalleryEventRetrieveElementl( e, ignoreSubItems ) { 8253 var r = { action: 'NONE', GOMidx: -1 }; 8254 8255 if( e == undefined ) { 8256 return r; 8257 } 8258 var target = e.target || e.srcElement; 8259 while( target != G.$E.conTnParent[0] ) { // loop element parent up to find the thumbnail element 8260 if( jQuery(target).hasClass('nGY2GThumbnail') ) { 8261 if( r.action == 'NONE' ) { 8262 r.action = 'OPEN'; 8263 } 8264 r.GOMidx = jQuery(target).data('index'); 8265 return r; 8266 } 8267 // if( !ignoreSubItems && jQuery(target).hasClass('nGY2GThumbnailIcon') ) { 8268 if( !ignoreSubItems ) { 8269 var a = jQuery(target).data('ngy2action'); 8270 if( a != '' && a != undefined ) { 8271 r.action = a; 8272 } 8273 } 8274 if( target.parentNode == null ) { 8275 return r; 8276 } 8277 target = target.parentNode; 8278 } 8279 return r; 8280 } 8281 8282 8283 // OPEN ONE THUMBNAIL 8284 function ThumbnailOpen( idx, ignoreSelected ) { 8285 var item = G.I[idx]; 8286 8287 G.GOM.albumIdxLoading = idx; // store idx -> may be used to display loader on album thumbnail 8288 8289 var fu = G.O.fnThumbnailClicked; 8290 if( fu !== null ) { 8291 typeof fu == 'function' ? fu(item.$elt, item) : window[fu](item.$elt, item); 8292 } 8293 8294 // open URL 8295 if( item.destinationURL !== undefined && item.destinationURL.length > 0 ) { 8296 window.location = item.destinationURL; 8297 return; 8298 } 8299 8300 switch( item.kind ) { 8301 case 'image': 8302 if( ignoreSelected === false && G.GOM.nbSelected > 0 ) { 8303 ThumbnailSelectionToggle(idx); 8304 } 8305 else { 8306 // display image 8307 DisplayPhotoIdx( idx ); 8308 } 8309 break; 8310 case 'album': 8311 if( ignoreSelected === false && G.GOM.nbSelected > 0 ) { 8312 ThumbnailSelectionToggle( idx ); 8313 } 8314 else { 8315 if( G.O.thumbnailAlbumDisplayImage && idx != 0 ) { 8316 // display album content in lightbox 8317 DisplayFirstMediaInAlbum( idx ); 8318 return; 8319 } 8320 else { 8321 // display album content in gallery 8322 DisplayAlbum('-1', item.GetID()); 8323 } 8324 } 8325 break; 8326 case 'albumUp': 8327 var parent = NGY2Item.Get(G, item.albumID); 8328 DisplayAlbum('-1', parent.albumID); 8329 break; 8330 } 8331 } 8332 8333 function DisplayFirstMediaInAlbum( albumIdx ) { 8334 if( G.O.debugMode ) { console.log('#DisplayFirstPhotoInAlbum : '+ albumIdx); } 8335 8336 var item = G.I[albumIdx]; 8337 8338 var l = G.I.length; 8339 for( var i = 0; i < l; i++ ) { 8340 if( G.I[i].albumID == item.GetID() ) { 8341 DisplayPhotoIdx( i ); 8342 return; 8343 } 8344 } 8345 8346 // load album content 8347 AlbumGetContent( item.GetID(), DisplayFirstMediaInAlbum, albumIdx, null ); 8348 8349 } 8350 8351 8352 // Open link to original image (new window) 8353 function OpenOriginal( item ) { 8354 switch( G.O.kind ) { 8355 case 'flickr': 8356 var sU = 'https://www.flickr.com/photos/' + G.O.userID + '/' + item.GetID(); 8357 if( item.albumID != '0' ) { 8358 sU += '/in/album-' + item.albumID + '/'; 8359 } 8360 window.open(sU, '_blank'); 8361 break; 8362 case 'picasa': 8363 case 'google': 8364 case 'google2': 8365 // no more working since Google changed the access to Google Photos in 2017 8366 // var sU='https://plus.google.com/photos/'+G.O.userID+'/albums/'+item.albumID+'/'+item.GetID(); 8367 // window.open(sU,'_blank'); 8368 // break; 8369 default: 8370 var sU = item.responsiveURL(); 8371 window.open(sU, '_blank'); 8372 break; 8373 } 8374 } 8375 8376 // ######################################################## 8377 // DISPLAY ONE MEDIA 8378 // with internal or external viewer 8379 // ######################################################## 8380 function DisplayPhotoIdx( ngy2ItemIdx ) { 8381 8382 if( !G.O.thumbnailOpenInLightox ) { return; } 8383 8384 if( G.O.thumbnailOpenOriginal ) { 8385 // Open link to original image 8386 OpenOriginal( G.I[ngy2ItemIdx] ); 8387 return; 8388 } 8389 8390 var items = []; 8391// G.VOM.currItemIdx = 0; 8392 G.VOM.content.current.vIdx = 0; 8393 G.VOM.items = []; 8394 G.VOM.albumID = G.I[ngy2ItemIdx].albumID; 8395 8396 var vimg = new VImg(ngy2ItemIdx); 8397 G.VOM.items.push(vimg); 8398 items.push(G.I[ngy2ItemIdx]); 8399 //TODO -> danger? -> pourquoi reconstruire la liste si déjà ouvert (back/forward) 8400 var l = G.I.length; 8401 for( let idx = ngy2ItemIdx+1; idx < l ; idx++) { 8402 let item = G.I[idx]; 8403 if( item.kind == 'image' && item.isToDisplay(G.VOM.albumID) && item.destinationURL == '' ) { 8404 let vimg = new VImg(idx); 8405 G.VOM.items.push(vimg); 8406 items.push(item); 8407 } 8408 } 8409 var last = G.VOM.items.length; 8410 var cnt = 1; 8411 for( let idx = 0; idx < ngy2ItemIdx ; idx++) { 8412 let item = G.I[idx]; 8413 if( item.kind == 'image' && item.isToDisplay(G.VOM.albumID) && item.destinationURL == '' ) { 8414 let vimg = new VImg(idx); 8415 vimg.mediaNumber = cnt; 8416 G.VOM.items.push(vimg); 8417 items.push(item); 8418 cnt++; 8419 } 8420 } 8421 for( let i = 0; i < last; i++ ) { 8422 G.VOM.items[i].mediaNumber = cnt; 8423 cnt++; 8424 } 8425 8426 // opens media with external viewer 8427 var fu = G.O.fnThumbnailOpen; 8428 if( fu !== null ) { 8429 typeof fu == 'function' ? fu(items) : window[fu](items); 8430 return; 8431 } 8432 8433 // use internal viewer 8434 if( !G.VOM.viewerDisplayed ) { 8435 // build viewer and display 8436 LightboxOpen(); 8437 } 8438 else { 8439 // viewer already displayed -> display new media in current viewer 8440 G.VOM.content.current.$media.empty(); 8441 let item = G.VOM.content.current.NGY2Item(); 8442 var spreloader = '<div class="nGY2ViewerMediaLoaderDisplayed"></div>'; 8443 if( item.mediaKind == 'img' && item.imageWidth != 0 && item.imageHeight != 0 ) { 8444 spreloader = '<div class="nGY2ViewerMediaLoaderHidden"></div>'; 8445 } 8446 G.VOM.content.current.$media.append( spreloader + item.mediaMarkup); 8447 ViewerSetMediaVisibility(G.VOM.content.next, 0); 8448 ViewerSetMediaVisibility(G.VOM.content.previous, 0); 8449 if( item.mediaKind == 'img' ) { 8450 G.VOM.ImageLoader.loadImage(VieweImgSizeRetrieved, item); 8451 } 8452 // G.VOM.$mediaCurrent.css({ opacity:0 }).attr('src',''); 8453 // G.VOM.ImageLoader.loadImage(VieweImgSizeRetrieved, G.VOM.NGY2Item(0)); 8454 // G.VOM.$mediaCurrent.children().eq(0).attr('src',G.emptyGif).attr('src', G.VOM.NGY2Item(0).responsiveURL()); 8455 // LightboxDisplay(0, ''); 8456 LightboxDisplay(''); 8457 } 8458 } 8459 8460 function ViewerZoomStart() { 8461 if( G.O.viewerZoom && !G.VOM.viewerMediaIsChanged ) { 8462 var item = G.VOM.content.current.NGY2Item(); 8463 if( item.mediaKind == 'img' && item.imageHeight > 0 && item.imageWidth > 0 ) { 8464 if( G.VOM.zoom.isZooming === false ) { 8465 // default zoom 8466 G.VOM.zoom.userFactor = 1; 8467 G.VOM.zoom.isZooming = true; 8468 } 8469 return true; 8470 } 8471 } 8472 return false; 8473 } 8474 8475 function ViewerZoomIn( zoomIn ) { 8476 if( zoomIn ) { 8477 // zoom in 8478 G.VOM.zoom.userFactor += 0.1; 8479 ViewerZoomMax(); 8480 } 8481 else { 8482 // zoom out 8483 G.VOM.zoom.userFactor -= 0.1; 8484 ViewerZoomMin(); 8485 } 8486 ViewerMediaSetPosAndZoom(); 8487 } 8488 8489 function ViewerZoomMax() { 8490 if( G.VOM.zoom.userFactor > 3 ) { 8491 G.VOM.zoom.userFactor = 3; 8492 } 8493 } 8494 function ViewerZoomMin() { 8495 8496 if( G.VOM.zoom.userFactor < 0.2 ) { 8497 G.VOM.zoom.userFactor = 0.2; 8498 } 8499 } 8500 8501 8502 8503 // Set position and size of all 3 media containers 8504 function ViewerMediaSetPosAndZoom() { 8505 8506 if( !G.VOM.zoom.isZooming ) { 8507 G.VOM.zoom.userFactor = 1; 8508 } 8509 // window.ng_draf( function() { 8510 ViewerMediaSetPosAndZoomOne( G.VOM.content.current, true ); 8511 ViewerMediaSetPosAndZoomOne( G.VOM.content.previous, false ); 8512 ViewerMediaSetPosAndZoomOne( G.VOM.content.next, false ); 8513 // }); 8514 } 8515 8516 8517 8518 // Media which is not IMG -> center and set size 8519 function ViewerMediaCenterNotImg( $mediaContainer ) { 8520 var $media = $mediaContainer.children().eq(1); 8521 var h = 90; 8522 if( G.O.viewerGallery != 'none' ) { h -= 10; } 8523 if( G.O.viewerToolbar.display != 'none' ) { h -= 10; } 8524 $media.css( {'height': h+'%' }); 8525 $media.css( {'width': '90%' }); 8526 $media[0].style[G.CSStransformName] = 'translate(0px, "50%") '; 8527 } 8528 8529 // Set position and size of ONE media container 8530 function ViewerMediaSetPosAndZoomOne(content_item, isCurrent ) { 8531 8532 var item = content_item.NGY2Item(); 8533 var $img = content_item.$media; 8534 8535 8536 if( item.mediaKind != 'img' ) { 8537 ViewerMediaCenterNotImg( $img ); 8538 return; 8539 } 8540 8541 if( item.imageHeight == 0 || item.imageWidth == 0 ) { 8542 // ViewerSetMediaVisibility( item, $img, 0 ); 8543 ViewerSetMediaVisibility( content_item, 0 ); 8544 return; 8545 } 8546 8547 // part 1: set the image size 8548 var zoomUserFactor = isCurrent == true ? G.VOM.zoom.userFactor : 1; 8549 8550 var dpr = 1; 8551 if( G.O.viewerImageDisplay == 'bestImageQuality' ) { 8552 dpr = window.devicePixelRatio; 8553 } 8554 8555 // retrieve the base zoom factor (image fill screen) 8556 var zoomBaseFactorW = (G.VOM.window.lastWidth - G.VOM.padding.V) / (item.imageWidth / dpr); 8557 var zoomBaseFactorH = (G.VOM.window.lastHeight - G.VOM.padding.H) / (item.imageHeight / dpr); 8558 var zoomBaseFactor = Math.min(zoomBaseFactorW, zoomBaseFactorH); 8559 if( zoomBaseFactor > 1 && G.O.viewerImageDisplay != 'upscale' ) { 8560 // no upscale 8561 zoomBaseFactor = 1; 8562 } 8563 8564 var imageCurrentHeight = (item.imageHeight / dpr) * zoomUserFactor * zoomBaseFactor; 8565 var imageCurrentWidth = (item.imageWidth / dpr) * zoomUserFactor * zoomBaseFactor; 8566 $img.children().eq(1).css( {'height': imageCurrentHeight }); 8567 $img.children().eq(1).css( {'width': imageCurrentWidth }); 8568 8569 // retrieve posX/Y to center image 8570 var posX = 0; 8571 if( imageCurrentWidth > G.VOM.window.lastWidth ) { 8572 posX = -(imageCurrentWidth - G.VOM.window.lastWidth)/2; 8573 } 8574 var posY = 0; 8575 // if( imageCurrentHeight > G.VOM.window.lastHeight ) { 8576 // posY = ( imageCurrentHeight - G.VOM.window.lastHeight ) / 2; 8577 // } 8578 // posY = 0; // actually, it seems that the image is always centered vertically -> so no need to to anything 8579 8580 // Part 2: set the X/Y position (for zoom/pan) 8581 if( isCurrent ) { 8582 if( !G.VOM.zoom.isZooming ) { 8583 G.VOM.panPosX = 0; 8584 G.VOM.panPosY = 0; 8585 } 8586 G.VOM.zoom.posX = posX; 8587 G.VOM.zoom.posY = posY; 8588 ViewerImagePanSetPosition(G.VOM.panPosX, G.VOM.panPosY, $img, false); 8589 } 8590 // else { 8591 //$img[0].style[G.CSStransformName]= 'translate3D('+ posX+'px, '+ posY+'px, 0) '; 8592 // } 8593 else { 8594 // set the pan position of each media container 8595 ViewerMediaPanX( G.VOM.swipePosX ); 8596 $img.children().eq(1)[0].style[G.CSStransformName]= 'translate(0px, 0px) rotate('+ item.rotationAngle +'deg)'; 8597 } 8598 8599 } 8600 8601 // position the image depending on the zoom factor and the pan X/Y position 8602 // IMG is the only media kind supporting zoom/pan 8603 function ViewerImagePanSetPosition(posX, posY, imageContainer, savePosition ) { 8604 if( savePosition ) { 8605 G.VOM.panPosX = posX; 8606 G.VOM.panPosY = posY; 8607 } 8608 8609 posX += G.VOM.zoom.posX; 8610 posY += G.VOM.zoom.posY; 8611 8612 // imageContainer.children().eq(1)[0].style[G.CSStransformName]= 'translate('+ posX + 'px, '+ posY + 'px)'; 8613 imageContainer.children().eq(1)[0].style[G.CSStransformName]= 'translate('+ posX + 'px, '+ posY + 'px) rotate('+ G.VOM.content.current.NGY2Item().rotationAngle +'deg)'; 8614 8615 8616 } 8617 8618 8619 // LIGHTBOX 8620 // display media with internal viewer 8621 function LightboxOpen( idx ) { 8622 8623 // G.VOM.viewerDisplayed = true; 8624 G.GOM.firstDisplay = false; 8625 8626 // remove scrollbar and add right margin with same width as the scrollbar to avoid page reflow 8627 jQuery('head').append('<style id="nGY2_body_scrollbar_style" type="text/css">.nGY2_body_scrollbar{margin-right: ' + (window.innerWidth - document.documentElement.clientWidth) + 'px;}</style>'); 8628 jQuery("body").addClass("nGY2_body_scrollbar"); 8629 8630 8631 G.VOM.$baseCont = jQuery('<div class="nGY2 nGY2ViewerContainer" style="opacity:1"></div>').appendTo('body'); 8632 8633 SetViewerTheme(); 8634 8635 G.VOM.$viewer = jQuery('<div class="nGY2Viewer" style="opacity:0" itemscope itemtype="http://schema.org/ImageObject"></div>').appendTo( G.VOM.$baseCont ); 8636 G.VOM.$viewer.css({ msTouchAction: 'none', touchAction: 'none' }); // avoid pinch zoom 8637 8638 if( idx == undefined ) { 8639 G.VOM.content.current.vIdx = 0; 8640 } 8641 else { 8642 G.VOM.content.current.vIdx = idx; 8643 } 8644 G.VOM.content.previous.vIdx = G.VOM.IdxNext(); 8645 G.VOM.content.next.vIdx = G.VOM.IdxPrevious(); 8646 8647 var sMedia = '<div class="nGY2ViewerMediaPan"><div class="nGY2ViewerMediaLoaderDisplayed"></div>' + G.VOM.content.previous.NGY2Item().mediaMarkup + '</div>'; // previous media 8648 sMedia += '<div class="nGY2ViewerMediaPan"><div class="nGY2ViewerMediaLoaderDisplayed"></div>' + G.VOM.content.current.NGY2Item().mediaMarkup + '</div>'; // current media 8649 sMedia += '<div class="nGY2ViewerMediaPan"><div class="nGY2ViewerMediaLoaderDisplayed"></div>' + G.VOM.content.next.NGY2Item().mediaMarkup + '</div>'; // next media 8650 8651 var sNav = ''; 8652 var iconP = G.O.icons.viewerImgPrevious; 8653 if( iconP != undefined && iconP != '') { 8654 sNav += '<div class="nGY2ViewerAreaPrevious ngy2viewerToolAction" data-ngy2action="previous">' + iconP + '</div>'; 8655 } 8656 var iconN = G.O.icons.viewerImgNext; 8657 if( iconN != undefined && iconN != '') { 8658 sNav += '<div class="nGY2ViewerAreaNext ngy2viewerToolAction" data-ngy2action="next">' + iconN + '</div>'; 8659 } 8660 8661 G.VOM.$content = jQuery('<div class="nGY2ViewerContent">' + sMedia + sNav + '</div>').appendTo( G.VOM.$viewer ); 8662 8663 G.VOM.$buttonLeft = G.VOM.$content.find('.nGY2ViewerAreaPrevious'); 8664 G.VOM.$buttonRight = G.VOM.$content.find('.nGY2ViewerAreaNext'); 8665 8666 var $mediaPan = G.VOM.$content.find('.nGY2ViewerMediaPan'); 8667 G.VOM.content.previous.$media = $mediaPan.eq(0); // pointer to previous media container 8668 G.VOM.content.current.$media = $mediaPan.eq(1); // pointer to current media container 8669 G.VOM.content.next.$media = $mediaPan.eq(2); // pointer to next media container 8670 8671 // position next/previous media 8672 var vP = G.GOM.cache.viewport; 8673 G.VOM.content.previous.$media[0].style[G.CSStransformName] = 'translate(-' + vP.w + 'px, 0px)'; 8674 G.VOM.content.next.$media[0].style[G.CSStransformName] = 'translate(' + vP.w + 'px, 0px)'; 8675 8676 8677 G.VOM.ImageLoader.loadImage( VieweImgSizeRetrieved, G.VOM.content.current.NGY2Item() ); 8678 G.VOM.ImageLoader.loadImage( VieweImgSizeRetrieved, G.VOM.content.previous.NGY2Item() ); 8679 G.VOM.ImageLoader.loadImage( VieweImgSizeRetrieved, G.VOM.content.next.NGY2Item() ); 8680 8681 G.VOM.padding.H = parseInt(G.VOM.$content.css("padding-left")) + parseInt(G.VOM.$content.css("padding-right")); 8682 G.VOM.padding.V = parseInt(G.VOM.$content.css("padding-top")) + parseInt(G.VOM.$content.css("padding-bottom")); 8683 8684 // build media toolbar container 8685 var vtbBg1 = ''; 8686 var vtbBg2 = ' toolbarBackground'; 8687 if( G.O.viewerToolbar.fullWidth ) { 8688 vtbBg1 = ' toolbarBackground'; 8689 vtbBg2 = ''; 8690 } 8691 var vtbAlign = 'text-align:center;'; 8692 switch ( G.O.viewerToolbar.align ) { 8693 case 'left': 8694 vtbAlign = 'text-align:left;'; 8695 break; 8696 case 'right': 8697 vtbAlign = 'text-align:right;'; 8698 break; 8699 } 8700 var sTB = '<div class="toolbarContainer nGEvent' + vtbBg1 + '" style="visibility:' +(G.O.viewerToolbar.display ? "visible" : "hidden")+';'+vtbAlign+'"><div class="toolbar nGEvent' + vtbBg2 + '"></div></div>'; 8701 G.VOM.$toolbar = jQuery(sTB).appendTo(G.VOM.$viewer); 8702 8703 if( G.VOM.toolbarMode == 'min' || (G.O.viewerToolbar.autoMinimize > 0 && G.O.viewerToolbar.autoMinimize >= G.GOM.cache.viewport.w) ) { 8704 ViewerToolbarForVisibilityMin(); 8705 } 8706 else { 8707 ViewerToolbarForVisibilityStd(); 8708 } 8709 8710 // top-left toolbar 8711 var sTopLeft = '<div class="nGY2ViewerToolsTopLeft nGEvent"><div class="toolbar nGEvent">'; 8712 var sTL = G.O.viewerTools.topLeft.split(','); 8713 for( var i = 0, sTLL = sTL.length; i < sTLL; i++) { 8714 sTopLeft += ToolbarAddElt( sTL[i] ); 8715 } 8716 sTopLeft += '</div></div>'; 8717 G.VOM.$toolbarTL = jQuery(sTopLeft).appendTo(G.VOM.$viewer); 8718 8719 // top-right toolbar 8720 var sTopRight = '<div class="nGY2ViewerToolsTopRight nGEvent"><div class="toolbar nGEvent">'; 8721 var sTR = G.O.viewerTools.topRight.split(','); 8722 for( var i = 0, sTRL = sTR.length; i < sTRL; i++) { 8723 sTopRight += ToolbarAddElt( sTR[i] ); 8724 } 8725 sTopRight += '</div></div>'; 8726 G.VOM.$toolbarTR = jQuery(sTopRight).appendTo(G.VOM.$viewer); 8727 8728 // set the events handler on the toolbars 8729 ViewerToolsOn(); 8730 8731 // Go to fullscreen mode 8732 if( ngscreenfull.enabled && G.O.viewerFullscreen ) { 8733 ngscreenfull.request(); 8734 G.VOM.viewerIsFullscreen=true; 8735 } 8736 8737 // Gallery 8738 LightboxGalleryBuild(); 8739 8740 setElementOnTop('', G.VOM.$viewer); 8741 ResizeLightbox(true); 8742 G.VOM.gallery.Resize(); 8743 G.VOM.timeImgChanged = new Date().getTime(); 8744 8745 // viewer display transition 8746 G.VOM.$toolbarTL.css('opacity', 0); 8747 G.VOM.$toolbarTR.css('opacity', 0); 8748 G.VOM.$buttonLeft.css('opacity', 0); 8749 G.VOM.$buttonRight.css('opacity', 0); 8750 if( G.O.viewerGallery != 'none' ) { G.VOM.gallery.$elt.css('opacity', 0); } 8751 G.VOM.$content.css('opacity', 0); 8752 G.VOM.$toolbarTR[0].style[G.CSStransformName] = 'translateY(-40px) '; 8753 G.VOM.$toolbarTL[0].style[G.CSStransformName] = 'translateY(-40px) '; 8754 G.VOM.$buttonLeft[0].style[G.CSStransformName] = 'translateX(-40px) '; 8755 G.VOM.$buttonRight[0].style[G.CSStransformName] = 'translateX(40px) '; 8756 8757 // STEP 1: display main container, including media 8758 new NGTweenable().tween({ 8759 from: { opacity: 0, posY: G.VOM.window.lastHeight*.5 }, 8760 to: { opacity: 1, posY: 0 }, 8761 delay: 10, 8762 duration: 450, 8763 easing: 'easeInOutQuint', 8764 step: function (state) { 8765 // lightbox 8766 G.VOM.$viewer.css('opacity', state.opacity); 8767 G.VOM.$viewer[0].style[G.CSStransformName] = 'translateY(' + (state.posY) + 'px) '; 8768 8769 // media in lightbox 8770 G.VOM.$content.css('opacity', state.opacity); 8771 } 8772 }); 8773 8774 8775 // STEP 2: display tools, left/right navigation buttons, gallery 8776 new NGTweenable().tween({ 8777 from: { posY: -40, opacity: 0, scale: 3 }, 8778 to: { posY: 0, opacity: 1, scale: 1 }, 8779 delay: 300, 8780 duration: 400, 8781 easing: 'easeInOutQuint', 8782 step: function (state) { 8783 8784 // tools 8785 G.VOM.$toolbarTR[0].style[G.CSStransformName] = 'translateY(' + (state.posY) + 'px) '; 8786 G.VOM.$toolbarTL[0].style[G.CSStransformName] = 'translateY(' + (state.posY) + 'px) '; 8787 G.VOM.$buttonLeft[0].style[G.CSStransformName] = 'translateX(' + (state.posY) + 'px) '; 8788 G.VOM.$buttonRight[0].style[G.CSStransformName] = 'translateX(' + (-state.posY) + 'px) '; 8789 8790 // gallery 8791 if( G.O.viewerGallery != 'none' ) { 8792 G.VOM.gallery.$elt.css({ opacity: state.opacity }); 8793 G.VOM.gallery.$elt[0].style[G.CSStransformName] = 'scale('+state.scale+')'; 8794 } 8795 8796 }, 8797 finish: function() { 8798 G.VOM.viewerDisplayed = true; 8799 ViewerMediaPanX(0); 8800 ViewerSetEvents(); 8801 8802 LightboxDisplay(''); 8803 8804 if( G.O.slideshowAutoStart ) { 8805 G.VOM.playSlideshow = false; 8806 SlideshowToggle(); 8807 } 8808 8809 ViewerToolsUnHide(); 8810 LightboxDisplayFinalize(''); 8811 } 8812 }); 8813 8814 8815 8816 8817 // stop click propagation on media ==> if the user clicks outside of an media, the viewer is closed 8818 // --> no more supported since v2.0.0 8819 // G.VOM.$viewer.find('img').on('click', function (e) { e.stopPropagation(); }); 8820 8821 8822 // ViewerMediaPanX(0); 8823 // ViewerSetEvents(); 8824 8825 // LightboxDisplay(''); 8826 8827 // if( G.O.slideshowAutoStart ) { 8828 // G.VOM.playSlideshow = false; 8829 // SlideshowToggle(); 8830 // } 8831 } 8832 8833 function ViewerEvents() { 8834 if( !G.VOM.viewerDisplayed || G.VOM.viewerMediaIsChanged ) { 8835 // if( !G.VOM.viewerDisplayed || G.VOM.viewerMediaIsChanged || G.VOM.content.current.NGY2Item().mediaKind != 'img') { 8836 // ignore fired event if viewer not displayed or if currently changed (or if current media not an image) 8837 return false; 8838 } 8839 return true; 8840 } 8841 8842 // VIEWER - BUILD THE THUMBNAILS GALLERY 8843 function LightboxGalleryBuild() { 8844 8845 G.VOM.gallery.firstDisplay = true; 8846 8847 if( G.O.viewerGallery != 'none' ) { 8848 8849 var tw = G.O.viewerGalleryTWidth; 8850 var th = G.O.viewerGalleryTHeight; 8851 var gutter = 2; 8852 8853 var t = ''; 8854 for( var i=0; i< G.VOM.items.length; i++) { 8855 var idx = G.VOM.items[i].ngy2ItemIdx; 8856 var o = G.I[idx]; 8857 var src = (o.thumbImg().src).replace(/'/g, "%27"); // replace single quote with %27 8858 src = src.replace(/\\/g, '\\\\'); // single backslashes are replaced by double backslashes 8859 t += '<div class="nGY2VThumbnail" style="width:'+tw+'px;height:'+th+'px;left:'+i*(tw+gutter*2)+'px;background-image: url(''+src+'');" data-ngy2_lightbox_thumbnail="true" data-ngy2_idx="' + idx + '" data-ngy2_vidx="' + i + '" ></div>'; 8860 } 8861 G.VOM.gallery.gwidth = (tw+2*gutter) * G.VOM.items.length; 8862 G.VOM.gallery.oneTmbWidth = tw+2*gutter; 8863 var tc = "<div class='nGY2VThumbnailContainer' style='height:"+(th+gutter*2)+"px;left:0;width:"+G.VOM.gallery.gwidth+"px;' data-ngy2_lightbox_gallery='true'>" + t + "</div>"; 8864 G.VOM.gallery.$elt = jQuery('<div class="nGY2viewerGallery" style="display: inline-block;height:'+(th+gutter*2)+'px;left:0;right:0;">'+ tc +'</div>').appendTo(G.VOM.$viewer); 8865 G.VOM.gallery.$tmbCont = G.VOM.gallery.$elt.find('.nGY2VThumbnailContainer') 8866 8867 G.VOM.gallery.Resize(); 8868 G.VOM.gallery.SetThumbnailActive(); 8869 8870 } 8871 } 8872 8873 8874 // Lightbox gesture handling 8875 function ViewerSetEvents() { 8876 8877 if( G.VOM.hammertime == null ) { 8878 8879 G.VOM.hammertime = new NGHammer.Manager(G.VOM.$baseCont[0], { 8880 // domEvents: true, 8881 recognizers: [ 8882 [NGHammer.Pinch, { enable: true }], 8883 [NGHammer.Pan, { direction: NGHammer.DIRECTION_ALL }] 8884 ] 8885 }); 8886 8887 // PAN 8888 G.VOM.hammertime.on('pan', function(ev) { 8889 if( !ViewerEvents() ) { return; } 8890 8891 8892 if( G.VOM.panMode == 'off' ) { 8893 // PAN START -> determine the element to pan 8894 if( ev.target.dataset.ngy2_lightbox_thumbnail != undefined || ev.target.dataset.ngy2_lightbox_gallery != undefined ){ 8895 G.VOM.panMode = 'gallery'; 8896 } 8897 else { 8898 if( G.VOM.zoom.isZooming ) { 8899 G.VOM.panMode = 'zoom'; 8900 } 8901 else { 8902 G.VOM.panMode = 'media'; 8903 } 8904 } 8905 } 8906 8907 // PAN the determined element 8908 switch( G.VOM.panMode ) { 8909 case 'zoom': 8910 // pan zoomed image 8911 ViewerImagePanSetPosition(G.VOM.panPosX + ev.deltaX, G.VOM.panPosY + ev.deltaY, G.VOM.content.current.$media, false); 8912 G.VOM.toolsHide(); 8913 break; 8914 8915 case 'media': 8916 if( Math.abs(ev.deltaY) > G.VOM.panThreshold && Math.abs(ev.deltaX) < G.VOM.panThreshold && !G.VOM.panXOnly ) { 8917 // pan viewer down/up to close the lightbox 8918 ViewerMediaPanX( 0 ); 8919 var dist = 0; 8920 if( ev.deltaY < 0 ) { 8921 // pan up 8922 dist = Math.max( ev.deltaY, -200); 8923 } 8924 else { 8925 // pan down 8926 dist = Math.min( ev.deltaY, 200); 8927 } 8928 G.VOM.$viewer[0].style[G.CSStransformName] = 'translateY(' + dist + 'px) '; 8929 G.VOM.$viewer.css('opacity', 1-Math.abs(dist)/200/2); 8930 } 8931 else { 8932 // pan media left/right 8933 if( Math.abs(ev.deltaX) > G.VOM.panThreshold ) { 8934 G.VOM.panXOnly = true; 8935 } 8936 ViewerMediaPanX( ev.deltaX ); 8937 G.VOM.$viewer[0].style[G.CSStransformName] = 'translateY(0px)'; 8938 G.VOM.$viewer.css('opacity', 1); 8939 } 8940 break; 8941 8942 case 'gallery': 8943 G.VOM.gallery.PanGallery( ev.deltaX ); 8944 break; 8945 } 8946 8947 }); 8948 8949 // PAN END 8950 G.VOM.hammertime.on('panend', function(ev) { 8951 if( !ViewerEvents() ) { return; } 8952 8953 switch( G.VOM.panMode ) { 8954 case 'zoom': 8955 // PAN END in image zoom mode 8956 G.VOM.timeImgChanged = new Date().getTime(); 8957 ViewerImagePanSetPosition( G.VOM.panPosX+ev.deltaX, G.VOM.panPosY+ev.deltaY, G.VOM.content.current.$media, true); 8958 break; 8959 case 'media': 8960 var panY = false; 8961 if( !G.VOM.panXOnly ) { 8962 if( Math.abs(ev.deltaY) > 50 && Math.abs(ev.deltaX) < 50 ) { 8963 // close viewer 8964 LightboxClose(); 8965 panY = true; 8966 } 8967 } 8968 if( !panY ) { 8969 if( Math.abs( ev.deltaX ) < 50 ) { 8970 ViewerMediaPanX(0); 8971 } 8972 else { 8973 ev.deltaX > 50 ? DisplayPreviousMedia( Math.abs(ev.velocityX) ) : DisplayNextMedia( Math.abs(ev.velocityX) ); 8974 } 8975 } 8976 G.VOM.panXOnly = false; 8977 break; 8978 case 'gallery': 8979 // PAN END on thumbnail gallery 8980 G.VOM.gallery.posX += ev.deltaX; 8981 G.VOM.gallery.PanGallery( 0 ); 8982 G.VOM.gallery.PanGalleryEnd( ev.velocityX ); 8983 break; 8984 } 8985 8986 G.VOM.panMode = 'off'; 8987 }); 8988 8989 8990 // ZOOM FEATURE ENABLED 8991 if( G.O.viewerZoom ) { 8992 8993 G.VOM.hammertime.add( new NGHammer.Tap({ event: 'doubletap', taps: 2, interval: 250 }) ); 8994 G.VOM.hammertime.add( new NGHammer.Tap({ event: 'singletap' }) ); 8995 G.VOM.hammertime.get('doubletap').recognizeWith('singletap'); 8996 G.VOM.hammertime.get('singletap').requireFailure('doubletap'); 8997 8998 // single tap -> next/previous media 8999 G.VOM.hammertime.on('singletap', function(ev) { 9000 9001 if( !ViewerEvents() ) { return; } 9002 9003 // Gallery on viewer -> click/touch on one thumbnail -> display corresponding image 9004 if( ev.target.dataset.ngy2_lightbox_thumbnail != undefined ){ 9005 9006 var idx = parseInt(ev.target.dataset.ngy2_idx); 9007 var vidx = parseInt(ev.target.dataset.ngy2_vidx); 9008 9009 if( !isNaN(idx) && vidx != G.VOM.content.current.vIdx ) { 9010 9011 if( vidx > G.VOM.content.current.vIdx ) { 9012 TriggerCustomEvent('lightboxNextImage'); 9013 9014 // replace the next media with selected media 9015 G.VOM.content.next.$media.empty(); 9016 var nextItem = G.I[idx]; 9017 G.VOM.content.next.vIdx = vidx; 9018 let spreloader = '<div class="nGY2ViewerMediaLoaderDisplayed"></div>'; 9019 if( nextItem.mediaKind == 'img' && nextItem.imageWidth != 0 && nextItem.imageHeight != 0 ) { 9020 spreloader = '<div class="nGY2ViewerMediaLoaderHidden"></div>'; 9021 } 9022 G.VOM.content.next.$media.append( spreloader + nextItem.mediaMarkup ); 9023 if( nextItem.mediaKind == 'img' ) { 9024 G.VOM.ImageLoader.loadImage(VieweImgSizeRetrieved, nextItem); 9025 } 9026 else { 9027 ViewerMediaCenterNotImg( G.VOM.content.next.$media ); 9028 } 9029 LightboxDisplay('nextImage'); 9030 9031 } 9032 else { 9033 TriggerCustomEvent('lightboxPreviousImage'); 9034 9035 // replace the previous media with selected media 9036 G.VOM.content.previous.$media.empty(); 9037 var previousItem = G.I[idx]; 9038 G.VOM.content.previous.vIdx = vidx; 9039 let spreloader = '<div class="nGY2ViewerMediaLoaderDisplayed"></div>'; 9040 if( previousItem.mediaKind == 'img' && previousItem.imageWidth != 0 && previousItem.imageHeight != 0 ) { 9041 spreloader = '<div class="nGY2ViewerMediaLoaderHidden"></div>'; 9042 } 9043 G.VOM.content.previous.$media.append( spreloader + previousItem.mediaMarkup ); 9044 if( previousItem.mediaKind == 'img' ) { 9045 G.VOM.ImageLoader.loadImage(VieweImgSizeRetrieved, previousItem); 9046 } 9047 else { 9048 ViewerMediaCenterNotImg( G.VOM.content.previous.$media ); 9049 } 9050 LightboxDisplay('previousImage'); 9051 } 9052 return; 9053 } 9054 } 9055 9056 9057 StopPropagationPreventDefault(ev.srcEvent); 9058 if( G.VOM.toolbarsDisplayed == false ) { 9059 debounce( ViewerToolsUnHide, 100, false)(); 9060 G.VOM.singletapTime = new Date().getTime(); 9061 } 9062 else { 9063 // toolbars are displayed -> display next/previous media 9064 if( (new Date().getTime()) - G.VOM.singletapTime < 400 ) { return; } // to avoid conflict with MOUSEMOVE event 9065 9066 if( G.VOM.content.current.NGY2Item().mediaKind == 'img' && ev.target.className.indexOf('nGY2ViewerMedia') !== -1 ) { 9067 var x =0; 9068 if( ev.srcEvent instanceof MouseEvent ) { 9069 x = ev.srcEvent.pageX; 9070 } 9071 else { 9072 x = ev.srcEvent.changedTouches[0].pageX; 9073 } 9074 if( x < (G.GOM.cache.viewport.w/2) ) { 9075 DisplayPreviousMedia(); 9076 } 9077 else { 9078 DisplayNextMedia(); 9079 } 9080 } 9081 } 9082 }); 9083 9084 // double tap -> zoom 9085 G.VOM.hammertime.on('doubletap', function(ev) { 9086 if( !ViewerEvents() ) { return; } 9087 StopPropagationPreventDefault(ev.srcEvent); 9088 9089 if( ev.target.className.indexOf('nGY2ViewerMedia') !== -1 ) { 9090 // double tap only on image 9091 if( G.VOM.zoom.isZooming ) { 9092 G.VOM.zoom.isZooming = false; 9093 // G.VOM.zoom.userFactor = 1; 9094 ResizeLightbox(true); 9095 } 9096 else { 9097 if( ViewerZoomStart() ) { 9098 G.VOM.zoom.userFactor = 1.5; 9099 ViewerMediaSetPosAndZoom(); 9100 } 9101 } 9102 } 9103 }); 9104 9105 // pinch end 9106 G.VOM.hammertime.on('pinchend', function(ev) { 9107 ev.srcEvent.stopPropagation(); 9108 ev.srcEvent.preventDefault(); // cancel mouseenter event 9109 G.VOM.timeImgChanged = new Date().getTime(); 9110 }); 9111 G.VOM.hammertime.on('pinch', function(ev) { 9112 ev.srcEvent.stopPropagation(); 9113 ev.srcEvent.preventDefault(); // cancel mouseenter event 9114 9115 if( ViewerZoomStart() ) { 9116 G.VOM.zoom.userFactor = ev.scale; 9117 ViewerZoomMax(); 9118 ViewerZoomMin(); 9119 ViewerMediaSetPosAndZoom(); // center media 9120 } 9121 }); 9122 } 9123 9124 9125 else { 9126 // ZOOM FEATURE DISABLED 9127 9128 G.VOM.hammertime.add( new NGHammer.Tap({ event: 'singletap' }) ); 9129 9130 // click/tap on image to go to next/previous one 9131 // G.VOM.hammertime.on('tap', function(ev) { 9132 G.VOM.hammertime.on('singletap', function(ev) { 9133 if( !ViewerEvents() ) { return; } 9134 StopPropagationPreventDefault( ev.srcEvent ); 9135 if( G.VOM.toolbarsDisplayed == false ){ 9136 // display tools on tap if hidden 9137 debounce( ViewerToolsUnHide, 100, false)(); 9138 G.VOM.singletapTime = new Date().getTime(); 9139 } 9140 else { 9141 // toolbars are displayed -> display next/previous media 9142 if( (new Date().getTime()) - G.VOM.singletapTime < 400 ) { return; } // to avoid conflict with MOUSEMOVE event 9143 if( ev.target.className.indexOf('nGY2ViewerMedia') !== -1 ) { 9144 var x = 0; 9145 if( ev.srcEvent instanceof MouseEvent ) { 9146 x = ev.srcEvent.pageX; 9147 } 9148 else { 9149 x = ev.srcEvent.changedTouches[0].pageX; 9150 } 9151 if( x < (G.GOM.cache.viewport.w/2) ) { 9152 DisplayPreviousMedia(); 9153 } 9154 else { 9155 DisplayNextMedia(); 9156 } 9157 } 9158 } 9159 9160 }); 9161 } 9162 } 9163 } 9164 9165 9166 function StopPropagationPreventDefault(e) { 9167 e.stopPropagation(); 9168 e.preventDefault(); 9169 } 9170 9171 // Hide toolbars on user inactivity 9172 function ViewerToolsHide() { 9173 if( G.VOM.viewerDisplayed ) { 9174 G.VOM.toolbarsDisplayed = false; 9175 ViewerToolsOpacity(0); 9176 } 9177 } 9178 9179 function ViewerToolsUnHide() { 9180 if( G.VOM.viewerDisplayed ) { 9181 G.VOM.toolbarsDisplayed = true; 9182 ViewerToolsOpacity(1); 9183 G.VOM.toolsHide(); // re-init delay before hide tools+gallery 9184 } 9185 } 9186 9187 function ViewerToolsOpacity( op ) { 9188 if( G.VOM.$toolbar != null ) { 9189 G.VOM.$toolbar.css('opacity', op); 9190 } 9191 if( G.VOM.$toolbarTL != null ) { 9192 G.VOM.$toolbarTL.css('opacity', op); 9193 } 9194 if( G.VOM.$toolbarTR != null ) { 9195 G.VOM.$toolbarTR.css('opacity', op); 9196 } 9197 9198 // next/previous 9199 G.VOM.$content.find('.nGY2ViewerAreaNext').css('opacity', op); 9200 G.VOM.$content.find('.nGY2ViewerAreaPrevious').css('opacity', op); 9201 9202 // gallery 9203 // G.VOM.gallery.$elt.css('opacity', op); 9204 } 9205 9206 9207 9208 function ViewerToolsOn() { 9209 // removes all events 9210 G.VOM.$viewer.off('touchstart click', '.ngy2viewerToolAction', ViewerToolsAction); 9211 9212 // action button 9213 G.VOM.$viewer.on('touchstart click', '.ngy2viewerToolAction', ViewerToolsAction); 9214 } 9215 9216 9217 // Actions of the button/elements 9218 function ViewerToolsAction(e) { 9219 // delay to avoid twice handling on smartphone/tablet (both touchstart click events are fired) 9220 if( (new Date().getTime()) - G.timeLastTouchStart < 300 ) { return; } 9221 G.timeLastTouchStart = new Date().getTime(); 9222 9223 var $this = $(this); 9224 var ngy2action = $this.data('ngy2action'); 9225 if( ngy2action == undefined ) { return; } 9226 switch( ngy2action ) { 9227 case 'next': 9228 StopPropagationPreventDefault(e); 9229 DisplayNextMedia(); 9230 break; 9231 case 'previous': 9232 StopPropagationPreventDefault(e); 9233 DisplayPreviousMedia(); 9234 break; 9235 case 'playPause': 9236 e.stopPropagation(); 9237 SlideshowToggle(); 9238 break; 9239 case 'zoomIn': 9240 StopPropagationPreventDefault(e); 9241 if( ViewerZoomStart() ) { ViewerZoomIn( true ); } 9242 break; 9243 case 'zoomOut': 9244 StopPropagationPreventDefault(e); 9245 if( ViewerZoomStart() ) { ViewerZoomIn( false ); } 9246 break; 9247 case 'minimize': 9248 // toggle toolbar visibility 9249 StopPropagationPreventDefault(e); 9250 if( G.VOM.toolbarMode == 'std' ) { 9251 ViewerToolbarForVisibilityMin(); 9252 } 9253 else { 9254 ViewerToolbarForVisibilityStd(); 9255 } 9256 break; 9257 case 'fullScreen': 9258 // Toggle viewer fullscreen mode on/off 9259 e.stopPropagation(); 9260 if( ngscreenfull.enabled ) { 9261 ngscreenfull.toggle(); 9262 } 9263 break; 9264 case 'info': 9265 e.stopPropagation(); 9266 ItemDisplayInfo( G.VOM.content.current.NGY2Item() ); 9267 break; 9268 case 'close': 9269 StopPropagationPreventDefault(e); 9270 if( (new Date().getTime()) - G.VOM.timeImgChanged < 400 ) { return; } 9271 LightboxClose(); 9272 break; 9273 case 'download': 9274 StopPropagationPreventDefault(e); 9275 DownloadImage(G.VOM.items[G.VOM.content.current.vIdx].ngy2ItemIdx); 9276 break; 9277 case 'share': 9278 StopPropagationPreventDefault(e); 9279 PopupShare(G.VOM.items[G.VOM.content.current.vIdx].ngy2ItemIdx); 9280 break; 9281 case 'linkOriginal': 9282 StopPropagationPreventDefault(e); 9283 OpenOriginal( G.VOM.content.current.NGY2Item() ); 9284 break; 9285 case 'rotateLeft': 9286 StopPropagationPreventDefault(e); 9287 ViewerImageRotate(-90); 9288 break; 9289 case 'rotateRight': 9290 StopPropagationPreventDefault(e); 9291 ViewerImageRotate(90); 9292 break; 9293 case 'shoppingcart': 9294 StopPropagationPreventDefault(e); 9295 AddToCart( G.VOM.items[G.VOM.content.current.vIdx].ngy2ItemIdx, 'lightbox'); 9296 break; 9297 } 9298 9299 // custom button 9300 var fu = G.O.fnImgToolbarCustClick; 9301 if( ngy2action.indexOf('custom') == 0 && fu !== null ) { 9302 typeof fu == 'function' ? fu(ngy2action, $this, G.VOM.content.current.NGY2Item() ) : window[fu](ngy2action, $this, G.VOM.content.current.NGY2Item() ); 9303 } 9304 } 9305 9306 // rotate displayed image 9307 function ViewerImageRotate( angle ) { 9308 var item = G.VOM.content.current.NGY2Item(); 9309 if( item.mediaKind == 'img' ) { 9310 item.rotationAngle += angle; 9311 item.rotationAngle = item.rotationAngle % 360; 9312 if( item.rotationAngle < 0 ) { 9313 item.rotationAngle += 360; 9314 } 9315 ViewerMediaPanX( 0 ); 9316 ViewerMediaSetPosAndZoomOne( G.VOM.content.current, true ); 9317 } 9318 } 9319 9320 9321 // Display photo infos in popup/modal 9322 function ItemDisplayInfo( ng2item ) { 9323 9324 var content = '<div class="nGY2PopupOneItem">' + ng2item.title + '</div>'; 9325 content += '<div class="nGY2PopupOneItemText">' + ng2item.description + '</div>'; 9326 if( ng2item.author != '' ) { 9327 content += '<div class="nGY2PopupOneItemText">' + G.O.icons.user + ' ' + ng2item.author + '</div>'; 9328 } 9329 if( ng2item.exif.model != '' ) { 9330 content += '<div class="nGY2PopupOneItemText">' + G.O.icons.config + ' ' + ng2item.exif.model + '</div>'; 9331 } 9332 var sexif = G.O.icons.picture + ':'; 9333 if( ng2item.exif.flash != '' || ng2item.exif.focallength != '' || ng2item.exif.fstop != '' || ng2item.exif.exposure != '' || ng2item.exif.iso != '' || ng2item.exif.time != '' ) { 9334 sexif += '<br>'; 9335 sexif += ng2item.exif.flash == '' ? '' : ' ' + ng2item.exif.flash; 9336 sexif += ng2item.exif.focallength == '' ? '' : ' ' + ng2item.exif.focallength+'mm'; 9337 sexif += ng2item.exif.fstop == '' ? '' : ' f' + ng2item.exif.fstop; 9338 sexif += ng2item.exif.exposure == '' ? '' : ' ' + ng2item.exif.exposure+'s'; 9339 sexif += ng2item.exif.iso == '' ? '' : ' ' + ng2item.exif.iso+' ISO'; 9340 if( ng2item.exif.time != '' ) { 9341 // var date = new Date(parseInt(ng2item.exif.time)); 9342 // sexif += ' '+date.toLocaleDateString(); 9343 sexif += ' ' + ng2item.exif.time; 9344 } 9345 } 9346 else { 9347 sexif += ' n/a'; 9348 } 9349 content += '<div class="nGY2PopupOneItemText">' + sexif + '</div>'; 9350 9351 if( ng2item.exif.location != '' ) { 9352 content += '<div class="nGY2PopupOneItemText">'+G.O.icons.location+' <a href="http://maps.google.com/maps?z=12&t=m&q='+encodeURIComponent(ng2item.exif.location)+'" target="_blank">'+ng2item.exif.location+'</a></div>'; 9353 // embed google map in iframe (no api key required) 9354 content += '<iframe width="300" height="150" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="https://maps.google.com/maps?&t=m&q='+encodeURIComponent( ng2item.exif.location ) +'&output=embed"></iframe>'; 9355 } 9356 else { 9357 content += '<div class="nGY2PopupOneItemText">' + G.O.icons.location + ': n/a</div>'; 9358 } 9359 9360 var r = { title: G.O.icons.viewerInfo, content: content }; 9361 9362 // callback 9363 var fu = G.O.fnPopupMediaInfo; 9364 if( fu !== null ) { 9365 typeof fu == 'function' ? r=fu(ng2item, r.title, r.content) : r=window[fu](ng2item, r.title, r.content); 9366 } 9367 9368 9369 Popup( r.title, r.content, 'Left'); 9370 9371 } 9372 9373 9374 9375 function ToolbarAddElt( elt ) { 9376 var r = '<div class="ngbt ngy2viewerToolAction ', 9377 e=elt.replace(/^\s+|\s+$/g, ''); // remove trailing/leading whitespace 9378 switch( e ) { 9379 case 'minimizeButton': 9380 case 'minimize': 9381 var ic = G.O.icons.viewerToolbarMin; 9382 if( G.VOM.toolbarMode == 'min' ) { 9383 ic = G.O.icons.viewerToolbarStd; 9384 } 9385 r += 'minimizeButton nGEvent" data-ngy2action="minimize">'+ic+'</div>'; 9386 break; 9387 case 'previousButton': 9388 case 'previous': 9389 r += 'previousButton nGEvent" data-ngy2action="previous">'+ G.O.icons.viewerPrevious +'</div>'; 9390 break; 9391 case 'pageCounter': 9392 r += 'pageCounter nGEvent"></div>'; 9393 break; 9394 case 'nextButton': 9395 case 'next': 9396 r += 'nextButton nGEvent" data-ngy2action="next">'+ G.O.icons.viewerNext +'</div>'; 9397 break; 9398 case 'playPauseButton': 9399 case 'playPause': 9400 r += 'playButton playPauseButton nGEvent" data-ngy2action="playPause">'+ G.O.icons.viewerPlay +'</div>'; 9401 break; 9402 case 'rotateLeft': 9403 r += 'rotateLeftButton nGEvent" data-ngy2action="rotateLeft">'+ G.O.icons.viewerRotateLeft +'</div>'; 9404 break; 9405 case 'rotateRight': 9406 r += 'rotateRightButton nGEvent" data-ngy2action="rotateRight">'+ G.O.icons.viewerRotateRight +'</div>'; 9407 break; 9408 case 'downloadButton': 9409 case 'download': 9410 r += 'downloadButton nGEvent" data-ngy2action="download">'+ G.O.icons.viewerDownload +'</div>'; 9411 break; 9412 case 'zoomButton': 9413 case 'zoom': 9414 r += 'nGEvent" data-ngy2action="zoomIn">'+ G.O.icons.viewerZoomIn +'</div><div class="ngbt ngy2viewerToolAction nGEvent" data-ngy2action="zoomOut">'+ G.O.icons.viewerZoomOut +'</div>'; 9415 break; 9416 case 'fullscreenButton': 9417 case 'fullscreen': 9418 var s = G.O.icons.viewerFullscreenOn; 9419 if( ngscreenfull.enabled && G.VOM.viewerIsFullscreen ) { 9420 s = G.O.icons.viewerFullscreenOff; 9421 } 9422 r += 'setFullscreenButton fullscreenButton nGEvent" data-ngy2action="fullScreen">'+s+'</div>'; 9423 break; 9424 case 'infoButton': 9425 case 'info': 9426 r += 'infoButton nGEvent" data-ngy2action="info">'+ G.O.icons.viewerInfo +'</div>'; 9427 break; 9428 case 'linkOriginalButton': 9429 case 'linkOriginal': 9430 r += 'linkOriginalButton nGEvent" data-ngy2action="linkOriginal">' + G.O.icons.viewerLinkOriginal + '</div>'; 9431 break; 9432 case 'closeButton': 9433 case 'close': 9434 r += 'closeButton nGEvent" data-ngy2action="close">'+ G.O.icons.buttonClose +'</div>'; 9435 break; 9436 case 'shareButton': 9437 case 'share': 9438 r += 'nGEvent" data-ngy2action="share">'+ G.O.icons.viewerShare +'</div>'; 9439 break; 9440 case 'label': 9441 r += '"><div class="label"><div class="title nGEvent" itemprop="name"></div><div class="description nGEvent" itemprop="description"></div></div></div>'; 9442 break; 9443 case 'shoppingcart': 9444 r += 'closeButton nGEvent" data-ngy2action="shoppingcart">'+ G.O.icons.viewerShoppingcart +'</div>'; 9445 break; 9446 default: 9447 // custom button 9448 if( e.indexOf('custom') == 0 ) { 9449 var t = ''; 9450 // content to display from custom script 9451 var fu = G.O.fnImgToolbarCustInit; 9452 if( fu !== null ) { 9453 typeof fu == 'function' ? fu(e) : window[fu](e); 9454 } 9455 if( t == undefined || t == '' ) { 9456 // content from icons 9457 var n = e.substring(6); 9458 t = G.O.icons['viewerCustomTool'+n]; 9459 } 9460 r += 'ngy2CustomBtn ' + e + ' nGEvent" data-ngy2action="' + e + '">' + t + '</div>'; 9461 } 9462 else { 9463 r = ''; 9464 } 9465 break; 9466 } 9467 return r; 9468 } 9469 9470 9471 // toggle slideshow mode on/off 9472 function SlideshowToggle(){ 9473 if( G.VOM.playSlideshow ) { 9474 window.clearTimeout(G.VOM.playSlideshowTimerID); 9475 G.VOM.playSlideshow = false; 9476 G.VOM.$viewer.find('.playPauseButton').html(G.O.icons.viewerPlay); 9477 } 9478 else { 9479 G.VOM.playSlideshow = true; 9480 DisplayNextMedia(); 9481 G.VOM.$viewer.find('.playPauseButton').html(G.O.icons.viewerPause); 9482 } 9483 } 9484 9485 function ViewerToolbarForVisibilityStd() { 9486 G.VOM.toolbarMode = 'std'; 9487 9488 var sTB = ''; 9489 var t = G.O.viewerToolbar.standard.split(','); 9490 for( var i = 0, lt = t.length; i < lt; i++) { 9491 sTB += ToolbarAddElt( t[i] ); 9492 } 9493 G.VOM.$toolbar.find('.toolbar').html(sTB); 9494 ViewerToolbarElementContent(); 9495 } 9496 9497 function ViewerToolbarForVisibilityMin() { 9498 if( G.O.viewerToolbar.minimized == undefined || G.O.viewerToolbar.minimized == '' ) { 9499 ViewerToolbarForVisibilityStd(); 9500 } 9501 else { 9502 G.VOM.toolbarMode = 'min'; 9503 var sTB = ''; 9504 var t = G.O.viewerToolbar.minimized.split(','); 9505 for( var i = 0, lt = t.length; i < lt; i++) { 9506 sTB += ToolbarAddElt( t[i] ); 9507 } 9508 G.VOM.$toolbar.find('.toolbar').html(sTB); 9509 ViewerToolbarElementContent(); 9510 } 9511 } 9512 9513 function ViewerToolbarElementContent() { 9514 9515 var vomIdx = G.VOM.content.current.vIdx; 9516 if( vomIdx == null ) { return; } 9517 9518 var item = G.VOM.content.current.NGY2Item(); 9519 9520 // LABEL 9521 var setTxt = false; 9522 // set title 9523 if( item.title !== undefined && item.title != '' ) { 9524 G.VOM.$viewer.find('.ngy2viewerToolAction').find('.title').html(item.title); 9525 setTxt = true; 9526 } 9527 else { 9528 G.VOM.$viewer.find('.ngy2viewerToolAction').find('.title').html(''); 9529 } 9530 // set description 9531 if( item.description !== undefined && item.description != '' ) { 9532 G.VOM.$viewer.find('.ngy2viewerToolAction').find('.description').html(item.description); 9533 setTxt = true; 9534 } 9535 else { 9536 G.VOM.$viewer.find('.ngy2viewerToolAction').find('.description').html(''); 9537 } 9538 9539 if( setTxt ) { 9540 G.VOM.$viewer.find('.ngy2viewerToolAction').find('.label').show(); 9541 } 9542 else { 9543 G.VOM.$viewer.find('.ngy2viewerToolAction').find('.label').hide(); 9544 } 9545 9546 // set page number 9547 var viewerMaxImages = G.VOM.items.length; 9548 if( viewerMaxImages > 0 ) { 9549 G.VOM.$viewer.find('.pageCounter').html((G.VOM.items[vomIdx].mediaNumber)+'/'+viewerMaxImages); 9550 } 9551 9552 // custom elements 9553 var $cu = G.VOM.$viewer.find('.ngy2CustomBtn'); 9554 var fu = G.O.fnImgToolbarCustDisplay; 9555 if( $cu.length > 0 && fu !== null ) { 9556 typeof fu == 'function' ? fu($cu, item) : window[fu]($cu, item); 9557 } 9558 9559 // set event handlers again 9560 ViewerToolsOn(); 9561 } 9562 9563 // Pan the media container in the lightbox (left/right) 9564 function ViewerMediaPanX( posX ) { 9565 G.VOM.swipePosX = posX; 9566 if( G.CSStransformName == null ) { 9567 // no pan if CSS transform not supported 9568 // G.VOM.$mediaCurrent.css({ left: posX }); 9569 } 9570 else { 9571 9572 // pan left/right the current media 9573 // window.ng_draf( function() { 9574 G.VOM.content.current.$media[0].style[G.CSStransformName] = 'translate(' + posX + 'px, 0px)'; 9575 // }); 9576 9577 var itemPrevious = G.VOM.content.previous.NGY2Item(); 9578 var itemNext = G.VOM.content.next.NGY2Item(); 9579 9580 // next/previous media 9581 if( G.O.imageTransition.startsWith('SWIPE') ) { 9582 if( itemPrevious.mediaTransition() ) { 9583 ViewerSetMediaVisibility(G.VOM.content.previous, 1); 9584 } 9585 if( itemNext.mediaTransition() ) { 9586 ViewerSetMediaVisibility(G.VOM.content.next, 1); 9587 } 9588 9589 var sc = Math.min( Math.max( Math.abs(posX) / G.VOM.window.lastWidth, .8), 1); 9590 if( G.O.imageTransition == 'SWIPE' ) { sc = 1; } 9591 9592 if( posX > 0 ) { 9593 let dir = G.VOM.window.lastWidth; 9594 if( itemPrevious.mediaTransition() ) { 9595 // window.ng_draf( function() { 9596 G.VOM.content.previous.$media[0].style[G.CSStransformName] = 'translate(' + (-dir + posX) + 'px, 0px) scale(' + sc + ')'; 9597 // }); 9598 } 9599 if( itemNext.mediaTransition() ) { 9600 // window.ng_draf( function() { 9601 G.VOM.content.next.$media[0].style[G.CSStransformName] = 'translate(' + (dir) + 'px, 0px) scale(' + sc + ')'; 9602 // }); 9603 } 9604 } 9605 else { 9606 let dir = -G.VOM.window.lastWidth; 9607 if( itemNext.mediaTransition() ) { 9608 // window.ng_draf( function() { 9609 G.VOM.content.next.$media[0].style[G.CSStransformName] = 'translate(' + (-dir + posX) + 'px, 0px) scale(' + sc + ')'; 9610 // }); 9611 } 9612 if( itemPrevious.mediaTransition() ) { 9613 // window.ng_draf( function() { 9614 G.VOM.content.previous.$media[0].style[G.CSStransformName] = 'translate(' + (dir) + 'px, 0px) scale(' + sc + ')'; 9615 // }); 9616 } 9617 } 9618 } 9619 9620 9621 if( G.O.imageTransition == 'SLIDEAPPEAR' ) { 9622 G.VOM.content.previous.$media[0].style[G.CSStransformName] = ''; 9623 G.VOM.content.next.$media[0].style[G.CSStransformName] = ''; 9624 if( posX < 0 ) { 9625 let o = (-posX) / G.VOM.window.lastWidth; 9626 if( itemNext.mediaTransition() ) { 9627 ViewerSetMediaVisibility(G.VOM.content.next, o); 9628 } 9629 if( itemPrevious.mediaTransition() ) { 9630 ViewerSetMediaVisibility(G.VOM.content.previous, 0); 9631 } 9632 } 9633 else { 9634 let o = posX / G.VOM.window.lastWidth; 9635 if( itemPrevious.mediaTransition() ) { 9636 ViewerSetMediaVisibility(G.VOM.content.previous, o); 9637 } 9638 if( itemNext.mediaTransition() ) { 9639 ViewerSetMediaVisibility(G.VOM.content.next, 0); 9640 } 9641 } 9642 } 9643 } 9644 } 9645 9646 // Display next image 9647 function DisplayNextMedia( velocity ) { 9648 velocity = velocity || 0; 9649 9650 if( G.VOM.viewerMediaIsChanged || ((new Date().getTime()) - G.VOM.timeImgChanged < 300) ) { return; } 9651 9652 TriggerCustomEvent('lightboxNextImage'); 9653 LightboxDisplay('nextImage', velocity); 9654 }; 9655 9656 // Display previous image 9657 function DisplayPreviousMedia( velocity ) { 9658 velocity = velocity || 0; 9659 9660 if( G.VOM.viewerMediaIsChanged || ((new Date().getTime()) - G.VOM.timeImgChanged < 300) ) { return; } 9661 if( G.VOM.playSlideshow ) { 9662 SlideshowToggle(); 9663 } 9664 9665 TriggerCustomEvent('lightboxPreviousImage'); 9666 LightboxDisplay( 'previousImage', velocity); 9667 }; 9668 9669 9670 9671 // Display image (with animation if possible) 9672 function LightboxDisplay( displayType, velocity ) { 9673 9674 velocity = velocity || 0; 9675 9676 if( G.O.debugMode && console.timeline ) { console.timeline('nanogallery2_viewer'); } 9677 9678 if( G.VOM.playSlideshow ) { window.clearTimeout( G.VOM.playSlideshowTimerID ); } 9679 9680 var current_content_item = null; 9681 var new_content_item = null; 9682 9683 G.VOM.timeImgChanged = new Date().getTime(); 9684 G.VOM.viewerMediaIsChanged = true; 9685 G.VOM.zoom.isZooming = false; 9686 ResizeLightbox(true); 9687 9688 switch( displayType ) { 9689 case '': 9690 current_content_item = G.VOM.content.current; 9691 new_content_item = G.VOM.content.current; 9692 break; 9693 case 'previousImage': 9694 current_content_item = G.VOM.content.current; 9695 new_content_item = G.VOM.content.previous; 9696 break; 9697 default: 9698 current_content_item = G.VOM.content.current; 9699 new_content_item = G.VOM.content.next; 9700 } 9701 9702 // SetLocationHash( next_ng2item.albumID, next_ng2item.GetID() ); 9703 SetLocationHash( new_content_item.NGY2Item().albumID, new_content_item.NGY2Item().GetID() ); 9704 9705 if( displayType == '' ) { 9706 // first media -> no transition -> exit 9707 return; 9708 } 9709 9710 // animation duration is proportional of the remaining distance 9711 var vP = G.GOM.cache.viewport; 9712 var t_easing = ''; 9713 var t_dur = 500 * (vP.w - Math.abs(G.VOM.swipePosX)) / vP.w; 9714 if( velocity > 0 ) { 9715 // velocity = pixels/millisecond 9716 t_dur = Math.min( (vP.w - Math.abs(G.VOM.swipePosX)) / velocity, t_dur); 9717 t_easing = 'linear'; // use linear to avoid a slow-down in the transition after user swipe 9718 } 9719 9720 9721 // animate the image transition between 2 medias 9722 9723 if( G.CSStransformName == null ) { 9724 // no CSS transform support -> no animation 9725 ViewerSetMediaVisibility(new_content_item, 1); 9726 ViewerSetMediaVisibility(current_content_item, 1); 9727 LightboxDisplayFinalize(displayType); 9728 } 9729 else { 9730 switch( G.O.imageTransition ) { 9731 case 'SWIPE': 9732 case 'SWIPE2': 9733 var dir = ( displayType == 'nextImage' ? - vP.w : vP.w ); 9734 new_content_item.$media[0].style[G.CSStransformName] = 'translate(' + (-dir) + 'px, 0px) ' 9735 9736 if( velocity == 0 ) { 9737 t_easing = G.O.imageTransition == 'swipe' ? 'easeInOutSine' : 'easeOutCubic'; 9738 } 9739 9740 ViewerSetMediaVisibility(G.VOM.content.current, 1); 9741 G.VOM.content.current.$media[0].style[G.CSStransformName] = 'translate(0px, 0px)'; 9742 ViewerSetMediaVisibility(new_content_item, 1); 9743 9744 new NGTweenable().tween({ 9745 from: { t: G.VOM.swipePosX }, 9746 to: { t: (displayType == 'nextImage' ? - vP.w : vP.w) }, 9747 attachment: { dT: displayType, new_content_item: new_content_item, dir: dir, media_transition: new_content_item.NGY2Item().mediaTransition()}, 9748 // delay: 30, 9749 duration: t_dur, 9750 easing: ( t_easing ), 9751 step: function (state, att) { 9752 // current displayed media 9753 G.VOM.content.current.$media[0].style[G.CSStransformName] = 'translate(' + state.t + 'px, 0px)'; 9754 9755 // new media 9756 if( att.media_transition ) { 9757 // new media supports transition 9758 var sc = Math.min( Math.max( (Math.abs(state.t)) / G.VOM.window.lastWidth, .8), 1); 9759 if( G.O.imageTransition == 'SWIPE' ) { sc = 1; } 9760 att.new_content_item.$media[0].style[G.CSStransformName] = 'translate(' + (-att.dir+state.t) + 'px, 0px) scale(' + sc + ')'; 9761 } 9762 }, 9763 finish: function (state, att) { 9764 G.VOM.content.current.$media[0].style[G.CSStransformName] = ''; 9765 ViewerSetMediaVisibility(G.VOM.content.current, 0); 9766 att.new_content_item.$media[0].style[G.CSStransformName] = ''; 9767 LightboxDisplayFinalize(att.dT); 9768 } 9769 }); 9770 break; 9771 9772 case 'SLIDEAPPEAR': 9773 default: 9774 // var dir=(displayType == 'nextImage' ? - vP.w : vP.w); 9775 var op = (Math.abs(G.VOM.swipePosX)) / G.VOM.window.lastWidth; 9776 new_content_item.$media[0].style[G.CSStransformName] = ''; 9777 if( velocity == 0 ) { 9778 t_easing ='easeInOutSine'; 9779 } 9780 new NGTweenable().tween({ 9781 from: { o: op, t: G.VOM.swipePosX }, 9782 to: { o: 1, t: (displayType == 'nextImage' ? - vP.w : vP.w) }, 9783 attachment: { dT: displayType, new_content_item:new_content_item, media_transition: new_content_item.NGY2Item().mediaTransition() }, 9784 delay: 30, 9785 duration: t_dur, 9786 easing: t_easing, 9787 step: function (state, att) { 9788 // current media - translate 9789 G.VOM.content.current.$media[0].style[G.CSStransformName]= 'translate('+state.t+'px, 0px)'; 9790 9791 // new media - opacity 9792 if( att.media_transition ) { 9793 // new media supports transition 9794 ViewerSetMediaVisibility(att.new_content_item, state.o); 9795 } 9796 }, 9797 finish: function (state, att) { 9798 G.VOM.content.current.$media[0].style[G.CSStransformName]= ''; 9799 LightboxDisplayFinalize(att.dT); 9800 } 9801 }); 9802 break; 9803 } 9804 } 9805 9806 } 9807 9808 9809 function LightboxDisplayFinalize( displayType ) { 9810 9811 var newVomIdx = 0; 9812 switch( displayType ) { 9813 case '': 9814 // first media to display in lightbox 9815 newVomIdx = G.VOM.content.current.vIdx; 9816 break; 9817 case 'previousImage': 9818 // previous media 9819 newVomIdx = G.VOM.content.previous.vIdx; 9820 break; 9821 default: 9822 // next media 9823 newVomIdx = G.VOM.content.next.vIdx; 9824 } 9825 9826 9827 9828 G.VOM.content.current.vIdx = newVomIdx; 9829 G.VOM.content.next.vIdx = G.VOM.IdxNext(); 9830 G.VOM.content.previous.vIdx = G.VOM.IdxPrevious(); 9831 G.VOM.gallery.Resize(); 9832 G.VOM.gallery.SetThumbnailActive(); 9833 9834 var ngy2item = G.VOM.content.current.NGY2Item(); 9835 9836 ViewerToolbarElementContent(); 9837 if( G.O.debugMode && console.timeline ) { console.timelineEnd('nanogallery2_viewer'); } 9838 9839 var fu=G.O.fnImgDisplayed; 9840 if( fu !== null ) { 9841 typeof fu == 'function' ? fu(ngy2item) : window[fu](ngy2item); 9842 } 9843 9844 G.VOM.swipePosX = 0; 9845 if( displayType != '' ) { 9846 // not on first media display 9847 G.VOM.content.current.$media.removeClass('imgCurrent'); 9848 9849 var $tmp = G.VOM.content.current.$media; 9850 switch( displayType ) { 9851 case 'nextImage': 9852 G.VOM.content.current.$media = G.VOM.content.next.$media; 9853 G.VOM.content.next.$media = $tmp; 9854 break; 9855 case 'previousImage': 9856 G.VOM.content.current.$media = G.VOM.content.previous.$media; 9857 G.VOM.content.previous.$media = $tmp; 9858 break; 9859 } 9860 } 9861 9862 G.VOM.content.current.$media.addClass('imgCurrent'); 9863 9864 // re-sort the media containers --> current on top 9865 var $pans = G.VOM.$content.find('.nGY2ViewerMediaPan'); 9866 G.VOM.content.current.$media.insertAfter($pans.last()); 9867 9868 if( ngy2item.mediaKind == 'img' && ngy2item.imageWidth == 0 ) { 9869 ViewerSetMediaVisibility(G.VOM.content.current, 0); 9870 } 9871 else { 9872 G.VOM.content.current.$media.children().eq(0).attr('class', 'nGY2ViewerMediaLoaderHidden'); // hide preloader 9873 ViewerSetMediaVisibility(G.VOM.content.current, 1); 9874 } 9875 9876 9877 // set the new NEXT media 9878 G.VOM.content.next.$media.empty(); 9879 var nextItem = G.VOM.content.next.NGY2Item(); 9880 var spreloader = '<div class="nGY2ViewerMediaLoaderDisplayed"></div>'; 9881 if( nextItem.mediaKind == 'img' && nextItem.imageWidth != 0 && nextItem.imageHeight != 0 ) { 9882 spreloader = '<div class="nGY2ViewerMediaLoaderHidden"></div>'; 9883 } 9884 G.VOM.content.next.$media.append( spreloader + nextItem.mediaMarkup ); 9885 ViewerSetMediaVisibility(G.VOM.content.next, 0); 9886 ViewerSetMediaVisibility(G.VOM.content.previous, 0); 9887 if( nextItem.mediaKind == 'img' ) { 9888 G.VOM.ImageLoader.loadImage(VieweImgSizeRetrieved, nextItem); 9889 } 9890 else { 9891 ViewerMediaCenterNotImg( G.VOM.content.next.$media ); 9892 } 9893 9894 // set the new PREVIOUS media 9895 G.VOM.content.previous.$media.empty(); 9896 var previousItem = G.VOM.content.previous.NGY2Item(); 9897 spreloader = '<div class="nGY2ViewerMediaLoaderDisplayed"></div>'; 9898 if( previousItem.mediaKind == 'img' && previousItem.imageWidth != 0 && previousItem.imageHeight != 0 ) { 9899 spreloader = '<div class="nGY2ViewerMediaLoaderHidden"></div>'; 9900 } 9901 G.VOM.content.previous.$media.append( spreloader + previousItem.mediaMarkup ); 9902 ViewerSetMediaVisibility(G.VOM.content.previous, 0); 9903 ViewerSetMediaVisibility(G.VOM.content.next, 0); 9904 if( previousItem.mediaKind == 'img' ) { 9905 G.VOM.ImageLoader.loadImage( VieweImgSizeRetrieved, previousItem ); 9906 } 9907 else { 9908 ViewerMediaCenterNotImg( G.VOM.content.previous.$media ); 9909 } 9910 9911 9912 // slideshow mode - wait until image is loaded to start the delay for next image 9913 if( G.VOM.playSlideshow ) { 9914 G.VOM.content.current.$media.children().eq(1).ngimagesLoaded().always( function( instance ) { 9915 if( G.VOM.playSlideshow ) { 9916 // in the meantime the user could have stopped the slideshow 9917 G.VOM.playSlideshowTimerID = window.setTimeout( function(){ DisplayNextMedia(); }, G.VOM.slideshowDelay ); 9918 } 9919 }); 9920 } 9921 9922 // close viewer when user clicks outside of the image 9923 // G.VOM.$mediaCurrent.on("click",function(e){ 9924 // e.stopPropagation(); 9925 // if( (new Date().getTime()) - G.VOM.timeImgChanged < 400 ) { return; } 9926 // StopPropagationPreventDefault(e); 9927 // LightboxClose(G.VOM.currItemIdx); 9928 // return false; 9929 // }); 9930 9931 ResizeLightbox(); 9932 9933 G.VOM.viewerMediaIsChanged = false; 9934 TriggerCustomEvent('lightboxImageDisplayed'); 9935 9936 } 9937 9938 9939 // Is fired as soon as the size of an image has been retrieved (the download may not be finished) 9940 function VieweImgSizeRetrieved(w, h, item, n) { 9941 9942 item.imageWidth = w; 9943 item.imageHeight = h; 9944 9945 // image sized retrieved for currently displayed media 9946 if( G.VOM.content.current.NGY2Item() == item ) { 9947 // it is the current displayed media 9948 G.VOM.content.current.$media.children().eq(0).attr('class', 'nGY2ViewerMediaLoaderHidden'); // hide preloader 9949 ViewerSetMediaVisibility(G.VOM.content.current, 1); 9950 G.VOM.zoom.userFactor = 1; 9951 } 9952 9953 if( G.VOM.content.next.NGY2Item() == item ) { // next media 9954 G.VOM.content.next.$media.children().eq(0).attr('class', 'nGY2ViewerMediaLoaderHidden'); // hide preloader 9955 } 9956 if( G.VOM.content.previous.NGY2Item() == item ) { // previous media 9957 G.VOM.content.previous.$media.children().eq(0).attr('class', 'nGY2ViewerMediaLoaderHidden'); // hide preloader 9958 } 9959 9960 ViewerMediaSetPosAndZoom(); 9961 9962 } 9963 9964 // Viewer - Set the visibility of the media and it's container 9965 // function ViewerSetMediaVisibility(item, $media, opacity ) { 9966 function ViewerSetMediaVisibility( content_item, opacity ) { 9967 9968 var item = content_item.NGY2Item(); 9969 var $media = content_item.$media; 9970 9971 if( item.mediaKind == 'img' && item.imageWidth == 0 ) { 9972 // do not display image if width is unknown (--> callback will set the width when know) 9973 // setting opacity to 0 is not enough -> it is mandatory to change also the visibility to hidden to avoid responds to events (click/touch) 9974 // $media.children().css({ opacity: 0, visibility: 'hidden' }); 9975 $media.children().eq(1).css({ opacity: 0, visibility: 'hidden' }); // hide media 9976 // $media.css({ opacity: 0, visibility: 'hidden' }); 9977 return; 9978 } 9979 9980 if( opacity == 0 ) { 9981 // setting opacity to 0 is not enough -> it is mandatory to change also the visibility to hidden to avoid responds to events (click/touch) 9982 // $media.css({ opacity: 0, visibility: 'hidden' }); 9983 $media.children().css({ opacity: 0, visibility: 'hidden' }); // hide media 9984 } 9985 else { 9986 // $media.css({ opacity: opacity, visibility: 'visible' }); 9987 $media.children().css({ opacity: opacity, visibility: 'visible' }); // display media 9988 } 9989 } 9990 9991 9992 // Close the internal lightbox 9993 function LightboxClose( vomIdx ) { 9994 9995 9996 if( vomIdx == undefined ) { 9997 vomIdx = G.VOM.content.current.vIdx; 9998 } 9999 10000 G.VOM.viewerMediaIsChanged = false; 10001 10002 if( G.VOM.viewerDisplayed ) { 10003 10004 // set scrollbar visible again 10005 jQuery("body").removeClass("nGY2_body_scrollbar"); 10006 jQuery("#nGY2_body_scrollbar_style").remove(); 10007 10008 if( G.VOM.playSlideshow ) { 10009 window.clearTimeout( G.VOM.playSlideshowTimerID ); 10010 G.VOM.playSlideshow = false; 10011 } 10012 10013 G.VOM.hammertime.destroy(); 10014 G.VOM.hammertime = null; 10015 10016 if( ngscreenfull.enabled && G.VOM.viewerIsFullscreen ) { 10017 G.VOM.viewerIsFullscreen = false; 10018 ngscreenfull.exit(); 10019 } 10020 10021 // G.VOM.$baseCont.hide(0).off().show(0).html('').remove(); 10022 // G.VOM.$baseCont.remove(); // does not work... (?) 10023 jQuery('.nGY2ViewerContainer').remove(); 10024 G.VOM.$baseCont = null; 10025 G.VOM.viewerDisplayed = false; 10026 10027 if( G.O.lightboxStandalone ) { return; } 10028 10029 if( G.O.thumbnailAlbumDisplayImage ) { 10030 // content of album displayed directly in lightbox (no gallery display for album content) 10031 if( vomIdx == null ) { 10032 // lightbox closed with browser back-button 10033 // the gallery is already displayed 10034 } 10035 else { 10036 var item = G.I[G.VOM.items[vomIdx].ngy2ItemIdx]; 10037 var parent = NGY2Item.Get(G, item.albumID); 10038 if( G.GOM.albumIdx != parent.albumID ) { 10039 // display album only if not already displayed 10040 DisplayAlbum('-1', parent.albumID); 10041 } 10042 else { 10043 GalleryResize(); 10044 SetLocationHash( '', '' ); 10045 ThumbnailHoverReInitAll(); 10046 } 10047 } 10048 // DisplayAlbum( '-', G.I[G.VOM.items[vomIdx].ngy2ItemIdx].albumID ); 10049 } 10050 else { 10051 if( vomIdx != null ) { 10052 if( G.GOM.albumIdx == -1 ) { 10053 // album not displayed --> display gallery 10054 DisplayAlbum( '', G.I[G.VOM.items[vomIdx].ngy2ItemIdx].albumID ); 10055 } 10056 else { 10057 GalleryResize(); 10058 SetLocationHash( G.I[G.VOM.items[vomIdx].ngy2ItemIdx].albumID, '' ); 10059 ThumbnailHoverReInitAll(); 10060 } 10061 } 10062 } 10063 G.VOM.timeImgChanged = new Date().getTime(); 10064 } 10065 } 10066 10067 10068 // Lightbox resized -> reposition elements 10069 function ResizeLightbox( forceUpdate ) { 10070 forceUpdate = typeof forceUpdate !== 'undefined' ? forceUpdate : false; 10071 10072 if( G.VOM.$toolbar === null ) { return; } // viewer build not finished 10073 10074 10075 // window.requestAnimationFrame( function() { // synchronize with screen 10076 var windowsW = G.VOM.$viewer.width(); 10077 var windowsH = G.VOM.$viewer.height(); 10078 var $elt = G.VOM.content.current.$media.children().eq(1); 10079 if( $elt == null || G.VOM.content.current.vIdx == -1 ) { return; } 10080 10081 if( !forceUpdate && G.VOM.window.lastWidth == windowsW && G.VOM.window.lastHeight == windowsH ) { return; } 10082 10083 G.VOM.window.lastWidth = windowsW; 10084 G.VOM.window.lastHeight = windowsH; 10085 10086 // var $tb = G.VOM.$toolbar.find('.toolbar'); 10087 // var tb_OHt = $tb.outerHeight(true); 10088 10089 10090 var galleryHeight = 0; 10091 var cBottom = 0; 10092 // Height of the thumbnails gallery 10093 if( G.O.viewerGallery != 'none' ) { 10094 galleryHeight = G.O.viewerGalleryTHeight + 10; 10095 } 10096 if( G.O.viewerGallery == 'bottom' ) { 10097 cBottom = galleryHeight; 10098 } 10099 10100 10101 switch( G.O.viewerToolbar.position ) { 10102 case 'top': 10103 case 'topOverImage': 10104 G.VOM.$content.css({height: windowsH, width: windowsW, top: 0 }); 10105 G.VOM.$toolbar.css({top: 0, bottom: ''}); 10106 break; 10107 // case 'top': 10108 // windowsH -= tb_OHt; 10109 // G.VOM.$content.css({height: windowsH, width: windowsW, top: tb_OHt }); 10110 // G.VOM.$toolbar.css({top: 0}); 10111 // break; 10112 case 'bottom': 10113 case 'bottomOverImage': 10114 default: 10115 windowsH -= cBottom; 10116 G.VOM.$content.css({height: windowsH, width: windowsW, bottom: -cBottom, top: 0 }); 10117 G.VOM.$toolbar.css({bottom: galleryHeight}); 10118 break; 10119 // case 'bottom': 10120 // default: 10121 // windowsH -= tb_OHt; 10122 // G.VOM.$content.css({ width: windowsW, top: 0, bottom: tb_OHt }); 10123 // G.VOM.$toolbar.css({bottom: galleryHeight}); 10124 // break; 10125 } 10126 10127 10128 if( !G.VOM.viewerMediaIsChanged && G.VOM.zoom.isZooming ) { 10129 ViewerMediaSetPosAndZoom(); 10130 } 10131 else { 10132 if( !G.VOM.zoom.isZooming && ( G.VOM.zoom.userFactor != 0 || G.VOM.panPosX != 0 || G.VOM.panPosY != 0 || G.VOM.zoom.posX != 0 || G.VOM.zoom.posY != 0 )) { 10133 // animate image zoom factor and position back to initial values 10134 G.VOM.zoom.isZooming= true; // activate zooming temporarily 10135 new NGTweenable().tween({ 10136 from: { userFactor: G.VOM.zoom.userFactor, panPosX: G.VOM.panPosX, panPosY: G.VOM.panPosY, zoomPosX: G.VOM.zoom.posX, zoomPosY: G.VOM.zoom.posY }, 10137 to: { userFactor: 1, panPosX: 0, panPosY: 0, zoomPosX: 0, zoomPosY: 0 }, 10138 easing: 'easeInOutSine', 10139 delay: 0, 10140 duration: 150, 10141 step: function (state) { 10142 G.VOM.zoom.userFactor = state.userFactor; 10143 G.VOM.panPosX = state.panPosX; 10144 G.VOM.panPosY = state.panPosY; 10145 G.VOM.zoom.posX = state.zoomPosX; 10146 G.VOM.zoom.posY = state.zoomPosY; 10147 ViewerMediaSetPosAndZoom(); 10148 }, 10149 finish: function (state) { 10150 G.VOM.zoom.isZooming=false; 10151 } 10152 }); 10153 10154 } 10155 else { 10156 G.VOM.zoom.userFactor = 1; 10157 G.VOM.zoom.isZooming = false; 10158 G.VOM.panPosX = 0; 10159 G.VOM.panPosY = 0; 10160 G.VOM.zoom.posX = 0; 10161 G.VOM.zoom.posY = 0; 10162 ViewerMediaSetPosAndZoom(); 10163 } 10164 } 10165 } 10166 10167 // Retrieve the first parent element which is scrollable 10168 // source: ncubica - https://stackoverflow.com/questions/35939886/find-first-scrollable-parent 10169 // returns null if nothing found 10170 function getScrollableParent (node) { 10171 const regex = /(auto|scroll)/; 10172 const parents = (_node, ps) => { 10173 if (_node.parentNode === null) { return ps; } 10174 return parents(_node.parentNode, ps.concat([_node])); 10175 }; 10176 10177 const style = (_node, prop) => getComputedStyle(_node, null).getPropertyValue(prop); 10178 const overflow = _node => style(_node, 'overflow') + style(_node, 'overflow-y') + style(_node, 'overflow-x'); 10179 const scroll = _node => regex.test(overflow(_node)); 10180 10181 const scrollParent = (_node) => { 10182 if (!(_node instanceof HTMLElement || _node instanceof SVGElement)) { 10183 return undefined; 10184 } 10185 10186 const ps = parents(_node.parentNode, []); 10187 10188 for (let i = 0; i < ps.length; i += 1) { 10189 if( ps[i] === document.body ) { 10190 return null; 10191 } 10192 if (scroll(ps[i])) { 10193 return ps[i]; 10194 } 10195 } 10196 10197 return document.scrollingElement || document.documentElement; 10198 }; 10199 10200 return scrollParent(node); 10201 }; 10202 10203 10204 10205 /** @function BuildSkeleton */ 10206 /** Build the gallery structure **/ 10207 function BuildSkeleton() { 10208 10209 10210 // store markup if defined 10211 // var $elements = G.$E.base.children('a'); 10212 var $elements = G.$E.base.children(); 10213 if( $elements.length > 0 ) { 10214 G.O.$markup = $elements; 10215 } 10216 10217 if( !G.O.lightboxStandalone ) { 10218 G.$E.base.text(''); 10219 G.$E.base.addClass('ngy2_container'); 10220 10221 // RTL or LTR 10222 // var sRTL=''; 10223 // if( G.O.RTL ) { 10224 // sRTL = 'style="text-align:right;direction:rtl;"'; 10225 // } 10226 10227 // theme 10228 G.$E.base.addClass(G.O.theme) 10229 // gallery color scheme 10230 SetGalleryTheme(); 10231 10232 // Hide icons (thumbnails and breadcrumb) 10233 if( G.O.thumbnailLabel.get('hideIcons') ) { 10234 G.O.icons.thumbnailAlbum = ''; 10235 G.O.icons.thumbnailImage = ''; 10236 } 10237 10238 // Navigation bar 10239 var styleNavigation=""; 10240 if( G.O.navigationFontSize != undefined && G.O.navigationFontSize != '' ) { 10241 styleNavigation=' style="font-size:'+G.O.navigationFontSize+';"'; 10242 } 10243 G.$E.conNavigationBar = jQuery('<div class="nGY2Navigationbar" '+styleNavigation+'></div>').appendTo(G.$E.base); 10244 10245 // pre-loader 10246 G.$E.conLoadingB = jQuery('<div class="nanoGalleryLBarOff"><div></div><div></div><div></div><div></div><div></div></div>').appendTo(G.$E.base); 10247 10248 // gallery 10249 G.$E.conTnParent = jQuery('<div class="nGY2Gallery"></div>').appendTo( G.$E.base ); 10250 G.$E.conTn = jQuery('<div class="nGY2GallerySub"></div>').appendTo( G.$E.conTnParent ); 10251 10252 // configure gallery 10253 switch( G.O.thumbnailAlignment ) { 10254 case 'left': 10255 G.$E.conTnParent.css({'text-align':'left'}); 10256 // G.$E.conNavBCon.css({'margin-left':0 }); 10257 break; 10258 case 'right': 10259 G.$E.conTnParent.css({'text-align':'right'}); 10260 // G.$E.conNavBCon.css({ 'margin-right':0}); 10261 break; 10262 } 10263 10264 // apply galleryBuildInit2 css settings to the gallery 10265 if( G.O.galleryBuildInit2 !== undefined ) { 10266 var t1=G.O.galleryBuildInit2.split('|'); 10267 for( var i=0; i<t1.length; i++ ) { 10268 var o1=t1[i].split('_'); 10269 if( o1.length == 2 ) { 10270 G.$E.conTn.css(o1[0], o1[1]); 10271 } 10272 } 10273 } 10274 10275 // configure gallery depending on some thumbnail hover effects 10276 var effects=G.tn.hoverEffects.std.concat(G.tn.hoverEffects.level1); 10277 for( var j=0; j<effects.length; j++) { 10278 switch( effects[j].type ) { 10279 case 'scale': 10280 case 'rotateZ': 10281 case 'rotateX': 10282 case 'rotateY': 10283 case 'translateX': 10284 case 'translateY': 10285 // handle some special cases 10286 if( effects[j].element == '.nGY2GThumbnail' ) { 10287 // allow thumbnail upscale over the gallery's aera 10288 G.$E.base.css('overflow', 'visible'); 10289 G.$E.base.find('.nGY2GallerySub').css('overflow', 'visible'); 10290 G.$E.conTnParent.css('overflow', 'visible'); 10291 } 10292 break; 10293 } 10294 } 10295 10296 // Gallery bottom container
10297 G.$E.conTnBottom = jQuery('<div class="nGY2GalleryBottom" '+styleNavigation+'></div>').appendTo( G.$E.conTnParent ); 10298 10299 // portable edition 10300 if( G.O.portable ) { 10301 // http://www.picresize.com/ 10302 // http://base64encode.net/base64-image-encoder 10303 // var logo='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB8AAAAWCAYAAAA4oUfxAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QA/wD/AP+gvaeTAAAAB3RJTUUH4QMPBwY6mxZgsAAABTFJREFUSMe1ll9oVGcaxn/fd86ZSWbSkEBMiWNdTTfRxiVbXFiU1bjKGqNexlURKys0tHqXpQZ64Sq4FxKqFy4qFSm9kA1FHNhFISgJqFCd6lL/YC7M3jhrJv5JmGSSMzPnzDnfuxdpZtP4b1vaF154P3gPD+/zPC/nVSKiAQOsBj7O5XK/nZiYeEtELH6iUEqFNTU1U9XV1d8AnwNfA1qJCMCfHz169NcjR45UXL16VWWzWQnD0PxU4JZl6draWtXW1iYHDx4sLlmy5C/AZwRB0JVOpyWRSHhACMjPmOHChQuL6XRagiDoUiIyumvXrpq+vr6obduqs7OTjRvbsbSFUgqUgKjyFG5+mlKpVH6LCMYYRAQRQSmF1hqtNd+xijGGVCpFMpkkCALZuXOn19fXN6Gmp6dNc3NzMDo66nR2dnL+/Hm+Ov933PwUAPHKagqei4gBFNs7dxGPx38U/du2bSOZTNLQ0FB6+PChbWez2WI+n3dEhI3tf+Det0N8de0Imz9YQWHa48u/3afjgxbqEpUM/es/uF8W+fijffi+TywWQ0S4fv06t2/fJpfLsXjxYtauXUtTUxNBECAihGFIJBJh1apVXLhwgXw+r7LZbNGeYU7MLD1BEPCLxkWs+HUT+SmPJY0TvPerd6l/J05YcLCGHWzbxrZtHjx4wP79+7l27dr3Jqyurqarq4ujR49i2zYAWmvCMJyVygCiZ7dh9kOtNb5XopD3KBQ8fL9EseBRyHsUCz6zS3Dnzh3WrVtXBq6oqGDBggUA5HI5jh07xo4dOzDmf0ujVBlGAWjmhTGC41hEow6RiI3j2DgRh0jUxonYWJaFGGHPnj2Mj49jWRYHDhzg7t27DA0NMTAwwOrVqwFIJpOcOHECx3Fe6oEXwG3bYux5ltHHz3mSGePpk+c8yczUI+knVFVVcePmDe7fvw9AT08Pvb29NDc3U1dXx4YNG7h8+TItLS1orTl58iT5fL68Ga8En55yWb6iifff/iPD/0iQGfglG3/zJ6a+beHf/3yH6Mjv+P269Vy5cgWlFDU1NXR3dxOGYdlcnudRVVXFvn37MMaQTqcZHh5+Kbg99zHjSodPuj997cqMjY0hItTW1hKPx9FalzW1LIswDFm0aBEAQRDguu6bJ581hOd5GBNiTEgYhuXa8z1EhIaGBgAymQzpdBqlFKVSiTCc6bcsi5s3bwJQWVlJfX39fMO9XHMAy7LQeibn1o7toJSio6MDAN/36e7uxvd9IpEIlmURjUZJpVKcOXMGpRStra0sXbr0peDfo30+LS+4U2uMMaxcuZLdu3dz7tw5+vv7aWtrY+/evdTX13Pr1i1OnTrF5OQkAIcPH8ayrNeCvx51njTGGE6fPk0mk2FwcJBUKkUqlXqh9/jx42zatKnMzJzhBEArpZT+zjGWZSEiBEHwypzVtbKykosXL3Lo0CEaGxvLpovFYqxZs4ZLly6VJQnDEBEpM6C11kopheu6JpFI+Fpr2bJli/zYGBkZkeHhYZmcnHxlz9atW0VrLYlEwndd19ixWOzx5s2b3z579qzp7+/X7e3ttLa2Yox5QaP5MfenEY1G0VoTBAHFYhFjTJlJrTX37t1jYGAAY4zp6OiQWCz2mCAItj979kyWL1/uAwE/7zERLFu2zH/69KkEQbB99ozaOz4+fqy3t7d2cHAwdF1XKaXe6P7/16AiQjwel/Xr1+uenp6Jurq6T4Av1JwD8j3gQ2BVsVh8S72J8x8QIiIVFRVTQAo4CwwB+r93qCLI9wKZ8AAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxNy0wMy0xNVQwNzowNjo1OC0wNDowMBNQsyUAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTctMDMtMTVUMDc6MDY6NTgtMDQ6MDBiDQuZAAAAAElFTkSuQmCC'; 10304 var logo = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAYCAYAAACbU/80AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QA/wD/AP+gvaeTAAAAB3RJTUUH4QgDBCAWVVC/hwAABRxJREFUSMetll9oVFcexz/nnDvJRBmSzWTrmD9uNGZsHta0/qFIFQTxRcnCBgTFNlX0YR8W+1AK9lGwCBJYgn0KKr5136S4gpUQTR4caJRslcxYWV3iaphQapJJppO5957z60Mmk4mN1q75wg/OPefc+/v9vt/fueenKEFEqICqsNWAVNiCA7XwaS0iZeejo6OIiCltdIBdJXMLOYp5/PjxsoTVS5nr0mYDJIE/lObeBhaYAn4oJbboAwBvBedHJicnPx8YGGh/8eJF1dvKoJSShoYGf//+/Zl4PP4l8M2yIEoSLErx6c2bN6W1tXVRglWzLVu2SCqVEhE5LiI457SIoEREW2udMaZtcnLy+2QyWZ3L5XRHR4f+4MNdoBUahUJhcWilmZ/NE4ZhOQHn3LIi1lqjtS6vjY6O8uTJE9vc3MyDBw+mYrHYn0Uk63me8gCtlHLA7uHh4bW5XC7oePddPTQ8xHffDjM/PYe3thqMws35iAcHPj5ENBp9Yxmy2Sw7d+40z549C+7du9ewb9++D6y13wDaK+kE0DAzMyNKKbXtvfd5EfzM+Ef/4C+8x23+wzPm+IhtfMf3/Ksuyl+7u9FaY63l+vXrpFIpCoUCmzdvpquri9bWVoIgQClFIpFg48aNPH/+XE9NTQkQLTGmvEXKRERprZWIEIYhQRjQbN6hmUb+tCaPNnM055v40f3If7XBGMPT8af0fNLD0NDQsozPnDlDb28vx44dIwxDRARrLSKCKmUbiUQQkWWnoLJ20UpjFYAjVA6rBJTFV5ZIJIIfBBw4eICxsTHq6uo4dOgQ8XicgYEB7t69y/Hjx4nH43R1dVHB8q+w4hlXSmGd5edwmjCco5DLkZ+aJvTnyIdTrFmzhn9+/TVjY2M0NTVx+/Zt+vv7OXfuHKlUip6eHgBOnz6N7/vlYl0JKzIw78/T+sdGbn6yjf5ZS2HtJgIP+mcC5kySI1uSXPjqAlprTp06RWdnJ8ViEaUUVVVVnD9/nqtXr5LJZHj48CFbt279fQEEYUisZi2fXel9bWU750gmkwRBgNYaz/Ow1lJfX088Hmd2dpZcLvdaBl4pgQChH4B1iHU4a8E6Qj9ARGhpaUFrzeDgIJFIBGMM1lqMMWQyGSYmJohEIqxfv/7314CIoADtGTAaZTTaLI2VUhw+fBjnHBcvXuTy5cs45/A8j3Q6zcmTJ/F9n71799LW1rbgSOs3D+B1lBljcM7R3d3N0aNHKRQKnDhxgs7OTnbt2sX27dsZGRkhHo/T19e3+Kt/fQ1YawFwzolSCs/zUEqVtX1VcJcuXSKRSNDf3086nS6v79mzh76+Pjo6OigWi1RXV2OMWZC29PL8/PxSAL7vE41Gf4rFYkpEePToEb7vU1VVxW+ht7eXs2fPcv/+fQqFAps2baKlpaW8Xl1dTS6XY3x8HBFxtbW1BiiW4hAlInp8fNxt2LChPZvN/ru9vT2Sz+e93bt3qx07diwrzJWYcM5RU1NDNBots5bP53HOlS+kO3fuMDIy4hKJhKTT6ena2tqtxWJxoqamRr98HX9x7do1qaurExYaiXCVzK5bt04GBwdFRP728nVcWZAO+Hsmk/nsxo0bTTMzM5FXHZ83hYhQX1/vHzx48H9tbW1ngSsVvpYCmJ2dJRaLKRbapjpgOxB7K+9LmAbuAnOAnpiYcI2NjUsRLlo2myUMQ1M5t5rmnDO3bt1aNlfmd4W2XL/0/H8pUDF2rNCW/wLRuCkxx8V6wgAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxNy0wOC0wM1QwNDozMjoyMi0wNDowMO7mdkwAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTctMDgtMDNUMDQ6MzI6MjItMDQ6MDCfu87wAAAAAElFTkSuQmCC'; 10305 var st = "font-weight:bold !important;color: #FF0075 !important;font-size: 14px !important;text-transform: lowercase !important;cursor:pointer !important;text-align: center !important;
vendor: 67,815 bytes, lines 10305-12543
10305Text-Shadow: #000000 1px 0px 0px, #000000 1px 1px 0px, #000000 1px -1px 0px, #000000 -1px 1px 0px, #000000 -1px 0px 0px, #000000 -1px -1px 0px, #000000 0px 1px 0px, #000000 0px -1px 0px !important;"; 10306 G.$E.ngy2i=jQuery('<div class="nGY2PortInfo"><a href="http://nano.gallery" target="_blank" title="nanogallery2 | easy photo gallery for your website" style="' + st + '"><img src="' + logo + '" style="height:32px !important;width:initial !important;box-shadow: none !important;vertical-align: middle !important;"/> nanogallery2</a></div>').appendTo(G.$E.base); 10307 10308 G.$E.ngy2i.find('a').on({ 10309 mouseenter: function () { 10310 jQuery(this).attr('style', st); 10311 }, 10312 mouseleave: function () { 10313 jQuery(this).attr('style', st); 10314 } 10315 }); 10316 } 10317 } 10318 10319 // Error console 10320 G.$E.conConsole = jQuery('<div class="nGY2ConsoleParent"></div>').appendTo(G.$E.base); 10321 10322 // i18n translations 10323 i18n(); 10324 10325 if( !G.O.lightboxStandalone ) { 10326 // cache some thumbnails data (sizes, styles...) 10327 ThumbnailDefCaches(); 10328 10329 // do special settings depending for some options 10330 // thumbnail display transition 10331 switch( G.tn.opt.Get('displayTransition') ) { 10332 case 'SCALEDOWN': 10333 case 'RANDOMSCALE': 10334 default: 10335 G.$E.base.css('overflow', 'visible'); 10336 G.$E.conTnParent.css('overflow', 'visible'); 10337 G.$E.conTn.css('overflow', 'visible'); 10338 break; 10339 } 10340 } 10341 10342 } 10343 10344 function TriggerCustomEvent ( eventName ) { 10345 // G.$E.base.trigger('pageChanged.nanogallery2', new Event('pageChanged.nanogallery2')); 10346 var eN = eventName + '.nanogallery2'; 10347 var event=null; 10348 try { 10349 event = new Event( eN ); 10350 } catch(e) { 10351 event = document.createEvent('Event'); 10352 event.initEvent(eN, false, false); 10353 } 10354 G.$E.base.trigger(eN, event); 10355 } 10356 10357 10358 /** @function SetGlobalEvents */ 10359 function SetGlobalEvents() { 10360 // GLOBAL EVENT MANAGEMENT 10361 10362 if( !G.O.lightboxStandalone ) { 10363 G.$E.conTnParent.on({ 10364 mouseenter: GalleryMouseEnter, 10365 mouseleave: GalleryMouseLeave 10366 }, ".nGY2GThumbnail"); //pass the element as an argument to .on 10367 10368 // G.GOM.hammertime = new NGHammer(G.$E.conTn[0], { touchAction: 'none' }); 10369 G.GOM.hammertime = new NGHammer( G.$E.conTn[0] ); 10370 // G.GOM.hammertime.domEvents = true; 10371 10372 10373 // PAN on gallery (pagination) 10374 G.GOM.hammertime.on('pan', function(ev) { 10375 if( !G.VOM.viewerDisplayed ) { 10376 if( G.O.paginationSwipe && G.layout.support.rows && G.galleryDisplayMode.Get() == 'PAGINATION' ) { 10377 if( Math.abs(ev.deltaY) > G.GOM.panThreshold ) { 10378 G.GOM.panYOnly = true; 10379 } 10380 if( !G.GOM.panYOnly ) { 10381 G.$E.conTn.css( G.CSStransformName , 'translate('+(ev.deltaX)+'px,0px)'); 10382 } 10383 } 10384 } 10385 }); 10386 G.GOM.hammertime.on('panend', function(ev) { 10387 if( !G.VOM.viewerDisplayed ) { 10388 if( G.O.paginationSwipe && G.layout.support.rows && G.galleryDisplayMode.Get() == 'PAGINATION' ) { 10389 if( !G.GOM.panYOnly ) { 10390 if( ev.deltaX > 50 ) { 10391 paginationPreviousPage(); 10392 return; 10393 } 10394 if( ev.deltaX < -50 ) { 10395 paginationNextPage(); 10396 return; 10397 } 10398 } 10399 G.GOM.panYOnly = false; 10400 G.$E.conTn.css( G.CSStransformName , 'translate(0px,0px)'); 10401 // pX=0; 10402 } 10403 } 10404 }); 10405 // tap on gallery 10406 G.GOM.hammertime.on('tap', function(ev) { 10407 if( !G.VOM.viewerDisplayed ) { 10408 ev.srcEvent.stopPropagation(); 10409 ev.srcEvent.preventDefault(); // cancel mouseenter event 10410 10411 if( ev.pointerType == 'mouse') { 10412 if( GalleryClicked(ev.srcEvent) == 'exit' ) { return; } 10413 } 10414 else { 10415 var r = GalleryEventRetrieveElementl(ev.srcEvent, false); 10416 if( r.GOMidx == -1 ) { return; } 10417 if( r.action != 'NONE' && r.action != 'OPEN' ) { 10418 // toolbar touched --> execute action 10419 GalleryClicked(ev.srcEvent); 10420 return; 10421 } 10422 10423 if( G.GOM.slider.hostIdx == r.GOMidx ) { 10424 // touch on thumbnail slider -> open immediately 10425 ThumbnailHoverOutAll(); 10426 ThumbnailOpen(G.GOM.items[G.GOM.slider.currentIdx].thumbnailIdx, true); 10427 return; 10428 } 10429 10430 if( (G.GOM.curNavLevel == 'l1' && G.O.touchAnimationL1 == false) || (G.GOM.curNavLevel == 'lN' && G.O.touchAnimation == false) ) { 10431 // open on single touch (no hover animation) 10432 ThumbnailOpen(G.GOM.items[r.GOMidx].thumbnailIdx, true); 10433 return; 10434 } 10435 10436 if( G.O.touchAutoOpenDelay > 0 ) { 10437 // open on single touch after end of hover animation (=defined delay) 10438 ThumbnailHoverOutAll(); 10439 ThumbnailHover( r.GOMidx ); 10440 window.clearInterval( G.touchAutoOpenDelayTimerID ); 10441 G.touchAutoOpenDelayTimerID = window.setInterval(function(){ 10442 window.clearInterval( G.touchAutoOpenDelayTimerID ); 10443 ThumbnailOpen( G.GOM.items[r.GOMidx].thumbnailIdx, true ); 10444 }, G.O.touchAutoOpenDelay ); 10445 } 10446 else { 10447 // two touch scenario 10448 if( !G.I[G.GOM.items[r.GOMidx].thumbnailIdx].hovered ) { 10449 ThumbnailHoverOutAll(); 10450 ThumbnailHover(r.GOMidx); 10451 } 10452 else { 10453 // second touch 10454 ThumbnailOpen(G.GOM.items[r.GOMidx].thumbnailIdx, true); 10455 } 10456 } 10457 } 10458 } 10459 }); 10460 10461 // browser location hash management 10462 if( G.O.locationHash ) { 10463 // jQuery(window).bind( 'hashchange', function() { 10464 // ProcessLocationHash(); 10465 // }); 10466 jQuery(window).on('hashchange.nanogallery2.' + G.baseEltID, function() {ProcessLocationHash();} ); 10467 } 10468 } 10469 10470 // Page resize / orientation change 10471 jQuery(window).on('resize.nanogallery2.' + G.baseEltID + ' orientationChange.nanogallery2.' + G.baseEltID, debounce( ResizeWindowEvent, G.O.eventsDebounceDelay, false) ); 10472 10473 // Event page scrolled 10474 jQuery(window).on('scroll.nanogallery2.' + G.baseEltID, debounce( OnScrollEvent, G.O.eventsDebounceDelay, false) ); 10475 10476 if( !G.O.lightboxStandalone ) { 10477 // Scroll event on first scrollable parent element 10478 G.$E.scrollableParent = getScrollableParent( G.$E.base[0] ); 10479 var sp = getScrollableParent( G.$E.base[0] ); 10480 if( sp !== null ) { 10481 G.$E.scrollableParent = jQuery( sp ); 10482 G.$E.scrollableParent.on('scroll.nanogallery2.' + G.baseEltID, debounce( OnScrollEvent, G.O.eventsDebounceDelay, false) ); 10483 } 10484 } 10485 10486 // lightbox: hide tools/gallery after defined delay 10487 G.VOM.toolsHide = debounce( ViewerToolsHide, G.O.viewerHideToolsDelay, false ); 10488 10489 // Keyboard management 10490 jQuery(document).keyup(function(e) { 10491 if( G.popup.isDisplayed ) { 10492 switch( e.keyCode) { 10493 case 27: // Esc key 10494 G.popup.close(); 10495 break; 10496 } 10497 } 10498 else { 10499 if( G.VOM.viewerDisplayed ) { 10500 ViewerToolsUnHide(); 10501 switch( e.keyCode) { 10502 case 27: // Escape key 10503 case 40: // DOWN 10504 case 38: // UP 10505 LightboxClose(); 10506 break; 10507 case 32: // SPACE 10508 case 13: // ENTER 10509 SlideshowToggle(); 10510 break; 10511 case 39: // RIGHT 10512 case 33: // PAGE UP 10513 DisplayNextMedia(); 10514 break; 10515 case 37: // LEFT 10516 case 34: // PAGE DOWN 10517 DisplayPreviousMedia(); 10518 break; 10519 case 35: // END 10520 case 36: // BEGIN 10521 } 10522 } 10523 } 10524 }); 10525 10526 // mouse wheel to zoom in/out the image displayed in the internal lightbox 10527 jQuery(window).bind('mousewheel wheel', function(e){ 10528 10529 if( G.VOM.viewerDisplayed && G.VOM.content.current.NGY2Item().mediaKind == 'img' ) { 10530 10531 var deltaY = 0; 10532 e.preventDefault(); 10533 10534 if( ViewerZoomStart() ) { 10535 if (e.originalEvent.deltaY) { // FireFox 17+ (IE9+, Chrome 31+?) 10536 deltaY = e.originalEvent.deltaY; 10537 } else if (e.originalEvent.wheelDelta) { 10538 deltaY = -e.originalEvent.wheelDelta; 10539 } 10540 ViewerZoomIn( deltaY <= 0 ? true : false ); 10541 } 10542 } 10543 }); 10544 10545 // mouse move -> unhide lightbox toolbars 10546 jQuery(window).bind('mousemove', function(e){ 10547 if( G.VOM.viewerDisplayed ) { 10548 if( G.VOM.toolbarsDisplayed == false ) { 10549 G.VOM.singletapTime = new Date().getTime(); // to avoid conflict with SINGLETAP event 10550 debounce( ViewerToolsUnHide, 100, false )(); 10551 } 10552 } 10553 }); 10554 10555 // fullscreen mode on/off --> internal lightbox 10556 if( ngscreenfull.enabled ) { 10557 // ngscreenfull.onchange(() => { 10558 ngscreenfull.onchange( function() { 10559 if( G.VOM.viewerDisplayed ) { 10560 if( ngscreenfull.isFullscreen ) { 10561 G.VOM.viewerIsFullscreen=true; 10562 G.VOM.$viewer.find('.fullscreenButton').html(G.O.icons.viewerFullscreenOff); 10563 } 10564 else { 10565 G.VOM.viewerIsFullscreen=false; 10566 G.VOM.$viewer.find('.fullscreenButton').html(G.O.icons.viewerFullscreenOn); 10567 } 10568 } 10569 }); 10570 } 10571 10572 } 10573 10574 10575 //----- Manage browser location hash (deep linking and browser back/forward) 10576 function ProcessLocationHash() { 10577 10578 // standard use case -> location hash processing 10579 if( !G.O.locationHash ) { return false; } 10580 10581 var curGal = '#nanogallery/' + G.baseEltID + '/', 10582 newLocationHash = location.hash; 10583 if( G.O.debugMode ) { 10584 console.log('------------------------ PROCESS LOCATION HASH'); 10585 console.log('newLocationHash1: ' +newLocationHash); 10586 console.log('G.locationHashLastUsed: ' +G.locationHashLastUsed); 10587 } 10588 10589 if( newLocationHash == '' ) { 10590 // if( G.GOM.lastDisplayedIdx != -1 ) { 10591 if( G.locationHashLastUsed !== '' ) { 10592 // back button and no hash --> display first album 10593 if( G.O.debugMode ) { console.log('display root album' ); } 10594 G.locationHashLastUsed = ''; 10595 if( G.O.debugMode ) { console.log('new3 G.locationHashLastUsed: ' + G.locationHashLastUsed); } 10596 DisplayAlbum('', '0'); 10597 return true; 10598 } 10599 } 10600 10601 if( newLocationHash == G.locationHashLastUsed ) { return; } 10602 10603 if( newLocationHash.indexOf(curGal) == 0 ) { 10604 // item IDs detected 10605 var IDs=parseIDs( newLocationHash.substring(curGal.length) ); 10606 if( IDs.imageID != '0' ) { 10607 if( G.O.debugMode ) { console.log('display image: ' + IDs.albumID +'-'+ IDs.imageID ); } 10608 DisplayPhoto( IDs.imageID, IDs.albumID ); 10609 return true; 10610 } 10611 else { 10612 if( G.O.debugMode ) { console.log('display album: ' + IDs.albumID ); } 10613 DisplayAlbum( '-1', IDs.albumID ); 10614 return true; 10615 } 10616 } 10617 10618 return false; 10619 } 10620 10621 //---- Set a new browser location hash 10622 function SetLocationHash(albumID, imageID ) { 10623 if( !G.O.locationHash || G.O.lightboxStandalone ) { return false; } 10624 10625 if( G.O.debugMode ) { 10626 console.log('------------------------ SET LOCATION HASH'); 10627 } 10628 10629 if( imageID == '' && (albumID == '-1' || albumID == '0' || G.O.album == albumID ) ) { 10630 // root album level --> do not set top.location.hash if not already set 10631 if( location.hash != '' ) { 10632 // try to clear the hash if set 10633 if ("pushState" in history) { 10634 history.pushState("", document.title, window.location.pathname + window.location.search); 10635 } 10636 else { 10637 location.hash=''; 10638 } 10639 } 10640 G.locationHashLastUsed=''; 10641 if( G.O.debugMode ) { console.log('new2 G.locationHashLastUsed: '+G.locationHashLastUsed); } 10642 return; 10643 } 10644 10645 var newLocationHash='#'+'nanogallery/'+G.baseEltID+'/'+ albumID; 10646 if( imageID != '' ) { 10647 newLocationHash+='/'+imageID; 10648 } 10649 10650 var lH=location.hash; 10651 if( G.O.debugMode ) { 10652 console.log('newLocationHash2: '+newLocationHash); 10653 console.log('location.hash: '+lH); 10654 } 10655 10656 G.locationHashLastUsed=newLocationHash; 10657 if( G.O.debugMode ) { console.log('new G.locationHashLastUsed: '+G.locationHashLastUsed); } 10658 10659 if( lH == '' || lH != newLocationHash ) { 10660 // G.locationHashLastUsed='#'+newLocationHash; 10661 try { 10662 top.location.hash=newLocationHash; 10663 } 10664 catch(e) { 10665 // location hash is not supported by current browser --> disable the option 10666 G.O.locationHash=false; 10667 } 10668 } 10669 } 10670 10671 10672 // WINDOW RESIZE EVENT 10673 function ResizeWindowEvent() { 10674 CacheViewport(); 10675 10676 var l = G.GOM.curNavLevel; 10677 var w = G.GOM.curWidth; 10678 10679 if( G.VOM.viewerDisplayed ) { 10680 // lightbox 10681 ResizeLightbox(); 10682 G.VOM.gallery.Resize(); 10683 } 10684 else { 10685 // gallery 10686 if( G.galleryResizeEventEnabled ) { 10687 var nw = RetrieveCurWidth(); 10688 10689 if( G.GOM.albumIdx != -1 ) { 10690 10691 // check if the gallery needs to be rendered again because the width changed 10692 10693 var s = G.tn.settings; 10694 if( G.layout.engine == "MOSAIC") { 10695 // Mosaic layout 10696 if( JSON.stringify(s.mosaic[l][w]) !== JSON.stringify(s.mosaic[l][nw]) ) { 10697 // mosaic definition changed 10698 G.GOM.curWidth = nw; 10699 G.GOM.pagination.currentPage = 0; 10700 GalleryRender( G.GOM.albumIdx ); 10701 return; 10702 } 10703 } 10704 else { 10705 // other layouts 10706 if( s.height[l][w] != s.height[l][nw] || s.width[l][w] != s.width[l][nw] || s.gutterHeight[l][w] != s.gutterHeight[l][nw] || s.gutterWidth[l][w] != s.gutterWidth[l][nw] ) { 10707 // thumbnail size / gutter size changed --> render the gallery with the new values 10708 G.GOM.curWidth = nw; 10709 //G.layout.SetEngine(); 10710 G.GOM.pagination.currentPage = 0; 10711 GalleryRender( G.GOM.albumIdx ); 10712 return; 10713 } 10714 } 10715 // return; 10716 } 10717 // else { 10718 GalleryResize(); 10719 // } 10720 } 10721 } 10722 } 10723 10724 10725 10726 // SCROLL EVENT -> on WINDOW or SCROLLABLE PARENT CONTAINER 10727 function OnScrollEvent() { 10728 if( !G.VOM.viewerDisplayed ) { 10729 GalleryResizeOnScrollEvent(); 10730 } 10731 } 10732 10733 // the gallery may currently be refreshed, so ensure that at the end of the refresh, the gallery is refreshed again because the page may have been scrolled in the meantime 10734 function GalleryResizeOnScrollEvent() { 10735 if( G.galleryResizeEventEnabled == false) { 10736 window.setTimeout(GalleryResizeOnScrollEvent, 10); // check again in 10ms 10737 } else { 10738 GalleryResize(); 10739 } 10740 } 10741 10742 10743 10744 // I18N : define text translations 10745 function i18n() { 10746 10747 // browser language 10748 G.i18nLang = (navigator.language || navigator.userLanguage).toUpperCase(); 10749 if( G.i18nLang === 'UNDEFINED') { G.i18nLang=''; } 10750 10751 var llang=-('_'+G.i18nLang).length; 10752 10753 if( toType(G.O.i18n) == 'object' ){ 10754 10755 for( var key in G.O.i18n ) { 10756 //var value = G.O.i18n[key]; 10757 var s=key.substr(llang); 10758 if( s == ('_'+G.i18nLang) ) { 10759 G.i18nTranslations[key.substr(0,key.length-s.length)]=G.O.i18n[key]; 10760 } 10761 else { 10762 G.i18nTranslations[key]=G.O.i18n[key]; 10763 } 10764 } 10765 } 10766 } 10767 10768 function GetI18nItem( item, property ) { 10769 var s=''; 10770 if( G.i18nLang != '' ) { 10771 if( item[property+'_'+G.i18nLang] !== undefined && item[property+'_'+G.i18nLang].length>0 ) { 10772 s=item[property+'_'+G.i18nLang]; 10773 return s; 10774 } 10775 } 10776 s=item[property]; 10777 return s; 10778 } 10779 10780 10781 function RetrieveCurWidth() { 10782 var vpW = G.GOM.cache.viewport.w; 10783 10784 if( G.O.breakpointSizeSM > 0 && vpW < G.O.breakpointSizeSM) { return 'xs'; } 10785 if( G.O.breakpointSizeME > 0 && vpW < G.O.breakpointSizeME) { return 'sm'; } 10786 if( G.O.breakpointSizeLA > 0 && vpW < G.O.breakpointSizeLA) { return 'me'; } 10787 if( G.O.breakpointSizeXL > 0 && vpW < G.O.breakpointSizeXL) { return 'la'; } 10788 10789 return 'xl'; 10790 } 10791 10792 10793 /** @function browserNotification */ 10794 function browserNotification() { 10795 var m = 'Your browser version is not supported anymore. The image gallery cannot be displayed. <br><br>Please update to a more recent one. Download:<br>'; 10796 m += ' <a href="http://www.google.com/chrome/?hl=en-US)">Chrome</a><br>'; 10797 m += ' <a href="http://www.mozilla.com/firefox/)">Firefox</a><br>'; 10798 m += ' <a href="http://www.microsoft.com/windows/internet-explorer/default.aspx">Internet Explorer</a><br>'; 10799 m += ' <a href="http://www.apple.com/safari/download/">Safari</a>'; 10800 NanoAlert(G, m, false); 10801 } 10802 10803 // Original author : John Hrvatin, Lead Program Manager, Internet Explorer - http://blogs.msdn.com/b/ie/archive/2011/10/28/a-best-practice-for-programming-with-vendor-prefixes.aspx 10804 function FirstSupportedPropertyName(prefixedPropertyNames) { 10805 var tempDiv = document.createElement("div"); 10806 for (var i = 0; i < prefixedPropertyNames.length; ++i) { 10807 if (typeof tempDiv.style[prefixedPropertyNames[i]] != 'undefined') 10808 return prefixedPropertyNames[i]; 10809 } 10810 return null; 10811 } 10812 10813 10814 10815 10816 } 10817 10818 10819 10820//########################################################################################################################## 10821//## imagesLoaded ########################################################################################################## 10822//########################################################################################################################## 10823 10824// external module EMBEDED in nanogallery 10825// NGY BUILD: 10826// replace "imagesLoaded" with "ngimagesLoaded" 10827// replace "ImagesLoaded" with "ngImagesLoaded" 10828// replace "EvEmitter" with "ngEvEmitter" 10829// replace "var $ = window.jQuery" with "var $ = jQuery;" 10830// 2x (global.ngEvEmitter and window.ngimagesLoaded = f...)ignore package manager and set browser global 10831 10832 10833 10834/*! 10835 * imagesLoaded PACKAGED v4.1.1 10836 * JavaScript is all like "You images are done yet or what?" 10837 * MIT License 10838 */ 10839 10840/** 10841 * EvEmitter v1.0.3 10842 * Lil' event emitter 10843 * MIT License 10844 */ 10845 10846 10847/* jshint unused: true, undef: true, strict: true */ 10848 10849( function( global, factory ) { 10850 // universal module definition 10851 /* jshint strict: false */ /* globals define, module, window */ 10852// if ( typeof define == 'function' && define.amd ) { 10853 // AMD - RequireJS 10854// define( 'ev-emitter/ev-emitter',factory ); 10855// } else if ( typeof module == 'object' && module.exports ) { 10856 // CommonJS - Browserify, Webpack 10857// module.exports = factory(); 10858// } else { 10859 // Browser globals 10860 global.ngEvEmitter = factory(); 10861// } 10862 10863}( typeof window != 'undefined' ? window : this, function() { 10864 10865 10866 10867function ngEvEmitter() {} 10868 10869var proto = ngEvEmitter.prototype; 10870 10871proto.on = function( eventName, listener ) { 10872 if ( !eventName || !listener ) { 10873 return; 10874 } 10875 // set events hash 10876 var events = this._events = this._events || {}; 10877 // set listeners array 10878 var listeners = events[ eventName ] = events[ eventName ] || []; 10879 // only add once 10880 if ( listeners.indexOf( listener ) == -1 ) { 10881 listeners.push( listener ); 10882 } 10883 10884 return this; 10885}; 10886 10887proto.once = function( eventName, listener ) { 10888 if ( !eventName || !listener ) { 10889 return; 10890 } 10891 // add event 10892 this.on( eventName, listener ); 10893 // set once flag 10894 // set onceEvents hash 10895 var onceEvents = this._onceEvents = this._onceEvents || {}; 10896 // set onceListeners object 10897 var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {}; 10898 // set flag 10899 onceListeners[ listener ] = true; 10900 10901 return this; 10902}; 10903 10904proto.off = function( eventName, listener ) { 10905 var listeners = this._events && this._events[ eventName ]; 10906 if ( !listeners || !listeners.length ) { 10907 return; 10908 } 10909 var index = listeners.indexOf( listener ); 10910 if ( index != -1 ) { 10911 listeners.splice( index, 1 ); 10912 } 10913 10914 return this; 10915}; 10916 10917proto.emitEvent = function( eventName, args ) { 10918 var listeners = this._events && this._events[ eventName ]; 10919 if ( !listeners || !listeners.length ) { 10920 return; 10921 } 10922 var i = 0; 10923 var listener = listeners[i]; 10924 args = args || []; 10925 // once stuff 10926 var onceListeners = this._onceEvents && this._onceEvents[ eventName ]; 10927 10928 while ( listener ) { 10929 var isOnce = onceListeners && onceListeners[ listener ]; 10930 if ( isOnce ) { 10931 // remove listener 10932 // remove before trigger to prevent recursion 10933 this.off( eventName, listener ); 10934 // unset once flag 10935 delete onceListeners[ listener ]; 10936 } 10937 // trigger listener 10938 listener.apply( this, args ); 10939 // get next listener 10940 i += isOnce ? 0 : 1; 10941 listener = listeners[i]; 10942 } 10943 10944 return this; 10945}; 10946 10947return ngEvEmitter; 10948 10949})); 10950 10951/*! 10952 * ngimagesLoaded v4.1.1 10953 * JavaScript is all like "You images are done yet or what?" 10954 * MIT License 10955 */ 10956 10957( function( window, factory ) { 'use strict'; 10958 // universal module definition 10959 10960 /*global define: false, module: false, require: false */ 10961 10962// if ( typeof define == 'function' && define.amd ) { 10963 // AMD 10964// define( [ 10965// 'ev-emitter/ev-emitter' 10966// ], function( ngEvEmitter ) { 10967// return factory( window, ngEvEmitter ); 10968// }); 10969// } else if ( typeof module == 'object' && module.exports ) { 10970 // CommonJS 10971// module.exports = factory( 10972// window, 10973// require('ev-emitter') 10974// ); 10975// } else { 10976 // browser global 10977 window.ngimagesLoaded = factory( 10978 window, 10979 window.ngEvEmitter 10980 ); 10981 //} 10982 10983})( window, 10984 10985// -------------------------- factory -------------------------- // 10986 10987function factory( window, ngEvEmitter ) { 10988 10989 10990 10991// var $ = window.jQuery; 10992var $ = jQuery; 10993var console = window.console; 10994 10995// -------------------------- helpers -------------------------- // 10996 10997// extend objects 10998function extend( a, b ) { 10999 for ( var prop in b ) { 11000 a[ prop ] = b[ prop ]; 11001 } 11002 return a; 11003} 11004 11005// turn element or nodeList into an array 11006function makeArray( obj ) { 11007 var ary = []; 11008 if ( Array.isArray( obj ) ) { 11009 // use object if already an array 11010 ary = obj; 11011 } else if ( typeof obj.length == 'number' ) { 11012 // convert nodeList to array 11013 for ( var i=0; i < obj.length; i++ ) { 11014 ary.push( obj[i] ); 11015 } 11016 } else { 11017 // array of single index 11018 ary.push( obj ); 11019 } 11020 return ary; 11021} 11022 11023// -------------------------- ngimagesLoaded -------------------------- // 11024 11025/** 11026 * @param {Array, Element, NodeList, String} elem 11027 * @param {Object or Function} options - if function, use as callback 11028 * @param {Function} onAlways - callback function 11029 */ 11030function ngImagesLoaded( elem, options, onAlways ) { 11031 // coerce ngImagesLoaded() without new, to be new ngImagesLoaded() 11032 if ( !( this instanceof ngImagesLoaded ) ) { 11033 return new ngImagesLoaded( elem, options, onAlways ); 11034 } 11035 // use elem as selector string 11036 if ( typeof elem == 'string' ) { 11037 elem = document.querySelectorAll( elem ); 11038 } 11039 11040 this.elements = makeArray( elem ); 11041 this.options = extend( {}, this.options ); 11042 11043 if ( typeof options == 'function' ) { 11044 onAlways = options; 11045 } else { 11046 extend( this.options, options ); 11047 } 11048 11049 if ( onAlways ) { 11050 this.on( 'always', onAlways ); 11051 } 11052 11053 this.getImages(); 11054 11055 if ( $ ) { 11056 // add jQuery Deferred object 11057 this.jqDeferred = new $.Deferred(); 11058 } 11059 11060 // HACK check async to allow time to bind listeners 11061 setTimeout( function() { 11062 this.check(); 11063 }.bind( this )); 11064} 11065 11066ngImagesLoaded.prototype = Object.create( ngEvEmitter.prototype ); 11067 11068ngImagesLoaded.prototype.options = {}; 11069 11070ngImagesLoaded.prototype.getImages = function() { 11071 this.images = []; 11072 11073 // filter & find items if we have an item selector 11074 this.elements.forEach( this.addElementImages, this ); 11075}; 11076 11077/** 11078 * @param {Node} element 11079 */ 11080ngImagesLoaded.prototype.addElementImages = function( elem ) { 11081 // filter siblings 11082 if ( elem.nodeName == 'IMG' ) { 11083 this.addImage( elem ); 11084 } 11085 // get background image on element 11086 if ( this.options.background === true ) { 11087 this.addElementBackgroundImages( elem ); 11088 } 11089 11090 // find children 11091 // no non-element nodes, #143 11092 var nodeType = elem.nodeType; 11093 if ( !nodeType || !elementNodeTypes[ nodeType ] ) { 11094 return; 11095 } 11096 var childImgs = elem.querySelectorAll('img'); 11097 // concat childElems to filterFound array 11098 for ( var i=0; i < childImgs.length; i++ ) { 11099 var img = childImgs[i]; 11100 this.addImage( img ); 11101 } 11102 11103 // get child background images 11104 if ( typeof this.options.background == 'string' ) { 11105 var children = elem.querySelectorAll( this.options.background ); 11106 for ( i=0; i < children.length; i++ ) { 11107 var child = children[i]; 11108 this.addElementBackgroundImages( child ); 11109 } 11110 } 11111}; 11112 11113var elementNodeTypes = { 11114 1: true, 11115 9: true, 11116 11: true 11117}; 11118 11119ngImagesLoaded.prototype.addElementBackgroundImages = function( elem ) { 11120 var style = getComputedStyle( elem ); 11121 if ( !style ) { 11122 // Firefox returns null if in a hidden iframe https://bugzil.la/548397 11123 return; 11124 } 11125 // get url inside url("...") 11126 var reURL = /url\((['"])?(.*?)\1\)/gi; 11127 var matches = reURL.exec( style.backgroundImage ); 11128 while ( matches !== null ) { 11129 var url = matches && matches[2]; 11130 if ( url ) { 11131 this.addBackground( url, elem ); 11132 } 11133 matches = reURL.exec( style.backgroundImage ); 11134 } 11135}; 11136 11137/** 11138 * @param {Image} img 11139 */ 11140ngImagesLoaded.prototype.addImage = function( img ) { 11141 var loadingImage = new LoadingImage( img ); 11142 this.images.push( loadingImage ); 11143}; 11144 11145ngImagesLoaded.prototype.addBackground = function( url, elem ) { 11146 var background = new Background( url, elem ); 11147 this.images.push( background ); 11148}; 11149 11150ngImagesLoaded.prototype.check = function() { 11151 var _this = this; 11152 this.progressedCount = 0; 11153 this.hasAnyBroken = false; 11154 // complete if no images 11155 if ( !this.images.length ) { 11156 this.complete(); 11157 return; 11158 } 11159 11160 function onProgress( image, elem, message ) { 11161 // HACK - Chrome triggers event before object properties have changed. #83 11162 setTimeout( function() { 11163 _this.progress( image, elem, message ); 11164 }); 11165 } 11166 11167 this.images.forEach( function( loadingImage ) { 11168 loadingImage.once( 'progress', onProgress ); 11169 loadingImage.check(); 11170 }); 11171}; 11172 11173ngImagesLoaded.prototype.progress = function( image, elem, message ) { 11174 this.progressedCount++; 11175 this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded; 11176 // progress event 11177 this.emitEvent( 'progress', [ this, image, elem ] ); 11178 if ( this.jqDeferred && this.jqDeferred.notify ) { 11179 this.jqDeferred.notify( this, image ); 11180 } 11181 // check if completed 11182 if ( this.progressedCount == this.images.length ) { 11183 this.complete(); 11184 } 11185 11186 if ( this.options.debug && console ) { 11187 console.log( 'progress: ' + message, image, elem ); 11188 } 11189}; 11190 11191ngImagesLoaded.prototype.complete = function() { 11192 var eventName = this.hasAnyBroken ? 'fail' : 'done'; 11193 this.isComplete = true; 11194 this.emitEvent( eventName, [ this ] ); 11195 this.emitEvent( 'always', [ this ] ); 11196 if ( this.jqDeferred ) { 11197 var jqMethod = this.hasAnyBroken ? 'reject' : 'resolve'; 11198 this.jqDeferred[ jqMethod ]( this ); 11199 } 11200}; 11201 11202// -------------------------- -------------------------- // 11203 11204function LoadingImage( img ) { 11205 this.img = img; 11206} 11207 11208LoadingImage.prototype = Object.create( ngEvEmitter.prototype ); 11209 11210LoadingImage.prototype.check = function() { 11211 // If complete is true and browser supports natural sizes, 11212 // try to check for image status manually. 11213 var isComplete = this.getIsImageComplete(); 11214 if ( isComplete ) { 11215 // report based on naturalWidth 11216 this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' ); 11217 return; 11218 } 11219 11220 // If none of the checks above matched, simulate loading on detached element. 11221 this.proxyImage = new Image(); 11222 this.proxyImage.addEventListener( 'load', this ); 11223 this.proxyImage.addEventListener( 'error', this ); 11224 // bind to image as well for Firefox. #191 11225 this.img.addEventListener( 'load', this ); 11226 this.img.addEventListener( 'error', this ); 11227 this.proxyImage.src = this.img.src; 11228}; 11229 11230LoadingImage.prototype.getIsImageComplete = function() { 11231 return this.img.complete && this.img.naturalWidth !== undefined; 11232}; 11233 11234LoadingImage.prototype.confirm = function( isLoaded, message ) { 11235 this.isLoaded = isLoaded; 11236 this.emitEvent( 'progress', [ this, this.img, message ] ); 11237}; 11238 11239// ----- events ----- // 11240 11241// trigger specified handler for event type 11242LoadingImage.prototype.handleEvent = function( event ) { 11243 var method = 'on' + event.type; 11244 if ( this[ method ] ) { 11245 this[ method ]( event ); 11246 } 11247}; 11248 11249LoadingImage.prototype.onload = function() { 11250 this.confirm( true, 'onload' ); 11251 this.unbindEvents(); 11252}; 11253 11254LoadingImage.prototype.onerror = function() { 11255 this.confirm( false, 'onerror' ); 11256 this.unbindEvents(); 11257}; 11258 11259LoadingImage.prototype.unbindEvents = function() { 11260 this.proxyImage.removeEventListener( 'load', this ); 11261 this.proxyImage.removeEventListener( 'error', this ); 11262 this.img.removeEventListener( 'load', this ); 11263 this.img.removeEventListener( 'error', this ); 11264}; 11265 11266// -------------------------- Background -------------------------- // 11267 11268function Background( url, element ) { 11269 this.url = url; 11270 this.element = element; 11271 this.img = new Image(); 11272} 11273 11274// inherit LoadingImage prototype 11275Background.prototype = Object.create( LoadingImage.prototype ); 11276 11277Background.prototype.check = function() { 11278 this.img.addEventListener( 'load', this ); 11279 this.img.addEventListener( 'error', this ); 11280 this.img.src = this.url; 11281 // check if image is already complete 11282 var isComplete = this.getIsImageComplete(); 11283 if ( isComplete ) { 11284 this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' ); 11285 this.unbindEvents(); 11286 } 11287}; 11288 11289Background.prototype.unbindEvents = function() { 11290 this.img.removeEventListener( 'load', this ); 11291 this.img.removeEventListener( 'error', this ); 11292}; 11293 11294Background.prototype.confirm = function( isLoaded, message ) { 11295 this.isLoaded = isLoaded; 11296 this.emitEvent( 'progress', [ this, this.element, message ] ); 11297}; 11298 11299// -------------------------- jQuery -------------------------- // 11300 11301ngImagesLoaded.makeJQueryPlugin = function( jQuery ) { 11302 jQuery = jQuery || window.jQuery; 11303 if ( !jQuery ) { 11304 return; 11305 } 11306 // set local variable 11307 $ = jQuery; 11308 // $().ngimagesLoaded() 11309 $.fn.ngimagesLoaded = function( options, callback ) { 11310 var instance = new ngImagesLoaded( this, options, callback ); 11311 return instance.jqDeferred.promise( $(this) ); 11312 }; 11313}; 11314// try making plugin 11315ngImagesLoaded.makeJQueryPlugin(); 11316 11317// -------------------------- -------------------------- // 11318 11319return ngImagesLoaded; 11320 11321}); 11322 11323 11324 11325//########################################################################################################################## 11326//## screenfull.js ######################################################################################################### 11327//########################################################################################################################## 11328 11329// screenfull.js 11330// v4.0.1 11331// by sindresorhus - https://github.com/sindresorhus 11332// from: https://github.com/sindresorhus/screenfull.js 11333 11334// external module embeded in nanogallery 11335// NGY BUILD: 11336// replace "screenfull" with "ngscreenfull" 11337// 11338 11339(function () { 11340 'use strict'; 11341 11342 var document = typeof window !== 'undefined' && typeof window.document !== 'undefined' ? window.document : {}; 11343 var isCommonjs = typeof module !== 'undefined' && module.exports; 11344 var keyboardAllowed = typeof Element !== 'undefined' && 'ALLOW_KEYBOARD_INPUT' in Element; 11345 11346 var fn = (function () { 11347 var val; 11348 11349 var fnMap = [ 11350 [ 11351 'requestFullscreen', 11352 'exitFullscreen', 11353 'fullscreenElement', 11354 'fullscreenEnabled', 11355 'fullscreenchange', 11356 'fullscreenerror' 11357 ], 11358 // New WebKit 11359 [ 11360 'webkitRequestFullscreen', 11361 'webkitExitFullscreen', 11362 'webkitFullscreenElement', 11363 'webkitFullscreenEnabled', 11364 'webkitfullscreenchange', 11365 'webkitfullscreenerror' 11366 11367 ], 11368 // Old WebKit (Safari 5.1) 11369 [ 11370 'webkitRequestFullScreen', 11371 'webkitCancelFullScreen', 11372 'webkitCurrentFullScreenElement', 11373 'webkitCancelFullScreen', 11374 'webkitfullscreenchange', 11375 'webkitfullscreenerror' 11376 11377 ], 11378 [ 11379 'mozRequestFullScreen', 11380 'mozCancelFullScreen', 11381 'mozFullScreenElement', 11382 'mozFullScreenEnabled', 11383 'mozfullscreenchange', 11384 'mozfullscreenerror' 11385 ], 11386 [ 11387 'msRequestFullscreen', 11388 'msExitFullscreen', 11389 'msFullscreenElement', 11390 'msFullscreenEnabled', 11391 'MSFullscreenChange', 11392 'MSFullscreenError' 11393 ] 11394 ]; 11395 11396 var i = 0; 11397 var l = fnMap.length; 11398 var ret = {}; 11399 11400 for (; i < l; i++) { 11401 val = fnMap[i]; 11402 if (val && val[1] in document) { 11403 for (i = 0; i < val.length; i++) { 11404 ret[fnMap[0][i]] = val[i]; 11405 } 11406 return ret; 11407 } 11408 } 11409 11410 return false; 11411 })(); 11412 11413 var eventNameMap = { 11414 change: fn.fullscreenchange, 11415 error: fn.fullscreenerror 11416 }; 11417 11418 var ngscreenfull = { 11419 request: function (elem) { 11420 return new Promise(function (resolve) { 11421 var request = fn.requestFullscreen; 11422 11423 var onFullScreenEntered = function () { 11424 this.off('change', onFullScreenEntered); 11425 resolve(); 11426 }.bind(this); 11427 11428 elem = elem || document.documentElement; 11429 11430 // Work around Safari 5.1 bug: reports support for 11431 // keyboard in fullscreen even though it doesn't. 11432 // Browser sniffing, since the alternative with 11433 // setTimeout is even worse. 11434 if (/ Version\/5\.1(?:\.\d+)? Safari\//.test(navigator.userAgent)) { 11435 elem[request](); 11436 } else { 11437 elem[request](keyboardAllowed ? Element.ALLOW_KEYBOARD_INPUT : {}); 11438 } 11439 11440 this.on('change', onFullScreenEntered); 11441 }.bind(this)); 11442 }, 11443 exit: function () { 11444 return new Promise(function (resolve) { 11445 if (!this.isFullscreen) { 11446 resolve(); 11447 return; 11448 } 11449 11450 var onFullScreenExit = function () { 11451 this.off('change', onFullScreenExit); 11452 resolve(); 11453 }.bind(this); 11454 11455 document[fn.exitFullscreen](); 11456 11457 this.on('change', onFullScreenExit); 11458 }.bind(this)); 11459 }, 11460 toggle: function (elem) { 11461 return this.isFullscreen ? this.exit() : this.request(elem); 11462 }, 11463 onchange: function (callback) { 11464 this.on('change', callback); 11465 }, 11466 onerror: function (callback) { 11467 this.on('error', callback); 11468 }, 11469 on: function (event, callback) { 11470 var eventName = eventNameMap[event]; 11471 if (eventName) { 11472 document.addEventListener(eventName, callback, false); 11473 } 11474 }, 11475 off: function (event, callback) { 11476 var eventName = eventNameMap[event]; 11477 if (eventName) { 11478 document.removeEventListener(eventName, callback, false); 11479 } 11480 }, 11481 raw: fn 11482 }; 11483 11484 if (!fn) { 11485 if (isCommonjs) { 11486 module.exports = false; 11487 } else { 11488 window.ngscreenfull = false; 11489 } 11490 11491 return; 11492 } 11493 11494 Object.defineProperties(ngscreenfull, { 11495 isFullscreen: { 11496 get: function () { 11497 return Boolean(document[fn.fullscreenElement]); 11498 } 11499 }, 11500 element: { 11501 enumerable: true, 11502 get: function () { 11503 return document[fn.fullscreenElement]; 11504 } 11505 }, 11506 enabled: { 11507 enumerable: true, 11508 get: function () { 11509 // Coerce to boolean in case of old WebKit 11510 return Boolean(document[fn.fullscreenEnabled]); 11511 } 11512 } 11513 }); 11514 11515 if (isCommonjs) { 11516 module.exports = ngscreenfull; 11517 } else { 11518 window.ngscreenfull = ngscreenfull; 11519 } 11520})(); 11521 11522 11523 11524//########################################################################################################################## 11525//## Shifty ################################################################################################################ 11526//########################################################################################################################## 11527 11528 /*! 11529 * Shifty 11530 * By Jeremy Kahn - [email protected] 11531 */ 11532 11533// external module EMBEDED in nanogallery 11534// NGY BUILD: 11535// 11536// replace "Tweenable" with "NGTweenable" 11537// replace "define.amd" with "define.amdDISABLED" 11538// replace "var root = this || Function('return this')();" with "const root = typeof window !== 'undefined' ? window : global" 11539 11540/* shifty - v1.5.3 - 2016-11-29 - http://jeremyckahn.github.io/shifty */ 11541;(function () { 11542 const root = typeof window !== 'undefined' ? window : global 11543 11544/** 11545 * Shifty Core 11546 * By Jeremy Kahn - [email protected] 11547 */ 11548 11549var NGTweenable = (function () { 11550 11551 'use strict'; 11552 11553 // Aliases that get defined later in this function 11554 var formula; 11555 11556 // CONSTANTS 11557 var DEFAULT_SCHEDULE_FUNCTION; 11558 var DEFAULT_EASING = 'linear'; 11559 var DEFAULT_DURATION = 500; 11560 var UPDATE_TIME = 1000 / 60; 11561 11562 var _now = Date.now 11563 ? Date.now 11564 : function () {return +new Date();}; 11565 11566 var now = typeof SHIFTY_DEBUG_NOW !== 'undefined' ? SHIFTY_DEBUG_NOW : _now; 11567 11568 if (typeof window !== 'undefined') { 11569 // requestAnimationFrame() shim by Paul Irish (modified for Shifty) 11570 // http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 11571 DEFAULT_SCHEDULE_FUNCTION = window.requestAnimationFrame 11572 || window.webkitRequestAnimationFrame 11573 || window.oRequestAnimationFrame 11574 || window.msRequestAnimationFrame 11575 || (window.mozCancelRequestAnimationFrame 11576 && window.mozRequestAnimationFrame) 11577 || setTimeout; 11578 } else { 11579 DEFAULT_SCHEDULE_FUNCTION = setTimeout; 11580 } 11581 11582 function noop () { 11583 // NOOP! 11584 } 11585 11586 /** 11587 * Handy shortcut for doing a for-in loop. This is not a "normal" each 11588 * function, it is optimized for Shifty. The iterator function only receives 11589 * the property name, not the value. 11590 * @param {Object} obj 11591 * @param {Function(string)} fn 11592 * @private 11593 */ 11594 function each (obj, fn) { 11595 var key; 11596 for (key in obj) { 11597 if (Object.hasOwnProperty.call(obj, key)) { 11598 fn(key); 11599 } 11600 } 11601 } 11602 11603 /** 11604 * Perform a shallow copy of Object properties. 11605 * @param {Object} targetObject The object to copy into 11606 * @param {Object} srcObject The object to copy from 11607 * @return {Object} A reference to the augmented `targetObj` Object 11608 * @private 11609 */ 11610 function shallowCopy (targetObj, srcObj) { 11611 each(srcObj, function (prop) { 11612 targetObj[prop] = srcObj[prop]; 11613 }); 11614 11615 return targetObj; 11616 } 11617 11618 /** 11619 * Copies each property from src onto target, but only if the property to 11620 * copy to target is undefined. 11621 * @param {Object} target Missing properties in this Object are filled in 11622 * @param {Object} src 11623 * @private 11624 */ 11625 function defaults (target, src) { 11626 each(src, function (prop) { 11627 if (typeof target[prop] === 'undefined') { 11628 target[prop] = src[prop]; 11629 } 11630 }); 11631 } 11632 11633 /** 11634 * Calculates the interpolated tween values of an Object for a given 11635 * timestamp. 11636 * @param {Number} forPosition The position to compute the state for. 11637 * @param {Object} currentState Current state properties. 11638 * @param {Object} originalState: The original state properties the Object is 11639 * tweening from. 11640 * @param {Object} targetState: The destination state properties the Object 11641 * is tweening to. 11642 * @param {number} duration: The length of the tween in milliseconds. 11643 * @param {number} timestamp: The UNIX epoch time at which the tween began. 11644 * @param {Object} easing: This Object's keys must correspond to the keys in 11645 * targetState. 11646 * @private 11647 */ 11648 function tweenProps (forPosition, currentState, originalState, targetState, 11649 duration, timestamp, easing) { 11650 var normalizedPosition = 11651 forPosition < timestamp ? 0 : (forPosition - timestamp) / duration; 11652 11653 11654 var prop; 11655 var easingObjectProp; 11656 var easingFn; 11657 for (prop in currentState) { 11658 if (currentState.hasOwnProperty(prop)) { 11659 easingObjectProp = easing[prop]; 11660 easingFn = typeof easingObjectProp === 'function' 11661 ? easingObjectProp 11662 : formula[easingObjectProp]; 11663 11664 currentState[prop] = tweenProp( 11665 originalState[prop], 11666 targetState[prop], 11667 easingFn, 11668 normalizedPosition 11669 ); 11670 } 11671 } 11672 11673 return currentState; 11674 } 11675 11676 /** 11677 * Tweens a single property. 11678 * @param {number} start The value that the tween started from. 11679 * @param {number} end The value that the tween should end at. 11680 * @param {Function} easingFunc The easing curve to apply to the tween. 11681 * @param {number} position The normalized position (between 0.0 and 1.0) to 11682 * calculate the midpoint of 'start' and 'end' against. 11683 * @return {number} The tweened value. 11684 * @private 11685 */ 11686 function tweenProp (start, end, easingFunc, position) { 11687 return start + (end - start) * easingFunc(position); 11688 } 11689 11690 /** 11691 * Applies a filter to NGTweenable instance. 11692 * @param {NGTweenable} tweenable The `NGTweenable` instance to call the filter 11693 * upon. 11694 * @param {String} filterName The name of the filter to apply. 11695 * @private 11696 */ 11697 function applyFilter (tweenable, filterName) { 11698 var filters = NGTweenable.prototype.filter; 11699 var args = tweenable._filterArgs; 11700 11701 each(filters, function (name) { 11702 if (typeof filters[name][filterName] !== 'undefined') { 11703 filters[name][filterName].apply(tweenable, args); 11704 } 11705 }); 11706 } 11707 11708 var timeoutHandler_endTime; 11709 var timeoutHandler_currentTime; 11710 var timeoutHandler_isEnded; 11711 var timeoutHandler_offset; 11712 /** 11713 * Handles the update logic for one step of a tween. 11714 * @param {NGTweenable} tweenable 11715 * @param {number} timestamp 11716 * @param {number} delay 11717 * @param {number} duration 11718 * @param {Object} currentState 11719 * @param {Object} originalState 11720 * @param {Object} targetState 11721 * @param {Object} easing 11722 * @param {Function(Object, *, number)} step 11723 * @param {Function(Function,number)}} schedule 11724 * @param {number=} opt_currentTimeOverride Needed for accurate timestamp in 11725 * NGTweenable#seek. 11726 * @private 11727 */ 11728 function timeoutHandler (tweenable, timestamp, delay, duration, currentState, 11729 originalState, targetState, easing, step, schedule, 11730 opt_currentTimeOverride) { 11731 11732 timeoutHandler_endTime = timestamp + delay + duration; 11733 11734 timeoutHandler_currentTime = 11735 Math.min(opt_currentTimeOverride || now(), timeoutHandler_endTime); 11736 11737 timeoutHandler_isEnded = 11738 timeoutHandler_currentTime >= timeoutHandler_endTime; 11739 11740 timeoutHandler_offset = duration - ( 11741 timeoutHandler_endTime - timeoutHandler_currentTime); 11742 11743 if (tweenable.isPlaying()) { 11744 if (timeoutHandler_isEnded) { 11745 step(targetState, tweenable._attachment, timeoutHandler_offset); 11746 tweenable.stop(true); 11747 } else { 11748 tweenable._scheduleId = 11749 schedule(tweenable._timeoutHandler, UPDATE_TIME); 11750 11751 applyFilter(tweenable, 'beforeTween'); 11752 11753 // If the animation has not yet reached the start point (e.g., there was 11754 // delay that has not yet completed), just interpolate the starting 11755 // position of the tween. 11756 if (timeoutHandler_currentTime < (timestamp + delay)) { 11757 tweenProps(1, currentState, originalState, targetState, 1, 1, easing); 11758 } else { 11759 tweenProps(timeoutHandler_currentTime, currentState, originalState, 11760 targetState, duration, timestamp + delay, easing); 11761 } 11762 11763 applyFilter(tweenable, 'afterTween'); 11764 11765 step(currentState, tweenable._attachment, timeoutHandler_offset); 11766 } 11767 } 11768 } 11769 11770 11771 /** 11772 * Creates a usable easing Object from a string, a function or another easing 11773 * Object. If `easing` is an Object, then this function clones it and fills 11774 * in the missing properties with `"linear"`. 11775 * @param {Object.<string|Function>} fromTweenParams 11776 * @param {Object|string|Function} easing 11777 * @return {Object.<string|Function>} 11778 * @private 11779 */ 11780 function composeEasingObject (fromTweenParams, easing) { 11781 var composedEasing = {}; 11782 var typeofEasing = typeof easing; 11783 11784 if (typeofEasing === 'string' || typeofEasing === 'function') { 11785 each(fromTweenParams, function (prop) { 11786 composedEasing[prop] = easing; 11787 }); 11788 } else { 11789 each(fromTweenParams, function (prop) { 11790 if (!composedEasing[prop]) { 11791 composedEasing[prop] = easing[prop] || DEFAULT_EASING; 11792 } 11793 }); 11794 } 11795 11796 return composedEasing; 11797 } 11798 11799 /** 11800 * NGTweenable constructor. 11801 * @class NGTweenable 11802 * @param {Object=} opt_initialState The values that the initial tween should 11803 * start at if a `from` object is not provided to `{{#crossLink 11804 * "NGTweenable/tween:method"}}{{/crossLink}}` or `{{#crossLink 11805 * "NGTweenable/setConfig:method"}}{{/crossLink}}`. 11806 * @param {Object=} opt_config Configuration object to be passed to 11807 * `{{#crossLink "NGTweenable/setConfig:method"}}{{/crossLink}}`. 11808 * @module NGTweenable 11809 * @constructor 11810 */ 11811 function NGTweenable (opt_initialState, opt_config) { 11812 this._currentState = opt_initialState || {}; 11813 this._configured = false; 11814 this._scheduleFunction = DEFAULT_SCHEDULE_FUNCTION; 11815 11816 // To prevent unnecessary calls to setConfig do not set default 11817 // configuration here. Only set default configuration immediately before 11818 // tweening if none has been set. 11819 if (typeof opt_config !== 'undefined') { 11820 this.setConfig(opt_config); 11821 } 11822 } 11823 11824 /** 11825 * Configure and start a tween. 11826 * @method tween 11827 * @param {Object=} opt_config Configuration object to be passed to 11828 * `{{#crossLink "NGTweenable/setConfig:method"}}{{/crossLink}}`. 11829 * @chainable 11830 */ 11831 NGTweenable.prototype.tween = function (opt_config) { 11832 if (this._isTweening) { 11833 return this; 11834 } 11835 11836 // Only set default config if no configuration has been set previously and 11837 // none is provided now. 11838 if (opt_config !== undefined || !this._configured) { 11839 this.setConfig(opt_config); 11840 } 11841 11842 this._timestamp = now(); 11843 this._start(this.get(), this._attachment); 11844 return this.resume(); 11845 }; 11846 11847 /** 11848 * Configure a tween that will start at some point in the future. 11849 * 11850 * @method setConfig 11851 * @param {Object} config The following values are valid: 11852 * - __from__ (_Object=_): Starting position. If omitted, `{{#crossLink 11853 * "NGTweenable/get:method"}}get(){{/crossLink}}` is used. 11854 * - __to__ (_Object=_): Ending position. 11855 * - __duration__ (_number=_): How many milliseconds to animate for. 11856 * - __delay__ (_delay=_): How many milliseconds to wait before starting the 11857 * tween. 11858 * - __start__ (_Function(Object, *)_): Function to execute when the tween 11859 * begins. Receives the state of the tween as the first parameter and 11860 * `attachment` as the second parameter. 11861 * - __step__ (_Function(Object, *, number)_): Function to execute on every 11862 * tick. Receives `{{#crossLink 11863 * "NGTweenable/get:method"}}get(){{/crossLink}}` as the first parameter, 11864 * `attachment` as the second parameter, and the time elapsed since the 11865 * start of the tween as the third. This function is not called on the 11866 * final step of the animation, but `finish` is. 11867 * - __finish__ (_Function(Object, *)_): Function to execute upon tween 11868 * completion. Receives the state of the tween as the first parameter and 11869 * `attachment` as the second parameter. 11870 * - __easing__ (_Object.<string|Function>|string|Function=_): Easing curve 11871 * name(s) or function(s) to use for the tween. 11872 * - __attachment__ (_*_): Cached value that is passed to the 11873 * `step`/`start`/`finish` methods. 11874 * @chainable 11875 */ 11876 NGTweenable.prototype.setConfig = function (config) { 11877 config = config || {}; 11878 this._configured = true; 11879 11880 // Attach something to this NGTweenable instance (e.g.: a DOM element, an 11881 // object, a string, etc.); 11882 this._attachment = config.attachment; 11883 11884 // Init the internal state 11885 this._pausedAtTime = null; 11886 this._scheduleId = null; 11887 this._delay = config.delay || 0; 11888 this._start = config.start || noop; 11889 this._step = config.step || noop; 11890 this._finish = config.finish || noop; 11891 this._duration = config.duration || DEFAULT_DURATION; 11892 this._currentState = shallowCopy({}, config.from || this.get()); 11893 this._originalState = this.get(); 11894 this._targetState = shallowCopy({}, config.to || this.get()); 11895 11896 var self = this; 11897 this._timeoutHandler = function () { 11898 timeoutHandler(self, 11899 self._timestamp, 11900 self._delay, 11901 self._duration, 11902 self._currentState, 11903 self._originalState, 11904 self._targetState, 11905 self._easing, 11906 self._step, 11907 self._scheduleFunction 11908 ); 11909 }; 11910 11911 // Aliases used below 11912 var currentState = this._currentState; 11913 var targetState = this._targetState; 11914 11915 // Ensure that there is always something to tween to. 11916 defaults(targetState, currentState); 11917 11918 this._easing = composeEasingObject( 11919 currentState, config.easing || DEFAULT_EASING); 11920 11921 this._filterArgs = 11922 [currentState, this._originalState, targetState, this._easing]; 11923 11924 applyFilter(this, 'tweenCreated'); 11925 return this; 11926 }; 11927 11928 /** 11929 * @method get 11930 * @return {Object} The current state. 11931 */ 11932 NGTweenable.prototype.get = function () { 11933 return shallowCopy({}, this._currentState); 11934 }; 11935 11936 /** 11937 * @method set 11938 * @param {Object} state The current state. 11939 */ 11940 NGTweenable.prototype.set = function (state) { 11941 this._currentState = state; 11942 }; 11943 11944 /** 11945 * Pause a tween. Paused tweens can be resumed from the point at which they 11946 * were paused. This is different from `{{#crossLink 11947 * "NGTweenable/stop:method"}}{{/crossLink}}`, as that method 11948 * causes a tween to start over when it is resumed. 11949 * @method pause 11950 * @chainable 11951 */ 11952 NGTweenable.prototype.pause = function () { 11953 this._pausedAtTime = now(); 11954 this._isPaused = true; 11955 return this; 11956 }; 11957 11958 /** 11959 * Resume a paused tween. 11960 * @method resume 11961 * @chainable 11962 */ 11963 NGTweenable.prototype.resume = function () { 11964 if (this._isPaused) { 11965 this._timestamp += now() - this._pausedAtTime; 11966 } 11967 11968 this._isPaused = false; 11969 this._isTweening = true; 11970 11971 this._timeoutHandler(); 11972 11973 return this; 11974 }; 11975 11976 /** 11977 * Move the state of the animation to a specific point in the tween's 11978 * timeline. If the animation is not running, this will cause the `step` 11979 * handlers to be called. 11980 * @method seek 11981 * @param {millisecond} millisecond The millisecond of the animation to seek 11982 * to. This must not be less than `0`. 11983 * @chainable 11984 */ 11985 NGTweenable.prototype.seek = function (millisecond) { 11986 millisecond = Math.max(millisecond, 0); 11987 var currentTime = now(); 11988 11989 if ((this._timestamp + millisecond) === 0) { 11990 return this; 11991 } 11992 11993 this._timestamp = currentTime - millisecond; 11994 11995 if (!this.isPlaying()) { 11996 this._isTweening = true; 11997 this._isPaused = false; 11998 11999 // If the animation is not running, call timeoutHandler to make sure that 12000 // any step handlers are run. 12001 timeoutHandler(this, 12002 this._timestamp, 12003 this._delay, 12004 this._duration, 12005 this._currentState, 12006 this._originalState, 12007 this._targetState, 12008 this._easing, 12009 this._step, 12010 this._scheduleFunction, 12011 currentTime 12012 ); 12013 12014 this.pause(); 12015 } 12016 12017 return this; 12018 }; 12019 12020 /** 12021 * Stops and cancels a tween. 12022 * @param {boolean=} gotoEnd If `false` or omitted, the tween just stops at 12023 * its current state, and the `finish` handler is not invoked. If `true`, 12024 * the tweened object's values are instantly set to the target values, and 12025 * `finish` is invoked. 12026 * @method stop 12027 * @chainable 12028 */ 12029 NGTweenable.prototype.stop = function (gotoEnd) { 12030 this._isTweening = false; 12031 this._isPaused = false; 12032 this._timeoutHandler = noop; 12033 12034 (root.cancelAnimationFrame || 12035 root.webkitCancelAnimationFrame || 12036 root.oCancelAnimationFrame || 12037 root.msCancelAnimationFrame || 12038 root.mozCancelRequestAnimationFrame || 12039 root.clearTimeout)(this._scheduleId); 12040 12041 if (gotoEnd) { 12042 applyFilter(this, 'beforeTween'); 12043 tweenProps( 12044 1, 12045 this._currentState, 12046 this._originalState, 12047 this._targetState, 12048 1, 12049 0, 12050 this._easing 12051 ); 12052 applyFilter(this, 'afterTween'); 12053 applyFilter(this, 'afterTweenEnd'); 12054 this._finish.call(this, this._currentState, this._attachment); 12055 } 12056 12057 return this; 12058 }; 12059 12060 /** 12061 * @method isPlaying 12062 * @return {boolean} Whether or not a tween is running. 12063 */ 12064 NGTweenable.prototype.isPlaying = function () { 12065 return this._isTweening && !this._isPaused; 12066 }; 12067 12068 /** 12069 * Set a custom schedule function. 12070 * 12071 * If a custom function is not set, 12072 * [`requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame) 12073 * is used if available, otherwise 12074 * [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/Window.setTimeout) 12075 * is used. 12076 * @method setScheduleFunction 12077 * @param {Function(Function,number)} scheduleFunction The function to be 12078 * used to schedule the next frame to be rendered. 12079 */ 12080 NGTweenable.prototype.setScheduleFunction = function (scheduleFunction) { 12081 this._scheduleFunction = scheduleFunction; 12082 }; 12083 12084 /** 12085 * `delete` all "own" properties. Call this when the `NGTweenable` instance 12086 * is no longer needed to free memory. 12087 * @method dispose 12088 */ 12089 NGTweenable.prototype.dispose = function () { 12090 var prop; 12091 for (prop in this) { 12092 if (this.hasOwnProperty(prop)) { 12093 delete this[prop]; 12094 } 12095 } 12096 }; 12097 12098 /** 12099 * Filters are used for transforming the properties of a tween at various 12100 * points in a NGTweenable's life cycle. See the README for more info on this. 12101 * @private 12102 */ 12103 NGTweenable.prototype.filter = {}; 12104 12105 /** 12106 * This object contains all of the tweens available to Shifty. It is 12107 * extensible - simply attach properties to the `NGTweenable.prototype.formula` 12108 * Object following the same format as `linear`. 12109 * 12110 * `pos` should be a normalized `number` (between 0 and 1). 12111 * @property formula 12112 * @type {Object(function)} 12113 */ 12114 NGTweenable.prototype.formula = { 12115 linear: function (pos) { 12116 return pos; 12117 } 12118 }; 12119 12120 formula = NGTweenable.prototype.formula; 12121 12122 shallowCopy(NGTweenable, { 12123 'now': now 12124 ,'each': each 12125 ,'tweenProps': tweenProps 12126 ,'tweenProp': tweenProp 12127 ,'applyFilter': applyFilter 12128 ,'shallowCopy': shallowCopy 12129 ,'defaults': defaults 12130 ,'composeEasingObject': composeEasingObject 12131 }); 12132 12133 // `root` is provided in the intro/outro files. 12134 12135 // A hook used for unit testing. 12136 if (typeof SHIFTY_DEBUG_NOW === 'function') { 12137 root.timeoutHandler = timeoutHandler; 12138 } 12139 12140 // Bootstrap NGTweenable appropriately for the environment. 12141 if (typeof exports === 'object') { 12142 // CommonJS 12143 module.exports = NGTweenable; 12144 } else if (typeof define === 'function' && define.amdDISABLED) { 12145 // AMD 12146 define(function () {return NGTweenable;}); 12147 } else if (typeof root.NGTweenable === 'undefined') { 12148 // Browser: Make `NGTweenable` globally accessible. 12149 root.NGTweenable = NGTweenable; 12150 } 12151 12152 return NGTweenable; 12153 12154} ()); 12155 12156/*! 12157 * All equations are adapted from Thomas Fuchs' 12158 * [Scripty2](https://github.com/madrobby/scripty2/blob/master/src/effects/transitions/penner.js). 12159 * 12160 * Based on Easing Equations (c) 2003 [Robert 12161 * Penner](http://www.robertpenner.com/), all rights reserved. This work is 12162 * [subject to terms](http://www.robertpenner.com/easing_terms_of_use.html). 12163 */ 12164 12165/*! 12166 * TERMS OF USE - EASING EQUATIONS 12167 * Open source under the BSD License. 12168 * Easing Equations (c) 2003 Robert Penner, all rights reserved. 12169 */ 12170 12171;(function () { 12172 12173 NGTweenable.shallowCopy(NGTweenable.prototype.formula, { 12174 easeInQuad: function (pos) { 12175 return Math.pow(pos, 2); 12176 }, 12177 12178 easeOutQuad: function (pos) { 12179 return -(Math.pow((pos - 1), 2) - 1); 12180 }, 12181 12182 easeInOutQuad: function (pos) { 12183 if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,2);} 12184 return -0.5 * ((pos -= 2) * pos - 2); 12185 }, 12186 12187 easeInCubic: function (pos) { 12188 return Math.pow(pos, 3); 12189 }, 12190 12191 easeOutCubic: function (pos) { 12192 return (Math.pow((pos - 1), 3) + 1); 12193 }, 12194 12195 easeInOutCubic: function (pos) { 12196 if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,3);} 12197 return 0.5 * (Math.pow((pos - 2),3) + 2); 12198 }, 12199 12200 easeInQuart: function (pos) { 12201 return Math.pow(pos, 4); 12202 }, 12203 12204 easeOutQuart: function (pos) { 12205 return -(Math.pow((pos - 1), 4) - 1); 12206 }, 12207 12208 easeInOutQuart: function (pos) { 12209 if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,4);} 12210 return -0.5 * ((pos -= 2) * Math.pow(pos,3) - 2); 12211 }, 12212 12213 easeInQuint: function (pos) { 12214 return Math.pow(pos, 5); 12215 }, 12216 12217 easeOutQuint: function (pos) { 12218 return (Math.pow((pos - 1), 5) + 1); 12219 }, 12220 12221 easeInOutQuint: function (pos) { 12222 if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,5);} 12223 return 0.5 * (Math.pow((pos - 2),5) + 2); 12224 }, 12225 12226 easeInSine: function (pos) { 12227 return -Math.cos(pos * (Math.PI / 2)) + 1; 12228 }, 12229 12230 easeOutSine: function (pos) { 12231 return Math.sin(pos * (Math.PI / 2)); 12232 }, 12233 12234 easeInOutSine: function (pos) { 12235 return (-0.5 * (Math.cos(Math.PI * pos) - 1)); 12236 }, 12237 12238 easeInExpo: function (pos) { 12239 return (pos === 0) ? 0 : Math.pow(2, 10 * (pos - 1)); 12240 }, 12241 12242 easeOutExpo: function (pos) { 12243 return (pos === 1) ? 1 : -Math.pow(2, -10 * pos) + 1; 12244 }, 12245 12246 easeInOutExpo: function (pos) { 12247 if (pos === 0) {return 0;} 12248 if (pos === 1) {return 1;} 12249 if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(2,10 * (pos - 1));} 12250 return 0.5 * (-Math.pow(2, -10 * --pos) + 2); 12251 }, 12252 12253 easeInCirc: function (pos) { 12254 return -(Math.sqrt(1 - (pos * pos)) - 1); 12255 }, 12256 12257 easeOutCirc: function (pos) { 12258 return Math.sqrt(1 - Math.pow((pos - 1), 2)); 12259 }, 12260 12261 easeInOutCirc: function (pos) { 12262 if ((pos /= 0.5) < 1) {return -0.5 * (Math.sqrt(1 - pos * pos) - 1);} 12263 return 0.5 * (Math.sqrt(1 - (pos -= 2) * pos) + 1); 12264 }, 12265 12266 easeOutBounce: function (pos) { 12267 if ((pos) < (1 / 2.75)) { 12268 return (7.5625 * pos * pos); 12269 } else if (pos < (2 / 2.75)) { 12270 return (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75); 12271 } else if (pos < (2.5 / 2.75)) { 12272 return (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375); 12273 } else { 12274 return (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375); 12275 } 12276 }, 12277 12278 easeInBack: function (pos) { 12279 var s = 1.70158; 12280 return (pos) * pos * ((s + 1) * pos - s); 12281 }, 12282 12283 easeOutBack: function (pos) { 12284 var s = 1.70158; 12285 return (pos = pos - 1) * pos * ((s + 1) * pos + s) + 1; 12286 }, 12287 12288 easeInOutBack: function (pos) { 12289 var s = 1.70158; 12290 if ((pos /= 0.5) < 1) { 12291 return 0.5 * (pos * pos * (((s *= (1.525)) + 1) * pos - s)); 12292 } 12293 return 0.5 * ((pos -= 2) * pos * (((s *= (1.525)) + 1) * pos + s) + 2); 12294 }, 12295 12296 elastic: function (pos) { 12297 // jshint maxlen:90 12298 return -1 * Math.pow(4,-8 * pos) * Math.sin((pos * 6 - 1) * (2 * Math.PI) / 2) + 1; 12299 }, 12300 12301 swingFromTo: function (pos) { 12302 var s = 1.70158; 12303 return ((pos /= 0.5) < 1) ? 12304 0.5 * (pos * pos * (((s *= (1.525)) + 1) * pos - s)) : 12305 0.5 * ((pos -= 2) * pos * (((s *= (1.525)) + 1) * pos + s) + 2); 12306 }, 12307 12308 swingFrom: function (pos) { 12309 var s = 1.70158; 12310 return pos * pos * ((s + 1) * pos - s); 12311 }, 12312 12313 swingTo: function (pos) { 12314 var s = 1.70158; 12315 return (pos -= 1) * pos * ((s + 1) * pos + s) + 1; 12316 }, 12317 12318 bounce: function (pos) { 12319 if (pos < (1 / 2.75)) { 12320 return (7.5625 * pos * pos); 12321 } else if (pos < (2 / 2.75)) { 12322 return (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75); 12323 } else if (pos < (2.5 / 2.75)) { 12324 return (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375); 12325 } else { 12326 return (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375); 12327 } 12328 }, 12329 12330 bouncePast: function (pos) { 12331 if (pos < (1 / 2.75)) { 12332 return (7.5625 * pos * pos); 12333 } else if (pos < (2 / 2.75)) { 12334 return 2 - (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75); 12335 } else if (pos < (2.5 / 2.75)) { 12336 return 2 - (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375); 12337 } else { 12338 return 2 - (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375); 12339 } 12340 }, 12341 12342 easeFromTo: function (pos) { 12343 if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,4);} 12344 return -0.5 * ((pos -= 2) * Math.pow(pos,3) - 2); 12345 }, 12346 12347 easeFrom: function (pos) { 12348 return Math.pow(pos,4); 12349 }, 12350 12351 easeTo: function (pos) { 12352 return Math.pow(pos,0.25); 12353 } 12354 }); 12355 12356}()); 12357 12358// jshint maxlen:100 12359/** 12360 * The Bezier magic in this file is adapted/copied almost wholesale from 12361 * [Scripty2](https://github.com/madrobby/scripty2/blob/master/src/effects/transitions/cubic-bezier.js), 12362 * which was adapted from Apple code (which probably came from 12363 * [here](http://opensource.apple.com/source/WebCore/WebCore-955.66/platform/graphics/UnitBezier.h)). 12364 * Special thanks to Apple and Thomas Fuchs for much of this code. 12365 */ 12366 12367/** 12368 * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. 12369 * 12370 * Redistribution and use in source and binary forms, with or without 12371 * modification, are pfervereted provided that the following conditions are met: 12372 * 12373 * 1. Redistributions of source code must retain the above copyright notice, 12374 * this list of conditions and the following disclaimer. 12375 * 12376 * 2. Redistributions in binary form must reproduce the above copyright notice, 12377 * this list of conditions and the following disclaimer in the documentation 12378 * and/or other materials provided with the distribution. 12379 * 12380 * 3. Neither the name of the copyright holder(s) nor the names of any 12381 * contributors may be used to endorse or promote products derived from 12382 * this software without specific prior written permission. 12383 * 12384 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 12385 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 12386 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 12387 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 12388 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 12389 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 12390 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 12391 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 12392 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 12393 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 12394 * POSSIBILITY OF SUCH DAMAGE. 12395 */ 12396;(function () { 12397 // port of webkit cubic bezier handling by http://www.netzgesta.de/dev/ 12398 function cubicBezierAtTime(t,p1x,p1y,p2x,p2y,duration) { 12399 var ax = 0,bx = 0,cx = 0,ay = 0,by = 0,cy = 0; 12400 function sampleCurveX(t) { 12401 return ((ax * t + bx) * t + cx) * t; 12402 } 12403 function sampleCurveY(t) { 12404 return ((ay * t + by) * t + cy) * t; 12405 } 12406 function sampleCurveDerivativeX(t) { 12407 return (3.0 * ax * t + 2.0 * bx) * t + cx; 12408 } 12409 function solveEpsilon(duration) { 12410 return 1.0 / (200.0 * duration); 12411 } 12412 function solve(x,epsilon) { 12413 return sampleCurveY(solveCurveX(x, epsilon)); 12414 } 12415 function fabs(n) { 12416 if (n >= 0) { 12417 return n; 12418 } else { 12419 return 0 - n; 12420 } 12421 } 12422 function solveCurveX(x, epsilon) { 12423 var t0,t1,t2,x2,d2,i; 12424 for (t2 = x, i = 0; i < 8; i++) { 12425 x2 = sampleCurveX(t2) - x; 12426 if (fabs(x2) < epsilon) { 12427 return t2; 12428 } 12429 d2 = sampleCurveDerivativeX(t2); 12430 if (fabs(d2) < 1e-6) { 12431 break; 12432 } 12433 t2 = t2 - x2 / d2; 12434 } 12435 t0 = 0.0; 12436 t1 = 1.0; 12437 t2 = x; 12438 if (t2 < t0) { 12439 return t0; 12440 } 12441 if (t2 > t1) { 12442 return t1; 12443 } 12444 while (t0 < t1) { 12445 x2 = sampleCurveX(t2); 12446 if (fabs(x2 - x) < epsilon) { 12447 return t2; 12448 } 12449 if (x > x2) { 12450 t0 = t2; 12451 }else { 12452 t1 = t2; 12453 } 12454 t2 = (t1 - t0) * 0.5 + t0; 12455 } 12456 return t2; // Failure. 12457 } 12458 cx = 3.0 * p1x; 12459 bx = 3.0 * (p2x - p1x) - cx; 12460 ax = 1.0 - cx - bx; 12461 cy = 3.0 * p1y; 12462 by = 3.0 * (p2y - p1y) - cy; 12463 ay = 1.0 - cy - by; 12464 return solve(t, solveEpsilon(duration)); 12465 } 12466 /** 12467 * getCubicBezierTransition(x1, y1, x2, y2) -> Function 12468 * 12469 * Generates a transition easing function that is compatible 12470 * with WebKit's CSS transitions `-webkit-transition-timing-function` 12471 * CSS property. 12472 * 12473 * The W3C has more information about CSS3 transition timing functions: 12474 * http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag 12475 * 12476 * @param {number} x1 12477 * @param {number} y1 12478 * @param {number} x2 12479 * @param {number} y2 12480 * @return {function} 12481 * @private 12482 */ 12483 function getCubicBezierTransition (x1, y1, x2, y2) { 12484 return function (pos) { 12485 return cubicBezierAtTime(pos,x1,y1,x2,y2,1); 12486 }; 12487 } 12488 // End ported code 12489 12490 /** 12491 * Create a Bezier easing function and attach it to `{{#crossLink 12492 * "NGTweenable/formula:property"}}NGTweenable#formula{{/crossLink}}`. This 12493 * function gives you total control over the easing curve. Matthew Lein's 12494 * [Ceaser](http://matthewlein.com/ceaser/) is a useful tool for visualizing 12495 * the curves you can make with this function. 12496 * @method setBezierFunction 12497 * @param {string} name The name of the easing curve. Overwrites the old 12498 * easing function on `{{#crossLink 12499 * "NGTweenable/formula:property"}}NGTweenable#formula{{/crossLink}}` if it 12500 * exists. 12501 * @param {number} x1 12502 * @param {number} y1 12503 * @param {number} x2 12504 * @param {number} y2 12505 * @return {function} The easing function that was attached to 12506 * NGTweenable.prototype.formula. 12507 */ 12508 NGTweenable.setBezierFunction = function (name, x1, y1, x2, y2) { 12509 var cubicBezierTransition = getCubicBezierTransition(x1, y1, x2, y2); 12510 cubicBezierTransition.displayName = name; 12511 cubicBezierTransition.x1 = x1; 12512 cubicBezierTransition.y1 = y1; 12513 cubicBezierTransition.x2 = x2; 12514 cubicBezierTransition.y2 = y2; 12515 12516 return NGTweenable.prototype.formula[name] = cubicBezierTransition; 12517 }; 12518 12519 12520 /** 12521 * `delete` an easing function from `{{#crossLink 12522 * "NGTweenable/formula:property"}}NGTweenable#formula{{/crossLink}}`. Be 12523 * careful with this method, as it `delete`s whatever easing formula matches 12524 * `name` (which means you can delete standard Shifty easing functions). 12525 * @method unsetBezierFunction 12526 * @param {string} name The name of the easing function to delete. 12527 * @return {function} 12528 */ 12529 NGTweenable.unsetBezierFunction = function (name) { 12530 delete NGTweenable.prototype.formula[name]; 12531 }; 12532 12533})(); 12534 12535;(function () { 12536 12537 function getInterpolatedValues ( 12538 from, current, targetState, position, easing, delay) { 12539 return NGTweenable.tweenProps( 12540 position, current, from, targetState, 1, delay, easing); 12541 } 12542 12543 // Fake a NGTweenable and patch some internals. This approach allows us to
12544 // skip uneccessary processing and object recreation, cutting down on garbage 12545 // collection pauses. 12546 var mockNGTweenable = new NGTweenable(); 12547 mockNGTweenable._filterArgs = []; 12548 12549 /** 12550 * Compute the midpoint of two Objects. This method effectively calculates a 12551 * specific frame of animation that `{{#crossLink 12552 * "NGTweenable/tween:method"}}{{/crossLink}}` does many times over the course 12553 * of a full tween. 12554 * 12555 * var interpolatedValues = NGTweenable.interpolate({ 12556 * width: '100px', 12557 * opacity: 0, 12558 * color: '#fff' 12559 * }, { 12560 * width: '200px', 12561 * opacity: 1, 12562 * color: '#000' 12563 * }, 0.5); 12564 * 12565 * console.log(interpolatedValues); 12566 * // {opacity: 0.5, width: "150px", color: "rgb(127,127,127)"} 12567 * 12568 * @static 12569 * @method interpolate 12570 * @param {Object} from The starting values to tween from. 12571 * @param {Object} targetState The ending values to tween to. 12572 * @param {number} position The normalized position value (between `0.0` and 12573 * `1.0`) to interpolate the values between `from` and `to` for. `from` 12574 * represents `0` and `to` represents `1`. 12575 * @param {Object.<string|Function>|string|Function} easing The easing 12576 * curve(s) to calculate the midpoint against. You can reference any easing 12577 * function attached to `NGTweenable.prototype.formula`, or provide the easing 12578 * function(s) directly. If omitted, this defaults to "linear". 12579 * @param {number=} opt_delay Optional delay to pad the beginning of the 12580 * interpolated tween with. This increases the range of `position` from (`0` 12581 * through `1`) to (`0` through `1 + opt_delay`). So, a delay of `0.5` would 12582 * increase all valid values of `position` to numbers between `0` and `1.5`. 12583 * @return {Object} 12584 */ 12585 NGTweenable.interpolate = function ( 12586 from, targetState, position, easing, opt_delay) { 12587 12588 var current = NGTweenable.shallowCopy({}, from); 12589 var delay = opt_delay || 0; 12590 var easingObject = NGTweenable.composeEasingObject( 12591 from, easing || 'linear'); 12592 12593 mockNGTweenable.set({}); 12594 12595 // Alias and reuse the _filterArgs array instead of recreating it. 12596 var filterArgs = mockNGTweenable._filterArgs; 12597 filterArgs.length = 0; 12598 filterArgs[0] = current; 12599 filterArgs[1] = from; 12600 filterArgs[2] = targetState; 12601 filterArgs[3] = easingObject; 12602 12603 // Any defined value transformation must be applied 12604 NGTweenable.applyFilter(mockNGTweenable, 'tweenCreated'); 12605 NGTweenable.applyFilter(mockNGTweenable, 'beforeTween'); 12606 12607 var interpolatedValues = getInterpolatedValues( 12608 from, current, targetState, position, easingObject, delay); 12609 12610 // Transform values back into their original format 12611 NGTweenable.applyFilter(mockNGTweenable, 'afterTween'); 12612 12613 return interpolatedValues; 12614 }; 12615 12616}()); 12617 12618/** 12619 * This module adds string interpolation support to Shifty. 12620 * 12621 * The Token extension allows Shifty to tween numbers inside of strings. Among 12622 * other things, this allows you to animate CSS properties. For example, you 12623 * can do this: 12624 * 12625 * var tweenable = new NGTweenable(); 12626 * tweenable.tween({ 12627 * from: { transform: 'translateX(45px)' }, 12628 * to: { transform: 'translateX(90xp)' } 12629 * }); 12630 * 12631 * `translateX(45)` will be tweened to `translateX(90)`. To demonstrate: 12632 * 12633 * var tweenable = new NGTweenable(); 12634 * tweenable.tween({ 12635 * from: { transform: 'translateX(45px)' }, 12636 * to: { transform: 'translateX(90px)' }, 12637 * step: function (state) { 12638 * console.log(state.transform); 12639 * } 12640 * }); 12641 * 12642 * The above snippet will log something like this in the console: 12643 * 12644 * translateX(60.3px) 12645 * ... 12646 * translateX(76.05px) 12647 * ... 12648 * translateX(90px) 12649 * 12650 * Another use for this is animating colors: 12651 * 12652 * var tweenable = new NGTweenable(); 12653 * tweenable.tween({ 12654 * from: { color: 'rgb(0,255,0)' }, 12655 * to: { color: 'rgb(255,0,255)' }, 12656 * step: function (state) { 12657 * console.log(state.color); 12658 * } 12659 * }); 12660 * 12661 * The above snippet will log something like this: 12662 * 12663 * rgb(84,170,84) 12664 * ... 12665 * rgb(170,84,170) 12666 * ... 12667 * rgb(255,0,255) 12668 * 12669 * This extension also supports hexadecimal colors, in both long (`#ff00ff`) 12670 * and short (`#f0f`) forms. Be aware that hexadecimal input values will be 12671 * converted into the equivalent RGB output values. This is done to optimize 12672 * for performance. 12673 *
12674 * var tweenable = new NGTweenable(); 12675 * tweenable.tween({ 12676 * from: { color: '#0f0' }, 12677 * to: { color: '#f0f' }, 12678 * step: function (state) { 12679 * console.log(state.color); 12680 * } 12681 * }); 12682 * 12683 * This snippet will generate the same output as the one before it because 12684 * equivalent values were supplied (just in hexadecimal form rather than RGB): 12685 * 12686 * rgb(84,170,84) 12687 * ... 12688 * rgb(170,84,170) 12689 * ... 12690 * rgb(255,0,255) 12691 * 12692 * ## Easing support 12693 * 12694 * Easing works somewhat differently in the Token extension. This is because 12695 * some CSS properties have multiple values in them, and you might need to 12696 * tween each value along its own easing curve. A basic example: 12697 * 12698 * var tweenable = new NGTweenable(); 12699 * tweenable.tween({ 12700 * from: { transform: 'translateX(0px) translateY(0px)' }, 12701 * to: { transform: 'translateX(100px) translateY(100px)' }, 12702 * easing: { transform: 'easeInQuad' }, 12703 * step: function (state) { 12704 * console.log(state.transform); 12705 * } 12706 * }); 12707 * 12708 * The above snippet will create values like this: 12709 * 12710 * translateX(11.56px) translateY(11.56px) 12711 * ... 12712 * translateX(46.24px) translateY(46.24px) 12713 * ... 12714 * translateX(100px) translateY(100px) 12715 * 12716 * In this case, the values for `translateX` and `translateY` are always the 12717 * same for each step of the tween, because they have the same start and end 12718 * points and both use the same easing curve. We can also tween `translateX` 12719 * and `translateY` along independent curves: 12720 * 12721 * var tweenable = new NGTweenable(); 12722 * tweenable.tween({ 12723 * from: { transform: 'translateX(0px) translateY(0px)' }, 12724 * to: { transform: 'translateX(100px) translateY(100px)' }, 12725 * easing: { transform: 'easeInQuad bounce' }, 12726 * step: function (state) { 12727 * console.log(state.transform); 12728 * } 12729 * }); 12730 * 12731 * The above snippet will create values like this: 12732 * 12733 * translateX(10.89px) translateY(82.35px) 12734 * ... 12735 * translateX(44.89px) translateY(86.73px) 12736 * ... 12737 * translateX(100px) translateY(100px) 12738 * 12739 * `translateX` and `translateY` are not in sync anymore, because `easeInQuad` 12740 * was specified for `translateX` and `bounce` for `translateY`. Mixing and 12741 * matching easing curves can make for some interesting motion in your 12742 * animations. 12743 * 12744 * The order of the space-separated easing curves correspond the token values 12745 * they apply to. If there are more token values than easing curves listed, 12746 * the last easing curve listed is used. 12747 * @submodule NGTweenable.token 12748 */ 12749 12750// token function is defined above only so that dox-foundation sees it as 12751// documentation and renders it. It is never used, and is optimized away at 12752// build time. 12753 12754;(function (NGTweenable) { 12755 12756 /** 12757 * @typedef {{ 12758 * formatString: string 12759 * chunkNames: Array.<string> 12760 * }} 12761 * @private 12762 */ 12763 var formatManifest; 12764 12765 // CONSTANTS 12766 12767 var R_NUMBER_COMPONENT = /(\d|\-|\.)/; 12768 var R_FORMAT_CHUNKS = /([^\-0-9\.]+)/g; 12769 var R_UNFORMATTED_VALUES = /[0-9.\-]+/g; 12770 var R_RGB = new RegExp( 12771 'rgb\\(' + R_UNFORMATTED_VALUES.source + 12772 (/,\s*/.source) + R_UNFORMATTED_VALUES.source + 12773 (/,\s*/.source) + R_UNFORMATTED_VALUES.source + '\\)', 'g'); 12774 var R_RGB_PREFIX = /^.*\(/; 12775 var R_HEX = /#([0-9]|[a-f]){3,6}/gi; 12776 var VALUE_PLACEHOLDER = 'VAL'; 12777 12778 // HELPERS 12779 12780 /** 12781 * @param {Array.number} rawValues 12782 * @param {string} prefix 12783 * 12784 * @return {Array.<string>} 12785 * @private 12786 */
vendor: 13,135 bytes, lines 12787-13251
12787 function getFormatChunksFrom (rawValues, prefix) { 12788 var accumulator = []; 12789 12790 var rawValuesLength = rawValues.length; 12791 var i; 12792 12793 for (i = 0; i < rawValuesLength; i++) { 12794 accumulator.push('_' + prefix + '_' + i); 12795 } 12796 12797 return accumulator; 12798 } 12799 12800 /** 12801 * @param {string} formattedString 12802 * 12803 * @return {string} 12804 * @private 12805 */ 12806 function getFormatStringFrom (formattedString) { 12807 var chunks = formattedString.match(R_FORMAT_CHUNKS); 12808 12809 if (!chunks) { 12810 // chunks will be null if there were no tokens to parse in 12811 // formattedString (for example, if formattedString is '2'). Coerce 12812 // chunks to be useful here. 12813 chunks = ['', '']; 12814 12815 // If there is only one chunk, assume that the string is a number 12816 // followed by a token... 12817 // NOTE: This may be an unwise assumption. 12818 } else if (chunks.length === 1 || 12819 // ...or if the string starts with a number component (".", "-", or a 12820 // digit)... 12821 formattedString.charAt(0).match(R_NUMBER_COMPONENT)) { 12822 // ...prepend an empty string here to make sure that the formatted number 12823 // is properly replaced by VALUE_PLACEHOLDER 12824 chunks.unshift(''); 12825 } 12826 12827 return chunks.join(VALUE_PLACEHOLDER); 12828 } 12829 12830 /** 12831 * Convert all hex color values within a string to an rgb string. 12832 * 12833 * @param {Object} stateObject 12834 * 12835 * @return {Object} The modified obj 12836 * @private 12837 */ 12838 function sanitizeObjectForHexProps (stateObject) { 12839 NGTweenable.each(stateObject, function (prop) { 12840 var currentProp = stateObject[prop]; 12841 12842 if (typeof currentProp === 'string' && currentProp.match(R_HEX)) { 12843 stateObject[prop] = sanitizeHexChunksToRGB(currentProp); 12844 } 12845 }); 12846 } 12847 12848 /** 12849 * @param {string} str 12850 * 12851 * @return {string} 12852 * @private 12853 */ 12854 function sanitizeHexChunksToRGB (str) { 12855 return filterStringChunks(R_HEX, str, convertHexToRGB); 12856 } 12857 12858 /** 12859 * @param {string} hexString 12860 * 12861 * @return {string} 12862 * @private 12863 */ 12864 function convertHexToRGB (hexString) { 12865 var rgbArr = hexToRGBArray(hexString); 12866 return 'rgb(' + rgbArr[0] + ',' + rgbArr[1] + ',' + rgbArr[2] + ')'; 12867 } 12868 12869 var hexToRGBArray_returnArray = []; 12870 /** 12871 * Convert a hexadecimal string to an array with three items, one each for 12872 * the red, blue, and green decimal values. 12873 * 12874 * @param {string} hex A hexadecimal string. 12875 * 12876 * @returns {Array.<number>} The converted Array of RGB values if `hex` is a 12877 * valid string, or an Array of three 0's. 12878 * @private 12879 */ 12880 function hexToRGBArray (hex) { 12881 12882 hex = hex.replace(/#/, ''); 12883 12884 // If the string is a shorthand three digit hex notation, normalize it to 12885 // the standard six digit notation 12886 if (hex.length === 3) { 12887 hex = hex.split(''); 12888 hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; 12889 } 12890 12891 hexToRGBArray_returnArray[0] = hexToDec(hex.substr(0, 2)); 12892 hexToRGBArray_returnArray[1] = hexToDec(hex.substr(2, 2)); 12893 hexToRGBArray_returnArray[2] = hexToDec(hex.substr(4, 2)); 12894 12895 return hexToRGBArray_returnArray; 12896 } 12897 12898 /** 12899 * Convert a base-16 number to base-10. 12900 * 12901 * @param {Number|String} hex The value to convert 12902 * 12903 * @returns {Number} The base-10 equivalent of `hex`. 12904 * @private 12905 */ 12906 function hexToDec (hex) { 12907 return parseInt(hex, 16); 12908 } 12909 12910 /** 12911 * Runs a filter operation on all chunks of a string that match a RegExp 12912 * 12913 * @param {RegExp} pattern 12914 * @param {string} unfilteredString 12915 * @param {function(string)} filter 12916 * 12917 * @return {string} 12918 * @private 12919 */ 12920 function filterStringChunks (pattern, unfilteredString, filter) { 12921 var pattenMatches = unfilteredString.match(pattern); 12922 var filteredString = unfilteredString.replace(pattern, VALUE_PLACEHOLDER); 12923 12924 if (pattenMatches) { 12925 var pattenMatchesLength = pattenMatches.length; 12926 var currentChunk; 12927 12928 for (var i = 0; i < pattenMatchesLength; i++) { 12929 currentChunk = pattenMatches.shift(); 12930 filteredString = filteredString.replace( 12931 VALUE_PLACEHOLDER, filter(currentChunk)); 12932 } 12933 } 12934 12935 return filteredString; 12936 } 12937 12938 /** 12939 * Check for floating point values within rgb strings and rounds them. 12940 * 12941 * @param {string} formattedString 12942 * 12943 * @return {string} 12944 * @private 12945 */ 12946 function sanitizeRGBChunks (formattedString) { 12947 return filterStringChunks(R_RGB, formattedString, sanitizeRGBChunk); 12948 } 12949 12950 /** 12951 * @param {string} rgbChunk 12952 * 12953 * @return {string} 12954 * @private 12955 */ 12956 function sanitizeRGBChunk (rgbChunk) { 12957 var numbers = rgbChunk.match(R_UNFORMATTED_VALUES); 12958 var numbersLength = numbers.length; 12959 var sanitizedString = rgbChunk.match(R_RGB_PREFIX)[0]; 12960 12961 for (var i = 0; i < numbersLength; i++) { 12962 sanitizedString += parseInt(numbers[i], 10) + ','; 12963 } 12964 12965 sanitizedString = sanitizedString.slice(0, -1) + ')'; 12966 12967 return sanitizedString; 12968 } 12969 12970 /** 12971 * @param {Object} stateObject 12972 * 12973 * @return {Object} An Object of formatManifests that correspond to 12974 * the string properties of stateObject 12975 * @private 12976 */ 12977 function getFormatManifests (stateObject) { 12978 var manifestAccumulator = {}; 12979 12980 NGTweenable.each(stateObject, function (prop) { 12981 var currentProp = stateObject[prop]; 12982 12983 if (typeof currentProp === 'string') { 12984 var rawValues = getValuesFrom(currentProp); 12985 12986 manifestAccumulator[prop] = { 12987 'formatString': getFormatStringFrom(currentProp) 12988 ,'chunkNames': getFormatChunksFrom(rawValues, prop) 12989 }; 12990 } 12991 }); 12992 12993 return manifestAccumulator; 12994 } 12995 12996 /** 12997 * @param {Object} stateObject 12998 * @param {Object} formatManifests 12999 * @private 13000 */ 13001 function expandFormattedProperties (stateObject, formatManifests) { 13002 NGTweenable.each(formatManifests, function (prop) { 13003 var currentProp = stateObject[prop]; 13004 var rawValues = getValuesFrom(currentProp); 13005 var rawValuesLength = rawValues.length; 13006 13007 for (var i = 0; i < rawValuesLength; i++) { 13008 stateObject[formatManifests[prop].chunkNames[i]] = +rawValues[i]; 13009 } 13010 13011 delete stateObject[prop]; 13012 }); 13013 } 13014 13015 /** 13016 * @param {Object} stateObject 13017 * @param {Object} formatManifests 13018 * @private 13019 */ 13020 function collapseFormattedProperties (stateObject, formatManifests) { 13021 NGTweenable.each(formatManifests, function (prop) { 13022 var currentProp = stateObject[prop]; 13023 var formatChunks = extractPropertyChunks( 13024 stateObject, formatManifests[prop].chunkNames); 13025 var valuesList = getValuesList( 13026 formatChunks, formatManifests[prop].chunkNames); 13027 currentProp = getFormattedValues( 13028 formatManifests[prop].formatString, valuesList); 13029 stateObject[prop] = sanitizeRGBChunks(currentProp); 13030 }); 13031 } 13032 13033 /** 13034 * @param {Object} stateObject 13035 * @param {Array.<string>} chunkNames 13036 * 13037 * @return {Object} The extracted value chunks. 13038 * @private 13039 */ 13040 function extractPropertyChunks (stateObject, chunkNames) { 13041 var extractedValues = {}; 13042 var currentChunkName, chunkNamesLength = chunkNames.length; 13043 13044 for (var i = 0; i < chunkNamesLength; i++) { 13045 currentChunkName = chunkNames[i]; 13046 extractedValues[currentChunkName] = stateObject[currentChunkName]; 13047 delete stateObject[currentChunkName]; 13048 } 13049 13050 return extractedValues; 13051 } 13052 13053 var getValuesList_accumulator = []; 13054 /** 13055 * @param {Object} stateObject 13056 * @param {Array.<string>} chunkNames 13057 * 13058 * @return {Array.<number>} 13059 * @private 13060 */ 13061 function getValuesList (stateObject, chunkNames) { 13062 getValuesList_accumulator.length = 0; 13063 var chunkNamesLength = chunkNames.length; 13064 13065 for (var i = 0; i < chunkNamesLength; i++) { 13066 getValuesList_accumulator.push(stateObject[chunkNames[i]]); 13067 } 13068 13069 return getValuesList_accumulator; 13070 } 13071 13072 /** 13073 * @param {string} formatString 13074 * @param {Array.<number>} rawValues 13075 * 13076 * @return {string} 13077 * @private 13078 */ 13079 function getFormattedValues (formatString, rawValues) { 13080 var formattedValueString = formatString; 13081 var rawValuesLength = rawValues.length; 13082 13083 for (var i = 0; i < rawValuesLength; i++) { 13084 formattedValueString = formattedValueString.replace( 13085 VALUE_PLACEHOLDER, +rawValues[i].toFixed(4)); 13086 } 13087 13088 return formattedValueString; 13089 } 13090 13091 /** 13092 * Note: It's the duty of the caller to convert the Array elements of the 13093 * return value into numbers. This is a performance optimization. 13094 * 13095 * @param {string} formattedString 13096 * 13097 * @return {Array.<string>|null} 13098 * @private 13099 */ 13100 function getValuesFrom (formattedString) { 13101 return formattedString.match(R_UNFORMATTED_VALUES); 13102 } 13103 13104 /** 13105 * @param {Object} easingObject 13106 * @param {Object} tokenData 13107 * @private 13108 */ 13109 function expandEasingObject (easingObject, tokenData) { 13110 NGTweenable.each(tokenData, function (prop) { 13111 var currentProp = tokenData[prop]; 13112 var chunkNames = currentProp.chunkNames; 13113 var chunkLength = chunkNames.length; 13114 13115 var easing = easingObject[prop]; 13116 var i; 13117 13118 if (typeof easing === 'string') { 13119 var easingChunks = easing.split(' '); 13120 var lastEasingChunk = easingChunks[easingChunks.length - 1]; 13121 13122 for (i = 0; i < chunkLength; i++) { 13123 easingObject[chunkNames[i]] = easingChunks[i] || lastEasingChunk; 13124 } 13125 13126 } else { 13127 for (i = 0; i < chunkLength; i++) { 13128 easingObject[chunkNames[i]] = easing; 13129 } 13130 } 13131 13132 delete easingObject[prop]; 13133 }); 13134 } 13135 13136 /** 13137 * @param {Object} easingObject 13138 * @param {Object} tokenData 13139 * @private 13140 */ 13141 function collapseEasingObject (easingObject, tokenData) { 13142 NGTweenable.each(tokenData, function (prop) { 13143 var currentProp = tokenData[prop]; 13144 var chunkNames = currentProp.chunkNames; 13145 var chunkLength = chunkNames.length; 13146 13147 var firstEasing = easingObject[chunkNames[0]]; 13148 var typeofEasings = typeof firstEasing; 13149 13150 if (typeofEasings === 'string') { 13151 var composedEasingString = ''; 13152 13153 for (var i = 0; i < chunkLength; i++) { 13154 composedEasingString += ' ' + easingObject[chunkNames[i]]; 13155 delete easingObject[chunkNames[i]]; 13156 } 13157 13158 easingObject[prop] = composedEasingString.substr(1); 13159 } else { 13160 easingObject[prop] = firstEasing; 13161 } 13162 }); 13163 } 13164 13165 NGTweenable.prototype.filter.token = { 13166 'tweenCreated': function (currentState, fromState, toState, easingObject) { 13167 sanitizeObjectForHexProps(currentState); 13168 sanitizeObjectForHexProps(fromState); 13169 sanitizeObjectForHexProps(toState); 13170 this._tokenData = getFormatManifests(currentState); 13171 }, 13172 13173 'beforeTween': function (currentState, fromState, toState, easingObject) { 13174 expandEasingObject(easingObject, this._tokenData); 13175 expandFormattedProperties(currentState, this._tokenData); 13176 expandFormattedProperties(fromState, this._tokenData); 13177 expandFormattedProperties(toState, this._tokenData); 13178 }, 13179 13180 'afterTween': function (currentState, fromState, toState, easingObject) { 13181 collapseFormattedProperties(currentState, this._tokenData); 13182 collapseFormattedProperties(fromState, this._tokenData); 13183 collapseFormattedProperties(toState, this._tokenData); 13184 collapseEasingObject(easingObject, this._tokenData); 13185 } 13186 }; 13187 13188} (NGTweenable)); 13189 13190}).call(null); 13191 13192 13193 13194 13195//########################################################################################################################## 13196//## HAMMER.JS ############################################################################################################# 13197//########################################################################################################################## 13198 13199// HAMMER.JS 13200 13201// external module EMBEDED in nanogallery 13202// NGY BUILD: 13203// replace "Hammer" with "NGHammer" (case sensitive) 13204// replace "var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined;" with "var SUPPORT_POINTER_EVENTS = false;" 13205// replace "define.amd" with "define.amdDISABLED" 13206 13207 13208 13209/*! NGHammer.JS - v2.0.7 - 2016-04-22 13210 * http://hammerjs.github.io/ 13211 * 13212 * Copyright (c) 2016 Jorik Tangelder; 13213 * Licensed under the MIT license */ 13214(function(window, document, exportName, undefined) { 13215 'use strict'; 13216 13217var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o']; 13218var TEST_ELEMENT = document.createElement('div'); 13219 13220var TYPE_FUNCTION = 'function'; 13221 13222var round = Math.round; 13223var abs = Math.abs; 13224var now = Date.now; 13225 13226/** 13227 * set a timeout with a given scope 13228 * @param {Function} fn 13229 * @param {Number} timeout 13230 * @param {Object} context 13231 * @returns {number} 13232 */ 13233function setTimeoutContext(fn, timeout, context) { 13234 return setTimeout(bindFn(fn, context), timeout); 13235} 13236 13237/** 13238 * if the argument is an array, we want to execute the fn on each entry 13239 * if it aint an array we don't want to do a thing. 13240 * this is used by all the methods that accept a single and array argument. 13241 * @param {*|Array} arg 13242 * @param {String} fn 13243 * @param {Object} [context] 13244 * @returns {Boolean} 13245 */ 13246function invokeArrayArg(arg, fn, context) { 13247 if (Array.isArray(arg)) { 13248 each(arg, context[fn], context); 13249 return true; 13250 } 13251 return false;
vendor: 5,340 bytes, lines 13252-13460
13252} 13253 13254/** 13255 * walk objects and arrays 13256 * @param {Object} obj 13257 * @param {Function} iterator 13258 * @param {Object} context 13259 */ 13260function each(obj, iterator, context) { 13261 var i; 13262 13263 if (!obj) { 13264 return; 13265 } 13266 13267 if (obj.forEach) { 13268 obj.forEach(iterator, context); 13269 } else if (obj.length !== undefined) { 13270 i = 0; 13271 while (i < obj.length) { 13272 iterator.call(context, obj[i], i, obj); 13273 i++; 13274 } 13275 } else { 13276 for (i in obj) { 13277 obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj); 13278 } 13279 } 13280} 13281 13282/** 13283 * wrap a method with a deprecation warning and stack trace 13284 * @param {Function} method 13285 * @param {String} name 13286 * @param {String} message 13287 * @returns {Function} A new function wrapping the supplied method. 13288 */ 13289function deprecate(method, name, message) { 13290 var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\n' + message + ' AT \n'; 13291 return function() { 13292 var e = new Error('get-stack-trace'); 13293 var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '') 13294 .replace(/^\s+at\s+/gm, '') 13295 .replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace'; 13296 13297 var log = window.console && (window.console.warn || window.console.log); 13298 if (log) { 13299 log.call(window.console, deprecationMessage, stack); 13300 } 13301 return method.apply(this, arguments); 13302 }; 13303} 13304 13305/** 13306 * extend object. 13307 * means that properties in dest will be overwritten by the ones in src. 13308 * @param {Object} target 13309 * @param {...Object} objects_to_assign 13310 * @returns {Object} target 13311 */ 13312var assign; 13313if (typeof Object.assign !== 'function') { 13314 assign = function assign(target) { 13315 if (target === undefined || target === null) { 13316 throw new TypeError('Cannot convert undefined or null to object'); 13317 } 13318 13319 var output = Object(target); 13320 for (var index = 1; index < arguments.length; index++) { 13321 var source = arguments[index]; 13322 if (source !== undefined && source !== null) { 13323 for (var nextKey in source) { 13324 if (source.hasOwnProperty(nextKey)) { 13325 output[nextKey] = source[nextKey]; 13326 } 13327 } 13328 } 13329 } 13330 return output; 13331 }; 13332} else { 13333 assign = Object.assign; 13334} 13335 13336/** 13337 * extend object. 13338 * means that properties in dest will be overwritten by the ones in src. 13339 * @param {Object} dest 13340 * @param {Object} src 13341 * @param {Boolean} [merge=false] 13342 * @returns {Object} dest 13343 */ 13344var extend = deprecate(function extend(dest, src, merge) { 13345 var keys = Object.keys(src); 13346 var i = 0; 13347 while (i < keys.length) { 13348 if (!merge || (merge && dest[keys[i]] === undefined)) { 13349 dest[keys[i]] = src[keys[i]]; 13350 } 13351 i++; 13352 } 13353 return dest; 13354}, 'extend', 'Use `assign`.'); 13355 13356/** 13357 * merge the values from src in the dest. 13358 * means that properties that exist in dest will not be overwritten by src 13359 * @param {Object} dest 13360 * @param {Object} src 13361 * @returns {Object} dest 13362 */ 13363var merge = deprecate(function merge(dest, src) { 13364 return extend(dest, src, true); 13365}, 'merge', 'Use `assign`.'); 13366 13367/** 13368 * simple class inheritance 13369 * @param {Function} child 13370 * @param {Function} base 13371 * @param {Object} [properties] 13372 */ 13373function inherit(child, base, properties) { 13374 var baseP = base.prototype, 13375 childP; 13376 13377 childP = child.prototype = Object.create(baseP); 13378 childP.constructor = child; 13379 childP._super = baseP; 13380 13381 if (properties) { 13382 assign(childP, properties); 13383 } 13384} 13385 13386/** 13387 * simple function bind 13388 * @param {Function} fn 13389 * @param {Object} context 13390 * @returns {Function} 13391 */ 13392function bindFn(fn, context) { 13393 return function boundFn() { 13394 return fn.apply(context, arguments); 13395 }; 13396} 13397 13398/** 13399 * let a boolean value also be a function that must return a boolean 13400 * this first item in args will be used as the context 13401 * @param {Boolean|Function} val 13402 * @param {Array} [args] 13403 * @returns {Boolean} 13404 */ 13405function boolOrFn(val, args) { 13406 if (typeof val == TYPE_FUNCTION) { 13407 return val.apply(args ? args[0] || undefined : undefined, args); 13408 } 13409 return val; 13410} 13411 13412/** 13413 * use the val2 when val1 is undefined 13414 * @param {*} val1 13415 * @param {*} val2 13416 * @returns {*} 13417 */ 13418function ifUndefined(val1, val2) { 13419 return (val1 === undefined) ? val2 : val1; 13420} 13421 13422/** 13423 * addEventListener with multiple events at once 13424 * @param {EventTarget} target 13425 * @param {String} types 13426 * @param {Function} handler 13427 */ 13428function addEventListeners(target, types, handler) { 13429 each(splitStr(types), function(type) { 13430 target.addEventListener(type, handler, false); 13431 }); 13432} 13433 13434/** 13435 * removeEventListener with multiple events at once 13436 * @param {EventTarget} target 13437 * @param {String} types 13438 * @param {Function} handler 13439 */ 13440function removeEventListeners(target, types, handler) { 13441 each(splitStr(types), function(type) { 13442 target.removeEventListener(type, handler, false); 13443 }); 13444} 13445 13446/** 13447 * find if a node is in the given parent 13448 * @method hasParent 13449 * @param {HTMLElement} node 13450 * @param {HTMLElement} parent 13451 * @return {Boolean} found 13452 */ 13453function hasParent(node, parent) { 13454 while (node) { 13455 if (node == parent) { 13456 return true; 13457 } 13458 node = node.parentNode; 13459 } 13460 return false;
vendor: 5,301 bytes, lines 13461-13676
13461} 13462 13463/** 13464 * small indexOf wrapper 13465 * @param {String} str 13466 * @param {String} find 13467 * @returns {Boolean} found 13468 */ 13469function inStr(str, find) { 13470 return str.indexOf(find) > -1; 13471} 13472 13473/** 13474 * split string on whitespace 13475 * @param {String} str 13476 * @returns {Array} words 13477 */ 13478function splitStr(str) { 13479 return str.trim().split(/\s+/g); 13480} 13481 13482/** 13483 * find if a array contains the object using indexOf or a simple polyFill 13484 * @param {Array} src 13485 * @param {String} find 13486 * @param {String} [findByKey] 13487 * @return {Boolean|Number} false when not found, or the index 13488 */ 13489function inArray(src, find, findByKey) { 13490 if (src.indexOf && !findByKey) { 13491 return src.indexOf(find); 13492 } else { 13493 var i = 0; 13494 while (i < src.length) { 13495 if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) { 13496 return i; 13497 } 13498 i++; 13499 } 13500 return -1; 13501 } 13502} 13503 13504/** 13505 * convert array-like objects to real arrays 13506 * @param {Object} obj 13507 * @returns {Array} 13508 */ 13509function toArray(obj) { 13510 return Array.prototype.slice.call(obj, 0); 13511} 13512 13513/** 13514 * unique array with objects based on a key (like 'id') or just by the array's value 13515 * @param {Array} src [{id:1},{id:2},{id:1}] 13516 * @param {String} [key] 13517 * @param {Boolean} [sort=False] 13518 * @returns {Array} [{id:1},{id:2}] 13519 */ 13520function uniqueArray(src, key, sort) { 13521 var results = []; 13522 var values = []; 13523 var i = 0; 13524 13525 while (i < src.length) { 13526 var val = key ? src[i][key] : src[i]; 13527 if (inArray(values, val) < 0) { 13528 results.push(src[i]); 13529 } 13530 values[i] = val; 13531 i++; 13532 } 13533 13534 if (sort) { 13535 if (!key) { 13536 results = results.sort(); 13537 } else { 13538 results = results.sort(function sortUniqueArray(a, b) { 13539 return a[key] > b[key]; 13540 }); 13541 } 13542 } 13543 13544 return results; 13545} 13546 13547/** 13548 * get the prefixed property 13549 * @param {Object} obj 13550 * @param {String} property 13551 * @returns {String|Undefined} prefixed 13552 */ 13553function prefixed(obj, property) { 13554 var prefix, prop; 13555 var camelProp = property[0].toUpperCase() + property.slice(1); 13556 13557 var i = 0; 13558 while (i < VENDOR_PREFIXES.length) { 13559 prefix = VENDOR_PREFIXES[i]; 13560 prop = (prefix) ? prefix + camelProp : property; 13561 13562 if (prop in obj) { 13563 return prop; 13564 } 13565 i++; 13566 } 13567 return undefined; 13568} 13569 13570/** 13571 * get a unique id 13572 * @returns {number} uniqueId 13573 */ 13574var _uniqueId = 1; 13575function uniqueId() { 13576 return _uniqueId++; 13577} 13578 13579/** 13580 * get the window object of an element 13581 * @param {HTMLElement} element 13582 * @returns {DocumentView|Window} 13583 */ 13584function getWindowForElement(element) { 13585 var doc = element.ownerDocument || element; 13586 return (doc.defaultView || doc.parentWindow || window); 13587} 13588 13589var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i; 13590 13591var SUPPORT_TOUCH = ('ontouchstart' in window); 13592// var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined; 13593var SUPPORT_POINTER_EVENTS = false; 13594var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent); 13595 13596var INPUT_TYPE_TOUCH = 'touch'; 13597var INPUT_TYPE_PEN = 'pen'; 13598var INPUT_TYPE_MOUSE = 'mouse'; 13599var INPUT_TYPE_KINECT = 'kinect'; 13600 13601var COMPUTE_INTERVAL = 25; 13602 13603var INPUT_START = 1; 13604var INPUT_MOVE = 2; 13605var INPUT_END = 4; 13606var INPUT_CANCEL = 8; 13607 13608var DIRECTION_NONE = 1; 13609var DIRECTION_LEFT = 2; 13610var DIRECTION_RIGHT = 4; 13611var DIRECTION_UP = 8; 13612var DIRECTION_DOWN = 16; 13613 13614var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT; 13615var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN; 13616var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL; 13617 13618var PROPS_XY = ['x', 'y']; 13619var PROPS_CLIENT_XY = ['clientX', 'clientY']; 13620 13621/** 13622 * create new input type manager 13623 * @param {Manager} manager 13624 * @param {Function} callback 13625 * @returns {Input} 13626 * @constructor 13627 */ 13628function Input(manager, callback) { 13629 var self = this; 13630 this.manager = manager; 13631 this.callback = callback; 13632 this.element = manager.element; 13633 this.target = manager.options.inputTarget; 13634 13635 // smaller wrapper around the handler, for the scope and the enabled state of the manager, 13636 // so when disabled the input events are completely bypassed. 13637 this.domHandler = function(ev) { 13638 if (boolOrFn(manager.options.enable, [manager])) { 13639 self.handler(ev); 13640 } 13641 }; 13642 13643 this.init(); 13644 13645} 13646 13647Input.prototype = { 13648 /** 13649 * should handle the inputEvent data and trigger the callback 13650 * @virtual 13651 */ 13652 handler: function() { }, 13653 13654 /** 13655 * bind the events 13656 */ 13657 init: function() { 13658 this.evEl && addEventListeners(this.element, this.evEl, this.domHandler); 13659 this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler); 13660 this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); 13661 }, 13662 13663 /** 13664 * unbind the events 13665 */ 13666 destroy: function() { 13667 this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler); 13668 this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler); 13669 this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); 13670 } 13671}; 13672 13673/** 13674 * create new input type manager 13675 * called by the Manager constructor 13676 * @param {NG
vendor: 17,717 bytes, lines 13676-14327
13676Hammer} manager 13677 * @returns {Input} 13678 */ 13679function createInputInstance(manager) { 13680 var Type; 13681 var inputClass = manager.options.inputClass; 13682 13683 if (inputClass) { 13684 Type = inputClass; 13685 } else if (SUPPORT_POINTER_EVENTS) { 13686 Type = PointerEventInput; 13687 } else if (SUPPORT_ONLY_TOUCH) { 13688 Type = TouchInput; 13689 } else if (!SUPPORT_TOUCH) { 13690 Type = MouseInput; 13691 } else { 13692 Type = TouchMouseInput; 13693 } 13694 return new (Type)(manager, inputHandler); 13695} 13696 13697/** 13698 * handle input events 13699 * @param {Manager} manager 13700 * @param {String} eventType 13701 * @param {Object} input 13702 */ 13703function inputHandler(manager, eventType, input) { 13704 var pointersLen = input.pointers.length; 13705 var changedPointersLen = input.changedPointers.length; 13706 var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0)); 13707 var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0)); 13708 13709 input.isFirst = !!isFirst; 13710 input.isFinal = !!isFinal; 13711 13712 if (isFirst) { 13713 manager.session = {}; 13714 } 13715 13716 // source event is the normalized value of the domEvents 13717 // like 'touchstart, mouseup, pointerdown' 13718 input.eventType = eventType; 13719 13720 // compute scale, rotation etc 13721 computeInputData(manager, input); 13722 13723 // emit secret event 13724 manager.emit('hammer.input', input); 13725 13726 manager.recognize(input); 13727 manager.session.prevInput = input; 13728} 13729 13730/** 13731 * extend the data with some usable properties like scale, rotate, velocity etc 13732 * @param {Object} manager 13733 * @param {Object} input 13734 */ 13735function computeInputData(manager, input) { 13736 var session = manager.session; 13737 var pointers = input.pointers; 13738 var pointersLength = pointers.length; 13739 13740 // store the first input to calculate the distance and direction 13741 if (!session.firstInput) { 13742 session.firstInput = simpleCloneInputData(input); 13743 } 13744 13745 // to compute scale and rotation we need to store the multiple touches 13746 if (pointersLength > 1 && !session.firstMultiple) { 13747 session.firstMultiple = simpleCloneInputData(input); 13748 } else if (pointersLength === 1) { 13749 session.firstMultiple = false; 13750 } 13751 13752 var firstInput = session.firstInput; 13753 var firstMultiple = session.firstMultiple; 13754 var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center; 13755 13756 var center = input.center = getCenter(pointers); 13757 input.timeStamp = now(); 13758 input.deltaTime = input.timeStamp - firstInput.timeStamp; 13759 13760 input.angle = getAngle(offsetCenter, center); 13761 input.distance = getDistance(offsetCenter, center); 13762 13763 computeDeltaXY(session, input); 13764 input.offsetDirection = getDirection(input.deltaX, input.deltaY); 13765 13766 var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY); 13767 input.overallVelocityX = overallVelocity.x; 13768 input.overallVelocityY = overallVelocity.y; 13769 input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y; 13770 13771 input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1; 13772 input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0; 13773 13774 input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length > 13775 session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers); 13776 13777 computeIntervalInputData(session, input); 13778 13779 // find the correct target 13780 var target = manager.element; 13781 if (hasParent(input.srcEvent.target, target)) { 13782 target = input.srcEvent.target; 13783 } 13784 input.target = target; 13785} 13786 13787function computeDeltaXY(session, input) { 13788 var center = input.center; 13789 var offset = session.offsetDelta || {}; 13790 var prevDelta = session.prevDelta || {}; 13791 var prevInput = session.prevInput || {}; 13792 13793 if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) { 13794 prevDelta = session.prevDelta = { 13795 x: prevInput.deltaX || 0, 13796 y: prevInput.deltaY || 0 13797 }; 13798 13799 offset = session.offsetDelta = { 13800 x: center.x, 13801 y: center.y 13802 }; 13803 } 13804 13805 input.deltaX = prevDelta.x + (center.x - offset.x); 13806 input.deltaY = prevDelta.y + (center.y - offset.y); 13807} 13808 13809/** 13810 * velocity is calculated every x ms 13811 * @param {Object} session 13812 * @param {Object} input 13813 */ 13814function computeIntervalInputData(session, input) { 13815 var last = session.lastInterval || input, 13816 deltaTime = input.timeStamp - last.timeStamp, 13817 velocity, velocityX, velocityY, direction; 13818 13819 if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) { 13820 var deltaX = input.deltaX - last.deltaX; 13821 var deltaY = input.deltaY - last.deltaY; 13822 13823 var v = getVelocity(deltaTime, deltaX, deltaY); 13824 velocityX = v.x; 13825 velocityY = v.y; 13826 velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y; 13827 direction = getDirection(deltaX, deltaY); 13828 13829 session.lastInterval = input; 13830 } else { 13831 // use latest velocity info if it doesn't overtake a minimum period 13832 velocity = last.velocity; 13833 velocityX = last.velocityX; 13834 velocityY = last.velocityY; 13835 direction = last.direction; 13836 } 13837 13838 input.velocity = velocity; 13839 input.velocityX = velocityX; 13840 input.velocityY = velocityY; 13841 input.direction = direction; 13842} 13843 13844/** 13845 * create a simple clone from the input used for storage of firstInput and firstMultiple 13846 * @param {Object} input 13847 * @returns {Object} clonedInputData 13848 */ 13849function simpleCloneInputData(input) { 13850 // make a simple copy of the pointers because we will get a reference if we don't 13851 // we only need clientXY for the calculations 13852 var pointers = []; 13853 var i = 0; 13854 while (i < input.pointers.length) { 13855 pointers[i] = { 13856 clientX: round(input.pointers[i].clientX), 13857 clientY: round(input.pointers[i].clientY) 13858 }; 13859 i++; 13860 } 13861 13862 return { 13863 timeStamp: now(), 13864 pointers: pointers, 13865 center: getCenter(pointers), 13866 deltaX: input.deltaX, 13867 deltaY: input.deltaY 13868 }; 13869} 13870 13871/** 13872 * get the center of all the pointers 13873 * @param {Array} pointers 13874 * @return {Object} center contains `x` and `y` properties 13875 */ 13876function getCenter(pointers) { 13877 var pointersLength = pointers.length; 13878 13879 // no need to loop when only one touch 13880 if (pointersLength === 1) { 13881 return { 13882 x: round(pointers[0].clientX), 13883 y: round(pointers[0].clientY) 13884 }; 13885 } 13886 13887 var x = 0, y = 0, i = 0; 13888 while (i < pointersLength) { 13889 x += pointers[i].clientX; 13890 y += pointers[i].clientY; 13891 i++; 13892 } 13893 13894 return { 13895 x: round(x / pointersLength), 13896 y: round(y / pointersLength) 13897 }; 13898} 13899 13900/** 13901 * calculate the velocity between two points. unit is in px per ms. 13902 * @param {Number} deltaTime 13903 * @param {Number} x 13904 * @param {Number} y 13905 * @return {Object} velocity `x` and `y` 13906 */ 13907function getVelocity(deltaTime, x, y) { 13908 return { 13909 x: x / deltaTime || 0, 13910 y: y / deltaTime || 0 13911 }; 13912} 13913 13914/** 13915 * get the direction between two points 13916 * @param {Number} x 13917 * @param {Number} y 13918 * @return {Number} direction 13919 */ 13920function getDirection(x, y) { 13921 if (x === y) { 13922 return DIRECTION_NONE; 13923 } 13924 13925 if (abs(x) >= abs(y)) { 13926 return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; 13927 } 13928 return y < 0 ? DIRECTION_UP : DIRECTION_DOWN; 13929} 13930 13931/** 13932 * calculate the absolute distance between two points 13933 * @param {Object} p1 {x, y} 13934 * @param {Object} p2 {x, y} 13935 * @param {Array} [props] containing x and y keys 13936 * @return {Number} distance 13937 */ 13938function getDistance(p1, p2, props) { 13939 if (!props) { 13940 props = PROPS_XY; 13941 } 13942 var x = p2[props[0]] - p1[props[0]], 13943 y = p2[props[1]] - p1[props[1]]; 13944 13945 return Math.sqrt((x * x) + (y * y)); 13946} 13947 13948/** 13949 * calculate the angle between two coordinates 13950 * @param {Object} p1 13951 * @param {Object} p2 13952 * @param {Array} [props] containing x and y keys 13953 * @return {Number} angle 13954 */ 13955function getAngle(p1, p2, props) { 13956 if (!props) { 13957 props = PROPS_XY; 13958 } 13959 var x = p2[props[0]] - p1[props[0]], 13960 y = p2[props[1]] - p1[props[1]]; 13961 return Math.atan2(y, x) * 180 / Math.PI; 13962} 13963 13964/** 13965 * calculate the rotation degrees between two pointersets 13966 * @param {Array} start array of pointers 13967 * @param {Array} end array of pointers 13968 * @return {Number} rotation 13969 */ 13970function getRotation(start, end) { 13971 return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY); 13972} 13973 13974/** 13975 * calculate the scale factor between two pointersets 13976 * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out 13977 * @param {Array} start array of pointers 13978 * @param {Array} end array of pointers 13979 * @return {Number} scale 13980 */ 13981function getScale(start, end) { 13982 return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY); 13983} 13984 13985var MOUSE_INPUT_MAP = { 13986 mousedown: INPUT_START, 13987 mousemove: INPUT_MOVE, 13988 mouseup: INPUT_END 13989}; 13990 13991var MOUSE_ELEMENT_EVENTS = 'mousedown'; 13992var MOUSE_WINDOW_EVENTS = 'mousemove mouseup'; 13993 13994/** 13995 * Mouse events input 13996 * @constructor 13997 * @extends Input 13998 */ 13999function MouseInput() { 14000 this.evEl = MOUSE_ELEMENT_EVENTS; 14001 this.evWin = MOUSE_WINDOW_EVENTS; 14002 14003 this.pressed = false; // mousedown state 14004 14005 Input.apply(this, arguments); 14006} 14007 14008inherit(MouseInput, Input, { 14009 /** 14010 * handle mouse events 14011 * @param {Object} ev 14012 */ 14013 handler: function MEhandler(ev) { 14014 var eventType = MOUSE_INPUT_MAP[ev.type]; 14015 14016 // on start we want to have the left mouse button down 14017 if (eventType & INPUT_START && ev.button === 0) { 14018 this.pressed = true; 14019 } 14020 14021 if (eventType & INPUT_MOVE && ev.which !== 1) { 14022 eventType = INPUT_END; 14023 } 14024 14025 // mouse must be down 14026 if (!this.pressed) { 14027 return; 14028 } 14029 14030 if (eventType & INPUT_END) { 14031 this.pressed = false; 14032 } 14033 14034 this.callback(this.manager, eventType, { 14035 pointers: [ev], 14036 changedPointers: [ev], 14037 pointerType: INPUT_TYPE_MOUSE, 14038 srcEvent: ev 14039 }); 14040 } 14041}); 14042 14043var POINTER_INPUT_MAP = { 14044 pointerdown: INPUT_START, 14045 pointermove: INPUT_MOVE, 14046 pointerup: INPUT_END, 14047 pointercancel: INPUT_CANCEL, 14048 pointerout: INPUT_CANCEL 14049}; 14050 14051// in IE10 the pointer types is defined as an enum 14052var IE10_POINTER_TYPE_ENUM = { 14053 2: INPUT_TYPE_TOUCH, 14054 3: INPUT_TYPE_PEN, 14055 4: INPUT_TYPE_MOUSE, 14056 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816 14057}; 14058 14059var POINTER_ELEMENT_EVENTS = 'pointerdown'; 14060var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel'; 14061 14062// IE10 has prefixed support, and case-sensitive 14063if (window.MSPointerEvent && !window.PointerEvent) { 14064 POINTER_ELEMENT_EVENTS = 'MSPointerDown'; 14065 POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel'; 14066} 14067 14068/** 14069 * Pointer events input 14070 * @constructor 14071 * @extends Input 14072 */ 14073function PointerEventInput() { 14074 this.evEl = POINTER_ELEMENT_EVENTS; 14075 this.evWin = POINTER_WINDOW_EVENTS; 14076 14077 Input.apply(this, arguments); 14078 14079 this.store = (this.manager.session.pointerEvents = []); 14080} 14081 14082inherit(PointerEventInput, Input, { 14083 /** 14084 * handle mouse events 14085 * @param {Object} ev 14086 */ 14087 handler: function PEhandler(ev) { 14088 var store = this.store; 14089 var removePointer = false; 14090 14091 var eventTypeNormalized = ev.type.toLowerCase().replace('ms', ''); 14092 var eventType = POINTER_INPUT_MAP[eventTypeNormalized]; 14093 var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType; 14094 14095 var isTouch = (pointerType == INPUT_TYPE_TOUCH); 14096 14097 // get index of the event in the store 14098 var storeIndex = inArray(store, ev.pointerId, 'pointerId'); 14099 14100 // start and mouse must be down 14101 if (eventType & INPUT_START && (ev.button === 0 || isTouch)) { 14102 if (storeIndex < 0) { 14103 store.push(ev); 14104 storeIndex = store.length - 1; 14105 } 14106 } else if (eventType & (INPUT_END | INPUT_CANCEL)) { 14107 removePointer = true; 14108 } 14109 14110 // it not found, so the pointer hasn't been down (so it's probably a hover) 14111 if (storeIndex < 0) { 14112 return; 14113 } 14114 14115 // update the event in the store 14116 store[storeIndex] = ev; 14117 14118 this.callback(this.manager, eventType, { 14119 pointers: store, 14120 changedPointers: [ev], 14121 pointerType: pointerType, 14122 srcEvent: ev 14123 }); 14124 14125 if (removePointer) { 14126 // remove from the store 14127 store.splice(storeIndex, 1); 14128 } 14129 } 14130}); 14131 14132var SINGLE_TOUCH_INPUT_MAP = { 14133 touchstart: INPUT_START, 14134 touchmove: INPUT_MOVE, 14135 touchend: INPUT_END, 14136 touchcancel: INPUT_CANCEL 14137}; 14138 14139var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart'; 14140var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel'; 14141 14142/** 14143 * Touch events input 14144 * @constructor 14145 * @extends Input 14146 */ 14147function SingleTouchInput() { 14148 this.evTarget = SINGLE_TOUCH_TARGET_EVENTS; 14149 this.evWin = SINGLE_TOUCH_WINDOW_EVENTS; 14150 this.started = false; 14151 14152 Input.apply(this, arguments); 14153} 14154 14155inherit(SingleTouchInput, Input, { 14156 handler: function TEhandler(ev) { 14157 var type = SINGLE_TOUCH_INPUT_MAP[ev.type]; 14158 14159 // should we handle the touch events? 14160 if (type === INPUT_START) { 14161 this.started = true; 14162 } 14163 14164 if (!this.started) { 14165 return; 14166 } 14167 14168 var touches = normalizeSingleTouches.call(this, ev, type); 14169 14170 // when done, reset the started state 14171 if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) { 14172 this.started = false; 14173 } 14174 14175 this.callback(this.manager, type, { 14176 pointers: touches[0], 14177 changedPointers: touches[1], 14178 pointerType: INPUT_TYPE_TOUCH, 14179 srcEvent: ev 14180 }); 14181 } 14182}); 14183 14184/** 14185 * @this {TouchInput} 14186 * @param {Object} ev 14187 * @param {Number} type flag 14188 * @returns {undefined|Array} [all, changed] 14189 */ 14190function normalizeSingleTouches(ev, type) { 14191 var all = toArray(ev.touches); 14192 var changed = toArray(ev.changedTouches); 14193 14194 if (type & (INPUT_END | INPUT_CANCEL)) { 14195 all = uniqueArray(all.concat(changed), 'identifier', true); 14196 } 14197 14198 return [all, changed]; 14199} 14200 14201var TOUCH_INPUT_MAP = { 14202 touchstart: INPUT_START, 14203 touchmove: INPUT_MOVE, 14204 touchend: INPUT_END, 14205 touchcancel: INPUT_CANCEL 14206}; 14207 14208var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel'; 14209 14210/** 14211 * Multi-user touch events input 14212 * @constructor 14213 * @extends Input 14214 */ 14215function TouchInput() { 14216 this.evTarget = TOUCH_TARGET_EVENTS; 14217 this.targetIds = {}; 14218 14219 Input.apply(this, arguments); 14220} 14221 14222inherit(TouchInput, Input, { 14223 handler: function MTEhandler(ev) { 14224 var type = TOUCH_INPUT_MAP[ev.type]; 14225 var touches = getTouches.call(this, ev, type); 14226 if (!touches) { 14227 return; 14228 } 14229 14230 this.callback(this.manager, type, { 14231 pointers: touches[0], 14232 changedPointers: touches[1], 14233 pointerType: INPUT_TYPE_TOUCH, 14234 srcEvent: ev 14235 }); 14236 } 14237}); 14238 14239/** 14240 * @this {TouchInput} 14241 * @param {Object} ev 14242 * @param {Number} type flag 14243 * @returns {undefined|Array} [all, changed] 14244 */ 14245function getTouches(ev, type) { 14246 var allTouches = toArray(ev.touches); 14247 var targetIds = this.targetIds; 14248 14249 // when there is only one touch, the process can be simplified 14250 if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) { 14251 targetIds[allTouches[0].identifier] = true; 14252 return [allTouches, allTouches]; 14253 } 14254 14255 var i, 14256 targetTouches, 14257 changedTouches = toArray(ev.changedTouches), 14258 changedTargetTouches = [], 14259 target = this.target; 14260 14261 // get target touches from touches 14262 targetTouches = allTouches.filter(function(touch) { 14263 return hasParent(touch.target, target); 14264 }); 14265 14266 // collect touches 14267 if (type === INPUT_START) { 14268 i = 0; 14269 while (i < targetTouches.length) { 14270 targetIds[targetTouches[i].identifier] = true; 14271 i++; 14272 } 14273 } 14274 14275 // filter changed touches to only contain touches that exist in the collected target ids 14276 i = 0; 14277 while (i < changedTouches.length) { 14278 if (targetIds[changedTouches[i].identifier]) { 14279 changedTargetTouches.push(changedTouches[i]); 14280 } 14281 14282 // cleanup removed touches 14283 if (type & (INPUT_END | INPUT_CANCEL)) { 14284 delete targetIds[changedTouches[i].identifier]; 14285 } 14286 i++; 14287 } 14288 14289 if (!changedTargetTouches.length) { 14290 return; 14291 } 14292 14293 return [ 14294 // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel' 14295 uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true), 14296 changedTargetTouches 14297 ]; 14298} 14299 14300/** 14301 * Combined touch and mouse input 14302 * 14303 * Touch has a higher priority then mouse, and while touching no mouse events are allowed. 14304 * This because touch devices also emit mouse events while doing a touch. 14305 * 14306 * @constructor 14307 * @extends Input 14308 */ 14309 14310var DEDUP_TIMEOUT = 2500; 14311var DEDUP_DISTANCE = 25; 14312 14313function TouchMouseInput() { 14314 Input.apply(this, arguments); 14315 14316 var handler = bindFn(this.handler, this); 14317 this.touch = new TouchInput(this.manager, handler); 14318 this.mouse = new MouseInput(this.manager, handler); 14319 14320 this.primaryTouch = null; 14321 this.lastTouches = []; 14322} 14323 14324inherit(TouchMouseInput, Input, { 14325 /** 14326 * handle mouse and touch events 14327 * @param {
14327NG
vendor: 29,735 bytes, lines 14327-15348
14327Hammer} manager 14328 * @param {String} inputEvent 14329 * @param {Object} inputData 14330 */ 14331 handler: function TMEhandler(manager, inputEvent, inputData) { 14332 var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH), 14333 isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE); 14334 14335 if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) { 14336 return; 14337 } 14338 14339 // when we're in a touch event, record touches to de-dupe synthetic mouse event 14340 if (isTouch) { 14341 recordTouches.call(this, inputEvent, inputData); 14342 } else if (isMouse && isSyntheticEvent.call(this, inputData)) { 14343 return; 14344 } 14345 14346 this.callback(manager, inputEvent, inputData); 14347 }, 14348 14349 /** 14350 * remove the event listeners 14351 */ 14352 destroy: function destroy() { 14353 this.touch.destroy(); 14354 this.mouse.destroy(); 14355 } 14356}); 14357 14358function recordTouches(eventType, eventData) { 14359 if (eventType & INPUT_START) { 14360 this.primaryTouch = eventData.changedPointers[0].identifier; 14361 setLastTouch.call(this, eventData); 14362 } else if (eventType & (INPUT_END | INPUT_CANCEL)) { 14363 setLastTouch.call(this, eventData); 14364 } 14365} 14366 14367function setLastTouch(eventData) { 14368 var touch = eventData.changedPointers[0]; 14369 14370 if (touch.identifier === this.primaryTouch) { 14371 var lastTouch = {x: touch.clientX, y: touch.clientY}; 14372 this.lastTouches.push(lastTouch); 14373 var lts = this.lastTouches; 14374 var removeLastTouch = function() { 14375 var i = lts.indexOf(lastTouch); 14376 if (i > -1) { 14377 lts.splice(i, 1); 14378 } 14379 }; 14380 setTimeout(removeLastTouch, DEDUP_TIMEOUT); 14381 } 14382} 14383 14384function isSyntheticEvent(eventData) { 14385 var x = eventData.srcEvent.clientX, y = eventData.srcEvent.clientY; 14386 for (var i = 0; i < this.lastTouches.length; i++) { 14387 var t = this.lastTouches[i]; 14388 var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y); 14389 if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) { 14390 return true; 14391 } 14392 } 14393 return false; 14394} 14395 14396var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction'); 14397var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined; 14398 14399// magical touchAction value 14400var TOUCH_ACTION_COMPUTE = 'compute'; 14401var TOUCH_ACTION_AUTO = 'auto'; 14402var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented 14403var TOUCH_ACTION_NONE = 'none'; 14404var TOUCH_ACTION_PAN_X = 'pan-x'; 14405var TOUCH_ACTION_PAN_Y = 'pan-y'; 14406var TOUCH_ACTION_MAP = getTouchActionProps(); 14407 14408/** 14409 * Touch Action 14410 * sets the touchAction property or uses the js alternative 14411 * @param {Manager} manager 14412 * @param {String} value 14413 * @constructor 14414 */ 14415function TouchAction(manager, value) { 14416 this.manager = manager; 14417 this.set(value); 14418} 14419 14420TouchAction.prototype = { 14421 /** 14422 * set the touchAction value on the element or enable the polyfill 14423 * @param {String} value 14424 */ 14425 set: function(value) { 14426 // find out the touch-action by the event handlers 14427 if (value == TOUCH_ACTION_COMPUTE) { 14428 value = this.compute(); 14429 } 14430 14431 if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) { 14432 this.manager.element.style[PREFIXED_TOUCH_ACTION] = value; 14433 } 14434 this.actions = value.toLowerCase().trim(); 14435 }, 14436 14437 /** 14438 * just re-set the touchAction value 14439 */ 14440 update: function() { 14441 this.set(this.manager.options.touchAction); 14442 }, 14443 14444 /** 14445 * compute the value for the touchAction property based on the recognizer's settings 14446 * @returns {String} value 14447 */ 14448 compute: function() { 14449 var actions = []; 14450 each(this.manager.recognizers, function(recognizer) { 14451 if (boolOrFn(recognizer.options.enable, [recognizer])) { 14452 actions = actions.concat(recognizer.getTouchAction()); 14453 } 14454 }); 14455 return cleanTouchActions(actions.join(' ')); 14456 }, 14457 14458 /** 14459 * this method is called on each input cycle and provides the preventing of the browser behavior 14460 * @param {Object} input 14461 */ 14462 preventDefaults: function(input) { 14463 var srcEvent = input.srcEvent; 14464 var direction = input.offsetDirection; 14465 14466 // if the touch action did prevented once this session 14467 if (this.manager.session.prevented) { 14468 srcEvent.preventDefault(); 14469 return; 14470 } 14471 14472 var actions = this.actions; 14473 var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE]; 14474 var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y]; 14475 var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X]; 14476 14477 if (hasNone) { 14478 //do not prevent defaults if this is a tap gesture 14479 14480 var isTapPointer = input.pointers.length === 1; 14481 var isTapMovement = input.distance < 2; 14482 var isTapTouchTime = input.deltaTime < 250; 14483 14484 if (isTapPointer && isTapMovement && isTapTouchTime) { 14485 return; 14486 } 14487 } 14488 14489 if (hasPanX && hasPanY) { 14490 // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent 14491 return; 14492 } 14493 14494 if (hasNone || 14495 (hasPanY && direction & DIRECTION_HORIZONTAL) || 14496 (hasPanX && direction & DIRECTION_VERTICAL)) { 14497 return this.preventSrc(srcEvent); 14498 } 14499 }, 14500 14501 /** 14502 * call preventDefault to prevent the browser's default behavior (scrolling in most cases) 14503 * @param {Object} srcEvent 14504 */ 14505 preventSrc: function(srcEvent) { 14506 this.manager.session.prevented = true; 14507 srcEvent.preventDefault(); 14508 } 14509}; 14510 14511/** 14512 * when the touchActions are collected they are not a valid value, so we need to clean things up. * 14513 * @param {String} actions 14514 * @returns {*} 14515 */ 14516function cleanTouchActions(actions) { 14517 // none 14518 if (inStr(actions, TOUCH_ACTION_NONE)) { 14519 return TOUCH_ACTION_NONE; 14520 } 14521 14522 var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); 14523 var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); 14524 14525 // if both pan-x and pan-y are set (different recognizers 14526 // for different directions, e.g. horizontal pan but vertical swipe?) 14527 // we need none (as otherwise with pan-x pan-y combined none of these 14528 // recognizers will work, since the browser would handle all panning 14529 if (hasPanX && hasPanY) { 14530 return TOUCH_ACTION_NONE; 14531 } 14532 14533 // pan-x OR pan-y 14534 if (hasPanX || hasPanY) { 14535 return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y; 14536 } 14537 14538 // manipulation 14539 if (inStr(actions, TOUCH_ACTION_MANIPULATION)) { 14540 return TOUCH_ACTION_MANIPULATION; 14541 } 14542 14543 return TOUCH_ACTION_AUTO; 14544} 14545 14546function getTouchActionProps() { 14547 if (!NATIVE_TOUCH_ACTION) { 14548 return false; 14549 } 14550 var touchMap = {}; 14551 var cssSupports = window.CSS && window.CSS.supports; 14552 ['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function(val) { 14553 14554 // If css.supports is not supported but there is native touch-action assume it supports 14555 // all values. This is the case for IE 10 and 11. 14556 touchMap[val] = cssSupports ? window.CSS.supports('touch-action', val) : true; 14557 }); 14558 return touchMap; 14559} 14560 14561/** 14562 * Recognizer flow explained; * 14563 * All recognizers have the initial state of POSSIBLE when a input session starts. 14564 * The definition of a input session is from the first input until the last input, with all it's movement in it. * 14565 * Example session for mouse-input: mousedown -> mousemove -> mouseup 14566 * 14567 * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed 14568 * which determines with state it should be. 14569 * 14570 * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to 14571 * POSSIBLE to give it another change on the next cycle. 14572 * 14573 * Possible 14574 * | 14575 * +-----+---------------+ 14576 * | | 14577 * +-----+-----+ | 14578 * | | | 14579 * Failed Cancelled | 14580 * +-------+------+ 14581 * | | 14582 * Recognized Began 14583 * | 14584 * Changed 14585 * | 14586 * Ended/Recognized 14587 */ 14588var STATE_POSSIBLE = 1; 14589var STATE_BEGAN = 2; 14590var STATE_CHANGED = 4; 14591var STATE_ENDED = 8; 14592var STATE_RECOGNIZED = STATE_ENDED; 14593var STATE_CANCELLED = 16; 14594var STATE_FAILED = 32; 14595 14596/** 14597 * Recognizer 14598 * Every recognizer needs to extend from this class. 14599 * @constructor 14600 * @param {Object} options 14601 */ 14602function Recognizer(options) { 14603 this.options = assign({}, this.defaults, options || {}); 14604 14605 this.id = uniqueId(); 14606 14607 this.manager = null; 14608 14609 // default is enable true 14610 this.options.enable = ifUndefined(this.options.enable, true); 14611 14612 this.state = STATE_POSSIBLE; 14613 14614 this.simultaneous = {}; 14615 this.requireFail = []; 14616} 14617 14618Recognizer.prototype = { 14619 /** 14620 * @virtual 14621 * @type {Object} 14622 */ 14623 defaults: {}, 14624 14625 /** 14626 * set options 14627 * @param {Object} options 14628 * @return {Recognizer} 14629 */ 14630 set: function(options) { 14631 assign(this.options, options); 14632 14633 // also update the touchAction, in case something changed about the directions/enabled state 14634 this.manager && this.manager.touchAction.update(); 14635 return this; 14636 }, 14637 14638 /** 14639 * recognize simultaneous with an other recognizer. 14640 * @param {Recognizer} otherRecognizer 14641 * @returns {Recognizer} this 14642 */ 14643 recognizeWith: function(otherRecognizer) { 14644 if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) { 14645 return this; 14646 } 14647 14648 var simultaneous = this.simultaneous; 14649 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); 14650 if (!simultaneous[otherRecognizer.id]) { 14651 simultaneous[otherRecognizer.id] = otherRecognizer; 14652 otherRecognizer.recognizeWith(this); 14653 } 14654 return this; 14655 }, 14656 14657 /** 14658 * drop the simultaneous link. it doesnt remove the link on the other recognizer. 14659 * @param {Recognizer} otherRecognizer 14660 * @returns {Recognizer} this 14661 */ 14662 dropRecognizeWith: function(otherRecognizer) { 14663 if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) { 14664 return this; 14665 } 14666 14667 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); 14668 delete this.simultaneous[otherRecognizer.id]; 14669 return this; 14670 }, 14671 14672 /** 14673 * recognizer can only run when an other is failing 14674 * @param {Recognizer} otherRecognizer 14675 * @returns {Recognizer} this 14676 */ 14677 requireFailure: function(otherRecognizer) { 14678 if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) { 14679 return this; 14680 } 14681 14682 var requireFail = this.requireFail; 14683 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); 14684 if (inArray(requireFail, otherRecognizer) === -1) { 14685 requireFail.push(otherRecognizer); 14686 otherRecognizer.requireFailure(this); 14687 } 14688 return this; 14689 }, 14690 14691 /** 14692 * drop the requireFailure link. it does not remove the link on the other recognizer. 14693 * @param {Recognizer} otherRecognizer 14694 * @returns {Recognizer} this 14695 */ 14696 dropRequireFailure: function(otherRecognizer) { 14697 if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) { 14698 return this; 14699 } 14700 14701 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); 14702 var index = inArray(this.requireFail, otherRecognizer); 14703 if (index > -1) { 14704 this.requireFail.splice(index, 1); 14705 } 14706 return this; 14707 }, 14708 14709 /** 14710 * has require failures boolean 14711 * @returns {boolean} 14712 */ 14713 hasRequireFailures: function() { 14714 return this.requireFail.length > 0; 14715 }, 14716 14717 /** 14718 * if the recognizer can recognize simultaneous with an other recognizer 14719 * @param {Recognizer} otherRecognizer 14720 * @returns {Boolean} 14721 */ 14722 canRecognizeWith: function(otherRecognizer) { 14723 return !!this.simultaneous[otherRecognizer.id]; 14724 }, 14725 14726 /** 14727 * You should use `tryEmit` instead of `emit` directly to check 14728 * that all the needed recognizers has failed before emitting. 14729 * @param {Object} input 14730 */ 14731 emit: function(input) { 14732 var self = this; 14733 var state = this.state; 14734 14735 function emit(event) { 14736 self.manager.emit(event, input); 14737 } 14738 14739 // 'panstart' and 'panmove' 14740 if (state < STATE_ENDED) { 14741 emit(self.options.event + stateStr(state)); 14742 } 14743 14744 emit(self.options.event); // simple 'eventName' events 14745 14746 if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...) 14747 emit(input.additionalEvent); 14748 } 14749 14750 // panend and pancancel 14751 if (state >= STATE_ENDED) { 14752 emit(self.options.event + stateStr(state)); 14753 } 14754 }, 14755 14756 /** 14757 * Check that all the require failure recognizers has failed, 14758 * if true, it emits a gesture event, 14759 * otherwise, setup the state to FAILED. 14760 * @param {Object} input 14761 */ 14762 tryEmit: function(input) { 14763 if (this.canEmit()) { 14764 return this.emit(input); 14765 } 14766 // it's failing anyway 14767 this.state = STATE_FAILED; 14768 }, 14769 14770 /** 14771 * can we emit? 14772 * @returns {boolean} 14773 */ 14774 canEmit: function() { 14775 var i = 0; 14776 while (i < this.requireFail.length) { 14777 if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) { 14778 return false; 14779 } 14780 i++; 14781 } 14782 return true; 14783 }, 14784 14785 /** 14786 * update the recognizer 14787 * @param {Object} inputData 14788 */ 14789 recognize: function(inputData) { 14790 // make a new copy of the inputData 14791 // so we can change the inputData without messing up the other recognizers 14792 var inputDataClone = assign({}, inputData); 14793 14794 // is is enabled and allow recognizing? 14795 if (!boolOrFn(this.options.enable, [this, inputDataClone])) { 14796 this.reset(); 14797 this.state = STATE_FAILED; 14798 return; 14799 } 14800 14801 // reset when we've reached the end 14802 if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) { 14803 this.state = STATE_POSSIBLE; 14804 } 14805 14806 this.state = this.process(inputDataClone); 14807 14808 // the recognizer has recognized a gesture 14809 // so trigger an event 14810 if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) { 14811 this.tryEmit(inputDataClone); 14812 } 14813 }, 14814 14815 /** 14816 * return the state of the recognizer 14817 * the actual recognizing happens in this method 14818 * @virtual 14819 * @param {Object} inputData 14820 * @returns {Const} STATE 14821 */ 14822 process: function(inputData) { }, // jshint ignore:line 14823 14824 /** 14825 * return the preferred touch-action 14826 * @virtual 14827 * @returns {Array} 14828 */ 14829 getTouchAction: function() { }, 14830 14831 /** 14832 * called when the gesture isn't allowed to recognize 14833 * like when another is being recognized or it is disabled 14834 * @virtual 14835 */ 14836 reset: function() { } 14837}; 14838 14839/** 14840 * get a usable string, used as event postfix 14841 * @param {Const} state 14842 * @returns {String} state 14843 */ 14844function stateStr(state) { 14845 if (state & STATE_CANCELLED) { 14846 return 'cancel'; 14847 } else if (state & STATE_ENDED) { 14848 return 'end'; 14849 } else if (state & STATE_CHANGED) { 14850 return 'move'; 14851 } else if (state & STATE_BEGAN) { 14852 return 'start'; 14853 } 14854 return ''; 14855} 14856 14857/** 14858 * direction cons to string 14859 * @param {Const} direction 14860 * @returns {String} 14861 */ 14862function directionStr(direction) { 14863 if (direction == DIRECTION_DOWN) { 14864 return 'down'; 14865 } else if (direction == DIRECTION_UP) { 14866 return 'up'; 14867 } else if (direction == DIRECTION_LEFT) { 14868 return 'left'; 14869 } else if (direction == DIRECTION_RIGHT) { 14870 return 'right'; 14871 } 14872 return ''; 14873} 14874 14875/** 14876 * get a recognizer by name if it is bound to a manager 14877 * @param {Recognizer|String} otherRecognizer 14878 * @param {Recognizer} recognizer 14879 * @returns {Recognizer} 14880 */ 14881function getRecognizerByNameIfManager(otherRecognizer, recognizer) { 14882 var manager = recognizer.manager; 14883 if (manager) { 14884 return manager.get(otherRecognizer); 14885 } 14886 return otherRecognizer; 14887} 14888 14889/** 14890 * This recognizer is just used as a base for the simple attribute recognizers. 14891 * @constructor 14892 * @extends Recognizer 14893 */ 14894function AttrRecognizer() { 14895 Recognizer.apply(this, arguments); 14896} 14897 14898inherit(AttrRecognizer, Recognizer, { 14899 /** 14900 * @namespace 14901 * @memberof AttrRecognizer 14902 */ 14903 defaults: { 14904 /** 14905 * @type {Number} 14906 * @default 1 14907 */ 14908 pointers: 1 14909 }, 14910 14911 /** 14912 * Used to check if it the recognizer receives valid input, like input.distance > 10. 14913 * @memberof AttrRecognizer 14914 * @param {Object} input 14915 * @returns {Boolean} recognized 14916 */ 14917 attrTest: function(input) { 14918 var optionPointers = this.options.pointers; 14919 return optionPointers === 0 || input.pointers.length === optionPointers; 14920 }, 14921 14922 /** 14923 * Process the input and return the state for the recognizer 14924 * @memberof AttrRecognizer 14925 * @param {Object} input 14926 * @returns {*} State 14927 */ 14928 process: function(input) { 14929 var state = this.state; 14930 var eventType = input.eventType; 14931 14932 var isRecognized = state & (STATE_BEGAN | STATE_CHANGED); 14933 var isValid = this.attrTest(input); 14934 14935 // on cancel input and we've recognized before, return STATE_CANCELLED 14936 if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) { 14937 return state | STATE_CANCELLED; 14938 } else if (isRecognized || isValid) { 14939 if (eventType & INPUT_END) { 14940 return state | STATE_ENDED; 14941 } else if (!(state & STATE_BEGAN)) { 14942 return STATE_BEGAN; 14943 } 14944 return state | STATE_CHANGED; 14945 } 14946 return STATE_FAILED; 14947 } 14948}); 14949 14950/** 14951 * Pan 14952 * Recognized when the pointer is down and moved in the allowed direction. 14953 * @constructor 14954 * @extends AttrRecognizer 14955 */ 14956function PanRecognizer() { 14957 AttrRecognizer.apply(this, arguments); 14958 14959 this.pX = null; 14960 this.pY = null; 14961} 14962 14963inherit(PanRecognizer, AttrRecognizer, { 14964 /** 14965 * @namespace 14966 * @memberof PanRecognizer 14967 */ 14968 defaults: { 14969 event: 'pan', 14970 threshold: 10, 14971 pointers: 1, 14972 direction: DIRECTION_ALL 14973 }, 14974 14975 getTouchAction: function() { 14976 var direction = this.options.direction; 14977 var actions = []; 14978 if (direction & DIRECTION_HORIZONTAL) { 14979 actions.push(TOUCH_ACTION_PAN_Y); 14980 } 14981 if (direction & DIRECTION_VERTICAL) { 14982 actions.push(TOUCH_ACTION_PAN_X); 14983 } 14984 return actions; 14985 }, 14986 14987 directionTest: function(input) { 14988 var options = this.options; 14989 var hasMoved = true; 14990 var distance = input.distance; 14991 var direction = input.direction; 14992 var x = input.deltaX; 14993 var y = input.deltaY; 14994 14995 // lock to axis? 14996 if (!(direction & options.direction)) { 14997 if (options.direction & DIRECTION_HORIZONTAL) { 14998 direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; 14999 hasMoved = x != this.pX; 15000 distance = Math.abs(input.deltaX); 15001 } else { 15002 direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN; 15003 hasMoved = y != this.pY; 15004 distance = Math.abs(input.deltaY); 15005 } 15006 } 15007 input.direction = direction; 15008 return hasMoved && distance > options.threshold && direction & options.direction; 15009 }, 15010 15011 attrTest: function(input) { 15012 return AttrRecognizer.prototype.attrTest.call(this, input) && 15013 (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input))); 15014 }, 15015 15016 emit: function(input) { 15017 15018 this.pX = input.deltaX; 15019 this.pY = input.deltaY; 15020 15021 var direction = directionStr(input.direction); 15022 15023 if (direction) { 15024 input.additionalEvent = this.options.event + direction; 15025 } 15026 this._super.emit.call(this, input); 15027 } 15028}); 15029 15030/** 15031 * Pinch 15032 * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out). 15033 * @constructor 15034 * @extends AttrRecognizer 15035 */ 15036function PinchRecognizer() { 15037 AttrRecognizer.apply(this, arguments); 15038} 15039 15040inherit(PinchRecognizer, AttrRecognizer, { 15041 /** 15042 * @namespace 15043 * @memberof PinchRecognizer 15044 */ 15045 defaults: { 15046 event: 'pinch', 15047 threshold: 0, 15048 pointers: 2 15049 }, 15050 15051 getTouchAction: function() { 15052 return [TOUCH_ACTION_NONE]; 15053 }, 15054 15055 attrTest: function(input) { 15056 return this._super.attrTest.call(this, input) && 15057 (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN); 15058 }, 15059 15060 emit: function(input) { 15061 if (input.scale !== 1) { 15062 var inOut = input.scale < 1 ? 'in' : 'out'; 15063 input.additionalEvent = this.options.event + inOut; 15064 } 15065 this._super.emit.call(this, input); 15066 } 15067}); 15068 15069/** 15070 * Press 15071 * Recognized when the pointer is down for x ms without any movement. 15072 * @constructor 15073 * @extends Recognizer 15074 */ 15075function PressRecognizer() { 15076 Recognizer.apply(this, arguments); 15077 15078 this._timer = null; 15079 this._input = null; 15080} 15081 15082inherit(PressRecognizer, Recognizer, { 15083 /** 15084 * @namespace 15085 * @memberof PressRecognizer 15086 */ 15087 defaults: { 15088 event: 'press', 15089 pointers: 1, 15090 time: 251, // minimal time of the pointer to be pressed 15091 threshold: 9 // a minimal movement is ok, but keep it low 15092 }, 15093 15094 getTouchAction: function() { 15095 return [TOUCH_ACTION_AUTO]; 15096 }, 15097 15098 process: function(input) { 15099 var options = this.options; 15100 var validPointers = input.pointers.length === options.pointers; 15101 var validMovement = input.distance < options.threshold; 15102 var validTime = input.deltaTime > options.time; 15103 15104 this._input = input; 15105 15106 // we only allow little movement 15107 // and we've reached an end event, so a tap is possible 15108 if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) { 15109 this.reset(); 15110 } else if (input.eventType & INPUT_START) { 15111 this.reset(); 15112 this._timer = setTimeoutContext(function() { 15113 this.state = STATE_RECOGNIZED; 15114 this.tryEmit(); 15115 }, options.time, this); 15116 } else if (input.eventType & INPUT_END) { 15117 return STATE_RECOGNIZED; 15118 } 15119 return STATE_FAILED; 15120 }, 15121 15122 reset: function() { 15123 clearTimeout(this._timer); 15124 }, 15125 15126 emit: function(input) { 15127 if (this.state !== STATE_RECOGNIZED) { 15128 return; 15129 } 15130 15131 if (input && (input.eventType & INPUT_END)) { 15132 this.manager.emit(this.options.event + 'up', input); 15133 } else { 15134 this._input.timeStamp = now(); 15135 this.manager.emit(this.options.event, this._input); 15136 } 15137 } 15138}); 15139 15140/** 15141 * Rotate 15142 * Recognized when two or more pointer are moving in a circular motion. 15143 * @constructor 15144 * @extends AttrRecognizer 15145 */ 15146function RotateRecognizer() { 15147 AttrRecognizer.apply(this, arguments); 15148} 15149 15150inherit(RotateRecognizer, AttrRecognizer, { 15151 /** 15152 * @namespace 15153 * @memberof RotateRecognizer 15154 */ 15155 defaults: { 15156 event: 'rotate', 15157 threshold: 0, 15158 pointers: 2 15159 }, 15160 15161 getTouchAction: function() { 15162 return [TOUCH_ACTION_NONE]; 15163 }, 15164 15165 attrTest: function(input) { 15166 return this._super.attrTest.call(this, input) && 15167 (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN); 15168 } 15169}); 15170 15171/** 15172 * Swipe 15173 * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction. 15174 * @constructor 15175 * @extends AttrRecognizer 15176 */ 15177function SwipeRecognizer() { 15178 AttrRecognizer.apply(this, arguments); 15179} 15180 15181inherit(SwipeRecognizer, AttrRecognizer, { 15182 /** 15183 * @namespace 15184 * @memberof SwipeRecognizer 15185 */ 15186 defaults: { 15187 event: 'swipe', 15188 threshold: 10, 15189 velocity: 0.3, 15190 direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL, 15191 pointers: 1 15192 }, 15193 15194 getTouchAction: function() { 15195 return PanRecognizer.prototype.getTouchAction.call(this); 15196 }, 15197 15198 attrTest: function(input) { 15199 var direction = this.options.direction; 15200 var velocity; 15201 15202 if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) { 15203 velocity = input.overallVelocity; 15204 } else if (direction & DIRECTION_HORIZONTAL) { 15205 velocity = input.overallVelocityX; 15206 } else if (direction & DIRECTION_VERTICAL) { 15207 velocity = input.overallVelocityY; 15208 } 15209 15210 return this._super.attrTest.call(this, input) && 15211 direction & input.offsetDirection && 15212 input.distance > this.options.threshold && 15213 input.maxPointers == this.options.pointers && 15214 abs(velocity) > this.options.velocity && input.eventType & INPUT_END; 15215 }, 15216 15217 emit: function(input) { 15218 var direction = directionStr(input.offsetDirection); 15219 if (direction) { 15220 this.manager.emit(this.options.event + direction, input); 15221 } 15222 15223 this.manager.emit(this.options.event, input); 15224 } 15225}); 15226 15227/** 15228 * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur 15229 * between the given interval and position. The delay option can be used to recognize multi-taps without firing 15230 * a single tap. 15231 * 15232 * The eventData from the emitted event contains the property `tapCount`, which contains the amount of 15233 * multi-taps being recognized. 15234 * @constructor 15235 * @extends Recognizer 15236 */ 15237function TapRecognizer() { 15238 Recognizer.apply(this, arguments); 15239 15240 // previous time and center, 15241 // used for tap counting 15242 this.pTime = false; 15243 this.pCenter = false; 15244 15245 this._timer = null; 15246 this._input = null; 15247 this.count = 0; 15248} 15249 15250inherit(TapRecognizer, Recognizer, { 15251 /** 15252 * @namespace 15253 * @memberof PinchRecognizer 15254 */ 15255 defaults: { 15256 event: 'tap', 15257 pointers: 1, 15258 taps: 1, 15259 interval: 300, // max time between the multi-tap taps 15260 time: 250, // max time of the pointer to be down (like finger on the screen) 15261 threshold: 9, // a minimal movement is ok, but keep it low 15262 posThreshold: 10 // a multi-tap can be a bit off the initial position 15263 }, 15264 15265 getTouchAction: function() { 15266 return [TOUCH_ACTION_MANIPULATION]; 15267 }, 15268 15269 process: function(input) { 15270 var options = this.options; 15271 15272 var validPointers = input.pointers.length === options.pointers; 15273 var validMovement = input.distance < options.threshold; 15274 var validTouchTime = input.deltaTime < options.time; 15275 15276 this.reset(); 15277 15278 if ((input.eventType & INPUT_START) && (this.count === 0)) { 15279 return this.failTimeout(); 15280 } 15281 15282 // we only allow little movement 15283 // and we've reached an end event, so a tap is possible 15284 if (validMovement && validTouchTime && validPointers) { 15285 if (input.eventType != INPUT_END) { 15286 return this.failTimeout(); 15287 } 15288 15289 var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true; 15290 var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold; 15291 15292 this.pTime = input.timeStamp; 15293 this.pCenter = input.center; 15294 15295 if (!validMultiTap || !validInterval) { 15296 this.count = 1; 15297 } else { 15298 this.count += 1; 15299 } 15300 15301 this._input = input; 15302 15303 // if tap count matches we have recognized it, 15304 // else it has began recognizing... 15305 var tapCount = this.count % options.taps; 15306 if (tapCount === 0) { 15307 // no failing requirements, immediately trigger the tap event 15308 // or wait as long as the multitap interval to trigger 15309 if (!this.hasRequireFailures()) { 15310 return STATE_RECOGNIZED; 15311 } else { 15312 this._timer = setTimeoutContext(function() { 15313 this.state = STATE_RECOGNIZED; 15314 this.tryEmit(); 15315 }, options.interval, this); 15316 return STATE_BEGAN; 15317 } 15318 } 15319 } 15320 return STATE_FAILED; 15321 }, 15322 15323 failTimeout: function() { 15324 this._timer = setTimeoutContext(function() { 15325 this.state = STATE_FAILED; 15326 }, this.options.interval, this); 15327 return STATE_FAILED; 15328 }, 15329 15330 reset: function() { 15331 clearTimeout(this._timer); 15332 }, 15333 15334 emit: function() { 15335 if (this.state == STATE_RECOGNIZED) { 15336 this._input.tapCount = this.count; 15337 this.manager.emit(this.options.event, this._input); 15338 } 15339 } 15340}); 15341 15342/** 15343 * Simple way to create a manager with a default set of recognizers. 15344 * @param {HTMLElement} element 15345 * @param {Object} [options] 15346 * @constructor 15347 */ 15348function
vendor: 8,263 bytes, lines 15348-15610
15348NGHammer(element, options) { 15349 options = options || {}; 15350 options.recognizers = ifUndefined(options.recognizers, NGHammer.defaults.preset); 15351 return new Manager(element, options); 15352} 15353 15354/** 15355 * @const {string} 15356 */ 15357NGHammer.VERSION = '2.0.7'; 15358 15359/** 15360 * default settings 15361 * @namespace 15362 */ 15363NGHammer.defaults = { 15364 /** 15365 * set if DOM events are being triggered. 15366 * But this is slower and unused by simple implementations, so disabled by default. 15367 * @type {Boolean} 15368 * @default false 15369 */ 15370 domEvents: false, 15371 15372 /** 15373 * The value for the touchAction property/fallback. 15374 * When set to `compute` it will magically set the correct value based on the added recognizers. 15375 * @type {String} 15376 * @default compute 15377 */ 15378 touchAction: TOUCH_ACTION_COMPUTE, 15379 15380 /** 15381 * @type {Boolean} 15382 * @default true 15383 */ 15384 enable: true, 15385 15386 /** 15387 * EXPERIMENTAL FEATURE -- can be removed/changed 15388 * Change the parent input target element. 15389 * If Null, then it is being set the to main element. 15390 * @type {Null|EventTarget} 15391 * @default null 15392 */ 15393 inputTarget: null, 15394 15395 /** 15396 * force an input class 15397 * @type {Null|Function} 15398 * @default null 15399 */ 15400 inputClass: null, 15401 15402 /** 15403 * Default recognizer setup when calling `NGHammer()` 15404 * When creating a new Manager these will be skipped. 15405 * @type {Array} 15406 */ 15407 preset: [ 15408 // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...] 15409 [RotateRecognizer, {enable: false}], 15410 [PinchRecognizer, {enable: false}, ['rotate']], 15411 [SwipeRecognizer, {direction: DIRECTION_HORIZONTAL}], 15412 [PanRecognizer, {direction: DIRECTION_HORIZONTAL}, ['swipe']], 15413 [TapRecognizer], 15414 [TapRecognizer, {event: 'doubletap', taps: 2}, ['tap']], 15415 [PressRecognizer] 15416 ], 15417 15418 /** 15419 * Some CSS properties can be used to improve the working of NGHammer. 15420 * Add them to this method and they will be set when creating a new Manager. 15421 * @namespace 15422 */ 15423 cssProps: { 15424 /** 15425 * Disables text selection to improve the dragging gesture. Mainly for desktop browsers. 15426 * @type {String} 15427 * @default 'none' 15428 */ 15429 userSelect: 'none', 15430 15431 /** 15432 * Disable the Windows Phone grippers when pressing an element. 15433 * @type {String} 15434 * @default 'none' 15435 */ 15436 touchSelect: 'none', 15437 15438 /** 15439 * Disables the default callout shown when you touch and hold a touch target. 15440 * On iOS, when you touch and hold a touch target such as a link, Safari displays 15441 * a callout containing information about the link. This property allows you to disable that callout. 15442 * @type {String} 15443 * @default 'none' 15444 */ 15445 touchCallout: 'none', 15446 15447 /** 15448 * Specifies whether zooming is enabled. Used by IE10> 15449 * @type {String} 15450 * @default 'none' 15451 */ 15452 contentZooming: 'none', 15453 15454 /** 15455 * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers. 15456 * @type {String} 15457 * @default 'none' 15458 */ 15459 userDrag: 'none', 15460 15461 /** 15462 * Overrides the highlight color shown when the user taps a link or a JavaScript 15463 * clickable element in iOS. This property obeys the alpha value, if specified. 15464 * @type {String} 15465 * @default 'rgba(0,0,0,0)' 15466 */ 15467 tapHighlightColor: 'rgba(0,0,0,0)' 15468 } 15469}; 15470 15471var STOP = 1; 15472var FORCED_STOP = 2; 15473 15474/** 15475 * Manager 15476 * @param {HTMLElement} element 15477 * @param {Object} [options] 15478 * @constructor 15479 */ 15480function Manager(element, options) { 15481 this.options = assign({}, NGHammer.defaults, options || {}); 15482 15483 this.options.inputTarget = this.options.inputTarget || element; 15484 15485 this.handlers = {}; 15486 this.session = {}; 15487 this.recognizers = []; 15488 this.oldCssProps = {}; 15489 15490 this.element = element; 15491 this.input = createInputInstance(this); 15492 this.touchAction = new TouchAction(this, this.options.touchAction); 15493 15494 toggleCssProps(this, true); 15495 15496 each(this.options.recognizers, function(item) { 15497 var recognizer = this.add(new (item[0])(item[1])); 15498 item[2] && recognizer.recognizeWith(item[2]); 15499 item[3] && recognizer.requireFailure(item[3]); 15500 }, this); 15501} 15502 15503Manager.prototype = { 15504 /** 15505 * set options 15506 * @param {Object} options 15507 * @returns {Manager} 15508 */ 15509 set: function(options) { 15510 assign(this.options, options); 15511 15512 // Options that need a little more setup 15513 if (options.touchAction) { 15514 this.touchAction.update(); 15515 } 15516 if (options.inputTarget) { 15517 // Clean up existing event listeners and reinitialize 15518 this.input.destroy(); 15519 this.input.target = options.inputTarget; 15520 this.input.init(); 15521 } 15522 return this; 15523 }, 15524 15525 /** 15526 * stop recognizing for this session. 15527 * This session will be discarded, when a new [input]start event is fired. 15528 * When forced, the recognizer cycle is stopped immediately. 15529 * @param {Boolean} [force] 15530 */ 15531 stop: function(force) { 15532 this.session.stopped = force ? FORCED_STOP : STOP; 15533 }, 15534 15535 /** 15536 * run the recognizers! 15537 * called by the inputHandler function on every movement of the pointers (touches) 15538 * it walks through all the recognizers and tries to detect the gesture that is being made 15539 * @param {Object} inputData 15540 */ 15541 recognize: function(inputData) { 15542 var session = this.session; 15543 if (session.stopped) { 15544 return; 15545 } 15546 15547 // run the touch-action polyfill 15548 this.touchAction.preventDefaults(inputData); 15549 15550 var recognizer; 15551 var recognizers = this.recognizers; 15552 15553 // this holds the recognizer that is being recognized. 15554 // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED 15555 // if no recognizer is detecting a thing, it is set to `null` 15556 var curRecognizer = session.curRecognizer; 15557 15558 // reset when the last recognizer is recognized 15559 // or when we're in a new session 15560 if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) { 15561 curRecognizer = session.curRecognizer = null; 15562 } 15563 15564 var i = 0; 15565 while (i < recognizers.length) { 15566 recognizer = recognizers[i]; 15567 15568 // find out if we are allowed try to recognize the input for this one. 15569 // 1. allow if the session is NOT forced stopped (see the .stop() method) 15570 // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one 15571 // that is being recognized. 15572 // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer. 15573 // this can be setup with the `recognizeWith()` method on the recognizer. 15574 if (session.stopped !== FORCED_STOP && ( // 1 15575 !curRecognizer || recognizer == curRecognizer || // 2 15576 recognizer.canRecognizeWith(curRecognizer))) { // 3 15577 recognizer.recognize(inputData); 15578 } else { 15579 recognizer.reset(); 15580 } 15581 15582 // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the 15583 // current active recognizer. but only if we don't already have an active recognizer 15584 if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) { 15585 curRecognizer = session.curRecognizer = recognizer; 15586 } 15587 i++; 15588 } 15589 }, 15590 15591 /** 15592 * get a recognizer by its event name. 15593 * @param {Recognizer|String} recognizer 15594 * @returns {Recognizer|Null} 15595 */ 15596 get: function(recognizer) { 15597 if (recognizer instanceof Recognizer) { 15598 return recognizer; 15599 } 15600 15601 var recognizers = this.recognizers; 15602 for (var i = 0; i < recognizers.length; i++) { 15603 if (recognizers[i].options.event == recognizer) { 15604 return recognizers[i]; 15605 } 15606 } 15607 return null; 15608 }, 15609 15610 /**
vendor: 9,974 bytes, lines 15611-15964
15611 * add a recognizer to the manager 15612 * existing recognizers with the same event name will be removed 15613 * @param {Recognizer} recognizer 15614 * @returns {Recognizer|Manager} 15615 */ 15616 add: function(recognizer) { 15617 if (invokeArrayArg(recognizer, 'add', this)) { 15618 return this; 15619 } 15620 15621 // remove existing 15622 var existing = this.get(recognizer.options.event); 15623 if (existing) { 15624 this.remove(existing); 15625 } 15626 15627 this.recognizers.push(recognizer); 15628 recognizer.manager = this; 15629 15630 this.touchAction.update(); 15631 return recognizer; 15632 }, 15633 15634 /** 15635 * remove a recognizer by name or instance 15636 * @param {Recognizer|String} recognizer 15637 * @returns {Manager} 15638 */ 15639 remove: function(recognizer) { 15640 if (invokeArrayArg(recognizer, 'remove', this)) { 15641 return this; 15642 } 15643 15644 recognizer = this.get(recognizer); 15645 15646 // let's make sure this recognizer exists 15647 if (recognizer) { 15648 var recognizers = this.recognizers; 15649 var index = inArray(recognizers, recognizer); 15650 15651 if (index !== -1) { 15652 recognizers.splice(index, 1); 15653 this.touchAction.update(); 15654 } 15655 } 15656 15657 return this; 15658 }, 15659 15660 /** 15661 * bind event 15662 * @param {String} events 15663 * @param {Function} handler 15664 * @returns {EventEmitter} this 15665 */ 15666 on: function(events, handler) { 15667 if (events === undefined) { 15668 return; 15669 } 15670 if (handler === undefined) { 15671 return; 15672 } 15673 15674 var handlers = this.handlers; 15675 each(splitStr(events), function(event) { 15676 handlers[event] = handlers[event] || []; 15677 handlers[event].push(handler); 15678 }); 15679 return this; 15680 }, 15681 15682 /** 15683 * unbind event, leave emit blank to remove all handlers 15684 * @param {String} events 15685 * @param {Function} [handler] 15686 * @returns {EventEmitter} this 15687 */ 15688 off: function(events, handler) { 15689 if (events === undefined) { 15690 return; 15691 } 15692 15693 var handlers = this.handlers; 15694 each(splitStr(events), function(event) { 15695 if (!handler) { 15696 delete handlers[event]; 15697 } else { 15698 handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1); 15699 } 15700 }); 15701 return this; 15702 }, 15703 15704 /** 15705 * emit event to the listeners 15706 * @param {String} event 15707 * @param {Object} data 15708 */ 15709 emit: function(event, data) { 15710 // we also want to trigger dom events 15711 if (this.options.domEvents) { 15712 triggerDomEvent(event, data); 15713 } 15714 15715 // no handlers, so skip it all 15716 var handlers = this.handlers[event] && this.handlers[event].slice(); 15717 if (!handlers || !handlers.length) { 15718 return; 15719 } 15720 15721 data.type = event; 15722 data.preventDefault = function() { 15723 data.srcEvent.preventDefault(); 15724 }; 15725 15726 var i = 0; 15727 while (i < handlers.length) { 15728 handlers[i](data); 15729 i++; 15730 } 15731 }, 15732 15733 /** 15734 * destroy the manager and unbinds all events 15735 * it doesn't unbind dom events, that is the user own responsibility 15736 */ 15737 destroy: function() { 15738 this.element && toggleCssProps(this, false); 15739 15740 this.handlers = {}; 15741 this.session = {}; 15742 this.input.destroy(); 15743 this.element = null; 15744 } 15745}; 15746 15747/** 15748 * add/remove the css properties as defined in manager.options.cssProps 15749 * @param {Manager} manager 15750 * @param {Boolean} add 15751 */ 15752function toggleCssProps(manager, add) { 15753 var element = manager.element; 15754 if (!element.style) { 15755 return; 15756 } 15757 var prop; 15758 each(manager.options.cssProps, function(value, name) { 15759 prop = prefixed(element.style, name); 15760 if (add) { 15761 manager.oldCssProps[prop] = element.style[prop]; 15762 element.style[prop] = value; 15763 } else { 15764 element.style[prop] = manager.oldCssProps[prop] || ''; 15765 } 15766 }); 15767 if (!add) { 15768 manager.oldCssProps = {}; 15769 } 15770} 15771 15772/** 15773 * trigger dom event 15774 * @param {String} event 15775 * @param {Object} data 15776 */ 15777function triggerDomEvent(event, data) { 15778 var gestureEvent = document.createEvent('Event'); 15779 gestureEvent.initEvent(event, true, true); 15780 gestureEvent.gesture = data; 15781 data.target.dispatchEvent(gestureEvent); 15782} 15783 15784assign(NGHammer, { 15785 INPUT_START: INPUT_START, 15786 INPUT_MOVE: INPUT_MOVE, 15787 INPUT_END: INPUT_END, 15788 INPUT_CANCEL: INPUT_CANCEL, 15789 15790 STATE_POSSIBLE: STATE_POSSIBLE, 15791 STATE_BEGAN: STATE_BEGAN, 15792 STATE_CHANGED: STATE_CHANGED, 15793 STATE_ENDED: STATE_ENDED, 15794 STATE_RECOGNIZED: STATE_RECOGNIZED, 15795 STATE_CANCELLED: STATE_CANCELLED, 15796 STATE_FAILED: STATE_FAILED, 15797 15798 DIRECTION_NONE: DIRECTION_NONE, 15799 DIRECTION_LEFT: DIRECTION_LEFT, 15800 DIRECTION_RIGHT: DIRECTION_RIGHT, 15801 DIRECTION_UP: DIRECTION_UP, 15802 DIRECTION_DOWN: DIRECTION_DOWN, 15803 DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL, 15804 DIRECTION_VERTICAL: DIRECTION_VERTICAL, 15805 DIRECTION_ALL: DIRECTION_ALL, 15806 15807 Manager: Manager, 15808 Input: Input, 15809 TouchAction: TouchAction, 15810 15811 TouchInput: TouchInput, 15812 MouseInput: MouseInput, 15813 PointerEventInput: PointerEventInput, 15814 TouchMouseInput: TouchMouseInput, 15815 SingleTouchInput: SingleTouchInput, 15816 15817 Recognizer: Recognizer, 15818 AttrRecognizer: AttrRecognizer, 15819 Tap: TapRecognizer, 15820 Pan: PanRecognizer, 15821 Swipe: SwipeRecognizer, 15822 Pinch: PinchRecognizer, 15823 Rotate: RotateRecognizer, 15824 Press: PressRecognizer, 15825 15826 on: addEventListeners, 15827 off: removeEventListeners, 15828 each: each, 15829 merge: merge, 15830 extend: extend, 15831 assign: assign, 15832 inherit: inherit, 15833 bindFn: bindFn, 15834 prefixed: prefixed 15835}); 15836 15837// this prevents errors when NGHammer is loaded in the presence of an AMD 15838// style loader but by script tag, not by the loader. 15839var freeGlobal = (typeof window !== 'undefined' ? window : (typeof self !== 'undefined' ? self : {})); // jshint ignore:line 15840freeGlobal.NGHammer = NGHammer; 15841 15842if (typeof define === 'function' && define.amdDISABLED) { 15843 define(function() { 15844 return NGHammer; 15845 }); 15846} else if (typeof module != 'undefined' && module.exports) { 15847 module.exports = NGHammer; 15848} else { 15849 window[exportName] = NGHammer; 15850} 15851 15852})(window, document, 'NGHammer'); 15853 15854 15855 15856 15857 15858 15859// END NANOGALLERY2 15860// }( jQuery ))); 15861})); 15862 15863 15864//########################################################################################################################## 15865//########################################################################################################################## 15866//########################################################################################################################## 15867//########################################################################################################################## 15868//########################################################################################################################## 15869 15870// nanogallery2 auto start whithout javascript call 15871(function(){ 15872 'use strict'; 15873 15874 function document_ready(callback){ 15875 // in case the document is already rendered 15876 if (document.readyState!='loading') callback(); 15877 // modern browsers 15878 else if (document.addEventListener) document.addEventListener('DOMContentLoaded', callback); 15879 // IE <= 8 15880 else document.attachEvent('onreadystatechange', function(){ 15881 if (document.readyState=='complete') callback(); 15882 }); 15883 } 15884 15885 document_ready(function(){ 15886 15887 // retrieve GALLERIES 15888 var t=document.querySelectorAll('[data-nanogallery2]'); 15889 for( var i=0; i < t.length; i++ ) { 15890 jQuery( t[i] ).nanogallery2( jQuery(t[i]).data('nanogallery2') ); 15891 } 15892 15893 // retrieve SINGLE ELEMENTS -> ONLY LIGHTBOX / NO GALLERY 15894 var t = document.querySelectorAll('[data-nanogallery2-lightbox]'); 15895 for( var i=0; i < t.length; i++ ) { 15896 15897 // set mouse pointer 15898 t[i].classList.add('NGY2ThumbnailLightbox'); 15899 15900 // add click event 15901 t[i].addEventListener('click', function(e) { 15902 // disable link tag if <A> element 15903 e.preventDefault(); 15904 15905 // default options for standalone lightbox 15906 var options = { 15907 lightboxStandalone: true, 15908 viewerToolbar: { display: false } 15909 }; 15910 15911 // group of images 15912 var g = this.dataset.nanogallery2Lgroup; 15913 15914 // Retrieve the lightbox configuration 15915 // it just need to be defined on one of the elements, which will be displayed in the lightbox 15916 var t = document.querySelectorAll('[data-nanogallery2-lightbox]'); 15917 for( var i=0; i < t.length; i++ ) { 15918 if( t[i].dataset.nanogallery2Lgroup == g ) { 15919 if( t[i].dataset.nanogallery2Lightbox !== "" ) { 15920 options = jQuery.extend(true, {}, options, jQuery(t[i]).data('nanogallery2Lightbox')); 15921 break; 15922 } 15923 } 15924 } 15925 jQuery( this ).nanogallery2( options ); 15926 15927 }); 15928 15929 } 15930 }); 15931 15932 15933 15934 // jQuery(document).ready(function () { 15935 15936 // var t=document.querySelectorAll('[data-nanogallery2-portable]'); 15937 // if( t.length > 0 ) { 15938 // portable mode 15939 // var link = document.createElement('link'); 15940 // link.setAttribute("rel", "stylesheet"); 15941 // link.setAttribute("type", "text/css"); 15942 // link.onload = function(){ 15943 // for( var i=0; i < t.length; i++ ) { 15944 // jQuery(t[i]).nanogallery2(jQuery(t[i]).data('nanogallery2-portable')); 15945 // } 15946 // } 15947 // link.setAttribute("href", '//nano.gallery/css/nanogallery2.css'); 15948 // document.getElementsByTagName("head")[0].appendChild(link); 15949 // } 15950 // else { 15951 // standard mode 15952 15953 // GALLERIES 15954 // var t=document.querySelectorAll('[data-nanogallery2]'); 15955 // for( var i=0; i < t.length; i++ ) { 15956 // jQuery( t[i] ).nanogallery2( jQuery(t[i]).data('nanogallery2') ); 15957 // } 15958 15959 15960 // } 15961 15962 // }); 15963}).call(null); 15964
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.