vendor: 37,254 bytes, lines 1-874
1/* nanogallery2 - v2.4.2 - 2019-03-22 - https://nanogallery2.nanostudio.org */ 2/**! 3 * @preserve nanogallery2 - javascript photo / video gallery and lightbox 4 * Homepage: http://nanogallery2.nanostudio.org 5 * Sources: https://github.com/nanostudio-org/nanogallery2 6 * 7 * License: GPLv3 and commercial licence 8 * 9 * Requirements: 10 * - jQuery (http://www.jquery.com) - version >= 1.7.1 11 * 12 * Embeded components: 13 * - shifty (https://github.com/jeremyckahn/shifty) 14 * - imagesloaded (https://github.com/desandro/imagesloaded) 15 * - hammer.js (http://hammerjs.github.io/) 16 * - screenfull.js (https://github.com/sindresorhus/screenfull.js) 17 * Tools: 18 * - webfont generated with http://fontello.com - mainly based on Font Awesome Copyright (C) 2012 by Dave Gandy (http://fontawesome.io/) 19 * - ICO online converter: https://iconverticons.com/online/ 20 */ 21 22 23 24// ########################################### 25// ##### nanogallery2 as a JQUERY PLUGIN ##### 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 // avoid if possible (performance issue) 133 function inViewport( $elt, threshold ) { 134 var wp = getViewport(), 135 eltOS = $elt.offset(), 136 th = $elt.outerHeight(true), 137 tw = $elt.outerWidth(true); 138 if( eltOS.top >= (wp.t - threshold) 139 && (eltOS.top + th) <= (wp.t + wp.h + threshold) 140 && eltOS.left >= (wp.l - threshold) 141 && (eltOS.left + tw) <= (wp.l + wp.w + threshold) ) { 142 return true; 143 } 144 else { 145 return false; 146 } 147 } 148 149 // avoid if possible (performance issue) 150 function inViewportVert( $elt, threshold ) { 151 var wp = getViewport(), 152 eltOS = $elt.offset(), 153 th = $elt.outerHeight(true); 154 //var tw=$elt.outerWidth(true); 155 156 if( wp.t == 0 && (eltOS.top) <= (wp.t + wp.h ) ) { return true; } 157 158 if( eltOS.top >= (wp.t) 159 && (eltOS.top + th) <= (wp.t + wp.h - threshold) ) { 160 return true; 161 } 162 else { 163 return false; 164 } 165 } 166 167 168 // set z-index to display 2 elements on top of all others 169 function set2ElementsOnTop( start, elt1, elt2 ) { 170 var highest_index = 0; 171 if( start=='' ) { start= '*'; } 172 jQuery(start).each(function() { 173 var cur = parseInt(jQuery(this).css('z-index')); 174 highest_index = cur > highest_index ? cur : highest_index; 175 }); 176 highest_index++; 177 jQuery(elt2).css('z-index',highest_index+1); 178 jQuery(elt1).css('z-index',highest_index); 179 } 180 181 // set z-index to display element on top of all others 182 function setElementOnTop( start, elt ) { 183 var highest_index = 0; 184 if( start == '' ) { start = '*'; } 185 jQuery(start).each(function() { 186 var cur = parseInt(jQuery(this).css('z-index')); 187 highest_index = cur > highest_index ? cur : highest_index; 188 }); 189 highest_index++; 190 jQuery(elt).css('z-index',highest_index); 191 } 192 193 // return the real type of the object 194 var toType = function( obj ) { 195 // by Angus Croll - http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/ 196 return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase() 197 }; 198 199 200 $.nanogallery2 = function (elt, options) { 201 // To avoid scope issues, use '_this' instead of 'this' 202 // to reference this class from internal events and functions. 203 var _this = this; 204 205 // Access to jQuery and DOM versions of element 206 _this.$e = jQuery(elt); 207 _this.e = elt; 208 209 // Add a reverse reference to the DOM object 210 _this.$e.data('nanogallery2data', _this); 211 212 _this.init = function () { 213 214 // define these global objects only once per HTML page 215 if (typeof window.NGY2Item === 'undefined') { 216 217 window.NGY2Tools = (function () { 218 219 function NGY2Tools() { 220 var nextId = 1; // private static --> all instances 221 } 222 223 // check album name - albumList/blackList/whiteList 224 NGY2Tools.FilterAlbumName = function( title, ID ) { 225 var s = title.toUpperCase(); 226 if( this.albumList.length > 0 ) { 227 for( var j=0; j < this.albumList.length; j++) { 228 if( s === this.albumList[j].toUpperCase() || ID === this.albumList[j] ) { 229 return true; 230 } 231 } 232 } 233 else { 234 var found = false; 235 if( this.whiteList !== null ) { 236 //whiteList : authorize only album cointaining one of the specified keyword in the title 237 for( var j = 0; j < this.whiteList.length; j++) { 238 if( s.indexOf(this.whiteList[j]) !== -1 ) { 239 found = true; 240 } 241 } 242 if( !found ) { return false; } 243 } 244 245 246 if( this.blackList !== null ) { 247 //blackList : ignore album cointaining one of the specified keyword in the title 248 for( var j = 0; j < this.blackList.length; j++) { 249 if( s.indexOf(this.blackList[j]) !== -1 ) { 250 return false; 251 } 252 } 253 } 254 return true; 255 } 256 }; 257 258 259 /** @function nanoAlert */ 260 /* Display an alert message in a specific element */ 261 NGY2Tools.NanoAlert = function(context, msg, verbose) { 262 NGY2Tools.NanoConsoleLog.call(context, msg); 263 if( context.$E.conConsole != null ) { 264 context.$E.conConsole.css({visibility: 'visible', minHeight: '100px'}); 265 if( verbose == false ) { 266 context.$E.conConsole.append('<p>' + msg + '</p>'); 267 } 268 else { 269 context.$E.conConsole.append('<p>nanogallery2: '+ msg + ' [' + context.baseEltID + ']</p>'); 270 } 271 } 272 }; 273 274 275 /** @function NanoConsoleLog */ 276 /* write message to the browser console */ 277 NGY2Tools.NanoConsoleLog = function(context, msg) { 278 if (window.console) { console.log('nanogallery2: ' + msg + ' [' + context.baseEltID + ']'); } 279 }; 280 281 282 /** @function PreloaderDisplay() */ 283 /* Display/hide preloader */ 284 NGY2Tools.PreloaderDisplay = function(display) { 285 if( display === true ) { 286 this.$E.conLoadingB.removeClass('nanoGalleryLBarOff').addClass('nanoGalleryLBar'); 287 } 288 else { 289 this.$E.conLoadingB.removeClass('nanoGalleryLBar').addClass('nanoGalleryLBarOff'); 290 } 291 }; 292 293 //+ Jonas Raoni Soares Silva 294 //@ http://jsfromhell.com/array/shuffle [v1.0] 295 NGY2Tools.AreaShuffle = function (o) { 296 for (var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x); 297 return o; 298 }; 299 300 /** @function GetImageTitleFromURL() */ 301 /* retrieve filemane */ 302 NGY2Tools.GetImageTitleFromURL = function( imageURL ) { 303 if( this.O.thumbnailLabel.get('title') == '%filename' ) { 304 return (imageURL.split('/').pop()).replace('_',' '); 305 } 306 307 if( this.O.thumbnailLabel.get('title') == '%filenameNoExt' ) { 308 var s=imageURL.split('/').pop(); 309 return (s.split('.').shift()).replace('_',' '); 310 } 311 // return imageURL; 312 return ''; 313 }; 314 315 316 /** @function AlbumPostProcess() */ 317 /* post process one album based on plugin general parameters --> sorting/maxItems*/ 318 NGY2Tools.AlbumPostProcess = function(albumID) { 319 320 // this function can probably be optimized.... 321 322 var sortOrder = this.gallerySorting[this.GOM.curNavLevel]; 323 var maxItems = this.galleryMaxItems[this.GOM.curNavLevel]; 324 325 if( sortOrder != '' || maxItems > 0 ) { 326 327 // copy album's items to a new array 328 var currentAlbum = this.I.filter( function( obj ) { 329 return( obj.albumID == albumID && obj.kind != 'albumUp' ); 330 }); 331 332 // sorting options 333 switch( sortOrder ) { 334 case 'RANDOM': 335 currentAlbum = NGY2Tools.AreaShuffle(currentAlbum); 336 break; 337 case 'REVERSED': 338 currentAlbum = currentAlbum.reverse(); 339 break; 340 case 'TITLEASC': 341 currentAlbum.sort(function (a, b) { 342 return( (a.title.toUpperCase() < b.title.toUpperCase()) ? -1 : ((a.title.toUpperCase() > b.title.toUpperCase()) ? 1 : 0) ); 343 }); 344 break; 345 case 'TITLEDESC': 346 currentAlbum.sort(function (a, b) { 347 return( (a.title.toUpperCase() > b.title.toUpperCase()) ? -1 : ((a.title.toUpperCase() < b.title.toUpperCase()) ? 1 : 0) ); 348 }); 349 break; 350 } 351 352 // max Items 353 if( maxItems > 0 && currentAlbum.length > maxItems ) { 354 currentAlbum.splice(maxItems - 1, currentAlbum.length-maxItems ); 355 } 356 357 // remove the albums's items from the global items array 358 this.I.removeIf( function( obj ) { 359 return( obj.albumID == albumID && obj.kind != 'albumUp' ); 360 }); 361 362 // add the sorted items back to the album 363 this.I.push.apply(this.I, currentAlbum); 364 365 } 366 }; 367 368 return NGY2Tools; 369 })(); 370 371 // ==================== 372 // ===== NGY2Item ===== 373 // ==================== 374 window.NGY2Item = (function() { 375 var nextId = 1; // private static --> all instances 376 377 // constructor 378 function NGY2Item( itemID ) { 379 //window.NGY2Item = function( itemID ) { 380 var ID = 0; // private 381 382 // public (this instance only) 383 if( itemID === undefined || itemID === null ) { 384 ID = nextId++; 385 } 386 else { 387 ID = itemID; 388 } 389 this.GetID = function () { return ID; }; 390 391 // public 392 this.kind = ''; // 'image', 'album' or 'albumUp' 393 this.mediaKind = 'img'; // 'img', 'iframe' 394 this.mediaMarkup = ''; 395 this.G = null; // pointer to global instance 396 this.title = ''; // image title 397 this.description = ''; // image description 398 this.albumID = 0; // ID of the parent album 399 this.src = ''; // full sized image URL 400 this.width = 0; // image width 401 this.height = 0; // image height 402 this.destinationURL = ''; // thumbnail destination URL --> open URL instead of displaying image 403 this.downloadURL = ''; // thumbnail download URL --> specify the image for download button 404 this.author = ''; // image/album author 405 this.left= 0; // store position to animate from old to new 406 this.top= 0; 407 this.width= 0; // store size to avoid setting width/height if not required 408 this.height= 0; 409 this.resizedContentWidth= 0; // store size of content (image) to avoid setting width/height if not required 410 this.resizedContentHeight= 0; 411 this.thumbs = { // URLs and sizes for user defined 412 url: { l1: { xs: '', sm:'', me: '', la: '', xl: '' }, lN: { xs: '', sm: '', me: '', la:'', xl: '' } }, 413 width: { l1: { xs: 0, sm: 0, me: 0, la: 0 , xl: 0 }, lN: { xs: 0 , sm: 0, me: 0, la: 0, xl: 0 } }, 414 height: { l1: { xs: 0, sm: 0, me: 0, la: 0 , xl: 0 }, lN: { xs: 0, sm: 0, me: 0, la: 0, xl: 0 } } 415 }; 416 this.thumbnailImgRevealed = false; // thumbnail image already revealed 417 this.imageDominantColors = null; // base64 GIF 418 this.imageDominantColor = null; // HEX RGB 419 this.featured = false; // featured element 420 this.flickrThumbSizes = {}; // store URLs for all available thumbnail sizes (flickr) 421 this.picasaThumbs = null; // store URLs and sizes 422 this.hovered = false; // is the thumbnail currently hovered? 423 this.hoverInitDone = false; 424 this.contentIsLoaded = false; // album: are items already loaded? 425 this.contentLength = 0; // album: number of items (real number of items in memory) 426 this.numberItems = 0; // album: number of items (value returned by data source) 427 this.mediaNumber = 0; // media number in the album 428 this.imageCounter = 0; // number of images in an album 429 this.eltTransform = []; // store the CSS transformations 430 this.eltFilter = []; // store the CSS filters 431 this.eltEffect = []; // store data about hover effects animations 432 this.authkey = ''; // for Google Photos private (hidden) albums 433 this.paginationLastPage = 0; // for albums 434 this.paginationLastWidth = 0; // for albums 435 this.customData = {}; 436 this.selected = false; 437 this.imageWidth = 0; // image natural (real) width 438 this.imageHeight = 0; // image natural (real) height 439 this.$elt = null; // pointer to the corresponding DOM element 440 this.$Elts = []; // cached pointers to the thumbnail content -> to avoid jQuery().find() 441 this.tags = []; // list of tags of the current item 442 this.albumTagList = []; // list of all the tags of the items contained in the current album 443 this.albumTagListSel = []; // list of currently selected tags (only for albums) 444 this.exif = { exposure: '', flash: '', focallength: '', fstop: '', iso: '', model: '', time: '', location: ''}; 445 this.deleted = false; // item is deleted -> do not display anymore 446 this.rotationAngle = 0; // image display rotation angle 447 } 448 449 // public static 450 451 NGY2Item.Get = function( instance, ID ) { 452 var l = instance.I.length; 453 for( var i = 0; i < l; i++ ) { 454 if( instance.I[i].GetID() == ID ) { 455 return instance.I[i]; 456 } 457 } 458 return null; 459 }; 460 461 NGY2Item.GetIdx = function( instance, ID ) { 462 var l = instance.I.length; 463 for( var i = 0; i < l; i++ ) { 464 if( instance.I[i].GetID() == ID ) { 465 return i; 466 } 467 } 468 return -1; 469 }; 470 471 // create new item (image, album or albumUp) 472 NGY2Item.New = function( instance, title, description, ID, albumID, kind, tags ) { 473 var album = NGY2Item.Get( instance, albumID ); 474 475 if( albumID != -1 && albumID != 0 && title !='image gallery by nanogallery2 [build]' ) { 476 if( instance.O.thumbnailLevelUp && album.getContentLength(false) == 0 && instance.O.album == '' ) { 477 // add navigation thumbnail (album up) 478 var item = new NGY2Item('0'); 479 instance.I.push( item ); 480 album.contentLength += 1; 481 item.title = 'UP'; 482 item.albumID = albumID; 483 item.kind = 'albumUp'; 484 item.G = instance; 485 486 jQuery.extend( true, item.thumbs.width, instance.tn.defaultSize.width); 487 jQuery.extend( true, item.thumbs.height, instance.tn.defaultSize.height); 488 } 489 } 490 491 var item = NGY2Item.Get(instance, ID); 492 if( item === null ){ 493 // create a new item (otherwise, just update the existing one) 494 item = new NGY2Item(ID); 495 instance.I.push(item); 496 if( albumID != -1 && title !='image gallery by nanogallery2 [build]' ) { 497 album.contentLength+=1; 498 } 499 } 500 item.G = instance; 501 502 item.albumID = albumID; 503 item.kind = kind; 504 if( kind == 'image' ) { 505 album.imageCounter += 1; 506 item.mediaNumber = album.imageCounter; 507 } 508 509 // check keyword to find features images/albums 510 var kw = instance.O.thumbnailFeaturedKeyword; 511 if( kw != '' ) { 512 // check if item featured based on a keyword in the title or in the description 513 kw = kw.toUpperCase(); 514 var p = title.toUpperCase().indexOf(kw); 515 if( p > -1) { 516 item.featured = true; 517 // remove keyword case unsensitive 518 title = title.substring(0, p) + title.substring(p+kw.length, title.length); 519 } 520 p = description.toUpperCase().indexOf(kw); 521 if( p > -1) { 522 item.featured=true; 523 // remove keyword case unsensitive 524 description=description.substring(0, p) + description.substring(p + kw.length, description.length); 525 } 526 } 527 528 // TAGS 529 // if( instance.galleryFilterTags.Get() != false ) { 530 // if( instance.galleryFilterTags.Get() == true ) { 531 // if( tags != '' && tags != undefined ) { 532 // use set tags 533 // item.setTags(tags.split(' ')); 534 // } 535 // } 536 // else { 537 // extract tags starting with # (in title) 538 if( typeof instance.galleryFilterTags.Get() == 'string' ) { 539 switch( instance.galleryFilterTags.Get().toUpperCase() ) { 540 case 'TITLE': 541 var re = /(?:^|\W)#(\w+)(?!\w)/g, match, matches = []; 542 var tags = ""; 543 while (match = re.exec(title)) { 544 matches.push(match[1].replace(/^\s*|\s*$/, '')); //trim trailing/leading whitespace 545 } 546 item.setTags(matches); //tags; 547 title = title.split('#').join(''); //replaceall 548 break; 549 case 'DESCRIPTION': 550 var re = /(?:^|\W)#(\w+)(?!\w)/g, match, matches = []; 551 var tags = ""; 552 while (match = re.exec(description)) { 553 matches.push(match[1].replace(/^\s*|\s*$/, '')); //trim trailing/leading whitespace 554 } 555 item.setTags(matches); //tags; 556 description = description.split('#').join(''); //replaceall 557 break; 558 } 559 } 560 else { 561 if( tags != '' && tags != undefined ) { 562 // use set tags 563 item.setTags(tags.split(' ')); 564 } 565 } 566 // } 567 // } 568 569 // set (maybe modified) fields title and description 570 item.title = escapeHtml(instance, title); 571 item.description = escapeHtml(instance, description); 572 return item; 573 }; 574 575 576 // removes logically current item 577 NGY2Item.prototype.delete = function( ) { 578 this.deleted = true; 579 580 // update content length of parent album 581 this.G.I[NGY2Item.GetIdx(this.G, this.albumID)].contentLength--; 582 this.G.I[NGY2Item.GetIdx(this.G, this.albumID)].numberItems--; 583 584 // check if in DOM and removes it 585 var nbTn = this.G.GOM.items.length; 586 var ID = this.GetID(); 587 var foundIdx = -1; 588 var foundGOMidx = -1; 589 for( var i = 0; i < nbTn ; i++ ) { 590 var curTn = this.G.GOM.items[i]; 591 var item=this.G.I[curTn.thumbnailIdx]; 592 if( item.GetID() == ID ) { 593 // FOUND 594 if( !curTn.neverDisplayed ) { 595 foundIdx= curTn.thumbnailIdx; 596 foundGOMidx= i; 597 } 598 } 599 else { 600 if( foundIdx != -1 ) { 601 if( !curTn.neverDisplayed ) { 602 // update index value 603 item.$getElt('.nGY2GThumbnail').data('index', i-1); 604 item.$getElt('.nGY2GThumbnailImg').data('index', i-1); 605 } 606 } 607 } 608 } 609 if( foundIdx != -1 ) { 610 // delete item in GOM and delete thumbnail 611 var G = this.G; 612 if( this.selected == true ) { 613 this.selected = false; 614 G.GOM.nbSelected--; // update the global counter 615 } 616 if( G.I[foundIdx].$elt !== null ) { 617 G.I[foundIdx].$elt.remove(); // delete thumbnail DOM object 618 } 619 G.GOM.items.splice(foundGOMidx, 1); // delete in GOM 620 if( G.GOM.lastDisplayedIdx != -1 ) { 621 G.GOM.lastDisplayedIdx-=1; 622 } 623 } 624 } 625 626 NGY2Item.prototype.addToGOM = function( ) { 627 // retrieve index 628 var ID = this.GetID(); 629 var l = this.G.I.length; 630 for( var idx = 0; idx < l; idx++ ) { 631 var item = this.G.I[idx]; 632 if( item.GetID() == ID ) { 633 var w = item.thumbImg().width; 634 var h = item.thumbImg().height; 635 // set default size if required 636 if( h == 0 ) { 637 h = this.G.tn.defaultSize.getHeight(); 638 } 639 if( w == 0 ) { 640 w = this.G.tn.defaultSize.getWidth(); 641 } 642 // add to GOM -> will be displayed on next refresh/resize 643 var tn = new this.G.GOM.GTn(idx, w, h); 644 this.G.GOM.items.push(tn); 645 break; 646 } 647 } 648 649 } 650 651 652 // function to avoid XSS issue - Cross Site Scripting 653 // original: https://github.com/janl/mustache.js/blob/master/mustache.js#L55 654 var entityMap = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '/': '/', '`': '`', '=': '=' }; 655 function escapeHtml (instance, string) { 656 if( instance.O.allowHTMLinData == true ) { 657 return string; 658 } 659 else { 660 return String(string).replace(/[&<>"'`=\/]/g, function fromEntityMap (s) { 661 return entityMap[s]; 662 }); 663 } 664 } 665 666 667 NGY2Item.get_nextId = function () { 668 return nextId; 669 }; 670 671 //=== public (shared across instances) 672 673 //--- cached sub elements 674 NGY2Item.prototype.$getElt = function( elt, forceRefresh ) { 675 if( this.$elt == null ) { return null; } 676 if( this.$Elts[elt] !== undefined && !forceRefresh == true ) { 677 return this.$Elts[elt]; 678 } 679 else { 680 if( elt == '.nGY2GThumbnail' ) { 681 this.$Elts[elt]=this.$elt; 682 } 683 else { 684 this.$Elts[elt]=this.$elt.find(elt); 685 } 686 return this.$Elts[elt]; 687 } 688 }; 689 690 // remove one element (in DOM and in cache) 691 NGY2Item.prototype.removeElt = function( elt ) { 692 if( this.$elt == null ) { return; } 693 if( this.$Elts[elt] == undefined) { return; } 694 this.$Elts[elt].remove(); 695 var index = this.$Elts.indexOf(elt); 696 this.$Elts.splice(index, 1); 697 }; 698 699 //--- returns the album containing the item 700 NGY2Item.prototype.album = function() { 701 return this.G.I[NGY2Item.GetIdx(this.G, this.albumID)]; 702 }; 703 704 //--- viewer - transition can be disabled per media kind - returns true if current media supports transition (swipe) 705 NGY2Item.prototype.mediaTransition = function( ) { 706 if( this.G.O.viewerTransitionMediaKind.indexOf( this.mediaKind ) > -1 ) { 707 return true; 708 } 709 return false; 710 }; 711 712 //--- set one image (url and size) 713 NGY2Item.prototype.imageSet = function( src, w, h ) { 714 this.src = src; 715 this.width = w; 716 this.height = h; 717 }; 718 719 //--- set one thumbnail (url and size) - screenSize and level are optionnal 720 NGY2Item.prototype.thumbSet = function( src, w, h, screenSize, level ) { 721 var lst=['xs','sm','me','la','xl']; 722 if( typeof screenSize === 'undefined' || screenSize == '' || screenSize == null ) { 723 for( var i=0; i< lst.length; i++ ) { 724 if( typeof level === 'undefined' || level == '' ) { 725 this.thumbs.url.l1[lst[i]]=src; 726 this.thumbs.height.l1[lst[i]]=h; 727 this.thumbs.width.l1[lst[i]]=w; 728 this.thumbs.url.lN[lst[i]]=src; 729 this.thumbs.height.lN[lst[i]]=h; 730 this.thumbs.width.lN[lst[i]]=w; 731 } 732 else { 733 this.thumbs.url[level][lst[i]]=src; 734 this.thumbs.height[level][lst[i]]=h; 735 this.thumbs.width[level][lst[i]]=w; 736 } 737 } 738 } 739 else { 740 if( typeof level === 'undefined' || level == '' || level == null ) { 741 this.thumbs.url.l1[screenSize]=src; 742 this.thumbs.height.l1[screenSize]=h; 743 this.thumbs.width.l1[screenSize]=w; 744 this.thumbs.url.lN[screenSize]=src; 745 this.thumbs.height.lN[screenSize]=h; 746 this.thumbs.width.lN[screenSize]=w; 747 } 748 else { 749 this.thumbs.url[level][screenSize]=src; 750 this.thumbs.height[level][screenSize]=h; 751 this.thumbs.width[level][screenSize]=w; 752 } 753 } 754 755 var lst=['xs','sm','me','la','xl']; 756 for( var i=0; i< lst.length; i++ ) { 757 this.thumbs.height.l1[lst[i]]=h; 758 } 759 for( var i=0; i< lst.length; i++ ) { 760 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() ) { 761 this.thumbs.height.lN[lst[i]]=h; 762 } 763 } 764 }; 765 766 //--- set thumbnail image real height for current level/resolution, and for all others level/resolutions having the same settings 767 NGY2Item.prototype.thumbSetImgHeight = function( h ) { 768 var lst=['xs','sm','me','la','xl']; 769 for( var i=0; i< lst.length; i++ ) { 770 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() ) { 771 this.thumbs.height.l1[lst[i]]=h; 772 } 773 } 774 for( var i=0; i< lst.length; i++ ) { 775 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() ) { 776 this.thumbs.height.lN[lst[i]]=h; 777 } 778 } 779 }; 780 781 //--- set thumbnail image real width for current level/resolution, and for all others level/resolutions having the same settings 782 NGY2Item.prototype.thumbSetImgWidth = function( w ) { 783 var lst=['xs','sm','me','la','xl']; 784 for( var i=0; i< lst.length; i++ ) { 785 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() ) { 786 this.thumbs.width.l1[lst[i]]=w; 787 } 788 } 789 for( var i=0; i< lst.length; i++ ) { 790 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() ) { 791 this.thumbs.width.lN[lst[i]]=w; 792 } 793 } 794 }; 795 796 //--- Returns Thumbnail image (depending of the screen resolution) 797 NGY2Item.prototype.thumbImg = function () { 798 var tnImg = { src: '', width: 0, height: 0 }; 799 800 if( this.title == 'image gallery by nanogallery2 [build]' ) { 801 tnImg.src = this.G.emptyGif; 802 tnImg.url = this.G.emptyGif; 803 return tnImg; 804 } 805 tnImg.src = this.thumbs.url[this.G.GOM.curNavLevel][this.G.GOM.curWidth]; 806 tnImg.width = this.thumbs.width[this.G.GOM.curNavLevel][this.G.GOM.curWidth]; 807 tnImg.height = this.thumbs.height[this.G.GOM.curNavLevel][this.G.GOM.curWidth]; 808 return tnImg; 809 }; 810 811 //--- Set tags to items and add these tags to the album 812 NGY2Item.prototype.setTags = function( tags ) { 813 if( tags.length > 0 ) { 814 this.tags = tags; 815 var lstTags = this.album().albumTagList; 816 for( var i = 0; i < tags.length; i++ ) { 817 var tfound = false; 818 for( var j = 0; j < lstTags.length; j++ ) { 819 if( tags[i].toUpperCase() == lstTags[j].toUpperCase() ) { 820 tfound = true; 821 } 822 } 823 if( tfound == false) { 824 this.album().albumTagList.push(tags[i]) 825 this.album().albumTagListSel.push(tags[i]) 826 } 827 } 828 } 829 }; 830 831 //--- check if 1 of current item's tags is selected (tag filter) 832 NGY2Item.prototype.checkTagFilter = function() { 833 if( this.G.galleryFilterTags.Get() != false && this.album().albumTagList.length > 0 ) { 834 if( this.G.O.thumbnailLevelUp && this.kind=='albumUp' ) { 835 return true; 836 } 837 var found = false; 838 var lstTags = this.album().albumTagListSel; 839 for( var i = 0; i < this.tags.length; i++ ) { 840 for( var j = 0; j < lstTags.length; j++ ) { 841 if( this.tags[i].toUpperCase() == lstTags[j].toUpperCase() ) { 842 found = true; 843 break; 844 } 845 } 846 } 847 return found; 848 } 849 else 850 return true; 851 }; 852 853 //--- check if 1 of current item's tags is found using API search 854 NGY2Item.prototype.isSearchTagFound = function() { 855 if( this.G.GOM.albumSearchTags == '' ) { return true; } 856 if( this.G.O.thumbnailLevelUp && this.kind == 'albumUp' ) { return true; } 857 858 //var lstTags=this.album().albumTagListSel; 859 for( var i = 0; i < this.tags.length; i++ ) { 860 if( this.tags[i].toUpperCase().indexOf( this.G.GOM.albumSearchTags ) >= 0 ) { 861 return true; 862 } 863 } 864 return false; 865 }; 866 867 //--- set the URL of the media to display in the viewer 868 //--- markup is defined for images 869 NGY2Item.prototype.setMediaURL = function( url, mediaKind, description ) { 870 this.src = url; 871 this.mediaKind = mediaKind; 872 this.description = description; 873 if( mediaKind == 'img' ) { 874 this.mediaMarkup = '<img class="nGY2ViewerMedia" src="' + url + '" alt="' + description + '
vendor: 10,429 bytes, lines 874-1108
874" itemprop="contentURL" draggable="false">'; 875 } 876 }; 877 878 879 //--- check if current item should be displayed 880 NGY2Item.prototype.isToDisplay = function( albumID ) { 881 return this.albumID == albumID && this.checkTagFilter() && this.isSearchFound() && this.isSearchTagFound() && this.deleted == false; 882 }; 883 884 885 886 //--- returns the number of items of the current album 887 //--- count using tags filter 888 NGY2Item.prototype.getContentLength = function( filterTags ) { 889 if( filterTags == false || this.albumTagList.length == 0 || this.G.galleryFilterTags.Get() == false ) { 890 return this.contentLength; 891 } 892 else { 893 var l = this.G.I.length; 894 var cnt = 0; 895 var albumID = this.GetID(); 896 for( var idx = 0; idx < l; idx++ ) { 897 var item = this.G.I[idx]; 898 if( item.isToDisplay(albumID) ) { 899 cnt++; 900 } 901 } 902 return cnt; 903 } 904 }; 905 906 NGY2Item.prototype.isSearchFound = function() { 907 if( this.G.GOM.albumSearch != '' ) { 908 if( this.title.toUpperCase().indexOf( this.G.GOM.albumSearch ) == -1 ) { 909 return false; 910 } 911 } 912 return true; 913 } 914 915 916 //--- for future use... 917 NGY2Item.prototype.responsiveURL = function () { 918 var url = ''; 919 switch(this.G.O.kind) { 920 case '': 921 url = this.src; 922 break; 923 case 'flickr': 924 url = this.src; 925 break; 926 case 'picasa': 927 case 'google': 928 case 'google2': 929 default: 930 url = this.src; 931 break; 932 } 933 return url; 934 }; 935 936 937 //--- Reveal the thumbnail image with animation on opacity 938 NGY2Item.prototype.ThumbnailImageReveal = function () { 939 940 if( this.thumbnailImgRevealed == false ) { 941 this.thumbnailImgRevealed = true; 942 new NGTweenable().tween({ 943 from: { opacity: 0 }, 944 to: { opacity: 1 }, 945 attachment: { item: this }, 946 delay: 30, 947 duration: 400, 948 easing: 'easeOutQuart', 949 step: function (state, att) { 950 var $e=att.item.$getElt('.nGY2TnImg'); 951 if( $e != null ) { 952 $e.css( state ); 953 } 954 } 955 }); 956 } 957 }; 958 959 960 // In case of thumbnails with stacks - apply a percent to a value which include a unit 961 function ValueApplyPercent( str, percent ) { 962 str=String(str); 963 if( str === '0' || percent == 1 ) { return str; } 964 var n = Number(str.replace(/[a-zA-Z]/g, '')); 965 var ar = str.match(/([^\-0-9\.]+)/g); 966 var a = ''; 967 if( ar != null && ar.length > 0 ) { 968 a = ar.join(); 969 } 970 971 if( isNaN(n) || n == 0 ) { 972 return str; 973 } 974 975 n = n * percent; 976 return n + a; 977 } 978 979 //--- 2D/3D CSS transform - apply the cached value to element 980 NGY2Item.prototype.CSSTransformApply = function ( eltClass ) { 981 var obj = this.eltTransform[eltClass]; 982 983 if( eltClass == '.nGY2GThumbnail' ) { 984 // thumbnail 985 var nbStacks = obj.$elt.length-1; 986 var pTranslateX = 1; 987 var pTranslateY = 1; 988 var pTranslateZ = 1; 989 var pTranslate = 1; 990 var pRotateX = 1; 991 var pRotateY = 1; 992 var pRotateZ = 1; 993 var pRotate = 1; 994 var pScale = 1; 995 for( var n = nbStacks; n >= 0; n-- ) { 996 // units must be given with 997 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) + ')'; 998 if( !(this.G.IE <= 9) && !this.G.isGingerbread ) { 999 v += ' rotateX(' + ValueApplyPercent(obj.rotateX,pRotateX) + ') rotateY(' + ValueApplyPercent(obj.rotateY,pRotateY) + ') rotateZ(' + ValueApplyPercent(obj.rotateZ,pRotateZ) + ') rotate(' + ValueApplyPercent(obj.rotate,pRotate) + ')'; 1000 } 1001 else { 1002 v += ' rotate(' + ValueApplyPercent(obj.rotateZ,pRotateZ) + ')'; 1003 } 1004 obj.$elt[n].style[this.G.CSStransformName] = v; 1005 1006 if( nbStacks > 0 ) { 1007 // apply a percent to the stack elements 1008 pTranslateX -= this.G.tn.opt.Get('stacksTranslateX'); 1009 pTranslateY -= this.G.tn.opt.Get('stacksTranslateY'); 1010 pTranslateZ -= this.G.tn.opt.Get('stacksTranslateZ'); 1011 pRotateX -= this.G.tn.opt.Get('stacksRotateX'); 1012 pRotateY -= this.G.tn.opt.Get('stacksRotateY'); 1013 pRotateZ -= this.G.tn.opt.Get('stacksRotateZ'); 1014 pScale -= this.G.tn.opt.Get('stacksScale'); 1015 } 1016 } 1017 } 1018 else { 1019 // thumbnail sub element 1020 if( obj.$elt != null ) { 1021 for( var n = 0; n < obj.$elt.length; n++ ) { 1022 if( obj.$elt[n] != undefined ) { 1023 // units must be given with 1024 var v = 'translateX(' + obj.translateX + ') translateY(' + obj.translateY + ') translateZ(' + obj.translateZ + ') scale(' + obj.scale + ') translate(' + obj.translate + ')'; 1025 if( !(this.G.IE <= 9) && !this.G.isGingerbread ) { 1026 v += ' rotateX(' + obj.rotateX + ') rotateY(' + obj.rotateY + ') rotateZ(' + obj.rotateZ + ') rotate(' + obj.rotate + ')'; 1027 } 1028 else { 1029 v += ' rotate(' + obj.rotateZ + ')'; 1030 } 1031 obj.$elt[n].style[this.G.CSStransformName] = v; 1032 } 1033 } 1034 } 1035 } 1036 }; 1037 1038 //--- 2D/3D CSS transform - set a value in cache 1039 NGY2Item.prototype.CSSTransformSet = function ( eltClass, transform, value, forceRefresh ) { 1040 if( this.eltTransform[eltClass] == undefined ) { 1041 this.eltTransform[eltClass] = { translateX: 0, translateY: 0, translateZ: 0, rotateX: 0, rotateY: 0, rotateZ: 0, scale: 1, translate: '0px,0px', rotate: 0 }; 1042 this.eltTransform[eltClass].$elt = this.$getElt(eltClass); 1043 } 1044 this.eltTransform[eltClass][transform] = value; 1045 if( forceRefresh === true ) { 1046 this.eltTransform[eltClass].$elt = this.$getElt(eltClass, true); 1047 } 1048 }; 1049 1050 //--- CSS Filters - apply the cached value to element 1051 NGY2Item.prototype.CSSFilterApply = function ( eltClass ) { 1052 var obj = this.eltFilter[eltClass]; 1053 var v = 'blur(' + obj.blur + ') brightness(' + obj.brightness + ') grayscale(' + obj.grayscale + ') sepia(' + obj.sepia + ') contrast(' + obj.contrast + ') opacity(' + obj.opacity + ') saturate(' + obj.saturate + ')'; 1054 if( obj.$elt != null ) { 1055 for( var n = 0; n < obj.$elt.length; n++ ) { 1056 if( obj.$elt[n] != undefined ) { 1057 obj.$elt[n].style.WebkitFilter = v; 1058 obj.$elt[n].style.filter = v; 1059 } 1060 } 1061 } 1062 }; 1063 1064 //--- CSS Filters - set a value in cache 1065 NGY2Item.prototype.CSSFilterSet = function ( eltClass, filter, value, forceRefresh ) { 1066 if( this.eltFilter[eltClass] == undefined ) { 1067 this.eltFilter[eltClass] = { blur: 0, brightness: '100%', grayscale: '0%', sepia: '0%', contrast: '100%', opacity: '100%', saturate: '100%' }; 1068 this.eltFilter[eltClass].$elt = this.$getElt(eltClass); 1069 } 1070 this.eltFilter[eltClass][filter] = value; 1071 if( forceRefresh === true ) { 1072 this.eltTransform[eltClass].$elt = this.$getElt(eltClass, true); 1073 } 1074 }; 1075 1076 //--- thumbnail hover animation 1077 NGY2Item.prototype.animate = function ( effect, delay, hoverIn ) { 1078 if( this.$getElt() == null ) { return; } 1079 1080 var context = {}; 1081 context.G = this.G; 1082 context.item = this; 1083 context.effect = effect; 1084 context.hoverIn = hoverIn; 1085 context.cssKind = ''; 1086 if( hoverIn ) { 1087 // HOVER IN 1088 1089 if( this.eltEffect[effect.element] == undefined ) { 1090 this.eltEffect[effect.element] = []; 1091 } 1092 if( this.eltEffect[effect.element][effect.type] == undefined ) { 1093 this.eltEffect[effect.element][effect.type] = { initialValue: 0, lastValue: 0 }; 1094 } 1095 if( effect.firstKeyframe ) { 1096 // store initial and current value -> for use in the back animation 1097 this.eltEffect[effect.element][effect.type] = { initialValue: effect.from, lastValue: effect.from}; 1098 } 1099 1100 context.animeFrom = effect.from; 1101 context.animeTo = effect.to; 1102 context.animeDuration = parseInt(effect.duration); 1103 context.animeDelay = 30 + parseInt(effect.delay + delay); // 30ms is a default delay to avoid conflict with other initializations 1104 context.animeEasing = effect.easing; 1105 } 1106 else { 1107 // HOVER OUT 1108
vendor: 8,341 bytes, lines 1108-1310
1108if( effect.firstKeyframe ) { 1109 context.animeFrom = this.eltEffect[effect.element][effect.type].lastValue; 1110 context.animeTo = this.eltEffect[effect.element][effect.type].initialValue; 1111 // context.animeTo=effect.from; 1112 } 1113 else { 1114 // context.animeFrom=effect.from; 1115 context.animeFrom = this.eltEffect[effect.element][effect.type].lastValue; 1116 context.animeTo = this.eltEffect[effect.element][effect.type].initialValue; 1117 // context.animeTo=effect.to; 1118 1119 } 1120 1121 context.animeDuration = parseInt(effect.durationBack); 1122 context.animeDelay = 30 + parseInt(effect.delayBack + delay); // 30ms is a default delay to avoid conflict with other initializations 1123 context.animeEasing = effect.easingBack; 1124 } 1125 1126 1127 // detect if animation on CSS transform 1128 var transform=['translateX', 'translateY', 'translateZ', 'scale', 'rotateX', 'rotateY', 'rotateZ']; 1129 for( var i = 0; i < transform.length; i++ ) { 1130 if( effect.type == transform[i] ) { 1131 context.cssKind = 'transform'; 1132 break; 1133 } 1134 } 1135 1136 // detect if animation on CSS filter 1137 var filter=['blur', 'brightness', 'grayscale', 'sepia', 'contrast', 'opacity', 'saturate']; 1138 for( var i = 0; i < filter.length; i++ ) { 1139 if( effect.type == filter[i] ) { 1140 context.cssKind = 'filter'; 1141 break; 1142 } 1143 } 1144 // handle some special cases 1145 if( hoverIn && effect.element == '.nGY2GThumbnail' && ( effect.type == 'scale' || effect.type == 'rotateX') ) { 1146 this.G.GOM.lastZIndex++; 1147 this.$getElt(effect.element).css('z-index', this.G.GOM.lastZIndex); 1148 // setElementOnTop(this.G.$E.base, this.$getElt(effect.element) ); 1149 } 1150 1151 // animation 1152 var tweenable = new NGTweenable(); 1153 context.tweenable=tweenable; 1154 tweenable.tween({ 1155 attachment: context, 1156 from: { 'v': context.animeFrom }, 1157 to: { 'v': context.animeTo }, 1158 duration: context.animeDuration, //parseInt(effect.duration), 1159 delay: context.animeDelay, //parseInt(effect.delay), 1160 easing: context.animeEasing, //'easeOutQuart', 1161 1162 step: function (state, att) { 1163 if( att.item.$getElt() == null ) { 1164 // the thumbnail may be destroyed since the start of the animation 1165 att.tweenable.stop(false); 1166 // att.tweenable.dispose(); 1167 return; 1168 } 1169 if( att.hoverIn && !att.item.hovered ) { 1170 // thumbnail no more hovered 1171 att.tweenable.stop(false); 1172 // att.tweenable.dispose(); 1173 return; 1174 } 1175 1176 if( att.G.VOM.viewerDisplayed ) { 1177 att.tweenable.stop(false); 1178 // att.tweenable.dispose(); 1179 return; 1180 } 1181 1182 // test if in delay phase 1183 if( state.v == att.animeFrom ) { return; } 1184 1185 switch( att.cssKind ) { 1186 case 'transform': 1187 // window.ng_draf( function() { 1188 att.item.CSSTransformSet(att.effect.element, att.effect.type, state.v); 1189 att.item.CSSTransformApply( att.effect.element ); 1190 // }); 1191 break; 1192 case 'filter': 1193 // window.ng_draf( function() { 1194 att.item.CSSFilterSet(att.effect.element, att.effect.type, state.v); 1195 att.item.CSSFilterApply( att.effect.element ); 1196 // }); 1197 break; 1198 default: 1199 var v=state.v; 1200 if( state.v.substring(0,4) == 'rgb(' || state.v.substring(0,5) == 'rgba(' ) { 1201 // to remove values after the dot (not supported by RGB/RGBA) 1202 // v=ngtinycolor(state.v).toRgbString(); 1203 v = ShadeBlendConvert(0, v); 1204 } 1205 // window.ng_draf( function() { 1206 att.item.$getElt( att.effect.element ).css( att.effect.type, v ); 1207 // }); 1208 break; 1209 } 1210 if( hoverIn ) { 1211 // store value for back animation 1212 att.item.eltEffect[att.effect.element][att.effect.type].lastValue = state.v; 1213 } 1214 }, 1215 1216 finish: function (state, att) { 1217 if( hoverIn ) { 1218 // store value for back animation 1219 att.item.eltEffect[att.effect.element][att.effect.type].lastValue = state.v; 1220 } 1221 1222 if( att.item.$getElt() == null ) { 1223 // the thumbnail may be destroyed since the start of the animation 1224 return; 1225 } 1226 if( att.hoverIn && !att.item.hovered ) { 1227 // thumbnail no more hovered 1228 return; 1229 } 1230 1231 if( att.G.VOM.viewerDisplayed ) { 1232 return; 1233 } 1234 1235 // window.ng_draf( function() { 1236 switch( att.cssKind ) { 1237 case 'transform': 1238 att.item.CSSTransformSet(att.effect.element, att.effect.type, att.animeTo); 1239 att.item.CSSTransformApply(att.effect.element); 1240 break; 1241 case 'filter': 1242 att.item.CSSFilterSet(att.effect.element, att.effect.type, att.animeTo); 1243 att.item.CSSFilterApply(att.effect.element); 1244 break; 1245 default: 1246 att.item.$getElt(att.effect.element).css(att.effect.type, att.animeTo); 1247 break; 1248 } 1249 // }); 1250 } 1251 }); 1252 }; 1253 1254 return NGY2Item; 1255 })(); 1256 1257 } 1258 1259 _this.options = jQuery.extend(true, {}, jQuery.nanogallery2.defaultOptions, options); 1260 // Initialization code 1261 _this.nG2 = null; 1262 _this.nG2 = new nanoGALLERY2(); 1263 _this.nG2.initiateGallery2(_this.e, _this.options ); 1264 1265 }; 1266 1267 // PUBLIC EXPOSED METHODS 1268 _this.test = function() { 1269 //alert('test'); 1270 // console.dir(_this.nG.G.I.length); 1271 // console.dir(_this.nG); 1272 //privateTest(); 1273 } 1274 1275 1276 // Run initializer 1277 _this.init(); 1278 }; 1279 1280 jQuery.nanogallery2.defaultOptions = { 1281 kind : '', 1282 userID : '', 1283 photoset : '', 1284 album: '', 1285 blackList : 'scrapbook|profil|auto backup', 1286 whiteList : '', 1287 albumList : '', 1288 albumList2 : null, 1289 RTL : false, 1290 poogleplusUseUrlCrossDomain : true, 1291 flickrSkipOriginal : true, 1292 breadcrumbAutoHideTopLevel : true, 1293 displayBreadcrumb : true, 1294 breadcrumbOnlyCurrentLevel : true, 1295 breadcrumbHideIcons : true, 1296 theme : 'nGY2', 1297 galleryTheme : 'dark', 1298 viewerTheme : 'dark', 1299 items : null, 1300 itemsBaseURL : '', 1301 thumbnailSelectable : false, 1302 dataProvider: '', 1303 dataCharset: 'Latin', 1304 allowHTMLinData: false, 1305 locationHash : true, 1306 slideshowDelay : 3000, 1307 slideshowAutoStart : false, 1308 1309 debugMode: false, 1310
1311 galleryDisplayMoreStep : 2, 1312 galleryDisplayMode : 'fullContent', 1313 galleryL1DisplayMode : null, 1314 galleryPaginationMode : 'rectangles', // 'dots', 'rectangles', 'numbers' 1315 // galleryThumbnailsDisplayDelay : 2000, 1316 galleryMaxRows : 2, 1317 galleryL1MaxRows : null, 1318 galleryLastRowFull: false, 1319 galleryLayoutEngine : 'default', 1320 paginationSwipe: true, 1321 paginationVisiblePages : 10, 1322 // paginationSwipeSensibilityVert : 10, 1323 galleryFilterTags : false, // possible values: false, true, 'title', 'description' 1324 galleryL1FilterTags : null, // possible values: false, true, 'title', 'description' 1325 galleryMaxItems : 0, // maximum number of items per album --> only flickr, google2, nano_photos_provider2 1326 galleryL1MaxItems : null, // maximum number of items per gallery page --> only flickr, google2, nano_photos_provider2 1327 gallerySorting : '', 1328 galleryL1Sorting : null, 1329 galleryDisplayTransition : 'none', 1330 galleryL1DisplayTransition : null, 1331 galleryDisplayTransitionDuration : 1000, 1332 galleryL1DisplayTransitionDuration : null, 1333 galleryResizeAnimation : true, 1334 galleryRenderDelay : 60, 1335 1336 thumbnailCrop : true, 1337 thumbnailL1Crop : null, 1338 thumbnailCropScaleFactor : 1.5, 1339 thumbnailLevelUp : false, 1340 thumbnailAlignment : 'fillWidth', 1341 thumbnailWidth : 300, 1342 thumbnailL1Width : null, 1343 thumbnailHeight : 200, 1344 thumbnailL1Height : null, 1345 thumbnailBaseGridHeight : 0, 1346 thumbnailL1BaseGridHeight : null, 1347 thumbnailGutterWidth : 2, 1348 thumbnailL1GutterWidth : null, 1349 thumbnailGutterHeight : 2, 1350 thumbnailL1GutterHeight : null, 1351 thumbnailBorderVertical : 2, 1352 thumbnailBorderHorizontal : 2, 1353 thumbnailFeaturedKeyword : '*featured', 1354 thumbnailAlbumDisplayImage : false, 1355 thumbnailHoverEffect2 : 'toolsAppear', 1356 thumbnailBuildInit2 : '', 1357 thumbnailStacks : 0, 1358 thumbnailL1Stacks : null, 1359 thumbnailStacksTranslateX : 0, 1360 thumbnailL1StacksTranslateX : null, 1361 thumbnailStacksTranslateY : 0, 1362 thumbnailL1StacksTranslateY : null, 1363 thumbnailStacksTranslateZ : 0, 1364 thumbnailL1StacksTranslateZ : null, 1365 thumbnailStacksRotateX : 0, 1366 thumbnailL1StacksRotateX : null, 1367 thumbnailStacksRotateY : 0, 1368 thumbnailL1StacksRotateY : null, 1369 thumbnailStacksRotateZ : 0, 1370 thumbnailL1StacksRotateZ : null, 1371 thumbnailStacksScale : 0, 1372 thumbnailL1StacksScale : null, 1373 thumbnailDisplayOutsideScreen: true, 1374 thumbnailWaitImageLoaded: true, 1375 thumbnailSliderDelay: 2000, 1376 galleryBuildInit2 : '', 1377 portable : false, 1378 eventsDebounceDelay: 30, 1379 1380 touchAnimation : true, 1381 touchAnimationL1 : undefined, 1382 touchAutoOpenDelay : 0, 1383 1384 thumbnailLabel : { 1385 position : 'overImageOnBottom', 1386 align: 'center', 1387 display : true, 1388 displayDescription : false, 1389 titleMaxLength : 0, 1390 titleMultiLine : false, 1391 descriptionMaxLength : 0, 1392 descriptionMultiLine : false, 1393 hideIcons : true, 1394 title : '' 1395 }, 1396 1397 thumbnailToolbarImage : { topLeft: 'select', topRight : 'featured' }, 1398 thumbnailToolbarAlbum : { topLeft: 'select', topRight : 'counter' }, 1399 thumbnailDisplayInterval : 15, 1400 thumbnailL1DisplayInterval : null, 1401 thumbnailDisplayTransition : 'fadeIn', 1402 thumbnailL1DisplayTransition : null, 1403 thumbnailDisplayTransitionDuration: 240, 1404 thumbnailL1DisplayTransitionDuration: null, 1405 thumbnailOpenImage : true, 1406 thumbnailOpenOriginal : false, 1407 thumbnailGlobalImageTitle : '', 1408 thumbnailGlobalAlbumTitle : '', 1409 1410 viewer : 'internal', 1411 viewerFullscreen: false, 1412 viewerDisplayLogo : false, 1413 imageTransition : 'swipe2', 1414 viewerTransitionMediaKind : 'img', 1415 viewerZoom : true, 1416 viewerImageDisplay : '', 1417 openOnStart : '', 1418 viewerHideToolsDelay : 3000, 1419 viewerToolbar : { 1420 display : true, 1421 position : 'bottomOverImage', 1422 fullWidth : true, 1423 align : 'center', 1424 autoMinimize : 0, 1425 standard : 'minimizeButton,label', 1426 minimized : 'minimizeButton,label,infoButton,shareButton,downloadButton,linkOriginalButton,fullscreenButton' 1427 }, 1428 viewerTools : {
1429 topLeft : 'pageCounter,playPauseButton', 1430 topRight : 'rotateLeft,rotateRight,fullscreenButton,closeButton' 1431 }, 1432 1433 breakpointSizeSM : 480, 1434 breakpointSizeME : 992, 1435 breakpointSizeLA : 1200, 1436 breakpointSizeXL : 1800, 1437 1438 fnThumbnailInit : null, 1439 fnThumbnailHoverInit : null, 1440 fnThumbnailHover : null, 1441 fnThumbnailHoverOut : null, 1442 fnThumbnailDisplayEffect : null, 1443 fnViewerInfo : null, 1444 fnImgToolbarCustInit : null, 1445 fnImgToolbarCustDisplay : null, 1446 fnImgToolbarCustClick : null, 1447 fnProcessData : null, 1448 fnThumbnailSelection : null, 1449 fnGalleryRenderStart : null, 1450 fnGalleryRenderEnd : null, 1451 fnGalleryObjectModelBuilt : null, 1452 fnGalleryLayoutApplied : null, 1453 fnThumbnailClicked : null, 1454 fnShoppingCartUpdated : null, 1455 fnThumbnailToolCustAction : null, 1456 fnThumbnailOpen : null, 1457 fnImgDisplayed : null, 1458 1459 i18n : { 1460 'breadcrumbHome' : 'Galleries', 'breadcrumbHome_FR' : 'Galeries', 1461 'thumbnailImageTitle' : '', 'thumbnailAlbumTitle' : '', 1462 'thumbnailImageDescription' : '', 'thumbnailAlbumDescription' : '', 1463 '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' 1464 }, 1465 icons : { 1466 // sample for font awesome: <i style="color:#eee;" class="fa fa-search-plus"></i> 1467 thumbnailAlbum: '<i class="nGY2Icon-folder-empty"></i>', 1468 thumbnailImage: '<i class="nGY2Icon-picture"></i>', 1469 breadcrumbAlbum: '<i class="nGY2Icon-folder-empty"></i>', 1470 breadcrumbHome: '<i class="nGY2Icon-home"></i>', 1471 breadcrumbSeparator: '<i class="nGY2Icon-left-open"></i>', 1472 breadcrumbSeparatorRtl: '<i class="nGY2Icon-right-open"></i>', 1473 navigationFilterSelected: '<i style="color:#fff;" class="nGY2Icon-toggle-on"></i>', 1474 navigationFilterUnselected: '<i style="color:#ddd;" class="nGY2Icon-toggle-off"></i>', 1475 navigationFilterSelectedAll: '<i class="nGY2Icon-toggle-on"></i><i class="nGY2Icon-ok"></i>', 1476 thumbnailSelected: '<i style="color:#bff;" class="nGY2Icon-ok-circled"></i>', 1477 thumbnailUnselected: '<i style="color:#bff;" class="nGY2Icon-circle-empty"></i>', 1478 thumbnailFeatured: '<i style="color:#dd5;" class="nGY2Icon-star"></i>', 1479 thumbnailCounter: '<i class="nGY2Icon-picture"></i>', 1480 thumbnailShare: '<i class="nGY2Icon-ngy2_share2"></i>', 1481 thumbnailDownload: '<i class="nGY2Icon-ngy2_download2"></i>', 1482 thumbnailInfo: '<i class="nGY2Icon-ngy2_info2"></i>', 1483 thumbnailCart: '<i class="nGY2Icon-basket"></i>', 1484 thumbnailDisplay: '<i class="nGY2Icon-ngy2_zoom_in2"></i>', 1485 thumbnailCustomTool1: 'T1', 1486 thumbnailCustomTool2: 'T2', 1487 thumbnailCustomTool3: 'T3', 1488 thumbnailCustomTool4: 'T4', 1489 thumbnailCustomTool5: 'T5', 1490 thumbnailCustomTool6: 'T6', 1491 thumbnailCustomTool7: 'T7', 1492 thumbnailCustomTool8: 'T8', 1493 thumbnailCustomTool9: 'T9', 1494 thumbnailCustomTool10: 'T10', 1495 thumbnailAlbumUp: '<i style="font-size: 3em;" class="nGY2Icon-ngy2_chevron_up2"></i>', 1496 paginationNext: '<i class="nGY2Icon-right-open"></i>', 1497 paginationPrevious: '<i class="nGY2Icon-left-open"></i>', 1498 galleryMoreButton: '<i class="nGY2Icon-picture"></i> <i class="nGY2Icon-right-open"></i>', 1499 buttonClose: '<i class="nGY2Icon-ngy2_close2"></i>', 1500 viewerPrevious: '<i class="nGY2Icon-ngy2_chevron-left"></i>', 1501 viewerNext: '<i class="nGY2Icon-ngy2_chevron-right"></i>', 1502 viewerImgPrevious: '<i class="nGY2Icon-ngy2_chevron_left3"></i>', 1503 viewerImgNext: '<i class="nGY2Icon-ngy2_chevron_right3"></i>', 1504 viewerDownload: '<i class="nGY2Icon-ngy2_download2"></i>', 1505 viewerToolbarMin: '<i class="nGY2Icon-ellipsis-vert"></i>', 1506 viewerToolbarStd: '<i class="nGY2Icon-menu"></i>', 1507 viewerPlay: '<i class="nGY2Icon-play"></i>', 1508 viewerPause: '<i class="nGY2Icon-pause"></i>', 1509 viewerFullscreenOn: '<i class="nGY2Icon-resize-full"></i>', 1510 viewerFullscreenOff: '<i class="nGY2Icon-resize-small"></i>', 1511 viewerZoomIn: '<i class="nGY2Icon-ngy2_zoom_in2"></i>', 1512 viewerZoomOut: '<i class="nGY2Icon-ngy2_zoom_out2"></i>', 1513 viewerLinkOriginal: '<i class="nGY2Icon-ngy2_external2"></i>', 1514 viewerInfo: '<i class="nGY2Icon-ngy2_info2"></i>',
vendor: 43,049 bytes, lines 1515-2619
1515 viewerShare: '<i class="nGY2Icon-ngy2_share2"></i>', 1516 viewerRotateLeft: '<i class="nGY2Icon-ccw"></i>', 1517 viewerRotateRight: '<i class="nGY2Icon-cw"></i>', 1518 user: '<i class="nGY2Icon-user"></i>', 1519 location: '<i class="nGY2Icon-location"></i>', 1520 config: '<i class="nGY2Icon-wrench"></i>', 1521 shareFacebook: '<i style="color:#3b5998;" class="nGY2Icon-facebook-squared"></i>', 1522 shareTwitter: '<i style="color:#00aced;" class="nGY2Icon-twitter-squared"></i>', 1523 // shareGooglePlus: '<i style="color:#dd4b39;" class="nGY2Icon-gplus-squared"></i>', 1524 shareTumblr: '<i style="color:#32506d;" class="nGY2Icon-tumblr-squared"></i>', 1525 sharePinterest: '<i style="color:#cb2027;" class="nGY2Icon-pinterest-squared"></i>', 1526 shareVK: '<i style="color:#3b5998;" class="nGY2Icon-vkontakte"></i>', 1527 shareMail: '<i style="color:#555;" class="nGY2Icon-mail-alt"></i>', 1528 viewerCustomTool1: 'T1', 1529 viewerCustomTool2: 'T2', 1530 viewerCustomTool3: 'T3', 1531 viewerCustomTool4: 'T4', 1532 viewerCustomTool5: 'T5', 1533 viewerCustomTool6: 'T6', 1534 viewerCustomTool7: 'T7', 1535 viewerCustomTool8: 'T8', 1536 viewerCustomTool9: 'T9', 1537 viewerCustomTool10: 'T10' 1538 } 1539 }; 1540 1541 jQuery.fn.nanogallery2 = function (args, option, value) { 1542 1543 if( typeof jQuery(this).data('nanogallery2data') === 'undefined'){ 1544 if( args == 'destroy' ) { 1545 // command to destroy but no instance yet --> exit 1546 return; 1547 } 1548 1549 return this.each( function(){ 1550 (new jQuery.nanogallery2(this, args)); 1551 }); 1552 } 1553 else { 1554 // no options --> 1555 // This function breaks the chain, but provides some API methods 1556 1557 var nG2=$(this).data('nanogallery2data').nG2; 1558 switch(args){ 1559 case 'displayItem': 1560 nG2.DisplayItem(option); 1561 break; 1562 1563 case 'search': 1564 return( nG2.Search(option)); 1565 break; 1566 1567 case 'search2': 1568 return nG2.Search2(option, value); 1569 break; 1570 1571 case 'search2Execute': 1572 return nG2.Search2Execute(); 1573 break; 1574 1575 case 'refresh': 1576 nG2.Refresh(); 1577 break; 1578 1579 case 'resize': 1580 nG2.Resize(); 1581 break; 1582 1583 case 'instance': 1584 return nG2; 1585 break; 1586 1587 case 'data': 1588 nG2.data= { 1589 items: nG2.I, 1590 gallery: nG2.GOM, 1591 lightbox: nG2.VOM 1592 }; 1593 return nG2.data; 1594 break; 1595 1596 case 'reload': 1597 nG2.ReloadAlbum(); 1598 return $(this); 1599 break; 1600 1601 case 'itemsSelectedGet': 1602 return nG2.ItemsSelectedGet(); 1603 break; 1604 1605 case 'itemsSetSelectedValue': 1606 nG2.ItemsSetSelectedValue(option, value); 1607 break; 1608 1609 case 'option': 1610 if(typeof value === 'undefined'){ 1611 return nG2.Get(option); 1612 }else{ 1613 nG2.Set(option,value); 1614 if( option == 'demoViewportWidth' ) { 1615 // force resize event -> for demo purposes 1616 $(window).trigger('resize'); 1617 } 1618 } 1619 break; 1620 1621 case 'destroy': 1622 nG2.Destroy(); 1623 $(this).removeData('nanogallery2data'); 1624 break; 1625 1626 case 'shoppingCartGet': 1627 return nG2.shoppingCart; 1628 break; 1629 1630 case 'shoppingCartUpdate': 1631 if( typeof value === 'undefined' || typeof option === 'undefined' ){ 1632 return false; 1633 } 1634 var ID = option; 1635 var cnt = value; 1636 for( var i = 0; i < nG2.shoppingCart.length; i++) { 1637 if( nG2.shoppingCart[i].ID = ID ) { 1638 nG2.shoppingCart[i].cnt = cnt; 1639 } 1640 } 1641 var fu = G.O.fnShoppingCartUpdated; 1642 if( fu !== null ) { 1643 typeof fu == 'function' ? fu(nG2.shoppingCart, NGY2Item.Get(G, ID)) : window[fu](nG2.shoppingCart, NGY2Item.Get(G, ID)); 1644 } 1645 return nG2.shoppingCart; 1646 break; 1647 1648 case 'shoppingCartRemove': 1649 if( typeof option === 'undefined' ){ 1650 return false; 1651 } 1652 var ID=option; 1653 for( var i=0; i<nG2.shoppingCart.length; i++) { 1654 if( nG2.shoppingCart[i].ID=ID ) { 1655 nG2.shoppingCart.splice(i,1); 1656 break; 1657 } 1658 } 1659 var fu = G.O.fnShoppingCartUpdated; 1660 if( fu !== null ) { 1661 typeof fu == 'function' ? fu(nG2.shoppingCart, NGY2Item.Get(G, ID)) : window[fu](nG2.shoppingCart, NGY2Item.Get(G, ID)); 1662 } 1663 return nG2.shoppingCart; 1664 break; 1665 1666 case 'closeViewer': 1667 nG2.CloseViewer(); 1668 break; 1669 case 'minimizeToolbar': 1670 nG2.MinimizeToolbar(); 1671 break; 1672 case 'maximizeToolbar': 1673 nG2.MaximizeToolbar(); 1674 break; 1675 case 'paginationPreviousPage': 1676 nG2.PaginationPreviousPage(); 1677 break; 1678 case 'paginationNextPage': 1679 nG2.paginationNextPage(); 1680 break; 1681 case 'paginationGotoPage': 1682 nG2.PaginationGotoPage( option ); 1683 break; 1684 case 'paginationCountPages': 1685 nG2.PaginationCountPages(); 1686 break; 1687 1688 } 1689 return $(this); 1690 1691 } 1692 }; 1693 1694 1695 // ############################### 1696 // ##### nanogallery2 script ##### 1697 // ############################### 1698 1699 /** @function nanoGALLERY2 */ 1700 function nanoGALLERY2() { 1701 "use strict"; 1702 1703 /** 1704 * Force reload the current album, if provided by Json 1705 */ 1706 this.ReloadAlbum = function(){ 1707 if( G.O.kind === '' ) { 1708 throw 'Not supported for this content source:' + G.O.kind; 1709 } 1710 1711 var albumIdx = G.GOM.albumIdx; 1712 if( albumIdx == -1 ) { 1713 throw ('Current album not found.'); 1714 } 1715 1716 var albumID = G.I[albumIdx].GetID(); 1717 1718 // unselect everything & remove link to album (=logical delete) 1719 var l = G.I.length; 1720 for( var i = 0; i < l ; i++ ) { 1721 var item = G.I[i]; 1722 if( item.albumID == albumID ) { 1723 item.selected = false; 1724 } 1725 } 1726 1727 G.I[albumIdx].contentIsLoaded = false; 1728 1729 DisplayAlbum('-1', albumID); 1730 }; 1731 1732 /** 1733 * Set one or several items selected/unselected 1734 * @param {array} items 1735 */ 1736 this.ItemsSetSelectedValue = function(items, value){ 1737 var l = items.length; 1738 for( var j = 0; j < l ; j++) { 1739 ThumbnailSelectionSet(items[j], value); 1740 } 1741 }; 1742 1743 /** 1744 * Returns an array of selected items 1745 * @returns {Array} 1746 */ 1747 this.ItemsSelectedGet = function(){ 1748 var selectedItems = []; 1749 var l = G.I.length; 1750 for( var i = 0; i < l ; i++ ) { 1751 if( G.I[i].selected == true ) { 1752 selectedItems.push(G.I[i]); 1753 } 1754 } 1755 return selectedItems; 1756 }; 1757 1758 /** 1759 * Returns the value of an option 1760 * @param {string} option 1761 * @returns {nanoGALLERY.G.O} 1762 */ 1763 this.Get = function(option){ 1764 return G.O[option]; 1765 }; 1766 1767 /** 1768 * Set a new value for a defined option 1769 * @param {string} option 1770 */ 1771 this.Set = function(option, value){ 1772 G.O[option] = value; 1773 switch( option ) { 1774 case 'thumbnailSelectable': 1775 ThumbnailSelectionClear(); 1776 // refresh the displayed gallery 1777 GalleryRender( G.GOM.albumIdx ); 1778 break; 1779 } 1780 }; 1781 1782 /** 1783 * refresh the current gallery 1784 */ 1785 this.Refresh = function() { 1786 // refresh the displayed gallery 1787 GalleryRender( G.GOM.albumIdx ); 1788 }; 1789 /** 1790 * resize the current gallery 1791 */ 1792 this.Resize = function() { 1793 // resize the displayed gallery 1794 GalleryResize(); 1795 }; 1796 1797 /** 1798 * display one item (image or gallery) 1799 * itemID syntax: 1800 * - albumID --> display one album 1801 * - albumID/imageID --> display one image 1802 */ 1803 this.DisplayItem = function( itemID ) { 1804 var IDs=parseIDs( itemID ); 1805 if( IDs.imageID != '0' ) { 1806 DisplayPhoto( IDs.imageID, IDs.albumID ); 1807 } 1808 else { 1809 DisplayAlbum( '-1', IDs.albumID ); 1810 } 1811 }; 1812 1813 1814 1815 var CountItemsToDisplay = function( gIdx ) { 1816 if( G.I[gIdx] == undefined ) { return 0; } 1817 var albumID = G.I[gIdx].GetID(); 1818 var l = G.I.length; 1819 var cnt = 0; 1820 for( var idx = 0; idx < l; idx++ ) { 1821 var item = G.I[idx]; 1822 if( item.isToDisplay(albumID) ) { 1823 cnt++; 1824 } 1825 } 1826 return cnt; 1827 } 1828 /** 1829 * Search in the displayed gallery (in thumbnails title) 1830 */ 1831 this.Search = function( search ) { 1832 G.GOM.albumSearch = search.toUpperCase(); 1833 var gIdx = G.GOM.albumIdx; 1834 GalleryRender( G.GOM.albumIdx ); 1835 return CountItemsToDisplay( gIdx ); 1836 }; 1837 1838 /** 1839 * Search2 in title and tags - set search values 1840 */ 1841 this.Search2 = function( searchTitle, searchTags ) { 1842 if( searchTitle != null && searchTitle != undefined ) { 1843 G.GOM.albumSearch = searchTitle.toUpperCase(); 1844 } 1845 else { 1846 G.GOM.albumSearch = ''; 1847 } 1848 1849 if( searchTags != null && searchTags != undefined ) { 1850 G.GOM.albumSearchTags = searchTags.toUpperCase(); 1851 } 1852 else { 1853 G.GOM.albumSearchTags = ''; 1854 } 1855 return CountItemsToDisplay( G.GOM.albumIdx ); 1856 }; 1857 1858 /** 1859 * Search2 - execute the search on title and tags 1860 */ 1861 this.Search2Execute = function() { 1862 var gIdx = G.GOM.albumIdx; 1863 GalleryRender( G.GOM.albumIdx ); 1864 return CountItemsToDisplay( gIdx ); 1865 }; 1866 1867 1868 /** 1869 * Destroy the current gallery 1870 */ 1871 this.Destroy = function(){ 1872 // alert('destroy'); 1873 // var event = new Event('build'); 1874 if( G.GOM.hammertime != null ) { 1875 G.GOM.hammertime.destroy(); 1876 G.GOM.hammertime = null; 1877 } 1878 // G.GOM.userEvents.RemoveEvtListener(); 1879 // G.GOM.userEvents=null; 1880 // G.VOM.userEvents.RemoveEvtListener(); 1881 // G.VOM.userEvents=null; 1882 if( G.VOM.hammertime != null ) { 1883 G.VOM.hammertime.destroy(); 1884 G.VOM.hammertime = null; 1885 } 1886 //ThumbnailHoverReInitAll(); 1887 1888 // color scheme 1889 $('#ngycs_' + G.baseEltID).remove() 1890 1891 G.GOM.items = []; 1892 NGY2Item.New( G, G.i18nTranslations.breadcrumbHome, '', '0', '-1', 'album' ); 1893 G.GOM.navigationBar.$newContent = null; 1894 G.$E.base.empty(); 1895 G.$E.base.removeData(); 1896 if( G.O.locationHash ) { 1897 jQuery(window).off('hashchange.nanogallery2.' + G.baseEltID); 1898 } 1899 jQuery(window).off('resize.nanogallery2.' + G.baseEltID); 1900 jQuery(window).off('orientationChange.nanogallery2.' + G.baseEltID); 1901 jQuery(window).off('scroll.nanogallery2.' + G.baseEltID); 1902 G.GOM.firstDisplay = false; 1903 }; 1904 1905 /** 1906 * CloseViewer - close the media viewer 1907 */ 1908 this.CloseViewer = function() { 1909 CloseInternalViewer(null); 1910 return false; 1911 }; 1912 1913 /** 1914 * MinimizeToolbar - display the minimized lightbox main toolbar 1915 */ 1916 this.MinimizeToolbar = function() { 1917 ViewerToolbarForVisibilityMin(); 1918 return false; 1919 }; 1920 1921 /** 1922 * MaximizeToolbar - display the maximized/standard lightbox main toolbar 1923 */ 1924 this.MaximizeToolbar = function() { 1925 ViewerToolbarForVisibilityStd(); 1926 return false; 1927 }; 1928 1929 /** 1930 * PaginationPreviousPage - gallery paginate to previous page 1931 */ 1932 this.PaginationPreviousPage = function() { 1933 paginationPreviousPage(); 1934 return false; 1935 }; 1936 1937 1938 /** 1939 * PaginationNextPage - gallery paginate to next page 1940 */ 1941 this.PaginationNextPage = function() { 1942 paginationNextPage(); 1943 return false; 1944 }; 1945 1946 1947 /** 1948 * PaginationGotoPage - gallery paginate to specific page 1949 */ 1950 this.PaginationGotoPage = function( page ) { 1951 var aIdx = G.$E.conPagin.data('galleryIdx'); 1952 if( page > 1 ) { page--; } 1953 G.GOM.pagination.currentPage = page; 1954 1955 // scroll to top of gallery if not displayed 1956 if( !inViewportVert(G.$E.base, 0) ) { 1957 $('html, body').animate({scrollTop: G.$E.base.offset().top}, 200); 1958 } 1959 GalleryDisplayPart1( true ); 1960 GalleryDisplayPart2( true ); 1961 return false; 1962 }; 1963 1964 /** 1965 * PaginationCountPages - gallery pagination - returns the number of pages 1966 */ 1967 this.PaginationCountPages = function() { 1968 if( G.GOM.items.length == 0 ) { return 0; } // no thumbnail to display 1969 1970 var nbPages = Math.ceil((G.GOM.items[G.GOM.items.length - 1].row + 1) / G.galleryMaxRows.Get()); 1971 return nbPages; 1972 }; 1973 1974 1975 1976 1977 // throttle() 1978 // author: underscore.js - http://underscorejs.org/docs/underscore.html 1979 // Returns a function, that, when invoked, will only be triggered at most once during a given window of time. 1980 // Normally, the throttled function will run as much as it can, without ever going more than once per wait duration; 1981 // but if you�d like to disable the execution on the leading edge, pass {leading: false}. 1982 // To disable execution on the trailing edge, ditto. 1983 var throttle = function(func, wait, options) { 1984 var context, args, result; 1985 var timeout = null; 1986 var previous = 0; 1987 if (!options) options = {}; 1988 var later = function() { 1989 previous = options.leading === false ? 0 : new Date().getTime(); 1990 timeout = null; 1991 result = func.apply(context, args); 1992 if (!timeout) context = args = null; 1993 }; 1994 return function() { 1995 var now = new Date().getTime(); 1996 if (!previous && options.leading === false) previous = now; 1997 var remaining = wait - (now - previous); 1998 context = this; 1999 args = arguments; 2000 if (remaining <= 0 || remaining > wait) { 2001 if (timeout) { 2002 clearTimeout(timeout); 2003 timeout = null; 2004 } 2005 previous = now; 2006 result = func.apply(context, args); 2007 if (!timeout) context = args = null; 2008 } else if (!timeout && options.trailing !== false) { 2009 timeout = setTimeout(later, remaining); 2010 } 2011 return result; 2012 }; 2013 }; 2014 2015 2016 // DEBOUNCE 2017 // author: John Hann - http://unscriptable.com/2009/03/20/debouncing-javascript-methods/ 2018 // execAsap - false means executing at the end of the detection period 2019 var debounce = function (func, threshold, execAsap) { 2020 var timeout; 2021 return function debounced () { 2022 var obj = this, args = arguments; 2023 function delayed () { 2024 if (!execAsap) 2025 func.apply(obj, args); 2026 timeout = null; 2027 }; 2028 2029 if (timeout) 2030 clearTimeout(timeout); 2031 // clearRequestTimeout(timeout); 2032 else if (execAsap) 2033 func.apply(obj, args); 2034 timeout = setTimeout(delayed, threshold || 100); 2035 // timeout = requestTimeout(delayed, threshold || 100); 2036 }; 2037 } 2038 2039 2040 // Double requestAnimationFrame 2041 window.ng_draf = function (cb) { 2042 return requestAnimationFrame(function() { 2043 window.requestAnimationFrame(cb) 2044 }) 2045 } 2046 2047 // REQUESTTIMEOUT - replace SETTIMEOUT - https://gist.github.com/joelambert/1002116 2048 /** 2049 * Behaves the same as setTimeout except uses requestAnimationFrame() where possible for better performance 2050 * @param {function} fn The callback function 2051 * @param {int} delay The delay in milliseconds 2052 */ 2053 2054 window.requestTimeout = function(fn, delay) { 2055 // if( !window.requestAnimationFrame && 2056 // !window.webkitRequestAnimationFrame && 2057 // !(window.mozRequestAnimationFrame && window.mozCancelRequestAnimationFrame) && // Firefox 5 ships without cancel support 2058 // !window.oRequestAnimationFrame && 2059 // !window.msRequestAnimationFrame) 2060 return window.setTimeout(fn, delay); 2061 2062 var start = new Date().getTime(), 2063 handle = new Object(); 2064 2065 function loop(){ 2066 var current = new Date().getTime(), 2067 delta = current - start; 2068 delta = delay; 2069 2070 delta >= delay ? fn.call() : handle.value = requestAnimFrame(loop); 2071 }; 2072 2073 handle.value = requestAnimFrame(loop); 2074 return handle; 2075 }; 2076 2077 2078 // requestAnimationFrame() shim by Paul Irish 2079 // http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 2080 window.requestAnimFrame = (function() { 2081 return window.requestAnimationFrame || 2082 window.webkitRequestAnimationFrame || 2083 window.mozRequestAnimationFrame || 2084 window.oRequestAnimationFrame || 2085 window.msRequestAnimationFrame || 2086 function(/* function */ callback, /* DOMElement */ element){ 2087 window.setTimeout(callback, 1000 / 60); 2088 }; 2089 })(); 2090 2091 2092 // CLEARREQUESTTIMEOUT - to replace CLEARTIMEOUT - https://gist.github.com/joelambert/1002116 2093 /** 2094 * Behaves the same as clearTimeout except uses cancelRequestAnimationFrame() where possible for better performance 2095 * @param {int|object} fn The callback function 2096 */ 2097 window.clearRequestTimeout = function(handle) { 2098 window.cancelAnimationFrame ? window.cancelAnimationFrame(handle.value) : 2099 window.webkitCancelAnimationFrame ? window.webkitCancelAnimationFrame(handle.value) : 2100 window.webkitCancelRequestAnimationFrame ? window.webkitCancelRequestAnimationFrame(handle.value) : /* Support for legacy API */ 2101 window.mozCancelRequestAnimationFrame ? window.mozCancelRequestAnimationFrame(handle.value) : 2102 window.oCancelRequestAnimationFrame ? window.oCancelRequestAnimationFrame(handle.value) : 2103 window.msCancelRequestAnimationFrame ? window.msCancelRequestAnimationFrame(handle.value) : 2104 clearTimeout(handle); 2105 }; 2106 2107 2108 2109 /* 2110 ** Global data for this nanogallery2 instance 2111 **/ 2112 var G=this; 2113 G.I = []; // gallery items 2114 G.Id = []; // gallery items 2115 G.O = null; // user options 2116 G.baseEltID = null; // ID of the base element 2117 G.$E = { 2118 base: null, // base element 2119 conTnParent: null, // $g_containerThumbnailsParent 2120 conLoadingB: null, // loading bar - nanoGalleryLBarOff 2121 conConsole: null, // console for error messages 2122 conNavigationBar: null, // gallery navigation bar 2123 conTnBottom: null // container on the bottom of the gallery 2124 }; 2125 G.shoppingCart = []; 2126 G.layout = { // Layout informations 2127 internal : true, 2128 engine : '', 2129 support : { rows: false }, 2130 prerequisite : { imageSize: false }, 2131 SetEngine: function() { 2132 2133 if( G.layout.internal ) { 2134 if( G.tn.settings.width[G.GOM.curNavLevel][G.GOM.curWidth] == 'auto' || G.tn.settings.width[G.GOM.curNavLevel][G.GOM.curWidth] == '' ) { 2135 // do not use getH() / getW() here! 2136 G.layout.engine = 'JUSTIFIED'; 2137 G.layout.support.rows = true; 2138 G.layout.prerequisite.imageSize = true; 2139 return; 2140 } 2141 if( G.tn.settings.height[G.GOM.curNavLevel][G.GOM.curWidth] == 'auto' || G.tn.settings.height[G.GOM.curNavLevel][G.GOM.curWidth] == '' ) { 2142 // do not use getH() / getW() here! 2143 G.layout.engine = 'CASCADING'; 2144 G.layout.support.rows = false; 2145 G.layout.prerequisite.imageSize = true; 2146 return; 2147 } 2148 2149 if( G.tn.settings.getMosaic() != null ) { 2150 G.layout.engine = 'MOSAIC'; 2151 G.layout.support.rows = true; 2152 G.layout.prerequisite.imageSize = false; 2153 return; 2154 } 2155 2156 G.layout.engine='GRID'; 2157 G.layout.support.rows=true; 2158 // if( G.tn.opt.Get('crop') === true ) { 2159 // G.layout.prerequisite.imageSize = true; 2160 // } 2161 // else { 2162 G.layout.prerequisite.imageSize = false; 2163 // } 2164 } 2165 } 2166 }; 2167 G.galleryResizeEventEnabled = false; 2168 G.galleryMaxRows = { l1: 0, lN: 0, 2169 Get: function() { 2170 return G.galleryMaxRows[G.GOM.curNavLevel]; 2171 } 2172 }; 2173 G.galleryMaxItems = { l1: 0, lN: 0, 2174 Get: function() { 2175 return G.galleryMaxItems[G.GOM.curNavLevel]; 2176 } 2177 }; 2178 G.galleryFilterTags = { l1: 0, lN: 0, 2179 Get: function() { 2180 return G.galleryFilterTags[G.GOM.curNavLevel]; 2181 } 2182 }; 2183 G.galleryDisplayMode = { l1: 'FULLCONTENT', lN: 'FULLCONTENT', 2184 Get: function() { 2185 return G.galleryDisplayMode[G.GOM.curNavLevel]; 2186 } 2187 }; 2188 G.galleryLastRowFull = { l1: false, lN: false, 2189 Get: function() { 2190 return G.galleryLastRowFull[G.GOM.curNavLevel]; 2191 } 2192 }; 2193 G.gallerySorting = { l1: '', lN: '', 2194 Get: function() { 2195 return G.gallerySorting[G.GOM.curNavLevel]; 2196 } 2197 }; 2198 G.galleryDisplayTransition = { l1: 'none', lN: 'none', 2199 Get: function() { 2200 return G.galleryDisplayTransition[G.GOM.curNavLevel]; 2201 } 2202 }; 2203 G.galleryDisplayTransitionDuration = { l1: 500, lN: 500, 2204 Get: function() { 2205 return G.galleryDisplayTransitionDuration[G.GOM.curNavLevel]; 2206 } 2207 }; 2208 G.$currentTouchedThumbnail = null; 2209 2210 // ##### GENERAL THUMBNAILS PROPERTIES --> 2211 G.tn = { 2212 // levell specific options 2213 opt: { 2214 l1: { crop: true, stacks: 0, stacksTranslateX: 0, stacksTranslateY: 0, stacksTranslateZ: 0, stacksRotateX: 0, stacksRotateY: 0, stacksRotateZ: 0, stacksScale: 0, gutterHeight: 0, gutterWidth: 0, baseGridHeight: 0, displayTransition: 'FADEIN', displayTransitionStartVal: 0, displayTransitionEasing: 'easeOutQuart', displayTransitionDuration: 240, displayInterval: 15 }, 2215 lN: { crop: true, stacks: 0, stacksTranslateX: 0, stacksTranslateY: 0, stacksTranslateZ: 0, stacksRotateX: 0, stacksRotateY: 0, stacksRotateZ: 0, stacksScale: 0, gutterHeight: 0, gutterWidth: 0, baseGridHeight: 0, displayTransition: 'FADEIN', displayTransitionStartVal: 0, displayTransitionEasing: 'easeOutQuart', displayTransitionDuration: 240, displayInterval: 15 }, 2216 Get: function(opt) { 2217 return G.tn.opt[G.GOM.curNavLevel][opt]; 2218 } 2219 }, 2220 scale: 1, // image scale depending of the hover effect 2221 borderWidth: 0, // thumbnail container border width 2222 borderHeight: 0, // thumbnail container border height 2223 labelHeight: { // in case label on bottom, otherwise always=0 2224 l1: 0, lN: 0, 2225 get: function() { 2226 return G.tn.labelHeight[G.GOM.curNavLevel]; 2227 } 2228 }, 2229 defaultSize: { // default thumbnail size 2230 // label height is not included 2231 width: { l1 : { xs:0, sm:0, me:0, la:0, xl:0 }, lN : { xs:0, sm:0, me:0, la:0, xl:0 } }, 2232 height: { l1 : { xs:0, sm:0, me:0, la:0, xl:0 }, lN : { xs:0, sm:0, me:0, la:0, xl:0 } }, 2233 getWidth: function() { 2234 return G.tn.defaultSize.width[G.GOM.curNavLevel][G.GOM.curWidth]; 2235 }, 2236 getOuterWidth: function() { // width border included 2237 return G.tn.defaultSize.width[G.GOM.curNavLevel][G.GOM.curWidth]+G.tn.borderWidth*2; 2238 }, 2239 getHeight: function() { 2240 return G.tn.defaultSize.height[G.GOM.curNavLevel][G.GOM.curWidth]; 2241 }, 2242 getOuterHeight: function() { // height, border included 2243 return G.tn.defaultSize.height[G.GOM.curNavLevel][G.GOM.curWidth]+G.tn.borderHeight*2; 2244 } 2245 }, 2246 settings: { // user defined width/height of the image to display depending on the screen size 2247 width: { l1 : { xs:0, sm:0, me:0, la:0, xl:0, xsc:'u', smc:'u', mec:'u', lac:'u', xlc:'u' }, 2248 lN : { xs:0, sm:0, me:0, la:0, xl:0, xsc:'u', smc:'u', mec:'u', lac:'u', xlc:'u' } }, 2249 height: { l1 : { xs:0, sm:0, me:0, la:0, xl:0, xsc:'u', smc:'u', mec:'u', lac:'u', xlc:'u' }, 2250 lN : { xs:0, sm:0, me:0, la:0, xl:0, xsc:'u', smc:'u', mec:'u', lac:'u', xlc:'u' } }, 2251 getH: function(l, w) { 2252 var cl = (l == undefined ? G.GOM.curNavLevel : l); 2253 var cw = (w == undefined ? G.GOM.curWidth : w); 2254 if( G.layout.engine == 'MOSAIC' ) { 2255 return G.tn.settings.height[cl][cw] * G.tn.settings.mosaic[cl+'Factor']['h'][cw]; 2256 } 2257 else { 2258 return G.tn.settings.height[cl][cw]; 2259 } 2260 }, 2261 getW: function(l, w) { 2262 var cl = (l == undefined ? G.GOM.curNavLevel : l); 2263 var cw = (w == undefined ? G.GOM.curWidth : w); 2264 if( G.layout.engine == 'MOSAIC' ) { 2265 return G.tn.settings.width[cl][cw] * G.tn.settings.mosaic[cl+'Factor']['w'][cw]; 2266 } 2267 else { 2268 return G.tn.settings.width[cl][cw]; 2269 // return G.tn.settings.width[G.GOM.curNavLevel][G.GOM.curWidth]; 2270 } 2271 }, 2272 mosaic: { l1 : { xs: null, sm: null, me: null, la: null, xl: null }, 2273 lN : { xs: null, sm: null, me: null, la: null, xl: null }, 2274 l1Factor : { h :{ xs: 1, sm: 1, me: 1, la: 1, xl: 1 }, w :{ xs: 1, sm: 1, me: 1, la: 1, xl: 1 }}, 2275 lNFactor : { h :{ xs: 1, sm: 1, me: 1, la: 1, xl: 1 }, w :{ xs: 1, sm: 1, me: 1, la: 1, xl: 1 }} 2276 }, 2277 getMosaic: function() { 2278 return G.tn.settings.mosaic[G.GOM.curNavLevel][G.GOM.curWidth]; 2279 }, 2280 mosaicCalcFactor: function(l, w) { 2281 // retrieve max size multiplicator 2282 var maxW = 1; 2283 var maxH = 1; 2284 for( var n = 0; n < G.tn.settings.mosaic[l][w].length; n++ ) { 2285 maxW = Math.max(maxW, G.tn.settings.mosaic[l][w][n]['w']); 2286 maxH = Math.max(maxH, G.tn.settings.mosaic[l][w][n]['h']); 2287 } 2288 G.tn.settings.mosaic[l + 'Factor']['h'][w] = maxH; 2289 G.tn.settings.mosaic[l + 'Factor']['w'][w] = maxW; 2290 } 2291 }, 2292 // thumbnail hover effects 2293 hoverEffects : { 2294 std : [], 2295 level1: [], 2296 get: function() { 2297 if( G.GOM.curNavLevel == 'l1' && G.tn.hoverEffects.level1.length !== 0 ) { 2298 return G.tn.hoverEffects.level1; 2299 } 2300 else { 2301 return G.tn.hoverEffects.std; 2302 } 2303 } 2304 }, 2305 // thumbnail init 2306 buildInit : { 2307 std : [], 2308 level1: [], 2309 get: function() { 2310 if( G.GOM.curNavLevel == 'l1' && G.tn.buildInit.level1.length !== 0 ) { 2311 return G.tn.buildInit.level1; 2312 } 2313 else { 2314 return G.tn.buildInit.std; 2315 } 2316 } 2317 }, 2318 // thumbnail toolbars 2319 toolbar: { 2320 album : { topLeft : '', topRight: '', bottomLeft: '', bottomRight: '' }, 2321 image : { topLeft : '', topRight: '', bottomLeft: '', bottomRight: '' }, 2322 albumUp : { topLeft : '', topRight: '', bottomLeft: '', bottomRight: '' }, 2323 get: function( item ) { 2324 return G.tn.toolbar[item.kind]; 2325 }, 2326 }, 2327 style: { 2328 // inline CSS 2329 l1 : { label: '', title: '', desc: '' }, 2330 lN : { label: '', title: '', desc: '' }, 2331 getTitle : function() { 2332 return ('style="' + G.tn.style[G.GOM.curNavLevel].title + '"'); 2333 }, 2334 getDesc : function() { 2335 return ('style="' + G.tn.style[G.GOM.curNavLevel].desc + '"'); 2336 }, 2337 getLabel: function() { 2338 var s='style="'+ G.tn.style[G.GOM.curNavLevel].label; 2339 s+= (G.O.RTL ? '"direction:RTL;"' :''); 2340 s+='"'; 2341 return s; 2342 } 2343 } 2344 }; 2345 G.scrollTimeOut = 0; 2346 G.i18nTranslations = {'paginationPrevious':'Previous', 'paginationNext':'Next', 'breadcrumbHome':'List of Albums', 'thumbnailImageTitle':'', 'thumbnailAlbumTitle':'', 'thumbnailImageDescription':'', 'thumbnailAlbumDescription':'' }; 2347 G.emptyGif = 'data:image/gif;base64,R0lGODlhEAAQAIAAAP///////yH5BAEKAAEALAAAAAAQABAAAAIOjI+py+0Po5y02ouzPgUAOw=='; 2348 G.CSStransformName = FirstSupportedPropertyName(["transform", "msTransform", "MozTransform", "WebkitTransform", "OTransform"]); 2349 // G.CSSfilterName = FirstSupportedPropertyName(["filter", "WebkitFilter"]); 2350 G.CSStransformStyle = FirstSupportedPropertyName(["transformStyle", "msTransformStyle", "MozTransformStyle", "WebkitTransformStyle", "OTransformStyle"]); 2351 G.CSSperspective = FirstSupportedPropertyName(["perspective", "msPerspective", "MozPerspective", "WebkitPerspective", "OPerspective"]); 2352 G.CSSbackfaceVisibilityName = FirstSupportedPropertyName(["backfaceVisibility", "msBackfaceVisibility", "MozBackfaceVisibility", "WebkitBackfaceVisibility", "OBackfaceVisibility"]); 2353 G.CSStransitionName = FirstSupportedPropertyName(["transition", "msTransition", "MozTransition", "WebkitTransition", "OTransition"]); 2354 G.CSSanimationName = FirstSupportedPropertyName(["animation", "msAnimation", "MozAnimation", "WebkitAnimation", "OAnimation"]); 2355 G.GalleryResizeThrottled = throttle(GalleryResize, 30, {leading: false}); 2356 2357 G.blackList = null; // album white list 2358 G.whiteList = null; // album black list 2359 G.albumList = []; // album list 2360 G.locationHashLastUsed = ''; 2361 G.custGlobals = {}; 2362 G.touchAutoOpenDelayTimerID = 0; 2363 G.i18nLang = ''; 2364 G.timeLastTouchStart = 0; 2365 G.custGlobals = {}; 2366 G.markupOrApiProcessed = false; 2367 2368 //------------------------ 2369 //--- Gallery Object Model 2370 G.GOM = { 2371 albumIdx : -1, // index (in G.I) of the currently displayed album 2372 clipArea : { top: 0, height: 0 }, // area of the GOM to display on screen 2373 displayArea : { width: 0 , height: 0 }, // size of the GOM area (=used area, not available area) 2374 displayAreaLast : { width: 0 , height: 0 }, // previous size of the GOM area 2375 displayedMoreSteps : 0, // current number of displayed steps (moreButton mode) 2376 items: [], // current items of the GOMS 2377 $imgPreloader: [], 2378 thumbnails2Display: [], 2379 itemsDisplayed : 0, // number of currently displayed thumbnails 2380 firstDisplay : true, 2381 firstDisplayTime : 0, // in conjunction with galleryRenderDelay 2382 navigationBar : { // content of the navigation bar (for breadcrumb and filter tags) 2383 displayed: false, 2384 $newContent: '' 2385 }, 2386 cache : { // cached data 2387 viewport: null, 2388 containerOffset: null, 2389 areaWidth: 100 // available area width 2390 }, 2391 nbSelected : 0, // number of selected items 2392 pagination : { currentPage: 0 }, // pagination data 2393 panThreshold: 60, // threshold value (in pixels) to block horizontal pan/swipe 2394 panYOnly: false, // threshold value reach -> definitively block horizontal pan until end of pan 2395 lastFullRow : -1, // number of the last row without holes 2396 lastDisplayedIdx: -1, // used to display the counter of not displayed items 2397 displayInterval : { from: 0, len: 0 }, 2398 userEvents: null, 2399 hammertime: null, 2400 curNavLevel: 'l1', // current navigation level (l1 or LN) 2401 curWidth: 'me', 2402 albumSearch: '', // current search string -> title (used to filter the thumbnails on screen) 2403 albumSearchTags: '', // current search string -> tags 2404 lastZIndex: 0, // used to put a thumbnail on top of all others (for exemple for scale hover effect) 2405 lastRandomValue: 0, 2406 slider : { // slider on last thumbnail 2407 hostIdx: -1, // idx of the thumbnail hosting the slider 2408 hostItem: null, // item hosting the slider 2409 currentIdx: 0, // idx of the current displayed item 2410 nextIdx: 0, // idx of the next item to display in the slider 2411 timerID: 0, 2412 tween: null // tranistion tween instance 2413 }, 2414 NGY2Item: function( idx ) { // returns a NGY2Item or null if it does not exist 2415 if( G.GOM.items[idx] == undefined || G.GOM.items[idx] == null ) { return null; } 2416 var i = G.GOM.items[idx].thumbnailIdx; 2417 return G.I[i]; 2418 }, 2419 // One GOM item (thumbnail) 2420 // function GTn(index, width, height) { 2421 GTn: function(index, width, height) { 2422 this.thumbnailIdx = index; 2423 this.width = 0; // thumbnail width 2424 this.height = 0; // thumbnail height 2425 this.top = 0; // position: top 2426 this.left = 0; // position: left 2427 this.row = 0; // position: row number 2428 this.imageWidth = width; // image width 2429 this.imageHeight = height; // image height 2430 this.resizedContentWidth = 0; 2431 this.resizedContentHeight = 0; 2432 this.displayed = false; 2433 this.neverDisplayed = true; 2434 this.inDisplayArea = false; 2435 } 2436 }; 2437 2438 2439 //------------------------ 2440 //--- Viewer Object Model 2441 2442 G.VOM = { 2443 viewerDisplayed: false, // is the viewer currently displayed 2444 viewerIsFullscreen: false, // viewer in fullscreen mode 2445 infoDisplayed: false, // is the info box displayed 2446 toolbarsDisplayed: true, // the toolbars are displayed 2447 toolsHide: null, 2448 saveOverflowX: 'visible', // store the value to restore it back after viewer is closed 2449 saveOverflowY: 'visible', 2450 zoom : { 2451 posX: 0, // position to center zoom in/out 2452 posY: 0, 2453 userFactor: 1, // user zoom factor (applied to the baseZoom factor) 2454 isZooming: false 2455 }, 2456 padding: { H: 0, V: 0 }, // padding for the image 2457 window: { lastWidth: 0, lastHeight: 0 }, 2458 $cont: null, // viewer container 2459 $viewer: null, 2460 $toolbar: null, // viewerToolbar 2461 $toolbarTL: null, // viewer toolbar on top left 2462 $toolbarTR: null, // viewer toolbar on top right 2463 $content: null, // viewer content 2464 2465 $mediaPrevious: null, // previous image 2466 $mediaCurrent: null, // current image 2467 $mediaNext: null, // next image 2468 toolbarMode: 'std', // current toolbar mode (standard, minimized) 2469 playSlideshow : false, // slide show mode status 2470 playSlideshowTimerID: 0, // slideshow mode time 2471 slideshowDelay: 3000, // slideshow mode - delay before next image 2472 albumID: -1, 2473 currItemIdx: -1, 2474 viewerMediaIsChanged: false, // media display is currently modified 2475 items: [], // current list of images to be managed by the viewer 2476 NGY2Item: function( n ) { // returns a NGY2Item 2477 switch( n ) { 2478 case -1: // previous 2479 var idx=this.IdxPrevious(); 2480 return G.I[this.items[idx].ngy2ItemIdx]; 2481 break; 2482 case 1: // next 2483 var idx=this.IdxNext(); 2484 return G.I[this.items[idx].ngy2ItemIdx]; 2485 break; 2486 case 0: // current 2487 default: 2488 return G.I[this.items[G.VOM.currItemIdx].ngy2ItemIdx]; 2489 break; 2490 } 2491 }, 2492 IdxNext: function() { 2493 var n = 0; 2494 if( G.VOM.currItemIdx != (G.VOM.items.length-1) ) { 2495 n = G.VOM.currItemIdx + 1; 2496 } 2497 return n; 2498 }, 2499 IdxPrevious: function() { 2500 var n = G.VOM.currItemIdx - 1; 2501 if( G.VOM.currItemIdx == 0 ) { 2502 n = G.VOM.items.length - 1; 2503 } 2504 return n; 2505 }, 2506 userEvents: null, // user events management 2507 hammertime: null, // hammer.js manager 2508 swipePosX: 0, // current horizontal swip position 2509 panPosX: 0, // position for manual pan 2510 panPosY: 0, 2511 panThreshold: 60, // threshold value (in pixels) to block vertical pan 2512 panXOnly: false, // threshold value reach -> definitively block vertical pan until end of pan 2513 viewerTheme: '', 2514 timeImgChanged: 0, 2515 ImageLoader: { 2516 // fires a callback when image size is know (during download) 2517 // inspired by ROB - http://stackoverflow.com/users/226507/rob 2518 maxChecks: 1000, 2519 list: [], 2520 intervalHandle : null, 2521 2522 loadImage : function (callback, ngitem) { 2523 if( ngitem.mediaKind != 'img' ) { return; } // ignore - only for images 2524 var img = new Image (); 2525 img.src = ngitem.responsiveURL(); 2526 if (img.width && img.height) { 2527 callback (img.width, img.height, ngitem, 0); 2528 } 2529 else { 2530 var obj = {image: img, url: ngitem.responsiveURL(), ngitem: ngitem, callback: callback, checks: 1}; 2531 var i; 2532 for (i=0; i < this.list.length; i++) { 2533 if (this.list[i] == null) 2534 break; 2535 } 2536 this.list[i] = obj; 2537 if (!this.intervalHandle) 2538 this.intervalHandle = setInterval(this.interval, 50); 2539 } 2540 }, 2541 2542 // called by setInterval 2543 interval : function () { 2544 var count = 0; 2545 var list = G.VOM.ImageLoader.list, item; 2546 for (var i=0; i<list.length; i++) { 2547 item = list[i]; 2548 if (item != null) { 2549 if (item.image.width && item.image.height) { 2550 G.VOM.ImageLoader.list[i] = null; 2551 item.callback (item.image.width, item.image.height, item.ngitem, item.checks); 2552 } 2553 else if (item.checks > G.VOM.ImageLoader.maxChecks) { 2554 G.VOM.ImageLoader.list[i] = null; 2555 item.callback (0, 0, item.ngitem, item.checks); 2556 } 2557 else { 2558 count++; 2559 item.checks++; 2560 } 2561 } 2562 } 2563 if (count == 0) { 2564 G.VOM.ImageLoader.list = []; 2565 clearInterval (G.VOM.ImageLoader.intervalHandle); 2566 delete G.VOM.ImageLoader.intervalHandle; 2567 } 2568 } 2569 } 2570 } 2571 // One VOM item (image) 2572 function VImg(index) { 2573 this.$e = null; 2574 this.ngy2ItemIdx = index; 2575 this.mediaNumber = 0; 2576 this.posX = 0; // to center the element 2577 this.posY = 0; 2578 } 2579 2580 2581 //------------------------ 2582 //--- popup 2583 G.popup = { 2584 isDisplayed: false, 2585 $elt: null, 2586 close: function() { 2587 if( this.$elt != null ) { 2588 var tweenable = new NGTweenable(); 2589 tweenable.tween({ 2590 from: { opacity:1 }, 2591 to: { opacity:0 }, 2592 attachment: { t: this }, 2593 easing: 'easeInOutSine', 2594 duration: 100, 2595 step: function (state, att) { 2596 if( att.t.$elt != null ) { 2597 att.t.$elt.css('opacity',state.opacity); 2598 } 2599 }, 2600 finish: function (state, att) { 2601 if( att.t.$elt != null ) { 2602 att.t.$elt.remove(); 2603 att.t.$elt=null; 2604 } 2605 att.t.isDisplayed=false; 2606 } 2607 }); 2608 } 2609 } 2610 } 2611 2612 2613 // Color schemes - Gallery 2614 // gadrient generator: https://www.grabient.com/ 2615 G.galleryTheme_dark = { 2616 navigationBar : { background: 'none', borderTop: '', borderBottom: '', borderRight: '', borderLeft: '' }, 2617 navigationBreadcrumb : { background: '#111', color: '#fff', colorHover: '#ccc', borderRadius: '4px' }, 2618 navigationFilter : { color: '#ddd', background: '#111', colorSelected: '#fff', backgroundSelected: '#111', borderRadius: '4px' }, 2619 thumbnail : { background: '#444', backgroundImage: 'linear-gradient(315deg, #111 0%, #445 90%)', borderColor: '#000', labelOpacity : 1, labelBackgroun
2619d: 'rgba(34, 34, 34, 0)', titleColor: '#fff', titleBgColor: 'transparent', titleShadow: '', descriptionColor: '#ccc', descriptionBgColor: 'transparent', descriptionShadow: '', stackBackground: '#aaa' }, 2620 thumbnailIcon : { padding: '5px', color: '#fff' }, 2621 pagination : { background: '#181818', backgroundSelected: '#666', color: '#fff', borderRadius: '2px', shapeBorder: '3px solid #666', shapeColor: '#444', shapeSelectedColor: '#aaa'} 2622 }; 2623 2624 G.galleryTheme_light = { 2625 navigationBar : { background: 'none', borderTop: '', borderBottom: '', borderRight: '', borderLeft: '' }, 2626 navigationBreadcrumb : { background: '#eee', color: '#000', colorHover: '#333', borderRadius: '4px' }, 2627 navigationFilter : { background: '#eee', color: '#222', colorSelected: '#000', backgroundSelected: '#eee', borderRadius: '4px' }, 2628 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' }, 2629 thumbnailIcon : { padding: '5px', color: '#fff' }, 2630 pagination : { background: '#eee', backgroundSelected: '#aaa', color: '#000', borderRadius: '2px', shapeBorder: '3px solid #666', shapeColor: '#444', shapeSelectedColor: '#aaa'} 2631 }; 2632 2633 // Color schemes - lightbox 2634 G.viewerTheme_dark = { 2635 background: '#000', 2636 imageBorder: 'none', 2637 imageBoxShadow: 'none', 2638 barBackground: 'rgba(4, 4, 4, 0.2)', 2639 barBorder: '0px solid #111', 2640 barColor: '#eee', 2641 barDescriptionColor: '#aaa' 2642 }; 2643 G.viewerTheme_border = { 2644 background: 'rgba(1, 1, 1, 0.75)', 2645 imageBorder: '4px solid #f8f8f8', 2646 imageBoxShadow: '#888 0px 0px 20px', 2647 barBackground: 'rgba(4, 4, 4, 0.2)', 2648 barBorder: '0px solid #111', 2649 barColor: '#eee', 2650 barDescriptionColor: '#aaa' 2651 }; 2652 G.viewerTheme_light = { 2653 background: '#f8f8f8', 2654 imageBorder: 'none', 2655 imageBoxShadow: 'none', 2656 barBackground: 'rgba(4, 4, 4, 0.7)', 2657 barBorder: '0px solid #111', 2658 barColor: '#eee', 2659 barDescriptionColor: '#aaa' 2660 }; 2661 2662 2663 2664 // shortcut with G context to NGY2TOOLS 2665 // var NanoAlert = NGY2Tools.NanoAlert.bind(G); 2666 // var NanoConsoleLog = NGY2Tools.NanoConsoleLog.bind(G); 2667 var NanoAlert = NGY2Tools.NanoAlert; 2668 var NanoConsoleLog = NGY2Tools.NanoConsoleLog; 2669 2670 2671 /** @function initiateGallery2 */ 2672 this.initiateGallery2 = function( element, params ) { 2673 2674 // GLOBAL OPTIONS 2675 G.O = params; 2676 // Base element 2677 G.$E.base = jQuery(element); 2678 G.baseEltID = G.$E.base.attr('id'); 2679 if( G.baseEltID == undefined ) { 2680 // set a default ID to the root container 2681 G.baseEltID='my_nanogallery'; 2682 G.$E.base.attr('id', G.baseEltID) 2683 } 2684 G.O.$markup = []; 2685 DefineVariables(); 2686 SetPolyFills(); 2687 BuildSkeleton(); 2688 G.GOM.firstDisplayTime=Date.now(); 2689 2690 SetGlobalEvents(); 2691 2692 // check if only one specific album will be used 2693 var albumToDisplay = G.O.album; 2694 if( albumToDisplay == '' && G.O.photoset != '' ) { 2695 albumToDisplay = G.O.photoset; 2696 G.O.album = G.O.photoset; 2697 } 2698 if( albumToDisplay != '' ) { 2699 G.O.displayBreadcrumb = false; // no breadcrumb since only 1 album 2700 if( albumToDisplay.toUpperCase() != 'NONE' ) { 2701 // open specific album 2702 2703 var p=albumToDisplay.indexOf('&authkey='); 2704 if( p == -1 ) { 2705 p=albumToDisplay.indexOf('?authkey='); 2706 } 2707 if( p > 0 ) { 2708 // privat album with authkey 2709 G.O.locationHash=false; // disable hash location for hidden/privat albums --> impossible to handle 2710 var albumID=albumToDisplay.substring(0,p); 2711 var opt=albumToDisplay.substring(p); 2712 if( opt.indexOf('Gv1sRg') == -1 ) { 2713 opt = '&authkey=Gv1sRg'+opt.substring(9); 2714 } 2715 var newItem = NGY2Item.New( G, '', '', albumID, '-1', 'album' ); 2716 newItem.authkey = opt; 2717 DisplayAlbum('-1', albumID); 2718 } 2719 else { 2720 // open a public album 2721 if( G.O.kind == "nano_photos_provider2") {
vendor: 141,104 bytes, lines 2722-6413
2722 if( albumToDisplay == decodeURIComponent(albumToDisplay)) { 2723 // album ID must be encoded 2724 albumToDisplay = encodeURIComponent(albumToDisplay); 2725 G.O.album = albumToDisplay; 2726 } 2727 } 2728 NGY2Item.New( G, '', '', albumToDisplay, '-1', 'album' ); 2729 if( !ProcessLocationHash() ) { 2730 DisplayAlbum('-1', albumToDisplay); 2731 } 2732 } 2733 return; 2734 } 2735 } 2736 2737 // use full content 2738 // add base album 2739 NGY2Item.New( G, G.i18nTranslations.breadcrumbHome, '', '0', '-1', 'album' ); 2740 2741 processStartOptions(); 2742 2743 } 2744 2745 2746 /** @function processStartOptions */ 2747 function processStartOptions() { 2748 // open image or album 2749 // 1. load hidden albums 2750 // 2. check if location hash set (deep linking) 2751 // 3. check openOnStart parameter 2752 // 4. open root album (ID=-1) 2753 2754 // hidden/private albums are loaded on plugin start (Picasa) --> no more available in Google Photos 2755 // if( G.albumListHidden.length > 0 ) { 2756 // jQuery.nanogallery2['data_'+G.O.kind](G, 'GetHiddenAlbums', G.albumListHidden, processStartOptionsPart2); 2757 // return; 2758 //} 2759 2760 if( !ProcessLocationHash() ) { 2761 processStartOptionsPart2(); 2762 } 2763 } 2764 2765 /** @function processStartOptionsPart2 */ 2766 function processStartOptionsPart2() { 2767 2768 // Check location hash + start parameters -> determine what to do on start 2769 // openOnStart parameter 2770 if( G.O.openOnStart != '' ) { 2771 var IDs = parseIDs(G.O.openOnStart); 2772 if( IDs.imageID != '0' ) { 2773 DisplayPhoto(IDs.imageID, IDs.albumID); 2774 } 2775 else { 2776 DisplayAlbum('-1', IDs.albumID); 2777 } 2778 } 2779 else { 2780 // open root album (ID = -1) 2781 DisplayAlbum('-1', 0); 2782 } 2783 } 2784 2785 // Parse string to extract albumID and imageID (format albumID/imageID) 2786 function parseIDs( IDs ) { 2787 var r={ albumID: '0', imageID: '0' }; 2788 2789 var t=IDs.split('/'); 2790 if( t.length > 0 ) { 2791 r.albumID = t[0]; 2792 if( t.length > 1 ) { 2793 r.imageID = t[1]; 2794 } 2795 } 2796 return r; 2797 } 2798 2799 2800 /** @function DisplayAlbum */ 2801 function DisplayAlbum( imageID, albumID ) { 2802 // close viewer if already displayed 2803 if( G.VOM.viewerDisplayed ) { 2804 CloseInternalViewer(null); 2805 } 2806 2807 // set current navigation level (l1 or lN) 2808 var albumIdx = NGY2Item.GetIdx(G, albumID); 2809 G.GOM.curNavLevel = 'lN'; 2810 if( albumIdx == 0 ) { 2811 G.GOM.curNavLevel = 'l1'; 2812 } 2813 G.layout.SetEngine(); 2814 G.galleryResizeEventEnabled = false; 2815 2816 if( albumIdx == -1 ) { 2817 NGY2Item.New( G, '', '', albumID, '0', 'album' ); // create empty album 2818 albumIdx = G.I.length - 1; 2819 } 2820 2821 if( !G.I[albumIdx].contentIsLoaded ) { 2822 // get content of the album if not already loaded 2823 AlbumGetContent( albumID, DisplayAlbum, imageID, albumID ); 2824 return; 2825 } 2826 2827 ThumbnailSelectionClear(); 2828 2829 G.GOM.pagination.currentPage = 0; 2830 SetLocationHash( albumID, '' ); 2831 GalleryRender( albumIdx ); 2832 2833 } 2834 2835 2836 //----- manage the bottom area of the gallery -> "pagination" or "more button" 2837 function GalleryBottomManage() { 2838 2839 switch( G.galleryDisplayMode.Get() ) { 2840 case 'PAGINATION': 2841 if( G.layout.support.rows && G.galleryMaxRows.Get() > 0 ) { 2842 ManagePagination( G.GOM.albumIdx ); 2843 } 2844 break; 2845 case 'MOREBUTTON': 2846 G.$E.conTnBottom.off('click'); 2847 var nb = G.GOM.items.length-G.GOM.itemsDisplayed; 2848 if( nb == 0 ) { 2849 G.$E.conTnBottom.empty(); 2850 } 2851 else { 2852 G.$E.conTnBottom.html('<div class="nGY2GalleryMoreButton"><div class="nGY2GalleryMoreButtonAnnotation">+'+nb+' ' + G.O.icons.galleryMoreButton +'</div></div>'); 2853 G.$E.conTnBottom.on('click', function(e) { 2854 G.GOM.displayedMoreSteps++; 2855 GalleryResize(); 2856 }); 2857 } 2858 break; 2859 case 'FULLCONTENT': 2860 default: 2861 break; 2862 } 2863 } 2864 2865 2866 // add one album/folder to the breadcrumb 2867 function breadcrumbAdd( albumIdx ) { 2868 2869 var ic=''; 2870 if( !G.O.breadcrumbHideIcons ) { 2871 ic=G.O.icons.breadcrumbAlbum; 2872 if( albumIdx == 0 ) { 2873 ic=G.O.icons.breadcrumbHome; 2874 } 2875 } 2876 var $newDiv =jQuery('<div class="oneItem">'+ic + G.I[albumIdx].title+'</div>').appendTo(G.GOM.navigationBar.$newContent.find('.nGY2Breadcrumb')); 2877 if( G.O.breadcrumbOnlyCurrentLevel ) { 2878 // link to parent folder (only 1 level is displayed in the breadcrumb) 2879 if( albumIdx == 0 ) { 2880 // no parent level -> stay on current one 2881 jQuery($newDiv).data('albumID','0'); 2882 } 2883 else { 2884 jQuery($newDiv).data('albumID',G.I[albumIdx].albumID); 2885 } 2886 } 2887 else { 2888 // link to current folder 2889 jQuery($newDiv).data('albumID',G.I[albumIdx].GetID()); 2890 } 2891 $newDiv.click(function() { 2892 var cAlbumID=jQuery(this).data('albumID'); 2893 DisplayAlbum('-1', cAlbumID); 2894 return; 2895 }); 2896 } 2897 2898 // add one separator to breadcrumb 2899 function breadcrumbAddSeparator( lastAlbumID ) { 2900 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')); 2901 jQuery($newSep).data('albumIdx',lastAlbumID); 2902 $newSep.click(function() { 2903 var sepAlbumIdx=jQuery(this).data('albumIdx'); 2904 DisplayAlbum('-1', G.I[sepAlbumIdx].GetID()); 2905 return; 2906 }); 2907 } 2908 2909 2910 2911 // Manage the gallery toolbar (breadcrumb + tag filter) 2912 function GalleryNavigationBar( albumIdx ) { 2913 2914 // Title + background image 2915 // var bgImage=''; 2916 // var l=G.I.length; 2917 // var albumID = G.I[albumIdx].GetID(); 2918 // for( var idx=0; idx<l ; idx++) { 2919 // var item=G.I[idx]; 2920 // if( item.kind == 'image' && item.isToDisplay(albumID) ) { 2921 // 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>'; 2922 // break; 2923 // } 2924 // } 2925 2926 //console.log(bgImage); 2927 2928 // new navigation bar items are not build in the DOM, but in memory 2929 G.GOM.navigationBar.$newContent=jQuery('<div class="nGY2Navigationbar"></div>'); 2930 //G.GOM.navigationBar.$newContent = jQuery(bgImage ); 2931 //console.log(G.GOM.navigationBar.$newContent); 2932 2933 //-- manage breadcrumb 2934 if( G.O.displayBreadcrumb == true && !G.O.thumbnailAlbumDisplayImage) { 2935 // retrieve new folder level 2936 var newLevel = 0, 2937 lstItems=[]; 2938 if( albumIdx != 0 ) { 2939 var l=G.I.length, 2940 parentID=0; 2941 2942 lstItems.push(albumIdx); 2943 var curIdx=albumIdx; 2944 newLevel++; 2945 2946 while( G.I[curIdx].albumID != 0 && G.I[curIdx].albumID != -1) { 2947 for(var i=1; i < l; i++ ) { 2948 if( G.I[i].GetID() == G.I[curIdx].albumID ) { 2949 curIdx=i; 2950 lstItems.push(curIdx); 2951 newLevel++; 2952 break; 2953 } 2954 } 2955 } 2956 } 2957 2958 // build breadcrumb 2959 if( !(G.O.breadcrumbAutoHideTopLevel && newLevel == 0) ) { 2960 BreadcrumbBuild( lstItems ); 2961 } 2962 } 2963 2964 2965 //-- manage and build tag filters 2966 if( G.galleryFilterTags.Get() != false ) { 2967 var nTags=G.I[albumIdx].albumTagList.length; 2968 if( nTags > 0 ) { 2969 for(var i=0; i < nTags; i++ ) { 2970 var s=G.I[albumIdx].albumTagList[i]; 2971 var ic=G.O.icons.navigationFilterUnselected; 2972 var tagClass='Unselected'; 2973 if( jQuery.inArray(s, G.I[albumIdx].albumTagListSel) >= 0 ) { 2974 tagClass='Selected'; 2975 ic=G.O.icons.navigationFilterSelected; 2976 } 2977 var $newTag=jQuery('<div class="nGY2NavigationbarItem nGY2NavFilter'+tagClass+'">'+ic+' '+s+'</div>').appendTo(G.GOM.navigationBar.$newContent); 2978 $newTag.click(function() { 2979 var $this=jQuery(this); 2980 var tag=$this.text().replace(/^\s*|\s*$/, ''); //trim trailing/leading whitespace 2981 // if( $this.hasClass('oneTagUnselected') ){ 2982 if( $this.hasClass('nGY2NavFilterUnselected') ){ 2983 G.I[albumIdx].albumTagListSel.push(tag); 2984 } 2985 else { 2986 var tidx=jQuery.inArray(tag,G.I[albumIdx].albumTagListSel); 2987 if( tidx != -1 ) { 2988 G.I[albumIdx].albumTagListSel.splice(tidx,1); 2989 } 2990 } 2991 $this.toggleClass('nGY2NavFilters-oneTagUnselected nGY2NavFilters-oneTagSelected'); 2992 DisplayAlbum('-1', G.I[albumIdx].GetID()); 2993 }); 2994 } 2995 var $newClearFilter=jQuery('<div class="nGY2NavigationbarItem nGY2NavFilterSelectAll">'+G.O.icons.navigationFilterSelectedAll+'</div>').appendTo(G.GOM.navigationBar.$newContent); 2996 $newClearFilter.click(function() { 2997 var nTags=G.I[albumIdx].albumTagList.length; 2998 G.I[albumIdx].albumTagListSel=[]; 2999 for(var i=0; i <nTags; i++ ) { 3000 var s=G.I[albumIdx].albumTagList[i]; 3001 G.I[albumIdx].albumTagListSel.push(s); 3002 } 3003 DisplayAlbum('-1', G.I[albumIdx].GetID()); 3004 }); 3005 } 3006 } 3007 3008 } 3009 3010 function BreadcrumbBuild(lstItems) { 3011 3012 // console.log(G.GOM.navigationBar.$newContent); 3013 jQuery('<div class="nGY2NavigationbarItem nGY2Breadcrumb"></div>').appendTo(G.GOM.navigationBar.$newContent); 3014 // console.log(G.GOM.navigationBar.$newContent); 3015 3016 if( G.O.breadcrumbOnlyCurrentLevel ) { 3017 // display only 1 separator and the current folder level 3018 if( lstItems.length == 0 ) { 3019 breadcrumbAdd(0); 3020 } 3021 else { 3022 var last=lstItems.length-1; 3023 if( lstItems.length == 1 ) { 3024 breadcrumbAddSeparator(0); // root level 3025 } 3026 else { 3027 breadcrumbAddSeparator(lstItems[0]); 3028 } 3029 breadcrumbAdd(lstItems[0]); 3030 } 3031 } 3032 else { 3033 // display the full breadcrum (full folder levels including root level) 3034 breadcrumbAdd(0); 3035 if( lstItems.length > 0 ) { 3036 breadcrumbAddSeparator(0); 3037 for(var i=lstItems.length-1; i>=0 ; i-- ) { 3038 breadcrumbAdd(lstItems[i]); 3039 if( i > 0 ) { 3040 breadcrumbAddSeparator(lstItems[i-1]); 3041 } 3042 } 3043 } 3044 } 3045 3046 } 3047 3048 3049 // Display gallery pagination 3050 function ManagePagination( albumIdx ) { 3051 3052 G.$E.conTnBottom.css('opacity', 0); 3053 G.$E.conTnBottom.children().remove(); 3054 3055 if( G.GOM.items.length == 0 ) { return; } // no thumbnail to display 3056 3057 // calculate the number of pages 3058 var nbPages=Math.ceil((G.GOM.items[G.GOM.items.length - 1].row + 1)/G.galleryMaxRows.Get()); 3059 3060 // only one page -> do not display pagination 3061 if( nbPages == 1 ) { return; } 3062 3063 // check if current page still exists (for example after a resize) 3064 if( G.GOM.pagination.currentPage > (nbPages-1) ) { 3065 G.GOM.pagination.currentPage = nbPages-1; 3066 } 3067 3068 GalleryRenderGetInterval(); 3069 // nothing to display --> exit 3070 if( G.GOM.displayInterval.len == 0 ) { return; } 3071 3072 // display "previous" 3073 if( G.O.galleryPaginationMode == 'NUMBERS' && G.GOM.pagination.currentPage > 0 ) { 3074 var $eltPrev = jQuery('<div class="nGY2PaginationPrev">'+G.O.icons.paginationPrevious+'</div>').appendTo(G.$E.conTnBottom); 3075 $eltPrev.click(function(e) { 3076 paginationPreviousPage(); 3077 }); 3078 } 3079 3080 var firstPage = 0; 3081 var lastPage = nbPages; 3082 if( G.O.galleryPaginationMode != 'NUMBERS' ) { 3083 // no 'previous'/'next' and no max number of pagination items 3084 firstPage = 0; 3085 } 3086 else { 3087 // display pagination numbers and previous/next 3088 var vp = G.O.paginationVisiblePages; 3089 var numberOfPagesToDisplay = G.O.paginationVisiblePages; 3090 if( numberOfPagesToDisplay >= nbPages ) { 3091 firstPage = 0; 3092 } 3093 else { 3094 // we have more pages than we want to display 3095 var nbBeforeAfter = 0; 3096 if( isOdd(numberOfPagesToDisplay) ) { 3097 nbBeforeAfter = (numberOfPagesToDisplay + 1) / 2; 3098 } 3099 else { 3100 nbBeforeAfter = numberOfPagesToDisplay / 2; 3101 } 3102 3103 if( G.GOM.pagination.currentPage < nbBeforeAfter ) { 3104 firstPage = 0; 3105 lastPage = numberOfPagesToDisplay - 1; 3106 if( lastPage > nbPages ) { 3107 lastPage = nbPages - 1; 3108 } 3109 } 3110 else { 3111 firstPage = G.GOM.pagination.currentPage - nbBeforeAfter; 3112 lastPage = firstPage + numberOfPagesToDisplay; 3113 if( lastPage > nbPages ) { 3114 lastPage = nbPages - 1; 3115 } 3116 } 3117 3118 if( (lastPage - firstPage) < numberOfPagesToDisplay ) { 3119 firstPage = lastPage - numberOfPagesToDisplay; 3120 if( firstPage < 0 ) { 3121 firstPage = 0; 3122 } 3123 } 3124 3125 } 3126 } 3127 3128 // render pagination items 3129 for(var i = firstPage; i < lastPage; i++ ) { 3130 var c = ''; 3131 var p = ''; 3132 3133 switch( G.O.galleryPaginationMode ) { 3134 case 'NUMBERS': 3135 c = 'nGY2paginationItem'; 3136 p = i + 1; 3137 break; 3138 case 'DOTS': 3139 c = 'nGY2paginationDot'; 3140 break; 3141 case 'RECTANGLES': 3142 c = 'nGY2paginationRectangle'; 3143 break; 3144 } 3145 if( i == G.GOM.pagination.currentPage ) { 3146 c += 'CurrentPage'; 3147 } 3148 3149 var elt$ = jQuery('<div class="' + c + '">' + p + '</div>').appendTo(G.$E.conTnBottom); 3150 elt$.data('pageNumber', i ); 3151 elt$.click( function(e) { 3152 G.GOM.pagination.currentPage = jQuery(this).data('pageNumber'); 3153 TriggerCustomEvent('pageChanged'); 3154 3155 // scroll to top of gallery if not displayed 3156 if( !inViewportVert(G.$E.base, 0) ) { 3157 $('html, body').animate({scrollTop: G.$E.base.offset().top}, 200); 3158 } 3159 GalleryDisplayPart1( true ); 3160 GalleryDisplayPart2( true ); 3161 }); 3162 3163 } 3164 3165 // display "next" 3166 if( G.O.galleryPaginationMode == 'NUMBERS' && (G.GOM.pagination.currentPage + 1) < nbPages ) { 3167 var $eltNext = jQuery('<div class="nGY2PaginationNext">' + G.O.icons.paginationNext + '</div>').appendTo(G.$E.conTnBottom); 3168 $eltNext.click( function(e) { 3169 paginationNextPage(); 3170 }); 3171 } 3172 3173 G.$E.conTnBottom.css('opacity', 1); 3174 3175 } 3176 function isOdd(num) { return (num % 2) == 1;} 3177 3178 // pagination - next page 3179 function paginationNextPage() { 3180 var aIdx = G.GOM.albumIdx, 3181 n1 = 0; 3182 ThumbnailHoverOutAll(); 3183 3184 // pagination - max lines per page mode 3185 if( G.galleryMaxRows.Get() > 0 ) { 3186 // number of pages 3187 n1 = (G.GOM.items[G.GOM.items.length - 1].row + 1) / G.galleryMaxRows.Get(); 3188 } 3189 var n2 = Math.ceil(n1); 3190 var pn = G.GOM.pagination.currentPage; 3191 if( pn < (n2-1) ) { 3192 pn++; 3193 } 3194 else { 3195 pn = 0; 3196 } 3197 3198 G.GOM.pagination.currentPage = pn; 3199 TriggerCustomEvent('pageChanged'); 3200 3201 // scroll to top of gallery if not displayed 3202 if( !inViewportVert(G.$E.base, 0) ) { 3203 $('html, body').animate({scrollTop: G.$E.base.offset().top}, 200); 3204 } 3205 GalleryDisplayPart1( true ); 3206 GalleryDisplayPart2( true ); 3207 } 3208 3209 // pagination - previous page 3210 function paginationPreviousPage() { 3211 // var aIdx=G.$E.conTnBottom.data('galleryIdx'), 3212 var aIdx = G.GOM.albumIdx, 3213 n1 = 0; 3214 3215 ThumbnailHoverOutAll(); 3216 3217 // pagination - max lines per page mode 3218 if( G.galleryMaxRows.Get() > 0 ) { 3219 // number of pages 3220 n1 = (G.GOM.items[G.GOM.items.length - 1].row + 1) / G.galleryMaxRows.Get(); 3221 } 3222 var n2 = Math.ceil(n1); 3223 3224 // var pn=G.$E.conTnBottom.data('currentPageNumber'); 3225 var pn = G.GOM.pagination.currentPage; 3226 if( pn > 0 ) { 3227 pn--; 3228 } 3229 else { 3230 pn = n2 - 1; 3231 } 3232 3233 G.GOM.pagination.currentPage = pn; 3234 TriggerCustomEvent('pageChanged'); 3235 3236 // scroll to top of gallery if not displayed 3237 if( !inViewportVert(G.$E.base, 0) ) { 3238 $('html, body').animate({scrollTop: G.$E.base.offset().top}, 200); 3239 } 3240 GalleryDisplayPart1( true ); 3241 GalleryDisplayPart2( true ); 3242 } 3243 3244 // retrieve the from/to intervall for gallery thumbnail render 3245 function GalleryRenderGetInterval() { 3246 G.GOM.displayInterval.from = 0; 3247 G.GOM.displayInterval.len = G.I.length; 3248 3249 switch( G.galleryDisplayMode.Get() ) { 3250 case 'PAGINATION': 3251 if( G.layout.support.rows ) { 3252 var nbTn = G.GOM.items.length; 3253 var firstRow = G.GOM.pagination.currentPage * G.galleryMaxRows.Get(); 3254 var lastRow = firstRow + G.galleryMaxRows.Get(); 3255 var firstTn = -1; 3256 G.GOM.displayInterval.len = 0; 3257 for( var i = 0; i < nbTn ; i++ ) { 3258 var curTn = G.GOM.items[i]; 3259 if( curTn.row >= firstRow && curTn.row < lastRow ) { 3260 if( firstTn == -1 ) { 3261 G.GOM.displayInterval.from = i; 3262 firstTn = i; 3263 } 3264 G.GOM.displayInterval.len++; 3265 } 3266 } 3267 } 3268 break; 3269 case 'MOREBUTTON': 3270 if( G.layout.support.rows ) { 3271 var nbTn = G.GOM.items.length; 3272 var lastRow = G.O.galleryDisplayMoreStep * (G.GOM.displayedMoreSteps+1); 3273 G.GOM.displayInterval.len = 0; 3274 for( var i = 0; i < nbTn ; i++ ) { 3275 var curTn = G.GOM.items[i]; 3276 if( curTn.row < lastRow ) { 3277 G.GOM.displayInterval.len++; 3278 } 3279 } 3280 } 3281 break; 3282 case 'ROWS': 3283 if( G.layout.support.rows ) { 3284 var nbTn = G.GOM.items.length; 3285 var lastRow = G.galleryMaxRows.Get(); 3286 if( G.galleryLastRowFull.Get() && G.GOM.lastFullRow != -1 ) { 3287 if( lastRow > (G.GOM.lastFullRow + 1) ) { 3288 lastRow = G.GOM.lastFullRow + 1; 3289 } 3290 } 3291 G.GOM.displayInterval.len = 0; 3292 for( var i = 0; i < nbTn ; i++ ) { 3293 var curTn = G.GOM.items[i]; 3294 if( curTn.row < lastRow ) { 3295 G.GOM.displayInterval.len++; 3296 } 3297 } 3298 } 3299 break; 3300 default: 3301 case 'FULLCONTENT': 3302 if( G.layout.support.rows && G.galleryLastRowFull.Get() && G.GOM.lastFullRow != -1 ) { 3303 var nbTn = G.GOM.items.length; 3304 var lastRow = G.GOM.lastFullRow + 1; 3305 G.GOM.displayInterval.len = 0; 3306 for( var i = 0; i < nbTn ; i++ ) { 3307 var curTn = G.GOM.items[i]; 3308 if( curTn.row < lastRow ) { 3309 G.GOM.displayInterval.len++; 3310 } 3311 } 3312 } 3313 break; 3314 } 3315 } 3316 3317 3318 // RENDER THE GALLERY 3319 function GalleryRender( albumIdx ) { 3320 TriggerCustomEvent('galleryRenderStart'); 3321 3322 clearTimeout(G.GOM.slider.timerID); 3323 G.GOM.slider.hostIdx = -1; // disabled slider on thumbnail 3324 3325 var fu=G.O.fnGalleryRenderStart; 3326 if( fu !== null ) { 3327 typeof fu == 'function' ? fu(albumIdx) : window[fu](albumIdx); 3328 } 3329 3330 G.layout.SetEngine(); 3331 G.galleryResizeEventEnabled = false; 3332 G.GOM.albumIdx = -1; 3333 G.GOM.lastDisplayedIdx = -1; 3334 3335 // pagination 3336 if( G.$E.conTnBottom !== undefined ) { 3337 // G.$E.conTnBottom.children().remove(); 3338 G.$E.conTnBottom.empty(); 3339 } 3340 3341 // navigation toolbar (breadcrumb + tag filters) 3342 GalleryNavigationBar(albumIdx); 3343 3344 if( G.GOM.firstDisplay ) { 3345 G.GOM.firstDisplay = false; 3346 var d = Date.now()-G.GOM.firstDisplayTime; 3347 if( d < G.O.galleryRenderDelay ) { 3348 // setTimeout( function() { GalleryRenderPart1( albumIdx )}, G.O.galleryRenderDelay-d ); 3349 requestTimeout( function() { GalleryRenderPart1( albumIdx )}, G.O.galleryRenderDelay-d ); 3350 } 3351 else { 3352 GalleryRenderPart1( albumIdx ); 3353 } 3354 G.O.galleryRenderDelay = 0; 3355 3356 } 3357 else { 3358 var hideNavigationBar = false; 3359 if( G.GOM.navigationBar.$newContent.children().length == 0 ) { 3360 hideNavigationBar = true; 3361 } 3362 3363 // hide everything 3364 var tweenable = new NGTweenable(); 3365 tweenable.tween({ 3366 from: { 'opacity': 1 }, 3367 to: { 'opacity': 0 }, 3368 duration: 300, 3369 easing: 'easeInQuart', 3370 attachment: { h: hideNavigationBar }, 3371 step: function (state, att) { 3372 // window.ng_draf( function() { 3373 G.$E.conTnParent.css({'opacity': state.opacity }); 3374 if( att.h ) { 3375 G.$E.conNavigationBar.css({ 'opacity': state.opacity }); 3376 } 3377 // }); 3378 }, 3379 finish: function (state, att) { 3380 // window.ng_draf( function() { 3381 if( att.h ) { 3382 G.$E.conNavigationBar.css({ 'opacity': 0, 'display': 'none' }); 3383 } 3384 // scroll to top of the gallery if needed 3385 var galleryOTop = G.$E.base.offset().top; 3386 if( galleryOTop < G.GOM.cache.viewport.t ) { 3387 // jQuery('html, body').animate({scrollTop: galleryOTop}, 200); 3388 jQuery('html, body').animate({scrollTop: galleryOTop}, 500, "linear", function() { 3389 GalleryRenderPart1( albumIdx ); 3390 }); 3391 } 3392 else { 3393 GalleryRenderPart1( albumIdx ); 3394 } 3395 // }); 3396 } 3397 }); 3398 } 3399 } 3400 3401 3402 function GalleryRenderPart1( albumIdx ) { 3403 // display new navigation bar 3404 var oldN = G.$E.conNavigationBar.children().length; 3405 G.$E.conNavigationBar.empty(); 3406 G.GOM.navigationBar.$newContent.children().clone(true,true).appendTo(G.$E.conNavigationBar); 3407 // G.GOM.navigationBar.$newContent.appendTo(G.$E.conNavigationBar); 3408 if( G.$E.conNavigationBar.children().length > 0 && oldN == 0 ) { 3409 G.$E.conNavigationBar.css({ 'opacity': 0, 'display': 'block' }); 3410 var tweenable = new NGTweenable(); 3411 tweenable.tween({ 3412 from: { opacity: 0 }, 3413 to: { opacity: 1 }, 3414 duration: 200, 3415 easing: 'easeInQuart', 3416 step: function (state) { 3417 // window.ng_draf( function() { 3418 G.$E.conNavigationBar.css( state ); 3419 // }); 3420 }, 3421 finish: function (state) { 3422 // window.ng_draf( function() { 3423 G.$E.conNavigationBar.css({ 'opacity': 1 }); 3424 // display gallery 3425 // GalleryRenderPart2( albumIdx ); 3426 // setTimeout(function(){ GalleryRenderPart2(albumIdx) }, 60); 3427 requestTimeout(function(){ GalleryRenderPart2(albumIdx) }, 60); 3428 // }); 3429 } 3430 }); 3431 } 3432 else { 3433 // display gallery 3434 // GalleryRenderPart2( albumIdx ); 3435 // setTimeout(function(){ GalleryRenderPart2(albumIdx) }, 60); 3436 requestTimeout(function(){ GalleryRenderPart2(albumIdx) }, 60); 3437 } 3438 3439 } 3440 3441 // Gallery render part 2 -> remove all thumbnails 3442 function GalleryRenderPart2(albumIdx) { 3443 G.GOM.lastZIndex = parseInt(G.$E.base.css('z-index')); 3444 if( isNaN(G.GOM.lastZIndex) ) { 3445 G.GOM.lastZIndex=0; 3446 } 3447 G.$E.conTnParent.css({ 'opacity': 0 }); 3448 G.$E.conTn.off().empty(); 3449 var l = G.I.length; 3450 for( var i = 0; i < l ; i++ ) { 3451 // reset each item 3452 var item = G.I[i]; 3453 item.hovered = false; 3454 item.$elt = null; 3455 item.$Elts = []; 3456 item.eltTransform = []; 3457 item.eltFilter = []; 3458 item.width = 0; 3459 item.height = 0; 3460 item.left = 0; 3461 item.top = 0; 3462 item.resizedContentWidth = 0; 3463 item.resizedContentHeight = 0; 3464 item.thumbnailImgRevealed = false; 3465 } 3466 3467 if( G.CSStransformName == null ) { 3468 G.$E.conTn.css('left', '0px'); 3469 } 3470 else { 3471 // G.$E.conTn.css( G.CSStransformName, 'translateX(0px)'); 3472 G.$E.conTn.css( G.CSStransformName, 'none'); 3473 } 3474 3475 // setTimeout(function(){ GalleryRenderPart3(albumIdx) }, 60); 3476 requestTimeout(function(){ GalleryRenderPart3(albumIdx) }, 60); 3477 // GalleryRenderPart3(albumIdx); 3478 3479 } 3480 3481 // Gallery render part 2 -> start building the new gallery 3482 function GalleryRenderPart3(albumIdx) { 3483 var d = new Date(); 3484 3485 G.$E.conTnParent.css( 'opacity', 1); 3486 3487 G.GOM.items = []; 3488 G.GOM.displayedMoreSteps = 0; 3489 // retrieve label height 3490 if( G.O.thumbnailLabel.get('position') == 'onBottom' ) { 3491 // retrieve height each time because size can change depending on thumbnail's settings 3492 G.tn.labelHeight[G.GOM.curNavLevel]=ThumbnailGetLabelHeight(); 3493 } 3494 else { 3495 G.tn.labelHeight[G.GOM.curNavLevel]=0; 3496 } 3497 G.GOM.albumIdx=albumIdx; 3498 3499 TriggerCustomEvent('galleryRenderEnd'); 3500 var fu=G.O.fnGalleryRenderEnd; 3501 if( fu !== null ) { 3502 typeof fu == 'function' ? fu(albumIdx) : window[fu](albumIdx); 3503 } 3504 3505 // Step 1: populate GOM 3506 if( GalleryPopulateGOM() ) { 3507 3508 // step 2: calculate layout 3509 GallerySetLayout(); 3510 3511 // step 3: display gallery 3512 GalleryAppear(); 3513 // GalleryDisplay( false ); 3514 GalleryDisplayPart1( false ); 3515 // setTimeout(function(){ GalleryDisplayPart2( false ) }, 120); 3516 requestTimeout(function(){ GalleryDisplayPart2( false ) }, 120); 3517 } 3518 else { 3519 G.galleryResizeEventEnabled = true; 3520 } 3521 3522 if( G.O.debugMode ) { console.log('GalleryRenderPart3: '+ (new Date()-d)); } 3523 3524 } 3525 3526 3527 // Resize the gallery 3528 function GalleryResize() { 3529 var d = new Date(); 3530 G.galleryResizeEventEnabled = false; 3531 // G.GOM.cache.areaWidth=G.$E.conTnParent.width(); 3532 if( GallerySetLayout() == false ) { 3533 G.galleryResizeEventEnabled = true; 3534 if( G.O.debugMode ) { console.log('GalleryResize1: '+ (new Date()-d)); } 3535 return; 3536 } 3537 if( G.O.debugMode ) { console.log('GalleryResizeSetLayout: '+ (new Date()-d)); } 3538 3539 GalleryDisplayPart1( false ); 3540 GalleryDisplayPart2( false ); 3541 3542 if( G.O.debugMode ) { console.log('GalleryResizeFull: '+ (new Date()-d)); } 3543 } 3544 3545 3546 3547 // copy items (album content) to GOM 3548 function GalleryPopulateGOM() { 3549 3550 var preloadImages = ''; 3551 var imageSizeRequested = false; 3552 var albumID = G.I[G.GOM.albumIdx].GetID(); 3553 var l = G.I.length; 3554 var cnt = 0; 3555 3556 for( var idx = 0; idx < l; idx++ ) { 3557 var item = G.I[idx]; 3558 // check album 3559 if( item.isToDisplay(albumID) ) { 3560 var w = item.thumbImg().width; 3561 var h = item.thumbImg().height; 3562 // if unknown image size and layout is not grid --> we need to retrieve the size of the images 3563 if( G.layout.prerequisite.imageSize && ( w == 0 || h == 0) ) { 3564 // if( true ) { 3565 imageSizeRequested = true; 3566 preloadImages += '<img src="'+item.thumbImg().src+'" data-idx="'+cnt+'" data-albumidx="'+G.GOM.albumIdx+'">'; 3567 } 3568 3569 // set default size if required 3570 if( h == 0 ) { 3571 h = G.tn.defaultSize.getHeight(); 3572 } 3573 if( w == 0 ) { 3574 w = G.tn.defaultSize.getWidth(); 3575 } 3576 var tn = new G.GOM.GTn(idx, w, h); 3577 G.GOM.items.push(tn); 3578 cnt++; 3579 } 3580 } 3581 3582 TriggerCustomEvent('galleryObjectModelBuilt'); 3583 var fu = G.O.fnGalleryObjectModelBuilt; 3584 if( fu !== null ) { 3585 typeof fu == 'function' ? fu() : window[fu](); 3586 } 3587 3588 if( imageSizeRequested ) { 3589 // preload images to retrieve their size and then resize the gallery (=GallerySetLayout()+ GalleryDisplay()) 3590 var $newImg = jQuery(preloadImages); 3591 var gi_imgLoad = ngimagesLoaded( $newImg ); 3592 $newImg = null; 3593 gi_imgLoad.on( 'progress', function( instance, image ) { 3594 3595 if( image.isLoaded ) { 3596 var idx = image.img.getAttribute('data-idx'); 3597 var albumIdx = image.img.getAttribute('data-albumidx'); 3598 if( albumIdx == G.GOM.albumIdx ) { 3599 // ignore event if not on current album 3600 var curTn = G.GOM.items[idx]; 3601 curTn.imageWidth = image.img.naturalWidth; 3602 curTn.imageHeight = image.img.naturalHeight; 3603 var item = G.I[curTn.thumbnailIdx]; 3604 item.thumbs.width[G.GOM.curNavLevel][G.GOM.curWidth] = curTn.imageWidth; 3605 item.thumbs.height[G.GOM.curNavLevel][G.GOM.curWidth] = curTn.imageHeight; 3606 3607 // resize the gallery 3608 G.GalleryResizeThrottled(); 3609 3610 // set the retrieved size to all levels with same configuration 3611 var object = item.thumbs.width.l1; 3612 for (var property in object) { 3613 if (object.hasOwnProperty(property)) { 3614 if( property != G.GOM.curWidth ) { 3615 if( G.tn.settings.width.l1[property] == G.tn.settings.getW() && G.tn.settings.height.l1[property] == G.tn.settings.getH() ) { 3616 item.thumbs.width.l1[property] = curTn.imageWidth; 3617 item.thumbs.height.l1[property] = curTn.imageHeight; 3618 } 3619 } 3620 } 3621 } 3622 object = item.thumbs.width.lN; 3623 for (var property in object) { 3624 if (object.hasOwnProperty(property)) { 3625 if( property != G.GOM.curWidth ) { 3626 if( G.tn.settings.width.lN[property] == G.tn.settings.getW() && G.tn.settings.height.lN[property] == G.tn.settings.getH() ) { 3627 item.thumbs.width.lN[property] = curTn.imageWidth; 3628 item.thumbs.height.lN[property] = curTn.imageHeight; 3629 } 3630 } 3631 } 3632 } 3633 } 3634 } 3635 }); 3636 G.galleryResizeEventEnabled = true; 3637 return false; 3638 } 3639 else { 3640 return true; 3641 } 3642 3643 } 3644 3645 //----- Calculate the layout of the thumbnails 3646 function GallerySetLayout() { 3647 var r = true; 3648 // width of the available area 3649 G.GOM.cache.areaWidth = G.$E.conTnParent.width(); 3650 G.GOM.displayArea = { width:0, height:0 }; 3651 3652 switch( G.layout.engine ) { 3653 case 'JUSTIFIED': 3654 r = GallerySetLayoutWidthtAuto(); 3655 break; 3656 case 'CASCADING': 3657 r = GallerySetLayoutHeightAuto(); 3658 break; 3659 case 'MOSAIC': 3660 r = GallerySetLayoutMosaic(); 3661 break; 3662 case 'GRID': 3663 default: 3664 r = GallerySetLayoutGrid(); 3665 break; 3666 } 3667 3668 TriggerCustomEvent('galleryLayoutApplied'); 3669 var fu = G.O.fnGalleryLayoutApplied; 3670 if( fu !== null ) { 3671 typeof fu == 'function' ? fu() : window[fu](); 3672 } 3673 return r; 3674 3675 } 3676 3677 3678 //----- CASCADING LAYOUT 3679 function GallerySetLayoutHeightAuto() { 3680 var curCol = 0, 3681 areaWidth = G.GOM.cache.areaWidth, 3682 curRow = 0, 3683 colHeight = [], 3684 maxCol = NbThumbnailsPerRow(areaWidth), 3685 gutterWidth = 0, 3686 gutterHeight = G.tn.opt.Get('gutterHeight'); 3687 var w = 0; 3688 var scaleFactor = 1; 3689 var tnWidth = G.tn.defaultSize.getOuterWidth(); 3690 var nbTn = G.GOM.items.length; 3691 var curPosY = 0; 3692 3693 if( G.O.thumbnailAlignment == 'justified' ) { 3694 maxCol = Math.min(maxCol, nbTn); 3695 gutterWidth = ( maxCol == 1 ? 0 : (areaWidth - (maxCol * tnWidth) ) / (maxCol - 1) ); 3696 } 3697 else { 3698 gutterWidth=G.tn.opt.Get('gutterWidth'); 3699 } 3700 3701 3702 var borderWidth = G.tn.borderWidth * 2; 3703 var borderHeight = G.tn.borderHeight * 2; 3704 3705 G.GOM.lastFullRow=-1; // feature disabled 3706 3707 // Retrieve the real used width of the area (the evaluation is based on the content of the first line) 3708 if( G.O.thumbnailAlignment == 'fillWidth' ) { 3709 // fillWidth --> evaluate scale factor and number of columns 3710 var totalGutterWidth = (maxCol - 1) * gutterWidth; 3711 scaleFactor = (areaWidth - totalGutterWidth) / (maxCol * tnWidth); 3712 if( scaleFactor > 1 ) { 3713 maxCol++; // add one column and re-evaluate the scale factor 3714 } 3715 totalGutterWidth = (maxCol - 1) * gutterWidth; 3716 scaleFactor = Math.min( (areaWidth - totalGutterWidth) / (maxCol*tnWidth), 1); // no upscale 3717 } 3718 3719 3720 tnWidth = tnWidth * scaleFactor; 3721 var contentWidth = tnWidth - borderWidth; 3722 3723 // loop to position the thumbnails, and set their size 3724 var baseHeight = G.tn.opt.Get('baseGridHeight') * scaleFactor; 3725 for( var i = 0; i < nbTn ; i++ ) { 3726 var curTn = G.GOM.items[i]; 3727 if( curTn.deleted == true ) { break; } // item is logically deleted 3728 if( curTn.imageHeight > 0 && curTn.imageWidth > 0 ) { 3729 var curPosX = 0, 3730 curPosY = 0; 3731 var imageRatio = curTn.imageHeight / curTn.imageWidth; 3732 // curTn.resizedContentWidth = tnWidth - borderWidth; 3733 curTn.resizedContentWidth = contentWidth; 3734 curTn.resizedContentHeight = curTn.resizedContentWidth * imageRatio; 3735 if( baseHeight > 0 ) { 3736 // grid based vertical position 3737 var t= Math.max( Math.trunc(curTn.resizedContentHeight/baseHeight), 1) ; 3738 curTn.resizedContentHeight = baseHeight * t + ((t-1)*(borderHeight+gutterHeight)); 3739 } 3740 3741 curTn.height = curTn.resizedContentHeight + borderHeight + G.tn.labelHeight.get(); 3742 curTn.width = tnWidth; 3743 curTn.row = 0; 3744 3745 if( curRow == 0 ) { 3746 // first row 3747 curPosX = curCol * (tnWidth + gutterWidth); 3748 colHeight[curCol] = curTn.height + gutterHeight; 3749 3750 curCol++; 3751 if( curCol >= maxCol ) { 3752 curCol = 0; 3753 curRow++; 3754 } 3755 } 3756 else { 3757 var c=0, 3758 minColHeight=colHeight[0]; 3759 for( var j = 1; j < maxCol; j++) { 3760 if( (colHeight[j] + 5) < minColHeight ) { // +5 --> threshold 3761 minColHeight = colHeight[j]; 3762 c = j; 3763 //break; 3764 } 3765 } 3766 curPosY = colHeight[c]; 3767 curPosX = c * (tnWidth + gutterWidth); 3768 colHeight[c] = curPosY + curTn.height + gutterHeight; 3769 } 3770 3771 var x = curPosX; 3772 if( G.O.RTL) { 3773 x= w - curPosX - tnWidth; 3774 } 3775 3776 curTn.left = x; 3777 curTn.top = curPosY; 3778 } 3779 } 3780 3781 G.GOM.displayArea.width= maxCol * (tnWidth + gutterWidth) - gutterWidth; 3782 return true; 3783 } 3784 3785 3786 //----- JUSTIFIED LAYOUT 3787 function GallerySetLayoutWidthtAuto() { 3788 var curWidth = 0, 3789 areaWidth = G.GOM.cache.areaWidth, 3790 lastPosX = 0, 3791 curPosY = 0, 3792 rowLastItem = [], 3793 rowNum = 0, 3794 rowHeight = [], 3795 bNewRow = false, 3796 cnt = 0, 3797 gutterWidth = G.tn.opt.Get('gutterWidth'), 3798 gutterHeight = G.tn.opt.Get('gutterHeight'); 3799 // by grief-of-these-days 3800 var maxRowHeightVertical = 0; // max height of a row with vertical thumbs 3801 var maxRowHeightHorizontal = 0; // max height of a row with horizontal thumbs 3802 var rowHasVertical = false; // current row has vertical thumbs 3803 var rowHasHorizontal = false; // current row has horizontal thumbs 3804 3805 var tnHeight = G.tn.defaultSize.getOuterHeight(); 3806 var borderWidth = G.tn.borderWidth * 2; 3807 var borderHeight = G.tn.borderHeight * 2; 3808 var nbTnInCurrRow = 1; 3809 var nbTn = G.GOM.items.length; 3810 3811 // first loop --> retrieve each row image height 3812 for( var i = 0; i < nbTn ; i++ ) { 3813 var curTn = G.GOM.items[i]; 3814 if( curTn.deleted == true ) { break; } // item is logically deleted 3815 if( curTn.imageWidth > 0 ) { 3816 var imageRatio = curTn.imageWidth / curTn.imageHeight; 3817 var imageWidth = Math.floor( tnHeight * imageRatio ); 3818 3819 if( bNewRow ) { 3820 bNewRow = false; 3821 rowNum++; 3822 curWidth = 0; 3823 rowHasVertical = false; 3824 rowHasHorizontal = false; 3825 nbTnInCurrRow = 1; 3826 } 3827 // by grief-of-these-days 3828 if( curTn.imageHeight > curTn.imageWidth ) { 3829 rowHasVertical = true; 3830 } 3831 else { 3832 rowHasHorizontal = true; 3833 } 3834 3835 if( (curWidth + gutterWidth + imageWidth) < (areaWidth - (nbTnInCurrRow * borderWidth)) ) { 3836 // enough place left in the current row 3837 curWidth += imageWidth + gutterWidth; 3838 rowHeight[rowNum] = tnHeight; 3839 3840 // prevent incomplete row from being heigher than the previous ones. 3841 // by grief-of-these-days 3842 var rowHeightLimit = Math.max(rowHasVertical ? maxRowHeightVertical : 0, rowHasHorizontal ? maxRowHeightHorizontal : 0); 3843 if( rowHeightLimit > 0 ) { 3844 rowHeight[rowNum] = Math.min(rowHeight[rowNum], rowHeightLimit); 3845 } 3846 3847 rowLastItem[rowNum] = i; 3848 } 3849 else { 3850 // new row after current item --> we need to adujet the row height to have enough space for the current thumbnail 3851 curWidth += gutterWidth+imageWidth; 3852 var ratio = (areaWidth - nbTnInCurrRow * borderWidth) / curWidth; 3853 var rH = Math.floor(tnHeight * ratio); 3854 rowHeight[rowNum] = rH; 3855 3856 // save the max row height for each thumb orientation. 3857 // by grief-of-these-days 3858 if( rowHasVertical ) { 3859 maxRowHeightVertical = Math.max( maxRowHeightVertical, rH ); 3860 } 3861 if( rowHasHorizontal ) { 3862 maxRowHeightHorizontal = Math.max( maxRowHeightHorizontal, rH ); 3863 } 3864 3865 rowLastItem[rowNum] = i; 3866 bNewRow = true; 3867 } 3868 cnt++; 3869 nbTnInCurrRow++; 3870 } 3871 } 3872 3873 rowNum = 0; 3874 curPosY = 0; 3875 lastPosX = 0; 3876 cnt = 0; 3877 3878 G.GOM.lastFullRow = 0; // display at leat 1 row (even if not full) 3879 3880 // second loop --> calculate each thumbnail size 3881 for( var i = 0; i < nbTn ; i++ ) { 3882 var curTn = G.GOM.items[i]; 3883 if( curTn.imageWidth > 0 ) { 3884 var imageRatio = curTn.imageWidth / curTn.imageHeight; 3885 var imageWidth = Math.floor( imageRatio * rowHeight[rowNum] ); // border is already NOT included 3886 3887 if( i == rowLastItem[rowNum] ) { 3888 // row last item --> adjust image width because of rounding problems 3889 if( rowLastItem.length != (rowNum+1) ) { 3890 // last item in current row -> use the full remaining width 3891 imageWidth = areaWidth - lastPosX - borderWidth; 3892 } 3893 else { 3894 // very last item (on the last row) 3895 if( (lastPosX + gutterWidth + imageWidth + borderWidth ) > areaWidth ) { 3896 // reduce size if image is wider as the remaining space 3897 imageWidth = areaWidth - lastPosX - borderWidth; 3898 } 3899 } 3900 } 3901 3902 var rh = parseInt( rowHeight[rowNum] ); 3903 imageWidth = parseInt( imageWidth ); 3904 3905 // thumbnail image size 3906 curTn.resizedContentWidth = imageWidth; 3907 curTn.resizedContentHeight = rh; 3908 // thumbnail position and size 3909 curTn.width = imageWidth + borderWidth; 3910 curTn.height= rh + G.tn.labelHeight.get() + borderHeight; 3911 curTn.row = rowNum; 3912 3913 curTn.top = curPosY; 3914 var x = lastPosX; 3915 if( G.O.RTL) { 3916 x = areaWidth - lastPosX - curTn.width ; 3917 } 3918 curTn.left = x; 3919 3920 lastPosX += curTn.width + gutterWidth; 3921 3922 if( i == rowLastItem[rowNum] ) { 3923 // start a new row 3924 curPosY += curTn.height + gutterHeight; 3925 G.GOM.lastFullRow = rowNum - 1; 3926 rowNum++; 3927 lastPosX = 0; 3928 } 3929 cnt++; 3930 } 3931 else { 3932 return false; 3933 } 3934 } 3935 3936 if( false ) { 3937 var newTop = 0; 3938 if( typeof GOMidx !== 'undefined' ) { 3939 // hover effect on gallery (vs on thumbnail) --> experimental / not used 3940 if( G.GOM.albumIdx != -1 ) { 3941 var hoveredTn = G.GOM.items[GOMidx]; 3942 var item = G.I[hoveredTn.thumbnailIdx]; 3943 3944 // hovered thumbnail 3945 hoveredTn.width += 40; 3946 hoveredTn.height += 40; 3947 // todo : left 3948 3949 for( var i = 0; i < nbTn ; i++ ) { 3950 var curTn = G.GOM.items[i]; 3951 if( curTn.imageWidth > 0 ) { 3952 if( curTn.row == hoveredTn.row ) { 3953 // hovered row 3954 newTop = 40; 3955 if( hoveredTn.thumbnailIdx != curTn.thumbnailIdx ) { 3956 // not hovered thumbnail 3957 // curTn.resizedContentWidth+=10; 3958 // curTn.resizedContentHeight+=20; 3959 // curTn.width+=10; 3960 curTn.top += 30; 3961 curTn.width -= 20; 3962 curTn.height -= 20; 3963 } 3964 } 3965 else { 3966 // not hovered row 3967 if( curTn.row == 0 ) { 3968 // first row 3969 } 3970 else { 3971 curTn.top += newTop; 3972 } 3973 } 3974 } 3975 } 3976 } 3977 } 3978 } 3979 3980 G.GOM.displayArea.width = areaWidth; 3981 return true; 3982 } 3983 3984 3985 //----- MOSAIC LAYOUT 3986 // Grid using a user defined pattern layout 3987 // With this layout, a pattern definition is handeld a row 3988 function GallerySetLayoutMosaic() { 3989 var areaWidth = G.GOM.cache.areaWidth; 3990 var gutterHeight = G.tn.opt.Get('gutterHeight'); 3991 var gutterWidth = G.tn.opt.Get('gutterWidth'); 3992 var borderWidth = G.tn.borderWidth * 2; 3993 var borderHeight = G.tn.borderHeight * 2; 3994 3995 var nbTn = G.GOM.items.length; 3996 var row = 0; 3997 var h = 0; 3998 var n = 0; 3999 4000 4001 // first loop: evaluate the gallery width based on the first row 4002 var nbCols = 0; 4003 var maxW = 0; 4004 var mosaicPattern = G.tn.settings.getMosaic(); 4005 for( var i = 0; i < nbTn ; i++ ) { 4006 var curPatternElt = mosaicPattern[n]; 4007 4008 var cLeft = (curPatternElt.c - 1) * G.tn.defaultSize.getOuterWidth() + (curPatternElt.c - 1) * gutterWidth; 4009 var cWidth = curPatternElt.w * G.tn.defaultSize.getOuterWidth() + (curPatternElt.w - 1) * gutterWidth; 4010 4011 maxW=Math.max(maxW, cLeft + cWidth ); 4012 4013 nbCols=Math.max(nbCols, (curPatternElt.c - 1) + curPatternElt.w ); 4014 4015 n++; 4016 if( n >= mosaicPattern.length ) { 4017 // end of pattern 4018 break; 4019 } 4020 } 4021 var totalGutterWidth = (nbCols - 1) * gutterWidth; 4022 var scaleFactor = Math.min( (areaWidth - totalGutterWidth ) / ( maxW - totalGutterWidth ), 1); 4023 4024 // second loop: position all the thumbnails based on the layout pattern 4025 row = 0; 4026 n = 0; 4027 var mosaicPattern = G.tn.settings.getMosaic(); 4028 for( var i = 0; i < nbTn ; i++ ) { 4029 var curTn = G.GOM.items[i]; 4030 var curPatternElt = mosaicPattern[n]; 4031 4032 curTn.top = (curPatternElt.r - 1) * G.tn.defaultSize.getOuterHeight()*scaleFactor + (curPatternElt.r - 1) * gutterHeight + row * h + (G.tn.labelHeight.get()*(curPatternElt.r-1)) ; 4033 if( row > 0 ) { 4034 curTn.top += gutterHeight; 4035 } 4036 4037 curTn.left = (curPatternElt.c - 1) * G.tn.defaultSize.getOuterWidth()*scaleFactor + (curPatternElt.c - 1) * gutterWidth; 4038 4039 curTn.height = curPatternElt.h * G.tn.defaultSize.getOuterHeight() * scaleFactor + (curPatternElt.h - 1) * gutterHeight + (G.tn.labelHeight.get() * curPatternElt.h); 4040 curTn.resizedContentHeight = curTn.height - G.tn.labelHeight.get() - borderHeight; 4041 4042 curTn.width = curPatternElt.w * G.tn.defaultSize.getOuterWidth()*scaleFactor + (curPatternElt.w - 1) * gutterWidth; 4043 curTn.resizedContentWidth = curTn.width - borderWidth ; 4044 4045 curTn.row = row; 4046 if( row == 0 ) { 4047 h=Math.max(h, curTn.top + curTn.height); 4048 } 4049 4050 n++; 4051 if( n >= mosaicPattern.length ) { 4052 // end pattern -> new line 4053 n = 0; 4054 row++; 4055 } 4056 } 4057 4058 G.GOM.displayArea.width = (maxW - totalGutterWidth) * scaleFactor + totalGutterWidth; 4059 return true; 4060 } 4061 4062 4063 4064 // --- GRID LAYOUT 4065 function GallerySetLayoutGrid() { 4066 var curPosX= 0, 4067 curPosY= 0, 4068 areaWidth= G.GOM.cache.areaWidth, 4069 gutterWidth= 0, 4070 gutterHeight= G.tn.opt.Get('gutterHeight'), 4071 maxCol= NbThumbnailsPerRow(areaWidth), 4072 w= 0, 4073 cols= [], 4074 curCol= 0, 4075 newAreaWidth = areaWidth, 4076 tnWidth= G.tn.defaultSize.getOuterWidth(); 4077 var scaleFactor = 1; 4078 var nbTn= G.GOM.items.length; 4079 var borderWidth = G.tn.borderWidth * 2; 4080 var borderHeight =G.tn.borderHeight * 2; 4081 4082 // retrieve gutter width 4083 if( G.O.thumbnailAlignment == 'justified' ) { 4084 maxCol = Math.min( maxCol, nbTn); 4085 gutterWidth = (maxCol==1 ? 0 : (areaWidth-(maxCol*tnWidth))/(maxCol-1)); 4086 } 4087 else { 4088 gutterWidth = G.tn.opt.Get('gutterWidth'); 4089 } 4090 4091 // first loop to retrieve the real used width of the area (the evaluation is based on the content of the first line) 4092 // Retrieve the real used width of the area (the evaluation is based on the content of the first line) 4093 if( G.O.RTL || G.O.thumbnailAlignment == 'fillWidth' ) { 4094 // scaled --> evaluate scale factor and number of columns 4095 var totalGutterWidth = (maxCol-1) * gutterWidth; 4096 scaleFactor = (areaWidth - totalGutterWidth) / (maxCol*tnWidth); 4097 if( scaleFactor > 1 ) { 4098 maxCol++; // add one column and re-evaluate the scale factor 4099 } 4100 totalGutterWidth = (maxCol-1) * gutterWidth; 4101 scaleFactor = Math.min( (areaWidth - totalGutterWidth) / (maxCol*tnWidth), 1); // no upscale 4102 newAreaWidth = (maxCol*tnWidth) + totalGutterWidth; 4103 } 4104 4105 4106 G.GOM.lastFullRow = 0 ; // display at leat 1 row (even if not full) 4107 var lastPosY = 0; 4108 var row = 0; 4109 4110 tnWidth = tnWidth * scaleFactor; 4111 var contentWidth = tnWidth - borderWidth; 4112 var tnHeight = G.tn.defaultSize.getOuterHeight() * scaleFactor + G.tn.labelHeight.get(); 4113 var contentHeight = G.tn.defaultSize.getOuterHeight() * scaleFactor - borderHeight; 4114 4115 // loop to position and to set size of all thumbnails 4116 for( var i = 0; i < nbTn ; i++ ) { 4117 if( curPosY == 0 ) { 4118 curPosX = curCol * (tnWidth + gutterWidth) 4119 cols[curCol] = curPosX; 4120 w = curPosX + tnWidth; 4121 } 4122 else { 4123 curPosX = cols[curCol]; 4124 } 4125 4126 var x = curPosX; 4127 if( G.O.RTL ) { 4128 x = parseInt(newAreaWidth) - curPosX - tnWidth; 4129 } 4130 4131 // MANDATORY : set thumbnail position AND size 4132 var curTn=G.GOM.items[i]; 4133 curTn.top = curPosY; 4134 curTn.left = x; 4135 curTn.height = tnHeight; 4136 curTn.width = tnWidth; 4137 // image size 4138 if( G.O.thumbnailAlignment == 'fillWidth' ) { 4139 curTn.resizedContentWidth = contentWidth; 4140 curTn.resizedContentHeight = contentHeight; 4141 } 4142 curTn.row = row; 4143 lastPosY = curPosY; 4144 4145 curCol++; 4146 if( curCol >= maxCol ){ 4147 // new line 4148 curCol = 0; 4149 curPosY += tnHeight + gutterHeight; 4150 G.GOM.lastFullRow = row; 4151 row++; 4152 } 4153 } 4154 G.GOM.displayArea.width = w; 4155 return true; 4156 } 4157 4158 4159 //----- Display the thumbnails according to the calculated layout 4160 function GalleryDisplayPart1( forceTransition ) { 4161 if( G.CSStransformName == null ) { 4162 G.$E.conTn.css( 'left' , '0px'); 4163 } 4164 else { 4165 G.$E.conTn.css( G.CSStransformName , 'none'); 4166 } 4167 CacheViewport(); 4168 } 4169 4170 function CacheViewport() { 4171 G.GOM.cache.viewport = getViewport(); 4172 G.GOM.cache.areaWidth = G.$E.conTnParent.width(); 4173 G.GOM.cache.containerOffset = G.$E.conTnParent.offset(); 4174 } 4175 4176 4177 4178 function GalleryDisplayPart2( forceTransition ) { 4179 4180 var nbTn = G.GOM.items.length; 4181 G.GOM.itemsDisplayed = 0; 4182 var threshold = 50; 4183 var cnt = 0; // counter for delay between each thumbnail display 4184 4185 4186 GalleryRenderGetInterval(); 4187 4188 for( var i = 0; i < nbTn ; i++ ) { 4189 var curTn = G.GOM.items[i]; 4190 if( i >= G.GOM.displayInterval.from && cnt < G.GOM.displayInterval.len ) { 4191 curTn.inDisplayArea = true; 4192 if( forceTransition ) { 4193 curTn.neverDisplayed = true; 4194 } 4195 G.GOM.itemsDisplayed++; 4196 cnt++; 4197 } 4198 else{ 4199 curTn.inDisplayArea = false; 4200 } 4201 } 4202 4203 // bottom of the gallery (pagination, more button...) 4204 GalleryBottomManage(); 4205 4206 var tnToDisplay = []; 4207 var tnToReDisplay = []; 4208 4209 G.GOM.clipArea.top = -1; 4210 cnt = 0 ; 4211 var lastTnIdx = -1; 4212 G.GOM.clipArea.height = 0; 4213 // NOTE: loop always the whole GOM.items --> in case an already displayed thumbnail needs to be removed 4214 for( var i = 0; i < nbTn ; i++ ) { 4215 var curTn = G.GOM.items[i]; 4216 if( curTn.inDisplayArea ) { 4217 if( G.GOM.clipArea.top == -1 ) { 4218 G.GOM.clipArea.top = curTn.top; 4219 } 4220 if( (curTn.top - G.GOM.clipArea.top) <= -1 ) { 4221 // with mosaic layout, the first thumbnail may not give the top position 4222 G.GOM.clipArea.top = curTn.top; 4223 } 4224 4225 G.GOM.clipArea.height = Math.max( G.GOM.clipArea.height, curTn.top-G.GOM.clipArea.top + curTn.height); 4226 4227 if( curTn.neverDisplayed ) { 4228 // thumbnail is not displayed -> check if in viewport to display or not 4229 var top = G.GOM.cache.containerOffset.top + (curTn.top - G.GOM.clipArea.top); 4230 // var left=containerOffset.left+curTn.left; 4231 if( (top + curTn.height) >= (G.GOM.cache.viewport.t - threshold) && top <= (G.GOM.cache.viewport.t + G.GOM.cache.viewport.h + threshold) ) { 4232 // build thumbnail 4233 var item = G.I[curTn.thumbnailIdx]; 4234 if( item.$elt == null ) { 4235 ThumbnailBuild( item, curTn.thumbnailIdx, i, (i+1) == nbTn ); 4236 } 4237 tnToDisplay.push({idx:i, delay:cnt}); 4238 cnt++; 4239 } 4240 } 4241 else { 4242 tnToReDisplay.push({idx: i, delay: 0}); 4243 } 4244 // G.GOM.itemsDisplayed++; 4245 lastTnIdx = i; 4246 } 4247 else { 4248 curTn.displayed = false; 4249 var item = G.I[curTn.thumbnailIdx]; 4250 if( item.$elt != null ){ 4251 item.$elt.css({ opacity: 0, display: 'none' }); 4252 } 4253 } 4254 } 4255 4256 var areaWidth = G.$E.conTnParent.width(); 4257 4258 // set gallery area really used size 4259 // if( G.GOM.displayArea.width != G.GOM.displayAreaLast.width || G.GOM.displayArea.height != G.GOM.displayAreaLast.height ) { 4260 if( G.GOM.displayArea.width != G.GOM.displayAreaLast.width || G.GOM.clipArea.height != G.GOM.displayAreaLast.height ) { 4261 G.$E.conTn.width( G.GOM.displayArea.width ).height( G.GOM.clipArea.height ); 4262 G.GOM.displayAreaLast.width = G.GOM.displayArea.width; 4263 G.GOM.displayAreaLast.height = G.GOM.clipArea.height; 4264 // G.GOM.displayAreaLast.height=G.GOM.displayArea.height-G.GOM.clipArea.top; 4265 } 4266 4267 if( areaWidth != G.$E.conTnParent.width() ) { 4268 // gallery area width changed since layout calculation (for example when a scrollbar appeared) 4269 // so we need re-calculate the layout before displaying the thumbnails 4270 G.GOM.cache.areaWidth = G.$E.conTnParent.width(); 4271 GallerySetLayout(); 4272 GalleryDisplayPart1( forceTransition ); 4273 GalleryDisplayPart2( forceTransition ); 4274 return; 4275 } 4276 4277 // counter of not displayed images (is displayed on the last thumbnail) 4278 if( G.layout.support.rows ) { 4279 if( G.galleryDisplayMode.Get() == 'ROWS' || (G.galleryDisplayMode.Get() == 'FULLCONTENT' && G.galleryLastRowFull.Get() && G.GOM.lastFullRow != -1) ){ 4280 if( lastTnIdx < (nbTn - 1) ) { 4281 G.GOM.lastDisplayedIdxNew = lastTnIdx; 4282 } 4283 else { 4284 G.GOM.lastDisplayedIdxNew =- 1; 4285 } 4286 // remove last displayed counter 4287 if( G.GOM.lastDisplayedIdx != -1 ) { 4288 var item = G.I[G.GOM.items[G.GOM.lastDisplayedIdx].thumbnailIdx]; 4289 item.$getElt('.nGY2GThumbnailIconsFullThumbnail').html(''); 4290 } 4291 } 4292 } 4293 4294 4295 // batch set position (and display animation) to all thumbnails 4296 // first display newly built thumbnails 4297 var nbBuild = tnToDisplay.length; 4298 G.GOM.thumbnails2Display=[]; 4299 for( var i = 0; i < nbBuild ; i++ ) { 4300 // ThumbnailSetPosition(tnToDisplay[i].idx, tnToDisplay[i].delay+10); 4301 ThumbnailSetPosition(tnToDisplay[i].idx, i); 4302 } 4303 4304 // then re-position already displayed thumbnails 4305 var n = tnToReDisplay.length; 4306 for( var i = 0; i < n ; i++ ) { 4307 // ThumbnailSetPosition(tnToReDisplay[i].idx, nbBuild+1); 4308 ThumbnailSetPosition(tnToReDisplay[i].idx, i); 4309 } 4310 4311 ThumbnailDisplayAnimBatch(); 4312 4313 if( G.tn.opt.Get('displayTransition') == 'NONE' ) { 4314 G.galleryResizeEventEnabled = true; 4315 // GalleryThumbnailSliderBuildAndStart(); // image slider on last displayed thumbnail 4316 TriggerCustomEvent('galleryDisplayed'); 4317 } 4318 else { 4319 // setTimeout(function() { 4320 requestTimeout( function() { 4321 // change value after the end of the display transistion of the newly built thumbnails 4322 G.galleryResizeEventEnabled = true; 4323 // GalleryThumbnailSliderBuildAndStart(); // image slider on last displayed thumbnail 4324 TriggerCustomEvent('galleryDisplayed'); 4325 }, nbBuild * G.tn.opt.Get('displayInterval')); 4326 } 4327 4328 } 4329 4330 4331 // Thumbnail: set the new position 4332 function ThumbnailSetPosition( GOMidx, cnt ) { 4333 var newTop= 0; 4334 var curTn= G.GOM.items[GOMidx]; 4335 var idx= G.GOM.items[GOMidx].thumbnailIdx; 4336 var item= G.I[idx]; 4337 4338 if( curTn.neverDisplayed ) { 4339 // thumbnail is built but has never been displayed (=first display) 4340 var top = curTn.top - G.GOM.clipArea.top; 4341 if( G.tn.opt.Get('stacks') > 0 ) { 4342 // we have stacks -> do not display them here. They will be displayed at the end of the display animation 4343 item.$elt.last().css({ display: 'block'}); 4344 item.$elt.css({ top: top , left: curTn.left }); 4345 } 4346 else { 4347 item.$elt.css({ display: 'block', top: top , left: curTn.left }); 4348 } 4349 newTop=top; 4350 4351 // display the image of the thumbnail when fully loaded 4352 if( G.O.thumbnailWaitImageLoaded === true ) { 4353 var gi_imgLoad = ngimagesLoaded( item.$getElt('.nGY2TnImg2') ); 4354 gi_imgLoad.on( 'progress', function( instance, image ) { 4355 if( image.isLoaded ) { 4356 var albumIdx = image.img.getAttribute('data-albumidx'); 4357 if( albumIdx == G.GOM.albumIdx ) { 4358 // ignore event if not on current album 4359 var idx = image.img.getAttribute('data-idx'); 4360 G.I[idx].ThumbnailImageReveal(); 4361 } 4362 } 4363 }); 4364 } 4365 // display the thumbnail 4366 ThumbnailAppear(GOMidx, cnt); 4367 4368 curTn.displayed = true; 4369 curTn.neverDisplayed = false; 4370 } 4371 else { 4372 var topOld = G.GOM.cache.containerOffset.top + item.top; 4373 var top = G.GOM.cache.containerOffset.top + (curTn.top - G.GOM.clipArea.top); 4374 newTop = curTn.top - G.GOM.clipArea.top; 4375 var vp = G.GOM.cache.viewport; 4376 if( G.O.thumbnailDisplayOutsideScreen || ( ( (topOld + curTn.height) >= (vp.t - vp.h) && topOld <= (vp.t + vp.h * 4) ) || 4377 ( (top + curTn.height) >= (vp.t - vp.h) && top <= (vp.t + vp.h * 4) ) ) ) { 4378 // thumbnail positioned in enlarged viewport (viewport + 4 x viewport height) (v1.5: changed from 2 to 4) 4379 if( curTn.displayed ) { 4380 // thumbnail is displayed 4381 if( item.top != curTn.top || item.left != curTn.left ) { 4382 // set position 4383 if( G.O.galleryResizeAnimation == true ) { 4384 // with transition 4385 var tweenable = new NGTweenable(); 4386 tweenable.tween({ 4387 from: { top: item.top, left: item.left, height: item.height, width: item.width }, 4388 to: { top: newTop, left: curTn.left, height: curTn.height, width: curTn.width }, 4389 attachment: { $e: item.$elt }, 4390 duration: 100, 4391 delay: cnt * G.tn.opt.Get('displayInterval') / 5, 4392 // easing: 'easeInOutQuad', 4393 easing: 'easeOutQuart', 4394 step: function (state, att) { 4395 // window.ng_draf( function() { 4396 att.$e.css(state); 4397 // }); 4398 }, 4399 finish: function (state, att) { 4400 var _this=this; 4401 // window.ng_draf( function() { 4402 _this.dispose(); 4403 // }); 4404 } 4405 }); 4406 } 4407 else { 4408 // set position without transition 4409 // item.$elt.css({ top: curTn.top , left: curTn.left }); 4410 item.$elt.css({ top: newTop , left: curTn.left }); 4411 } 4412 } 4413 } 4414 else { 4415 // re-display thumbnail 4416 curTn.displayed = true; 4417 // item.$elt.css({ display: 'block', top: curTn.top , left: curTn.left, opacity:1 }); 4418 item.$elt.css({ display: 'block', top: newTop, left: curTn.left, opacity: 1 }); 4419 ThumbnailAppearFinish(item); 4420 } 4421 } 4422 else { 4423 // undisplay thumbnail if not in viewport+margin --> performance gain 4424 curTn.displayed = false; 4425 item.$elt.css({ display: 'none'}); 4426 } 4427 } 4428 item.left = curTn.left; 4429 item.top = newTop; 4430 4431 // set new size if changed 4432 if( item.width != curTn.width || item.height != curTn.height ) { 4433 item.$elt.css({ width: curTn.width , height: curTn.height }); 4434 item.width = curTn.width; 4435 item.height = curTn.height; 4436 4437 // if( curTn.resizedContentWidth > 0 ) { 4438 // resize also the content (=image) 4439 if( item.resizedContentWidth != curTn.resizedContentWidth || item.resizedContentHeight != curTn.resizedContentHeight ) { 4440 if( item.kind == 'albumUp' ) { 4441 // item.$getElt('.nGY2GThumbnailAlbumUp').css({'height': curTn.resizedContentHeight, 'width': curTn.resizedContentWidth}); 4442 } 4443 else { 4444 item.$getElt('.nGY2GThumbnailImage').css({'height': curTn.resizedContentHeight, 'width': curTn.resizedContentWidth}); 4445 4446 if( G.layout.engine == 'JUSTIFIED' ) { 4447 item.$getElt('.nGY2GThumbnailImg').css({'height': curTn.resizedContentHeight, 'width': curTn.resizedContentWidth}); 4448 } 4449 } 4450 item.resizedContentWidth = curTn.resizedContentWidth; 4451 item.resizedContentHeight = curTn.resizedContentHeight; 4452 } 4453 } 4454 4455 4456 // add counter of remaining (not displayed) images 4457 if( G.GOM.lastDisplayedIdxNew == GOMidx && G.layout.support.rows ) { 4458 if( (G.galleryDisplayMode.Get() == 'ROWS' && G.galleryMaxRows.Get() > 0) || (G.galleryDisplayMode.Get() == 'FULLCONTENT' && G.galleryLastRowFull.Get() && G.GOM.lastFullRow != -1) ){ 4459 // number of items 4460 var nb = G.GOM.items.length - GOMidx - 1; 4461 if( item.albumID != '0' && G.O.thumbnailLevelUp ) { 4462 nb--; 4463 } 4464 4465 if( nb > 0 ) { 4466 // display counter 4467 if( G.O.thumbnailOpenImage || G.O.thumbnailSliderDelay > 0 ) { 4468 item.$getElt('.nGY2GThumbnailIconsFullThumbnail').html( '+' + nb); 4469 } 4470 4471 // if( G.layout.engine == 'GRID' && G.GOM.slider.hostItem != G.GOM.NGY2Item(GOMidx) ) { 4472 // image slider on last displayed thumbnail 4473 if( G.GOM.slider.hostItem != G.GOM.NGY2Item(GOMidx) ) { 4474 4475 // set current slider back to initial content 4476 GalleryThumbnailSliderSetContent( G.GOM.slider.hostItem ); 4477 // new slider 4478 G.GOM.slider.hostIdx = GOMidx; 4479 G.GOM.slider.hostItem = G.GOM.NGY2Item(GOMidx); 4480 G.GOM.slider.nextIdx = GOMidx; 4481 G.GOM.slider.currentIdx = GOMidx; 4482 GalleryThumbnailSliderBuildAndStart(); // image slider on last displayed thumbnail 4483 // GalleryThumbnailSliderSetNextContent(); 4484 } 4485 } 4486 else { 4487 // reset slider content to initial content because all thumbnails are displayed 4488 GalleryThumbnailSliderSetContent( G.GOM.slider.hostItem ); 4489 G.GOM.slider.hostIdx = -1; 4490 } 4491 4492 G.GOM.lastDisplayedIdx = GOMidx; 4493 } 4494 } 4495 4496 } 4497 4498 // --------------------- 4499 // replace image on last thumbnails with not displayed ones (mode ROWS or FULLCONTENT with galleryLastRowFull enabled) 4500 // function GalleryLastThumbnailSlideImage() { 4501 function GalleryThumbnailSliderBuildAndStart() { 4502 4503 if( G.O.thumbnailSliderDelay == 0 || G.GOM.slider.hostIdx == -1 ) { 4504 return; 4505 } 4506 clearTimeout(G.GOM.slider.timerID); 4507 4508 var item = G.GOM.slider.hostItem; 4509 4510 // dupplicate image layer -> for the next image 4511 if( item.$getElt('.nGY2TnImgNext').length == 0 ) { 4512 item.$getElt('.nGY2TnImg').clone().removeClass('nGY2TnImg').addClass('nGY2TnImgNext').insertAfter(item.$getElt('.nGY2TnImg')); 4513 item.$getElt('.nGY2TnImgBack').clone().removeClass('nGY2TnImgBack').addClass('nGY2TnImgBackNext').insertAfter(item.$getElt('.nGY2TnImg', true)); 4514 item.$getElt('.nGY2GThumbnailImage', true); // important -> refresh the cache 4515 item.$getElt('.nGY2GThumbnailImg', true); // important -> refresh the cache 4516 } 4517 4518 item.CSSTransformSet('.nGY2TnImgNext', 'translateX', '100%', true); 4519 item.CSSTransformApply( '.nGY2TnImgNext' ); 4520 item.CSSTransformSet('.nGY2TnImgBackNext', 'translateX', '100%', true); 4521 item.CSSTransformApply( '.nGY2TnImgBackNext' ); 4522 4523 GalleryThumbnailSliderSetNextContent(); 4524 4525 // clearTimeout(G.GOM.slider.timerID); 4526 // G.GOM.slider.timerID = setTimeout(function(){ GalleryThumbnailSliderStartTransition() }, G.O.thumbnailSliderDelay); 4527 G.GOM.slider.timerID = requestTimeout(function(){ GalleryThumbnailSliderStartTransition() }, G.O.thumbnailSliderDelay); 4528 } 4529 4530 4531 function GalleryThumbnailSliderSetNextContent() { 4532 4533 G.GOM.slider.nextIdx++; 4534 if( G.GOM.slider.nextIdx >= G.GOM.items.length ) { 4535 G.GOM.slider.nextIdx = G.GOM.slider.hostIdx; 4536 } 4537 4538 // new image 4539 var newItem = G.GOM.NGY2Item(G.GOM.slider.nextIdx); 4540 var imgBlurred = G.emptyGif; 4541 var bgImg = "url('" + G.emptyGif + "')"; 4542 if( newItem.imageDominantColors != null ) { 4543 imgBlurred = newItem.imageDominantColors; 4544 bgImg = "url('" + newItem.imageDominantColors + "')"; 4545 } 4546 G.GOM.slider.hostItem.$getElt('.nGY2TnImgBackNext', true).css({'background-image': bgImg, opacity: 1 }); 4547 G.GOM.slider.hostItem.$getElt('.nGY2TnImgNext', true).css({ 'background-image': "url('" + newItem.thumbImg().src + "')", opacity: 1 }); 4548 G.GOM.slider.hostItem.$getElt('.nGY2TnImgNext .nGY2GThumbnailImg', true).attr('src', newItem.thumbImg().src ); 4549 4550 4551 } 4552 4553 // thumbnail slider - transition from one image to the next one 4554 function GalleryThumbnailSliderStartTransition() { 4555 4556 if( G.GOM.slider.hostItem.$getElt() != null ) { 4557 4558 // slider transition 4559 var tweenable = new NGTweenable(); 4560 G.GOM.slider.tween = tweenable; 4561 tweenable.tween({ 4562 from: { 'left': 100 }, 4563 to: { 'left': 0 }, 4564 duration: 800, 4565 delay: 0, 4566 // easing: 'easeInOutQuad', 4567 easing: 'easeOutQuart', 4568 4569 step: function (state) { 4570 if( G.GOM.slider.hostItem.$getElt() == null ) { 4571 // the thumbnail may have been destroyed since the start of the animation 4572 G.GOM.slider.tween.stop(false); 4573 return; 4574 } 4575 4576 // window.ng_draf( function() { 4577 // slide current content 4578 G.GOM.slider.hostItem.CSSTransformSet('.nGY2TnImgBack', 'translateX', -(100 - state.left) + '%'); 4579 G.GOM.slider.hostItem.CSSTransformApply( '.nGY2TnImgBack' ); 4580 G.GOM.slider.hostItem.CSSTransformSet('.nGY2TnImg', 'translateX', -(100 - state.left) + '%'); 4581 G.GOM.slider.hostItem.CSSTransformApply( '.nGY2TnImg' ); 4582 4583 // slide new content 4584 G.GOM.slider.hostItem.CSSTransformSet('.nGY2TnImgBackNext', 'translateX', state.left + '%'); 4585 G.GOM.slider.hostItem.CSSTransformApply( '.nGY2TnImgBackNext' ); 4586 G.GOM.slider.hostItem.CSSTransformSet('.nGY2TnImgNext', 'translateX', state.left + '%'); 4587 G.GOM.slider.hostItem.CSSTransformApply( '.nGY2TnImgNext' ); 4588 // }); 4589 4590 4591 }, 4592 finish: function (state) { 4593 if( G.GOM.slider.hostItem.$getElt() == null ) { 4594 // the thumbnail may be destroyed since the start of the animation 4595 return; 4596 } 4597 4598 if( G.GOM.NGY2Item(G.GOM.slider.nextIdx) == null ) { return; } // item does not exist anymore 4599 4600 // window.ng_draf( function() { 4601 // set new content as current content 4602 GalleryThumbnailSliderSetContent( G.GOM.NGY2Item(G.GOM.slider.nextIdx) ); 4603 G.GOM.slider.currentIdx = G.GOM.slider.nextIdx; 4604 GalleryThumbnailSliderSetNextContent(); 4605 4606 clearTimeout(G.GOM.slider.timerID); 4607 // G.GOM.slider.timerID=setTimeout(function(){ GalleryThumbnailSliderStartTransition() }, G.O.thumbnailSliderDelay); 4608 G.GOM.slider.timerID = requestTimeout(function(){ GalleryThumbnailSliderStartTransition() }, G.O.thumbnailSliderDelay); 4609 // }); 4610 } 4611 }); 4612 } 4613 } 4614 4615 // set main content of the thumbnail hosting the slider 4616 // hide the elements for the next content of the slider 4617 function GalleryThumbnailSliderSetContent( ngy2itemContent ) { 4618 if( G.GOM.slider.hostIdx == -1 ) { return; } 4619 4620 if( G.GOM.slider.tween != null ) { 4621 if( G.GOM.slider.tween._isTweening == true ) { 4622 G.GOM.slider.tween.stop(false); 4623 } 4624 } 4625 4626 var bgImg = "url('" + G.emptyGif + "')"; 4627 if( ngy2itemContent.imageDominantColors != null ) { 4628 bgImg = "url('" + ngy2itemContent.imageDominantColors + "')"; 4629 } 4630 G.GOM.slider.hostItem.$getElt('.nGY2TnImgBack').css('background-image', bgImg); 4631 G.GOM.slider.hostItem.$getElt('.nGY2TnImg').css('background-image', "url('" + ngy2itemContent.thumbImg().src + "')" ); 4632 G.GOM.slider.hostItem.$getElt('.nGY2TnImg .nGY2GThumbnailImg').attr('src', ngy2itemContent.thumbImg().src ); 4633 4634 G.GOM.slider.hostItem.CSSTransformSet('.nGY2TnImgBack', 'translateX', '0'); 4635 G.GOM.slider.hostItem.CSSTransformApply( '.nGY2TnImgBack' ); 4636 G.GOM.slider.hostItem.CSSTransformSet('.nGY2TnImg', 'translateX', '0'); 4637 G.GOM.slider.hostItem.CSSTransformApply( '.nGY2TnImg' ); 4638 4639 // place the containers for the next image slider outside of the thumbnail (=hidden) 4640 G.GOM.slider.hostItem.CSSTransformSet('.nGY2TnImgBackNext', 'translateX', '100%', true); 4641 G.GOM.slider.hostItem.CSSTransformApply( '.nGY2TnImgBackNext' ); 4642 G.GOM.slider.hostItem.CSSTransformSet('.nGY2TnImgNext', 'translateX', '100%', true); 4643 G.GOM.slider.hostItem.CSSTransformApply( '.nGY2TnImgNext' ); 4644 4645 // set new title and description 4646 if( G.O.thumbnailLabel.get('display') == true ) { 4647 var icons = G.O.icons.thumbnailAlbum; 4648 if( ngy2itemContent.kind != 'album' ) { 4649 icons = G.O.icons.thumbnailImage; 4650 } 4651 G.GOM.slider.hostItem.$getElt('.nGY2GThumbnailTitle').html(icons + getThumbnailTitle(ngy2itemContent)); 4652 // G.GOM.slider.hostItem.$getElt('.nGY2GThumbnailDescription').html(icons + getTumbnailDescription(ngy2itemContent)); 4653 } 4654 } 4655 4656 4657 4658 // Compute the height of the label part of a thumbnail (title+description, both single line) 4659 function ThumbnailGetLabelHeight() { 4660 var newElt = [], 4661 newEltIdx = 0; 4662 4663 // if( G.O.thumbnailLabel.get('display') == false && G.tn.toolbar.getWidth(item) <= 0 ) { 4664 if( G.O.thumbnailLabel.get('display') == false ) { 4665 return 0; 4666 } 4667 4668 var desc=''; 4669 if( G.O.thumbnailLabel.get('displayDescription') == true ) { 4670 desc = 'aAzZjJ'; 4671 } 4672 4673 // visibility set to hidden 4674 newElt[newEltIdx++] = '<div class="nGY2GThumbnail ' + G.O.theme + '" style="display:block;visibility:hidden;position:absolute;top:-9999px;left:-9999px;" ><div class="nGY2GThumbnailSub">'; 4675 if( G.O.thumbnailLabel.get('display') == true ) { 4676 // Labels: title and description 4677 newElt[newEltIdx++] = ' <div class="nGY2GThumbnailLabel" '+ G.tn.style.getLabel() +'>'; 4678 newElt[newEltIdx++] = ' <div class="nGY2GThumbnailAlbumTitle" '+G.tn.style.getTitle()+'>aAzZjJ</div>'; 4679 if( G.O.thumbnailLabel.get('displayDescription') == true ) { 4680 // newElt[newEltIdx++] = ' <div class="nGY2GThumbnailDescription" '+G.tn.style.getDesc()+'>'+'aAzZjJ'+'</div>'; 4681 } 4682 newElt[newEltIdx++] = ' </div>'; 4683 } 4684 4685 newElt[newEltIdx++] = '</div></div>'; 4686 4687 var $newDiv = jQuery(newElt.join('')).appendTo(G.$E.conTn); 4688 var h = $newDiv.find('.nGY2GThumbnailLabel').outerHeight(true); 4689 $newDiv.remove(); 4690 4691 return h; 4692 } 4693 4694 function ThumbnailBuildStacks( bgColor ) { 4695 var ns=G.tn.opt.Get('stacks'); 4696 if( ns == 0 ) { return ''; } 4697 4698 var s=''; 4699 for( var i=0; i<ns; i++ ) { 4700 s='<div class="nGY2GThumbnailStack " style="display:none;'+bgColor+'"></div>'+s; 4701 } 4702 return s; 4703 } 4704 4705 //----- Build one UP thumbnail (=navigation thumbnail) 4706 function ThumbnailBuildAlbumpUp( item, idx, GOMidx ) { 4707 var newElt = [], 4708 newEltIdx = 0; 4709 4710 var mp = ''; 4711 if( G.O.thumbnailOpenImage === false ) { 4712 mp = 'cursor:default;' 4713 } 4714 4715 newElt[newEltIdx++] = ThumbnailBuildStacks('') + '<div class="nGY2GThumbnail" style="display:none;opacity:0;' + mp + '" >'; 4716 newElt[newEltIdx++] = ' <div class="nGY2GThumbnailSub">'; 4717 4718 var h=G.tn.defaultSize.getHeight(), 4719 w=G.tn.defaultSize.getWidth(); 4720 4721 newElt[newEltIdx++] = ' <div class="nGY2GThumbnailImage" style="width:'+w+'px;height:'+h+'px;"><img class="nGY2GThumbnailImg" src="'+G.emptyGif+'" alt="alt2" style="max-width:'+w+'px;max-height:'+h+'px;" ></div>'; 4722 // newElt[newEltIdx++] = ' <div class="nGY2GThumbnailAlbumUp" style="width:'+w+'px;height:'+h+'px;">'+G.O.icons.thumbnailAlbumUp+'</div>'; 4723 newElt[newEltIdx++] = ' <div class="nGY2GThumbnailAlbumUp" >'+G.O.icons.thumbnailAlbumUp+'</div>'; 4724 newElt[newEltIdx++] = ' </div>'; 4725 newElt[newEltIdx++] = '</div>'; 4726 4727 var $newDiv = jQuery(newElt.join('')).appendTo(G.$E.conTn); //.animate({ opacity: 1},1000, 'swing'); //.show('slow'); //.fadeIn('slow').slideDown('slow'); 4728 4729 item.$elt = $newDiv; 4730 $newDiv.data('index', GOMidx); 4731 item.$getElt('.nGY2GThumbnailImg').data('index', GOMidx); 4732 4733 return; 4734 } 4735 4736 4737 //----- Build one thumbnail 4738 function ThumbnailBuild( item, idx, GOMidx, lastOne ) { 4739 item.eltTransform = []; 4740 item.eltFilter = []; 4741 item.hoverInitDone = false; 4742 item.$Elts = []; 4743 4744 if( item.kind == 'albumUp' ) { 4745 ThumbnailBuildAlbumpUp( item, idx, GOMidx); 4746 return; 4747 } 4748 4749 var newElt = [], 4750 newEltIdx = 0; 4751 4752 var mp = ''; 4753 if( G.O.thumbnailOpenImage === false ) { 4754 mp = 'cursor:default;' 4755 } 4756 4757 // var src = encodeURI(item.thumbImg().src), 4758 var src = (item.thumbImg().src).replace(/'/g, "%27"), // replace single quote with %27 4759 sTitle = getThumbnailTitle(item); 4760 var sDesc = getTumbnailDescription(item); 4761 4762 4763 // image background -> visible during image download 4764 var bg = ''; 4765 var bgImg = "background-image: url('" + G.emptyGif + "');"; 4766 if( item.imageDominantColors != null ) { 4767 // dominant colorS (blurred preview image) 4768 bgImg = "background-image: url('" + item.imageDominantColors + "');"; 4769 } 4770 else { 4771 // dominant color -> background color 4772 if( item.imageDominantColor != null ) { 4773 bg = 'background-color:' + item.imageDominantColor + ';'; 4774 } 4775 else { 4776 bgImg = ''; 4777 } 4778 } 4779 4780 var op = 'opacity:1;'; 4781 if( G.O.thumbnailWaitImageLoaded == true ) { 4782 op = 'opacity:0;'; 4783 } 4784 4785 // ##### thumbnail containers (with stacks) 4786 newElt[newEltIdx++] = ThumbnailBuildStacks(bg) + '<div class="nGY2GThumbnail" style="display:none;opacity:0;' + mp + '"><div class="nGY2GThumbnailSub ' + ( G.O.thumbnailSelectable && item.selected ? "nGY2GThumbnailSubSelected" : "" ) + '">'; 4787 4788 4789 // image size 4790 var w = G.tn.settings.getW(); 4791 var h = G.tn.settings.getH(); 4792 if( G.tn.settings.getMosaic() !== null ) { 4793 // mosaic layout -> 4794 w = G.GOM.items[GOMidx].width; 4795 h = G.GOM.items[GOMidx].height; 4796 } 4797 4798 var bgSize = 'contain'; 4799 if( G.tn.opt.Get('crop') ) { 4800 bgSize = 'cover'; 4801 } 4802 4803 // ##### layer for image background (color, dominant color, blurred preview) 4804 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;"; 4805 newElt[newEltIdx++]='<div class="nGY2GThumbnailImage nGY2TnImgBack" style="' + s1 + '"></div>'; 4806 4807 // #### layer for image 4808 var s2 = op + "position: absolute; top: 0px; left: 0px; width:" + w + "px; height:" + h + "px; background-image: url('" + src + "'); background-position: center center; background-repeat: no-repeat; background-size:" + bgSize + "; overflow: hidden;"; 4809 newElt[newEltIdx++]='<div class="nGY2GThumbnailImage nGY2TnImg" style="' + s2 + '">'; 4810 newElt[newEltIdx++]=' <img class="nGY2GThumbnailImg nGY2TnImg2" src="' + src + '" alt="' + sDesc + '" style="opacity:0;" data-idx="' + idx + '" data-albumidx="' + G.GOM.albumIdx + '" >'; 4811 newElt[newEltIdx++]='</div>'; 4812 4813 // ##### layer for user customization purposes 4814 newElt[newEltIdx++]='<div class="nGY2GThumbnailCustomLayer"></div>'; 4815 4816 // ##### layer for labels (title + description and their icons) 4817 if( G.O.thumbnailLabel.get('display') == true ) { 4818 // Labels: title and description 4819 newElt[newEltIdx++]= ' <div class="nGY2GThumbnailLabel" '+ G.tn.style.getLabel(item) + '>'; 4820 if( item.kind == 'album' ) { 4821 // album kind 4822 newElt[newEltIdx++]= ' <div class="nGY2GThumbnailTitle nGY2GThumbnailAlbumTitle" ' + G.tn.style.getTitle() + '>' + G.O.icons.thumbnailAlbum + sTitle + '</div>'; 4823 } 4824 else { 4825 // image/media kind 4826 newElt[newEltIdx++]= ' <div class="nGY2GThumbnailTitle nGY2GThumbnailImageTitle" ' + G.tn.style.getTitle() + '>' + G.O.icons.thumbnailImage + sTitle + '</div>'; 4827 } 4828 // newElt[newEltIdx++]= ' <div class="nGY2GThumbnailDescription" ' + G.tn.style.getDesc() + '>' + getTumbnailDescription(item) + '</div>'; 4829 newElt[newEltIdx++]= ' </div>'; 4830 } 4831 4832 // ##### layer for tools 4833 newElt[newEltIdx++] = ThumbnailBuildTools(item, lastOne); 4834 4835 // close containers 4836 newElt[newEltIdx++]='</div></div>'; 4837 4838 var $newDiv =jQuery(newElt.join('')).appendTo(G.$E.conTn); 4839 4840 item.$elt=$newDiv; 4841 $newDiv.data('index',GOMidx); 4842 item.$getElt('.nGY2GThumbnailImg').data('index',GOMidx); 4843 4844 // Custom init function 4845 var fu=G.O.fnThumbnailInit; 4846 if( fu !== null ) { 4847 typeof fu == 'function' ? fu($newDiv, item, GOMidx) : window[fu]($newDiv, item, GOMidx); 4848 } 4849 4850 if( item.title != 'image gallery by nanogallery2 [build]' ) { 4851 ThumbnailOverInit(GOMidx); 4852 } 4853 4854 return ; 4855 } 4856 4857 4858 // Thumbnail layer for tools (toolbars and counter) 4859 function ThumbnailBuildTools( item, lastThumbnail ) { 4860 4861 // toolbars 4862 var tb = ThumbnailBuildToolbarOne(item, 'topLeft') + ThumbnailBuildToolbarOne(item, 'topRight') + ThumbnailBuildToolbarOne(item, 'bottomLeft') + ThumbnailBuildToolbarOne(item, 'bottomRight'); 4863 4864 // counter of not displayed images 4865 tb += '<div class="nGY2GThumbnailIconsFullThumbnail"></div>'; 4866 4867 return tb; 4868 } 4869 4870 function ThumbnailBuildToolbarOne( item, position ) { 4871 var toolbar = ''; 4872 var tb = G.tn.toolbar.get(item); 4873 var width = { xs:0, sm:1, me:2, la:3, xl:4 }; 4874 var cnt = 0; 4875 4876 if( tb[position] != '' ) { 4877 var pos='top: 0; right: 0; text-align: right;'; // 'topRight' and default 4878 switch( position ) { 4879 case 'topLeft': 4880 pos = 'top: 0; left: 0; text-align: left;'; 4881 break; 4882 case 'bottomRight': 4883 pos = 'bottom: 0; right: 0; text-align: right;'; 4884 break; 4885 case 'bottomLeft': 4886 pos = 'bottom: 0; left: 0; text-align: left;'; 4887 break; 4888 } 4889 4890 toolbar += ' <ul class="nGY2GThumbnailIcons" style="' + pos + '">'; 4891 4892 var icons = tb[position].split(','); 4893 var nb = icons.length; 4894 for( var i = 0; i < nb; i++ ) { 4895 var icon = icons[i].replace(/^\s*|\s*$/, ''); //trim trailing/leading whitespace 4896 4897 var minWidth = icon.substring(0,2).toLowerCase(); 4898 var tIcon = icon; 4899 var display = true; 4900 if( /xs|sm|me|la|xl/i.test(minWidth) ) { 4901 // check visbility (depending on screen width) 4902 if( width[minWidth] > width[G.GOM.curWidth] ) { 4903 display = false; 4904 } 4905 tIcon = icon.substring(2); 4906 } 4907 4908 if( display ) { 4909 var sp=(i+1<nb ? ' ' :''); 4910 switch( tIcon ) { 4911 case 'COUNTER': 4912 if( item.kind == 'album' ) { 4913 toolbar += ' <li class="nGY2GThumbnailIcon" data-ngy2action="">'; 4914 toolbar += ' <div class="nGY2GThumbnailIconImageCounter"></div>'; 4915 toolbar += ' <div class="nGY2GThumbnailIconText">' + G.O.icons.thumbnailCounter+Math.max((item.getContentLength(false)),item.numberItems) + sp + '</div>'; 4916 toolbar += ' </li>'; 4917 cnt++; 4918 } 4919 break; 4920 case 'COUNTER2': 4921 if( item.kind == 'album' ) { 4922 toolbar += ' <li class="nGY2GThumbnailIcon" data-ngy2action="">'; 4923 toolbar += ' <div class="nGY2GThumbnailIconTextBadge">' + G.O.icons.thumbnailCounter+Math.max((item.getContentLength(false)),item.numberItems) + sp + '</div>'; 4924 toolbar += ' </li>'; 4925 cnt++; 4926 } 4927 break; 4928 case 'SHARE': 4929 toolbar += ' <li class="nGY2GThumbnailIcon" data-ngy2action="' + tIcon + '">'; 4930 toolbar += ' <div>' + G.O.icons.thumbnailShare + '</div>'; 4931 toolbar += ' </li>'; 4932 cnt++; 4933 break; 4934 case 'DOWNLOAD': 4935 toolbar += ' <li class="nGY2GThumbnailIcon" data-ngy2action="' + tIcon + '">'; 4936 toolbar += ' <div>' + G.O.icons.thumbnailDownload + '</div>'; 4937 toolbar += ' </li>'; 4938 cnt++; 4939 break; 4940 case 'INFO': 4941 toolbar += ' <li class="nGY2GThumbnailIcon" data-ngy2action="' + tIcon + '">'; 4942 toolbar += ' <div>' + G.O.icons.thumbnailInfo + '</div>'; 4943 toolbar += ' </li>'; 4944 cnt++; 4945 break; 4946 case 'CART': 4947 toolbar += ' <li class="nGY2GThumbnailIcon" data-ngy2action="' + tIcon + '">'; 4948 toolbar += ' <div>' + G.O.icons.thumbnailCart + '</div>'; 4949 toolbar += ' </li>'; 4950 cnt++; 4951 break; 4952 case 'DISPLAY': 4953 toolbar += ' <li class="nGY2GThumbnailIcon" data-ngy2action="DISPLAY">'; 4954 toolbar += ' <div class="nGY2GThumbnailIconImageShare">' + G.O.icons.thumbnailDisplay + '</div>'; 4955 toolbar += ' </li>'; 4956 cnt++; 4957 break; 4958 case 'CUSTOM1': 4959 case 'CUSTOM2': 4960 case 'CUSTOM3': 4961 case 'CUSTOM4': 4962 case 'CUSTOM5': 4963 case 'CUSTOM6': 4964 case 'CUSTOM7': 4965 case 'CUSTOM8': 4966 case 'CUSTOM9': 4967 case 'CUSTOM10': 4968 var cust = tIcon.replace('CUSTOM', ''); 4969 toolbar += ' <li class="nGY2GThumbnailIcon" data-ngy2action="' + tIcon.toLowerCase() + '">'; 4970 toolbar += ' <div class="nGY2GThumbnailIconImageShare">' + G.O.icons['thumbnailCustomTool' + cust] + '</div>'; 4971 toolbar += ' </li>'; 4972 cnt++; 4973 break; 4974 case 'FEATURED': 4975 if( item.featured === true ) { 4976 toolbar += ' <li class="nGY2GThumbnailIcon" data-ngy2action="">'; 4977 toolbar += ' <div class="nGY2GThumbnailIconImageFeatured">' + G.O.icons.thumbnailFeatured + '</div>'; 4978 toolbar += ' </li>'; 4979 cnt++; 4980 } 4981 break; 4982 case 'SELECT': 4983 if( G.O.thumbnailSelectable == true ) { 4984 toolbar += ' <li class="nGY2GThumbnailIcon" data-ngy2action="TOGGLESELECT">'; 4985 if( item.selected === true ) { 4986 toolbar += ' <div class="nGY2GThumbnailIconImageSelect nGY2ThumbnailSelected">' + G.O.icons.thumbnailSelected + '</div>'; 4987 } 4988 else { 4989 toolbar += ' <div class="nGY2GThumbnailIconImageSelect nGY2ThumbnailUnselected">' + G.O.icons.thumbnailUnselected + '</div>'; 4990 } 4991 toolbar += ' </li>'; 4992 cnt++; 4993 } 4994 break; 4995 } 4996 } 4997 } 4998 toolbar += ' </ul>'; 4999 } 5000 5001 if( cnt > 0 ) { 5002 return toolbar; 5003 } 5004 else { 5005 return ''; 5006 } 5007 } 5008 5009 function getThumbnailTitle( item ) { 5010 5011 var sTitle = item.title; 5012 if( G.O.thumbnailLabel.get('display') == true ) { 5013 if( sTitle === undefined || sTitle.length == 0 ) { sTitle = ' '; } 5014 5015 if( G.i18nTranslations.thumbnailImageTitle != '' ) { 5016 sTitle = G.i18nTranslations.thumbnailImageTitle; 5017 } 5018 var ml = G.O.thumbnailLabel.get('titleMaxLength'); 5019 if( ml > 3 && sTitle.length > ml ){ 5020 sTitle = sTitle.substring(0, ml) + '...'; 5021 } 5022 } 5023 5024 return sTitle; 5025 } 5026 5027 function getTumbnailDescription( item ) { 5028 var sDesc = ''; 5029 sDesc = item.description; 5030 if( G.O.thumbnailLabel.get('displayDescription') == true ) { 5031 if( item.kind == 'album' ) { 5032 if( G.i18nTranslations.thumbnailImageDescription != '' ) { 5033 sDesc = G.i18nTranslations.thumbnailAlbumDescription; 5034 } 5035 else { 5036 sDesc = item.description; 5037 } 5038 } 5039 else { 5040 if( G.i18nTranslations.thumbnailImageDescription != '' ) { 5041 sDesc = G.i18nTranslations.thumbnailImageDescription; 5042 } 5043 else { 5044 sDesc = item.description; 5045 } 5046 } 5047 var ml = G.O.thumbnailLabel.get('descriptionMaxLength'); 5048 if( ml > 3 && sDesc.length > ml ){ 5049 sDesc = sDesc.substring(0, ml) + '...'; 5050 } 5051 if( sDesc.length == 0 ) { 5052 sDesc = ' '; 5053 } 5054 } 5055 if(sDesc == '') { 5056 sDesc = getThumbnailTitle(item); 5057 } 5058 5059 return sDesc; 5060 } 5061 5062 5063 5064 // Retrieve the maximum number of thumbnails that fits in one row 5065 function NbThumbnailsPerRow( areaWidth ) { 5066 var tnW = G.tn.defaultSize.getOuterWidth(); 5067 5068 var nbMaxTn = 0; 5069 if( G.O.thumbnailAlignment == 'justified' ) { 5070 nbMaxTn = Math.floor((areaWidth)/(tnW)); 5071 } 5072 else { 5073 nbMaxTn = Math.floor((areaWidth + G.tn.opt.Get('gutterWidth'))/(tnW + G.tn.opt.Get('gutterWidth'))); 5074 } 5075 5076 if( G.O.maxItemsPerLine >0 && nbMaxTn > G.O.maxItemsPerLine ) { 5077 nbMaxTn = G.O.maxItemsPerLine; 5078 } 5079 5080 if( nbMaxTn < 1 ) { nbMaxTn = 1; } 5081 5082 return nbMaxTn 5083 } 5084 5085 // Thumbnail display animation 5086 function ThumbnailAppear( n, cnt ) { 5087 var curTn = G.GOM.items[n]; 5088 var item = G.I[curTn.thumbnailIdx]; 5089 5090 5091 if( G.tn.opt.Get('displayTransition') == 'NONE' ) { 5092 item.$elt.css({ opacity: 1 }); 5093 ThumbnailAppearFinish( item ); 5094 } 5095 else { 5096 if( item.$elt == null ) { return; } 5097 var top = G.GOM.cache.containerOffset.top + ( curTn.top - G.GOM.clipArea.top ); 5098 var vp = G.GOM.cache.viewport; 5099 if( (top + (curTn.top - G.GOM.clipArea.top)) >= (vp.t - 50) && top <= (vp.t + vp.h + 50) ) { 5100 // display animation only if in the current viewport 5101 var delay = cnt * G.tn.opt.Get('displayInterval'); 5102 if( G.tn.opt.Get('displayTransition') == 'CUSTOM' ) { 5103 if( G.GOM.curNavLevel == 'lN' ) { 5104 G.O.fnThumbnailDisplayEffect(item.$elt, item, n, delay); 5105 } 5106 else { 5107 G.O.fnThumbnailL1DisplayEffect(item.$elt, item, n, delay); 5108 } 5109 } 5110 else { 5111 G.GOM.thumbnails2Display.push({itm: item, d: delay}); 5112 // ThumbnailDisplayAnim2(item, delay); 5113 } 5114 return; 5115 } 5116 else { 5117 item.$elt.css({ opacity: 1 }); 5118 ThumbnailAppearFinish(item); 5119 } 5120 } 5121 } 5122 5123 5124 // displays thumbnail stacks at the end of the display animation 5125 function ThumbnailAppearFinish( item ) { 5126 5127 // add stacks 5128 var ns = G.tn.opt.Get('stacks'); 5129 if( ns > 0 ) { 5130 // display stacks 5131 item.$elt.css({ display: 'block'}); 5132 var o = 0.9; 5133 // set stack opacity 5134 for( var i = ns-1; i>=0; i-- ) { 5135 item.$elt.eq(i).css('opacity', o); 5136 o = o - 0.2; 5137 } 5138 5139 } 5140 } 5141 5142 5143 function ThumbnailDisplayAnim2( item, delay ) { 5144 function randomIntFromInterval(min,max) { 5145 return Math.floor(Math.random()*(max-min+1)+min); 5146 } 5147 var oFrom = {}; 5148 var oTo = {}; 5149 5150 switch (G.tn.opt.Get('displayTransition')) { 5151 case 'RANDOMSCALE': 5152 var scales = [0.95, 1, 1.05, 1.1]; 5153 var zi = [1, 2, 3, 4]; 5154 5155 var r = randomIntFromInterval(0,3); 5156 while( r == G.GOM.lastRandomValue ) { 5157 r = randomIntFromInterval(0,3); 5158 } 5159 G.GOM.lastRandomValue = r; 5160 var f = scales[r]; 5161 // item.$elt.css({ 'z-index': G.GOM.lastZIndex+zi[r], 'box-shadow': '-1px 2px 5px 1px rgba(0, 0, 0, 0.7)' }); 5162 item.$elt.css({ 'z-index': G.GOM.lastZIndex+zi[r], 'box-shadow': '0px 0px 5px 3px rgba(0,0,0,0.74)' }); 5163 5164 oFrom = { scale: 0.5, opacity:0 }; 5165 oTo = { scale: f, opacity:1 }; 5166 break; 5167 5168 case 'SCALEUP': 5169 var f = G.tn.opt.Get('displayTransitionStartVal'); 5170 if( f == 0 ) { f = 0.6; } // default value 5171 oFrom = { scale: f, opacity: 0 }; 5172 oTo = { scale: 1, opacity: 1 }; 5173 break; 5174 5175 case 'SCALEDOWN': 5176 var f = G.tn.opt.Get('displayTransitionStartVal'); 5177 if( f == 0 ) { f=1.3; } // default value 5178 oFrom = { scale: f, opacity: 0 }; 5179 oTo = { scale: 1, opacity: 1 }; 5180 break; 5181 case 'SLIDEUP': 5182 var f = G.tn.opt.Get('displayTransitionStartVal'); 5183 if( f == 0 ) { f=50; } // default value 5184 oFrom = { opacity: 0, translateY: f }; 5185 oTo = { opacity: 1, translateY: 0 }; 5186 break; 5187 case 'SLIDEDOWN': 5188 var f=G.tn.opt.Get('displayTransitionStartVal'); 5189 if( f == 0 ) { f=-50; } // default value 5190 oFrom = { opacity: 0, translateY: f }; 5191 oTo = { opacity: 1, translateY: 0 }; 5192 break; 5193 case 'FLIPUP': 5194 var f=G.tn.opt.Get('displayTransitionStartVal'); 5195 if( f == 0 ) { f=100; } // default value 5196 oFrom = { opacity: 0, translateY: f, rotateX: 45 }; 5197 oTo = { opacity: 1, translateY: 0, rotateX: 0 }; 5198 break; 5199 5200 case 'FLIPDOWN': 5201 var f=G.tn.opt.Get('displayTransitionStartVal'); 5202 if( f == 0 ) { f=-100; } // default value 5203 oFrom = { opacity: 0, translateY: f, rotateX: -45 }; 5204 oTo = { opacity: 1, translateY: 0, rotateX: 0 }; 5205 break; 5206 case 'SLIDEUP2': 5207 var f=G.tn.opt.Get('displayTransitionStartVal'); 5208 if( f == 0 ) { f=100; } // default value 5209 oFrom = { opacity: 0, translateY: f, rotateY: 40 }; 5210 oTo = { opacity: 1, translateY: 0, rotateY: 0 }; 5211 break; 5212 case 'SLIDEDOWN2': 5213 var f=G.tn.opt.Get('displayTransitionStartVal'); 5214 if( f == 0 ) { f=-100; } // default value 5215 oFrom = { opacity: 0, translateY: f, rotateY: 40 }; 5216 oTo = { opacity: 1, translateY: 0, rotateY: 0 }; 5217 break; 5218 case 'SLIDERIGHT': 5219 var f=G.tn.opt.Get('displayTransitionStartVal'); 5220 if( f == 0 ) { f=-150; } // default value 5221 oFrom = { opacity: 0, translateX: f }; 5222 oTo = { opacity: 1, translateX: 0 }; 5223 break; 5224 5225 case 'SLIDELEFT': 5226 var f=G.tn.opt.Get('displayTransitionStartVal'); 5227 if( f == 0 ) { f=150; } // default value 5228 oFrom = { opacity: 0, translateX: f }; 5229 oTo = { opacity: 1, translateX: 0 }; 5230 break; 5231 5232 case 'FADEIN': 5233 oFrom = { opacity: 0 }; 5234 oTo = { opacity: 1 }; 5235 break; 5236 5237 5238 } 5239 5240 var tweenable = new NGTweenable(); 5241 tweenable.tween({ 5242 from: oFrom, 5243 to: oTo, 5244 attachment: { $e:item.$elt, item: item, tw: tweenable }, 5245 delay: delay, 5246 duration: G.tn.opt.Get('displayTransitionDuration'), 5247 easing: G.tn.opt.Get('displayTransitionEasing'), 5248 step: function (state, att) { 5249 window.requestAnimationFrame( function() { 5250 if( att.item.$elt === null ) { // the thumbnail may have been destroyed since the start of the animation 5251 att.tw.stop(false); 5252 return; 5253 } 5254 switch (G.tn.opt.Get('displayTransition')) { 5255 case 'RANDOMSCALE': 5256 att.$e.css( G.CSStransformName , 'scale(' + state.scale + ')').css('opacity', state.opacity); 5257 break; 5258 case 'SCALEUP': 5259 att.$e.css( G.CSStransformName , 'scale('+state.scale+')').css('opacity',state.opacity); 5260 break; 5261 case 'SCALEDOWN': 5262 att.item.$elt.last().css('opacity', state.opacity); 5263 att.item.CSSTransformSet('.nGY2GThumbnail', 'scale', state.scale); 5264 att.item.CSSTransformApply('.nGY2GThumbnail'); 5265 break; 5266 case 'SLIDEUP': 5267 att.item.$elt.css('opacity', state.opacity); 5268 att.item.CSSTransformSet('.nGY2GThumbnail', 'translate', '0px, '+state.translateY + 'px'); 5269 att.item.CSSTransformApply('.nGY2GThumbnail'); 5270 break; 5271 case 'SLIDEDOWN': 5272 att.item.$elt.css('opacity', state.opacity); 5273 att.item.CSSTransformSet('.nGY2GThumbnail', 'translate', '0px,'+state.translateY+'px'); 5274 att.item.CSSTransformApply('.nGY2GThumbnail'); 5275 break; 5276 case 'FLIPUP': 5277 att.item.CSSTransformSet('.nGY2GThumbnail', 'translate', '0px,'+state.translateY+'px'); 5278 att.item.CSSTransformSet('.nGY2GThumbnail', 'rotateX', state.rotateX+'deg'); 5279 att.item.$elt.css('opacity', state.opacity); 5280 att.item.CSSTransformApply('.nGY2GThumbnail'); 5281 break; 5282 case 'FLIPDOWN': 5283 att.item.$elt.css('opacity', state.opacity); 5284 att.item.CSSTransformSet('.nGY2GThumbnail', 'translate', '0px,' + state.translateY + 'px'); 5285 att.item.CSSTransformSet('.nGY2GThumbnail', 'rotateX', state.rotateX + 'deg'); 5286 att.item.CSSTransformApply('.nGY2GThumbnail'); 5287 break; 5288 case 'SLIDEUP2': 5289 att.item.$elt.css('opacity', state.opacity); 5290 att.item.CSSTransformSet('.nGY2GThumbnail', 'translate', '0px,' + state.translateY + 'px'); 5291 att.item.CSSTransformSet('.nGY2GThumbnail', 'rotateY', state.rotateY + 'deg'); 5292 att.item.CSSTransformApply('.nGY2GThumbnail'); 5293 break; 5294 case 'SLIDEDOWN2': 5295 att.item.$elt.css('opacity', state.opacity); 5296 att.item.CSSTransformSet('.nGY2GThumbnail', 'translate', '0px, ' + state.translateY + 'px'); 5297 att.item.CSSTransformSet('.nGY2GThumbnail', 'rotateY', state.rotateY + 'deg'); 5298 att.item.CSSTransformApply('.nGY2GThumbnail'); 5299 break; 5300 case 'SLIDERIGHT': 5301 att.item.$elt.css('opacity', state.opacity); 5302 att.item.CSSTransformSet('.nGY2GThumbnail', 'translate', state.translateX + 'px, 0px'); 5303 att.item.CSSTransformApply('.nGY2GThumbnail'); 5304 break; 5305 case 'SLIDELEFT': 5306 att.item.CSSTransformSet('.nGY2GThumbnail', 'translate', state.translateX + 'px, 0px'); 5307 att.item.$elt.css('opacity', state.opacity); 5308 att.item.CSSTransformApply('.nGY2GThumbnail'); 5309 break; 5310 case 'FADEIN': 5311 att.$e.css(state); 5312 break; 5313 } 5314 }); 5315 // att.$e.css( G.CSStransformName , 'scale('+state.scale+')').css('opacity',state.opacity); 5316 }, 5317 finish: function (state, att) { 5318 window.requestAnimationFrame( function() { 5319 if( att.item.$elt === null ) { return; } 5320 5321 switch (G.tn.opt.Get('displayTransition')) { 5322 case 'RANDOMSCALE': 5323 att.$e.css( G.CSStransformName , 'scale('+state.scale+')').css('opacity', ''); 5324 break; 5325 case 'SCALEUP': 5326 att.$e.css( G.CSStransformName , '').css('opacity', ''); 5327 break; 5328 case 'SCALEDOWN': 5329 att.item.$elt.last().css('opacity', ''); 5330 att.item.CSSTransformSet('.nGY2GThumbnail', 'scale', state.scale); 5331 att.item.CSSTransformApply('.nGY2GThumbnail'); 5332 break; 5333 case 'SLIDEUP': 5334 att.item.$elt.css('opacity', ''); 5335 break; 5336 case 'SLIDEDOWN': 5337 att.item.$elt.css('opacity', ''); 5338 break; 5339 case 'FLIPUP': 5340 att.item.$elt.css('opacity', ''); 5341 break; 5342 case 'FLIPDOWN': 5343 att.item.$elt.css('opacity', ''); 5344 break; 5345 case 'SLIDEUP2': 5346 att.item.$elt.css('opacity', ''); 5347 break; 5348 case 'SLIDEDOWN2': 5349 att.item.$elt.css('opacity', ''); 5350 att.item.CSSTransformApply('.nGY2GThumbnail'); 5351 break; 5352 case 'SLIDERIGHT': 5353 att.item.$elt.css('opacity', ''); 5354 break; 5355 case 'SLIDELEFT': 5356 att.item.$elt.css('opacity', ''); 5357 break; 5358 case 'FADEIN': 5359 att.$e.css('opacity', ''); 5360 break; 5361 } 5362 ThumbnailAppearFinish(att.item); 5363 }); 5364 5365 } 5366 }); 5367 5368 } 5369 5370 // batch display thumbnails with animation 5371 function ThumbnailDisplayAnimBatch() { 5372 5373 G.GOM.thumbnails2Display.forEach( function(one) { 5374 ThumbnailDisplayAnim2(one.itm, one.d); 5375 }); 5376 G.GOM.thumbnails2Display=[]; 5377 } 5378 5379 5380 5381 // ###################################### 5382 // Gallery display animation 5383 function GalleryAppear() { 5384 5385 var d=G.galleryDisplayTransitionDuration.Get(); 5386 switch( G.galleryDisplayTransition.Get() ){ 5387 case 'ROTATEX': 5388 G.$E.base.css({ perspective: '1000px', 'perspective-origin': '50% 0%' }); 5389 var tweenable = new NGTweenable(); 5390 tweenable.tween({ 5391 from: { r: 50 }, 5392 to: { r: 0 }, 5393 attachment: { orgIdx: G.GOM.albumIdx }, 5394 duration: d, 5395 easing: 'easeOutCirc', 5396 step: function (state, att) { 5397 if( att.orgIdx == G.GOM.albumIdx ) { 5398 // window.ng_draf( function() { 5399 G.$E.conTnParent.css( G.CSStransformName , 'rotateX(' + state.r + 'deg)'); 5400 // }); 5401 } 5402 } 5403 }); 5404 break; 5405 case 'SLIDEUP': 5406 G.$E.conTnParent.css({ opacity: 0 }); 5407 var tweenable = new NGTweenable(); 5408 tweenable.tween({ 5409 from: { y: 200, o: 0 }, 5410 to: { y: 0, o: 1 }, 5411 attachment: { orgIdx: G.GOM.albumIdx }, 5412 duration: d, 5413 easing: 'easeOutCirc', 5414 step: function (state, att) { 5415 if( att.orgIdx == G.GOM.albumIdx ) { 5416 // window.ng_draf( function() { 5417 G.$E.conTnParent.css( G.CSStransformName , 'translate( 0px, '+state.y + 'px)').css('opacity', state.o); 5418 // }); 5419 } 5420 } 5421 }); 5422 break; 5423 case 'NONE': 5424 default: 5425 break; 5426 } 5427 5428 5429 } 5430 5431 // ###################################### 5432 // ##### THUMBNAIL HOVER MANAGEMENT ##### 5433 // ###################################### 5434 5435 function ThumbnailOverInit( GOMidx ) { 5436 // Over init in 2 step: 5437 // 1) init with thumbnailBuildInit2 parameter 5438 // 2) init with the hover effect parameter 5439 5440 5441 var curTn = G.GOM.items[GOMidx]; 5442 var item = G.I[curTn.thumbnailIdx]; 5443 5444 if( item.$elt == null ) { return; } // zombie 5445 5446 var fu = G.O.fnThumbnailHoverInit; 5447 if( fu !== null ) { 5448 typeof fu == 'function' ? fu($e, item, GOMidx) : window[fu]($e, item, GOMidx); 5449 } 5450 5451 // build initialization 5452 var inits = G.tn.buildInit.get(); 5453 for( var j = 0; j < inits.length; j++) { 5454 switch( inits[j].property ) { 5455 // CSS Transform 5456 case 'scale': 5457 case 'rotateX': 5458 case 'rotateY': 5459 case 'rotateZ': 5460 case 'translateX': 5461 case 'translateY': 5462 case 'translateZ': 5463 item.CSSTransformSet(inits[j].element, inits[j].property, inits[j].value); 5464 item.CSSTransformApply(inits[j].element); 5465 break; 5466 // CSS filter 5467 case 'blur': 5468 case 'brightness': 5469 case 'grayscale': 5470 case 'sepia': 5471 case 'contrast': 5472 case 'opacity': 5473 case 'saturate': 5474 item.CSSFilterSet(inits[j].element, inits[j].property, inits[j].value); 5475 item.CSSFilterApply(inits[j].element); 5476 break; 5477 default: 5478 var $t=item.$getElt(inits[j].element); 5479 $t.css( inits[j].property, inits[j].value ); 5480 break; 5481 } 5482 } 5483 5484 // hover 5485 var effects = G.tn.hoverEffects.get(); 5486 for( var j = 0; j < effects.length; j++) { 5487 if( effects[j].firstKeyframe === true ) { 5488 switch( effects[j].type ) { 5489 case 'scale': 5490 case 'rotateX': 5491 case 'rotateY': 5492 case 'rotateZ': 5493 case 'translateX': 5494 case 'translateY': 5495 case 'translateZ': 5496 item.CSSTransformSet(effects[j].element, effects[j].type, effects[j].from); 5497 item.CSSTransformApply(effects[j].element); 5498 break; 5499 case 'blur': 5500 case 'brightness': 5501 case 'grayscale': 5502 case 'sepia': 5503 case 'contrast': 5504 case 'opacity': 5505 case 'saturate': 5506 item.CSSFilterSet(effects[j].element, effects[j].type, effects[j].from); 5507 item.CSSFilterApply(effects[j].element); 5508 break; 5509 default: 5510 var $t = item.$getElt(effects[j].element); 5511 $t.css( effects[j].type, effects[j].from ); 5512 break; 5513 5514 } 5515 } 5516 } 5517 item.hoverInitDone=true; 5518 } 5519 5520 function ThumbnailHoverReInitAll() { 5521 if( G.GOM.albumIdx == -1 ) { return; }; 5522 var l = G.GOM.items.length; 5523 for( var i = 0; i < l ; i++ ) { 5524 ThumbnailOverInit(i); 5525 // G.GOM.items[i].hovered=false; 5526 G.I[G.GOM.items[i].thumbnailIdx].hovered = false; 5527 } 5528 } 5529 5530 5531 function ThumbnailHover( GOMidx ) { 5532 if( G.GOM.albumIdx == -1 || !G.galleryResizeEventEnabled ) { return; }; 5533 if( G.GOM.slider.hostIdx == GOMidx ) { 5534 // slider hosted on thumbnail -> no hover effect 5535 return; 5536 } 5537 var curTn = G.GOM.items[GOMidx]; 5538 var item = G.I[curTn.thumbnailIdx]; 5539 if( item.kind == 'albumUp' || item.$elt == null ) { return; } 5540 5541 item.hovered = true; 5542 5543 var fu = G.O.fnThumbnailHover; 5544 if( fu !== null ) { 5545 typeof fu == 'function' ? fu(item.$elt, item, GOMidx) : window[fu](item.$elt, item, GOMidx); 5546 } 5547 var effects = G.tn.hoverEffects.get(); 5548 5549 try { 5550 for( var j = 0; j < effects.length; j++) { 5551 if( effects[j].hoverin === true ) { 5552 //item.animate( effects[j], j*10, true ); 5553 item.animate( effects[j], 0, true ); 5554 } 5555 } 5556 // effects on whole layout 5557 // GalleryResize( GOMidx ); 5558 } 5559 catch (e) { 5560 NanoAlert(G, 'error on hover: ' + e.message ); 5561 } 5562 5563 } 5564 5565 function ThumbnailHoverOutAll() { 5566 if( G.GOM.albumIdx == -1 ) { return; }; 5567 var l = G.GOM.items.length; 5568 for( var i = 0; i < l ; i++ ) { 5569 if( G.GOM.items[i].inDisplayArea ) { 5570 ThumbnailHoverOut(i); 5571 } 5572 else { 5573 G.I[G.GOM.items[i].thumbnailIdx].hovered = false; 5574 } 5575 } 5576 } 5577 5578 5579 function ThumbnailHoverOut( GOMidx ) { 5580 if( G.GOM.albumIdx == -1 || !G.galleryResizeEventEnabled ) { return; } 5581 5582 if( G.GOM.slider.hostIdx == GOMidx ) { 5583 // slider on thumbnail -> no hover effect 5584 return; 5585 } 5586 5587 var curTn = G.GOM.items[GOMidx]; 5588 var item = G.I[curTn.thumbnailIdx]; 5589 if( item.kind == 'albumUp' || !item.hovered ) { return; } 5590 item.hovered = false; 5591 if( item.$elt == null ) { return; } 5592 5593 var fu = G.O.fnThumbnailHoverOut; 5594 if( fu !== null ) { 5595 typeof fu == 'function' ? fu(item.$elt, item, GOMidx) : window[fu](item.$elt, item, GOMidx); 5596 } 5597 5598 var effects = G.tn.hoverEffects.get(); 5599 try { 5600 for( var j = 0; j < effects.length; j++) { 5601 if( effects[j].hoverout === true ) { 5602 // item.animate( effects[j], j*10, false ); 5603 item.animate( effects[j], 0, false ); 5604 } 5605 } 5606 // effects on whole layout 5607 // GalleryResize( ); 5608 } 5609 catch (e) { 5610 NanoAlert(G, 'error on hoverOut: ' + e.message ); 5611 } 5612 5613 } 5614 5615 5616 /** @function DisplayPhoto */ 5617 function DisplayPhoto( imageID, albumID ) { 5618 5619 if( G.O.debugMode ) { console.log('#DisplayPhoto : '+ albumID +'-'+ imageID); } 5620 var albumIdx = NGY2Item.GetIdx(G, albumID); 5621 if( albumIdx == 0 ) { 5622 G.GOM.curNavLevel = 'l1'; 5623 } 5624 else { 5625 G.GOM.curNavLevel = 'lN'; 5626 } 5627 5628 if( albumIdx == -1 ) { 5629 // get content of album on root level 5630 if( G.O.kind != '' ) { 5631 // do not add album if Markup or Javascript data 5632 NGY2Item.New( G, '', '', albumID, '0', 'album' ); // create empty album 5633 albumIdx = G.I.length - 1; 5634 } 5635 } 5636 5637 var ngy2ItemIdx = NGY2Item.GetIdx(G, imageID); 5638 if( ngy2ItemIdx == -1 ) { 5639 // get content of the album 5640 AlbumGetContent( albumID, DisplayPhoto, imageID, albumID ); 5641 return; 5642 } 5643 5644 if( G.O.debugMode ) { console.log('#DisplayPhoto : '+ ngy2ItemIdx); } 5645 5646 DisplayPhotoIdx(ngy2ItemIdx); 5647 5648 } 5649 5650 5651 // BETA -> NOT finished and not used at this time 5652 // Retrieve the title+description of ONE album 5653 function albumGetInfo( albumIdx, fnToCall ) { 5654 var url = ''; 5655 var kind = 'image'; 5656 5657 switch( G.O.kind ) { 5658 case 'json': 5659 // TODO 5660 case 'flickr': 5661 // TODO 5662 case 'picasa': 5663 case 'google': 5664 case 'google2': 5665 default: 5666 url = G.Google.url() + 'user/'+G.O.userID+'/albumid/'+G.I[albumIdx].GetID()+'?alt=json&&max-results=1&fields=title'; 5667 break; 5668 } 5669 5670 jQuery.ajaxSetup({ cache: false }); 5671 jQuery.support.cors = true; 5672 5673 var tId = setTimeout( function() { 5674 // workaround to handle JSONP (cross-domain) errors 5675 //PreloaderHide(); 5676 NanoAlert(G, 'Could not retrieve AJAX data...'); 5677 }, 60000 ); 5678 jQuery.getJSON(url, function(data, status, xhr) { 5679 clearTimeout(tId); 5680 //PreloaderHide(); 5681 5682 fnToCall( G.I[albumIdx].GetID() ); 5683 5684 }) 5685 .fail( function(jqxhr, textStatus, error) { 5686 clearTimeout(tId); 5687 //PreloaderHide(); 5688 var err = textStatus + ', ' + error; 5689 NanoAlert('Could not retrieve ajax data: ' + err); 5690 }); 5691 5692 } 5693 5694 5695 // function AlbumGetContent( albumIdx, fnToCall ) { 5696 function AlbumGetContent( albumID, fnToCall, fnParam1, fnParam2 ) { 5697 // var url=''; 5698 // var kind='image'; 5699 // var albumIdx=NGY2Item.GetIdx(G, albumID); 5700 // var photoIdx=NGY2Item.GetIdx(G, photoID); 5701 5702 switch( G.O.kind ) { 5703 // MARKUP / API 5704 case '': 5705 AlbumGetMarkupOrApi(fnToCall, fnParam1, fnParam2); 5706 break; 5707 // JSON, Flickr, Picasa, ... 5708 default: 5709 jQuery.nanogallery2['data_'+G.O.kind](G, 'AlbumGetContent', albumID, fnToCall, fnParam1, fnParam2 ); 5710 } 5711 5712 } 5713 5714 var mediaList = { 5715 youtube : { 5716 getID: function( url ) { 5717 // https://stackoverflow.com/questions/10591547/how-to-get-youtube-video-id-from-url 5718 var s = url.match( /(?:https?:\/{2})?(?:w{3}\.)?youtu(?:be)?\.(?:com|be)(?:\/watch\?v=|\/)([^\s&]+)/ ); 5719 return s != null ? s[1] : null; 5720 }, 5721 thumbUrl: function( id ) { 5722 return 'https://img.youtube.com/vi/' + id + '/hqdefault.jpg'; 5723 }, 5724 url: function( id ) { 5725 return 'https://www.youtube.com/embed/' + id; 5726 }, 5727 markup: function( id ) { 5728 // return '<iframe class="nGY2ViewerMedia" src="https://www.youtube.com/embed/' + id + '?rel=0" frameborder="0" gesture="media" allowfullscreen></iframe>'; 5729 return '<iframe class="nGY2ViewerMedia" src="https://www.youtube.com/embed/' + id + '?rel=0" frameborder="0" allow="autoplay" allowfullscreen></iframe>'; 5730 }, 5731 kind: 'iframe' 5732 }, 5733 vimeo : { 5734 getID: function( url ) { 5735 // https://stackoverflow.com/questions/2916544/parsing-a-vimeo-id-using-javascript 5736 var s = url.match( /^.*(vimeo\.com\/)((channels\/[A-z]+\/)|(groups\/[A-z]+\/videos\/))?([0-9]+)/ ); 5737 return s != null ? s[5] : null; 5738 }, 5739 url: function( id ) { 5740 return 'https://player.vimeo.com/video/' + id; 5741 }, 5742 markup: function( id ) { 5743 // return '<iframe class="nGY2ViewerMedia" src="https://player.vimeo.com/video/' + id + '?rel=0" frameborder="0" gesture="media" allowfullscreen></iframe>'; 5744 return '<iframe class="nGY2ViewerMedia" src="https://player.vimeo.com/video/' + id + '?rel=0" frameborder="0" allow="autoplay" allowfullscreen></iframe>'; 5745 }, 5746 kind: 'iframe' 5747 }, 5748 dailymotion : { 5749 getID: function( url ) { 5750 // https://stackoverflow.com/questions/12387389/how-to-parse-dailymotion-video-url-in-javascript 5751 var m = url.match(/^.+dailymotion.com\/(video|hub)\/([^_]+)[^#]*(#video=([^_&]+))?/); 5752 if (m !== null) { 5753 if(m[4] !== undefined) { 5754 return m[4]; 5755 } 5756 return m[2]; 5757 } 5758 return null; 5759 }, 5760 thumbUrl: function( id ) { 5761 return 'https://www.dailymotion.com/thumbnail/video/' + id; 5762 }, 5763 url: function( id ) { 5764 return 'https://www.dailymotion.com/embed/video/' + id; 5765 }, 5766 markup: function( id ) { 5767 // return '<iframe class="nGY2ViewerMedia" src="https://www.dailymotion.com/embed/video/' + id + '?rel=0" frameborder="0" gesture="media" allowfullscreen></iframe>'; 5768 return '<iframe class="nGY2ViewerMedia" src="https://www.dailymotion.com/embed/video/' + id + '?rel=0" frameborder="0" allow="autoplay" allowfullscreen></iframe>'; 5769 }, 5770 kind: 'iframe' 5771 }, 5772 selfhosted : { 5773 // SELF-HOSTED VIDEOS 5774 getID: function( url ) { 5775 // In order to leave things as is, I used ID to identify the extension 5776 // https://stackoverflow.com/questions/6997262/how-to-pull-url-file-extension-out-of-url-string-using-javascript 5777 // Make sure the method used for verifying the extension matches the kind of url your selfhosted video has 5778 var extension = url.split('.').pop(); 5779 5780 // supported extensions 5781 var s = ( extension === 'mp4' || extension === 'webm' || extension === 'ogv' || extension === '3gp' ) ? extension : null ; 5782 return s; 5783 }, 5784 markup: function( url ) { 5785 // 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>'; 5786 var extension = url.split('.').pop(); 5787 return '<video controls class="nGY2ViewerMedia"><source src="'+ url +'" type="video/'+ extension +'" preload="auto">Your browser does not support the video tag (HTML 5).</video>'; 5788 }, 5789 kind: 'video', 5790 selfhosted : true 5791 } 5792 }; 5793 5794 function AlbumGetMarkupOrApi ( fnToCall, fnParam1, fnParam2 ) { 5795 5796 if( G.markupOrApiProcessed === true ) { 5797 // already processed (maybe location hash to unknow reference) -> display root album 5798 DisplayAlbum('-1', 0); 5799 return; 5800 } 5801 5802 if( G.O.items !== undefined && G.O.items !== null ) { 5803 // data defined as an object in an option parameter 5804 GetContentApiObject(); 5805 } 5806 else { 5807 if( G.O.$markup.length > 0 ) { 5808 // data defined as markup (href elements) 5809 GetContentMarkup( G.O.$markup ); 5810 G.O.$markup=[] ; 5811 } 5812 else { 5813 NanoConsoleLog(G, 'error: no media to process.'); 5814 return; 5815 } 5816 } 5817 5818 G.markupOrApiProcessed = true; 5819 if( fnToCall !== null && fnToCall !== undefined) { 5820 fnToCall( fnParam1, fnParam2, null ); 5821 } 5822 } 5823 5824 function StartsWithProtocol ( path ) { 5825 if( path == null || path == undefined ) { return false; } 5826 5827 var pattern = /^((http|https|ftp|ftps|file):\/\/)/; 5828 if( !pattern.test(path) ) { 5829 // not a full URL 5830 return false; 5831 } 5832 return true; 5833 } 5834 5835 function GetContentApiObject() { 5836 var foundAlbumID=false; 5837 var nbTitles = 0; 5838 var AlbumPostProcess = NGY2Tools.AlbumPostProcess.bind(G); 5839 5840 G.I[0].contentIsLoaded=true; 5841 5842 jQuery.each(G.O.items, function(i,item){ 5843 5844 var title = ''; 5845 title=GetI18nItem(item, 'title'); 5846 if( title === undefined ) { title=''; } 5847 5848 var src=''; 5849 if( item['src'+RetrieveCurWidth().toUpperCase()] !== undefined ) { 5850 src = item['src'+RetrieveCurWidth().toUpperCase()]; 5851 } 5852 else { 5853 src = item.src; 5854 } 5855 if( !StartsWithProtocol(src) ) { 5856 src = G.O.itemsBaseURL + src; 5857 } 5858 5859 var thumbsrc = ''; 5860 if( item.srct !== undefined && item.srct.length > 0 ) { 5861 thumbsrc = item.srct; 5862 if( !StartsWithProtocol(thumbsrc) ) { 5863 thumbsrc = G.O.itemsBaseURL + thumbsrc; 5864 } 5865 } 5866 else { 5867 thumbsrc = src; 5868 } 5869 5870 var thumbsrcX2 = ''; 5871 if( item.srct2x !== undefined && item.srct2x.length > 0 ) { 5872 thumbsrcX2 = item.srct2x; 5873 if( !StartsWithProtocol(thumbsrcX2) ) { 5874 thumbsrcX2 = G.O.itemsBaseURL + thumbsrcX2; 5875 } 5876 } 5877 else { 5878 if( thumbsrc != '' ) { 5879 thumbsrcX2 = thumbsrc; 5880 } 5881 else { 5882 thumbsrcX2 = src; 5883 } 5884 } 5885 5886 if( G.O.thumbnailLabel.get('title') != '' ) { 5887 title = GetImageTitle(src); 5888 } 5889 5890 var description=''; //' '; 5891 description=GetI18nItem(item,'description'); 5892 if( description === undefined ) { description=''; } 5893 //if( toType(item.description) == 'string' ) { 5894 // description=item.description; 5895 //} 5896 5897 var tags = GetI18nItem(item, 'tags'); 5898 if( tags === undefined ) { tags=''; } 5899 5900 var albumID = 0; 5901 if( item.albumID !== undefined ) { 5902 albumID=item.albumID; 5903 foundAlbumID = true; 5904 } 5905 var ID = null; 5906 if( item.ID !== undefined ) { 5907 ID = item.ID; 5908 } 5909 var kind = 'image'; 5910 if( item.kind !== undefined && item.kind.length > 0 ) { 5911 kind = item.kind; 5912 } 5913 5914 var newItem=NGY2Item.New( G, title, description, ID, albumID, kind, tags ); 5915 if( title != '' ) { 5916 nbTitles++; 5917 } 5918 5919 // media source url - img is the default media kind 5920 newItem.setMediaURL( src, 'img', description ); 5921 5922 // manage media kinds other than IMG 5923 jQuery.each(mediaList, function ( n, media ) { 5924 var id = media.getID(src); 5925 if( id != null ) { 5926 if( typeof media.url == 'function' ) { src = media.url(id); } 5927 if( typeof media.thumbUrl == 'function' ) { thumbsrc = media.thumbUrl(id); } 5928 newItem.mediaKind = media.kind; 5929 newItem.mediaMarkup = ( media.selfhosted ) ? media.markup( src ) : media.markup(id); 5930 return false; 5931 } 5932 }); 5933 5934 // image size 5935 if( item.imageWidth !== undefined ) { newItem.imageWidth = item.width; } 5936 if( item.imageHeight !== undefined ) { newItem.imageHeight = item.height; } 5937 5938 // THUMBNAILS 5939 5940 // thumbnail image size 5941 var tw = item.imgtWidth !== undefined ? item.imgtWidth : 0; 5942 var th = item.imgtHeight !== undefined ? item.imgtHeight : 0; 5943 5944 // default thumbnail URL and size 5945 newItem.thumbs = { 5946 url: { l1 : { xs: thumbsrc, sm: thumbsrc, me: thumbsrc, la: thumbsrc, xl: thumbsrc }, lN : { xs: thumbsrc, sm: thumbsrc, me: thumbsrc, la: thumbsrc, xl: thumbsrc } }, 5947 width: { l1 : { xs: tw, sm: tw, me: tw, la: tw, xl: tw }, lN : { xs: tw, sm: tw, me: tw, la: tw, xl: tw } }, 5948 height: { l1 : { xs: th, sm: th, me: th, la: th, xl: th }, lN : { xs: th, sm: th, me: th, la: th, xl: th } } 5949 }; 5950 5951 // default media type -> IMG 5952 if( newItem.mediaKind == 'img' ) { 5953 5954 // responsive thumbnails URL and size 5955 var lst=['xs', 'sm', 'me', 'la', 'xl']; 5956 for( var i=0; i< lst.length; i++ ) { 5957 // url 5958 var turl = item['srct' + lst[i].toUpperCase()]; 5959 if( turl !== undefined ) { 5960 if( !StartsWithProtocol(turl) ) { 5961 turl = G.O.itemsBaseURL + turl; 5962 } 5963 newItem.url.l1[lst[i]] = turl; 5964 newItem.url.lN[lst[i]] = turl; 5965 } 5966 // width 5967 var tw = item['imgt' + lst[i].toUpperCase() + 'Width']; 5968 if( tw != undefined ) { 5969 newItem.width.l1[lst[i]] = parseInt(tw); 5970 newItem.width.lN[lst[i]] = parseInt(tw); 5971 } 5972 // height 5973 var th = item['imgt' + lst[i].toUpperCase() + 'Height']; 5974 if( th != undefined ) { 5975 newItem.height.l1[lst[i]] = parseInt(th); 5976 newItem.height.lN[lst[i]] = parseInt(th); 5977 } 5978 } 5979 } 5980 5981 // dominant colors (needs to be a base64 gif) 5982 if( item.imageDominantColors !== undefined ) { 5983 newItem.imageDominantColors = item.imageDominantColors; 5984 } 5985 // dominant color (rgb hex) 5986 if( item.imageDominantColor !== undefined ) { 5987 newItem.imageDominantColor = item.imageDominantColor; 5988 } 5989 5990 // dest url 5991 if( item.destURL !== undefined && item.destURL.length>0 ) { 5992 newItem.destinationURL = item.destURL; 5993 } 5994 5995 // download image url 5996 if( item.downloadURL !== undefined && item.downloadURL.length>0 ) { 5997 newItem.downloadURL = item.downloadURL; 5998 } 5999 6000 // EXIF DATA 6001 // Exif - model 6002 if( item.exifModel !== undefined ) { newItem.exif.model = item.exifModel; } 6003 // Exif - flash 6004 if( item.exifFlash !== undefined ) { newItem.exif.flash = item.exifFlash; } 6005 // Exif - focallength 6006 if( item.exifFocalLength !== undefined ) { newItem.exif.focallength = item.exifFocalLength; } 6007 // Exif - fstop 6008 if( item.exifFStop !== undefined ) { newItem.exif.fstop = item.exifFStop; } 6009 // Exif - exposure 6010 if( item.exifExposure !== undefined ) { newItem.exif.exposure = item.exifExposure; } 6011 // Exif - time 6012 if( item.exifIso !== undefined ) { newItem.exif.iso = item.exifIso; } 6013 // Exif - iso 6014 if( item.exifTime !== undefined ) { newItem.exif.time = item.exifTime; } 6015 // Exif - location 6016 if( item.exifLocation !== undefined ) { newItem.exif.location = item.exifLocation; } 6017 6018 6019 // custom data 6020 if( item.customData !== null ) { 6021 newItem.customData = cloneJSObject( item.customData ); 6022 } 6023 6024 newItem.contentIsLoaded = true; 6025 6026 var fu = G.O.fnProcessData; 6027 if( fu !== null ) { 6028 typeof fu == 'function' ? fu(newItem, 'api', item) : window[fu](newItem, 'api', item); 6029 } 6030 6031 AlbumPostProcess(albumID); 6032 }); 6033 6034 // if( foundAlbumID ) { G.O.displayBreadcrumb=true; } 6035 if( nbTitles == 0 ) { G.O.thumbnailLabel.display=false; } 6036 6037 } 6038 6039 6040 function GetContentMarkup( $elements ) { 6041 var foundAlbumID = false; 6042 var nbTitles = 0; 6043 var AlbumPostProcess = NGY2Tools.AlbumPostProcess.bind(G); 6044 6045 G.I[0].contentIsLoaded = true; 6046 6047 jQuery.each($elements, function(i, item){ 6048 6049 // create dictionnary with all data attribute name in lowercase (to be case unsensitive) 6050 var data = { 6051 // some default values 6052 'data-ngdesc': '', // item description 6053 'data-ngid': null, // ID 6054 'data-ngkind': 'image', // kind (image, album, albumup) 6055 'data-ngtags': null, // tags 6056 'data-ngdest': '', // destination URL 6057 'data-ngthumbimgwidth': 0, // thumbnail width 6058 'data-ngthumbimgheight': 0, // thumbnail height 6059 'data-ngimagewidth': 0, // image width 6060 'data-ngimageheight': 0, // image height 6061 'data-ngimagedominantcolors': null, // image dominant colors 6062 'data-ngimagedominantcolor': null, // image dominant colors 6063 'data-ngexifmodel': '', // EXIF data 6064 'data-ngexifflash': '', 6065 'data-ngexiffocallength': '', 6066 'data-ngexiffstop': '', 6067 'data-ngexifexposure': '', 6068 'data-ngexifiso': '', 6069 'data-ngexiftime': '', 6070 'data-ngexiflocation': '' 6071 }; 6072 [].forEach.call( item.attributes, function(attr) { 6073 data[attr.name.toLowerCase()] = attr.value; 6074 }); 6075 6076 // responsive image source 6077 var src = '', 6078 st = RetrieveCurWidth().toUpperCase(); 6079 if( data.hasOwnProperty('data-ngsrc'+st) ) { 6080 src = data['data-ngsrc'+st]; 6081 } 6082 if( src == '' ) { 6083 src = data['href']; 6084 } 6085 if( !StartsWithProtocol(src) ) { 6086 src = G.O.itemsBaseURL + src; 6087 } 6088 6089 // thumbnail 6090 var thumbsrc = ''; 6091 if( data.hasOwnProperty('data-ngthumb') ) { 6092 thumbsrc = data['data-ngthumb']; 6093 if( !StartsWithProtocol(thumbsrc) ) { 6094 thumbsrc = G.O.itemsBaseURL + thumbsrc; 6095 } 6096 } 6097 else { 6098 thumbsrc = src; 6099 } 6100 var thumbsrcX2 = ''; 6101 if( data.hasOwnProperty('data-ngthumb2x') ) { 6102 thumbsrcX2 = data['data-ngthumb2x']; 6103 if( !StartsWithProtocol(thumbsrcX2) ) { 6104 thumbsrcX2 = G.O.itemsBaseURL + thumbsrcX2; 6105 } 6106 } 6107 6108 //newObj.description=jQuery(item).attr('data-ngdesc'); 6109 var description = data['data-ngdesc']; 6110 var ID = data['id']; 6111 if( ID == undefined ) { 6112 ID = data['data-ngid']; 6113 } 6114 var kind = data['data-ngkind']; 6115 var tags = data['data-ngtags']; 6116 6117 var albumID = '0'; 6118 if( data.hasOwnProperty('data-ngalbumid') ) { 6119 albumID = data['data-ngalbumid']; 6120 foundAlbumID = true; 6121 } 6122 6123 var title = jQuery(item).text(); 6124 if( !(G.O.thumbnailLabel.get('title') == '' || G.O.thumbnailLabel.get('title') == undefined) ) { 6125 title = GetImageTitle(src); 6126 } 6127 6128 6129 var newItem = NGY2Item.New( G, title, description, ID, albumID, kind, tags ); 6130 if( title != '' ) { 6131 nbTitles++; 6132 } 6133 6134 // media source url - img is the default media kind 6135 newItem.setMediaURL( src, 'img', description); 6136 6137 // manage media kinds other than IMG 6138 newItem.mediaKind = 'img'; 6139 jQuery.each(mediaList, function ( n, media ) { 6140 var id = media.getID(src); 6141 if( id != null ) { 6142 if( typeof media.url == 'function' ) { src = media.url(id); } 6143 if( typeof media.thumbUrl == 'function' ) { thumbsrc = media.thumbUrl(id); } 6144 newItem.mediaKind = media.kind; 6145 newItem.mediaMarkup = ( media.selfhosted ) ? media.markup( src ) : media.markup(id); 6146 return false; 6147 } 6148 }); 6149 6150 6151 // image size 6152 newItem.imageWidth = parseInt( data['data-ngimagewidth'] ); 6153 newItem.imageHeight = parseInt( data['data-ngimageheight'] ); 6154 6155 // default thumbnail image URL and size 6156 var tw = parseInt(data['data-ngthumbimgwidth']); 6157 var th = parseInt(data['data-ngthumbimgheight']); 6158 newItem.thumbs = { 6159 url: { l1 : { xs: thumbsrc, sm: thumbsrc, me: thumbsrc, la: thumbsrc, xl: thumbsrc }, lN : { xs: thumbsrc, sm: thumbsrc, me: thumbsrc, la: thumbsrc, xl: thumbsrc } }, 6160 width: { l1 : { xs: tw, sm: tw, me: tw, la: tw, xl: tw }, lN : { xs: tw, sm: tw, me: tw, la: tw, xl: tw } }, 6161 height: { l1 : { xs: th, sm: th, me: th, la: th, xl: th }, lN : { xs: th, sm: th, me: th, la: th, xl: th } } 6162 }; 6163 6164 // default media type -> IMG 6165 if( newItem.mediaKind == 'img' ) { 6166 6167 // responsive thumbnails URL and size 6168 var lst = ['xs', 'sm', 'me', 'la', 'xl']; 6169 for( var i = 0; i < lst.length; i++ ) { 6170 // url 6171 if( data.hasOwnProperty('data-ngthumb' + lst[i]) ) { 6172 var turl=data['data-ngthumb' + lst[i]]; 6173 if( !StartsWithProtocol(turl) ) { 6174 turl = G.O.itemsBaseURL + turl; 6175 } 6176 newItem.url.l1[lst[i]] = turl; 6177 newItem.url.lN[lst[i]] = turl; 6178 } 6179 6180 // width 6181 if( data.hasOwnProperty('data-ngthumb' + lst[i] + 'width') ) { 6182 var tw=parseInt(data['data-ngthumb' + lst[i] + 'width']); 6183 newItem.width.l1[lst[i]] = tw; 6184 newItem.width.lN[lst[i]] = tw; 6185 } 6186 // height 6187 if( data.hasOwnProperty('data-ngthumb' + lst[i] + 'height') ) { 6188 var th=parseInt('data-ngthumb' + lst[i] + 'height'); 6189 newItem.height.l1[lst[i]] = th; 6190 newItem.height.lN[lst[i]] = th; 6191 } 6192 } 6193 } 6194 6195 6196 // dominant colorS (needs to be a base64 gif) 6197 newItem.imageDominantColors = data['data-ngimagedominantcolors']; 6198 // dominant color (rgb hex) 6199 newItem.imageDominantColor = data['data-ngimagedominantcolors']; 6200 6201 newItem.destinationURL = data['data-ngdest']; 6202 newItem.downloadURL = data['data-ngdownloadurl']; 6203 6204 // Exif - model 6205 newItem.exif.model=data['data-ngexifmodel']; 6206 // Exif - flash 6207 newItem.exif.flash=data['data-ngexifflash']; 6208 // Exif - focallength 6209 newItem.exif.focallength=data['data-ngexiffocallength']; 6210 // Exif - fstop 6211 newItem.exif.fstop=data['data-ngexiffstop']; 6212 // Exif - exposure 6213 newItem.exif.exposure=data['data-ngexifexposure']; 6214 // Exif - iso 6215 newItem.exif.iso=data['data-ngexifiso']; 6216 // Exif - time 6217 newItem.exif.time=data['data-ngexiftime']; 6218 // Exif - location 6219 newItem.exif.location=data['data-ngexiflocation']; 6220 6221 newItem.contentIsLoaded=true; 6222 6223 // custom data 6224 if( jQuery(item).data('customdata') !== undefined ) { 6225 newItem.customData=cloneJSObject(jQuery(item).data('customdata')); 6226 } 6227 // custom data 6228 if( jQuery(item).data('ngcustomdata') !== undefined ) { 6229 newItem.customData=cloneJSObject(jQuery(item).data('ngcustomdata')); 6230 } 6231 6232 var fu=G.O.fnProcessData; 6233 if( fu !== null ) { 6234 typeof fu == 'function' ? fu(newItem, 'markup', item) : window[fu](newItem, 'markup', item); 6235 } 6236 6237 AlbumPostProcess(albumID); 6238 6239 }); 6240 6241 // if( foundAlbumID ) { G.O.displayBreadcrumb=true; } 6242 if( nbTitles == 0 ) { G.O.thumbnailLabel.display = false; } 6243 6244 } 6245 6246 6247 // ################################ 6248 // ##### DEFINE VARIABLES ##### 6249 // ################################ 6250 6251 6252 /** @function DefineVariables */ 6253 function DefineVariables() { 6254 6255 // change 'picasa' to 'google' for compatibility reason 6256 if( G.O.kind.toUpperCase() == 'PICASA' || G.O.kind.toUpperCase() == 'GOOGLE') { 6257 G.O.kind='google2'; 6258 } 6259 6260 // management of screen width 6261 G.GOM.cache.viewport = getViewport(); 6262 G.GOM.curWidth = RetrieveCurWidth(); 6263 6264 // tumbnail toolbar 6265 jQuery.extend(true, G.tn.toolbar.image, G.O.thumbnailToolbarImage ); 6266 jQuery.extend(true, G.tn.toolbar.album, G.O.thumbnailToolbarAlbum ); 6267 var t = ['image', 'album']; 6268 var pos= ['topLeft', 'topRight', 'bottomLeft', 'bottomRight'] 6269 for( var i=0; i < t.length ; i++ ) { 6270 for( var j=0; j < pos.length ; j++ ) { 6271 G.tn.toolbar[t[i]][pos[j]] = G.tn.toolbar[t[i]][pos[j]].toUpperCase(); 6272 } 6273 } 6274 6275 // thumbnails label - level dependant settings 6276 G.O.thumbnailLabel.get = function( opt ) { 6277 if( G.GOM.curNavLevel == 'l1' && G.O.thumbnailL1Label !== undefined && G.O.thumbnailL1Label[opt] !== undefined ) { 6278 return G.O.thumbnailL1Label[opt]; 6279 } 6280 else { 6281 return G.O.thumbnailLabel[opt]; 6282 } 6283 }; 6284 G.O.thumbnailLabel.set = function( opt, value ) { 6285 if( G.GOM.curNavLevel == 'l1' && G.O.thumbnailL1Label !== undefined && G.O.thumbnailL1Label[opt] !== undefined ) { 6286 G.O.thumbnailL1Label[opt]=value; 6287 } 6288 else { 6289 G.O.thumbnailLabel[opt]=value; 6290 } 6291 }; 6292 6293 if( G.O.blackList != '' ) { G.blackList=G.O.blackList.toUpperCase().split('|'); } 6294 if( G.O.whiteList != '' ) { G.whiteList=G.O.whiteList.toUpperCase().split('|'); } 6295 6296 if( G.O.albumList2 !== undefined && G.O.albumList2 !== null && G.O.albumList2.constructor === Array ) { 6297 var l=G.O.albumList2.length; 6298 for(var i=0; i< l; i++ ) { 6299 G.albumList.push(G.O.albumList2[i]); 6300 } 6301 // G.albumList=G.O.albumList.toUpperCase().split('|'); 6302 } 6303 if( G.O.albumList2 !== undefined && typeof G.O.albumList2 == 'string' ) { 6304 G.albumList.push(G.O.albumList2); 6305 } 6306 6307 6308 // thumbnail image crop 6309 G.tn.opt.lN.crop = G.O.thumbnailCrop; 6310 G.tn.opt.l1.crop = G.O.thumbnailL1Crop != null ? G.O.thumbnailL1Crop : G.O.thumbnailCrop; 6311 6312 6313 function ThumbnailOpt( lN, l1, opt) { 6314 G.tn.opt.lN[opt]=G.O[lN]; 6315 G.tn.opt.l1[opt]=G.O[lN]; 6316 if( toType(G.O[l1]) == 'number' ) { 6317 G.tn.opt.l1[opt]=G.O[l1]; 6318 } 6319 } 6320 // thumbnail stacks 6321 ThumbnailOpt('thumbnailStacks', 'thumbnailL1Stacks', 'stacks'); 6322 // thumbnail stacks translate X 6323 ThumbnailOpt('thumbnailStacksTranslateX', 'thumbnailL1StacksTranslateX', 'stacksTranslateX'); 6324 // thumbnail stacks translate Y 6325 ThumbnailOpt('thumbnailStacksTranslateY', 'thumbnailL1StacksTranslateY', 'stacksTranslateY'); 6326 // thumbnail stacks translate Z 6327 ThumbnailOpt('thumbnailStacksTranslateZ', 'thumbnailL1StacksTranslateZ', 'stacksTranslateZ'); 6328 // thumbnail stacks rotate X 6329 ThumbnailOpt('thumbnailStacksRotateX', 'thumbnailL1StacksRotateX', 'stacksRotateX'); 6330 // thumbnail stacks rotate Y 6331 ThumbnailOpt('thumbnailStacksRotateY', 'thumbnailL1StacksRotateY', 'stacksRotateY'); 6332 // thumbnail stacks rotate Z 6333 ThumbnailOpt('thumbnailStacksRotateZ', 'thumbnailL1StacksRotateZ', 'stacksRotateZ'); 6334 // thumbnail stacks scale 6335 ThumbnailOpt('thumbnailStacksScale', 'thumbnailL1StacksScale', 'stacksScale'); 6336 // thumbnail gutter width 6337 ThumbnailOpt('thumbnailGutterWidth', 'thumbnailL1GutterWidth', 'gutterWidth'); 6338 // thumbnail gutter height 6339 ThumbnailOpt('thumbnailGutterHeight', 'thumbnailL1GutterHeight', 'gutterHeight'); 6340 // thumbnail grid base height (for cascading layout) 6341 ThumbnailOpt('thumbnailBaseGridHeight', 'thumbnailL1BaseGridHeight', 'baseGridHeight'); 6342 6343 // gallery display mode 6344 G.galleryDisplayMode.lN = G.O.galleryDisplayMode.toUpperCase(); 6345 G.galleryDisplayMode.l1 = G.O.galleryL1DisplayMode != null ? G.O.galleryL1DisplayMode.toUpperCase() : G.O.galleryDisplayMode.toUpperCase(); 6346 6347 // gallery maximum number of lines of thumbnails 6348 G.galleryMaxRows.lN = G.O.galleryMaxRows; 6349 G.galleryMaxRows.l1 = toType(G.O.galleryL1MaxRows) == 'number' ? G.O.galleryL1MaxRows : G.O.galleryMaxRows; 6350 6351 // gallery last row full 6352 G.galleryLastRowFull.lN = G.O.galleryLastRowFull; 6353 G.galleryLastRowFull.l1 = G.O.galleryL1LastRowFull != null ? G.O.galleryL1LastRowFull : G.O.galleryLastRowFull; 6354 6355 // gallery sorting 6356 G.gallerySorting.lN = G.O.gallerySorting.toUpperCase(); 6357 G.gallerySorting.l1 = G.O.galleryL1Sorting != null ? G.O.galleryL1Sorting.toUpperCase() : G.gallerySorting.lN; 6358 6359 // gallery display transition 6360 G.galleryDisplayTransition.lN = G.O.galleryDisplayTransition.toUpperCase(); 6361 G.galleryDisplayTransition.l1 = G.O.galleryL1DisplayTransition != null ? G.O.galleryL1DisplayTransition.toUpperCase() : G.galleryDisplayTransition.lN; 6362 6363 // gallery display transition duration 6364 G.galleryDisplayTransitionDuration.lN = G.O.galleryDisplayTransitionDuration; 6365 G.galleryDisplayTransitionDuration.l1 = G.O.galleryL1DisplayTransitionDuration != null ? G.O.galleryL1DisplayTransitionDuration : G.galleryDisplayTransitionDuration.lN; 6366 6367 // gallery max items per album (not for inline/api defined items) 6368 G.galleryMaxItems.lN = G.O.galleryMaxItems; 6369 G.galleryMaxItems.l1 = toType(G.O.galleryL1MaxItems) == 'number' ? G.O.galleryL1MaxItems : G.O.galleryMaxItems; 6370 6371 // gallery filter tags 6372 G.galleryFilterTags.lN = G.O.galleryFilterTags; 6373 G.galleryFilterTags.l1 = G.O.galleryL1FilterTags != null ? G.O.galleryL1FilterTags : G.O.galleryFilterTags; 6374 6375 // gallery pagination 6376 G.O.galleryPaginationMode = G.O.galleryPaginationMode.toUpperCase(); 6377 6378 if( toType(G.O.slideshowDelay) == 'number' && G.O.slideshowDelay >= 2000 ) { 6379 G.VOM.slideshowDelay = G.O.slideshowDelay; 6380 } 6381 else { 6382 NanoConsoleLog(G, 'Parameter "slideshowDelay" must be an integer >= 2000 ms.'); 6383 } 6384 6385 // gallery display transition 6386 if( typeof G.O.thumbnailDisplayTransition == 'boolean' ) { 6387 if( G.O.thumbnailDisplayTransition === true ) { 6388 G.tn.opt.lN.displayTransition = 'FADEIN'; 6389 G.tn.opt.l1.displayTransition = 'FADEIN'; 6390 } 6391 else { 6392 G.tn.opt.lN.displayTransition = 'NONE'; 6393 G.tn.opt.l1.displayTransition = 'NONE'; 6394 } 6395 } 6396 6397 if( G.O.fnThumbnailDisplayEffect !== '' ) { 6398 G.tn.opt.lN.displayTransition = 'CUSTOM'; 6399 G.tn.opt.l1.displayTransition = 'CUSTOM'; 6400 } 6401 if( G.O.fnThumbnailL1DisplayEffect !== '' ) { 6402 G.tn.opt.l1.displayTransition = 'CUSTOM'; 6403 } 6404 6405 // parse thumbnail display transition 6406 function thumbnailDisplayTransitionParse( cfg, level ) { 6407 if( typeof cfg == 'string' ) { 6408 var st=cfg.split('_'); 6409 if( st.length == 1 ) { 6410 G.tn.opt[level]['displayTransition'] = cfg.toUpperCase(); 6411 } 6412 if( st.length == 2 ) { 6413 G.tn.opt[level]['displayTransition'] = st[0].toUpperCase();
6414 G.tn.opt[level]['displayTransitionStartVal'] = Number(st[1]); 6415 } 6416 if( st.length == 3 ) { 6417 G.tn.opt[level]['displayTransition'] = st[0].toUpperCase(); 6418 G.tn.opt[level]['displayTransitionStartVal'] = Number(st[1]); 6419 G.tn.opt[level]['displayTransitionEasing'] = st[2]; 6420 } 6421 } 6422 } 6423 thumbnailDisplayTransitionParse( G.O.thumbnailDisplayTransition, 'lN'); 6424 thumbnailDisplayTransitionParse( G.O.thumbnailDisplayTransition, 'l1'); 6425 thumbnailDisplayTransitionParse( G.O.thumbnailL1DisplayTransition, 'l1'); 6426 6427 6428 // thumbnail display transition duration 6429 ThumbnailOpt('thumbnailDisplayTransitionDuration', 'thumbnailL1DisplayTransitionDuration', 'displayTransitionDuration'); 6430 // thumbnail display transition interval duration 6431 ThumbnailOpt('thumbnailDisplayInterval', 'thumbnailL1DisplayInterval', 'displayInterval'); 6432 6433 6434 // resolution breakpoints --> convert old syntax to new one 6435 if( G.O.thumbnailSizeSM !== undefined ) { G.O.breakpointSizeSM = G.O.thumbnailSizeSM; } 6436 if( G.O.thumbnailSizeME !== undefined ) { G.O.breakpointSizeME = G.O.thumbnailSizeME; } 6437 if( G.O.thumbnailSizeLA !== undefined ) { G.O.breakpointSizeLA = G.O.thumbnailSizeLA; } 6438 if( G.O.thumbnailSizeXL !== undefined ) { G.O.breakpointSizeXL = G.O.thumbnailSizeXL; } 6439 6440 // THUMBNAIL BUILD INIT 6441 //level 1 6442 if( G.O.thumbnailL1BuildInit2 !== undefined ) { 6443 var t1 = G.O.thumbnailL1BuildInit2.split('|'); 6444 for( var i = 0; i < t1.length; i++ ) { 6445 var o1 = t1[i].trim().split('_'); 6446 if( o1.length == 3 ) { 6447 var i1 = NewTBuildInit(); 6448 i1.element = ThumbnailOverEffectsGetCSSElement(o1[0], ''); 6449 i1.property = o1[1]; 6450 i1.value = o1[2]; 6451 G.tn.buildInit.level1.push(i1); 6452 } 6453 } 6454 } 6455 //level N 6456 if( G.O.thumbnailBuildInit2 !== undefined ) { 6457 var t1 = G.O.thumbnailBuildInit2.split('|'); 6458 for( var i = 0; i < t1.length; i++ ) { 6459 var o1 = t1[i].trim().split('_'); 6460 if( o1.length == 3 ) { 6461 var i1 = NewTBuildInit(); 6462 i1.element = ThumbnailOverEffectsGetCSSElement(o1[0], ''); 6463 i1.property = o1[1]; 6464 i1.value = o1[2]; 6465 G.tn.buildInit.std.push(i1); 6466 } 6467 } 6468 } 6469 6470 6471 // THUMBNAIL HOVER EFFETCS 6472 6473 // thumbnails hover effects - Level1 6474 var tL1HE = G.O.thumbnailL1HoverEffect2; 6475 if( tL1HE !== undefined ) { 6476 switch( toType(tL1HE) ) { 6477 case 'string': 6478 var tmp = tL1HE.split('|'); 6479 for(var i = 0; i < tmp.length; i++) { 6480 var oDef = NewTHoverEffect(); 6481 oDef = ThumbnailHoverEffectExtract( tmp[i].trim(), oDef ); 6482 if( oDef != null ) { 6483 G.tn.hoverEffects.level1.push(oDef); 6484 } 6485 } 6486 break; 6487 case 'object': 6488 var oDef = NewTHoverEffect(); 6489 oDef = jQuery.extend(oDef,tL1HE); 6490 oDef = ThumbnailHoverEffectExtract( oDef.name, oDef ); 6491 if( oDef != null ) { 6492 G.tn.hoverEffects.level1.push(oDef); 6493 } 6494 break; 6495 case 'array': 6496 for(var i = 0; i < tL1HE.length; i++) { 6497 var oDef = NewTHoverEffect(); 6498 oDef = jQuery.extend(oDef,tL1HE[i]); 6499 oDef = ThumbnailHoverEffectExtract( oDef.name, oDef ); 6500 if( oDef != null ) { 6501 G.tn.hoverEffects.level1.push(oDef); 6502 } 6503 } 6504 break; 6505 case 'null': 6506 break; 6507 default: 6508 NanoAlert(G, 'incorrect parameter for "thumbnailL1HoverEffect2".'); 6509 } 6510 } 6511 G.tn.hoverEffects.level1 = ThumbnailOverEffectsPreset(G.tn.hoverEffects.level1); 6512 6513 // thumbnails hover effects - other levels 6514 var tHE = G.O.thumbnailHoverEffect2; 6515 switch( toType(tHE) ) { 6516 case 'string': 6517 var tmp = tHE.split('|'); 6518 for(var i = 0; i < tmp.length; i++) { 6519 var oDef = NewTHoverEffect(); 6520 oDef = ThumbnailHoverEffectExtract( tmp[i].trim(), oDef ); 6521 if( oDef != null ) { 6522 G.tn.hoverEffects.std.push(oDef); 6523 } 6524 } 6525 break; 6526 case 'object': 6527 var oDef = NewTHoverEffect(); 6528 oDef = jQuery.extend(oDef, tHE); 6529 oDef = ThumbnailHoverEffectExtract( oDef.name, oDef ); 6530 if( oDef != null ) { 6531 G.tn.hoverEffects.std.push(oDef); 6532 } 6533 break; 6534 case 'array': 6535 for(var i = 0; i < tHE.length; i++) { 6536 var oDef = NewTHoverEffect(); 6537 oDef = jQuery.extend(oDef,tHE[i]); 6538 oDef = ThumbnailHoverEffectExtract( oDef.name, oDef ); 6539 if( oDef!= null ) { 6540 G.tn.hoverEffects.std.push(oDef); 6541 } 6542 } 6543 break; 6544 case 'null': 6545 break; 6546 default: 6547 NanoAlert(G, 'incorrect parameter for "thumbnailHoverEffect2".'); 6548 } 6549 G.tn.hoverEffects.std = ThumbnailOverEffectsPreset(G.tn.hoverEffects.std); 6550 6551 6552 if( G.O.touchAnimationL1 == undefined ) { 6553 G.O.touchAnimationL1 = G.O.touchAnimation; 6554 } 6555 6556 // disable thumbnail touch animation when no hover effect defined 6557 if( G.tn.hoverEffects.std.length == 0 ) { 6558 if( G.tn.hoverEffects.level1.length == 0 ) { 6559 G.O.touchAnimationL1 = false; 6560 } 6561 G.O.touchAnimation = false; 6562 } 6563 6564 6565 // thumbnail sizes 6566 if( G.O.thumbnailHeight == 0 || G.O.thumbnailHeight == '' ) { G.O.thumbnailHeight = 'auto'; } 6567 if( G.O.thumbnailWidth == 0 || G.O.thumbnailWidth == '' ) { G.O.thumbnailWidth = 'auto'; } 6568 if( G.O.thumbnailL1Height == 0 || G.O.thumbnailL1Height == '' ) { G.O.thumbnailL1Height = 'auto'; } 6569 if( G.O.thumbnailL1Width == 0 || G.O.thumbnailL1Width == '' ) { G.O.thumbnailL1Width = 'auto'; } 6570 6571 // RETRIEVE ALL THUMBNAIL SIZES 6572 function ThumbnailSizes( srcOpt, onlyl1, opt) { 6573 if( G.O[srcOpt] == null ) { return; } 6574 6575 if( toType(G.O[srcOpt]) == 'number' ) { 6576 ThumbnailsSetSize( opt, 'l1', G.O[srcOpt], 'u'); 6577 if( !onlyl1 ) { 6578 ThumbnailsSetSize( opt, 'lN', G.O[srcOpt], 'u'); 6579 } 6580 } 6581 else { 6582 var ws=G.O[srcOpt].split(' '); 6583 var v = 'auto'; 6584 if( ws[0].substring(0,4) != 'auto' ) { v=parseInt(ws[0]); } 6585 var c = 'u'; 6586 if( ws[0].charAt(ws[0].length - 1) == 'C' ) { c='c'; } 6587 ThumbnailsSetSize( opt, 'l1', v, c ); // default value for all resolutions and navigation levels 6588 if( !onlyl1 ) { 6589 ThumbnailsSetSize( opt, 'lN', v, c ); 6590 } 6591 for( var i = 1; i < ws.length; i++ ) { 6592 var r = ws[i].substring(0,2).toLowerCase(); 6593 if( /xs|sm|me|la|xl/i.test(r) ) { 6594 var w = ws[i].substring(2); 6595 var v = 'auto'; 6596 if( w.substring(0,4) != 'auto' ) { v = parseInt(w); } 6597 var c = 'u'; 6598 if( w.charAt(w.length - 1) == 'C' ) { c = 'c'; } 6599 G.tn.settings[opt]['l1'][r] = v; 6600 G.tn.settings[opt]['l1'][r + 'c'] = c; 6601 if( !onlyl1 ) { 6602 G.tn.settings[opt]['lN'][r] = v; 6603 G.tn.settings[opt]['lN'][r + 'c'] = c; 6604 } 6605 } 6606 } 6607 } 6608 } 6609 ThumbnailSizes( 'thumbnailWidth', false, 'width'); 6610 ThumbnailSizes( 'thumbnailL1Width', true, 'width'); 6611 6612 ThumbnailSizes( 'thumbnailHeight', false, 'height'); 6613 ThumbnailSizes( 'thumbnailL1Height', true, 'height'); 6614 6615 6616 G.O.thumbnailBorderHorizontal = parseInt(G.O.thumbnailBorderHorizontal); 6617 G.O.thumbnailBorderVertical = parseInt(G.O.thumbnailBorderVertical); 6618 G.O.thumbnailLabelHeight = parseInt(G.O.thumbnailLabelHeight); 6619 6620 6621 // retrieve all mosaic layout patterns 6622 // default pattern 6623 if( G.O.galleryMosaic != undefined ) { 6624 // clone object 6625 G.tn.settings.mosaic.l1.xs = JSON.parse(JSON.stringify(G.O.galleryMosaic)); 6626 G.tn.settings.mosaic.l1.sm = JSON.parse(JSON.stringify(G.O.galleryMosaic)); 6627 G.tn.settings.mosaic.l1.me = JSON.parse(JSON.stringify(G.O.galleryMosaic)); 6628 G.tn.settings.mosaic.l1.la = JSON.parse(JSON.stringify(G.O.galleryMosaic)); 6629 G.tn.settings.mosaic.l1.xl = JSON.parse(JSON.stringify(G.O.galleryMosaic)); 6630 G.tn.settings.mosaic.lN.xs = JSON.parse(JSON.stringify(G.O.galleryMosaic)); 6631 G.tn.settings.mosaic.lN.sm = JSON.parse(JSON.stringify(G.O.galleryMosaic)); 6632 G.tn.settings.mosaic.lN.me = JSON.parse(JSON.stringify(G.O.galleryMosaic)); 6633 G.tn.settings.mosaic.lN.la = JSON.parse(JSON.stringify(G.O.galleryMosaic)); 6634 G.tn.settings.mosaic.lN.xl = JSON.parse(JSON.stringify(G.O.galleryMosaic)); 6635 G.tn.settings.mosaicCalcFactor('l1', 'xs'); 6636 G.tn.settings.mosaicCalcFactor('l1', 'sm'); 6637 G.tn.settings.mosaicCalcFactor('l1', 'me'); 6638 G.tn.settings.mosaicCalcFactor('l1', 'la'); 6639 G.tn.settings.mosaicCalcFactor('l1', 'xl'); 6640 G.tn.settings.mosaicCalcFactor('lN', 'xs'); 6641 G.tn.settings.mosaicCalcFactor('lN', 'sm'); 6642 G.tn.settings.mosaicCalcFactor('lN', 'me'); 6643 G.tn.settings.mosaicCalcFactor('lN', 'la'); 6644 G.tn.settings.mosaicCalcFactor('lN', 'xl'); 6645 } 6646 if( G.O.galleryL1Mosaic != undefined ) { 6647 // default L1 pattern 6648 G.tn.settings.mosaic.l1.xs = JSON.parse(JSON.stringify(G.O.galleryL1Mosaic)); 6649 G.tn.settings.mosaic.l1.sm = JSON.parse(JSON.stringify(G.O.galleryL1Mosaic)); 6650 G.tn.settings.mosaic.l1.me = JSON.parse(JSON.stringify(G.O.galleryL1Mosaic)); 6651 G.tn.settings.mosaic.l1.la = JSON.parse(JSON.stringify(G.O.galleryL1Mosaic)); 6652 G.tn.settings.mosaic.l1.xl = JSON.parse(JSON.stringify(G.O.galleryL1Mosaic)); 6653 G.tn.settings.mosaicCalcFactor('l1', 'xs'); 6654 G.tn.settings.mosaicCalcFactor('l1', 'sm'); 6655 G.tn.settings.mosaicCalcFactor('l1', 'me'); 6656 G.tn.settings.mosaicCalcFactor('l1', 'la'); 6657 G.tn.settings.mosaicCalcFactor('l1', 'xl'); 6658 } 6659 for( var w = 0; w < G.tn.settings.mosaic.l1; w++ ) {
vendor: 83,618 bytes, lines 6660-8722
6660 if( G.O['galleryMosaic' + G.tn.settings.mosaic.l1[w].toUpperCase()] != undefined ) { 6661 G.tn.settings.mosaic.lN[tn.settings.mosaic.l1[w]] = JSON.parse(JSON.stringify( G.O['galleryMosaic' + G.tn.settings.mosaic.l1[w].toUpperCase()] )); 6662 G.tn.settings.mosaic.l1[tn.settings.mosaic.l1[w]] = JSON.parse(JSON.stringify( G.O['galleryMosaic' + G.tn.settings.mosaic.l1[w].toUpperCase()] )); 6663 G.tn.settings.mosaicCalcFactor('l1', G.tn.settings.mosaic.l1[w]); 6664 G.tn.settings.mosaicCalcFactor('lN', G.tn.settings.mosaic.l1[w]); 6665 } 6666 } 6667 for( var w = 0; w < G.tn.settings.mosaic.l1; w++ ) { 6668 if( G.O['galleryL1Mosaic'+G.tn.settings.mosaic.l1[w].toUpperCase()] != undefined ) { 6669 G.tn.settings.mosaic.l1[tn.settings.mosaic.l1[w]] = JSON.parse(JSON.stringify( G.O['galleryL1Mosaic'+G.tn.settings.mosaic.l1[w].toUpperCase()] )); 6670 G.tn.settings.mosaicCalcFactor('l1', G.tn.settings.mosaic.l1[w]); 6671 } 6672 } 6673 6674 G.O.imageTransition = G.O.imageTransition.toUpperCase(); 6675 6676 G.layout.SetEngine(); 6677 6678 // init plugins 6679 switch( G.O.kind ) { 6680 // MARKUP / API 6681 case '': 6682 break; 6683 // JSON, Flickr, Picasa, ... 6684 default: 6685 jQuery.nanogallery2['data_' + G.O.kind](G, 'Init' ); 6686 } 6687 6688 } 6689 6690 // HOVER EFFECTS 6691 function ThumbnailHoverEffectExtract( name, effect) { 6692 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']; 6693 6694 var sp = name.split('_'); 6695 if( sp.length >= 4 ) { 6696 // var oDef=NewTHoverEffect(); 6697 effect.name = ''; 6698 effect.type = sp[1]; 6699 effect.from = sp[2]; 6700 effect.to = sp[3]; 6701 if( sp.length >= 5 ) { 6702 // effect.duration=sp[4]; 6703 6704 for( var n = 4; n < sp.length; n++ ) { 6705 var v = sp[n]; 6706 6707 // check if an easing name 6708 var foundEasing = false; 6709 for( var e = 0; e < easings.length; e++) { 6710 if( v == easings[e] ) { 6711 foundEasing = true; 6712 effect.easing = v; 6713 break; 6714 } 6715 } 6716 if( foundEasing === true ) { 6717 continue; 6718 } 6719 6720 v = v.toUpperCase(); 6721 6722 if( v == 'HOVERIN' ) { 6723 effect.hoverout = false; 6724 continue; 6725 } 6726 if( v == 'HOVEROUT' ) { 6727 effect.hoverin = false; 6728 continue; 6729 } 6730 6731 if( v == 'KEYFRAME' ) { 6732 effect.firstKeyframe = false; 6733 continue; 6734 } 6735 6736 var num = parseInt(v.replace(/[^0-9\.]/g, ''), 10); // extract a number if one exists 6737 6738 if( num > 0 ) { 6739 // the string contains a numbers > 0 6740 if( v.indexOf('DURATION') >= 0 ) { 6741 effect.duration = num; 6742 continue; 6743 } 6744 if( v.indexOf('DURATIONBACK') >= 0 ) { 6745 effect.durationBack = num; 6746 continue; 6747 } 6748 if( v.indexOf('DELAY') >= 0 ) { 6749 effect.delay = num; 6750 continue; 6751 } 6752 if( v.indexOf('DELAYBACK') >= 0 ) { 6753 effect.delayBack = num; 6754 continue; 6755 } 6756 6757 // no parameter name found -> default is duration 6758 effect.duration = num; 6759 } 6760 } 6761 } 6762 effect.element=ThumbnailOverEffectsGetCSSElement(sp[0], effect.type); 6763 6764 } 6765 else { 6766 effect.name = name; 6767 // NanoAlert(G, 'incorrect parameter for "thumbnailHoverEffect": ' + name); 6768 // return null; 6769 } 6770 return effect; 6771 } 6772 6773 6774 function ThumbnailOverEffectsGetCSSElement( element, property ) { 6775 var r = element; 6776 6777 switch ( element ) { 6778 case 'image': 6779 if( property == 'blur' || property == 'brightness' || property == 'grayscale' || property == 'sepia' || property == 'contrast' || property == 'opacity'|| property == 'saturate' ) { 6780 // r='.nGY2GThumbnailImg'; 6781 r = '.nGY2GThumbnailImage'; 6782 } 6783 else { 6784 r = '.nGY2GThumbnailImage'; 6785 } 6786 break; 6787 case 'thumbnail': 6788 r = '.nGY2GThumbnail'; 6789 break; 6790 case 'label': 6791 r = '.nGY2GThumbnailLabel'; 6792 break; 6793 case 'title': 6794 r = '.nGY2GThumbnailTitle'; 6795 break; 6796 case 'description': 6797 r = '.nGY2GThumbnailDescription'; 6798 break; 6799 case 'tools': 6800 r = '.nGY2GThumbnailIcons'; 6801 break; 6802 case 'customlayer': 6803 r = '.nGY2GThumbnailCustomLayer'; 6804 break; 6805 } 6806 return r; 6807 } 6808 6809 // convert preset hover effects to new ones (nanogallery2) 6810 function ThumbnailOverEffectsPreset( effects ) { 6811 6812 // COMPATIBILITY WITH nanoGALLERY 6813 // OK: 6814 // 'borderLighter', 'borderDarker', 'scale120', 'labelAppear', 'labelAppear75', 'labelOpacity50', 'scaleLabelOverImage' 6815 // 'overScale', 'overScaleOutside', 'descriptionAppear' 6816 // 'slideUp', 'slideDown', 'slideRight', 'slideLeft' 6817 // 'imageScale150', 'imageScaleIn80', 'imageScale150Outside', 'imageSlideUp', 'imageSlideDown', 'imageSlideRight', 'imageSlideLeft' 6818 // 'labelSlideUpTop', 'labelSlideUp', 'labelSlideDown', 'descriptionSlideUp' 6819 // KO: 6820 // 'labelSplit4', 'labelSplitVert', 'labelAppearSplit4', 'labelAppearSplitVert' 6821 // TODO: 6822 // 'rotateCornerBL', 'rotateCornerBR', 'imageSplit4', 'imageSplitVert', 'imageRotateCornerBL', 'imageRotateCornerBR', 'imageFlipHorizontal', 'imageFlipVertical' 6823 6824 6825 6826 var newEffects=[]; 6827 for( var i=0; i< effects.length; i++ ) { 6828 switch( effects[i].name.toUpperCase() ) { 6829 case 'BORDERLIGHTER': 6830 // var color=ngtinycolor(GalleryThemeGetCurrent().thumbnail.borderColor); 6831 // name='thumbnail_borderColor_'+color.toRgbString()+'_'+color.lighten(50).toRgbString(); 6832 6833 var rgb = ColorHelperToRGB(GalleryThemeGetCurrent().thumbnail.borderColor); 6834 name = 'thumbnail_borderColor_'+rgb+'_'+ShadeBlendConvert(0.5, rgb ); 6835 newEffects.push(ThumbnailHoverEffectExtract(name, effects[i])); 6836 break; 6837 case 'BORDERDARKER': 6838 // var color=ngtinycolor(GalleryThemeGetCurrent().thumbnail.borderColor); 6839 // name='thumbnail_borderColor_'+color.toRgbString()+'_'+color.darken(50).toRgbString(); 6840 var rgb = ColorHelperToRGB(GalleryThemeGetCurrent().thumbnail.borderColor); 6841 name = 'thumbnail_borderColor_'+rgb+'_'+ShadeBlendConvert(-0.5, rgb ); 6842 newEffects.push(ThumbnailHoverEffectExtract(name, effects[i])); 6843 break; 6844 case 'SCALE120': 6845 newEffects.push(ThumbnailHoverEffectExtract('thumbnail_scale_1.00_1.20', effects[i])); 6846 break; 6847 case 'LABELAPPEAR': 6848 case 'LABELAPPEAR75': 6849 newEffects.push(ThumbnailHoverEffectExtract('label_opacity_0.00_1.00', effects[i])); 6850 break; 6851 case 'TOOLSAPPEAR': 6852 newEffects.push(ThumbnailHoverEffectExtract('tools_opacity_0_1', effects[i])); 6853 break; 6854 case 'TOOLSSLIDEDOWN': 6855 newEffects.push(ThumbnailHoverEffectExtract('tools_translateY_-100%_0%', effects[i])); 6856 break; 6857 case 'TOOLSSLIDEUP': 6858 newEffects.push(ThumbnailHoverEffectExtract('tools_translateY_100%_0%', effects[i])); 6859 break; 6860 case 'LABELOPACITY50': 6861 newEffects.push(ThumbnailHoverEffectExtract('label_opacity_1.00_0.50', effects[i])); 6862 break; 6863 case 'LABELSLIDEUPTOP': 6864 case 'LABELSLIDEUP': 6865 newEffects.push(ThumbnailHoverEffectExtract('label_translateY_100%_0%', effects[i])); 6866 break; 6867 case 'LABELSLIDEDOWN': 6868 newEffects.push(ThumbnailHoverEffectExtract('label_translateY_-100%_0%', effects[i])); 6869 break; 6870 case 'SCALELABELOVERIMAGE': 6871 newEffects.push(ThumbnailHoverEffectExtract('label_scale_0.00_1.00', effects[i])); 6872 var n = cloneJSObject(effects[i]); 6873 newEffects.push(ThumbnailHoverEffectExtract('image_scale_1.00_0.00', n)); 6874 break; 6875 case 'OVERSCALE': 6876 case 'OVERSCALEOUTSIDE': 6877 name = 'label_scale_0_100'; 6878 newEffects.push(ThumbnailHoverEffectExtract('label_scale_2.00_1.00', effects[i])); 6879 var n = cloneJSObject(effects[i]); 6880 newEffects.push(ThumbnailHoverEffectExtract('label_opacity_0.00_1.00', n)); 6881 n = cloneJSObject(effects[i]); 6882 newEffects.push(ThumbnailHoverEffectExtract('image_scale_1.00_0.00', n)); 6883 n = cloneJSObject(effects[i]); 6884 newEffects.push(ThumbnailHoverEffectExtract('image_opacity_1.00_0.00', n)); 6885 break; 6886 case 'DESCRIPTIONAPPEAR': 6887 newEffects.push(ThumbnailHoverEffectExtract('description_opacity_0_1', effects[i])); 6888 break; 6889 case 'SLIDERIGHT': 6890 newEffects.push(ThumbnailHoverEffectExtract('image_translateX_0%_100%', effects[i])); 6891 newEffects.push(ThumbnailHoverEffectExtract('label_translateX_-100%_0%', cloneJSObject(effects[i]))); 6892 break; 6893 case 'SLIDELEFT': 6894 newEffects.push(ThumbnailHoverEffectExtract('image_translateX_0%_-100%', effects[i])); 6895 newEffects.push(ThumbnailHoverEffectExtract('label_translateX_100%_0%', cloneJSObject(effects[i]))); 6896 break; 6897 case 'SLIDEUP': 6898 newEffects.push(ThumbnailHoverEffectExtract('image_translateY_0%_-100%', effects[i])); 6899 newEffects.push(ThumbnailHoverEffectExtract('label_translateY_100%_0%', cloneJSObject(effects[i]))); 6900 break; 6901 case 'SLIDEDOWN': 6902 newEffects.push(ThumbnailHoverEffectExtract('image_translateY_0%_100%', effects[i])); 6903 newEffects.push(ThumbnailHoverEffectExtract('label_translateY_-100%_0%', cloneJSObject(effects[i]))); 6904 break; 6905 case 'IMAGESCALE150': 6906 case 'IMAGESCALE150OUTSIDE': 6907 newEffects.push(ThumbnailHoverEffectExtract('image_scale_1.00_1.50', effects[i])); 6908 break; 6909 case 'IMAGESCALEIN80': 6910 newEffects.push(ThumbnailHoverEffectExtract('image_scale_1.20_1.00', effects[i])); 6911 break; 6912 case 'IMAGESLIDERIGHT': 6913 newEffects.push(ThumbnailHoverEffectExtract('image_translateX_0%_100%', effects[i])); 6914 break; 6915 case 'IMAGESLIDELEFT': 6916 newEffects.push(ThumbnailHoverEffectExtract('image_translateX_0%_-100%', effects[i])); 6917 break; 6918 case 'IMAGESLIDEUP': 6919 newEffects.push(ThumbnailHoverEffectExtract('image_translateY_0%_-100%', effects[i])); 6920 break; 6921 case 'IMAGESLIDEDOWN': 6922 newEffects.push(ThumbnailHoverEffectExtract('image_translateY_0%_100%', effects[i])); 6923 break; 6924 case 'LABELSLIDEUP': 6925 case 'LABELSLIDEUPTOP': 6926 newEffects.push(ThumbnailHoverEffectExtract('label_translateY_100%_0%', effects[i])); 6927 break; 6928 case 'LABELSLIDEUPDOWN': 6929 newEffects.push(ThumbnailHoverEffectExtract('label_translateY_0%_100%', effects[i])); 6930 break; 6931 case 'DESCRIPTIONSLIDEUP': 6932 newEffects.push(ThumbnailHoverEffectExtract('description_translateY_110%_0%', effects[i])); 6933 break; 6934 6935 case 'IMAGEBLURON': 6936 newEffects.push(ThumbnailHoverEffectExtract('image_blur_2.00px_0.00px', effects[i])); 6937 break; 6938 case 'IMAGEBLUROFF': 6939 newEffects.push(ThumbnailHoverEffectExtract('image_blur_0.00px_2.00px', effects[i])); 6940 break; 6941 case 'IMAGEGRAYON': 6942 newEffects.push(ThumbnailHoverEffectExtract('image_grayscale_0%_100%', effects[i])); 6943 break; 6944 case 'IMAGEGRAYOFF': 6945 newEffects.push(ThumbnailHoverEffectExtract('image_grayscale_100%_0%', effects[i])); 6946 break; 6947 case 'IMAGESEPIAON': 6948 newEffects.push(ThumbnailHoverEffectExtract('image_sepia_100%_1%', effects[i])); 6949 break; 6950 case 'IMAGESEPIAOFF': 6951 newEffects.push(ThumbnailHoverEffectExtract('image_sepia_1%_100%', effects[i])); 6952 break; 6953 6954 default: 6955 newEffects.push(effects[i]); 6956 break; 6957 } 6958 } 6959 6960 return newEffects; 6961 } 6962 6963 6964 // Thumbnail hover effect definition 6965 function NewTHoverEffect() { 6966 var oDef={ 6967 name: '', 6968 element: '', // element class 6969 type: '', 6970 from: '', // start value 6971 to: '', // end value 6972 hoverin: true, 6973 hoverout: true, 6974 firstKeyframe: true, 6975 delay: 0, 6976 delayBack: 0, 6977 duration: 400, 6978 durationBack: 300, 6979 easing: 'easeOutQuart', 6980 easingBack: 'easeOutQuart', 6981 animParam: null 6982 }; 6983 return oDef; 6984 } 6985 6986 function NewTBuildInit() { 6987 // to set CSS properties 6988 var oDef={ element: '', property: '', value: '' }; 6989 return oDef; 6990 } 6991 6992 6993 function ThumbnailStyle( cfg, level) { 6994 6995 switch( cfg.position ){ 6996 case 'onBottom' : 6997 G.tn.style[level]['label'] = 'bottom:0; '; 6998 break; 6999 case 'overImageOnTop' : 7000 G.tn.style[level]['label'] = 'top:0; position:absolute;'; 7001 break; 7002 case 'overImageOnMiddle' : 7003 G.tn.style[level]['label'] = 'top:0; bottom:0;'; 7004 G.tn.style[level]['title'] = 'position:absolute; bottom:50%;'; 7005 G.tn.style[level]['desc'] = 'position:absolute; top:50%;'; 7006 break; 7007 case 'custom' : 7008 break; 7009 case 'overImageOnBottom' : 7010 default : 7011 G.O.thumbnailLabel.position = 'overImageOnBottom'; 7012 G.tn.style[level].label = 'bottom:0; position:absolute;'; 7013 break; 7014 } 7015 // if( G.layout.engine != 'CASCADING' ) { 7016 if( cfg.position != 'onBottom' ) { 7017 // multi-line 7018 if( cfg.titleMultiLine ) { 7019 G.tn.style[level]['title'] = 'white-space:normal;'; 7020 } 7021 if( cfg.descriptionMultiLine ) { 7022 G.tn.style[level]['desc'] = 'white-space:normal;'; 7023 } 7024 } 7025 7026 7027 switch( cfg.align ) { 7028 case 'right': 7029 G.tn.style[level].label += 'text-align:right;'; 7030 break; 7031 case 'left': 7032 G.tn.style[level].label += 'text-align:left;'; 7033 break; 7034 default: 7035 G.tn.style[level].label += 'text-align:center;'; 7036 break; 7037 } 7038 if( cfg.titleFontSize != undefined && cfg.titleFontSize != '' ) { 7039 G.tn.style[level].title += 'font-size:' + cfg.titleFontSize + ';'; 7040 } 7041 if( cfg.descriptionFontSize != undefined && cfg.descriptionFontSize != '' ) { 7042 G.tn.style[level].desc += 'font-size:' + cfg.descriptionFontSize + ';'; 7043 } 7044 7045 if( cfg.displayDescription == false ) { 7046 G.tn.style[level].desc += 'display:none;'; 7047 } 7048 } 7049 7050 7051 // cache some thumbnail settings 7052 function ThumbnailDefCaches() { 7053 // thumbnail content CSS styles 7054 7055 // settings for level L1 and LN 7056 ThumbnailStyle( G.O.thumbnailLabel, 'lN'); 7057 ThumbnailStyle( G.O.thumbnailLabel, 'l1'); 7058 7059 if( G.O.thumbnailL1Label && G.O.thumbnailL1Label.display ) { 7060 // settings for level L1 7061 ThumbnailStyle( G.O.thumbnailL1Label, 'l1'); 7062 } 7063 7064 G.tn.borderWidth = G.O.thumbnailBorderHorizontal; 7065 G.tn.borderHeight = G.O.thumbnailBorderVertical; 7066 7067 7068 // default thumbnail sizes levels l1 and lN 7069 var lst=['xs','sm','me','la','xl']; 7070 for( var i = 0; i < lst.length; i++ ) { 7071 var w = G.tn.settings.width.lN[lst[i]]; 7072 if( w != 'auto' ) { 7073 G.tn.defaultSize.width.lN[lst[i]] = w; 7074 G.tn.defaultSize.width.l1[lst[i]] = w; 7075 } 7076 else { 7077 var h = G.tn.settings.height.lN[lst[i]]; 7078 G.tn.defaultSize.width.lN[lst[i]] = h; // dynamic width --> set height value as default for the width 7079 G.tn.defaultSize.width.l1[lst[i]] = h; // dynamic width --> set height value as default 7080 } 7081 } 7082 for( var i = 0; i < lst.length; i++ ) { 7083 var h = G.tn.settings.height.lN[lst[i]]; 7084 if( h != 'auto' ) { 7085 // grid or justified layout 7086 G.tn.defaultSize.height.lN[lst[i]] = h; //+G.tn.labelHeight.get(); 7087 G.tn.defaultSize.height.l1[lst[i]] = h; //+G.tn.labelHeight.get(); 7088 } 7089 else { 7090 var w = G.tn.settings.width.lN[lst[i]]; 7091 G.tn.defaultSize.height.lN[lst[i]] = w; // dynamic height --> set width value as default for the height 7092 G.tn.defaultSize.height.l1[lst[i]] = w; // dynamic height --> set width value as default 7093 } 7094 } 7095 7096 // default thumbnail sizes levels l1 7097 for( var i = 0; i < lst.length; i++ ) { 7098 var w = G.tn.settings.width.l1[lst[i]]; 7099 if( w != 'auto' ) { 7100 G.tn.defaultSize.width.l1[lst[i]] = w; 7101 } 7102 else { 7103 var h = G.tn.settings.height.l1[lst[i]]; 7104 G.tn.defaultSize.width.l1[lst[i]] = h; // dynamic width --> set height value as default 7105 } 7106 } 7107 for( var i = 0; i < lst.length; i++ ) { 7108 var h = G.tn.settings.height.l1[lst[i]]; 7109 if( h != 'auto' ) { 7110 // grid or justified layout 7111 G.tn.defaultSize.height.l1[lst[i]] = h; //+G.tn.labelHeight.get(); 7112 } 7113 else { 7114 var w = G.tn.settings.width.l1[lst[i]]; 7115 G.tn.defaultSize.height.l1[lst[i]]= w ; // dynamic height --> set width value as default 7116 } 7117 } 7118 7119 } 7120 7121 7122 // ##### THUMBNAIL SIZE MANAGEMENT 7123 function ThumbnailsSetSize( dir, level, v, crop ) { 7124 G.tn.settings[dir][level]['xs'] = v; 7125 G.tn.settings[dir][level]['sm'] = v; 7126 G.tn.settings[dir][level]['me'] = v; 7127 G.tn.settings[dir][level]['la'] = v; 7128 G.tn.settings[dir][level]['xl'] = v; 7129 G.tn.settings[dir][level]['xsc'] = crop; 7130 G.tn.settings[dir][level]['smc'] = crop; 7131 G.tn.settings[dir][level]['mec'] = crop; 7132 G.tn.settings[dir][level]['lac'] = crop; 7133 G.tn.settings[dir][level]['xlc'] = crop; 7134 } 7135 7136 7137 // 7138 function GalleryThemeGetCurrent() { 7139 7140 var cs=null; 7141 switch(toType(G.O.galleryTheme)) { 7142 case 'object': // user custom color scheme object 7143 cs = G.galleryTheme_dark; // default color scheme 7144 jQuery.extend(true,cs,G.O.galleryTheme); 7145 break; 7146 case 'string': // name of an internal defined color scheme 7147 switch( G.O.galleryTheme ) { 7148 case 'light': 7149 cs = G.galleryTheme_light; 7150 break; 7151 case 'default': 7152 case 'dark': 7153 case 'none': 7154 default: 7155 cs = G.galleryTheme_dark; 7156 } 7157 break; 7158 default: 7159 cs = G.galleryTheme_dark; 7160 } 7161 return cs; 7162 } 7163 7164 // ##### BREADCRUMB/THUMBNAIL COLOR SCHEME ##### 7165 function SetGalleryTheme() { 7166 7167 if( typeof G.O.colorScheme !== 'undefined' ) { 7168 G.O.galleryTheme = G.O.colorScheme; 7169 } 7170 7171 var cs = null; 7172 var galleryTheme = ''; 7173 switch(toType(G.O.galleryTheme)) { 7174 case 'object': // user custom color scheme object 7175 cs = G.galleryTheme_dark; // default color scheme 7176 jQuery.extend(true,cs,G.O.galleryTheme); 7177 galleryTheme='nanogallery_gallerytheme_custom_' + G.baseEltID; 7178 break; 7179 case 'string': // name of an internal defined color scheme 7180 switch( G.O.galleryTheme ) { 7181 case 'light': 7182 cs = G.galleryTheme_light; 7183 galleryTheme='nanogallery_gallerytheme_light_' + G.baseEltID; 7184 break; 7185 case 'default': 7186 case 'dark': 7187 case 'none': 7188 default: 7189 cs = G.galleryTheme_dark; 7190 galleryTheme='nanogallery_gallerytheme_dark_' + G.baseEltID; 7191 } 7192 break; 7193 default: 7194 NanoAlert(G, 'Error in galleryTheme parameter.'); 7195 return; 7196 } 7197 7198 //var s1='.nanogallery_theme_'+G.O.theme+' '; 7199 var s1='.' + galleryTheme + ' '; 7200 7201 // navigation bar 7202 var s=s1+'.nGY2Navigationbar { background:'+cs.navigationBar.background+'; }'+'\n'; 7203 if( cs.navigationBar.border !== undefined && cs.navigationBar.border !== '' ) { s+=s1+'.nGY2Navigationbar { border:'+cs.navigationBar.border+'; }'+'\n'; } 7204 if( cs.navigationBar.borderTop !== undefined && cs.navigationBar.borderTop !== '' ) { s+=s1+'.nGY2Navigationbar { border-top:'+cs.navigationBar.borderTop+'; }'+'\n'; } 7205 if( cs.navigationBar.borderBottom !== undefined && cs.navigationBar.borderBottom !== '' ) { s+=s1+'.nGY2Navigationbar { border-bottom:'+cs.navigationBar.borderBottom+'; }'+'\n'; } 7206 if( cs.navigationBar.borderRight !== undefined && cs.navigationBar.borderRight !== '' ) { s+=s1+'.nGY2Navigationbar { border-right:'+cs.navigationBar.borderRight+'; }'+'\n'; } 7207 if( cs.navigationBar.borderLeft !== undefined && cs.navigationBar.borderLeft !== '' ) { s+=s1+'.nGY2Navigationbar { border-left:'+cs.navigationBar.borderLeft+'; }'+'\n'; } 7208 7209 // navigation bar - breadcrumb 7210 s+=s1+'.nGY2Breadcrumb { background:'+cs.navigationBreadcrumb.background+'; border-radius:'+cs.navigationBreadcrumb.borderRadius+'; }'+'\n'; 7211 s+=s1+'.nGY2Breadcrumb .oneItem { color:'+cs.navigationBreadcrumb.color+'; }'+'\n'; 7212 s+=s1+'.nGY2Breadcrumb .oneItem:hover { color:'+cs.navigationBreadcrumb.colorHover+'; }'+'\n'; 7213 7214 // navigation bar - tag filter 7215 s+=s1+'.nGY2NavFilterUnselected { color:'+cs.navigationFilter.color+'; background:'+cs.navigationFilter.background+'; border-radius:'+cs.navigationFilter.borderRadius+'; }'+'\n'; 7216 s+=s1+'.nGY2NavFilterSelected { color:'+cs.navigationFilter.colorSelected+'; background:'+cs.navigationFilter.backgroundSelected+'; border-radius:'+cs.navigationFilter.borderRadius+'; }'+'\n'; 7217 s+=s1+'.nGY2NavFilterSelectAll { color:'+cs.navigationFilter.colorSelected+'; background:'+cs.navigationFilter.background+'; border-radius:'+cs.navigationFilter.borderRadius+'; }'+'\n'; 7218 7219 // thumbnails 7220 s+=s1+'.nGY2GThumbnail { background:'+cs.thumbnail.background+'; border-color:'+cs.thumbnail.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'; 7221 s+=s1+'.nGY2GThumbnailStack { background:'+cs.thumbnail.stackBackground+'; }'+'\n'; 7222 // s+=s1+'.nGY2GThumbnailImage { background:'+cs.thumbnail.background+'; background-image:'+cs.thumbnail.backgroundImage+'; }'+'\n'; 7223 s+=s1+'.nGY2TnImgBack { background:'+cs.thumbnail.background+'; background-image:'+cs.thumbnail.backgroundImage+'; }'+'\n'; 7224 s+=s1+'.nGY2GThumbnailAlbumUp { background:'+cs.thumbnail.background+'; background-image:'+cs.thumbnail.backgroundImage+'; color:'+cs.thumbnail.titleColor+'; }'+'\n'; 7225 s+=s1+'.nGY2GThumbnailIconsFullThumbnail { color:'+cs.thumbnail.titleColor+'; }\n'; 7226 s+=s1+'.nGY2GThumbnailLabel { background:'+cs.thumbnail.labelBackground+'; opacity:'+cs.thumbnail.labelOpacity+'; }'+'\n'; 7227 s+=s1+'.nGY2GThumbnailImageTitle { color:'+cs.thumbnail.titleColor+'; background-color:'+cs.thumbnail.titleBgColor+'; '+(cs.thumbnail.titleShadow =='' ? '': 'Text-Shadow:'+cs.thumbnail.titleShadow+';')+' }'+'\n'; 7228 s+=s1+'.nGY2GThumbnailAlbumTitle { color:'+cs.thumbnail.titleColor+'; background-color:'+cs.thumbnail.titleBgColor+'; '+(cs.thumbnail.titleShadow =='' ? '': 'Text-Shadow:'+cs.thumbnail.titleShadow+';')+' }'+'\n'; 7229 s+=s1+'.nGY2GThumbnailDescription { color:'+cs.thumbnail.descriptionColor+'; background-color:'+cs.thumbnail.descriptionBgColor+'; '+(cs.thumbnail.descriptionShadow =='' ? '': 'Text-Shadow:'+cs.thumbnail.descriptionShadow+';')+' }'+'\n'; 7230 7231 // thumbnails - icons 7232 s+=s1+'.nGY2GThumbnailIcons { padding:'+cs.thumbnailIcon.padding+'; }\n'; 7233 s+=s1+'.nGY2GThumbnailIcon { color:'+cs.thumbnailIcon.color+'; }\n'; 7234 s+=s1+'.nGY2GThumbnailIconTextBadge { background-color:'+cs.thumbnailIcon.color+'; }\n'; 7235 7236 // gallery pagination -> dot/rectangle based 7237 if( G.O.galleryPaginationMode != 'NUMBERS' ) { 7238 s+=s1+'.nGY2paginationDot { border:'+cs.pagination.shapeBorder+'; background:'+cs.pagination.shapeColor+';}\n'; 7239 s+=s1+'.nGY2paginationDotCurrentPage { border:'+cs.pagination.shapeBorder+'; background:'+cs.pagination.shapeSelectedColor+';}\n'; 7240 s+=s1+'.nGY2paginationRectangle { border:'+cs.pagination.shapeBorder+'; background:'+cs.pagination.shapeColor+';}\n'; 7241 s+=s1+'.nGY2paginationRectangleCurrentPage { border:'+cs.pagination.shapeBorder+'; background:'+cs.pagination.shapeSelectedColor+';}\n'; 7242 } else { 7243 s+=s1+'.nGY2paginationItem { background:'+cs.pagination.background+'; color:'+cs.pagination.color+'; border-radius:'+cs.pagination.borderRadius+'; }\n'; 7244 s+=s1+'.nGY2paginationItemCurrentPage { background:'+cs.pagination.background+'; color:'+cs.pagination.color+'; border-radius:'+cs.pagination.borderRadius+'; }\n'; 7245 s+=s1+'.nGY2PaginationPrev { background:'+cs.pagination.background+'; color:'+cs.pagination.color+'; border-radius:'+cs.pagination.borderRadius+'; }\n'; 7246 s+=s1+'.nGY2PaginationNext { background:'+cs.pagination.background+'; color:'+cs.pagination.color+'; border-radius:'+cs.pagination.borderRadius+'; }\n'; 7247 s+=s1+'.nGY2paginationItemCurrentPage { background:'+cs.pagination.backgroundSelected+'; }\n'; 7248 } 7249 7250 // gallery more button 7251 s+=s1+'.nGY2GalleryMoreButtonAnnotation { background:'+cs.thumbnail.background+'; border-color:'+cs.thumbnail.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'; 7252 s+=s1+'.nGY2GalleryMoreButtonAnnotation { color:'+cs.thumbnail.titleColor+'; '+(cs.thumbnail.titleShadow =='' ? '': 'Text-Shadow:'+cs.thumbnail.titleShadow)+'; }\n'; 7253 7254 jQuery('head').append('<style id="ngycs_'+G.baseEltID+'">'+s+'</style>'); 7255 G.$E.base.addClass(galleryTheme); 7256 7257 }; 7258 7259 // ##### VIEWER COLOR SCHEME ##### 7260 function SetViewerTheme( ) { 7261 7262 if( G.VOM.viewerTheme != '' ) { 7263 G.VOM.$cont.addClass(G.VOM.viewerTheme); 7264 return; 7265 } 7266 7267 if( typeof G.O.colorSchemeViewer !== 'undefined' ) { 7268 G.O.viewerTheme = G.O.colorSchemeViewer; 7269 } 7270 7271 var cs=null; 7272 switch(toType(G.O.viewerTheme)) { 7273 case 'object': // user custom color scheme object 7274 cs = G.viewerTheme_dark; 7275 jQuery.extend(true, cs, G.O.viewerTheme); 7276 G.VOM.viewerTheme = 'nanogallery_viewertheme_custom_' + G.baseEltID; 7277 break; 7278 case 'string': // name of an internal defined color scheme 7279 switch( G.O.viewerTheme ) { 7280 case 'none': 7281 return; 7282 break; 7283 case 'light': 7284 cs = G.viewerTheme_light; 7285 G.VOM.viewerTheme = 'nanogallery_viewertheme_light_' + G.baseEltID; 7286 break; 7287 case 'border': 7288 cs = G.viewerTheme_border; 7289 G.VOM.viewerTheme = 'nanogallery_viewertheme_border_' + G.baseEltID; 7290 break; 7291 case 'dark': 7292 case 'default': 7293 cs = G.viewerTheme_dark; 7294 G.VOM.viewerTheme = 'nanogallery_viewertheme_dark_' + G.baseEltID; 7295 break; 7296 } 7297 break; 7298 default: 7299 NanoAlert(G, 'Error in viewerTheme parameter.'); 7300 return; 7301 } 7302 7303 var s1 = '.' + G.VOM.viewerTheme + ' '; 7304 var s = s1 + '.nGY2Viewer { background:' + cs.background + '; }'+'\n'; 7305 s += s1 + '.nGY2ViewerMedia { border:' + cs.imageBorder + '; box-shadow:' + cs.imageBoxShadow + '; }'+'\n'; 7306 s += s1 + '.nGY2Viewer .toolbarBackground { background:' + cs.barBackground + '; }'+'\n'; 7307 s += s1 + '.nGY2Viewer .nGY2ViewerToolsTopLeft { background:' + cs.barBackground + '; }'+'\n'; 7308 s += s1 + '.nGY2Viewer .nGY2ViewerToolsTopRight { background:' + cs.barBackground + '; }'+'\n'; 7309 s += s1 + '.nGY2Viewer .nGY2ViewerAreaNext { background:' + cs.barBackground + '; }'+'\n'; 7310 s += s1 + '.nGY2Viewer .nGY2ViewerAreaPrevious { background:' + cs.barBackground + '; }'+'\n'; 7311 s += s1 + '.nGY2Viewer .toolbar { border:' + cs.barBorder + '; color:' + cs.barColor + '; }'+'\n'; 7312 s += s1 + '.nGY2Viewer .toolbar .previousButton:after { color:' + cs.barColor + '; }'+'\n'; 7313 s += s1 + '.nGY2Viewer .toolbar .nextButton:after { color:' + cs.barColor + '; }'+'\n'; 7314 s += s1 + '.nGY2Viewer .toolbar .closeButton:after { color:' + cs.barColor + '; }'+'\n'; 7315 s += s1 + '.nGY2Viewer .toolbar .label .title { color:' + cs.barColor + '; }'+'\n'; 7316 s += s1 + '.nGY2Viewer .toolbar .label .description { color:' + cs.barDescriptionColor + '; }'+'\n'; 7317 jQuery('head').append('<style>' + s + '</style>'); 7318 G.VOM.$cont.addClass(G.VOM.viewerTheme); 7319 }; 7320 7321 7322 7323 /** @function SetPolyFills */ 7324 function SetPolyFills() { 7325 7326 // POLYFILL FOR BIND function --> for older Safari mobile 7327 // found on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Compatibility 7328 if (!Function.prototype.bind) { 7329 Function.prototype.bind = function (oThis) { 7330 if (typeof this !== "function") { 7331 // closest thing possible to the ECMAScript 5 7332 // internal IsCallable function 7333 throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); 7334 } 7335 7336 var aArgs = Array.prototype.slice.call(arguments, 1), 7337 fToBind = this, 7338 fNOP = function () {}, 7339 fBound = function () { 7340 return fToBind.apply(this instanceof fNOP && oThis 7341 ? this 7342 : oThis, 7343 aArgs.concat(Array.prototype.slice.call(arguments))); 7344 }; 7345 7346 fNOP.prototype = this.prototype; 7347 fBound.prototype = new fNOP(); 7348 7349 return fBound; 7350 }; 7351 } 7352 7353 // requestAnimationFrame polyfill by Erik M�ller. fixes from Paul Irish and Tino Zijdel 7354 // http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 7355 // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating 7356 // MIT license 7357 (function() { 7358 var lastTime = 0; 7359 var vendors = ['ms', 'moz', 'webkit', 'o']; 7360 for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { 7361 window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; 7362 window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame']; 7363 } 7364 if (!window.requestAnimationFrame) 7365 window.requestAnimationFrame = function(callback, element) { 7366 var currTime = new Date().getTime(); 7367 var timeToCall = Math.max(0, 16 - (currTime - lastTime)); 7368 var id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall); 7369 lastTime = currTime + timeToCall; 7370 return id; 7371 }; 7372 7373 if (!window.cancelAnimationFrame) 7374 window.cancelAnimationFrame = function(id) { 7375 clearTimeout(id); 7376 }; 7377 }()); 7378 7379 // array.removeIf -> removes items from array base on a function's result 7380 Array.prototype.removeIf = function(callback) { 7381 var i = this.length; 7382 while (i--) { 7383 if (callback(this[i], i)) { 7384 this.splice(i, 1); 7385 } 7386 } 7387 }; 7388 7389 // IE11 for startsWith 7390 // thanks to @lichtamberg - https://github.com/lichtamberg 7391 if (!String.prototype.startsWith) { 7392 String.prototype.startsWith = function(searchString, position) { 7393 position = position || 0; 7394 return this.indexOf(searchString, position) === position; 7395 }; 7396 } 7397 7398 } 7399 7400 7401 // Gallery clicked/touched -> retrieve & execute action 7402 function GalleryClicked(e) { 7403 7404 var r = GalleryEventRetrieveElementl(e, false); 7405 7406 if( r.GOMidx == -1 ) { return 'exit'; } 7407 7408 var idx = G.GOM.items[r.GOMidx].thumbnailIdx; 7409 if( G.GOM.slider.hostIdx == r.GOMidx ) { 7410 // slider on thumbnail -> open the displayed image 7411 idx = G.GOM.items[G.GOM.slider.currentIdx].thumbnailIdx; 7412 } 7413 switch( r.action ) { 7414 case 'OPEN': 7415 ThumbnailOpen(idx, false); 7416 return 'exit'; 7417 break; 7418 case 'DISPLAY': 7419 // used the display icon (ignore if selection mode) 7420 ThumbnailOpen(idx, true); 7421 return 'exit'; 7422 break; 7423 case 'TOGGLESELECT': 7424 ThumbnailSelectionToggle(idx); 7425 return 'exit'; 7426 break; 7427 case 'SHARE': 7428 PopupShare(idx); 7429 return 'exit'; 7430 break; 7431 case 'DOWNLOAD': 7432 DownloadImage(idx); 7433 return 'exit'; 7434 break; 7435 case 'INFO': 7436 ItemDisplayInfo(G.I[idx]); 7437 return 'exit'; 7438 break; 7439 case 'CART': 7440 AddToCart(idx); 7441 return 'exit'; 7442 break; 7443 default: 7444 // all other actions (custom1..10, or anything else) 7445 var fu = G.O.fnThumbnailToolCustAction; 7446 if( fu !== null ) { 7447 typeof fu == 'function' ? fu(r.action, G.I[idx]) : window[fu](r.action, G.I[idx]); 7448 } 7449 break; 7450 } 7451 } 7452 7453 // Download an image 7454 function DownloadImage(idx) { 7455 if( G.I[idx].mediaKind != 'img' ) { return; } 7456 7457 7458 var url = G.I[idx].src; 7459 7460 if( G.I[idx].downloadURL != undefined && G.I[idx].downloadURL != '' ) { 7461 url = G.I[idx].downloadURL; 7462 } 7463 7464 var a = document.createElement('a'); 7465 a.href = url; 7466 // a.download = url.split('.').pop(); 7467 a.download = url.split('/').pop(); 7468 a.target = '_blank'; 7469 a.style.display = 'none'; 7470 document.body.appendChild(a); 7471 a.click(); 7472 document.body.removeChild(a); 7473 7474 } 7475 7476 // add one image to the shopping cart 7477 function AddToCart( idx ) { 7478 // increment counter if already in shopping cart 7479 var found=false; 7480 for( var i=0; i<G.shoppingCart.length; i++ ) { 7481 if( G.shoppingCart[i].idx == idx ) { 7482 G.shoppingCart[i].cnt++; 7483 var fu = G.O.fnShoppingCartUpdated; 7484 if( fu !== null ) { 7485 typeof fu == 'function' ? fu(G.shoppingCart, G.I[idx]) : window[fu](G.shoppingCart, G.I[idx]); 7486 } 7487 TriggerCustomEvent('shoppingCartUpdated'); 7488 return; 7489 } 7490 } 7491 7492 // add to shopping cart 7493 if( !found) { 7494 G.shoppingCart.push( { idx:idx, ID:G.I[idx].GetID(), cnt:1} ); 7495 var fu=G.O.fnShoppingCartUpdated; 7496 if( fu !== null ) { 7497 typeof fu == 'function' ? fu(G.shoppingCart, G.I[idx]) : window[fu](G.shoppingCart, G.I[idx]); 7498 } 7499 TriggerCustomEvent('shoppingCartUpdated'); 7500 } 7501 } 7502 7503 7504 // All thumbnails are set to unselected 7505 function ThumbnailSelectionClear() { 7506 G.GOM.nbSelected = 0; 7507 for( var i = 0, nbTn = G.GOM.items.length; i < nbTn ; i++ ) { 7508 var item = G .I[G.GOM.items[i].thumbnailIdx]; 7509 if( item.selected ) { 7510 item.selected = false; 7511 var fu = G.O.fnThumbnailSelection; 7512 if( fu !== null ) { 7513 typeof fu == 'function' ? fu(item.$elt, item, G.I) : window[fu](item.$elt, item, G.I); 7514 } 7515 } 7516 item.selected = false; 7517 } 7518 } 7519 7520 function ThumbnailSelectionToggle( idx ){ 7521 var item = G.I[idx]; 7522 if( item.selected === true ) { 7523 ThumbnailSelectionSet(item, false); 7524 G.GOM.nbSelected--; 7525 TriggerCustomEvent('itemUnSelected'); 7526 } 7527 else { 7528 ThumbnailSelectionSet(item, true); 7529 G.GOM.nbSelected++; 7530 TriggerCustomEvent('itemSelected'); 7531 } 7532 } 7533 7534 7535 // this replaces ThumbnailSelection() 7536 function ThumbnailSelectionSet(item, selected ){ 7537 7538 item.selected = selected; 7539 7540 ThumbnailSelectionSetIcon( item ); 7541 7542 // called when the selection status of an item changed 7543 var fu=G.O.fnThumbnailSelection; 7544 if( fu !== null ) { 7545 typeof fu == 'function' ? fu(item.$elt, item, G.I) : window[fu](item.$elt, item, G.I); 7546 } 7547 7548 } 7549 7550 function ThumbnailSelectionSetIcon( item ) { 7551 if( item.$elt == null ) { 7552 // thumbnail is not built 7553 return; 7554 } 7555 // var $sub = item.$getElt('.nGY2GThumbnailSub'); 7556 var $sub = item.$getElt('.nGY2GThumbnail'); 7557 var $icon = item.$getElt('.nGY2GThumbnailIconImageSelect'); 7558 if( item.selected === true) { 7559 $sub.addClass('nGY2GThumbnailSubSelected'); 7560 $icon.addClass('nGY2ThumbnailSelected'); 7561 $icon.removeClass('nGY2ThumbnailUnselected'); 7562 $icon.html(G.O.icons.thumbnailSelected); 7563 } 7564 else { 7565 $sub.removeClass('nGY2GThumbnailSubSelected'); 7566 $icon.removeClass('nGY2ThumbnailSelected'); 7567 $icon.addClass('nGY2ThumbnailUnselected'); 7568 $icon.html(G.O.icons.thumbnailUnselected); 7569 } 7570 } 7571 7572 7573 // display a modal popup for sharing image/album 7574 function PopupShare(idx) { 7575 7576 // SEE SAMPLES: https://gist.github.com/chrisjlee/5196139 7577 // https://github.com/Julienh/Sharrre 7578 7579 var item=G.I[idx]; 7580 7581 var currentURL=document.location.protocol + '//' + document.location.hostname + document.location.pathname; 7582 var newLocationHash = '#nanogallery/' + G.baseEltID + '/'; 7583 if( item.kind == 'image' ) { 7584 newLocationHash += item.albumID + '/' + item.GetID(); 7585 } 7586 else { 7587 newLocationHash += item.GetID(); 7588 } 7589 7590 var content = ''; 7591 content += '<div class="nGY2PopupOneItem" style="text-align:center;" data-share="facebook">' + G.O.icons.shareFacebook + '</div>'; 7592 content += '<div class="nGY2PopupOneItem" style="text-align:center;" data-share="pinterest">' + G.O.icons.sharePinterest + '</div>'; 7593 content += '<div class="nGY2PopupOneItem" style="text-align:center;" data-share="tumblr">' + G.O.icons.shareTumblr + '</div>'; 7594 content += '<div class="nGY2PopupOneItem" style="text-align:center;" data-share="twitter">' + G.O.icons.shareTwitter + '</div>'; 7595 // content += '<div class="nGY2PopupOneItem" style="text-align:center;" data-share="googleplus">' + G.O.icons.shareGooglePlus + '</div>'; 7596 content += '<div class="nGY2PopupOneItem" style="text-align:center;" data-share="vk">' + G.O.icons.shareVK + '</div>'; 7597 content += '<div class="nGY2PopupOneItem" style="text-align:center;" data-share="mail">' + G.O.icons.shareMail + '</div>'; 7598 content += '<div class="nGY2PopupOneItem" style="text-align:center;"></div>'; 7599 content += '<input class="nGY2PopupOneItemText" readonly type="text" value="' + currentURL+newLocationHash + '" style="width:100%;text-align:center;">'; 7600 content += '<br>'; 7601 7602 currentURL = encodeURIComponent(document.location.protocol + '//' + document.location.hostname + document.location.pathname + newLocationHash); 7603 7604 var currentTitle = item.title; 7605 var currentTn = item.thumbImg().src; 7606 7607 7608 Popup('Share to:', content, 'Center'); 7609 7610 G.popup.$elt.find('.nGY2PopupOneItem').on('click', function(e) { 7611 e.stopPropagation(); 7612 7613 var shareURL = ''; 7614 var found = true; 7615 switch(jQuery(this).attr('data-share').toUpperCase()) { 7616 case 'FACEBOOK': 7617 // <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> 7618 //window.open("https://www.facebook.com/sharer.php?u="+currentURL,"","height=368,width=600,left=100,top=100,menubar=0"); 7619 shareURL = 'https://www.facebook.com/sharer.php?u=' + currentURL; 7620 break; 7621 case 'VK': 7622 shareURL = 'http://vk.com/share.php?url=' + currentURL; 7623 break; 7624 case 'GOOGLEPLUS': 7625 shareURL = "https://plus.google.com/share?url=" + currentURL; 7626 break; 7627 case 'TWITTER': 7628 // shareURL="https://twitter.com/share?url="+currentURL+"&text="+currentTitle; 7629 shareURL = 'https://twitter.com/intent/tweet?text=' + currentTitle + 'url=' + currentURL; 7630 break; 7631 case 'PINTEREST': 7632 // shareURL='https://pinterest.com/pin/create/bookmarklet/?media='+currentTn+'&url='+currentURL+'&description='+currentTitle; 7633 shareURL = 'https://pinterest.com/pin/create/button/?media=' + currentTn + '&url=' + currentURL + '&description=' + currentTitle; 7634 break; 7635 case 'TUMBLR': 7636 //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; 7637 shareURL = 'http://www.tumblr.com/share/link?url=' + currentURL + '&name=' + currentTitle; 7638 break; 7639 case 'MAIL': 7640 shareURL = 'mailto:?subject=' + currentTitle + '&body=' + currentURL; 7641 break; 7642 default: 7643 found = false; 7644 break; 7645 } 7646 7647 if( found ) { 7648 window.open(shareURL, "" , "height=550,width=500,left=100,top=100,menubar=0" ); 7649 G.popup.close(); 7650 // $popup.remove(); 7651 } 7652 7653 }); 7654 } 7655 7656 // build a modal popup 7657 function Popup(title, content, align) { 7658 var pp = '<div class="nGY2Popup" style="opacity:0;"><div class="nGY2PopupContent' + align + '">'; 7659 pp += '<div class="nGY2PopupCloseButton">' + G.O.icons.buttonClose + '</div>'; 7660 pp += '<div class="nGY2PopupTitle">' + title + '</div>'; 7661 pp += content; 7662 pp += '</div></div>'; 7663 7664 G.popup.$elt = jQuery(pp).appendTo('body'); 7665 setElementOnTop( G.VOM.$viewer, G.popup.$elt); 7666 7667 G.popup.isDisplayed = true; 7668 7669 var tweenable = new NGTweenable(); 7670 tweenable.tween({ 7671 from: { opacity: 0 }, 7672 to: { opacity: 1 }, 7673 easing: 'easeInOutSine', 7674 duration: 250, 7675 step: function (state, att) { 7676 G.popup.$elt.css( state ); 7677 } 7678 }); 7679 7680 G.popup.$elt.find('.nGY2PopupCloseButton').on('click', function(e) { 7681 e.stopPropagation(); 7682 G.popup.close(); 7683 }); 7684 7685 } 7686 7687 7688 function GalleryMouseEnter(e) { 7689 if( !G.VOM.viewerDisplayed && G.GOM.albumIdx != -1 ) { 7690 var r = GalleryEventRetrieveElementl(e, true); 7691 // if( r.action == 'OPEN' && r.GOMidx != -1 ) { 7692 if( r.GOMidx != -1 ) { 7693 var target = e.target || e.srcElement; 7694 // if( target.getAttribute('class') != 'nGY2GThumbnail' ) { return; } 7695 ThumbnailHover(r.GOMidx); 7696 } 7697 } 7698 } 7699 7700 function GalleryMouseLeave(e) { 7701 if( !G.VOM.viewerDisplayed && G.GOM.albumIdx != -1 ) { 7702 var r = GalleryEventRetrieveElementl(e, true); 7703 if( r.GOMidx != -1 ) { 7704 var target = e.target || e.srcElement; 7705 // if( target.getAttribute('class') != 'nGY2GThumbnail' ) { return; } 7706 ThumbnailHoverOut(r.GOMidx); 7707 } 7708 } 7709 } 7710 7711 function GalleryEventRetrieveElementl( e, ignoreSubItems ) { 7712 var r = { action: 'NONE', GOMidx: -1 }; 7713 7714 if( e == undefined ) { 7715 return r; 7716 } 7717 var target = e.target || e.srcElement; 7718 while( target != G.$E.conTnParent[0] ) { // loop element parent up to find the thumbnail element 7719 if( jQuery(target).hasClass('nGY2GThumbnail') ) { 7720 if( r.action == 'NONE' ) { 7721 r.action = 'OPEN'; 7722 } 7723 r.GOMidx = jQuery(target).data('index'); 7724 return r; 7725 } 7726 // if( !ignoreSubItems && jQuery(target).hasClass('nGY2GThumbnailIcon') ) { 7727 if( !ignoreSubItems ) { 7728 var a = jQuery(target).data('ngy2action'); 7729 if( a != '' && a != undefined ) { 7730 r.action = a; 7731 } 7732 } 7733 if( target.parentNode == null ) { 7734 return r; 7735 } 7736 target = target.parentNode; 7737 } 7738 return r; 7739 } 7740 7741 7742 // Open one thumbnail 7743 function ThumbnailOpen( idx, ignoreSelected ) { 7744 var item = G.I[idx]; 7745 7746 var fu = G.O.fnThumbnailClicked; 7747 if( fu !== null ) { 7748 typeof fu == 'function' ? fu(item.$elt, item) : window[fu](item.$elt, item); 7749 } 7750 7751 // open URL 7752 if( item.destinationURL !== undefined && item.destinationURL.length > 0 ) { 7753 window.location = item.destinationURL; 7754 return; 7755 } 7756 7757 switch( item.kind ) { 7758 case 'image': 7759 if( ignoreSelected === false && G.GOM.nbSelected > 0 ) { 7760 ThumbnailSelectionToggle(idx); 7761 } 7762 else { 7763 // display image 7764 DisplayPhotoIdx(idx); 7765 } 7766 break; 7767 case 'album': 7768 if( ignoreSelected === false && G.GOM.nbSelected > 0 ) { 7769 ThumbnailSelectionToggle(idx); 7770 } 7771 else { 7772 if( G.O.thumbnailAlbumDisplayImage && idx != 0 ) { 7773 // display album content in lightbox 7774 DisplayFirstMediaInAlbum(idx); 7775 return; 7776 } 7777 else { 7778 // display album content in gallery 7779 DisplayAlbum('-1', item.GetID()); 7780 } 7781 } 7782 break; 7783 case 'albumUp': 7784 var parent = NGY2Item.Get(G, item.albumID); 7785 DisplayAlbum('-1', parent.albumID); 7786 break; 7787 } 7788 } 7789 7790 function DisplayFirstMediaInAlbum( albumIdx ) { 7791 if( G.O.debugMode ) { console.log('#DisplayFirstPhotoInAlbum : '+ albumIdx); } 7792 7793 var item = G.I[albumIdx]; 7794 7795 var l = G.I.length; 7796 for( var i = 0; i < l; i++ ) { 7797 if( G.I[i].albumID == item.GetID() ) { 7798 DisplayPhotoIdx( i ); 7799 return; 7800 } 7801 } 7802 7803 // load album content 7804 AlbumGetContent( item.GetID(), DisplayFirstMediaInAlbum, albumIdx, null ); 7805 7806 } 7807 7808 7809 // Open link to original image (new window) 7810 function OpenOriginal( item ) { 7811 switch( G.O.kind ) { 7812 case 'flickr': 7813 var sU = 'https://www.flickr.com/photos/' + G.O.userID + '/' + item.GetID(); 7814 if( item.albumID != '0' ) { 7815 sU += '/in/album-' + item.albumID + '/'; 7816 } 7817 window.open(sU, '_blank'); 7818 break; 7819 case 'picasa': 7820 case 'google': 7821 case 'google2': 7822 // no more working since Google changed the access to Google Photos in 2017 7823 // var sU='https://plus.google.com/photos/'+G.O.userID+'/albums/'+item.albumID+'/'+item.GetID(); 7824 // window.open(sU,'_blank'); 7825 // break; 7826 default: 7827 var sU = item.responsiveURL(); 7828 window.open(sU, '_blank'); 7829 break; 7830 } 7831 } 7832 7833 // Display one photo (with internal or external viewer) 7834 function DisplayPhotoIdx( ngy2ItemIdx ) { 7835 7836 if( !G.O.thumbnailOpenImage ) { return; } 7837 7838 if( G.O.thumbnailOpenOriginal ) { 7839 // Open link to original image 7840 OpenOriginal( G.I[ngy2ItemIdx] ); 7841 return; 7842 } 7843 7844 var items = []; 7845 G.VOM.currItemIdx = 0; 7846 G.VOM.items = []; 7847 G.VOM.albumID = G.I[ngy2ItemIdx].albumID; 7848 7849 var vimg = new VImg(ngy2ItemIdx); 7850 G.VOM.items.push(vimg); 7851 items.push(G.I[ngy2ItemIdx]); 7852 //TODO -> danger? -> pourquoi reconstruire la liste si d�j� ouvert (back/forward) 7853 var l = G.I.length; 7854 for( var idx = ngy2ItemIdx+1; idx < l ; idx++) { 7855 var item = G.I[idx]; 7856 if( item.kind == 'image' && item.isToDisplay(G.VOM.albumID) && item.destinationURL == '' ) { 7857 var vimg = new VImg(idx); 7858 G.VOM.items.push(vimg); 7859 items.push(item); 7860 } 7861 } 7862 var last = G.VOM.items.length; 7863 var cnt = 1; 7864 for( var idx = 0; idx < ngy2ItemIdx ; idx++) { 7865 var item = G.I[idx]; 7866 if( item.kind == 'image' && item.isToDisplay(G.VOM.albumID) && item.destinationURL == '' ) { 7867 var vimg = new VImg(idx); 7868 vimg.mediaNumber = cnt; 7869 G.VOM.items.push(vimg); 7870 items.push(item); 7871 cnt++; 7872 } 7873 } 7874 for( var i = 0; i < last; i++ ) { 7875 G.VOM.items[i].mediaNumber = cnt; 7876 cnt++; 7877 } 7878 7879 // opens media with external viewer 7880 var fu = G.O.fnThumbnailOpen; 7881 if( fu !== null ) { 7882 typeof fu == 'function' ? fu(items) : window[fu](items); 7883 return; 7884 } 7885 7886 // use internal viewer 7887 if( !G.VOM.viewerDisplayed ) { 7888 // build viewer and display 7889 OpenInternalViewer(); 7890 } 7891 else { 7892 // viewer already displayed -> display new media in current viewer 7893 G.VOM.$mediaCurrent.empty(); 7894 var item = G.VOM.NGY2Item(0); 7895 var spreloader = '<div class="nGY2ViewerMediaLoaderDisplayed"></div>'; 7896 if( item.mediaKind == 'img' && item.imageWidth != 0 && item.imageHeight != 0 ) { 7897 spreloader = '<div class="nGY2ViewerMediaLoaderHidden"></div>'; 7898 } 7899 G.VOM.$mediaCurrent.append( spreloader + item.mediaMarkup); 7900 ViewerSetMediaVisibility(G.VOM.NGY2Item(1), G.VOM.$mediaNext, 0); 7901 ViewerSetMediaVisibility(G.VOM.NGY2Item(-1), G.VOM.$mediaPrevious, 0); 7902 if( item.mediaKind == 'img' ) { 7903 G.VOM.ImageLoader.loadImage(VieweImgSizeRetrieved, item); 7904 } 7905 // G.VOM.$mediaCurrent.css({ opacity:0 }).attr('src',''); 7906 // G.VOM.ImageLoader.loadImage(VieweImgSizeRetrieved, G.VOM.NGY2Item(0)); 7907 // G.VOM.$mediaCurrent.children().eq(0).attr('src',G.emptyGif).attr('src', G.VOM.NGY2Item(0).responsiveURL()); 7908 DisplayInternalViewer(0, ''); 7909 } 7910 } 7911 7912 function ViewerZoomStart() { 7913 if( G.O.viewerZoom && !G.VOM.viewerMediaIsChanged ) { 7914 var item=G.VOM.NGY2Item(0); 7915 if( item.imageHeight > 0 && item.imageWidth > 0 ) { 7916 if( G.VOM.zoom.isZooming === false ) { 7917 // default zoom 7918 G.VOM.zoom.userFactor = 1; 7919 G.VOM.zoom.isZooming = true; 7920 } 7921 return true; 7922 } 7923 } 7924 } 7925 7926 function ViewerZoomIn( zoomIn ) { 7927 if( zoomIn ) { 7928 // zoom in 7929 G.VOM.zoom.userFactor+=0.1; 7930 ViewerZoomMax(); 7931 } 7932 else { 7933 // zoom out 7934 G.VOM.zoom.userFactor-=0.1; 7935 ViewerZoomMin(); 7936 } 7937 ViewerMediaSetPosAndZoom(); 7938 } 7939 7940 function ViewerZoomMax() { 7941 if( G.VOM.zoom.userFactor > 3 ) { 7942 G.VOM.zoom.userFactor = 3; 7943 } 7944 } 7945 function ViewerZoomMin() { 7946 7947 if( G.VOM.zoom.userFactor < 0.2 ) { 7948 G.VOM.zoom.userFactor = 0.2; 7949 } 7950 } 7951 7952 7953 7954 // Set position and size of all 3 media containers 7955 function ViewerMediaSetPosAndZoom() { 7956 7957 if( !G.VOM.zoom.isZooming ) { 7958 G.VOM.zoom.userFactor = 1; 7959 } 7960 // window.ng_draf( function() { 7961 ViewerMediaSetPosAndZoomOne( G.VOM.NGY2Item(0), G.VOM.$mediaCurrent, true ); 7962 ViewerMediaSetPosAndZoomOne( G.VOM.NGY2Item(-1), G.VOM.$mediaPrevious, false ); 7963 ViewerMediaSetPosAndZoomOne( G.VOM.NGY2Item(1), G.VOM.$mediaNext, false ); 7964 // }); 7965 } 7966 7967 7968 7969 // Media which is not IMG -> center and set size 7970 function ViewerMediaCenterNotImg( $mediaContainer ) { 7971 var $media = $mediaContainer.children().eq(1); 7972 $media.css( {'height': '80%' }); 7973 $media.css( {'width': '90%' }); 7974 $media[0].style[G.CSStransformName] = 'translate(0px, "50%") '; 7975 } 7976 7977 // Set position and size of ONE media container 7978 function ViewerMediaSetPosAndZoomOne(item, $img, isCurrent ) { 7979 7980 if( item.mediaKind != 'img' ) { 7981 ViewerMediaCenterNotImg($img); 7982 return; 7983 } 7984 7985 if( item.imageHeight == 0 || item.imageWidth == 0 ) { 7986 ViewerSetMediaVisibility( item, $img, 0 ); 7987 return; 7988 } 7989 7990 // part 1: set the image size 7991 var zoomUserFactor = isCurrent == true ? G.VOM.zoom.userFactor : 1; 7992 7993 var dpr = 1; 7994 if( G.O.viewerImageDisplay == 'bestImageQuality' ) { 7995 dpr = window.devicePixelRatio; 7996 } 7997 7998 // retrieve the base zoom factor (image fill screen) 7999 var zoomBaseFactorW = (G.VOM.window.lastWidth - G.VOM.padding.V) / (item.imageWidth / dpr); 8000 var zoomBaseFactorH = (G.VOM.window.lastHeight - G.VOM.padding.H) / (item.imageHeight / dpr); 8001 var zoomBaseFactor = Math.min(zoomBaseFactorW, zoomBaseFactorH); 8002 if( zoomBaseFactor > 1 && G.O.viewerImageDisplay != 'upscale' ) { 8003 // no upscale 8004 zoomBaseFactor = 1; 8005 } 8006 8007 var imageCurrentHeight = (item.imageHeight / dpr) * zoomUserFactor * zoomBaseFactor; 8008 var imageCurrentWidth = (item.imageWidth / dpr) * zoomUserFactor * zoomBaseFactor; 8009 $img.children().eq(1).css( {'height': imageCurrentHeight }); 8010 $img.children().eq(1).css( {'width': imageCurrentWidth }); 8011 8012 // retrieve posX/Y to center image 8013 var posX = 0; 8014 if( imageCurrentWidth > G.VOM.window.lastWidth ) { 8015 posX = -(imageCurrentWidth - G.VOM.window.lastWidth)/2; 8016 } 8017 var posY = 0; 8018 if( imageCurrentHeight > G.VOM.window.lastHeight ) { 8019 posY = ( imageCurrentHeight - G.VOM.window.lastHeight ) / 2; 8020 } 8021 posY = 0; // actually, it seems that the image is always centered vertically -> so no need to to anything 8022 8023 // Part 2: set the X/Y position (for zoom/pan) 8024 if( isCurrent ) { 8025 if( !G.VOM.zoom.isZooming ) { 8026 G.VOM.panPosX = 0; 8027 G.VOM.panPosY = 0; 8028 } 8029 G.VOM.zoom.posX = posX; 8030 G.VOM.zoom.posY = posY; 8031 ViewerImagePanSetPosition(G.VOM.panPosX, G.VOM.panPosY, $img, false); 8032 } 8033 // else { 8034 //$img[0].style[G.CSStransformName]= 'translate3D('+ posX+'px, '+ posY+'px, 0) '; 8035 // } 8036 else { 8037 // set the pan position of each media container 8038 ViewerMediaPanX( G.VOM.swipePosX ); 8039 $img.children().eq(1)[0].style[G.CSStransformName]= 'translate(0px, 0px) rotate('+ item.rotationAngle +'deg)'; 8040 } 8041 8042 } 8043 8044 // position the image depending on the zoom factor and the pan X/Y position 8045 // IMG is the only media supporting zoom/pan 8046 function ViewerImagePanSetPosition(posX, posY, imageContainer, savePosition ) { 8047 if( savePosition ) { 8048 G.VOM.panPosX = posX; 8049 G.VOM.panPosY = posY; 8050 } 8051 8052 posX += G.VOM.zoom.posX; 8053 posY += G.VOM.zoom.posY; 8054 8055 // imageContainer.children().eq(1)[0].style[G.CSStransformName]= 'translate('+ posX + 'px, '+ posY + 'px)'; 8056 imageContainer.children().eq(1)[0].style[G.CSStransformName]= 'translate('+ posX + 'px, '+ posY + 'px) rotate('+ G.VOM.NGY2Item(0).rotationAngle +'deg)'; 8057 8058 8059 } 8060 8061 8062 // display media with internal viewer 8063 function OpenInternalViewer( ) { 8064 8065 G.VOM.viewerDisplayed = true; 8066 G.GOM.firstDisplay = false; 8067 8068 G.VOM.saveOverflowX = window.getComputedStyle(document.body)['overflow-x']; 8069 G.VOM.saveOverflowY = window.getComputedStyle(document.body)['overflow-y']; 8070 jQuery('body').css({ overflow: 'hidden' }); //avoid scrollbars 8071 8072 G.VOM.$cont = jQuery('<div class="nGY2 nGY2ViewerContainer" style="opacity:1"></div>').appendTo('body'); 8073 8074 SetViewerTheme(); 8075 8076 G.VOM.$viewer = jQuery('<div class="nGY2Viewer" style="opacity:0" itemscope itemtype="http://schema.org/ImageObject"></div>').appendTo( G.VOM.$cont ); 8077 G.VOM.$viewer.css({ msTouchAction: 'none', touchAction: 'none' }); // avoid pinch zoom 8078 8079 G.VOM.currItemIdx = 0; 8080 var sMedia = '<div class="nGY2ViewerMediaPan"><div class="nGY2ViewerMediaLoaderDisplayed"></div>' + G.VOM.NGY2Item(-1).mediaMarkup + '</div>'; // previous media 8081 sMedia += '<div class="nGY2ViewerMediaPan"><div class="nGY2ViewerMediaLoaderDisplayed"></div>' + G.VOM.NGY2Item(0).mediaMarkup + '</div>'; // current media 8082 sMedia += '<div class="nGY2ViewerMediaPan"><div class="nGY2ViewerMediaLoaderDisplayed"></div>' + G.VOM.NGY2Item(1).mediaMarkup + '</div>'; // next media 8083 8084 var sNav = ''; 8085 var iconP = G.O.icons.viewerImgPrevious; 8086 if( iconP != undefined && iconP != '') { 8087 sNav += '<div class="nGY2ViewerAreaPrevious ngy2viewerToolAction" data-ngy2action="previous">' + iconP + '</div>'; 8088 } 8089 var iconN = G.O.icons.viewerImgNext; 8090 if( iconN != undefined && iconN != '') { 8091 sNav += '<div class="nGY2ViewerAreaNext ngy2viewerToolAction" data-ngy2action="next">' + iconN + '</div>'; 8092 } 8093 8094 G.VOM.$content = jQuery('<div class="nGY2ViewerContent">' + sMedia + sNav + '</div>').appendTo( G.VOM.$viewer ); 8095 8096 var $mediaPan = G.VOM.$content.find('.nGY2ViewerMediaPan'); 8097 G.VOM.$mediaPrevious = $mediaPan.eq(0); // pointer to previous media container 8098 G.VOM.$mediaCurrent = $mediaPan.eq(1); // pointer to current media container 8099 G.VOM.$mediaNext = $mediaPan.eq(2); // pointer to next media container 8100 8101 G.VOM.ImageLoader.loadImage( VieweImgSizeRetrieved, G.VOM.NGY2Item(0) ); 8102 G.VOM.ImageLoader.loadImage( VieweImgSizeRetrieved, G.VOM.NGY2Item(-1) ); 8103 G.VOM.ImageLoader.loadImage( VieweImgSizeRetrieved, G.VOM.NGY2Item(1) ); 8104 8105 G.VOM.padding.H = parseInt(G.VOM.$content.css("padding-left")) + parseInt(G.VOM.$content.css("padding-right")); 8106 G.VOM.padding.V = parseInt(G.VOM.$content.css("padding-top")) + parseInt(G.VOM.$content.css("padding-bottom")); 8107 8108 // build media toolbar container 8109 var vtbBg1 = ''; 8110 var vtbBg2 = ' toolbarBackground'; 8111 if( G.O.viewerToolbar.fullWidth ) { 8112 vtbBg1 = ' toolbarBackground'; 8113 vtbBg2 = ''; 8114 } 8115 var vtbAlign = 'text-align:center;'; 8116 switch ( G.O.viewerToolbar.align ) { 8117 case 'left': 8118 vtbAlign = 'text-align:left;'; 8119 break; 8120 case 'right': 8121 vtbAlign = 'text-align:right;'; 8122 break; 8123 } 8124 var sTB = '<div class="toolbarContainer nGEvent' + vtbBg1 + '" style="visibility:' +(G.O.viewerToolbar.display ? "visible" : "hidden")+';'+vtbAlign+'"><div class="toolbar nGEvent' + vtbBg2 + '"></div></div>'; 8125 G.VOM.$toolbar = jQuery(sTB).appendTo(G.VOM.$viewer); 8126 8127 if( G.VOM.toolbarMode == 'min' || (G.O.viewerToolbar.autoMinimize > 0 && G.O.viewerToolbar.autoMinimize >= G.GOM.cache.viewport.w) ) { 8128 ViewerToolbarForVisibilityMin(); 8129 } 8130 else { 8131 ViewerToolbarForVisibilityStd(); 8132 } 8133 8134 // top-left toolbar 8135 if( G.O.viewerTools.topLeft != '' ) { 8136 var sTopLeft = '<div class="nGY2ViewerToolsTopLeft nGEvent"><div class="toolbar nGEvent">'; 8137 var sTL = G.O.viewerTools.topLeft.split(','); 8138 for( var i = 0, sTLL = sTL.length; i < sTLL; i++) { 8139 sTopLeft += ToolbarAddElt( sTL[i] ); 8140 } 8141 sTopLeft += '</div></div>'; 8142 G.VOM.$toolbarTL = jQuery(sTopLeft).appendTo(G.VOM.$viewer); 8143 } 8144 // top-right toolbar 8145 if( G.O.viewerTools.topRight != '' ) { 8146 var sTopRight = '<div class="nGY2ViewerToolsTopRight nGEvent"><div class="toolbar nGEvent">'; 8147 var sTR = G.O.viewerTools.topRight.split(','); 8148 for( var i = 0, sTRL = sTR.length; i < sTRL; i++) { 8149 sTopRight += ToolbarAddElt( sTR[i] ); 8150 } 8151 sTopRight += '</div></div>'; 8152 G.VOM.$toolbarTR = jQuery(sTopRight).appendTo(G.VOM.$viewer); 8153 } 8154 8155 // set the events handler for toolbars 8156 ViewerToolsOn(); 8157 8158 // display logo 8159 if( G.O.viewerDisplayLogo ) { G.$E.vwLogo=jQuery('<div class="nGY2 nGY2ViewerLogo"></div>').appendTo(G.VOM.$viewer); } 8160 8161 // Go to fullscreen mode 8162 if( ngscreenfull.enabled && G.O.viewerFullscreen ) { ngscreenfull.request(); } 8163 8164 setElementOnTop('', G.VOM.$viewer); 8165 ResizeInternalViewer(true); 8166 G.VOM.timeImgChanged = new Date().getTime(); 8167 8168 // viewer display transition 8169 var tweenable = new NGTweenable(); 8170 tweenable.tween({ 8171 from: { opacity: 0, posY: G.VOM.window.lastHeight*.5 }, 8172 to: { opacity: 1, posY: 0 }, 8173 delay: 30, 8174 duration: 500, 8175 easing: 'easeOutQuart', 8176 step: function (state) { 8177 G.VOM.$viewer.css('opacity', state.opacity); 8178 G.VOM.$viewer[0].style[G.CSStransformName] = 'translateY(' + (state.posY) + 'px) '; 8179 } 8180 }); 8181 8182 // stop click propagation on media ==> if the user clicks outside of an media, the viewer is closed 8183 // --> no more supported since v2.0.0 8184 // G.VOM.$viewer.find('img').on('click', function (e) { e.stopPropagation(); }); 8185 8186 8187 ViewerMediaPanX(0); 8188 ViewerSetEvents(); 8189 8190 DisplayInternalViewer(0, ''); 8191 8192 if( G.O.slideshowAutoStart ) { 8193 G.VOM.playSlideshow = false; 8194 SlideshowToggle(); 8195 } 8196 } 8197 8198 function ViewerEvents() { 8199 if( !G.VOM.viewerDisplayed || G.VOM.viewerMediaIsChanged || G.VOM.NGY2Item(0).mediaKind != 'img') { 8200 // ignore fired event if viewer not displayed or if currently changed or if current media not an image 8201 return false; 8202 } 8203 return true; 8204 } 8205 8206 8207 // viewer gesture handling 8208 function ViewerSetEvents() { 8209 8210 if( G.VOM.hammertime == null ) { 8211 8212 G.VOM.hammertime = new NGHammer.Manager(G.VOM.$cont[0], { 8213 recognizers: [ 8214 [NGHammer.Pinch, { enable: true }], 8215 [NGHammer.Pan, { direction: NGHammer.DIRECTION_ALL }] 8216 ] 8217 }); 8218 8219 // PAN 8220 G.VOM.hammertime.on('pan', function(ev) { 8221 if( !ViewerEvents() ) { return; } 8222 8223 if( G.VOM.zoom.isZooming ) { 8224 // pan zoomed image 8225 ViewerImagePanSetPosition(G.VOM.panPosX + ev.deltaX, G.VOM.panPosY + ev.deltaY, G.VOM.$mediaCurrent, false); 8226 if( G.VOM.toolbarsDisplayed == true ) { 8227 G.VOM.toolsHide(); 8228 } 8229 } 8230 else { 8231 if( ev.deltaY > G.VOM.panThreshold && Math.abs(ev.deltaX) < G.VOM.panThreshold && !G.VOM.panXOnly ) { 8232 // pan viewer down 8233 ViewerMediaPanX( 0 ); 8234 var dist = Math.min(ev.deltaY, 200); 8235 G.VOM.$viewer[0].style[G.CSStransformName] = 'translateY(' + dist + 'px) '; 8236 G.VOM.$viewer.css('opacity', 1-dist/200/2); 8237 } 8238 else { 8239 // pan media left/right 8240 if( Math.abs(ev.deltaX) > G.VOM.panThreshold ) { 8241 G.VOM.panXOnly = true; 8242 } 8243 ViewerMediaPanX( ev.deltaX ); 8244 G.VOM.$viewer[0].style[G.CSStransformName] = 'translateY(0px)'; 8245 G.VOM.$viewer.css('opacity', 1); 8246 } 8247 } 8248 }); 8249 8250 // PAN END 8251 G.VOM.hammertime.on('panend', function(ev) { 8252 if( !ViewerEvents() ) { return; } 8253 if( G.VOM.zoom.isZooming ) { 8254 G.VOM.timeImgChanged = new Date().getTime(); 8255 ViewerImagePanSetPosition(G.VOM.panPosX+ev.deltaX, G.VOM.panPosY+ev.deltaY, G.VOM.$mediaCurrent, true); 8256 } 8257 else { 8258 var panY = false; 8259 if( !G.VOM.panXOnly ) { 8260 if( ev.deltaY > 50 && Math.abs(ev.deltaX) < 50 ) { 8261 // close viewer 8262 CloseInternalViewer(G.VOM.currItemIdx); 8263 panY = true; 8264 } 8265 } 8266 if( !panY ) { 8267 if( Math.abs( ev.deltaX ) < 50 ) { 8268 ViewerMediaPanX(0); 8269 } 8270 else { 8271 ev.deltaX > 50 ? DisplayPreviousMedia() : DisplayNextMedia(); 8272 } 8273 } 8274 } 8275 G.VOM.panXOnly = false; 8276 }); 8277 8278 if( G.O.viewerZoom ) { 8279 8280 G.VOM.hammertime.add( new NGHammer.Tap({ event: 'doubletap', taps: 2 }) ); 8281 G.VOM.hammertime.add( new NGHammer.Tap({ event: 'singletap' }) ); 8282 G.VOM.hammertime.get('doubletap').recognizeWith('singletap'); 8283 G.VOM.hammertime.get('singletap').requireFailure('doubletap'); 8284 8285 // single tap -> next/previous media 8286 G.VOM.hammertime.on('singletap', function(ev) { 8287 if( !ViewerEvents() ) { return; } 8288 StopPropagationPreventDefault(ev.srcEvent); 8289 if( G.VOM.toolbarsDisplayed == false ) { 8290 debounce( ViewerToolsUnHide, 100, false)(); 8291 } 8292 else { 8293 if( ev.target.className.indexOf('nGY2ViewerMedia') !== -1 ) { 8294 if( ev.srcEvent.pageX < (G.GOM.cache.viewport.w/2) ) { 8295 DisplayPreviousMedia(); 8296 } 8297 else { 8298 DisplayNextMedia(); 8299 } 8300 } 8301 } 8302 }); 8303 8304 // double tap -> zoom 8305 G.VOM.hammertime.on('doubletap', function(ev) { 8306 if( !ViewerEvents() ) { return; } 8307 StopPropagationPreventDefault(ev.srcEvent); 8308 8309 if( ev.target.className.indexOf('nGY2ViewerMedia') !== -1 ) { 8310 // double tap only on image 8311 if( G.VOM.zoom.isZooming ) { 8312 G.VOM.zoom.isZooming = false; 8313 // G.VOM.zoom.userFactor = 1; 8314 ResizeInternalViewer(true); 8315 } 8316 else { 8317 if( ViewerZoomStart() ) { 8318 G.VOM.zoom.userFactor = 1.5; 8319 ViewerMediaSetPosAndZoom(); 8320 } 8321 } 8322 } 8323 }); 8324 8325 // pinch end 8326 G.VOM.hammertime.on('pinchend', function(ev) { 8327 ev.srcEvent.stopPropagation(); 8328 ev.srcEvent.preventDefault(); // cancel mouseenter event 8329 G.VOM.timeImgChanged = new Date().getTime(); 8330 }); 8331 G.VOM.hammertime.on('pinch', function(ev) { 8332 ev.srcEvent.stopPropagation(); 8333 ev.srcEvent.preventDefault(); // cancel mouseenter event 8334 8335 if( ViewerZoomStart() ) { 8336 G.VOM.zoom.userFactor = ev.scale; 8337 ViewerZoomMax(); 8338 ViewerZoomMin(); 8339 ViewerMediaSetPosAndZoom(); // center media 8340 } 8341 }); 8342 } 8343 else { 8344 // no zoom -> click/tap on image to go to next/previous one 8345 G.VOM.hammertime.on('tap', function(ev) { 8346 if( !ViewerEvents() ) { return; } 8347 StopPropagationPreventDefault(ev.srcEvent); 8348 if( G.VOM.toolbarsDisplayed == false ){ 8349 // display tools on tap if hidden 8350 debounce( ViewerToolsUnHide, 100, false)(); 8351 } 8352 else { 8353 // display next/previous image if tools not hidden 8354 if( ev.target.className.indexOf('nGY2ViewerMedia') !== -1 ) { 8355 if( ev.srcEvent.pageX < (G.GOM.cache.viewport.w/2) ) { 8356 DisplayPreviousMedia(); 8357 } 8358 else { 8359 DisplayNextMedia(); 8360 } 8361 } 8362 } 8363 8364 }); 8365 } 8366 } 8367 } 8368 8369 function StopPropagationPreventDefault(e) { 8370 e.stopPropagation(); 8371 e.preventDefault(); 8372 } 8373 8374 // Hide toolbars on user inactivity 8375 function ViewerToolsHide() { 8376 if( G.VOM.viewerDisplayed ) { 8377 G.VOM.toolbarsDisplayed = false; 8378 ViewerToolsOpacity(0); 8379 } 8380 } 8381 8382 function ViewerToolsUnHide() { 8383 if( G.VOM.viewerDisplayed ) { 8384 G.VOM.toolbarsDisplayed = true; 8385 ViewerToolsOpacity(1); 8386 G.VOM.toolsHide(); 8387 } 8388 } 8389 8390 function ViewerToolsOpacity( op ) { 8391 if( G.VOM.$toolbar != null ) { 8392 G.VOM.$toolbar.css('opacity', op); 8393 } 8394 if( G.VOM.$toolbarTL != null ) { 8395 G.VOM.$toolbarTL.css('opacity', op); 8396 } 8397 if( G.VOM.$toolbarTR != null ) { 8398 G.VOM.$toolbarTR.css('opacity', op); 8399 } 8400 G.VOM.$content.find('.nGY2ViewerAreaNext').css('opacity', op); 8401 G.VOM.$content.find('.nGY2ViewerAreaPrevious').css('opacity', op); 8402 } 8403 8404 8405 8406 function ViewerToolsOn() { 8407 // removes all events 8408 G.VOM.$viewer.off('touchstart click', '.ngy2viewerToolAction', ViewerToolsAction); 8409 8410 // action button 8411 G.VOM.$viewer.on('touchstart click', '.ngy2viewerToolAction', ViewerToolsAction); 8412 } 8413 8414 8415 // Actions of the buttton/elements 8416 function ViewerToolsAction(e) { 8417 // delay to avoid twice handling on smartphone/tablet (both touchstart click events are fired) 8418 if( (new Date().getTime()) - G.timeLastTouchStart < 300 ) { return; } 8419 G.timeLastTouchStart = new Date().getTime(); 8420 8421 var $this = $(this); 8422 var ngy2action = $this.data('ngy2action'); 8423 if( ngy2action == undefined ) { return; } 8424 switch( ngy2action ) { 8425 case 'next': 8426 StopPropagationPreventDefault(e); 8427 DisplayNextMedia(); 8428 break; 8429 case 'previous': 8430 StopPropagationPreventDefault(e); 8431 DisplayPreviousMedia(); 8432 break; 8433 case 'playPause': 8434 e.stopPropagation(); 8435 SlideshowToggle(); 8436 break; 8437 case 'zoomIn': 8438 StopPropagationPreventDefault(e); 8439 if( ViewerZoomStart() ) { ViewerZoomIn( true ); } 8440 break; 8441 case 'zoomOut': 8442 StopPropagationPreventDefault(e); 8443 if( ViewerZoomStart() ) { ViewerZoomIn( false ); } 8444 break; 8445 case 'minimize': 8446 // toggle toolbar visibility 8447 StopPropagationPreventDefault(e); 8448 if( G.VOM.toolbarMode == 'std' ) { 8449 ViewerToolbarForVisibilityMin(); 8450 } 8451 else { 8452 ViewerToolbarForVisibilityStd(); 8453 } 8454 break; 8455 case 'fullScreen': 8456 // Toggle viewer fullscreen mode on/off 8457 e.stopPropagation(); 8458 if( ngscreenfull.enabled ) { 8459 ngscreenfull.toggle(); 8460 } 8461 break; 8462 case 'info': 8463 e.stopPropagation(); 8464 ItemDisplayInfo(G.VOM.NGY2Item(0)); 8465 break; 8466 case 'close': 8467 StopPropagationPreventDefault(e); 8468 if( (new Date().getTime()) - G.VOM.timeImgChanged < 400 ) { return; } 8469 CloseInternalViewer(G.VOM.currItemIdx); 8470 break; 8471 case 'download': 8472 StopPropagationPreventDefault(e); 8473 DownloadImage(G.VOM.items[G.VOM.currItemIdx].ngy2ItemIdx); 8474 break; 8475 case 'share': 8476 StopPropagationPreventDefault(e); 8477 PopupShare(G.VOM.items[G.VOM.currItemIdx].ngy2ItemIdx); 8478 break; 8479 case 'linkOriginal': 8480 StopPropagationPreventDefault(e); 8481 OpenOriginal( G.VOM.NGY2Item(0) ); 8482 break; 8483 case 'rotateLeft': 8484 StopPropagationPreventDefault(e); 8485 ViewerImageRotate(-90); 8486 break; 8487 case 'rotateRight': 8488 StopPropagationPreventDefault(e); 8489 ViewerImageRotate(90); 8490 break; 8491 } 8492 8493 // custom button 8494 var fu = G.O.fnImgToolbarCustClick; 8495 if( ngy2action.indexOf('custom') == 0 && fu !== null ) { 8496 typeof fu == 'function' ? fu(ngy2action, $this, G.VOM.NGY2Item(0)) : window[fu](ngy2action, $this, G.VOM.NGY2Item(0)); 8497 } 8498 } 8499 8500 // rotate displayed image 8501 function ViewerImageRotate( angle ) { 8502 var item = G.VOM.NGY2Item(0); 8503 if( item.mediaKind == 'img' ) { 8504 item.rotationAngle += angle; 8505 item.rotationAngle = item.rotationAngle % 360; 8506 if( item.rotationAngle < 0 ) { 8507 item.rotationAngle += 360; 8508 } 8509 ViewerMediaPanX( 0 ); 8510 ViewerMediaSetPosAndZoomOne( G.VOM.NGY2Item(0), G.VOM.$mediaCurrent, true ); 8511 } 8512 } 8513 8514 8515 // Display photo infos 8516 function ItemDisplayInfo( item ) { 8517 8518 var content = '<div class="nGY2PopupOneItemText">' + item.title + '</div>'; 8519 content += '<div class="nGY2PopupOneItemText">' + item.description + '</div>'; 8520 if( item.author != '' ) { 8521 content += '<div class="nGY2PopupOneItemText">' + G.O.icons.user + ' ' + item.author + '</div>'; 8522 } 8523 if( item.exif.model != '' ) { 8524 content += '<div class="nGY2PopupOneItemText">' + G.O.icons.config + ' ' + item.exif.model + '</div>'; 8525 } 8526 var sexif = ''; 8527 sexif += item.exif.flash == '' ? '' : ' ' + item.exif.flash; 8528 sexif += item.exif.focallength == '' ? '' : ' ' + item.exif.focallength+'mm'; 8529 sexif += item.exif.fstop == '' ? '' : ' f' + item.exif.fstop; 8530 sexif += item.exif.exposure == '' ? '' : ' ' + item.exif.exposure+'s'; 8531 sexif += item.exif.iso == '' ? '' : ' ' + item.exif.iso+' ISO'; 8532 if( item.exif.time != '' ) { 8533 // var date = new Date(parseInt(item.exif.time)); 8534 // sexif += ' '+date.toLocaleDateString(); 8535 sexif += ' ' + item.exif.time; 8536 } 8537 content += '<div class="nGY2PopupOneItemText">' + sexif + '</div>'; 8538 8539 if( item.exif.location != '' ) { 8540 content += '<div class="nGY2PopupOneItemText">'+G.O.icons.location+' <a href="http://maps.google.com/maps?z=12&t=m&q='+encodeURIComponent(item.exif.location)+'" target="_blank">'+item.exif.location+'</a></div>'; 8541 // embed google map in iframe (no api key required) 8542 content += '<iframe width="300" height="150" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="https://maps.google.com/maps?&t=m&q='+encodeURIComponent( item.exif.location ) +'&output=embed"></iframe>'; 8543 } 8544 8545 Popup(G.O.icons.viewerInfo, content, 'Left'); 8546 8547 } 8548 8549 8550 8551 function ToolbarAddElt( elt ) { 8552 var r = '<div class="ngbt ngy2viewerToolAction ', 8553 e=elt.replace(/^\s+|\s+$/g, ''); // remove trailing/leading whitespace 8554 switch( e ) { 8555 case 'minimizeButton': 8556 var ic = G.O.icons.viewerToolbarMin; 8557 if( G.VOM.toolbarMode == 'min' ) { 8558 ic = G.O.icons.viewerToolbarStd; 8559 } 8560 r += 'minimizeButton nGEvent" data-ngy2action="minimize">'+ic+'</div>'; 8561 break; 8562 case 'previousButton': 8563 r += 'previousButton nGEvent" data-ngy2action="previous">'+G.O.icons.viewerPrevious+'</div>'; 8564 break; 8565 case 'pageCounter': 8566 r += 'pageCounter nGEvent"></div>'; 8567 break; 8568 case 'nextButton': 8569 r += 'nextButton nGEvent" data-ngy2action="next">'+G.O.icons.viewerNext+'</div>'; 8570 break; 8571 case 'playPauseButton': 8572 r += 'playButton playPauseButton nGEvent" data-ngy2action="playPause">'+G.O.icons.viewerPlay+'</div>'; 8573 break; 8574 case 'rotateLeft': 8575 r += 'rotateLeftButton nGEvent" data-ngy2action="rotateLeft">'+G.O.icons.viewerRotateLeft+'</div>'; 8576 break; 8577 case 'rotateRight': 8578 r += 'rotateRightButton nGEvent" data-ngy2action="rotateRight">'+G.O.icons.viewerRotateRight+'</div>'; 8579 break; 8580 case 'downloadButton': 8581 r += 'downloadButton nGEvent" data-ngy2action="download">'+G.O.icons.viewerDownload+'</div>'; 8582 break; 8583 case 'zoomButton': 8584 r += 'nGEvent" data-ngy2action="zoomIn">'+G.O.icons.viewerZoomIn+'</div><div class="ngbt ngy2viewerToolAction nGEvent" data-ngy2action="zoomOut">'+G.O.icons.viewerZoomOut+'</div>'; 8585 break; 8586 case 'fullscreenButton': 8587 var s = G.O.icons.viewerFullscreenOn; 8588 if( ngscreenfull.enabled && G.VOM.viewerIsFullscreen ) { 8589 s = G.O.icons.viewerFullscreenOff; 8590 } 8591 r += 'setFullscreenButton fullscreenButton nGEvent" data-ngy2action="fullScreen">'+s+'</div>'; 8592 break; 8593 case 'infoButton': 8594 r += 'infoButton nGEvent" data-ngy2action="info">'+G.O.icons.viewerInfo+'</div>'; 8595 break; 8596 case 'linkOriginalButton': 8597 r += 'linkOriginalButton nGEvent" data-ngy2action="linkOriginal">' + G.O.icons.viewerLinkOriginal + '</div>'; 8598 break; 8599 case 'closeButton': 8600 r += 'closeButton nGEvent" data-ngy2action="close">'+G.O.icons.buttonClose+'</div>'; 8601 break; 8602 case 'shareButton': 8603 r += 'nGEvent" data-ngy2action="share">'+G.O.icons.viewerShare+'</div>'; 8604 break; 8605 case 'label': 8606 r += '"><div class="title nGEvent" itemprop="name"></div><div class="description nGEvent" itemprop="description"></div></div>'; 8607 break; 8608 default: 8609 // custom button 8610 if( e.indexOf('custom') == 0 ) { 8611 var t = ''; 8612 // content to display from custom script 8613 var fu = G.O.fnImgToolbarCustInit; 8614 if( fu !== null ) { 8615 typeof fu == 'function' ? fu(e) : window[fu](e); 8616 } 8617 if( t == undefined || t == '' ) { 8618 // content from icons 8619 var n = e.substring(6); 8620 t = G.O.icons['viewerCustomTool'+n]; 8621 } 8622 r += 'ngy2CustomBtn ' + e + ' nGEvent" data-ngy2action="' + e + '">' + t + '</div>'; 8623 } 8624 else { 8625 r = ''; 8626 } 8627 break; 8628 } 8629 return r; 8630 } 8631 8632 8633 // toggle slideshow mode on/off 8634 function SlideshowToggle(){ 8635 if( G.VOM.playSlideshow ) { 8636 window.clearTimeout(G.VOM.playSlideshowTimerID); 8637 G.VOM.playSlideshow = false; 8638 G.VOM.$viewer.find('.playPauseButton').html(G.O.icons.viewerPlay); 8639 } 8640 else { 8641 G.VOM.playSlideshow = true; 8642 DisplayNextMedia(); 8643 G.VOM.$viewer.find('.playPauseButton').html(G.O.icons.viewerPause); 8644 } 8645 } 8646 8647 function ViewerToolbarForVisibilityStd() { 8648 G.VOM.toolbarMode = 'std'; 8649 8650 var sTB = ''; 8651 var t = G.O.viewerToolbar.standard.split(','); 8652 for( var i = 0, lt = t.length; i < lt; i++) { 8653 sTB += ToolbarAddElt( t[i] ); 8654 } 8655 G.VOM.$toolbar.find('.toolbar').html(sTB); 8656 ViewerToolbarElementContent(); 8657 } 8658 8659 function ViewerToolbarForVisibilityMin() { 8660 if( G.O.viewerToolbar.minimized == undefined || G.O.viewerToolbar.minimized == '' ) { 8661 ViewerToolbarForVisibilityStd(); 8662 } 8663 else { 8664 G.VOM.toolbarMode = 'min'; 8665 var sTB = ''; 8666 var t = G.O.viewerToolbar.minimized.split(','); 8667 for( var i = 0, lt = t.length; i < lt; i++) { 8668 sTB += ToolbarAddElt( t[i] ); 8669 } 8670 G.VOM.$toolbar.find('.toolbar').html(sTB); 8671 ViewerToolbarElementContent(); 8672 } 8673 } 8674 8675 function ViewerToolbarElementContent() { 8676 8677 var vomIdx=G.VOM.currItemIdx; 8678 if( vomIdx == null ) { return; } 8679 8680 var item=G.VOM.NGY2Item(0); 8681 8682 // LABEL 8683 var setTxt = false; 8684 // set title 8685 if( item.title !== undefined && item.title != '' ) { 8686 G.VOM.$viewer.find('.ngy2viewerToolAction').find('.title').html(item.title); 8687 setTxt = true; 8688 } 8689 else { 8690 G.VOM.$viewer.find('.ngy2viewerToolAction').find('.title').html(''); 8691 } 8692 // set description 8693 if( item.description !== undefined && item.description != '' ) { 8694 // G.VOM.$viewer.find('.ngy2viewerToolAction').find('.description').html(item.description); 8695 // setTxt = true; 8696 } 8697 else { 8698 // G.VOM.$viewer.find('.ngy2viewerToolAction').find('.description').html(''); 8699 } 8700 8701 if( setTxt ) { 8702 G.VOM.$viewer.find('.ngy2viewerToolAction').find('.label').show(); 8703 } 8704 else { 8705 G.VOM.$viewer.find('.ngy2viewerToolAction').find('.label').hide(); 8706 } 8707 8708 // set page number 8709 var viewerMaxImages = G.VOM.items.length; 8710 if( viewerMaxImages > 0 ) { 8711 G.VOM.$viewer.find('.pageCounter').html((G.VOM.items[vomIdx].mediaNumber)+'/'+viewerMaxImages); 8712 } 8713 8714 // custom elements 8715 var $cu = G.VOM.$viewer.find('.ngy2CustomBtn'); 8716 var fu = G.O.fnImgToolbarCustDisplay; 8717 if( $cu.length > 0 && fu !== null ) { 8718 typeof fu == 'function' ? fu($cu, item) : window[fu]($cu, item); 8719 } 8720 8721 // set event handlers again 8722 ViewerToolsOn();
8723 } 8724 8725 // Pan the media container in the lightbox (left/right) 8726 function ViewerMediaPanX( posX ) { 8727 G.VOM.swipePosX = posX; 8728 if( G.CSStransformName == null ) { 8729 // no pan if CSS transform not supported 8730 // G.VOM.$mediaCurrent.css({ left: posX }); 8731 } 8732 else { 8733 8734 // pan left/right the current media 8735 // window.ng_draf( function() { 8736 G.VOM.$mediaCurrent[0].style[G.CSStransformName] = 'translate(' + posX + 'px, 0px)'; 8737 // }); 8738 8739 var itemPrevious = G.VOM.NGY2Item(-1); 8740 var itemNext = G.VOM.NGY2Item(1); 8741 8742 // next/previous media 8743 if( G.O.imageTransition.startsWith('SWIPE') ) { 8744 if( itemPrevious.mediaTransition() ) { 8745 ViewerSetMediaVisibility(G.VOM.NGY2Item(-1), G.VOM.$mediaPrevious, 1); 8746 } 8747 if( itemNext.mediaTransition() ) { 8748 ViewerSetMediaVisibility(G.VOM.NGY2Item(1), G.VOM.$mediaNext, 1); 8749 } 8750 8751 var sc = Math.min( Math.max( Math.abs(posX) / G.VOM.window.lastWidth, .8), 1); 8752 if( G.O.imageTransition == 'SWIPE' ) { sc = 1; } 8753 8754 if( posX > 0 ) { 8755 var dir = G.VOM.window.lastWidth; 8756 if( itemPrevious.mediaTransition() ) { 8757 // window.ng_draf( function() { 8758 G.VOM.$mediaPrevious[0].style[G.CSStransformName] = 'translate(' + (-dir + posX) + 'px, 0px) scale(' + sc + ')'; 8759 // }); 8760 } 8761 if( itemNext.mediaTransition() ) { 8762 // window.ng_draf( function() { 8763 G.VOM.$mediaNext[0].style[G.CSStransformName] = 'translate(' + (dir) + 'px, 0px) scale(' + sc + ')'; 8764 // }); 8765 } 8766 } 8767 else { 8768 var dir = -G.VOM.window.lastWidth; 8769 if( itemNext.mediaTransition() ) { 8770 // window.ng_draf( function() { 8771 G.VOM.$mediaNext[0].style[G.CSStransformName] = 'translate(' + (-dir + posX) + 'px, 0px) scale(' + sc + ')'; 8772 // }); 8773 } 8774 if( itemPrevious.mediaTransition() ) { 8775 // window.ng_draf( function() { 8776 G.VOM.$mediaPrevious[0].style[G.CSStransformName] = 'translate(' + (dir) + 'px, 0px) scale(' + sc + ')'; 8777 // }); 8778 } 8779 } 8780 } 8781 8782 8783 if( G.O.imageTransition == 'SLIDEAPPEAR' ) { 8784 G.VOM.$mediaPrevious[0].style[G.CSStransformName] = ''; 8785 G.VOM.$mediaNext[0].style[G.CSStransformName] = ''; 8786 if( posX < 0 ) { 8787 var o = (-posX) / G.VOM.window.lastWidth; 8788 if( itemNext.mediaTransition() ) { 8789 ViewerSetMediaVisibility(itemNext, G.VOM.$mediaNext, o); 8790 } 8791 if( itemPrevious.mediaTransition() ) { 8792 ViewerSetMediaVisibility(itemPrevious, G.VOM.$mediaPrevious, 0); 8793 } 8794 } 8795 else { 8796 var o = posX / G.VOM.window.lastWidth; 8797 if( itemPrevious.mediaTransition() ) { 8798 ViewerSetMediaVisibility(itemPrevious, G.VOM.$mediaPrevious, o); 8799 } 8800 if( itemNext.mediaTransition() ) { 8801 ViewerSetMediaVisibility(itemNext, G.VOM.$mediaNext, 0); 8802 } 8803 } 8804 } 8805 } 8806 } 8807 8808 // Display next image 8809 function DisplayNextMedia() { 8810 if( G.VOM.viewerMediaIsChanged || ((new Date().getTime()) - G.VOM.timeImgChanged < 300) ) { return; } 8811 8812 TriggerCustomEvent('lightboxNextImage'); 8813 DisplayInternalViewer(G.VOM.IdxNext(), 'nextImage'); 8814 }; 8815 8816 // Display previous image 8817 function DisplayPreviousMedia() { 8818 if( G.VOM.viewerMediaIsChanged || ((new Date().getTime()) - G.VOM.timeImgChanged < 300) ) { return; } 8819 if( G.VOM.playSlideshow ) { 8820 SlideshowToggle(); 8821 } 8822 8823 TriggerCustomEvent('lightboxPreviousImage'); 8824 DisplayInternalViewer(G.VOM.IdxPrevious(), 'previousImage'); 8825 }; 8826 8827 // Display image (and run animation)
8828 function DisplayInternalViewer( newVomIdx, displayType ) { 8829 8830 if( G.VOM.playSlideshow ) { window.clearTimeout(G.VOM.playSlideshowTimerID); } 8831 8832 var itemOld = G.VOM.NGY2Item(0); 8833 var itemNew = G.I[G.VOM.items[newVomIdx].ngy2ItemIdx]; 8834 var $new = (displayType == 'nextImage' ? G.VOM.$mediaNext : G.VOM.$mediaPrevious); 8835 if( displayType == 'nextImage' ) { 8836 ViewerSetMediaVisibility(G.VOM.NGY2Item(-1), G.VOM.$mediaPrevious, 0); 8837 } 8838 else { 8839 ViewerSetMediaVisibility(G.VOM.NGY2Item(1), G.VOM.$mediaNext, 0); 8840 } 8841 8842 G.VOM.timeImgChanged = new Date().getTime(); 8843 G.VOM.viewerMediaIsChanged = true; 8844 G.VOM.zoom.isZooming = false; 8845 ResizeInternalViewer(true); 8846 8847 if( G.O.debugMode && console.timeline ) { console.timeline('nanogallery2_viewer'); } 8848 8849 SetLocationHash( itemNew.albumID, itemNew.GetID() ); 8850 8851 // animation duration is proportional of the remaining distance 8852 var vP = G.GOM.cache.viewport; 8853 var dur = 400 * (vP.w - Math.abs(G.VOM.swipePosX)) / vP.w; 8854 8855 if( displayType == '' ) { 8856 // first image --> just appear / no slide animation 8857 ViewerSetMediaVisibility(G.VOM.NGY2Item(0), G.VOM.$mediaCurrent, 1); 8858 if( G.CSStransformName == null ) { 8859 // no CSS transform support -> no animation 8860 ViewerSetMediaVisibility(itemNew, $new, 1); 8861 DisplayInternalViewerComplete(displayType, newVomIdx); 8862 } 8863 else { 8864 ViewerSetMediaVisibility(itemNew, $new, 0); 8865 new NGTweenable().tween({ 8866 from: { opacity: 0 }, 8867 to: { opacity: 1 }, 8868 attachment: { dT: displayType, item: itemOld }, 8869 easing: 'easeInOutSine', 8870 delay: 30, 8871 duration: 400, 8872 step: function (state, att) { 8873 // using scale is not a good idea on Chrome -> image will be blurred 8874 G.VOM.$content.css('opacity', state.opacity); 8875 }, 8876 finish: function (state, att) { 8877 ViewerToolsUnHide(); 8878 DisplayInternalViewerComplete(att.dT, newVomIdx); 8879 } 8880 }); 8881 } 8882 } 8883 else { 8884 // animate the image transition between 2 images 8885 if( G.CSStransformName == null ) { 8886 // no CSS transform support -> no animation 8887 ViewerSetMediaVisibility(itemNew, $new, 1); 8888 ViewerSetMediaVisibility(G.VOM.NGY2Item(0), G.VOM.$mediaCurrent, 1); 8889 DisplayInternalViewerComplete(displayType, newVomIdx); 8890 } 8891 else { 8892 switch( G.O.imageTransition ) { 8893 case 'SWIPE': 8894 case 'SWIPE2': 8895 var dir = ( displayType == 'nextImage' ? - vP.w : vP.w ); 8896 $new[0].style[G.CSStransformName] = 'translate('+(-dir)+'px, 0px) ' 8897 8898 new NGTweenable().tween({ 8899 from: { t: G.VOM.swipePosX }, 8900 to: { t: (displayType == 'nextImage' ? - vP.w : vP.w) }, 8901 attachment: { dT: displayType, $e: $new, itemNew: itemNew, itemOld: itemOld, dir: dir }, 8902 delay: 30, 8903 duration: dur, 8904 easing: (G.O.imageTransition == 'swipe' ? 'easeInOutSine' : 'easeInOutCubic'), 8905 step: function (state, att) { 8906 // current media 8907 ViewerSetMediaVisibility(G.VOM.NGY2Item(0), G.VOM.$mediaCurrent, 1); 8908 // G.VOM.$mediaCurrent[0].style[G.CSStransformName] = 'translate(' + state.t + 'px, 0px) rotate(' + att.itemOld.rotationAngle + 'deg)'; 8909 G.VOM.$mediaCurrent[0].style[G.CSStransformName] = 'translate(' + state.t + 'px, 0px)'; 8910 // new media 8911 if( att.itemNew.mediaTransition() ) { 8912 ViewerSetMediaVisibility(att.itemNew, att.$e, 1); 8913 8914 var sc = Math.min( Math.max( (Math.abs(state.t)) / G.VOM.window.lastWidth, .8), 1); 8915 if( G.O.imageTransition == 'SWIPE' ) { sc = 1; } 8916 // att.$e[0].style[G.CSStransformName] = 'translate(' + (-att.dir+state.t) + 'px, 0px) scale(' + sc + ') rotate(' + att.itemNew.rotationAngle + 'deg)'; 8917 att.$e[0].style[G.CSStransformName] = 'translate(' + (-att.dir+state.t) + 'px, 0px) scale(' + sc + ')'; 8918 } 8919 }, 8920 finish: function (state, att) { 8921 G.VOM.$mediaCurrent[0].style[G.CSStransformName] = ''; 8922 att.$e[0].style[G.CSStransformName] = ''; 8923 DisplayInternalViewerComplete(att.dT, newVomId
8923x); 8924 } 8925 }); 8926 break; 8927 8928 case 'SLIDEAPPEAR': 8929 default: 8930 var dir=(displayType == 'nextImage' ? - vP.w : vP.w); 8931 var op = (Math.abs(G.VOM.swipePosX)) / G.VOM.window.lastWidth; 8932 $new[0].style[G.CSStransformName] = ''; 8933 new NGTweenable().tween({ 8934 from: { o: op, t: G.VOM.swipePosX }, 8935 to: { o: 1, t: (displayType == 'nextImage' ? - vP.w : vP.w) }, 8936 attachment: { dT:displayType, $e:$new, itemNew: itemNew, itemOld: itemOld, dir: dir }, 8937 delay: 30, 8938 duration: dur, 8939 easing: 'easeInOutSine', 8940 step: function (state, att) { 8941 // current media - translate 8942 // G.VOM.$mediaCurrent[0].style[G.CSStransformName]= 'translate('+state.t+'px, 0px) rotate(' + att.itemOld.rotationAngle + 'deg)'; 8943 G.VOM.$mediaCurrent[0].style[G.CSStransformName]= 'translate('+state.t+'px, 0px)'; 8944 // new media - opacity 8945 if( att.itemNew.mediaTransition() ) { 8946 ViewerSetMediaVisibility(att.itemNew, att.$e, state.o); 8947 } 8948 }, 8949 finish: function (state, att) { 8950 G.VOM.$mediaCurrent[0].style[G.CSStransformName]= ''; 8951 DisplayInternalViewerComplete(att.dT, newVomIdx); 8952 } 8953 }); 8954 break; 8955 } 8956 } 8957 } 8958 } 8959 8960 8961 function DisplayInternalViewerComplete( displayType, newVomIdx ) { 8962 G.VOM.currItemIdx = newVomIdx; 8963 8964 var ngy2item = G.VOM.NGY2Item(0); 8965 8966 ViewerToolbarElementContent(); 8967 if( G.O.debugMode && console.timeline ) { console.timelineEnd('nanogallery2_viewer'); } 8968 8969 var fu=G.O.fnImgDisplayed; 8970 if( fu !== null ) { 8971 typeof fu == 'function' ? fu(ngy2item) : window[fu](ngy2item); 8972 } 8973 8974 G.VOM.swipePosX = 0; 8975 if( displayType != '' ) { 8976 // not on first media display 8977 // G.VOM.$mediaCurrent.off("click"); 8978 G.VOM.$mediaCurrent.removeClass('imgCurrent'); 8979 8980 var $tmp = G.VOM.$mediaCurrent; 8981 switch( displayType ) { 8982 case 'nextImage': 8983 G.VOM.$mediaCurrent = G.VOM.$mediaNext; 8984 G.VOM.$mediaNext = $tmp; 8985 break; 8986 case 'previousImage': 8987 G.VOM.$mediaCurrent = G.VOM.$mediaPrevious; 8988 G.VOM.$mediaPrevious = $tmp; 8989 break; 8990 } 8991 } 8992 8993 G.VOM.$mediaCurrent.addClass('imgCurrent'); 8994 8995 // re-sort the media containers --> current on top 8996 var $pans = G.VOM.$content.find('.nGY2ViewerMediaPan'); 8997 G.VOM.$mediaCurrent.insertAfter($pans.last()); 8998 8999 if( ngy2item.mediaKind == 'img' && ngy2item.imageWidth == 0 ) { 9000 ViewerSetMediaVisibility(G.VOM.NGY2Item(0), G.VOM.$mediaCurrent, 0); 9001 } 9002 else { 9003 G.VOM.$mediaCurrent.children().eq(0).attr('class', 'nGY2ViewerMediaLoaderHidden'); // hide preloader 9004 ViewerSetMediaVisibility(G.VOM.NGY2Item(0), G.VOM.$mediaCurrent, 1); 9005 } 9006 9007 9008 // set the new NEXT media 9009 G.VOM.$mediaNext.empty(); 9010 var nextItem = G.VOM.NGY2Item(1); 9011 var spreloader = '<div class="nGY2ViewerMediaLoaderDisplayed"></div>'; 9012 if( nextItem.mediaKind == 'img' && nextItem.imageWidth != 0 && nextItem.imageHeight != 0 ) { 9013 spreloader = '<div class="nGY2ViewerMediaLoaderHidden"></div>'; 9014 } 9015 G.VOM.$mediaNext.append( spreloader + nextItem.mediaMarkup ); 9016 ViewerSetMediaVisibility(nextItem, G.VOM.$mediaNext, 0); 9017 if( nextItem.mediaKind == 'img' ) { 9018 G.VOM.ImageLoader.loadImage(VieweImgSizeRetrieved, nextItem); 9019 } 9020 else { 9021 ViewerMediaCenterNotImg( G.VOM.$mediaNext ); 9022 } 9023 9024 // set the new PREVIOUS media 9025 G.VOM.$mediaPrevious.empty(); 9026 var previousItem = G.VOM.NGY2Item(-1); 9027 spreloader = '<div class="nGY2ViewerMediaLoaderDisplayed"></div>'; 9028 if( previousItem.mediaKind == 'img' && previousItem.imageWidth != 0 && previousItem.imageHeight != 0 ) { 9029 spreloader = '<div class="nGY2ViewerMediaLoaderHidden"></div>'; 9030 } 9031 G.VOM.$mediaPrevious.append( spreloader + previousItem.mediaMarkup ); 9032 ViewerSetMediaVisibility(previousItem, G.VOM.$mediaPrevious, 0); 9033 if( previousItem.mediaKind == 'img' ) { 9034 G.VOM.ImageLoader.loadImage( VieweImgSizeRetrieved, previousItem ); 9035 } 9036 else {
vendor: 16,252 bytes, lines 9037-9370
9037 ViewerMediaCenterNotImg( G.VOM.$mediaPrevious ); 9038 } 9039 9040 9041 // slideshow mode - wait until image is loaded to start the delay for next image 9042 if( G.VOM.playSlideshow ) { 9043 G.VOM.$mediaCurrent.children().eq(1).ngimagesLoaded().always( function( instance ) { 9044 if( G.VOM.playSlideshow ) { 9045 // in the meantime the user could have stopped the slideshow 9046 G.VOM.playSlideshowTimerID = window.setTimeout( function(){ DisplayNextMedia(); }, G.VOM.slideshowDelay ); 9047 } 9048 }); 9049 } 9050 9051 // close viewer when user clicks outside of the image 9052 // G.VOM.$mediaCurrent.on("click",function(e){ 9053 // e.stopPropagation(); 9054 // if( (new Date().getTime()) - G.VOM.timeImgChanged < 400 ) { return; } 9055 // StopPropagationPreventDefault(e); 9056 // CloseInternalViewer(G.VOM.currItemIdx); 9057 // return false; 9058 // }); 9059 9060 ResizeInternalViewer(); 9061 9062 G.VOM.viewerMediaIsChanged = false; 9063 TriggerCustomEvent('lightboxImageDisplayed'); 9064 9065 } 9066 9067 9068 // Is fired as soon as the size of an image has been retrieved (the download may not be finished) 9069 function VieweImgSizeRetrieved(w, h, item, n) { 9070 item.imageWidth = w; 9071 item.imageHeight = h; 9072 9073 // image sized retrieved for currently displayed media 9074 // if( G.VOM.$mediaCurrent !== null && G.VOM.$mediaCurrent.children().attr('src') == item.responsiveURL() ) { 9075 if( G.VOM.NGY2Item(0) == item ) { 9076 // it is the current displayed media 9077 G.VOM.$mediaCurrent.children().eq(0).attr('class', 'nGY2ViewerMediaLoaderHidden'); // hide preloader 9078 ViewerSetMediaVisibility(G.VOM.NGY2Item(0), G.VOM.$mediaCurrent, 1); 9079 G.VOM.zoom.userFactor = 1; 9080 } 9081 9082 if( G.VOM.NGY2Item(1) == item ) { // next media 9083 G.VOM.$mediaNext.children().eq(0).attr('class', 'nGY2ViewerMediaLoaderHidden'); // hide preloader 9084 } 9085 if( G.VOM.NGY2Item(-1) == item ) { // previous media 9086 G.VOM.$mediaPrevious.children().eq(0).attr('class', 'nGY2ViewerMediaLoaderHidden'); // hide preloader 9087 } 9088 9089 ViewerMediaSetPosAndZoom(); 9090 9091 } 9092 9093 // Viewer - Set the visibility of the media and it's container 9094 function ViewerSetMediaVisibility(item, $media, opacity ) { 9095 9096 if( item.mediaKind == 'img' && item.imageWidth == 0 ) { 9097 // do not display image if width is unknown (--> callback will set the width when know) 9098 // setting opacity to 0 is not enough -> it is mandatory to change also the visibility to hidden to avoid responds to events (click/touch) 9099 // $media.children().css({ opacity: 0, visibility: 'hidden' }); 9100 $media.children().eq(1).css({ opacity: 0, visibility: 'hidden' }); // hide media 9101 // $media.css({ opacity: 0, visibility: 'hidden' }); 9102 return; 9103 } 9104 9105 if( opacity == 0 ) { 9106 // setting opacity to 0 is not enough -> it is mandatory to change also the visibility to hidden to avoid responds to events (click/touch) 9107 // $media.css({ opacity: 0, visibility: 'hidden' }); 9108 $media.children().css({ opacity: 0, visibility: 'hidden' }); // hide media 9109 } 9110 else { 9111 // $media.css({ opacity: opacity, visibility: 'visible' }); 9112 $media.children().css({ opacity: opacity, visibility: 'visible' }); // display media 9113 } 9114 } 9115 9116 9117 // Close the internal lightbox 9118 function CloseInternalViewer( vomIdx ) { 9119 9120 G.VOM.viewerMediaIsChanged = false; 9121 9122 if( G.VOM.viewerDisplayed ) { 9123 9124 // set scrollbar visible again 9125 jQuery('body').css({ overflowX: G.VOM.saveOverflowX, overflowY: G.VOM.saveOverflowY}); 9126 // jQuery('body').css({overflow: 'visible'}); 9127 9128 9129 if( G.VOM.playSlideshow ) { 9130 window.clearTimeout(G.VOM.playSlideshowTimerID); 9131 G.VOM.playSlideshow = false; 9132 } 9133 9134 // G.VOM.userEvents.removeEventListeners(); 9135 // G.VOM.userEvents=null; 9136 G.VOM.hammertime.destroy(); 9137 G.VOM.hammertime = null; 9138 9139 if( ngscreenfull.enabled && G.VOM.viewerIsFullscreen ) { 9140 G.VOM.viewerIsFullscreen = false; 9141 ngscreenfull.exit(); 9142 } 9143 9144 G.VOM.$cont.hide(0).off().show(0).html('').remove(); 9145 G.VOM.viewerDisplayed = false; 9146 9147 if( G.O.thumbnailAlbumDisplayImage ) { 9148 // content of album displayed directly in lightbox (no gallery display for album content) 9149 if( vomIdx == null ) { 9150 // lightbox closed with browser back-button 9151 // the gallery is already displayed 9152 } 9153 else { 9154 var item = G.I[G.VOM.items[vomIdx].ngy2ItemIdx]; 9155 var parent = NGY2Item.Get(G, item.albumID); 9156 if( G.GOM.albumIdx != parent.albumID ) { 9157 // display album only if not already displayed 9158 DisplayAlbum('-1', parent.albumID); 9159 } 9160 else { 9161 GalleryResize(); 9162 SetLocationHash( '', '' ); 9163 ThumbnailHoverReInitAll(); 9164 } 9165 } 9166 // DisplayAlbum( '-', G.I[G.VOM.items[vomIdx].ngy2ItemIdx].albumID ); 9167 } 9168 else { 9169 if( vomIdx != null ) { 9170 if( G.GOM.albumIdx == -1 ) { 9171 // album not displayed --> display gallery 9172 DisplayAlbum( '', G.I[G.VOM.items[vomIdx].ngy2ItemIdx].albumID ); 9173 } 9174 else { 9175 GalleryResize(); 9176 SetLocationHash( G.I[G.VOM.items[vomIdx].ngy2ItemIdx].albumID, '' ); 9177 ThumbnailHoverReInitAll(); 9178 } 9179 } 9180 } 9181 G.VOM.timeImgChanged = new Date().getTime(); 9182 } 9183 } 9184 9185 9186 // Internal viewer resized -> reposition elements 9187 function ResizeInternalViewer( forceUpdate ) { 9188 forceUpdate = typeof forceUpdate !== 'undefined' ? forceUpdate : false; 9189 9190 if( G.VOM.$toolbar === null ) { return; } // viewer build not finished 9191 9192 9193 // window.requestAnimationFrame( function() { // synchronize with screen 9194 var windowsW = G.VOM.$viewer.width(); 9195 var windowsH = G.VOM.$viewer.height(); 9196 var $elt = G.VOM.$mediaCurrent.children().eq(1); 9197 if( $elt == null || G.VOM.currItemIdx == -1 ) { return; } 9198 9199 if( !forceUpdate && G.VOM.window.lastWidth == windowsW && G.VOM.window.lastHeight == windowsH ) { return; } 9200 9201 G.VOM.window.lastWidth = windowsW; 9202 G.VOM.window.lastHeight = windowsH; 9203 9204 // var vwImgC_H=$elt.height(), 9205 // vwImgC_W=$elt.width(), 9206 // vwImgC_OHt=$elt.outerHeight(true), 9207 // vwImgC_OHf=$elt.outerHeight(false); 9208 9209 var $tb = G.VOM.$toolbar.find('.toolbar'); 9210 var tb_OHt = $tb.outerHeight(true); 9211 9212 switch( G.O.viewerToolbar.position ) { 9213 case 'topOverImage': 9214 G.VOM.$content.css({height: windowsH, width: windowsW, top: 0 }); 9215 G.VOM.$toolbar.css({top: 0, bottom: ''}); 9216 break; 9217 case 'top': 9218 windowsH -= tb_OHt; 9219 G.VOM.$content.css({height: windowsH, width: windowsW, top: tb_OHt }); 9220 G.VOM.$toolbar.css({top: 0}); 9221 break; 9222 case 'bottomOverImage': 9223 G.VOM.$content.css({height:windowsH, width: windowsW, bottom: 0, top: 0 }); 9224 G.VOM.$toolbar.css({bottom: 0}); 9225 break; 9226 case 'bottom': 9227 default: 9228 windowsH -= tb_OHt; 9229 G.VOM.$content.css({ width: windowsW, top: 0, bottom: tb_OHt }); 9230 G.VOM.$toolbar.css({bottom: 0}); 9231 break; 9232 } 9233 9234 if( !G.VOM.viewerMediaIsChanged && G.VOM.zoom.isZooming ) { 9235 ViewerMediaSetPosAndZoom(); 9236 } 9237 else { 9238 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 )) { 9239 // animate image zoom factor and position back to initial values 9240 G.VOM.zoom.isZooming= true; // activate zooming temporarily 9241 new NGTweenable().tween({ 9242 from: { userFactor: G.VOM.zoom.userFactor, panPosX: G.VOM.panPosX, panPosY: G.VOM.panPosY, zoomPosX: G.VOM.zoom.posX, zoomPosY: G.VOM.zoom.posY }, 9243 to: { userFactor: 1, panPosX: 0, panPosY: 0, zoomPosX: 0, zoomPosY: 0 }, 9244 easing: 'easeInOutSine', 9245 delay: 0, 9246 duration: 150, 9247 step: function (state) { 9248 G.VOM.zoom.userFactor=state.userFactor; 9249 G.VOM.panPosX=state.panPosX; 9250 G.VOM.panPosY=state.panPosY; 9251 G.VOM.zoom.posX=state.zoomPosX; 9252 G.VOM.zoom.posY=state.zoomPosY; 9253 ViewerMediaSetPosAndZoom(); 9254 }, 9255 finish: function (state) { 9256 G.VOM.zoom.isZooming=false; 9257 } 9258 }); 9259 9260 } 9261 else { 9262 G.VOM.zoom.userFactor = 1; 9263 G.VOM.zoom.isZooming = false; 9264 G.VOM.panPosX = 0; 9265 G.VOM.panPosY = 0; 9266 G.VOM.zoom.posX = 0; 9267 G.VOM.zoom.posY = 0; 9268 ViewerMediaSetPosAndZoom(); 9269 } 9270 } 9271 } 9272 9273 9274 9275 /** @function BuildSkeleton */ 9276 /** Build the gallery structure **/ 9277 function BuildSkeleton() { 9278 9279 // store markup if defined 9280 var $elements = G.$E.base.children('a'); 9281 if( $elements.length > 0 ) { 9282 G.O.$markup = $elements; 9283 } 9284 G.$E.base.text(''); 9285 G.$E.base.addClass('ngy2_container'); 9286 9287 // RTL or LTR 9288 var sRTL=''; 9289 if( G.O.RTL ) { 9290 sRTL = 'style="text-align:right;direction:rtl;"'; 9291 } 9292 9293 // theme 9294 G.$E.base.addClass(G.O.theme) 9295 // gallery color scheme 9296 SetGalleryTheme(); 9297 9298 // Hide icons (thumbnails and breadcrumb) 9299 if( G.O.thumbnailLabel.get('hideIcons') ) { 9300 G.O.icons.thumbnailAlbum = ''; 9301 G.O.icons.thumbnailImage = ''; 9302 } 9303 9304 // Navigation bar 9305 var styleNavigation=""; 9306 if( G.O.navigationFontSize != undefined && G.O.navigationFontSize != '' ) { 9307 styleNavigation=' style="font-size:'+G.O.navigationFontSize+';"'; 9308 } 9309 G.$E.conNavigationBar = jQuery('<div class="nGY2Navigationbar" '+styleNavigation+'></div>').appendTo(G.$E.base); 9310 9311 // pre-loader 9312 G.$E.conLoadingB = jQuery('<div class="nanoGalleryLBarOff"><div></div><div></div><div></div><div></div><div></div></div>').appendTo(G.$E.base); 9313 9314 // gallery 9315 G.$E.conTnParent = jQuery('<div class="nGY2Gallery"></div>').appendTo( G.$E.base ); 9316 G.$E.conTn = jQuery('<div class="nGY2GallerySub"></div>').appendTo( G.$E.conTnParent ); 9317 9318 // configure gallery 9319 switch( G.O.thumbnailAlignment ) { 9320 case 'left': 9321 G.$E.conTnParent.css({'text-align':'left'}); 9322 // G.$E.conNavBCon.css({'margin-left':0 }); 9323 break; 9324 case 'right': 9325 G.$E.conTnParent.css({'text-align':'right'}); 9326 // G.$E.conNavBCon.css({ 'margin-right':0}); 9327 break; 9328 } 9329 9330 // apply galleryBuildInit2 css settings to the gallery 9331 if( G.O.galleryBuildInit2 !== undefined ) { 9332 var t1=G.O.galleryBuildInit2.split('|'); 9333 for( var i=0; i<t1.length; i++ ) { 9334 var o1=t1[i].split('_'); 9335 if( o1.length == 2 ) { 9336 G.$E.conTn.css(o1[0], o1[1]); 9337 } 9338 } 9339 } 9340 9341 // configure gallery depending on some thumbnail hover effects 9342 var effects=G.tn.hoverEffects.std.concat(G.tn.hoverEffects.level1); 9343 for( var j=0; j<effects.length; j++) { 9344 switch( effects[j].type ) { 9345 case 'scale': 9346 case 'rotateZ': 9347 case 'rotateX': 9348 case 'rotateY': 9349 case 'translateX': 9350 case 'translateY': 9351 // handle some special cases 9352 if( effects[j].element == '.nGY2GThumbnail' ) { 9353 // allow thumbnail upscale over the gallery's aera 9354 G.$E.base.css('overflow', 'visible'); 9355 G.$E.base.find('.nGY2GallerySub').css('overflow', 'visible'); 9356 G.$E.conTnParent.css('overflow', 'visible'); 9357 } 9358 break; 9359 } 9360 } 9361 9362 // Gallery bottom container 9363 G.$E.conTnBottom = jQuery('<div class="nGY2GalleryBottom" '+styleNavigation+'></div>').appendTo( G.$E.conTnParent ); 9364 9365 // portable edition 9366 if( G.O.portable ) { 9367 // http://www.picresize.com/ 9368 // http://base64encode.net/base64-image-encoder 9369 // 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'; 9370 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/Ow1lJfX088Hmd2dpZcLvda
9370Bl4pgQChH4B1iHU4a8E6Qj9ARGhpaUFrzeDgIJFIBGMM1lqMMWQyGSYmJohEIqxfv/7314CIoADtGTAaZTTaLI2VUhw+fBjnHBcvXuTy5cs45/A8j3Q6zcmTJ/F9n71799LW1rbgSOs3D+B1lBljcM7R3d3N0aNHKRQKnDhxgs7OTnbt2sX27dsZGRkhHo/T19e3+Kt/fQ1YawFwzolSCs/zUEqVtX1VcJcuXSKRSNDf3086nS6v79mzh76+Pjo6OigWi1RXV2OMWZC29PL8/PxSAL7vE41Gf4rFYkpEePToEb7vU1VVxW+ht7eXs2fPcv/+fQqFAps2baKlpaW8Xl1dTS6XY3x8HBFxtbW1BiiW4hAlInp8fNxt2LChPZvN/ru9vT2Sz+e93bt3qx07diwrzJWYcM5RU1NDNBots5bP53HOlS+kO3fuMDIy4hKJhKTT6ena2tqtxWJxoqamRr98HX9x7do1qaurExYaiXCVzK5bt04GBwdFRP728nVcWZAO+Hsmk/nsxo0bTTMzM5FXHZ83hYhQX1/vHzx48H9tbW1ngSsVvpYCmJ2dJRaLKRbapjpgOxB7K+9LmAbuAnOAnpiYcI2NjUsRLlo2myUMQ1M5t5rmnDO3bt1aNlfmd4W2XL/0/H8pUDF2rNCW/wLRuCkxx8V6wgAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxNy0wOC0wM1QwNDozMjoyMi0wNDowMO7mdkwAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTctMDgtMDNUMDQ6MzI6MjItMDQ6MDCfu87wAAAAAElFTkSuQmCC'; 9371 var st = "font-weight:bold !important;color: #FF0075 !important;font-size: 14px !important;text-transform: lowercase !important;cursor:pointer !important;text-align: center !important;Text-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;"; 9372 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); 9373 9374 G.$E.ngy2i.find('a').on({ 9375 mouseenter: function () { 9376 jQuery(this).attr('style', st); 9377 }, 9378 mouseleave: function () { 9379 jQuery(this).attr('style', st); 9380 } 9381 }); 9382 } 9383 9384 // Error console 9385 G.$E.conConsole = jQuery('<div class="nGY2ConsoleParent"></div>').appendTo(G.$E.base); 9386 9387 // i18n translations 9388 i18n(); 9389 9390 // cache some thumbnails data (sizes, styles...) 9391 ThumbnailDefCaches(); 9392 9393 // do special settings depending for some options 9394 // thumbnail display transition 9395 switch( G.tn.opt.Get('displayTransition') ) { 9396 case 'SCALEDOWN': 9397 case 'RANDOMSCALE': 9398 default: 9399 G.$E.base.css('overflow', 'visible'); 9400 G.$E.conTnParent.css('overflow', 'visible'); 9401 G.$E.conTn.css('overflow', 'visible'); 9402 break; 9403 } 9404 9405 } 9406 9407 function TriggerCustomEvent ( eventName ) { 9408 // G.$E.base.trigger('pageChanged.nanogallery2', new Event('pageChanged.nanogallery2')); 9409 var eN = eventName + '.nanogallery2'; 9410 var event=null; 9411 try { 9412 event = new Event( eN ); 9413 } catch(e) { 9414 event = document.createEvent('Event'); 9415 event.initEvent(eN, false, false); 9416 } 9417 G.$E.base.trigger(eN, event); 9418 } 9419 9420 9421 /** @function SetGlobalEvents */ 9422 function SetGlobalEvents() { 9423 // GLOBAL EVENT MANAGEMENT 9424 9425 G.$E.conTnParent.on({ 9426 mouseenter: GalleryMouseEnter, 9427 mouseleave: GalleryMouseLeave 9428 }, ".nGY2GThumbnail"); //pass the element as an argument to .on 9429
vendor: 9,844 bytes, lines 9430-9688
9430 // G.GOM.hammertime = new NGHammer(G.$E.conTn[0], { touchAction: 'none' }); 9431 G.GOM.hammertime = new NGHammer( G.$E.conTn[0] ); 9432 // G.GOM.hammertime.domEvents = true; 9433 9434 G.GOM.hammertime.on('pan', function(ev) { 9435 if( !G.VOM.viewerDisplayed ) { 9436 if( G.O.paginationSwipe && G.layout.support.rows && G.galleryDisplayMode.Get() == 'PAGINATION' ) { 9437 if( Math.abs(ev.deltaY) > G.GOM.panThreshold ) { 9438 G.GOM.panYOnly = true; 9439 } 9440 if( !G.GOM.panYOnly ) { 9441 G.$E.conTn.css( G.CSStransformName , 'translate('+(ev.deltaX)+'px,0px)'); 9442 } 9443 } 9444 } 9445 }); 9446 G.GOM.hammertime.on('panend', function(ev) { 9447 if( !G.VOM.viewerDisplayed ) { 9448 if( G.O.paginationSwipe && G.layout.support.rows && G.galleryDisplayMode.Get() == 'PAGINATION' ) { 9449 if( !G.GOM.panYOnly ) { 9450 if( ev.deltaX > 50 ) { 9451 paginationPreviousPage(); 9452 return; 9453 } 9454 if( ev.deltaX < -50 ) { 9455 paginationNextPage(); 9456 return; 9457 } 9458 } 9459 G.GOM.panYOnly = false; 9460 G.$E.conTn.css( G.CSStransformName , 'translate(0px,0px)'); 9461 // pX=0; 9462 } 9463 } 9464 }); 9465 G.GOM.hammertime.on('tap', function(ev) { 9466 if( !G.VOM.viewerDisplayed ) { 9467 ev.srcEvent.stopPropagation(); 9468 ev.srcEvent.preventDefault(); // cancel mouseenter event 9469 9470 if( ev.pointerType == 'mouse') { 9471 if( GalleryClicked(ev.srcEvent) == 'exit' ) { return; } 9472 } 9473 else { 9474 var r = GalleryEventRetrieveElementl(ev.srcEvent, false); 9475 if( r.GOMidx == -1 ) { return; } 9476 if( r.action != 'NONE' && r.action != 'OPEN' ) { 9477 // toolbar touched --> execute action 9478 GalleryClicked(ev.srcEvent); 9479 return; 9480 } 9481 9482 if( G.GOM.slider.hostIdx == r.GOMidx ) { 9483 // touch on thumbnail slider -> open immediately 9484 ThumbnailHoverOutAll(); 9485 ThumbnailOpen(G.GOM.items[G.GOM.slider.currentIdx].thumbnailIdx, true); 9486 return; 9487 } 9488 9489 if( (G.GOM.curNavLevel == 'l1' && G.O.touchAnimationL1 == false) || (G.GOM.curNavLevel == 'lN' && G.O.touchAnimation == false) ) { 9490 // open on single touch (no hover animation) 9491 ThumbnailOpen(G.GOM.items[r.GOMidx].thumbnailIdx, true); 9492 return; 9493 } 9494 9495 if( G.O.touchAutoOpenDelay > 0 ) { 9496 // open on single touch after end of hover animation (=defined delay) 9497 ThumbnailHoverOutAll(); 9498 ThumbnailHover( r.GOMidx ); 9499 window.clearInterval( G.touchAutoOpenDelayTimerID ); 9500 G.touchAutoOpenDelayTimerID = window.setInterval(function(){ 9501 window.clearInterval( G.touchAutoOpenDelayTimerID ); 9502 ThumbnailOpen( G.GOM.items[r.GOMidx].thumbnailIdx, true ); 9503 }, G.O.touchAutoOpenDelay ); 9504 } 9505 else { 9506 // two touch scenario 9507 if( !G.I[G.GOM.items[r.GOMidx].thumbnailIdx].hovered ) { 9508 ThumbnailHoverOutAll(); 9509 ThumbnailHover(r.GOMidx); 9510 } 9511 else { 9512 // second touch 9513 ThumbnailOpen(G.GOM.items[r.GOMidx].thumbnailIdx, true); 9514 } 9515 } 9516 } 9517 } 9518 }); 9519 9520 9521 // browser location hash management 9522 if( G.O.locationHash ) { 9523 // jQuery(window).bind( 'hashchange', function() { 9524 // ProcessLocationHash(); 9525 // }); 9526 jQuery(window).on('hashchange.nanogallery2.' + G.baseEltID, function() {ProcessLocationHash();} ); 9527 } 9528 9529 // Page resize / orientation change 9530 jQuery(window).on('resize.nanogallery2.' + G.baseEltID + ' orientationChange.nanogallery2.' + G.baseEltID, debounce( ResizeWindowEvent, G.O.eventsDebounceDelay, false) ); 9531 9532 // Event page scrolled 9533 jQuery(window).on('scroll.nanogallery2.' + G.baseEltID, debounce( OnScrollEvent, G.O.eventsDebounceDelay, false) ); 9534 9535 // Debounced function to hide the toolbars on the viewer 9536 G.VOM.toolsHide = debounce( ViewerToolsHide, G.O.viewerHideToolsDelay, false ); 9537 9538 // Keyboard management 9539 jQuery(document).keyup(function(e) { 9540 if( G.popup.isDisplayed ) { 9541 switch( e.keyCode) { 9542 case 27: // Esc key 9543 G.popup.close(); 9544 break; 9545 } 9546 } 9547 else { 9548 if( G.VOM.viewerDisplayed ) { 9549 ViewerToolsUnHide(); 9550 switch( e.keyCode) { 9551 case 27: // Escape key 9552 case 40: // DOWN 9553 CloseInternalViewer(G.VOM.currItemIdx); 9554 break; 9555 case 32: // SPACE 9556 case 13: // ENTER 9557 SlideshowToggle(); 9558 break; 9559 case 38: // UP 9560 case 39: // RIGHT 9561 case 33: // PAGE UP 9562 DisplayNextMedia(); 9563 break; 9564 case 37: // LEFT 9565 case 34: // PAGE DOWN 9566 DisplayPreviousMedia(); 9567 break; 9568 case 35: // END 9569 case 36: // BEGIN 9570 } 9571 } 9572 } 9573 }); 9574 9575 // mouse wheel to zoom in/out the image displayed in the internal lightbox 9576 jQuery(window).bind('mousewheel wheel', function(e){ 9577 if( G.VOM.viewerDisplayed ) { 9578 var deltaY = 0; 9579 e.preventDefault(); 9580 9581 if( ViewerZoomStart() ) { 9582 if (e.originalEvent.deltaY) { // FireFox 17+ (IE9+, Chrome 31+?) 9583 deltaY = e.originalEvent.deltaY; 9584 } else if (e.originalEvent.wheelDelta) { 9585 deltaY = -e.originalEvent.wheelDelta; 9586 } 9587 ViewerZoomIn( deltaY <= 0 ? true : false ); 9588 } 9589 } 9590 }); 9591 9592 // mouse mouse -> unhide lightbox toolbars 9593 jQuery(window).bind('mousemove', function(e){ 9594 if( G.VOM.viewerDisplayed ) { 9595 debounce( ViewerToolsUnHide, 100, false )(); 9596 } 9597 }); 9598 9599 // fullscreen mode on/off --> internal lightbox 9600 if( ngscreenfull.enabled ) { 9601 // ngscreenfull.onchange(() => { 9602 ngscreenfull.onchange( function() { 9603 if( G.VOM.viewerDisplayed ) { 9604 if( ngscreenfull.isFullscreen ) { 9605 G.VOM.viewerIsFullscreen=true; 9606 G.VOM.$viewer.find('.fullscreenButton').html(G.O.icons.viewerFullscreenOff); 9607 } 9608 else { 9609 G.VOM.viewerIsFullscreen=false; 9610 G.VOM.$viewer.find('.fullscreenButton').html(G.O.icons.viewerFullscreenOn); 9611 } 9612 } 9613 }); 9614 } 9615 9616 } 9617 9618 //----- Manage browser location hash (deep linking and browser back/forward) 9619 function ProcessLocationHash() { 9620 9621 // standard use case -> location hash processing 9622 if( !G.O.locationHash ) { return false; } 9623 9624 var curGal = '#nanogallery/' + G.baseEltID + '/', 9625 newLocationHash = location.hash; 9626 if( G.O.debugMode ) { 9627 console.log('------------------------ PROCESS LOCATION HASH'); 9628 console.log('newLocationHash1: ' +newLocationHash); 9629 console.log('G.locationHashLastUsed: ' +G.locationHashLastUsed); 9630 } 9631 9632 if( newLocationHash == '' ) { 9633 // if( G.GOM.lastDisplayedIdx != -1 ) { 9634 if( G.locationHashLastUsed !== '' ) { 9635 // back button and no hash --> display first album 9636 if( G.O.debugMode ) { console.log('display root album' ); } 9637 G.locationHashLastUsed = ''; 9638 if( G.O.debugMode ) { console.log('new3 G.locationHashLastUsed: ' + G.locationHashLastUsed); } 9639 DisplayAlbum('', '0'); 9640 return true; 9641 } 9642 } 9643 9644 if( newLocationHash == G.locationHashLastUsed ) { return; } 9645 9646 if( newLocationHash.indexOf(curGal) == 0 ) { 9647 // item IDs detected 9648 var IDs=parseIDs( newLocationHash.substring(curGal.length) ); 9649 if( IDs.imageID != '0' ) { 9650 if( G.O.debugMode ) { console.log('display image: ' + IDs.albumID +'-'+ IDs.imageID ); } 9651 DisplayPhoto( IDs.imageID, IDs.albumID ); 9652 return true; 9653 } 9654 else { 9655 if( G.O.debugMode ) { console.log('display album: ' + IDs.albumID ); } 9656 DisplayAlbum( '-1', IDs.albumID ); 9657 return true; 9658 } 9659 } 9660 9661 return false; 9662 } 9663 9664 //---- Set a new browser location hash 9665 function SetLocationHash(albumID, imageID ) { 9666 if( !G.O.locationHash ) { return false; } 9667 9668 if( G.O.debugMode ) { 9669 console.log('------------------------ SET LOCATION HASH'); 9670 } 9671 9672 if( imageID == '' && (albumID == '-1' || albumID == '0' || G.O.album == albumID ) ) { 9673 // root album level --> do not set top.location.hash if not already set 9674 if( location.hash != '' ) { 9675 // try to clear the hash if set 9676 if ("pushState" in history) { 9677 history.pushState("", document.title, window.location.pathname + window.location.search); 9678 } 9679 else { 9680 location.hash=''; 9681 } 9682 } 9683 G.locationHashLastUsed=''; 9684 if( G.O.debugMode ) { console.log('new2 G.locationHashLastUsed: '+G.locationHashLastUsed); } 9685 return; 9686 } 9687 9688 var newLocationHash='#'+'nanogallery/'+G.baseEltID+'/'+ albumID;
9689 if( imageID != '' ) { 9690 newLocationHash+='/'+imageID; 9691 } 9692 9693 var lH=location.hash; 9694 if( G.O.debugMode ) { 9695 console.log('newLocationHash2: '+newLocationHash); 9696 console.log('location.hash: '+lH); 9697 } 9698 9699 G.locationHashLastUsed=newLocationHash; 9700 if( G.O.debugMode ) { console.log('new G.locationHashLastUsed: '+G.locationHashLastUsed); } 9701 9702 if( lH == '' || lH != newLocationHash ) { 9703 // G.locationHashLastUsed='#'+newLocationHash; 9704 try { 9705 top.location.hash=newLocationHash; 9706 } 9707 catch(e) { 9708 // location hash is not supported by current browser --> disable the option 9709 G.O.locationHash=false; 9710 } 9711 } 9712 } 9713 9714 9715 function ResizeWindowEvent() { 9716 CacheViewport(); 9717 9718 if( G.VOM.viewerDisplayed ) { 9719 ResizeInternalViewer(); 9720 } 9721 else { 9722 if( G.galleryResizeEventEnabled ) { 9723 var nw = RetrieveCurWidth(); 9724 if( G.GOM.albumIdx != -1 && 9725 ( G.tn.settings.height[G.GOM.curNavLevel][G.GOM.curWidth] != G.tn.settings.height[G.GOM.curNavLevel][nw] || 9726 G.tn.settings.width[G.GOM.curNavLevel][G.GOM.curWidth] != G.tn.settings.width[G.GOM.curNavLevel][nw] ) ) { 9727 // do not use settings.getH() / settings.getW() 9728 // thumbnail size changed --> render the gallery with the new sizes 9729 G.GOM.curWidth = nw; 9730 //G.layout.SetEngine(); 9731 G.GOM.pagination.currentPage = 0; 9732 GalleryRender( G.GOM.albumIdx ); 9733 } 9734 else { 9735 GalleryResize(); 9736 } 9737 } 9738 } 9739 } 9740 9741 9742 // Depreciated - if gallery currently refreshed (G.galleryResizeEventEnabled=false), page may be scrolled but it will not be refreshed again 9743 function OnScrollEvent_OLD() { 9744 if( !G.VOM.viewerDisplayed ) { 9745 if( G.galleryResizeEventEnabled ) { 9746 GalleryResize(); 9747 } 9748 return; 9749 } 9750 } 9751 9752 9753 function OnScrollEvent() { 9754 if( !G.VOM.viewerDisplayed ) { 9755 GalleryResizeOnScrollEvent(); 9756 } 9757 }
vendor: 17,549 bytes, lines 9757-10364
9757 9758 // 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 9759 function GalleryResizeOnScrollEvent() { 9760 if( G.galleryResizeEventEnabled == false) { 9761 window.setTimeout(GalleryResizeOnScrollEvent, 10); // check again in 10ms 9762 } else { 9763 GalleryResize(); 9764 } 9765 } 9766 9767 9768 9769 // I18N : define text translations 9770 function i18n() { 9771 9772 // browser language 9773 G.i18nLang = (navigator.language || navigator.userLanguage).toUpperCase(); 9774 if( G.i18nLang === 'UNDEFINED') { G.i18nLang=''; } 9775 9776 var llang=-('_'+G.i18nLang).length; 9777 9778 if( toType(G.O.i18n) == 'object' ){ 9779 9780 for( var key in G.O.i18n ) { 9781 //var value = G.O.i18n[key]; 9782 var s=key.substr(llang); 9783 if( s == ('_'+G.i18nLang) ) { 9784 G.i18nTranslations[key.substr(0,key.length-s.length)]=G.O.i18n[key]; 9785 } 9786 else { 9787 G.i18nTranslations[key]=G.O.i18n[key]; 9788 } 9789 } 9790 } 9791 } 9792 9793 function GetI18nItem( item, property ) { 9794 var s=''; 9795 if( G.i18nLang != '' ) { 9796 if( item[property+'_'+G.i18nLang] !== undefined && item[property+'_'+G.i18nLang].length>0 ) { 9797 s=item[property+'_'+G.i18nLang]; 9798 return s; 9799 } 9800 } 9801 s=item[property]; 9802 return s; 9803 } 9804 9805 9806 function RetrieveCurWidth() { 9807 var vpW = G.GOM.cache.viewport.w; 9808 9809 if( G.O.breakpointSizeSM > 0 && vpW < G.O.breakpointSizeSM) { return 'xs'; } 9810 if( G.O.breakpointSizeME > 0 && vpW < G.O.breakpointSizeME) { return 'sm'; } 9811 if( G.O.breakpointSizeLA > 0 && vpW < G.O.breakpointSizeLA) { return 'me'; } 9812 if( G.O.breakpointSizeXL > 0 && vpW < G.O.breakpointSizeXL) { return 'la'; } 9813 9814 return 'xl'; 9815 } 9816 9817 9818 /** @function browserNotification */ 9819 function browserNotification() { 9820 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>'; 9821 m += ' <a href="http://www.google.com/chrome/?hl=en-US)">Chrome</a><br>'; 9822 m += ' <a href="http://www.mozilla.com/firefox/)">Firefox</a><br>'; 9823 m += ' <a href="http://www.microsoft.com/windows/internet-explorer/default.aspx">Internet Explorer</a><br>'; 9824 m += ' <a href="http://www.apple.com/safari/download/">Safari</a>'; 9825 NanoAlert(G, m, false); 9826 } 9827 9828 // 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 9829 function FirstSupportedPropertyName(prefixedPropertyNames) { 9830 var tempDiv = document.createElement("div"); 9831 for (var i = 0; i < prefixedPropertyNames.length; ++i) { 9832 if (typeof tempDiv.style[prefixedPropertyNames[i]] != 'undefined') 9833 return prefixedPropertyNames[i]; 9834 } 9835 return null; 9836 } 9837 9838 9839 9840 9841 } 9842 9843 9844 9845//########################################################################################################################## 9846//## imagesLoaded ########################################################################################################## 9847//########################################################################################################################## 9848 9849// external module EMBEDED in nanogallery 9850// NGY BUILD: 9851// replace "imagesLoaded" with "ngimagesLoaded" 9852// replace "ImagesLoaded" with "ngImagesLoaded" 9853// replace "EvEmitter" with "ngEvEmitter" 9854// replace "var $ = window.jQuery" with "var $ = jQuery;" 9855// 2x (global.ngEvEmitter and window.ngimagesLoaded = f...)ignore package manager and set browser global 9856 9857 9858 9859/*! 9860 * imagesLoaded PACKAGED v4.1.1 9861 * JavaScript is all like "You images are done yet or what?" 9862 * MIT License 9863 */ 9864 9865/** 9866 * EvEmitter v1.0.3 9867 * Lil' event emitter 9868 * MIT License 9869 */ 9870 9871 9872/* jshint unused: true, undef: true, strict: true */ 9873 9874( function( global, factory ) { 9875 // universal module definition 9876 /* jshint strict: false */ /* globals define, module, window */ 9877// if ( typeof define == 'function' && define.amd ) { 9878 // AMD - RequireJS 9879// define( 'ev-emitter/ev-emitter',factory ); 9880// } else if ( typeof module == 'object' && module.exports ) { 9881 // CommonJS - Browserify, Webpack 9882// module.exports = factory(); 9883// } else { 9884 // Browser globals 9885 global.ngEvEmitter = factory(); 9886// } 9887 9888}( typeof window != 'undefined' ? window : this, function() { 9889 9890 9891 9892function ngEvEmitter() {} 9893 9894var proto = ngEvEmitter.prototype; 9895 9896proto.on = function( eventName, listener ) { 9897 if ( !eventName || !listener ) { 9898 return; 9899 } 9900 // set events hash 9901 var events = this._events = this._events || {}; 9902 // set listeners array 9903 var listeners = events[ eventName ] = events[ eventName ] || []; 9904 // only add once 9905 if ( listeners.indexOf( listener ) == -1 ) { 9906 listeners.push( listener ); 9907 } 9908 9909 return this; 9910}; 9911 9912proto.once = function( eventName, listener ) { 9913 if ( !eventName || !listener ) { 9914 return; 9915 } 9916 // add event 9917 this.on( eventName, listener ); 9918 // set once flag 9919 // set onceEvents hash 9920 var onceEvents = this._onceEvents = this._onceEvents || {}; 9921 // set onceListeners object 9922 var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {}; 9923 // set flag 9924 onceListeners[ listener ] = true; 9925 9926 return this; 9927}; 9928 9929proto.off = function( eventName, listener ) { 9930 var listeners = this._events && this._events[ eventName ]; 9931 if ( !listeners || !listeners.length ) { 9932 return; 9933 } 9934 var index = listeners.indexOf( listener ); 9935 if ( index != -1 ) { 9936 listeners.splice( index, 1 ); 9937 } 9938 9939 return this; 9940}; 9941 9942proto.emitEvent = function( eventName, args ) { 9943 var listeners = this._events && this._events[ eventName ]; 9944 if ( !listeners || !listeners.length ) { 9945 return; 9946 } 9947 var i = 0; 9948 var listener = listeners[i]; 9949 args = args || []; 9950 // once stuff 9951 var onceListeners = this._onceEvents && this._onceEvents[ eventName ]; 9952 9953 while ( listener ) { 9954 var isOnce = onceListeners && onceListeners[ listener ]; 9955 if ( isOnce ) { 9956 // remove listener 9957 // remove before trigger to prevent recursion 9958 this.off( eventName, listener ); 9959 // unset once flag 9960 delete onceListeners[ listener ]; 9961 } 9962 // trigger listener 9963 listener.apply( this, args ); 9964 // get next listener 9965 i += isOnce ? 0 : 1; 9966 listener = listeners[i]; 9967 } 9968 9969 return this; 9970}; 9971 9972return ngEvEmitter; 9973 9974})); 9975 9976/*! 9977 * ngimagesLoaded v4.1.1 9978 * JavaScript is all like "You images are done yet or what?" 9979 * MIT License 9980 */ 9981 9982( function( window, factory ) { 'use strict'; 9983 // universal module definition 9984 9985 /*global define: false, module: false, require: false */ 9986 9987// if ( typeof define == 'function' && define.amd ) { 9988 // AMD 9989// define( [ 9990// 'ev-emitter/ev-emitter' 9991// ], function( ngEvEmitter ) { 9992// return factory( window, ngEvEmitter ); 9993// }); 9994// } else if ( typeof module == 'object' && module.exports ) { 9995 // CommonJS 9996// module.exports = factory( 9997// window, 9998// require('ev-emitter') 9999// ); 10000// } else { 10001 // browser global 10002 window.ngimagesLoaded = factory( 10003 window, 10004 window.ngEvEmitter 10005 ); 10006 //} 10007 10008})( window, 10009 10010// -------------------------- factory -------------------------- // 10011 10012function factory( window, ngEvEmitter ) { 10013 10014 10015 10016// var $ = window.jQuery; 10017var $ = jQuery; 10018var console = window.console; 10019 10020// -------------------------- helpers -------------------------- // 10021 10022// extend objects 10023function extend( a, b ) { 10024 for ( var prop in b ) { 10025 a[ prop ] = b[ prop ]; 10026 } 10027 return a; 10028} 10029 10030// turn element or nodeList into an array 10031function makeArray( obj ) { 10032 var ary = []; 10033 if ( Array.isArray( obj ) ) { 10034 // use object if already an array 10035 ary = obj; 10036 } else if ( typeof obj.length == 'number' ) { 10037 // convert nodeList to array 10038 for ( var i=0; i < obj.length; i++ ) { 10039 ary.push( obj[i] ); 10040 } 10041 } else { 10042 // array of single index 10043 ary.push( obj ); 10044 } 10045 return ary; 10046} 10047 10048// -------------------------- ngimagesLoaded -------------------------- // 10049 10050/** 10051 * @param {Array, Element, NodeList, String} elem 10052 * @param {Object or Function} options - if function, use as callback 10053 * @param {Function} onAlways - callback function 10054 */ 10055function ngImagesLoaded( elem, options, onAlways ) { 10056 // coerce ngImagesLoaded() without new, to be new ngImagesLoaded() 10057 if ( !( this instanceof ngImagesLoaded ) ) { 10058 return new ngImagesLoaded( elem, options, onAlways ); 10059 } 10060 // use elem as selector string 10061 if ( typeof elem == 'string' ) { 10062 elem = document.querySelectorAll( elem ); 10063 } 10064 10065 this.elements = makeArray( elem ); 10066 this.options = extend( {}, this.options ); 10067 10068 if ( typeof options == 'function' ) { 10069 onAlways = options; 10070 } else { 10071 extend( this.options, options ); 10072 } 10073 10074 if ( onAlways ) { 10075 this.on( 'always', onAlways ); 10076 } 10077 10078 this.getImages(); 10079 10080 if ( $ ) { 10081 // add jQuery Deferred object 10082 this.jqDeferred = new $.Deferred(); 10083 } 10084 10085 // HACK check async to allow time to bind listeners 10086 setTimeout( function() { 10087 this.check(); 10088 }.bind( this )); 10089} 10090 10091ngImagesLoaded.prototype = Object.create( ngEvEmitter.prototype ); 10092 10093ngImagesLoaded.prototype.options = {}; 10094 10095ngImagesLoaded.prototype.getImages = function() { 10096 this.images = []; 10097 10098 // filter & find items if we have an item selector 10099 this.elements.forEach( this.addElementImages, this ); 10100}; 10101 10102/** 10103 * @param {Node} element 10104 */ 10105ngImagesLoaded.prototype.addElementImages = function( elem ) { 10106 // filter siblings 10107 if ( elem.nodeName == 'IMG' ) { 10108 this.addImage( elem ); 10109 } 10110 // get background image on element 10111 if ( this.options.background === true ) { 10112 this.addElementBackgroundImages( elem ); 10113 } 10114 10115 // find children 10116 // no non-element nodes, #143 10117 var nodeType = elem.nodeType; 10118 if ( !nodeType || !elementNodeTypes[ nodeType ] ) { 10119 return; 10120 } 10121 var childImgs = elem.querySelectorAll('img'); 10122 // concat childElems to filterFound array 10123 for ( var i=0; i < childImgs.length; i++ ) { 10124 var img = childImgs[i]; 10125 this.addImage( img ); 10126 } 10127 10128 // get child background images 10129 if ( typeof this.options.background == 'string' ) { 10130 var children = elem.querySelectorAll( this.options.background ); 10131 for ( i=0; i < children.length; i++ ) { 10132 var child = children[i]; 10133 this.addElementBackgroundImages( child ); 10134 } 10135 } 10136}; 10137 10138var elementNodeTypes = { 10139 1: true, 10140 9: true, 10141 11: true 10142}; 10143 10144ngImagesLoaded.prototype.addElementBackgroundImages = function( elem ) { 10145 var style = getComputedStyle( elem ); 10146 if ( !style ) { 10147 // Firefox returns null if in a hidden iframe https://bugzil.la/548397 10148 return; 10149 } 10150 // get url inside url("...") 10151 var reURL = /url\((['"])?(.*?)\1\)/gi; 10152 var matches = reURL.exec( style.backgroundImage ); 10153 while ( matches !== null ) { 10154 var url = matches && matches[2]; 10155 if ( url ) { 10156 this.addBackground( url, elem ); 10157 } 10158 matches = reURL.exec( style.backgroundImage ); 10159 } 10160}; 10161 10162/** 10163 * @param {Image} img 10164 */ 10165ngImagesLoaded.prototype.addImage = function( img ) { 10166 var loadingImage = new LoadingImage( img ); 10167 this.images.push( loadingImage ); 10168}; 10169 10170ngImagesLoaded.prototype.addBackground = function( url, elem ) { 10171 var background = new Background( url, elem ); 10172 this.images.push( background ); 10173}; 10174 10175ngImagesLoaded.prototype.check = function() { 10176 var _this = this; 10177 this.progressedCount = 0; 10178 this.hasAnyBroken = false; 10179 // complete if no images 10180 if ( !this.images.length ) { 10181 this.complete(); 10182 return; 10183 } 10184 10185 function onProgress( image, elem, message ) { 10186 // HACK - Chrome triggers event before object properties have changed. #83 10187 setTimeout( function() { 10188 _this.progress( image, elem, message ); 10189 }); 10190 } 10191 10192 this.images.forEach( function( loadingImage ) { 10193 loadingImage.once( 'progress', onProgress ); 10194 loadingImage.check(); 10195 }); 10196}; 10197 10198ngImagesLoaded.prototype.progress = function( image, elem, message ) { 10199 this.progressedCount++; 10200 this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded; 10201 // progress event 10202 this.emitEvent( 'progress', [ this, image, elem ] ); 10203 if ( this.jqDeferred && this.jqDeferred.notify ) { 10204 this.jqDeferred.notify( this, image ); 10205 } 10206 // check if completed 10207 if ( this.progressedCount == this.images.length ) { 10208 this.complete(); 10209 } 10210 10211 if ( this.options.debug && console ) { 10212 console.log( 'progress: ' + message, image, elem ); 10213 } 10214}; 10215 10216ngImagesLoaded.prototype.complete = function() { 10217 var eventName = this.hasAnyBroken ? 'fail' : 'done'; 10218 this.isComplete = true; 10219 this.emitEvent( eventName, [ this ] ); 10220 this.emitEvent( 'always', [ this ] ); 10221 if ( this.jqDeferred ) { 10222 var jqMethod = this.hasAnyBroken ? 'reject' : 'resolve'; 10223 this.jqDeferred[ jqMethod ]( this ); 10224 } 10225}; 10226 10227// -------------------------- -------------------------- // 10228 10229function LoadingImage( img ) { 10230 this.img = img; 10231} 10232 10233LoadingImage.prototype = Object.create( ngEvEmitter.prototype ); 10234 10235LoadingImage.prototype.check = function() { 10236 // If complete is true and browser supports natural sizes, 10237 // try to check for image status manually. 10238 var isComplete = this.getIsImageComplete(); 10239 if ( isComplete ) { 10240 // report based on naturalWidth 10241 this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' ); 10242 return; 10243 } 10244 10245 // If none of the checks above matched, simulate loading on detached element. 10246 this.proxyImage = new Image(); 10247 this.proxyImage.addEventListener( 'load', this ); 10248 this.proxyImage.addEventListener( 'error', this ); 10249 // bind to image as well for Firefox. #191 10250 this.img.addEventListener( 'load', this ); 10251 this.img.addEventListener( 'error', this ); 10252 this.proxyImage.src = this.img.src; 10253}; 10254 10255LoadingImage.prototype.getIsImageComplete = function() { 10256 return this.img.complete && this.img.naturalWidth !== undefined; 10257}; 10258 10259LoadingImage.prototype.confirm = function( isLoaded, message ) { 10260 this.isLoaded = isLoaded; 10261 this.emitEvent( 'progress', [ this, this.img, message ] ); 10262}; 10263 10264// ----- events ----- // 10265 10266// trigger specified handler for event type 10267LoadingImage.prototype.handleEvent = function( event ) { 10268 var method = 'on' + event.type; 10269 if ( this[ method ] ) { 10270 this[ method ]( event ); 10271 } 10272}; 10273 10274LoadingImage.prototype.onload = function() { 10275 this.confirm( true, 'onload' ); 10276 this.unbindEvents(); 10277}; 10278 10279LoadingImage.prototype.onerror = function() { 10280 this.confirm( false, 'onerror' ); 10281 this.unbindEvents(); 10282}; 10283 10284LoadingImage.prototype.unbindEvents = function() { 10285 this.proxyImage.removeEventListener( 'load', this ); 10286 this.proxyImage.removeEventListener( 'error', this ); 10287 this.img.removeEventListener( 'load', this ); 10288 this.img.removeEventListener( 'error', this ); 10289}; 10290 10291// -------------------------- Background -------------------------- // 10292 10293function Background( url, element ) { 10294 this.url = url; 10295 this.element = element; 10296 this.img = new Image(); 10297} 10298 10299// inherit LoadingImage prototype 10300Background.prototype = Object.create( LoadingImage.prototype ); 10301 10302Background.prototype.check = function() { 10303 this.img.addEventListener( 'load', this ); 10304 this.img.addEventListener( 'error', this ); 10305 this.img.src = this.url; 10306 // check if image is already complete 10307 var isComplete = this.getIsImageComplete(); 10308 if ( isComplete ) { 10309 this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' ); 10310 this.unbindEvents(); 10311 } 10312}; 10313 10314Background.prototype.unbindEvents = function() { 10315 this.img.removeEventListener( 'load', this ); 10316 this.img.removeEventListener( 'error', this ); 10317}; 10318 10319Background.prototype.confirm = function( isLoaded, message ) { 10320 this.isLoaded = isLoaded; 10321 this.emitEvent( 'progress', [ this, this.element, message ] ); 10322}; 10323 10324// -------------------------- jQuery -------------------------- // 10325 10326ngImagesLoaded.makeJQueryPlugin = function( jQuery ) { 10327 jQuery = jQuery || window.jQuery; 10328 if ( !jQuery ) { 10329 return; 10330 } 10331 // set local variable 10332 $ = jQuery; 10333 // $().ngimagesLoaded() 10334 $.fn.ngimagesLoaded = function( options, callback ) { 10335 var instance = new ngImagesLoaded( this, options, callback ); 10336 return instance.jqDeferred.promise( $(this) ); 10337 }; 10338}; 10339// try making plugin 10340ngImagesLoaded.makeJQueryPlugin(); 10341 10342// -------------------------- -------------------------- // 10343 10344return ngImagesLoaded; 10345 10346}); 10347 10348 10349 10350//########################################################################################################################## 10351//## screenfull.js ######################################################################################################### 10352//########################################################################################################################## 10353 10354// screenfull.js 10355// v4.0.1 10356// by sindresorhus - https://github.com/sindresorhus 10357// from: https://github.com/sindresorhus/screenfull.js 10358 10359// external module embeded in nanogallery 10360// NGY BUILD: 10361// replace "screenfull" with "ngscreenfull" 10362// 10363 10364(function () {
vendor: 5,376 bytes, lines 10365-10565
10365 'use strict'; 10366 10367 var document = typeof window !== 'undefined' && typeof window.document !== 'undefined' ? window.document : {}; 10368 var isCommonjs = typeof module !== 'undefined' && module.exports; 10369 var keyboardAllowed = typeof Element !== 'undefined' && 'ALLOW_KEYBOARD_INPUT' in Element; 10370 10371 var fn = (function () { 10372 var val; 10373 10374 var fnMap = [ 10375 [ 10376 'requestFullscreen', 10377 'exitFullscreen', 10378 'fullscreenElement', 10379 'fullscreenEnabled', 10380 'fullscreenchange', 10381 'fullscreenerror' 10382 ], 10383 // New WebKit 10384 [ 10385 'webkitRequestFullscreen', 10386 'webkitExitFullscreen', 10387 'webkitFullscreenElement', 10388 'webkitFullscreenEnabled', 10389 'webkitfullscreenchange', 10390 'webkitfullscreenerror' 10391 10392 ], 10393 // Old WebKit (Safari 5.1) 10394 [ 10395 'webkitRequestFullScreen', 10396 'webkitCancelFullScreen', 10397 'webkitCurrentFullScreenElement', 10398 'webkitCancelFullScreen', 10399 'webkitfullscreenchange', 10400 'webkitfullscreenerror' 10401 10402 ], 10403 [ 10404 'mozRequestFullScreen', 10405 'mozCancelFullScreen', 10406 'mozFullScreenElement', 10407 'mozFullScreenEnabled', 10408 'mozfullscreenchange', 10409 'mozfullscreenerror' 10410 ], 10411 [ 10412 'msRequestFullscreen', 10413 'msExitFullscreen', 10414 'msFullscreenElement', 10415 'msFullscreenEnabled', 10416 'MSFullscreenChange', 10417 'MSFullscreenError' 10418 ] 10419 ]; 10420 10421 var i = 0; 10422 var l = fnMap.length; 10423 var ret = {}; 10424 10425 for (; i < l; i++) { 10426 val = fnMap[i]; 10427 if (val && val[1] in document) { 10428 for (i = 0; i < val.length; i++) { 10429 ret[fnMap[0][i]] = val[i]; 10430 } 10431 return ret; 10432 } 10433 } 10434 10435 return false; 10436 })(); 10437 10438 var eventNameMap = { 10439 change: fn.fullscreenchange, 10440 error: fn.fullscreenerror 10441 }; 10442 10443 var ngscreenfull = { 10444 request: function (elem) { 10445 return new Promise(function (resolve) { 10446 var request = fn.requestFullscreen; 10447 10448 var onFullScreenEntered = function () { 10449 this.off('change', onFullScreenEntered); 10450 resolve(); 10451 }.bind(this); 10452 10453 elem = elem || document.documentElement; 10454 10455 // Work around Safari 5.1 bug: reports support for 10456 // keyboard in fullscreen even though it doesn't. 10457 // Browser sniffing, since the alternative with 10458 // setTimeout is even worse. 10459 if (/ Version\/5\.1(?:\.\d+)? Safari\//.test(navigator.userAgent)) { 10460 elem[request](); 10461 } else { 10462 elem[request](keyboardAllowed ? Element.ALLOW_KEYBOARD_INPUT : {}); 10463 } 10464 10465 this.on('change', onFullScreenEntered); 10466 }.bind(this)); 10467 }, 10468 exit: function () { 10469 return new Promise(function (resolve) { 10470 if (!this.isFullscreen) { 10471 resolve(); 10472 return; 10473 } 10474 10475 var onFullScreenExit = function () { 10476 this.off('change', onFullScreenExit); 10477 resolve(); 10478 }.bind(this); 10479 10480 document[fn.exitFullscreen](); 10481 10482 this.on('change', onFullScreenExit); 10483 }.bind(this)); 10484 }, 10485 toggle: function (elem) { 10486 return this.isFullscreen ? this.exit() : this.request(elem); 10487 }, 10488 onchange: function (callback) { 10489 this.on('change', callback); 10490 }, 10491 onerror: function (callback) { 10492 this.on('error', callback); 10493 }, 10494 on: function (event, callback) { 10495 var eventName = eventNameMap[event]; 10496 if (eventName) { 10497 document.addEventListener(eventName, callback, false); 10498 } 10499 }, 10500 off: function (event, callback) { 10501 var eventName = eventNameMap[event]; 10502 if (eventName) { 10503 document.removeEventListener(eventName, callback, false); 10504 } 10505 }, 10506 raw: fn 10507 }; 10508 10509 if (!fn) { 10510 if (isCommonjs) { 10511 module.exports = false; 10512 } else { 10513 window.ngscreenfull = false; 10514 } 10515 10516 return; 10517 } 10518 10519 Object.defineProperties(ngscreenfull, { 10520 isFullscreen: { 10521 get: function () { 10522 return Boolean(document[fn.fullscreenElement]); 10523 } 10524 }, 10525 element: { 10526 enumerable: true, 10527 get: function () { 10528 return document[fn.fullscreenElement]; 10529 } 10530 }, 10531 enabled: { 10532 enumerable: true, 10533 get: function () { 10534 // Coerce to boolean in case of old WebKit 10535 return Boolean(document[fn.fullscreenEnabled]); 10536 } 10537 } 10538 }); 10539 10540 if (isCommonjs) { 10541 module.exports = ngscreenfull; 10542 } else { 10543 window.ngscreenfull = ngscreenfull; 10544 } 10545})(); 10546 10547 10548 10549//########################################################################################################################## 10550//## Shifty ################################################################################################################ 10551//########################################################################################################################## 10552 10553 /*! 10554 * Shifty 10555 * By Jeremy Kahn - [email protected] 10556 */ 10557 10558// external module EMBEDED in nanogallery 10559// NGY BUILD: 10560// 10561// replace "Tweenable" with "NGTweenable" 10562// replace "define.amd" with "define.amdDISABLED" 10563/* shifty - v1.5.3 - 2016-11-29 - http://jeremyckahn.github.io/shifty */ 10564;(function () { 10565 var root = this || Function('return this')();
vendor: 129,699 bytes, lines 10565-14895
10565 10566 10567/** 10568 * Shifty Core 10569 * By Jeremy Kahn - [email protected] 10570 */ 10571 10572var NGTweenable = (function () { 10573 10574 'use strict'; 10575 10576 // Aliases that get defined later in this function 10577 var formula; 10578 10579 // CONSTANTS 10580 var DEFAULT_SCHEDULE_FUNCTION; 10581 var DEFAULT_EASING = 'linear'; 10582 var DEFAULT_DURATION = 500; 10583 var UPDATE_TIME = 1000 / 60; 10584 10585 var _now = Date.now 10586 ? Date.now 10587 : function () {return +new Date();}; 10588 10589 var now = typeof SHIFTY_DEBUG_NOW !== 'undefined' ? SHIFTY_DEBUG_NOW : _now; 10590 10591 if (typeof window !== 'undefined') { 10592 // requestAnimationFrame() shim by Paul Irish (modified for Shifty) 10593 // http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 10594 DEFAULT_SCHEDULE_FUNCTION = window.requestAnimationFrame 10595 || window.webkitRequestAnimationFrame 10596 || window.oRequestAnimationFrame 10597 || window.msRequestAnimationFrame 10598 || (window.mozCancelRequestAnimationFrame 10599 && window.mozRequestAnimationFrame) 10600 || setTimeout; 10601 } else { 10602 DEFAULT_SCHEDULE_FUNCTION = setTimeout; 10603 } 10604 10605 function noop () { 10606 // NOOP! 10607 } 10608 10609 /** 10610 * Handy shortcut for doing a for-in loop. This is not a "normal" each 10611 * function, it is optimized for Shifty. The iterator function only receives 10612 * the property name, not the value. 10613 * @param {Object} obj 10614 * @param {Function(string)} fn 10615 * @private 10616 */ 10617 function each (obj, fn) { 10618 var key; 10619 for (key in obj) { 10620 if (Object.hasOwnProperty.call(obj, key)) { 10621 fn(key); 10622 } 10623 } 10624 } 10625 10626 /** 10627 * Perform a shallow copy of Object properties. 10628 * @param {Object} targetObject The object to copy into 10629 * @param {Object} srcObject The object to copy from 10630 * @return {Object} A reference to the augmented `targetObj` Object 10631 * @private 10632 */ 10633 function shallowCopy (targetObj, srcObj) { 10634 each(srcObj, function (prop) { 10635 targetObj[prop] = srcObj[prop]; 10636 }); 10637 10638 return targetObj; 10639 } 10640 10641 /** 10642 * Copies each property from src onto target, but only if the property to 10643 * copy to target is undefined. 10644 * @param {Object} target Missing properties in this Object are filled in 10645 * @param {Object} src 10646 * @private 10647 */ 10648 function defaults (target, src) { 10649 each(src, function (prop) { 10650 if (typeof target[prop] === 'undefined') { 10651 target[prop] = src[prop]; 10652 } 10653 }); 10654 } 10655 10656 /** 10657 * Calculates the interpolated tween values of an Object for a given 10658 * timestamp. 10659 * @param {Number} forPosition The position to compute the state for. 10660 * @param {Object} currentState Current state properties. 10661 * @param {Object} originalState: The original state properties the Object is 10662 * tweening from. 10663 * @param {Object} targetState: The destination state properties the Object 10664 * is tweening to. 10665 * @param {number} duration: The length of the tween in milliseconds. 10666 * @param {number} timestamp: The UNIX epoch time at which the tween began. 10667 * @param {Object} easing: This Object's keys must correspond to the keys in 10668 * targetState. 10669 * @private 10670 */ 10671 function tweenProps (forPosition, currentState, originalState, targetState, 10672 duration, timestamp, easing) { 10673 var normalizedPosition = 10674 forPosition < timestamp ? 0 : (forPosition - timestamp) / duration; 10675 10676 10677 var prop; 10678 var easingObjectProp; 10679 var easingFn; 10680 for (prop in currentState) { 10681 if (currentState.hasOwnProperty(prop)) { 10682 easingObjectProp = easing[prop]; 10683 easingFn = typeof easingObjectProp === 'function' 10684 ? easingObjectProp 10685 : formula[easingObjectProp]; 10686 10687 currentState[prop] = tweenProp( 10688 originalState[prop], 10689 targetState[prop], 10690 easingFn, 10691 normalizedPosition 10692 ); 10693 } 10694 } 10695 10696 return currentState; 10697 } 10698 10699 /** 10700 * Tweens a single property. 10701 * @param {number} start The value that the tween started from. 10702 * @param {number} end The value that the tween should end at. 10703 * @param {Function} easingFunc The easing curve to apply to the tween. 10704 * @param {number} position The normalized position (between 0.0 and 1.0) to 10705 * calculate the midpoint of 'start' and 'end' against. 10706 * @return {number} The tweened value. 10707 * @private 10708 */ 10709 function tweenProp (start, end, easingFunc, position) { 10710 return start + (end - start) * easingFunc(position); 10711 } 10712 10713 /** 10714 * Applies a filter to NGTweenable instance. 10715 * @param {NGTweenable} tweenable The `NGTweenable` instance to call the filter 10716 * upon. 10717 * @param {String} filterName The name of the filter to apply. 10718 * @private 10719 */ 10720 function applyFilter (tweenable, filterName) { 10721 var filters = NGTweenable.prototype.filter; 10722 var args = tweenable._filterArgs; 10723 10724 each(filters, function (name) { 10725 if (typeof filters[name][filterName] !== 'undefined') { 10726 filters[name][filterName].apply(tweenable, args); 10727 } 10728 }); 10729 } 10730 10731 var timeoutHandler_endTime; 10732 var timeoutHandler_currentTime; 10733 var timeoutHandler_isEnded; 10734 var timeoutHandler_offset; 10735 /** 10736 * Handles the update logic for one step of a tween. 10737 * @param {NGTweenable} tweenable 10738 * @param {number} timestamp 10739 * @param {number} delay 10740 * @param {number} duration 10741 * @param {Object} currentState 10742 * @param {Object} originalState 10743 * @param {Object} targetState 10744 * @param {Object} easing 10745 * @param {Function(Object, *, number)} step 10746 * @param {Function(Function,number)}} schedule 10747 * @param {number=} opt_currentTimeOverride Needed for accurate timestamp in 10748 * NGTweenable#seek. 10749 * @private 10750 */ 10751 function timeoutHandler (tweenable, timestamp, delay, duration, currentState, 10752 originalState, targetState, easing, step, schedule, 10753 opt_currentTimeOverride) { 10754 10755 timeoutHandler_endTime = timestamp + delay + duration; 10756 10757 timeoutHandler_currentTime = 10758 Math.min(opt_currentTimeOverride || now(), timeoutHandler_endTime); 10759 10760 timeoutHandler_isEnded = 10761 timeoutHandler_currentTime >= timeoutHandler_endTime; 10762 10763 timeoutHandler_offset = duration - ( 10764 timeoutHandler_endTime - timeoutHandler_currentTime); 10765 10766 if (tweenable.isPlaying()) { 10767 if (timeoutHandler_isEnded) { 10768 step(targetState, tweenable._attachment, timeoutHandler_offset); 10769 tweenable.stop(true); 10770 } else { 10771 tweenable._scheduleId = 10772 schedule(tweenable._timeoutHandler, UPDATE_TIME); 10773 10774 applyFilter(tweenable, 'beforeTween'); 10775 10776 // If the animation has not yet reached the start point (e.g., there was 10777 // delay that has not yet completed), just interpolate the starting 10778 // position of the tween. 10779 if (timeoutHandler_currentTime < (timestamp + delay)) { 10780 tweenProps(1, currentState, originalState, targetState, 1, 1, easing); 10781 } else { 10782 tweenProps(timeoutHandler_currentTime, currentState, originalState, 10783 targetState, duration, timestamp + delay, easing); 10784 } 10785 10786 applyFilter(tweenable, 'afterTween'); 10787 10788 step(currentState, tweenable._attachment, timeoutHandler_offset); 10789 } 10790 } 10791 } 10792 10793 10794 /** 10795 * Creates a usable easing Object from a string, a function or another easing 10796 * Object. If `easing` is an Object, then this function clones it and fills 10797 * in the missing properties with `"linear"`. 10798 * @param {Object.<string|Function>} fromTweenParams 10799 * @param {Object|string|Function} easing 10800 * @return {Object.<string|Function>} 10801 * @private 10802 */ 10803 function composeEasingObject (fromTweenParams, easing) { 10804 var composedEasing = {}; 10805 var typeofEasing = typeof easing; 10806 10807 if (typeofEasing === 'string' || typeofEasing === 'function') { 10808 each(fromTweenParams, function (prop) { 10809 composedEasing[prop] = easing; 10810 }); 10811 } else { 10812 each(fromTweenParams, function (prop) { 10813 if (!composedEasing[prop]) { 10814 composedEasing[prop] = easing[prop] || DEFAULT_EASING; 10815 } 10816 }); 10817 } 10818 10819 return composedEasing; 10820 } 10821 10822 /** 10823 * NGTweenable constructor. 10824 * @class NGTweenable 10825 * @param {Object=} opt_initialState The values that the initial tween should 10826 * start at if a `from` object is not provided to `{{#crossLink 10827 * "NGTweenable/tween:method"}}{{/crossLink}}` or `{{#crossLink 10828 * "NGTweenable/setConfig:method"}}{{/crossLink}}`. 10829 * @param {Object=} opt_config Configuration object to be passed to 10830 * `{{#crossLink "NGTweenable/setConfig:method"}}{{/crossLink}}`. 10831 * @module NGTweenable 10832 * @constructor 10833 */ 10834 function NGTweenable (opt_initialState, opt_config) { 10835 this._currentState = opt_initialState || {}; 10836 this._configured = false; 10837 this._scheduleFunction = DEFAULT_SCHEDULE_FUNCTION; 10838 10839 // To prevent unnecessary calls to setConfig do not set default 10840 // configuration here. Only set default configuration immediately before 10841 // tweening if none has been set. 10842 if (typeof opt_config !== 'undefined') { 10843 this.setConfig(opt_config); 10844 } 10845 } 10846 10847 /** 10848 * Configure and start a tween. 10849 * @method tween 10850 * @param {Object=} opt_config Configuration object to be passed to 10851 * `{{#crossLink "NGTweenable/setConfig:method"}}{{/crossLink}}`. 10852 * @chainable 10853 */ 10854 NGTweenable.prototype.tween = function (opt_config) { 10855 if (this._isTweening) { 10856 return this; 10857 } 10858 10859 // Only set default config if no configuration has been set previously and 10860 // none is provided now. 10861 if (opt_config !== undefined || !this._configured) { 10862 this.setConfig(opt_config); 10863 } 10864 10865 this._timestamp = now(); 10866 this._start(this.get(), this._attachment); 10867 return this.resume(); 10868 }; 10869 10870 /** 10871 * Configure a tween that will start at some point in the future. 10872 * 10873 * @method setConfig 10874 * @param {Object} config The following values are valid: 10875 * - __from__ (_Object=_): Starting position. If omitted, `{{#crossLink 10876 * "NGTweenable/get:method"}}get(){{/crossLink}}` is used. 10877 * - __to__ (_Object=_): Ending position. 10878 * - __duration__ (_number=_): How many milliseconds to animate for. 10879 * - __delay__ (_delay=_): How many milliseconds to wait before starting the 10880 * tween. 10881 * - __start__ (_Function(Object, *)_): Function to execute when the tween 10882 * begins. Receives the state of the tween as the first parameter and 10883 * `attachment` as the second parameter. 10884 * - __step__ (_Function(Object, *, number)_): Function to execute on every 10885 * tick. Receives `{{#crossLink 10886 * "NGTweenable/get:method"}}get(){{/crossLink}}` as the first parameter, 10887 * `attachment` as the second parameter, and the time elapsed since the 10888 * start of the tween as the third. This function is not called on the 10889 * final step of the animation, but `finish` is. 10890 * - __finish__ (_Function(Object, *)_): Function to execute upon tween 10891 * completion. Receives the state of the tween as the first parameter and 10892 * `attachment` as the second parameter. 10893 * - __easing__ (_Object.<string|Function>|string|Function=_): Easing curve 10894 * name(s) or function(s) to use for the tween. 10895 * - __attachment__ (_*_): Cached value that is passed to the 10896 * `step`/`start`/`finish` methods. 10897 * @chainable 10898 */ 10899 NGTweenable.prototype.setConfig = function (config) { 10900 config = config || {}; 10901 this._configured = true; 10902 10903 // Attach something to this NGTweenable instance (e.g.: a DOM element, an 10904 // object, a string, etc.); 10905 this._attachment = config.attachment; 10906 10907 // Init the internal state 10908 this._pausedAtTime = null; 10909 this._scheduleId = null; 10910 this._delay = config.delay || 0; 10911 this._start = config.start || noop; 10912 this._step = config.step || noop; 10913 this._finish = config.finish || noop; 10914 this._duration = config.duration || DEFAULT_DURATION; 10915 this._currentState = shallowCopy({}, config.from || this.get()); 10916 this._originalState = this.get(); 10917 this._targetState = shallowCopy({}, config.to || this.get()); 10918 10919 var self = this; 10920 this._timeoutHandler = function () { 10921 timeoutHandler(self, 10922 self._timestamp, 10923 self._delay, 10924 self._duration, 10925 self._currentState, 10926 self._originalState, 10927 self._targetState, 10928 self._easing, 10929 self._step, 10930 self._scheduleFunction 10931 ); 10932 }; 10933 10934 // Aliases used below 10935 var currentState = this._currentState; 10936 var targetState = this._targetState; 10937 10938 // Ensure that there is always something to tween to. 10939 defaults(targetState, currentState); 10940 10941 this._easing = composeEasingObject( 10942 currentState, config.easing || DEFAULT_EASING); 10943 10944 this._filterArgs = 10945 [currentState, this._originalState, targetState, this._easing]; 10946 10947 applyFilter(this, 'tweenCreated'); 10948 return this; 10949 }; 10950 10951 /** 10952 * @method get 10953 * @return {Object} The current state. 10954 */ 10955 NGTweenable.prototype.get = function () { 10956 return shallowCopy({}, this._currentState); 10957 }; 10958 10959 /** 10960 * @method set 10961 * @param {Object} state The current state. 10962 */ 10963 NGTweenable.prototype.set = function (state) { 10964 this._currentState = state; 10965 }; 10966 10967 /** 10968 * Pause a tween. Paused tweens can be resumed from the point at which they 10969 * were paused. This is different from `{{#crossLink 10970 * "NGTweenable/stop:method"}}{{/crossLink}}`, as that method 10971 * causes a tween to start over when it is resumed. 10972 * @method pause 10973 * @chainable 10974 */ 10975 NGTweenable.prototype.pause = function () { 10976 this._pausedAtTime = now(); 10977 this._isPaused = true; 10978 return this; 10979 }; 10980 10981 /** 10982 * Resume a paused tween. 10983 * @method resume 10984 * @chainable 10985 */ 10986 NGTweenable.prototype.resume = function () { 10987 if (this._isPaused) { 10988 this._timestamp += now() - this._pausedAtTime; 10989 } 10990 10991 this._isPaused = false; 10992 this._isTweening = true; 10993 10994 this._timeoutHandler(); 10995 10996 return this; 10997 }; 10998 10999 /** 11000 * Move the state of the animation to a specific point in the tween's 11001 * timeline. If the animation is not running, this will cause the `step` 11002 * handlers to be called. 11003 * @method seek 11004 * @param {millisecond} millisecond The millisecond of the animation to seek 11005 * to. This must not be less than `0`. 11006 * @chainable 11007 */ 11008 NGTweenable.prototype.seek = function (millisecond) { 11009 millisecond = Math.max(millisecond, 0); 11010 var currentTime = now(); 11011 11012 if ((this._timestamp + millisecond) === 0) { 11013 return this; 11014 } 11015 11016 this._timestamp = currentTime - millisecond; 11017 11018 if (!this.isPlaying()) { 11019 this._isTweening = true; 11020 this._isPaused = false; 11021 11022 // If the animation is not running, call timeoutHandler to make sure that 11023 // any step handlers are run. 11024 timeoutHandler(this, 11025 this._timestamp, 11026 this._delay, 11027 this._duration, 11028 this._currentState, 11029 this._originalState, 11030 this._targetState, 11031 this._easing, 11032 this._step, 11033 this._scheduleFunction, 11034 currentTime 11035 ); 11036 11037 this.pause(); 11038 } 11039 11040 return this; 11041 }; 11042 11043 /** 11044 * Stops and cancels a tween. 11045 * @param {boolean=} gotoEnd If `false` or omitted, the tween just stops at 11046 * its current state, and the `finish` handler is not invoked. If `true`, 11047 * the tweened object's values are instantly set to the target values, and 11048 * `finish` is invoked. 11049 * @method stop 11050 * @chainable 11051 */ 11052 NGTweenable.prototype.stop = function (gotoEnd) { 11053 this._isTweening = false; 11054 this._isPaused = false; 11055 this._timeoutHandler = noop; 11056 11057 (root.cancelAnimationFrame || 11058 root.webkitCancelAnimationFrame || 11059 root.oCancelAnimationFrame || 11060 root.msCancelAnimationFrame || 11061 root.mozCancelRequestAnimationFrame || 11062 root.clearTimeout)(this._scheduleId); 11063 11064 if (gotoEnd) { 11065 applyFilter(this, 'beforeTween'); 11066 tweenProps( 11067 1, 11068 this._currentState, 11069 this._originalState, 11070 this._targetState, 11071 1, 11072 0, 11073 this._easing 11074 ); 11075 applyFilter(this, 'afterTween'); 11076 applyFilter(this, 'afterTweenEnd'); 11077 this._finish.call(this, this._currentState, this._attachment); 11078 } 11079 11080 return this; 11081 }; 11082 11083 /** 11084 * @method isPlaying 11085 * @return {boolean} Whether or not a tween is running. 11086 */ 11087 NGTweenable.prototype.isPlaying = function () { 11088 return this._isTweening && !this._isPaused; 11089 }; 11090 11091 /** 11092 * Set a custom schedule function. 11093 * 11094 * If a custom function is not set, 11095 * [`requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame) 11096 * is used if available, otherwise 11097 * [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/Window.setTimeout) 11098 * is used. 11099 * @method setScheduleFunction 11100 * @param {Function(Function,number)} scheduleFunction The function to be 11101 * used to schedule the next frame to be rendered. 11102 */ 11103 NGTweenable.prototype.setScheduleFunction = function (scheduleFunction) { 11104 this._scheduleFunction = scheduleFunction; 11105 }; 11106 11107 /** 11108 * `delete` all "own" properties. Call this when the `NGTweenable` instance 11109 * is no longer needed to free memory. 11110 * @method dispose 11111 */ 11112 NGTweenable.prototype.dispose = function () { 11113 var prop; 11114 for (prop in this) { 11115 if (this.hasOwnProperty(prop)) { 11116 delete this[prop]; 11117 } 11118 } 11119 }; 11120 11121 /** 11122 * Filters are used for transforming the properties of a tween at various 11123 * points in a NGTweenable's life cycle. See the README for more info on this. 11124 * @private 11125 */ 11126 NGTweenable.prototype.filter = {}; 11127 11128 /** 11129 * This object contains all of the tweens available to Shifty. It is 11130 * extensible - simply attach properties to the `NGTweenable.prototype.formula` 11131 * Object following the same format as `linear`. 11132 * 11133 * `pos` should be a normalized `number` (between 0 and 1). 11134 * @property formula 11135 * @type {Object(function)} 11136 */ 11137 NGTweenable.prototype.formula = { 11138 linear: function (pos) { 11139 return pos; 11140 } 11141 }; 11142 11143 formula = NGTweenable.prototype.formula; 11144 11145 shallowCopy(NGTweenable, { 11146 'now': now 11147 ,'each': each 11148 ,'tweenProps': tweenProps 11149 ,'tweenProp': tweenProp 11150 ,'applyFilter': applyFilter 11151 ,'shallowCopy': shallowCopy 11152 ,'defaults': defaults 11153 ,'composeEasingObject': composeEasingObject 11154 }); 11155 11156 // `root` is provided in the intro/outro files. 11157 11158 // A hook used for unit testing. 11159 if (typeof SHIFTY_DEBUG_NOW === 'function') { 11160 root.timeoutHandler = timeoutHandler; 11161 } 11162 11163 // Bootstrap NGTweenable appropriately for the environment. 11164 if (typeof exports === 'object') { 11165 // CommonJS 11166 module.exports = NGTweenable; 11167 } else if (typeof define === 'function' && define.amdDISABLED) { 11168 // AMD 11169 define(function () {return NGTweenable;}); 11170 } else if (typeof root.NGTweenable === 'undefined') { 11171 // Browser: Make `NGTweenable` globally accessible. 11172 root.NGTweenable = NGTweenable; 11173 } 11174 11175 return NGTweenable; 11176 11177} ()); 11178 11179/*! 11180 * All equations are adapted from Thomas Fuchs' 11181 * [Scripty2](https://github.com/madrobby/scripty2/blob/master/src/effects/transitions/penner.js). 11182 * 11183 * Based on Easing Equations (c) 2003 [Robert 11184 * Penner](http://www.robertpenner.com/), all rights reserved. This work is 11185 * [subject to terms](http://www.robertpenner.com/easing_terms_of_use.html). 11186 */ 11187 11188/*! 11189 * TERMS OF USE - EASING EQUATIONS 11190 * Open source under the BSD License. 11191 * Easing Equations (c) 2003 Robert Penner, all rights reserved. 11192 */ 11193 11194;(function () { 11195 11196 NGTweenable.shallowCopy(NGTweenable.prototype.formula, { 11197 easeInQuad: function (pos) { 11198 return Math.pow(pos, 2); 11199 }, 11200 11201 easeOutQuad: function (pos) { 11202 return -(Math.pow((pos - 1), 2) - 1); 11203 }, 11204 11205 easeInOutQuad: function (pos) { 11206 if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,2);} 11207 return -0.5 * ((pos -= 2) * pos - 2); 11208 }, 11209 11210 easeInCubic: function (pos) { 11211 return Math.pow(pos, 3); 11212 }, 11213 11214 easeOutCubic: function (pos) { 11215 return (Math.pow((pos - 1), 3) + 1); 11216 }, 11217 11218 easeInOutCubic: function (pos) { 11219 if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,3);} 11220 return 0.5 * (Math.pow((pos - 2),3) + 2); 11221 }, 11222 11223 easeInQuart: function (pos) { 11224 return Math.pow(pos, 4); 11225 }, 11226 11227 easeOutQuart: function (pos) { 11228 return -(Math.pow((pos - 1), 4) - 1); 11229 }, 11230 11231 easeInOutQuart: function (pos) { 11232 if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,4);} 11233 return -0.5 * ((pos -= 2) * Math.pow(pos,3) - 2); 11234 }, 11235 11236 easeInQuint: function (pos) { 11237 return Math.pow(pos, 5); 11238 }, 11239 11240 easeOutQuint: function (pos) { 11241 return (Math.pow((pos - 1), 5) + 1); 11242 }, 11243 11244 easeInOutQuint: function (pos) { 11245 if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,5);} 11246 return 0.5 * (Math.pow((pos - 2),5) + 2); 11247 }, 11248 11249 easeInSine: function (pos) { 11250 return -Math.cos(pos * (Math.PI / 2)) + 1; 11251 }, 11252 11253 easeOutSine: function (pos) { 11254 return Math.sin(pos * (Math.PI / 2)); 11255 }, 11256 11257 easeInOutSine: function (pos) { 11258 return (-0.5 * (Math.cos(Math.PI * pos) - 1)); 11259 }, 11260 11261 easeInExpo: function (pos) { 11262 return (pos === 0) ? 0 : Math.pow(2, 10 * (pos - 1)); 11263 }, 11264 11265 easeOutExpo: function (pos) { 11266 return (pos === 1) ? 1 : -Math.pow(2, -10 * pos) + 1; 11267 }, 11268 11269 easeInOutExpo: function (pos) { 11270 if (pos === 0) {return 0;} 11271 if (pos === 1) {return 1;} 11272 if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(2,10 * (pos - 1));} 11273 return 0.5 * (-Math.pow(2, -10 * --pos) + 2); 11274 }, 11275 11276 easeInCirc: function (pos) { 11277 return -(Math.sqrt(1 - (pos * pos)) - 1); 11278 }, 11279 11280 easeOutCirc: function (pos) { 11281 return Math.sqrt(1 - Math.pow((pos - 1), 2)); 11282 }, 11283 11284 easeInOutCirc: function (pos) { 11285 if ((pos /= 0.5) < 1) {return -0.5 * (Math.sqrt(1 - pos * pos) - 1);} 11286 return 0.5 * (Math.sqrt(1 - (pos -= 2) * pos) + 1); 11287 }, 11288 11289 easeOutBounce: function (pos) { 11290 if ((pos) < (1 / 2.75)) { 11291 return (7.5625 * pos * pos); 11292 } else if (pos < (2 / 2.75)) { 11293 return (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75); 11294 } else if (pos < (2.5 / 2.75)) { 11295 return (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375); 11296 } else { 11297 return (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375); 11298 } 11299 }, 11300 11301 easeInBack: function (pos) { 11302 var s = 1.70158; 11303 return (pos) * pos * ((s + 1) * pos - s); 11304 }, 11305 11306 easeOutBack: function (pos) { 11307 var s = 1.70158; 11308 return (pos = pos - 1) * pos * ((s + 1) * pos + s) + 1; 11309 }, 11310 11311 easeInOutBack: function (pos) { 11312 var s = 1.70158; 11313 if ((pos /= 0.5) < 1) { 11314 return 0.5 * (pos * pos * (((s *= (1.525)) + 1) * pos - s)); 11315 } 11316 return 0.5 * ((pos -= 2) * pos * (((s *= (1.525)) + 1) * pos + s) + 2); 11317 }, 11318 11319 elastic: function (pos) { 11320 // jshint maxlen:90 11321 return -1 * Math.pow(4,-8 * pos) * Math.sin((pos * 6 - 1) * (2 * Math.PI) / 2) + 1; 11322 }, 11323 11324 swingFromTo: function (pos) { 11325 var s = 1.70158; 11326 return ((pos /= 0.5) < 1) ? 11327 0.5 * (pos * pos * (((s *= (1.525)) + 1) * pos - s)) : 11328 0.5 * ((pos -= 2) * pos * (((s *= (1.525)) + 1) * pos + s) + 2); 11329 }, 11330 11331 swingFrom: function (pos) { 11332 var s = 1.70158; 11333 return pos * pos * ((s + 1) * pos - s); 11334 }, 11335 11336 swingTo: function (pos) { 11337 var s = 1.70158; 11338 return (pos -= 1) * pos * ((s + 1) * pos + s) + 1; 11339 }, 11340 11341 bounce: function (pos) { 11342 if (pos < (1 / 2.75)) { 11343 return (7.5625 * pos * pos); 11344 } else if (pos < (2 / 2.75)) { 11345 return (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75); 11346 } else if (pos < (2.5 / 2.75)) { 11347 return (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375); 11348 } else { 11349 return (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375); 11350 } 11351 }, 11352 11353 bouncePast: function (pos) { 11354 if (pos < (1 / 2.75)) { 11355 return (7.5625 * pos * pos); 11356 } else if (pos < (2 / 2.75)) { 11357 return 2 - (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75); 11358 } else if (pos < (2.5 / 2.75)) { 11359 return 2 - (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375); 11360 } else { 11361 return 2 - (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375); 11362 } 11363 }, 11364 11365 easeFromTo: function (pos) { 11366 if ((pos /= 0.5) < 1) {return 0.5 * Math.pow(pos,4);} 11367 return -0.5 * ((pos -= 2) * Math.pow(pos,3) - 2); 11368 }, 11369 11370 easeFrom: function (pos) { 11371 return Math.pow(pos,4); 11372 }, 11373 11374 easeTo: function (pos) { 11375 return Math.pow(pos,0.25); 11376 } 11377 }); 11378 11379}()); 11380 11381// jshint maxlen:100 11382/** 11383 * The Bezier magic in this file is adapted/copied almost wholesale from 11384 * [Scripty2](https://github.com/madrobby/scripty2/blob/master/src/effects/transitions/cubic-bezier.js), 11385 * which was adapted from Apple code (which probably came from 11386 * [here](http://opensource.apple.com/source/WebCore/WebCore-955.66/platform/graphics/UnitBezier.h)). 11387 * Special thanks to Apple and Thomas Fuchs for much of this code. 11388 */ 11389 11390/** 11391 * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. 11392 * 11393 * Redistribution and use in source and binary forms, with or without 11394 * modification, are permitted provided that the following conditions are met: 11395 * 11396 * 1. Redistributions of source code must retain the above copyright notice, 11397 * this list of conditions and the following disclaimer. 11398 * 11399 * 2. Redistributions in binary form must reproduce the above copyright notice, 11400 * this list of conditions and the following disclaimer in the documentation 11401 * and/or other materials provided with the distribution. 11402 * 11403 * 3. Neither the name of the copyright holder(s) nor the names of any 11404 * contributors may be used to endorse or promote products derived from 11405 * this software without specific prior written permission. 11406 * 11407 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 11408 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 11409 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 11410 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 11411 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 11412 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 11413 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 11414 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 11415 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 11416 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 11417 * POSSIBILITY OF SUCH DAMAGE. 11418 */ 11419;(function () { 11420 // port of webkit cubic bezier handling by http://www.netzgesta.de/dev/ 11421 function cubicBezierAtTime(t,p1x,p1y,p2x,p2y,duration) { 11422 var ax = 0,bx = 0,cx = 0,ay = 0,by = 0,cy = 0; 11423 function sampleCurveX(t) { 11424 return ((ax * t + bx) * t + cx) * t; 11425 } 11426 function sampleCurveY(t) { 11427 return ((ay * t + by) * t + cy) * t; 11428 } 11429 function sampleCurveDerivativeX(t) { 11430 return (3.0 * ax * t + 2.0 * bx) * t + cx; 11431 } 11432 function solveEpsilon(duration) { 11433 return 1.0 / (200.0 * duration); 11434 } 11435 function solve(x,epsilon) { 11436 return sampleCurveY(solveCurveX(x, epsilon)); 11437 } 11438 function fabs(n) { 11439 if (n >= 0) { 11440 return n; 11441 } else { 11442 return 0 - n; 11443 } 11444 } 11445 function solveCurveX(x, epsilon) { 11446 var t0,t1,t2,x2,d2,i; 11447 for (t2 = x, i = 0; i < 8; i++) { 11448 x2 = sampleCurveX(t2) - x; 11449 if (fabs(x2) < epsilon) { 11450 return t2; 11451 } 11452 d2 = sampleCurveDerivativeX(t2); 11453 if (fabs(d2) < 1e-6) { 11454 break; 11455 } 11456 t2 = t2 - x2 / d2; 11457 } 11458 t0 = 0.0; 11459 t1 = 1.0; 11460 t2 = x; 11461 if (t2 < t0) { 11462 return t0; 11463 } 11464 if (t2 > t1) { 11465 return t1; 11466 } 11467 while (t0 < t1) { 11468 x2 = sampleCurveX(t2); 11469 if (fabs(x2 - x) < epsilon) { 11470 return t2; 11471 } 11472 if (x > x2) { 11473 t0 = t2; 11474 }else { 11475 t1 = t2; 11476 } 11477 t2 = (t1 - t0) * 0.5 + t0; 11478 } 11479 return t2; // Failure. 11480 } 11481 cx = 3.0 * p1x; 11482 bx = 3.0 * (p2x - p1x) - cx; 11483 ax = 1.0 - cx - bx; 11484 cy = 3.0 * p1y; 11485 by = 3.0 * (p2y - p1y) - cy; 11486 ay = 1.0 - cy - by; 11487 return solve(t, solveEpsilon(duration)); 11488 } 11489 /** 11490 * getCubicBezierTransition(x1, y1, x2, y2) -> Function 11491 * 11492 * Generates a transition easing function that is compatible 11493 * with WebKit's CSS transitions `-webkit-transition-timing-function` 11494 * CSS property. 11495 * 11496 * The W3C has more information about CSS3 transition timing functions: 11497 * http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag 11498 * 11499 * @param {number} x1 11500 * @param {number} y1 11501 * @param {number} x2 11502 * @param {number} y2 11503 * @return {function} 11504 * @private 11505 */ 11506 function getCubicBezierTransition (x1, y1, x2, y2) { 11507 return function (pos) { 11508 return cubicBezierAtTime(pos,x1,y1,x2,y2,1); 11509 }; 11510 } 11511 // End ported code 11512 11513 /** 11514 * Create a Bezier easing function and attach it to `{{#crossLink 11515 * "NGTweenable/formula:property"}}NGTweenable#formula{{/crossLink}}`. This 11516 * function gives you total control over the easing curve. Matthew Lein's 11517 * [Ceaser](http://matthewlein.com/ceaser/) is a useful tool for visualizing 11518 * the curves you can make with this function. 11519 * @method setBezierFunction 11520 * @param {string} name The name of the easing curve. Overwrites the old 11521 * easing function on `{{#crossLink 11522 * "NGTweenable/formula:property"}}NGTweenable#formula{{/crossLink}}` if it 11523 * exists. 11524 * @param {number} x1 11525 * @param {number} y1 11526 * @param {number} x2 11527 * @param {number} y2 11528 * @return {function} The easing function that was attached to 11529 * NGTweenable.prototype.formula. 11530 */ 11531 NGTweenable.setBezierFunction = function (name, x1, y1, x2, y2) { 11532 var cubicBezierTransition = getCubicBezierTransition(x1, y1, x2, y2); 11533 cubicBezierTransition.displayName = name; 11534 cubicBezierTransition.x1 = x1; 11535 cubicBezierTransition.y1 = y1; 11536 cubicBezierTransition.x2 = x2; 11537 cubicBezierTransition.y2 = y2; 11538 11539 return NGTweenable.prototype.formula[name] = cubicBezierTransition; 11540 }; 11541 11542 11543 /** 11544 * `delete` an easing function from `{{#crossLink 11545 * "NGTweenable/formula:property"}}NGTweenable#formula{{/crossLink}}`. Be 11546 * careful with this method, as it `delete`s whatever easing formula matches 11547 * `name` (which means you can delete standard Shifty easing functions). 11548 * @method unsetBezierFunction 11549 * @param {string} name The name of the easing function to delete. 11550 * @return {function} 11551 */ 11552 NGTweenable.unsetBezierFunction = function (name) { 11553 delete NGTweenable.prototype.formula[name]; 11554 }; 11555 11556})(); 11557 11558;(function () { 11559 11560 function getInterpolatedValues ( 11561 from, current, targetState, position, easing, delay) { 11562 return NGTweenable.tweenProps( 11563 position, current, from, targetState, 1, delay, easing); 11564 } 11565 11566 // Fake a NGTweenable and patch some internals. This approach allows us to 11567 // skip uneccessary processing and object recreation, cutting down on garbage 11568 // collection pauses. 11569 var mockNGTweenable = new NGTweenable(); 11570 mockNGTweenable._filterArgs = []; 11571 11572 /** 11573 * Compute the midpoint of two Objects. This method effectively calculates a 11574 * specific frame of animation that `{{#crossLink 11575 * "NGTweenable/tween:method"}}{{/crossLink}}` does many times over the course 11576 * of a full tween. 11577 * 11578 * var interpolatedValues = NGTweenable.interpolate({ 11579 * width: '100px', 11580 * opacity: 0, 11581 * color: '#fff' 11582 * }, { 11583 * width: '200px', 11584 * opacity: 1, 11585 * color: '#000' 11586 * }, 0.5); 11587 * 11588 * console.log(interpolatedValues); 11589 * // {opacity: 0.5, width: "150px", color: "rgb(127,127,127)"} 11590 * 11591 * @static 11592 * @method interpolate 11593 * @param {Object} from The starting values to tween from. 11594 * @param {Object} targetState The ending values to tween to. 11595 * @param {number} position The normalized position value (between `0.0` and 11596 * `1.0`) to interpolate the values between `from` and `to` for. `from` 11597 * represents `0` and `to` represents `1`. 11598 * @param {Object.<string|Function>|string|Function} easing The easing 11599 * curve(s) to calculate the midpoint against. You can reference any easing 11600 * function attached to `NGTweenable.prototype.formula`, or provide the easing 11601 * function(s) directly. If omitted, this defaults to "linear". 11602 * @param {number=} opt_delay Optional delay to pad the beginning of the 11603 * interpolated tween with. This increases the range of `position` from (`0` 11604 * through `1`) to (`0` through `1 + opt_delay`). So, a delay of `0.5` would 11605 * increase all valid values of `position` to numbers between `0` and `1.5`. 11606 * @return {Object} 11607 */ 11608 NGTweenable.interpolate = function ( 11609 from, targetState, position, easing, opt_delay) { 11610 11611 var current = NGTweenable.shallowCopy({}, from); 11612 var delay = opt_delay || 0; 11613 var easingObject = NGTweenable.composeEasingObject( 11614 from, easing || 'linear'); 11615 11616 mockNGTweenable.set({}); 11617 11618 // Alias and reuse the _filterArgs array instead of recreating it. 11619 var filterArgs = mockNGTweenable._filterArgs; 11620 filterArgs.length = 0; 11621 filterArgs[0] = current; 11622 filterArgs[1] = from; 11623 filterArgs[2] = targetState; 11624 filterArgs[3] = easingObject; 11625 11626 // Any defined value transformation must be applied 11627 NGTweenable.applyFilter(mockNGTweenable, 'tweenCreated'); 11628 NGTweenable.applyFilter(mockNGTweenable, 'beforeTween'); 11629 11630 var interpolatedValues = getInterpolatedValues( 11631 from, current, targetState, position, easingObject, delay); 11632 11633 // Transform values back into their original format 11634 NGTweenable.applyFilter(mockNGTweenable, 'afterTween'); 11635 11636 return interpolatedValues; 11637 }; 11638 11639}()); 11640 11641/** 11642 * This module adds string interpolation support to Shifty. 11643 * 11644 * The Token extension allows Shifty to tween numbers inside of strings. Among 11645 * other things, this allows you to animate CSS properties. For example, you 11646 * can do this: 11647 * 11648 * var tweenable = new NGTweenable(); 11649 * tweenable.tween({ 11650 * from: { transform: 'translateX(45px)' }, 11651 * to: { transform: 'translateX(90xp)' } 11652 * }); 11653 * 11654 * `translateX(45)` will be tweened to `translateX(90)`. To demonstrate: 11655 * 11656 * var tweenable = new NGTweenable(); 11657 * tweenable.tween({ 11658 * from: { transform: 'translateX(45px)' }, 11659 * to: { transform: 'translateX(90px)' }, 11660 * step: function (state) { 11661 * console.log(state.transform); 11662 * } 11663 * }); 11664 * 11665 * The above snippet will log something like this in the console: 11666 * 11667 * translateX(60.3px) 11668 * ... 11669 * translateX(76.05px) 11670 * ... 11671 * translateX(90px) 11672 * 11673 * Another use for this is animating colors: 11674 * 11675 * var tweenable = new NGTweenable(); 11676 * tweenable.tween({ 11677 * from: { color: 'rgb(0,255,0)' }, 11678 * to: { color: 'rgb(255,0,255)' }, 11679 * step: function (state) { 11680 * console.log(state.color); 11681 * } 11682 * }); 11683 * 11684 * The above snippet will log something like this: 11685 * 11686 * rgb(84,170,84) 11687 * ... 11688 * rgb(170,84,170) 11689 * ... 11690 * rgb(255,0,255) 11691 * 11692 * This extension also supports hexadecimal colors, in both long (`#ff00ff`) 11693 * and short (`#f0f`) forms. Be aware that hexadecimal input values will be 11694 * converted into the equivalent RGB output values. This is done to optimize 11695 * for performance. 11696 * 11697 * var tweenable = new NGTweenable(); 11698 * tweenable.tween({ 11699 * from: { color: '#0f0' }, 11700 * to: { color: '#f0f' }, 11701 * step: function (state) { 11702 * console.log(state.color); 11703 * } 11704 * }); 11705 * 11706 * This snippet will generate the same output as the one before it because 11707 * equivalent values were supplied (just in hexadecimal form rather than RGB): 11708 * 11709 * rgb(84,170,84) 11710 * ... 11711 * rgb(170,84,170) 11712 * ... 11713 * rgb(255,0,255) 11714 * 11715 * ## Easing support 11716 * 11717 * Easing works somewhat differently in the Token extension. This is because 11718 * some CSS properties have multiple values in them, and you might need to 11719 * tween each value along its own easing curve. A basic example: 11720 * 11721 * var tweenable = new NGTweenable(); 11722 * tweenable.tween({ 11723 * from: { transform: 'translateX(0px) translateY(0px)' }, 11724 * to: { transform: 'translateX(100px) translateY(100px)' }, 11725 * easing: { transform: 'easeInQuad' }, 11726 * step: function (state) { 11727 * console.log(state.transform); 11728 * } 11729 * }); 11730 * 11731 * The above snippet will create values like this: 11732 * 11733 * translateX(11.56px) translateY(11.56px) 11734 * ... 11735 * translateX(46.24px) translateY(46.24px) 11736 * ... 11737 * translateX(100px) translateY(100px) 11738 * 11739 * In this case, the values for `translateX` and `translateY` are always the 11740 * same for each step of the tween, because they have the same start and end 11741 * points and both use the same easing curve. We can also tween `translateX` 11742 * and `translateY` along independent curves: 11743 * 11744 * var tweenable = new NGTweenable(); 11745 * tweenable.tween({ 11746 * from: { transform: 'translateX(0px) translateY(0px)' }, 11747 * to: { transform: 'translateX(100px) translateY(100px)' }, 11748 * easing: { transform: 'easeInQuad bounce' }, 11749 * step: function (state) { 11750 * console.log(state.transform); 11751 * } 11752 * }); 11753 * 11754 * The above snippet will create values like this: 11755 * 11756 * translateX(10.89px) translateY(82.35px) 11757 * ... 11758 * translateX(44.89px) translateY(86.73px) 11759 * ... 11760 * translateX(100px) translateY(100px) 11761 * 11762 * `translateX` and `translateY` are not in sync anymore, because `easeInQuad` 11763 * was specified for `translateX` and `bounce` for `translateY`. Mixing and 11764 * matching easing curves can make for some interesting motion in your 11765 * animations. 11766 * 11767 * The order of the space-separated easing curves correspond the token values 11768 * they apply to. If there are more token values than easing curves listed, 11769 * the last easing curve listed is used. 11770 * @submodule NGTweenable.token 11771 */ 11772 11773// token function is defined above only so that dox-foundation sees it as 11774// documentation and renders it. It is never used, and is optimized away at 11775// build time. 11776 11777;(function (NGTweenable) { 11778 11779 /** 11780 * @typedef {{ 11781 * formatString: string 11782 * chunkNames: Array.<string> 11783 * }} 11784 * @private 11785 */ 11786 var formatManifest; 11787 11788 // CONSTANTS 11789 11790 var R_NUMBER_COMPONENT = /(\d|\-|\.)/; 11791 var R_FORMAT_CHUNKS = /([^\-0-9\.]+)/g; 11792 var R_UNFORMATTED_VALUES = /[0-9.\-]+/g; 11793 var R_RGB = new RegExp( 11794 'rgb\\(' + R_UNFORMATTED_VALUES.source + 11795 (/,\s*/.source) + R_UNFORMATTED_VALUES.source + 11796 (/,\s*/.source) + R_UNFORMATTED_VALUES.source + '\\)', 'g'); 11797 var R_RGB_PREFIX = /^.*\(/; 11798 var R_HEX = /#([0-9]|[a-f]){3,6}/gi; 11799 var VALUE_PLACEHOLDER = 'VAL'; 11800 11801 // HELPERS 11802 11803 /** 11804 * @param {Array.number} rawValues 11805 * @param {string} prefix 11806 * 11807 * @return {Array.<string>} 11808 * @private 11809 */ 11810 function getFormatChunksFrom (rawValues, prefix) { 11811 var accumulator = []; 11812 11813 var rawValuesLength = rawValues.length; 11814 var i; 11815 11816 for (i = 0; i < rawValuesLength; i++) { 11817 accumulator.push('_' + prefix + '_' + i); 11818 } 11819 11820 return accumulator; 11821 } 11822 11823 /** 11824 * @param {string} formattedString 11825 * 11826 * @return {string} 11827 * @private 11828 */ 11829 function getFormatStringFrom (formattedString) { 11830 var chunks = formattedString.match(R_FORMAT_CHUNKS); 11831 11832 if (!chunks) { 11833 // chunks will be null if there were no tokens to parse in 11834 // formattedString (for example, if formattedString is '2'). Coerce 11835 // chunks to be useful here. 11836 chunks = ['', '']; 11837 11838 // If there is only one chunk, assume that the string is a number 11839 // followed by a token... 11840 // NOTE: This may be an unwise assumption. 11841 } else if (chunks.length === 1 || 11842 // ...or if the string starts with a number component (".", "-", or a 11843 // digit)... 11844 formattedString.charAt(0).match(R_NUMBER_COMPONENT)) { 11845 // ...prepend an empty string here to make sure that the formatted number 11846 // is properly replaced by VALUE_PLACEHOLDER 11847 chunks.unshift(''); 11848 } 11849 11850 return chunks.join(VALUE_PLACEHOLDER); 11851 } 11852 11853 /** 11854 * Convert all hex color values within a string to an rgb string. 11855 * 11856 * @param {Object} stateObject 11857 * 11858 * @return {Object} The modified obj 11859 * @private 11860 */ 11861 function sanitizeObjectForHexProps (stateObject) { 11862 NGTweenable.each(stateObject, function (prop) { 11863 var currentProp = stateObject[prop]; 11864 11865 if (typeof currentProp === 'string' && currentProp.match(R_HEX)) { 11866 stateObject[prop] = sanitizeHexChunksToRGB(currentProp); 11867 } 11868 }); 11869 } 11870 11871 /** 11872 * @param {string} str 11873 * 11874 * @return {string} 11875 * @private 11876 */ 11877 function sanitizeHexChunksToRGB (str) { 11878 return filterStringChunks(R_HEX, str, convertHexToRGB); 11879 } 11880 11881 /** 11882 * @param {string} hexString 11883 * 11884 * @return {string} 11885 * @private 11886 */ 11887 function convertHexToRGB (hexString) { 11888 var rgbArr = hexToRGBArray(hexString); 11889 return 'rgb(' + rgbArr[0] + ',' + rgbArr[1] + ',' + rgbArr[2] + ')'; 11890 } 11891 11892 var hexToRGBArray_returnArray = []; 11893 /** 11894 * Convert a hexadecimal string to an array with three items, one each for 11895 * the red, blue, and green decimal values. 11896 * 11897 * @param {string} hex A hexadecimal string. 11898 * 11899 * @returns {Array.<number>} The converted Array of RGB values if `hex` is a 11900 * valid string, or an Array of three 0's. 11901 * @private 11902 */ 11903 function hexToRGBArray (hex) { 11904 11905 hex = hex.replace(/#/, ''); 11906 11907 // If the string is a shorthand three digit hex notation, normalize it to 11908 // the standard six digit notation 11909 if (hex.length === 3) { 11910 hex = hex.split(''); 11911 hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; 11912 } 11913 11914 hexToRGBArray_returnArray[0] = hexToDec(hex.substr(0, 2)); 11915 hexToRGBArray_returnArray[1] = hexToDec(hex.substr(2, 2)); 11916 hexToRGBArray_returnArray[2] = hexToDec(hex.substr(4, 2)); 11917 11918 return hexToRGBArray_returnArray; 11919 } 11920 11921 /** 11922 * Convert a base-16 number to base-10. 11923 * 11924 * @param {Number|String} hex The value to convert 11925 * 11926 * @returns {Number} The base-10 equivalent of `hex`. 11927 * @private 11928 */ 11929 function hexToDec (hex) { 11930 return parseInt(hex, 16); 11931 } 11932 11933 /** 11934 * Runs a filter operation on all chunks of a string that match a RegExp 11935 * 11936 * @param {RegExp} pattern 11937 * @param {string} unfilteredString 11938 * @param {function(string)} filter 11939 * 11940 * @return {string} 11941 * @private 11942 */ 11943 function filterStringChunks (pattern, unfilteredString, filter) { 11944 var pattenMatches = unfilteredString.match(pattern); 11945 var filteredString = unfilteredString.replace(pattern, VALUE_PLACEHOLDER); 11946 11947 if (pattenMatches) { 11948 var pattenMatchesLength = pattenMatches.length; 11949 var currentChunk; 11950 11951 for (var i = 0; i < pattenMatchesLength; i++) { 11952 currentChunk = pattenMatches.shift(); 11953 filteredString = filteredString.replace( 11954 VALUE_PLACEHOLDER, filter(currentChunk)); 11955 } 11956 } 11957 11958 return filteredString; 11959 } 11960 11961 /** 11962 * Check for floating point values within rgb strings and rounds them. 11963 * 11964 * @param {string} formattedString 11965 * 11966 * @return {string} 11967 * @private 11968 */ 11969 function sanitizeRGBChunks (formattedString) { 11970 return filterStringChunks(R_RGB, formattedString, sanitizeRGBChunk); 11971 } 11972 11973 /** 11974 * @param {string} rgbChunk 11975 * 11976 * @return {string} 11977 * @private 11978 */ 11979 function sanitizeRGBChunk (rgbChunk) { 11980 var numbers = rgbChunk.match(R_UNFORMATTED_VALUES); 11981 var numbersLength = numbers.length; 11982 var sanitizedString = rgbChunk.match(R_RGB_PREFIX)[0]; 11983 11984 for (var i = 0; i < numbersLength; i++) { 11985 sanitizedString += parseInt(numbers[i], 10) + ','; 11986 } 11987 11988 sanitizedString = sanitizedString.slice(0, -1) + ')'; 11989 11990 return sanitizedString; 11991 } 11992 11993 /** 11994 * @param {Object} stateObject 11995 * 11996 * @return {Object} An Object of formatManifests that correspond to 11997 * the string properties of stateObject 11998 * @private 11999 */ 12000 function getFormatManifests (stateObject) { 12001 var manifestAccumulator = {}; 12002 12003 NGTweenable.each(stateObject, function (prop) { 12004 var currentProp = stateObject[prop]; 12005 12006 if (typeof currentProp === 'string') { 12007 var rawValues = getValuesFrom(currentProp); 12008 12009 manifestAccumulator[prop] = { 12010 'formatString': getFormatStringFrom(currentProp) 12011 ,'chunkNames': getFormatChunksFrom(rawValues, prop) 12012 }; 12013 } 12014 }); 12015 12016 return manifestAccumulator; 12017 } 12018 12019 /** 12020 * @param {Object} stateObject 12021 * @param {Object} formatManifests 12022 * @private 12023 */ 12024 function expandFormattedProperties (stateObject, formatManifests) { 12025 NGTweenable.each(formatManifests, function (prop) { 12026 var currentProp = stateObject[prop]; 12027 var rawValues = getValuesFrom(currentProp); 12028 var rawValuesLength = rawValues.length; 12029 12030 for (var i = 0; i < rawValuesLength; i++) { 12031 stateObject[formatManifests[prop].chunkNames[i]] = +rawValues[i]; 12032 } 12033 12034 delete stateObject[prop]; 12035 }); 12036 } 12037 12038 /** 12039 * @param {Object} stateObject 12040 * @param {Object} formatManifests 12041 * @private 12042 */ 12043 function collapseFormattedProperties (stateObject, formatManifests) { 12044 NGTweenable.each(formatManifests, function (prop) { 12045 var currentProp = stateObject[prop]; 12046 var formatChunks = extractPropertyChunks( 12047 stateObject, formatManifests[prop].chunkNames); 12048 var valuesList = getValuesList( 12049 formatChunks, formatManifests[prop].chunkNames); 12050 currentProp = getFormattedValues( 12051 formatManifests[prop].formatString, valuesList); 12052 stateObject[prop] = sanitizeRGBChunks(currentProp); 12053 }); 12054 } 12055 12056 /** 12057 * @param {Object} stateObject 12058 * @param {Array.<string>} chunkNames 12059 * 12060 * @return {Object} The extracted value chunks. 12061 * @private 12062 */ 12063 function extractPropertyChunks (stateObject, chunkNames) { 12064 var extractedValues = {}; 12065 var currentChunkName, chunkNamesLength = chunkNames.length; 12066 12067 for (var i = 0; i < chunkNamesLength; i++) { 12068 currentChunkName = chunkNames[i]; 12069 extractedValues[currentChunkName] = stateObject[currentChunkName]; 12070 delete stateObject[currentChunkName]; 12071 } 12072 12073 return extractedValues; 12074 } 12075 12076 var getValuesList_accumulator = []; 12077 /** 12078 * @param {Object} stateObject 12079 * @param {Array.<string>} chunkNames 12080 * 12081 * @return {Array.<number>} 12082 * @private 12083 */ 12084 function getValuesList (stateObject, chunkNames) { 12085 getValuesList_accumulator.length = 0; 12086 var chunkNamesLength = chunkNames.length; 12087 12088 for (var i = 0; i < chunkNamesLength; i++) { 12089 getValuesList_accumulator.push(stateObject[chunkNames[i]]); 12090 } 12091 12092 return getValuesList_accumulator; 12093 } 12094 12095 /** 12096 * @param {string} formatString 12097 * @param {Array.<number>} rawValues 12098 * 12099 * @return {string} 12100 * @private 12101 */ 12102 function getFormattedValues (formatString, rawValues) { 12103 var formattedValueString = formatString; 12104 var rawValuesLength = rawValues.length; 12105 12106 for (var i = 0; i < rawValuesLength; i++) { 12107 formattedValueString = formattedValueString.replace( 12108 VALUE_PLACEHOLDER, +rawValues[i].toFixed(4)); 12109 } 12110 12111 return formattedValueString; 12112 } 12113 12114 /** 12115 * Note: It's the duty of the caller to convert the Array elements of the 12116 * return value into numbers. This is a performance optimization. 12117 * 12118 * @param {string} formattedString 12119 * 12120 * @return {Array.<string>|null} 12121 * @private 12122 */ 12123 function getValuesFrom (formattedString) { 12124 return formattedString.match(R_UNFORMATTED_VALUES); 12125 } 12126 12127 /** 12128 * @param {Object} easingObject 12129 * @param {Object} tokenData 12130 * @private 12131 */ 12132 function expandEasingObject (easingObject, tokenData) { 12133 NGTweenable.each(tokenData, function (prop) { 12134 var currentProp = tokenData[prop]; 12135 var chunkNames = currentProp.chunkNames; 12136 var chunkLength = chunkNames.length; 12137 12138 var easing = easingObject[prop]; 12139 var i; 12140 12141 if (typeof easing === 'string') { 12142 var easingChunks = easing.split(' '); 12143 var lastEasingChunk = easingChunks[easingChunks.length - 1]; 12144 12145 for (i = 0; i < chunkLength; i++) { 12146 easingObject[chunkNames[i]] = easingChunks[i] || lastEasingChunk; 12147 } 12148 12149 } else { 12150 for (i = 0; i < chunkLength; i++) { 12151 easingObject[chunkNames[i]] = easing; 12152 } 12153 } 12154 12155 delete easingObject[prop]; 12156 }); 12157 } 12158 12159 /** 12160 * @param {Object} easingObject 12161 * @param {Object} tokenData 12162 * @private 12163 */ 12164 function collapseEasingObject (easingObject, tokenData) { 12165 NGTweenable.each(tokenData, function (prop) { 12166 var currentProp = tokenData[prop]; 12167 var chunkNames = currentProp.chunkNames; 12168 var chunkLength = chunkNames.length; 12169 12170 var firstEasing = easingObject[chunkNames[0]]; 12171 var typeofEasings = typeof firstEasing; 12172 12173 if (typeofEasings === 'string') { 12174 var composedEasingString = ''; 12175 12176 for (var i = 0; i < chunkLength; i++) { 12177 composedEasingString += ' ' + easingObject[chunkNames[i]]; 12178 delete easingObject[chunkNames[i]]; 12179 } 12180 12181 easingObject[prop] = composedEasingString.substr(1); 12182 } else { 12183 easingObject[prop] = firstEasing; 12184 } 12185 }); 12186 } 12187 12188 NGTweenable.prototype.filter.token = { 12189 'tweenCreated': function (currentState, fromState, toState, easingObject) { 12190 sanitizeObjectForHexProps(currentState); 12191 sanitizeObjectForHexProps(fromState); 12192 sanitizeObjectForHexProps(toState); 12193 this._tokenData = getFormatManifests(currentState); 12194 }, 12195 12196 'beforeTween': function (currentState, fromState, toState, easingObject) { 12197 expandEasingObject(easingObject, this._tokenData); 12198 expandFormattedProperties(currentState, this._tokenData); 12199 expandFormattedProperties(fromState, this._tokenData); 12200 expandFormattedProperties(toState, this._tokenData); 12201 }, 12202 12203 'afterTween': function (currentState, fromState, toState, easingObject) { 12204 collapseFormattedProperties(currentState, this._tokenData); 12205 collapseFormattedProperties(fromState, this._tokenData); 12206 collapseFormattedProperties(toState, this._tokenData); 12207 collapseEasingObject(easingObject, this._tokenData); 12208 } 12209 }; 12210 12211} (NGTweenable)); 12212 12213}).call(null); 12214 12215 12216 12217 12218//########################################################################################################################## 12219//## HAMMER.JS ############################################################################################################# 12220//########################################################################################################################## 12221 12222// HAMMER.JS 12223 12224// external module EMBEDED in nanogallery 12225// NGY BUILD: 12226// replace "Hammer" with "NGHammer" (case sensitive) 12227// replace "var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined;" with "var SUPPORT_POINTER_EVENTS = false;" 12228// replace "define.amd" with "define.amdDISABLED" 12229 12230 12231 12232/*! NGHammer.JS - v2.0.7 - 2016-04-22 12233 * http://hammerjs.github.io/ 12234 * 12235 * Copyright (c) 2016 Jorik Tangelder; 12236 * Licensed under the MIT license */ 12237(function(window, document, exportName, undefined) { 12238 'use strict'; 12239 12240var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o']; 12241var TEST_ELEMENT = document.createElement('div'); 12242 12243var TYPE_FUNCTION = 'function'; 12244 12245var round = Math.round; 12246var abs = Math.abs; 12247var now = Date.now; 12248 12249/** 12250 * set a timeout with a given scope 12251 * @param {Function} fn 12252 * @param {Number} timeout 12253 * @param {Object} context 12254 * @returns {number} 12255 */ 12256function setTimeoutContext(fn, timeout, context) { 12257 return setTimeout(bindFn(fn, context), timeout); 12258} 12259 12260/** 12261 * if the argument is an array, we want to execute the fn on each entry 12262 * if it aint an array we don't want to do a thing. 12263 * this is used by all the methods that accept a single and array argument. 12264 * @param {*|Array} arg 12265 * @param {String} fn 12266 * @param {Object} [context] 12267 * @returns {Boolean} 12268 */ 12269function invokeArrayArg(arg, fn, context) { 12270 if (Array.isArray(arg)) { 12271 each(arg, context[fn], context); 12272 return true; 12273 } 12274 return false; 12275} 12276 12277/** 12278 * walk objects and arrays 12279 * @param {Object} obj 12280 * @param {Function} iterator 12281 * @param {Object} context 12282 */ 12283function each(obj, iterator, context) { 12284 var i; 12285 12286 if (!obj) { 12287 return; 12288 } 12289 12290 if (obj.forEach) { 12291 obj.forEach(iterator, context); 12292 } else if (obj.length !== undefined) { 12293 i = 0; 12294 while (i < obj.length) { 12295 iterator.call(context, obj[i], i, obj); 12296 i++; 12297 } 12298 } else { 12299 for (i in obj) { 12300 obj.hasOwnProperty(i) && iterator.call(context, obj[i], i, obj); 12301 } 12302 } 12303} 12304 12305/** 12306 * wrap a method with a deprecation warning and stack trace 12307 * @param {Function} method 12308 * @param {String} name 12309 * @param {String} message 12310 * @returns {Function} A new function wrapping the supplied method. 12311 */ 12312function deprecate(method, name, message) { 12313 var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\n' + message + ' AT \n'; 12314 return function() { 12315 var e = new Error('get-stack-trace'); 12316 var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '') 12317 .replace(/^\s+at\s+/gm, '') 12318 .replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace'; 12319 12320 var log = window.console && (window.console.warn || window.console.log); 12321 if (log) { 12322 log.call(window.console, deprecationMessage, stack); 12323 } 12324 return method.apply(this, arguments); 12325 }; 12326} 12327 12328/** 12329 * extend object. 12330 * means that properties in dest will be overwritten by the ones in src. 12331 * @param {Object} target 12332 * @param {...Object} objects_to_assign 12333 * @returns {Object} target 12334 */ 12335var assign; 12336if (typeof Object.assign !== 'function') { 12337 assign = function assign(target) { 12338 if (target === undefined || target === null) { 12339 throw new TypeError('Cannot convert undefined or null to object'); 12340 } 12341 12342 var output = Object(target); 12343 for (var index = 1; index < arguments.length; index++) { 12344 var source = arguments[index]; 12345 if (source !== undefined && source !== null) { 12346 for (var nextKey in source) { 12347 if (source.hasOwnProperty(nextKey)) { 12348 output[nextKey] = source[nextKey]; 12349 } 12350 } 12351 } 12352 } 12353 return output; 12354 }; 12355} else { 12356 assign = Object.assign; 12357} 12358 12359/** 12360 * extend object. 12361 * means that properties in dest will be overwritten by the ones in src. 12362 * @param {Object} dest 12363 * @param {Object} src 12364 * @param {Boolean} [merge=false] 12365 * @returns {Object} dest 12366 */ 12367var extend = deprecate(function extend(dest, src, merge) { 12368 var keys = Object.keys(src); 12369 var i = 0; 12370 while (i < keys.length) { 12371 if (!merge || (merge && dest[keys[i]] === undefined)) { 12372 dest[keys[i]] = src[keys[i]]; 12373 } 12374 i++; 12375 } 12376 return dest; 12377}, 'extend', 'Use `assign`.'); 12378 12379/** 12380 * merge the values from src in the dest. 12381 * means that properties that exist in dest will not be overwritten by src 12382 * @param {Object} dest 12383 * @param {Object} src 12384 * @returns {Object} dest 12385 */ 12386var merge = deprecate(function merge(dest, src) { 12387 return extend(dest, src, true); 12388}, 'merge', 'Use `assign`.'); 12389 12390/** 12391 * simple class inheritance 12392 * @param {Function} child 12393 * @param {Function} base 12394 * @param {Object} [properties] 12395 */ 12396function inherit(child, base, properties) { 12397 var baseP = base.prototype, 12398 childP; 12399 12400 childP = child.prototype = Object.create(baseP); 12401 childP.constructor = child; 12402 childP._super = baseP; 12403 12404 if (properties) { 12405 assign(childP, properties); 12406 } 12407} 12408 12409/** 12410 * simple function bind 12411 * @param {Function} fn 12412 * @param {Object} context 12413 * @returns {Function} 12414 */ 12415function bindFn(fn, context) { 12416 return function boundFn() { 12417 return fn.apply(context, arguments); 12418 }; 12419} 12420 12421/** 12422 * let a boolean value also be a function that must return a boolean 12423 * this first item in args will be used as the context 12424 * @param {Boolean|Function} val 12425 * @param {Array} [args] 12426 * @returns {Boolean} 12427 */ 12428function boolOrFn(val, args) { 12429 if (typeof val == TYPE_FUNCTION) { 12430 return val.apply(args ? args[0] || undefined : undefined, args); 12431 } 12432 return val; 12433} 12434 12435/** 12436 * use the val2 when val1 is undefined 12437 * @param {*} val1 12438 * @param {*} val2 12439 * @returns {*} 12440 */ 12441function ifUndefined(val1, val2) { 12442 return (val1 === undefined) ? val2 : val1; 12443} 12444 12445/** 12446 * addEventListener with multiple events at once 12447 * @param {EventTarget} target 12448 * @param {String} types 12449 * @param {Function} handler 12450 */ 12451function addEventListeners(target, types, handler) { 12452 each(splitStr(types), function(type) { 12453 target.addEventListener(type, handler, false); 12454 }); 12455} 12456 12457/** 12458 * removeEventListener with multiple events at once 12459 * @param {EventTarget} target 12460 * @param {String} types 12461 * @param {Function} handler 12462 */ 12463function removeEventListeners(target, types, handler) { 12464 each(splitStr(types), function(type) { 12465 target.removeEventListener(type, handler, false); 12466 }); 12467} 12468 12469/** 12470 * find if a node is in the given parent 12471 * @method hasParent 12472 * @param {HTMLElement} node 12473 * @param {HTMLElement} parent 12474 * @return {Boolean} found 12475 */ 12476function hasParent(node, parent) { 12477 while (node) { 12478 if (node == parent) { 12479 return true; 12480 } 12481 node = node.parentNode; 12482 } 12483 return false; 12484} 12485 12486/** 12487 * small indexOf wrapper 12488 * @param {String} str 12489 * @param {String} find 12490 * @returns {Boolean} found 12491 */ 12492function inStr(str, find) { 12493 return str.indexOf(find) > -1; 12494} 12495 12496/** 12497 * split string on whitespace 12498 * @param {String} str 12499 * @returns {Array} words 12500 */ 12501function splitStr(str) { 12502 return str.trim().split(/\s+/g); 12503} 12504 12505/** 12506 * find if a array contains the object using indexOf or a simple polyFill 12507 * @param {Array} src 12508 * @param {String} find 12509 * @param {String} [findByKey] 12510 * @return {Boolean|Number} false when not found, or the index 12511 */ 12512function inArray(src, find, findByKey) { 12513 if (src.indexOf && !findByKey) { 12514 return src.indexOf(find); 12515 } else { 12516 var i = 0; 12517 while (i < src.length) { 12518 if ((findByKey && src[i][findByKey] == find) || (!findByKey && src[i] === find)) { 12519 return i; 12520 } 12521 i++; 12522 } 12523 return -1; 12524 } 12525} 12526 12527/** 12528 * convert array-like objects to real arrays 12529 * @param {Object} obj 12530 * @returns {Array} 12531 */ 12532function toArray(obj) { 12533 return Array.prototype.slice.call(obj, 0); 12534} 12535 12536/** 12537 * unique array with objects based on a key (like 'id') or just by the array's value 12538 * @param {Array} src [{id:1},{id:2},{id:1}] 12539 * @param {String} [key] 12540 * @param {Boolean} [sort=False] 12541 * @returns {Array} [{id:1},{id:2}] 12542 */ 12543function uniqueArray(src, key, sort) { 12544 var results = []; 12545 var values = []; 12546 var i = 0; 12547 12548 while (i < src.length) { 12549 var val = key ? src[i][key] : src[i]; 12550 if (inArray(values, val) < 0) { 12551 results.push(src[i]); 12552 } 12553 values[i] = val; 12554 i++; 12555 } 12556 12557 if (sort) { 12558 if (!key) { 12559 results = results.sort(); 12560 } else { 12561 results = results.sort(function sortUniqueArray(a, b) { 12562 return a[key] > b[key]; 12563 }); 12564 } 12565 } 12566 12567 return results; 12568} 12569 12570/** 12571 * get the prefixed property 12572 * @param {Object} obj 12573 * @param {String} property 12574 * @returns {String|Undefined} prefixed 12575 */ 12576function prefixed(obj, property) { 12577 var prefix, prop; 12578 var camelProp = property[0].toUpperCase() + property.slice(1); 12579 12580 var i = 0; 12581 while (i < VENDOR_PREFIXES.length) { 12582 prefix = VENDOR_PREFIXES[i]; 12583 prop = (prefix) ? prefix + camelProp : property; 12584 12585 if (prop in obj) { 12586 return prop; 12587 } 12588 i++; 12589 } 12590 return undefined; 12591} 12592 12593/** 12594 * get a unique id 12595 * @returns {number} uniqueId 12596 */ 12597var _uniqueId = 1; 12598function uniqueId() { 12599 return _uniqueId++; 12600} 12601 12602/** 12603 * get the window object of an element 12604 * @param {HTMLElement} element 12605 * @returns {DocumentView|Window} 12606 */ 12607function getWindowForElement(element) { 12608 var doc = element.ownerDocument || element; 12609 return (doc.defaultView || doc.parentWindow || window); 12610} 12611 12612var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i; 12613 12614var SUPPORT_TOUCH = ('ontouchstart' in window); 12615// var SUPPORT_POINTER_EVENTS = prefixed(window, 'PointerEvent') !== undefined; 12616var SUPPORT_POINTER_EVENTS = false; 12617var SUPPORT_ONLY_TOUCH = SUPPORT_TOUCH && MOBILE_REGEX.test(navigator.userAgent); 12618 12619var INPUT_TYPE_TOUCH = 'touch'; 12620var INPUT_TYPE_PEN = 'pen'; 12621var INPUT_TYPE_MOUSE = 'mouse'; 12622var INPUT_TYPE_KINECT = 'kinect'; 12623 12624var COMPUTE_INTERVAL = 25; 12625 12626var INPUT_START = 1; 12627var INPUT_MOVE = 2; 12628var INPUT_END = 4; 12629var INPUT_CANCEL = 8; 12630 12631var DIRECTION_NONE = 1; 12632var DIRECTION_LEFT = 2; 12633var DIRECTION_RIGHT = 4; 12634var DIRECTION_UP = 8; 12635var DIRECTION_DOWN = 16; 12636 12637var DIRECTION_HORIZONTAL = DIRECTION_LEFT | DIRECTION_RIGHT; 12638var DIRECTION_VERTICAL = DIRECTION_UP | DIRECTION_DOWN; 12639var DIRECTION_ALL = DIRECTION_HORIZONTAL | DIRECTION_VERTICAL; 12640 12641var PROPS_XY = ['x', 'y']; 12642var PROPS_CLIENT_XY = ['clientX', 'clientY']; 12643 12644/** 12645 * create new input type manager 12646 * @param {Manager} manager 12647 * @param {Function} callback 12648 * @returns {Input} 12649 * @constructor 12650 */ 12651function Input(manager, callback) { 12652 var self = this; 12653 this.manager = manager; 12654 this.callback = callback; 12655 this.element = manager.element; 12656 this.target = manager.options.inputTarget; 12657 12658 // smaller wrapper around the handler, for the scope and the enabled state of the manager, 12659 // so when disabled the input events are completely bypassed. 12660 this.domHandler = function(ev) { 12661 if (boolOrFn(manager.options.enable, [manager])) { 12662 self.handler(ev); 12663 } 12664 }; 12665 12666 this.init(); 12667 12668} 12669 12670Input.prototype = { 12671 /** 12672 * should handle the inputEvent data and trigger the callback 12673 * @virtual 12674 */ 12675 handler: function() { }, 12676 12677 /** 12678 * bind the events 12679 */ 12680 init: function() { 12681 this.evEl && addEventListeners(this.element, this.evEl, this.domHandler); 12682 this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler); 12683 this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); 12684 }, 12685 12686 /** 12687 * unbind the events 12688 */ 12689 destroy: function() { 12690 this.evEl && removeEventListeners(this.element, this.evEl, this.domHandler); 12691 this.evTarget && removeEventListeners(this.target, this.evTarget, this.domHandler); 12692 this.evWin && removeEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler); 12693 } 12694}; 12695 12696/** 12697 * create new input type manager 12698 * called by the Manager constructor 12699 * @param {NGHammer} manager 12700 * @returns {Input} 12701 */ 12702function createInputInstance(manager) { 12703 var Type; 12704 var inputClass = manager.options.inputClass; 12705 12706 if (inputClass) { 12707 Type = inputClass; 12708 } else if (SUPPORT_POINTER_EVENTS) { 12709 Type = PointerEventInput; 12710 } else if (SUPPORT_ONLY_TOUCH) { 12711 Type = TouchInput; 12712 } else if (!SUPPORT_TOUCH) { 12713 Type = MouseInput; 12714 } else { 12715 Type = TouchMouseInput; 12716 } 12717 return new (Type)(manager, inputHandler); 12718} 12719 12720/** 12721 * handle input events 12722 * @param {Manager} manager 12723 * @param {String} eventType 12724 * @param {Object} input 12725 */ 12726function inputHandler(manager, eventType, input) { 12727 var pointersLen = input.pointers.length; 12728 var changedPointersLen = input.changedPointers.length; 12729 var isFirst = (eventType & INPUT_START && (pointersLen - changedPointersLen === 0)); 12730 var isFinal = (eventType & (INPUT_END | INPUT_CANCEL) && (pointersLen - changedPointersLen === 0)); 12731 12732 input.isFirst = !!isFirst; 12733 input.isFinal = !!isFinal; 12734 12735 if (isFirst) { 12736 manager.session = {}; 12737 } 12738 12739 // source event is the normalized value of the domEvents 12740 // like 'touchstart, mouseup, pointerdown' 12741 input.eventType = eventType; 12742 12743 // compute scale, rotation etc 12744 computeInputData(manager, input); 12745 12746 // emit secret event 12747 manager.emit('hammer.input', input); 12748 12749 manager.recognize(input); 12750 manager.session.prevInput = input; 12751} 12752 12753/** 12754 * extend the data with some usable properties like scale, rotate, velocity etc 12755 * @param {Object} manager 12756 * @param {Object} input 12757 */ 12758function computeInputData(manager, input) { 12759 var session = manager.session; 12760 var pointers = input.pointers; 12761 var pointersLength = pointers.length; 12762 12763 // store the first input to calculate the distance and direction 12764 if (!session.firstInput) { 12765 session.firstInput = simpleCloneInputData(input); 12766 } 12767 12768 // to compute scale and rotation we need to store the multiple touches 12769 if (pointersLength > 1 && !session.firstMultiple) { 12770 session.firstMultiple = simpleCloneInputData(input); 12771 } else if (pointersLength === 1) { 12772 session.firstMultiple = false; 12773 } 12774 12775 var firstInput = session.firstInput; 12776 var firstMultiple = session.firstMultiple; 12777 var offsetCenter = firstMultiple ? firstMultiple.center : firstInput.center; 12778 12779 var center = input.center = getCenter(pointers); 12780 input.timeStamp = now(); 12781 input.deltaTime = input.timeStamp - firstInput.timeStamp; 12782 12783 input.angle = getAngle(offsetCenter, center); 12784 input.distance = getDistance(offsetCenter, center); 12785 12786 computeDeltaXY(session, input); 12787 input.offsetDirection = getDirection(input.deltaX, input.deltaY); 12788 12789 var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY); 12790 input.overallVelocityX = overallVelocity.x; 12791 input.overallVelocityY = overallVelocity.y; 12792 input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y; 12793 12794 input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1; 12795 input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0; 12796 12797 input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length > 12798 session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers); 12799 12800 computeIntervalInputData(session, input); 12801 12802 // find the correct target 12803 var target = manager.element; 12804 if (hasParent(input.srcEvent.target, target)) { 12805 target = input.srcEvent.target; 12806 } 12807 input.target = target; 12808} 12809 12810function computeDeltaXY(session, input) { 12811 var center = input.center; 12812 var offset = session.offsetDelta || {}; 12813 var prevDelta = session.prevDelta || {}; 12814 var prevInput = session.prevInput || {}; 12815 12816 if (input.eventType === INPUT_START || prevInput.eventType === INPUT_END) { 12817 prevDelta = session.prevDelta = { 12818 x: prevInput.deltaX || 0, 12819 y: prevInput.deltaY || 0 12820 }; 12821 12822 offset = session.offsetDelta = { 12823 x: center.x, 12824 y: center.y 12825 }; 12826 } 12827 12828 input.deltaX = prevDelta.x + (center.x - offset.x); 12829 input.deltaY = prevDelta.y + (center.y - offset.y); 12830} 12831 12832/** 12833 * velocity is calculated every x ms 12834 * @param {Object} session 12835 * @param {Object} input 12836 */ 12837function computeIntervalInputData(session, input) { 12838 var last = session.lastInterval || input, 12839 deltaTime = input.timeStamp - last.timeStamp, 12840 velocity, velocityX, velocityY, direction; 12841 12842 if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) { 12843 var deltaX = input.deltaX - last.deltaX; 12844 var deltaY = input.deltaY - last.deltaY; 12845 12846 var v = getVelocity(deltaTime, deltaX, deltaY); 12847 velocityX = v.x; 12848 velocityY = v.y; 12849 velocity = (abs(v.x) > abs(v.y)) ? v.x : v.y; 12850 direction = getDirection(deltaX, deltaY); 12851 12852 session.lastInterval = input; 12853 } else { 12854 // use latest velocity info if it doesn't overtake a minimum period 12855 velocity = last.velocity; 12856 velocityX = last.velocityX; 12857 velocityY = last.velocityY; 12858 direction = last.direction; 12859 } 12860 12861 input.velocity = velocity; 12862 input.velocityX = velocityX; 12863 input.velocityY = velocityY; 12864 input.direction = direction; 12865} 12866 12867/** 12868 * create a simple clone from the input used for storage of firstInput and firstMultiple 12869 * @param {Object} input 12870 * @returns {Object} clonedInputData 12871 */ 12872function simpleCloneInputData(input) { 12873 // make a simple copy of the pointers because we will get a reference if we don't 12874 // we only need clientXY for the calculations 12875 var pointers = []; 12876 var i = 0; 12877 while (i < input.pointers.length) { 12878 pointers[i] = { 12879 clientX: round(input.pointers[i].clientX), 12880 clientY: round(input.pointers[i].clientY) 12881 }; 12882 i++; 12883 } 12884 12885 return { 12886 timeStamp: now(), 12887 pointers: pointers, 12888 center: getCenter(pointers), 12889 deltaX: input.deltaX, 12890 deltaY: input.deltaY 12891 }; 12892} 12893 12894/** 12895 * get the center of all the pointers 12896 * @param {Array} pointers 12897 * @return {Object} center contains `x` and `y` properties 12898 */ 12899function getCenter(pointers) { 12900 var pointersLength = pointers.length; 12901 12902 // no need to loop when only one touch 12903 if (pointersLength === 1) { 12904 return { 12905 x: round(pointers[0].clientX), 12906 y: round(pointers[0].clientY) 12907 }; 12908 } 12909 12910 var x = 0, y = 0, i = 0; 12911 while (i < pointersLength) { 12912 x += pointers[i].clientX; 12913 y += pointers[i].clientY; 12914 i++; 12915 } 12916 12917 return { 12918 x: round(x / pointersLength), 12919 y: round(y / pointersLength) 12920 }; 12921} 12922 12923/** 12924 * calculate the velocity between two points. unit is in px per ms. 12925 * @param {Number} deltaTime 12926 * @param {Number} x 12927 * @param {Number} y 12928 * @return {Object} velocity `x` and `y` 12929 */ 12930function getVelocity(deltaTime, x, y) { 12931 return { 12932 x: x / deltaTime || 0, 12933 y: y / deltaTime || 0 12934 }; 12935} 12936 12937/** 12938 * get the direction between two points 12939 * @param {Number} x 12940 * @param {Number} y 12941 * @return {Number} direction 12942 */ 12943function getDirection(x, y) { 12944 if (x === y) { 12945 return DIRECTION_NONE; 12946 } 12947 12948 if (abs(x) >= abs(y)) { 12949 return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT; 12950 } 12951 return y < 0 ? DIRECTION_UP : DIRECTION_DOWN; 12952} 12953 12954/** 12955 * calculate the absolute distance between two points 12956 * @param {Object} p1 {x, y} 12957 * @param {Object} p2 {x, y} 12958 * @param {Array} [props] containing x and y keys 12959 * @return {Number} distance 12960 */ 12961function getDistance(p1, p2, props) { 12962 if (!props) { 12963 props = PROPS_XY; 12964 } 12965 var x = p2[props[0]] - p1[props[0]], 12966 y = p2[props[1]] - p1[props[1]]; 12967 12968 return Math.sqrt((x * x) + (y * y)); 12969} 12970 12971/** 12972 * calculate the angle between two coordinates 12973 * @param {Object} p1 12974 * @param {Object} p2 12975 * @param {Array} [props] containing x and y keys 12976 * @return {Number} angle 12977 */ 12978function getAngle(p1, p2, props) { 12979 if (!props) { 12980 props = PROPS_XY; 12981 } 12982 var x = p2[props[0]] - p1[props[0]], 12983 y = p2[props[1]] - p1[props[1]]; 12984 return Math.atan2(y, x) * 180 / Math.PI; 12985} 12986 12987/** 12988 * calculate the rotation degrees between two pointersets 12989 * @param {Array} start array of pointers 12990 * @param {Array} end array of pointers 12991 * @return {Number} rotation 12992 */ 12993function getRotation(start, end) { 12994 return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY); 12995} 12996 12997/** 12998 * calculate the scale factor between two pointersets 12999 * no scale is 1, and goes down to 0 when pinched together, and bigger when pinched out 13000 * @param {Array} start array of pointers 13001 * @param {Array} end array of pointers 13002 * @return {Number} scale 13003 */ 13004function getScale(start, end) { 13005 return getDistance(end[0], end[1], PROPS_CLIENT_XY) / getDistance(start[0], start[1], PROPS_CLIENT_XY); 13006} 13007 13008var MOUSE_INPUT_MAP = { 13009 mousedown: INPUT_START, 13010 mousemove: INPUT_MOVE, 13011 mouseup: INPUT_END 13012}; 13013 13014var MOUSE_ELEMENT_EVENTS = 'mousedown'; 13015var MOUSE_WINDOW_EVENTS = 'mousemove mouseup'; 13016 13017/** 13018 * Mouse events input 13019 * @constructor 13020 * @extends Input 13021 */ 13022function MouseInput() { 13023 this.evEl = MOUSE_ELEMENT_EVENTS; 13024 this.evWin = MOUSE_WINDOW_EVENTS; 13025 13026 this.pressed = false; // mousedown state 13027 13028 Input.apply(this, arguments); 13029} 13030 13031inherit(MouseInput, Input, { 13032 /** 13033 * handle mouse events 13034 * @param {Object} ev 13035 */ 13036 handler: function MEhandler(ev) { 13037 var eventType = MOUSE_INPUT_MAP[ev.type]; 13038 13039 // on start we want to have the left mouse button down 13040 if (eventType & INPUT_START && ev.button === 0) { 13041 this.pressed = true; 13042 } 13043 13044 if (eventType & INPUT_MOVE && ev.which !== 1) { 13045 eventType = INPUT_END; 13046 } 13047 13048 // mouse must be down 13049 if (!this.pressed) { 13050 return; 13051 } 13052 13053 if (eventType & INPUT_END) { 13054 this.pressed = false; 13055 } 13056 13057 this.callback(this.manager, eventType, { 13058 pointers: [ev], 13059 changedPointers: [ev], 13060 pointerType: INPUT_TYPE_MOUSE, 13061 srcEvent: ev 13062 }); 13063 } 13064}); 13065 13066var POINTER_INPUT_MAP = { 13067 pointerdown: INPUT_START, 13068 pointermove: INPUT_MOVE, 13069 pointerup: INPUT_END, 13070 pointercancel: INPUT_CANCEL, 13071 pointerout: INPUT_CANCEL 13072}; 13073 13074// in IE10 the pointer types is defined as an enum 13075var IE10_POINTER_TYPE_ENUM = { 13076 2: INPUT_TYPE_TOUCH, 13077 3: INPUT_TYPE_PEN, 13078 4: INPUT_TYPE_MOUSE, 13079 5: INPUT_TYPE_KINECT // see https://twitter.com/jacobrossi/status/480596438489890816 13080}; 13081 13082var POINTER_ELEMENT_EVENTS = 'pointerdown'; 13083var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel'; 13084 13085// IE10 has prefixed support, and case-sensitive 13086if (window.MSPointerEvent && !window.PointerEvent) { 13087 POINTER_ELEMENT_EVENTS = 'MSPointerDown'; 13088 POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel'; 13089} 13090 13091/** 13092 * Pointer events input 13093 * @constructor 13094 * @extends Input 13095 */ 13096function PointerEventInput() { 13097 this.evEl = POINTER_ELEMENT_EVENTS; 13098 this.evWin = POINTER_WINDOW_EVENTS; 13099 13100 Input.apply(this, arguments); 13101 13102 this.store = (this.manager.session.pointerEvents = []); 13103} 13104 13105inherit(PointerEventInput, Input, { 13106 /** 13107 * handle mouse events 13108 * @param {Object} ev 13109 */ 13110 handler: function PEhandler(ev) { 13111 var store = this.store; 13112 var removePointer = false; 13113 13114 var eventTypeNormalized = ev.type.toLowerCase().replace('ms', ''); 13115 var eventType = POINTER_INPUT_MAP[eventTypeNormalized]; 13116 var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType; 13117 13118 var isTouch = (pointerType == INPUT_TYPE_TOUCH); 13119 13120 // get index of the event in the store 13121 var storeIndex = inArray(store, ev.pointerId, 'pointerId'); 13122 13123 // start and mouse must be down 13124 if (eventType & INPUT_START && (ev.button === 0 || isTouch)) { 13125 if (storeIndex < 0) { 13126 store.push(ev); 13127 storeIndex = store.length - 1; 13128 } 13129 } else if (eventType & (INPUT_END | INPUT_CANCEL)) { 13130 removePointer = true; 13131 } 13132 13133 // it not found, so the pointer hasn't been down (so it's probably a hover) 13134 if (storeIndex < 0) { 13135 return; 13136 } 13137 13138 // update the event in the store 13139 store[storeIndex] = ev; 13140 13141 this.callback(this.manager, eventType, { 13142 pointers: store, 13143 changedPointers: [ev], 13144 pointerType: pointerType, 13145 srcEvent: ev 13146 }); 13147 13148 if (removePointer) { 13149 // remove from the store 13150 store.splice(storeIndex, 1); 13151 } 13152 } 13153}); 13154 13155var SINGLE_TOUCH_INPUT_MAP = { 13156 touchstart: INPUT_START, 13157 touchmove: INPUT_MOVE, 13158 touchend: INPUT_END, 13159 touchcancel: INPUT_CANCEL 13160}; 13161 13162var SINGLE_TOUCH_TARGET_EVENTS = 'touchstart'; 13163var SINGLE_TOUCH_WINDOW_EVENTS = 'touchstart touchmove touchend touchcancel'; 13164 13165/** 13166 * Touch events input 13167 * @constructor 13168 * @extends Input 13169 */ 13170function SingleTouchInput() { 13171 this.evTarget = SINGLE_TOUCH_TARGET_EVENTS; 13172 this.evWin = SINGLE_TOUCH_WINDOW_EVENTS; 13173 this.started = false; 13174 13175 Input.apply(this, arguments); 13176} 13177 13178inherit(SingleTouchInput, Input, { 13179 handler: function TEhandler(ev) { 13180 var type = SINGLE_TOUCH_INPUT_MAP[ev.type]; 13181 13182 // should we handle the touch events? 13183 if (type === INPUT_START) { 13184 this.started = true; 13185 } 13186 13187 if (!this.started) { 13188 return; 13189 } 13190 13191 var touches = normalizeSingleTouches.call(this, ev, type); 13192 13193 // when done, reset the started state 13194 if (type & (INPUT_END | INPUT_CANCEL) && touches[0].length - touches[1].length === 0) { 13195 this.started = false; 13196 } 13197 13198 this.callback(this.manager, type, { 13199 pointers: touches[0], 13200 changedPointers: touches[1], 13201 pointerType: INPUT_TYPE_TOUCH, 13202 srcEvent: ev 13203 }); 13204 } 13205}); 13206 13207/** 13208 * @this {TouchInput} 13209 * @param {Object} ev 13210 * @param {Number} type flag 13211 * @returns {undefined|Array} [all, changed] 13212 */ 13213function normalizeSingleTouches(ev, type) { 13214 var all = toArray(ev.touches); 13215 var changed = toArray(ev.changedTouches); 13216 13217 if (type & (INPUT_END | INPUT_CANCEL)) { 13218 all = uniqueArray(all.concat(changed), 'identifier', true); 13219 } 13220 13221 return [all, changed]; 13222} 13223 13224var TOUCH_INPUT_MAP = { 13225 touchstart: INPUT_START, 13226 touchmove: INPUT_MOVE, 13227 touchend: INPUT_END, 13228 touchcancel: INPUT_CANCEL 13229}; 13230 13231var TOUCH_TARGET_EVENTS = 'touchstart touchmove touchend touchcancel'; 13232 13233/** 13234 * Multi-user touch events input 13235 * @constructor 13236 * @extends Input 13237 */ 13238function TouchInput() { 13239 this.evTarget = TOUCH_TARGET_EVENTS; 13240 this.targetIds = {}; 13241 13242 Input.apply(this, arguments); 13243} 13244 13245inherit(TouchInput, Input, { 13246 handler: function MTEhandler(ev) { 13247 var type = TOUCH_INPUT_MAP[ev.type]; 13248 var touches = getTouches.call(this, ev, type); 13249 if (!touches) { 13250 return; 13251 } 13252 13253 this.callback(this.manager, type, { 13254 pointers: touches[0], 13255 changedPointers: touches[1], 13256 pointerType: INPUT_TYPE_TOUCH, 13257 srcEvent: ev 13258 }); 13259 } 13260}); 13261 13262/** 13263 * @this {TouchInput} 13264 * @param {Object} ev 13265 * @param {Number} type flag 13266 * @returns {undefined|Array} [all, changed] 13267 */ 13268function getTouches(ev, type) { 13269 var allTouches = toArray(ev.touches); 13270 var targetIds = this.targetIds; 13271 13272 // when there is only one touch, the process can be simplified 13273 if (type & (INPUT_START | INPUT_MOVE) && allTouches.length === 1) { 13274 targetIds[allTouches[0].identifier] = true; 13275 return [allTouches, allTouches]; 13276 } 13277 13278 var i, 13279 targetTouches, 13280 changedTouches = toArray(ev.changedTouches), 13281 changedTargetTouches = [], 13282 target = this.target; 13283 13284 // get target touches from touches 13285 targetTouches = allTouches.filter(function(touch) { 13286 return hasParent(touch.target, target); 13287 }); 13288 13289 // collect touches 13290 if (type === INPUT_START) { 13291 i = 0; 13292 while (i < targetTouches.length) { 13293 targetIds[targetTouches[i].identifier] = true; 13294 i++; 13295 } 13296 } 13297 13298 // filter changed touches to only contain touches that exist in the collected target ids 13299 i = 0; 13300 while (i < changedTouches.length) { 13301 if (targetIds[changedTouches[i].identifier]) { 13302 changedTargetTouches.push(changedTouches[i]); 13303 } 13304 13305 // cleanup removed touches 13306 if (type & (INPUT_END | INPUT_CANCEL)) { 13307 delete targetIds[changedTouches[i].identifier]; 13308 } 13309 i++; 13310 } 13311 13312 if (!changedTargetTouches.length) { 13313 return; 13314 } 13315 13316 return [ 13317 // merge targetTouches with changedTargetTouches so it contains ALL touches, including 'end' and 'cancel' 13318 uniqueArray(targetTouches.concat(changedTargetTouches), 'identifier', true), 13319 changedTargetTouches 13320 ]; 13321} 13322 13323/** 13324 * Combined touch and mouse input 13325 * 13326 * Touch has a higher priority then mouse, and while touching no mouse events are allowed. 13327 * This because touch devices also emit mouse events while doing a touch. 13328 * 13329 * @constructor 13330 * @extends Input 13331 */ 13332 13333var DEDUP_TIMEOUT = 2500; 13334var DEDUP_DISTANCE = 25; 13335 13336function TouchMouseInput() { 13337 Input.apply(this, arguments); 13338 13339 var handler = bindFn(this.handler, this); 13340 this.touch = new TouchInput(this.manager, handler); 13341 this.mouse = new MouseInput(this.manager, handler); 13342 13343 this.primaryTouch = null; 13344 this.lastTouches = []; 13345} 13346 13347inherit(TouchMouseInput, Input, { 13348 /** 13349 * handle mouse and touch events 13350 * @param {NGHammer} manager 13351 * @param {String} inputEvent 13352 * @param {Object} inputData 13353 */ 13354 handler: function TMEhandler(manager, inputEvent, inputData) { 13355 var isTouch = (inputData.pointerType == INPUT_TYPE_TOUCH), 13356 isMouse = (inputData.pointerType == INPUT_TYPE_MOUSE); 13357 13358 if (isMouse && inputData.sourceCapabilities && inputData.sourceCapabilities.firesTouchEvents) { 13359 return; 13360 } 13361 13362 // when we're in a touch event, record touches to de-dupe synthetic mouse event 13363 if (isTouch) { 13364 recordTouches.call(this, inputEvent, inputData); 13365 } else if (isMouse && isSyntheticEvent.call(this, inputData)) { 13366 return; 13367 } 13368 13369 this.callback(manager, inputEvent, inputData); 13370 }, 13371 13372 /** 13373 * remove the event listeners 13374 */ 13375 destroy: function destroy() { 13376 this.touch.destroy(); 13377 this.mouse.destroy(); 13378 } 13379}); 13380 13381function recordTouches(eventType, eventData) { 13382 if (eventType & INPUT_START) { 13383 this.primaryTouch = eventData.changedPointers[0].identifier; 13384 setLastTouch.call(this, eventData); 13385 } else if (eventType & (INPUT_END | INPUT_CANCEL)) { 13386 setLastTouch.call(this, eventData); 13387 } 13388} 13389 13390function setLastTouch(eventData) { 13391 var touch = eventData.changedPointers[0]; 13392 13393 if (touch.identifier === this.primaryTouch) { 13394 var lastTouch = {x: touch.clientX, y: touch.clientY}; 13395 this.lastTouches.push(lastTouch); 13396 var lts = this.lastTouches; 13397 var removeLastTouch = function() { 13398 var i = lts.indexOf(lastTouch); 13399 if (i > -1) { 13400 lts.splice(i, 1); 13401 } 13402 }; 13403 setTimeout(removeLastTouch, DEDUP_TIMEOUT); 13404 } 13405} 13406 13407function isSyntheticEvent(eventData) { 13408 var x = eventData.srcEvent.clientX, y = eventData.srcEvent.clientY; 13409 for (var i = 0; i < this.lastTouches.length; i++) { 13410 var t = this.lastTouches[i]; 13411 var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y); 13412 if (dx <= DEDUP_DISTANCE && dy <= DEDUP_DISTANCE) { 13413 return true; 13414 } 13415 } 13416 return false; 13417} 13418 13419var PREFIXED_TOUCH_ACTION = prefixed(TEST_ELEMENT.style, 'touchAction'); 13420var NATIVE_TOUCH_ACTION = PREFIXED_TOUCH_ACTION !== undefined; 13421 13422// magical touchAction value 13423var TOUCH_ACTION_COMPUTE = 'compute'; 13424var TOUCH_ACTION_AUTO = 'auto'; 13425var TOUCH_ACTION_MANIPULATION = 'manipulation'; // not implemented 13426var TOUCH_ACTION_NONE = 'none'; 13427var TOUCH_ACTION_PAN_X = 'pan-x'; 13428var TOUCH_ACTION_PAN_Y = 'pan-y'; 13429var TOUCH_ACTION_MAP = getTouchActionProps(); 13430 13431/** 13432 * Touch Action 13433 * sets the touchAction property or uses the js alternative 13434 * @param {Manager} manager 13435 * @param {String} value 13436 * @constructor 13437 */ 13438function TouchAction(manager, value) { 13439 this.manager = manager; 13440 this.set(value); 13441} 13442 13443TouchAction.prototype = { 13444 /** 13445 * set the touchAction value on the element or enable the polyfill 13446 * @param {String} value 13447 */ 13448 set: function(value) { 13449 // find out the touch-action by the event handlers 13450 if (value == TOUCH_ACTION_COMPUTE) { 13451 value = this.compute(); 13452 } 13453 13454 if (NATIVE_TOUCH_ACTION && this.manager.element.style && TOUCH_ACTION_MAP[value]) { 13455 this.manager.element.style[PREFIXED_TOUCH_ACTION] = value; 13456 } 13457 this.actions = value.toLowerCase().trim(); 13458 }, 13459 13460 /** 13461 * just re-set the touchAction value 13462 */ 13463 update: function() { 13464 this.set(this.manager.options.touchAction); 13465 }, 13466 13467 /** 13468 * compute the value for the touchAction property based on the recognizer's settings 13469 * @returns {String} value 13470 */ 13471 compute: function() { 13472 var actions = []; 13473 each(this.manager.recognizers, function(recognizer) { 13474 if (boolOrFn(recognizer.options.enable, [recognizer])) { 13475 actions = actions.concat(recognizer.getTouchAction()); 13476 } 13477 }); 13478 return cleanTouchActions(actions.join(' ')); 13479 }, 13480 13481 /** 13482 * this method is called on each input cycle and provides the preventing of the browser behavior 13483 * @param {Object} input 13484 */ 13485 preventDefaults: function(input) { 13486 var srcEvent = input.srcEvent; 13487 var direction = input.offsetDirection; 13488 13489 // if the touch action did prevented once this session 13490 if (this.manager.session.prevented) { 13491 srcEvent.preventDefault(); 13492 return; 13493 } 13494 13495 var actions = this.actions; 13496 var hasNone = inStr(actions, TOUCH_ACTION_NONE) && !TOUCH_ACTION_MAP[TOUCH_ACTION_NONE]; 13497 var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_Y]; 13498 var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X) && !TOUCH_ACTION_MAP[TOUCH_ACTION_PAN_X]; 13499 13500 if (hasNone) { 13501 //do not prevent defaults if this is a tap gesture 13502 13503 var isTapPointer = input.pointers.length === 1; 13504 var isTapMovement = input.distance < 2; 13505 var isTapTouchTime = input.deltaTime < 250; 13506 13507 if (isTapPointer && isTapMovement && isTapTouchTime) { 13508 return; 13509 } 13510 } 13511 13512 if (hasPanX && hasPanY) { 13513 // `pan-x pan-y` means browser handles all scrolling/panning, do not prevent 13514 return; 13515 } 13516 13517 if (hasNone || 13518 (hasPanY && direction & DIRECTION_HORIZONTAL) || 13519 (hasPanX && direction & DIRECTION_VERTICAL)) { 13520 return this.preventSrc(srcEvent); 13521 } 13522 }, 13523 13524 /** 13525 * call preventDefault to prevent the browser's default behavior (scrolling in most cases) 13526 * @param {Object} srcEvent 13527 */ 13528 preventSrc: function(srcEvent) { 13529 this.manager.session.prevented = true; 13530 srcEvent.preventDefault(); 13531 } 13532}; 13533 13534/** 13535 * when the touchActions are collected they are not a valid value, so we need to clean things up. * 13536 * @param {String} actions 13537 * @returns {*} 13538 */ 13539function cleanTouchActions(actions) { 13540 // none 13541 if (inStr(actions, TOUCH_ACTION_NONE)) { 13542 return TOUCH_ACTION_NONE; 13543 } 13544 13545 var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X); 13546 var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y); 13547 13548 // if both pan-x and pan-y are set (different recognizers 13549 // for different directions, e.g. horizontal pan but vertical swipe?) 13550 // we need none (as otherwise with pan-x pan-y combined none of these 13551 // recognizers will work, since the browser would handle all panning 13552 if (hasPanX && hasPanY) { 13553 return TOUCH_ACTION_NONE; 13554 } 13555 13556 // pan-x OR pan-y 13557 if (hasPanX || hasPanY) { 13558 return hasPanX ? TOUCH_ACTION_PAN_X : TOUCH_ACTION_PAN_Y; 13559 } 13560 13561 // manipulation 13562 if (inStr(actions, TOUCH_ACTION_MANIPULATION)) { 13563 return TOUCH_ACTION_MANIPULATION; 13564 } 13565 13566 return TOUCH_ACTION_AUTO; 13567} 13568 13569function getTouchActionProps() { 13570 if (!NATIVE_TOUCH_ACTION) { 13571 return false; 13572 } 13573 var touchMap = {}; 13574 var cssSupports = window.CSS && window.CSS.supports; 13575 ['auto', 'manipulation', 'pan-y', 'pan-x', 'pan-x pan-y', 'none'].forEach(function(val) { 13576 13577 // If css.supports is not supported but there is native touch-action assume it supports 13578 // all values. This is the case for IE 10 and 11. 13579 touchMap[val] = cssSupports ? window.CSS.supports('touch-action', val) : true; 13580 }); 13581 return touchMap; 13582} 13583 13584/** 13585 * Recognizer flow explained; * 13586 * All recognizers have the initial state of POSSIBLE when a input session starts. 13587 * The definition of a input session is from the first input until the last input, with all it's movement in it. * 13588 * Example session for mouse-input: mousedown -> mousemove -> mouseup 13589 * 13590 * On each recognizing cycle (see Manager.recognize) the .recognize() method is executed 13591 * which determines with state it should be. 13592 * 13593 * If the recognizer has the state FAILED, CANCELLED or RECOGNIZED (equals ENDED), it is reset to 13594 * POSSIBLE to give it another change on the next cycle. 13595 * 13596 * Possible 13597 * | 13598 * +-----+---------------+ 13599 * | | 13600 * +-----+-----+ | 13601 * | | | 13602 * Failed Cancelled | 13603 * +-------+------+ 13604 * | | 13605 * Recognized Began 13606 * | 13607 * Changed 13608 * | 13609 * Ended/Recognized 13610 */ 13611var STATE_POSSIBLE = 1; 13612var STATE_BEGAN = 2; 13613var STATE_CHANGED = 4; 13614var STATE_ENDED = 8; 13615var STATE_RECOGNIZED = STATE_ENDED; 13616var STATE_CANCELLED = 16; 13617var STATE_FAILED = 32; 13618 13619/** 13620 * Recognizer 13621 * Every recognizer needs to extend from this class. 13622 * @constructor 13623 * @param {Object} options 13624 */ 13625function Recognizer(options) { 13626 this.options = assign({}, this.defaults, options || {}); 13627 13628 this.id = uniqueId(); 13629 13630 this.manager = null; 13631 13632 // default is enable true 13633 this.options.enable = ifUndefined(this.options.enable, true); 13634 13635 this.state = STATE_POSSIBLE; 13636 13637 this.simultaneous = {}; 13638 this.requireFail = []; 13639} 13640 13641Recognizer.prototype = { 13642 /** 13643 * @virtual 13644 * @type {Object} 13645 */ 13646 defaults: {}, 13647 13648 /** 13649 * set options 13650 * @param {Object} options 13651 * @return {Recognizer} 13652 */ 13653 set: function(options) { 13654 assign(this.options, options); 13655 13656 // also update the touchAction, in case something changed about the directions/enabled state 13657 this.manager && this.manager.touchAction.update(); 13658 return this; 13659 }, 13660 13661 /** 13662 * recognize simultaneous with an other recognizer. 13663 * @param {Recognizer} otherRecognizer 13664 * @returns {Recognizer} this 13665 */ 13666 recognizeWith: function(otherRecognizer) { 13667 if (invokeArrayArg(otherRecognizer, 'recognizeWith', this)) { 13668 return this; 13669 } 13670 13671 var simultaneous = this.simultaneous; 13672 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); 13673 if (!simultaneous[otherRecognizer.id]) { 13674 simultaneous[otherRecognizer.id] = otherRecognizer; 13675 otherRecognizer.recognizeWith(this); 13676 } 13677 return this; 13678 }, 13679 13680 /** 13681 * drop the simultaneous link. it doesnt remove the link on the other recognizer. 13682 * @param {Recognizer} otherRecognizer 13683 * @returns {Recognizer} this 13684 */ 13685 dropRecognizeWith: function(otherRecognizer) { 13686 if (invokeArrayArg(otherRecognizer, 'dropRecognizeWith', this)) { 13687 return this; 13688 } 13689 13690 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); 13691 delete this.simultaneous[otherRecognizer.id]; 13692 return this; 13693 }, 13694 13695 /** 13696 * recognizer can only run when an other is failing 13697 * @param {Recognizer} otherRecognizer 13698 * @returns {Recognizer} this 13699 */ 13700 requireFailure: function(otherRecognizer) { 13701 if (invokeArrayArg(otherRecognizer, 'requireFailure', this)) { 13702 return this; 13703 } 13704 13705 var requireFail = this.requireFail; 13706 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); 13707 if (inArray(requireFail, otherRecognizer) === -1) { 13708 requireFail.push(otherRecognizer); 13709 otherRecognizer.requireFailure(this); 13710 } 13711 return this; 13712 }, 13713 13714 /** 13715 * drop the requireFailure link. it does not remove the link on the other recognizer. 13716 * @param {Recognizer} otherRecognizer 13717 * @returns {Recognizer} this 13718 */ 13719 dropRequireFailure: function(otherRecognizer) { 13720 if (invokeArrayArg(otherRecognizer, 'dropRequireFailure', this)) { 13721 return this; 13722 } 13723 13724 otherRecognizer = getRecognizerByNameIfManager(otherRecognizer, this); 13725 var index = inArray(this.requireFail, otherRecognizer); 13726 if (index > -1) { 13727 this.requireFail.splice(index, 1); 13728 } 13729 return this; 13730 }, 13731 13732 /** 13733 * has require failures boolean 13734 * @returns {boolean} 13735 */ 13736 hasRequireFailures: function() { 13737 return this.requireFail.length > 0; 13738 }, 13739 13740 /** 13741 * if the recognizer can recognize simultaneous with an other recognizer 13742 * @param {Recognizer} otherRecognizer 13743 * @returns {Boolean} 13744 */ 13745 canRecognizeWith: function(otherRecognizer) { 13746 return !!this.simultaneous[otherRecognizer.id]; 13747 }, 13748 13749 /** 13750 * You should use `tryEmit` instead of `emit` directly to check 13751 * that all the needed recognizers has failed before emitting. 13752 * @param {Object} input 13753 */ 13754 emit: function(input) { 13755 var self = this; 13756 var state = this.state; 13757 13758 function emit(event) { 13759 self.manager.emit(event, input); 13760 } 13761 13762 // 'panstart' and 'panmove' 13763 if (state < STATE_ENDED) { 13764 emit(self.options.event + stateStr(state)); 13765 } 13766 13767 emit(self.options.event); // simple 'eventName' events 13768 13769 if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...) 13770 emit(input.additionalEvent); 13771 } 13772 13773 // panend and pancancel 13774 if (state >= STATE_ENDED) { 13775 emit(self.options.event + stateStr(state)); 13776 } 13777 }, 13778 13779 /** 13780 * Check that all the require failure recognizers has failed, 13781 * if true, it emits a gesture event, 13782 * otherwise, setup the state to FAILED. 13783 * @param {Object} input 13784 */ 13785 tryEmit: function(input) { 13786 if (this.canEmit()) { 13787 return this.emit(input); 13788 } 13789 // it's failing anyway 13790 this.state = STATE_FAILED; 13791 }, 13792 13793 /** 13794 * can we emit? 13795 * @returns {boolean} 13796 */ 13797 canEmit: function() { 13798 var i = 0; 13799 while (i < this.requireFail.length) { 13800 if (!(this.requireFail[i].state & (STATE_FAILED | STATE_POSSIBLE))) { 13801 return false; 13802 } 13803 i++; 13804 } 13805 return true; 13806 }, 13807 13808 /** 13809 * update the recognizer 13810 * @param {Object} inputData 13811 */ 13812 recognize: function(inputData) { 13813 // make a new copy of the inputData 13814 // so we can change the inputData without messing up the other recognizers 13815 var inputDataClone = assign({}, inputData); 13816 13817 // is is enabled and allow recognizing? 13818 if (!boolOrFn(this.options.enable, [this, inputDataClone])) { 13819 this.reset(); 13820 this.state = STATE_FAILED; 13821 return; 13822 } 13823 13824 // reset when we've reached the end 13825 if (this.state & (STATE_RECOGNIZED | STATE_CANCELLED | STATE_FAILED)) { 13826 this.state = STATE_POSSIBLE; 13827 } 13828 13829 this.state = this.process(inputDataClone); 13830 13831 // the recognizer has recognized a gesture 13832 // so trigger an event 13833 if (this.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED | STATE_CANCELLED)) { 13834 this.tryEmit(inputDataClone); 13835 } 13836 }, 13837 13838 /** 13839 * return the state of the recognizer 13840 * the actual recognizing happens in this method 13841 * @virtual 13842 * @param {Object} inputData 13843 * @returns {Const} STATE 13844 */ 13845 process: function(inputData) { }, // jshint ignore:line 13846 13847 /** 13848 * return the preferred touch-action 13849 * @virtual 13850 * @returns {Array} 13851 */ 13852 getTouchAction: function() { }, 13853 13854 /** 13855 * called when the gesture isn't allowed to recognize 13856 * like when another is being recognized or it is disabled 13857 * @virtual 13858 */ 13859 reset: function() { } 13860}; 13861 13862/** 13863 * get a usable string, used as event postfix 13864 * @param {Const} state 13865 * @returns {String} state 13866 */ 13867function stateStr(state) { 13868 if (state & STATE_CANCELLED) { 13869 return 'cancel'; 13870 } else if (state & STATE_ENDED) { 13871 return 'end'; 13872 } else if (state & STATE_CHANGED) { 13873 return 'move'; 13874 } else if (state & STATE_BEGAN) { 13875 return 'start'; 13876 } 13877 return ''; 13878} 13879 13880/** 13881 * direction cons to string 13882 * @param {Const} direction 13883 * @returns {String} 13884 */ 13885function directionStr(direction) { 13886 if (direction == DIRECTION_DOWN) { 13887 return 'down'; 13888 } else if (direction == DIRECTION_UP) { 13889 return 'up'; 13890 } else if (direction == DIRECTION_LEFT) { 13891 return 'left'; 13892 } else if (direction == DIRECTION_RIGHT) { 13893 return 'right'; 13894 } 13895 return ''; 13896} 13897 13898/** 13899 * get a recognizer by name if it is bound to a manager 13900 * @param {Recognizer|String} otherRecognizer 13901 * @param {Recognizer} recognizer 13902 * @returns {Recognizer} 13903 */ 13904function getRecognizerByNameIfManager(otherRecognizer, recognizer) { 13905 var manager = recognizer.manager; 13906 if (manager) { 13907 return manager.get(otherRecognizer); 13908 } 13909 return otherRecognizer; 13910} 13911 13912/** 13913 * This recognizer is just used as a base for the simple attribute recognizers. 13914 * @constructor 13915 * @extends Recognizer 13916 */ 13917function AttrRecognizer() { 13918 Recognizer.apply(this, arguments); 13919} 13920 13921inherit(AttrRecognizer, Recognizer, { 13922 /** 13923 * @namespace 13924 * @memberof AttrRecognizer 13925 */ 13926 defaults: { 13927 /** 13928 * @type {Number} 13929 * @default 1 13930 */ 13931 pointers: 1 13932 }, 13933 13934 /** 13935 * Used to check if it the recognizer receives valid input, like input.distance > 10. 13936 * @memberof AttrRecognizer 13937 * @param {Object} input 13938 * @returns {Boolean} recognized 13939 */ 13940 attrTest: function(input) { 13941 var optionPointers = this.options.pointers; 13942 return optionPointers === 0 || input.pointers.length === optionPointers; 13943 }, 13944 13945 /** 13946 * Process the input and return the state for the recognizer 13947 * @memberof AttrRecognizer 13948 * @param {Object} input 13949 * @returns {*} State 13950 */ 13951 process: function(input) { 13952 var state = this.state; 13953 var eventType = input.eventType; 13954 13955 var isRecognized = state & (STATE_BEGAN | STATE_CHANGED); 13956 var isValid = this.attrTest(input); 13957 13958 // on cancel input and we've recognized before, return STATE_CANCELLED 13959 if (isRecognized && (eventType & INPUT_CANCEL || !isValid)) { 13960 return state | STATE_CANCELLED; 13961 } else if (isRecognized || isValid) { 13962 if (eventType & INPUT_END) { 13963 return state | STATE_ENDED; 13964 } else if (!(state & STATE_BEGAN)) { 13965 return STATE_BEGAN; 13966 } 13967 return state | STATE_CHANGED; 13968 } 13969 return STATE_FAILED; 13970 } 13971}); 13972 13973/** 13974 * Pan 13975 * Recognized when the pointer is down and moved in the allowed direction. 13976 * @constructor 13977 * @extends AttrRecognizer 13978 */ 13979function PanRecognizer() { 13980 AttrRecognizer.apply(this, arguments); 13981 13982 this.pX = null; 13983 this.pY = null; 13984} 13985 13986inherit(PanRecognizer, AttrRecognizer, { 13987 /** 13988 * @namespace 13989 * @memberof PanRecognizer 13990 */ 13991 defaults: { 13992 event: 'pan', 13993 threshold: 10, 13994 pointers: 1, 13995 direction: DIRECTION_ALL 13996 }, 13997 13998 getTouchAction: function() { 13999 var direction = this.options.direction; 14000 var actions = []; 14001 if (direction & DIRECTION_HORIZONTAL) { 14002 actions.push(TOUCH_ACTION_PAN_Y); 14003 } 14004 if (direction & DIRECTION_VERTICAL) { 14005 actions.push(TOUCH_ACTION_PAN_X); 14006 } 14007 return actions; 14008 }, 14009 14010 directionTest: function(input) { 14011 var options = this.options; 14012 var hasMoved = true; 14013 var distance = input.distance; 14014 var direction = input.direction; 14015 var x = input.deltaX; 14016 var y = input.deltaY; 14017 14018 // lock to axis? 14019 if (!(direction & options.direction)) { 14020 if (options.direction & DIRECTION_HORIZONTAL) { 14021 direction = (x === 0) ? DIRECTION_NONE : (x < 0) ? DIRECTION_LEFT : DIRECTION_RIGHT; 14022 hasMoved = x != this.pX; 14023 distance = Math.abs(input.deltaX); 14024 } else { 14025 direction = (y === 0) ? DIRECTION_NONE : (y < 0) ? DIRECTION_UP : DIRECTION_DOWN; 14026 hasMoved = y != this.pY; 14027 distance = Math.abs(input.deltaY); 14028 } 14029 } 14030 input.direction = direction; 14031 return hasMoved && distance > options.threshold && direction & options.direction; 14032 }, 14033 14034 attrTest: function(input) { 14035 return AttrRecognizer.prototype.attrTest.call(this, input) && 14036 (this.state & STATE_BEGAN || (!(this.state & STATE_BEGAN) && this.directionTest(input))); 14037 }, 14038 14039 emit: function(input) { 14040 14041 this.pX = input.deltaX; 14042 this.pY = input.deltaY; 14043 14044 var direction = directionStr(input.direction); 14045 14046 if (direction) { 14047 input.additionalEvent = this.options.event + direction; 14048 } 14049 this._super.emit.call(this, input); 14050 } 14051}); 14052 14053/** 14054 * Pinch 14055 * Recognized when two or more pointers are moving toward (zoom-in) or away from each other (zoom-out). 14056 * @constructor 14057 * @extends AttrRecognizer 14058 */ 14059function PinchRecognizer() { 14060 AttrRecognizer.apply(this, arguments); 14061} 14062 14063inherit(PinchRecognizer, AttrRecognizer, { 14064 /** 14065 * @namespace 14066 * @memberof PinchRecognizer 14067 */ 14068 defaults: { 14069 event: 'pinch', 14070 threshold: 0, 14071 pointers: 2 14072 }, 14073 14074 getTouchAction: function() { 14075 return [TOUCH_ACTION_NONE]; 14076 }, 14077 14078 attrTest: function(input) { 14079 return this._super.attrTest.call(this, input) && 14080 (Math.abs(input.scale - 1) > this.options.threshold || this.state & STATE_BEGAN); 14081 }, 14082 14083 emit: function(input) { 14084 if (input.scale !== 1) { 14085 var inOut = input.scale < 1 ? 'in' : 'out'; 14086 input.additionalEvent = this.options.event + inOut; 14087 } 14088 this._super.emit.call(this, input); 14089 } 14090}); 14091 14092/** 14093 * Press 14094 * Recognized when the pointer is down for x ms without any movement. 14095 * @constructor 14096 * @extends Recognizer 14097 */ 14098function PressRecognizer() { 14099 Recognizer.apply(this, arguments); 14100 14101 this._timer = null; 14102 this._input = null; 14103} 14104 14105inherit(PressRecognizer, Recognizer, { 14106 /** 14107 * @namespace 14108 * @memberof PressRecognizer 14109 */ 14110 defaults: { 14111 event: 'press', 14112 pointers: 1, 14113 time: 251, // minimal time of the pointer to be pressed 14114 threshold: 9 // a minimal movement is ok, but keep it low 14115 }, 14116 14117 getTouchAction: function() { 14118 return [TOUCH_ACTION_AUTO]; 14119 }, 14120 14121 process: function(input) { 14122 var options = this.options; 14123 var validPointers = input.pointers.length === options.pointers; 14124 var validMovement = input.distance < options.threshold; 14125 var validTime = input.deltaTime > options.time; 14126 14127 this._input = input; 14128 14129 // we only allow little movement 14130 // and we've reached an end event, so a tap is possible 14131 if (!validMovement || !validPointers || (input.eventType & (INPUT_END | INPUT_CANCEL) && !validTime)) { 14132 this.reset(); 14133 } else if (input.eventType & INPUT_START) { 14134 this.reset(); 14135 this._timer = setTimeoutContext(function() { 14136 this.state = STATE_RECOGNIZED; 14137 this.tryEmit(); 14138 }, options.time, this); 14139 } else if (input.eventType & INPUT_END) { 14140 return STATE_RECOGNIZED; 14141 } 14142 return STATE_FAILED; 14143 }, 14144 14145 reset: function() { 14146 clearTimeout(this._timer); 14147 }, 14148 14149 emit: function(input) { 14150 if (this.state !== STATE_RECOGNIZED) { 14151 return; 14152 } 14153 14154 if (input && (input.eventType & INPUT_END)) { 14155 this.manager.emit(this.options.event + 'up', input); 14156 } else { 14157 this._input.timeStamp = now(); 14158 this.manager.emit(this.options.event, this._input); 14159 } 14160 } 14161}); 14162 14163/** 14164 * Rotate 14165 * Recognized when two or more pointer are moving in a circular motion. 14166 * @constructor 14167 * @extends AttrRecognizer 14168 */ 14169function RotateRecognizer() { 14170 AttrRecognizer.apply(this, arguments); 14171} 14172 14173inherit(RotateRecognizer, AttrRecognizer, { 14174 /** 14175 * @namespace 14176 * @memberof RotateRecognizer 14177 */ 14178 defaults: { 14179 event: 'rotate', 14180 threshold: 0, 14181 pointers: 2 14182 }, 14183 14184 getTouchAction: function() { 14185 return [TOUCH_ACTION_NONE]; 14186 }, 14187 14188 attrTest: function(input) { 14189 return this._super.attrTest.call(this, input) && 14190 (Math.abs(input.rotation) > this.options.threshold || this.state & STATE_BEGAN); 14191 } 14192}); 14193 14194/** 14195 * Swipe 14196 * Recognized when the pointer is moving fast (velocity), with enough distance in the allowed direction. 14197 * @constructor 14198 * @extends AttrRecognizer 14199 */ 14200function SwipeRecognizer() { 14201 AttrRecognizer.apply(this, arguments); 14202} 14203 14204inherit(SwipeRecognizer, AttrRecognizer, { 14205 /** 14206 * @namespace 14207 * @memberof SwipeRecognizer 14208 */ 14209 defaults: { 14210 event: 'swipe', 14211 threshold: 10, 14212 velocity: 0.3, 14213 direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL, 14214 pointers: 1 14215 }, 14216 14217 getTouchAction: function() { 14218 return PanRecognizer.prototype.getTouchAction.call(this); 14219 }, 14220 14221 attrTest: function(input) { 14222 var direction = this.options.direction; 14223 var velocity; 14224 14225 if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) { 14226 velocity = input.overallVelocity; 14227 } else if (direction & DIRECTION_HORIZONTAL) { 14228 velocity = input.overallVelocityX; 14229 } else if (direction & DIRECTION_VERTICAL) { 14230 velocity = input.overallVelocityY; 14231 } 14232 14233 return this._super.attrTest.call(this, input) && 14234 direction & input.offsetDirection && 14235 input.distance > this.options.threshold && 14236 input.maxPointers == this.options.pointers && 14237 abs(velocity) > this.options.velocity && input.eventType & INPUT_END; 14238 }, 14239 14240 emit: function(input) { 14241 var direction = directionStr(input.offsetDirection); 14242 if (direction) { 14243 this.manager.emit(this.options.event + direction, input); 14244 } 14245 14246 this.manager.emit(this.options.event, input); 14247 } 14248}); 14249 14250/** 14251 * A tap is ecognized when the pointer is doing a small tap/click. Multiple taps are recognized if they occur 14252 * between the given interval and position. The delay option can be used to recognize multi-taps without firing 14253 * a single tap. 14254 * 14255 * The eventData from the emitted event contains the property `tapCount`, which contains the amount of 14256 * multi-taps being recognized. 14257 * @constructor 14258 * @extends Recognizer 14259 */ 14260function TapRecognizer() { 14261 Recognizer.apply(this, arguments); 14262 14263 // previous time and center, 14264 // used for tap counting 14265 this.pTime = false; 14266 this.pCenter = false; 14267 14268 this._timer = null; 14269 this._input = null; 14270 this.count = 0; 14271} 14272 14273inherit(TapRecognizer, Recognizer, { 14274 /** 14275 * @namespace 14276 * @memberof PinchRecognizer 14277 */ 14278 defaults: { 14279 event: 'tap', 14280 pointers: 1, 14281 taps: 1, 14282 interval: 300, // max time between the multi-tap taps 14283 time: 250, // max time of the pointer to be down (like finger on the screen) 14284 threshold: 9, // a minimal movement is ok, but keep it low 14285 posThreshold: 10 // a multi-tap can be a bit off the initial position 14286 }, 14287 14288 getTouchAction: function() { 14289 return [TOUCH_ACTION_MANIPULATION]; 14290 }, 14291 14292 process: function(input) { 14293 var options = this.options; 14294 14295 var validPointers = input.pointers.length === options.pointers; 14296 var validMovement = input.distance < options.threshold; 14297 var validTouchTime = input.deltaTime < options.time; 14298 14299 this.reset(); 14300 14301 if ((input.eventType & INPUT_START) && (this.count === 0)) { 14302 return this.failTimeout(); 14303 } 14304 14305 // we only allow little movement 14306 // and we've reached an end event, so a tap is possible 14307 if (validMovement && validTouchTime && validPointers) { 14308 if (input.eventType != INPUT_END) { 14309 return this.failTimeout(); 14310 } 14311 14312 var validInterval = this.pTime ? (input.timeStamp - this.pTime < options.interval) : true; 14313 var validMultiTap = !this.pCenter || getDistance(this.pCenter, input.center) < options.posThreshold; 14314 14315 this.pTime = input.timeStamp; 14316 this.pCenter = input.center; 14317 14318 if (!validMultiTap || !validInterval) { 14319 this.count = 1; 14320 } else { 14321 this.count += 1; 14322 } 14323 14324 this._input = input; 14325 14326 // if tap count matches we have recognized it, 14327 // else it has began recognizing... 14328 var tapCount = this.count % options.taps; 14329 if (tapCount === 0) { 14330 // no failing requirements, immediately trigger the tap event 14331 // or wait as long as the multitap interval to trigger 14332 if (!this.hasRequireFailures()) { 14333 return STATE_RECOGNIZED; 14334 } else { 14335 this._timer = setTimeoutContext(function() { 14336 this.state = STATE_RECOGNIZED; 14337 this.tryEmit(); 14338 }, options.interval, this); 14339 return STATE_BEGAN; 14340 } 14341 } 14342 } 14343 return STATE_FAILED; 14344 }, 14345 14346 failTimeout: function() { 14347 this._timer = setTimeoutContext(function() { 14348 this.state = STATE_FAILED; 14349 }, this.options.interval, this); 14350 return STATE_FAILED; 14351 }, 14352 14353 reset: function() { 14354 clearTimeout(this._timer); 14355 }, 14356 14357 emit: function() { 14358 if (this.state == STATE_RECOGNIZED) { 14359 this._input.tapCount = this.count; 14360 this.manager.emit(this.options.event, this._input); 14361 } 14362 } 14363}); 14364 14365/** 14366 * Simple way to create a manager with a default set of recognizers. 14367 * @param {HTMLElement} element 14368 * @param {Object} [options] 14369 * @constructor 14370 */ 14371function NGHammer(element, options) { 14372 options = options || {}; 14373 options.recognizers = ifUndefined(options.recognizers, NGHammer.defaults.preset); 14374 return new Manager(element, options); 14375} 14376 14377/** 14378 * @const {string} 14379 */ 14380NGHammer.VERSION = '2.0.7'; 14381 14382/** 14383 * default settings 14384 * @namespace 14385 */ 14386NGHammer.defaults = { 14387 /** 14388 * set if DOM events are being triggered. 14389 * But this is slower and unused by simple implementations, so disabled by default. 14390 * @type {Boolean} 14391 * @default false 14392 */ 14393 domEvents: false, 14394 14395 /** 14396 * The value for the touchAction property/fallback. 14397 * When set to `compute` it will magically set the correct value based on the added recognizers. 14398 * @type {String} 14399 * @default compute 14400 */ 14401 touchAction: TOUCH_ACTION_COMPUTE, 14402 14403 /** 14404 * @type {Boolean} 14405 * @default true 14406 */ 14407 enable: true, 14408 14409 /** 14410 * EXPERIMENTAL FEATURE -- can be removed/changed 14411 * Change the parent input target element. 14412 * If Null, then it is being set the to main element. 14413 * @type {Null|EventTarget} 14414 * @default null 14415 */ 14416 inputTarget: null, 14417 14418 /** 14419 * force an input class 14420 * @type {Null|Function} 14421 * @default null 14422 */ 14423 inputClass: null, 14424 14425 /** 14426 * Default recognizer setup when calling `NGHammer()` 14427 * When creating a new Manager these will be skipped. 14428 * @type {Array} 14429 */ 14430 preset: [ 14431 // RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...] 14432 [RotateRecognizer, {enable: false}], 14433 [PinchRecognizer, {enable: false}, ['rotate']], 14434 [SwipeRecognizer, {direction: DIRECTION_HORIZONTAL}], 14435 [PanRecognizer, {direction: DIRECTION_HORIZONTAL}, ['swipe']], 14436 [TapRecognizer], 14437 [TapRecognizer, {event: 'doubletap', taps: 2}, ['tap']], 14438 [PressRecognizer] 14439 ], 14440 14441 /** 14442 * Some CSS properties can be used to improve the working of NGHammer. 14443 * Add them to this method and they will be set when creating a new Manager. 14444 * @namespace 14445 */ 14446 cssProps: { 14447 /** 14448 * Disables text selection to improve the dragging gesture. Mainly for desktop browsers. 14449 * @type {String} 14450 * @default 'none' 14451 */ 14452 userSelect: 'none', 14453 14454 /** 14455 * Disable the Windows Phone grippers when pressing an element. 14456 * @type {String} 14457 * @default 'none' 14458 */ 14459 touchSelect: 'none', 14460 14461 /** 14462 * Disables the default callout shown when you touch and hold a touch target. 14463 * On iOS, when you touch and hold a touch target such as a link, Safari displays 14464 * a callout containing information about the link. This property allows you to disable that callout. 14465 * @type {String} 14466 * @default 'none' 14467 */ 14468 touchCallout: 'none', 14469 14470 /** 14471 * Specifies whether zooming is enabled. Used by IE10> 14472 * @type {String} 14473 * @default 'none' 14474 */ 14475 contentZooming: 'none', 14476 14477 /** 14478 * Specifies that an entire element should be draggable instead of its contents. Mainly for desktop browsers. 14479 * @type {String} 14480 * @default 'none' 14481 */ 14482 userDrag: 'none', 14483 14484 /** 14485 * Overrides the highlight color shown when the user taps a link or a JavaScript 14486 * clickable element in iOS. This property obeys the alpha value, if specified. 14487 * @type {String} 14488 * @default 'rgba(0,0,0,0)' 14489 */ 14490 tapHighlightColor: 'rgba(0,0,0,0)' 14491 } 14492}; 14493 14494var STOP = 1; 14495var FORCED_STOP = 2; 14496 14497/** 14498 * Manager 14499 * @param {HTMLElement} element 14500 * @param {Object} [options] 14501 * @constructor 14502 */ 14503function Manager(element, options) { 14504 this.options = assign({}, NGHammer.defaults, options || {}); 14505 14506 this.options.inputTarget = this.options.inputTarget || element; 14507 14508 this.handlers = {}; 14509 this.session = {}; 14510 this.recognizers = []; 14511 this.oldCssProps = {}; 14512 14513 this.element = element; 14514 this.input = createInputInstance(this); 14515 this.touchAction = new TouchAction(this, this.options.touchAction); 14516 14517 toggleCssProps(this, true); 14518 14519 each(this.options.recognizers, function(item) { 14520 var recognizer = this.add(new (item[0])(item[1])); 14521 item[2] && recognizer.recognizeWith(item[2]); 14522 item[3] && recognizer.requireFailure(item[3]); 14523 }, this); 14524} 14525 14526Manager.prototype = { 14527 /** 14528 * set options 14529 * @param {Object} options 14530 * @returns {Manager} 14531 */ 14532 set: function(options) { 14533 assign(this.options, options); 14534 14535 // Options that need a little more setup 14536 if (options.touchAction) { 14537 this.touchAction.update(); 14538 } 14539 if (options.inputTarget) { 14540 // Clean up existing event listeners and reinitialize 14541 this.input.destroy(); 14542 this.input.target = options.inputTarget; 14543 this.input.init(); 14544 } 14545 return this; 14546 }, 14547 14548 /** 14549 * stop recognizing for this session. 14550 * This session will be discarded, when a new [input]start event is fired. 14551 * When forced, the recognizer cycle is stopped immediately. 14552 * @param {Boolean} [force] 14553 */ 14554 stop: function(force) { 14555 this.session.stopped = force ? FORCED_STOP : STOP; 14556 }, 14557 14558 /** 14559 * run the recognizers! 14560 * called by the inputHandler function on every movement of the pointers (touches) 14561 * it walks through all the recognizers and tries to detect the gesture that is being made 14562 * @param {Object} inputData 14563 */ 14564 recognize: function(inputData) { 14565 var session = this.session; 14566 if (session.stopped) { 14567 return; 14568 } 14569 14570 // run the touch-action polyfill 14571 this.touchAction.preventDefaults(inputData); 14572 14573 var recognizer; 14574 var recognizers = this.recognizers; 14575 14576 // this holds the recognizer that is being recognized. 14577 // so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED 14578 // if no recognizer is detecting a thing, it is set to `null` 14579 var curRecognizer = session.curRecognizer; 14580 14581 // reset when the last recognizer is recognized 14582 // or when we're in a new session 14583 if (!curRecognizer || (curRecognizer && curRecognizer.state & STATE_RECOGNIZED)) { 14584 curRecognizer = session.curRecognizer = null; 14585 } 14586 14587 var i = 0; 14588 while (i < recognizers.length) { 14589 recognizer = recognizers[i]; 14590 14591 // find out if we are allowed try to recognize the input for this one. 14592 // 1. allow if the session is NOT forced stopped (see the .stop() method) 14593 // 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one 14594 // that is being recognized. 14595 // 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer. 14596 // this can be setup with the `recognizeWith()` method on the recognizer. 14597 if (session.stopped !== FORCED_STOP && ( // 1 14598 !curRecognizer || recognizer == curRecognizer || // 2 14599 recognizer.canRecognizeWith(curRecognizer))) { // 3 14600 recognizer.recognize(inputData); 14601 } else { 14602 recognizer.reset(); 14603 } 14604 14605 // if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the 14606 // current active recognizer. but only if we don't already have an active recognizer 14607 if (!curRecognizer && recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)) { 14608 curRecognizer = session.curRecognizer = recognizer; 14609 } 14610 i++; 14611 } 14612 }, 14613 14614 /** 14615 * get a recognizer by its event name. 14616 * @param {Recognizer|String} recognizer 14617 * @returns {Recognizer|Null} 14618 */ 14619 get: function(recognizer) { 14620 if (recognizer instanceof Recognizer) { 14621 return recognizer; 14622 } 14623 14624 var recognizers = this.recognizers; 14625 for (var i = 0; i < recognizers.length; i++) { 14626 if (recognizers[i].options.event == recognizer) { 14627 return recognizers[i]; 14628 } 14629 } 14630 return null; 14631 }, 14632 14633 /** 14634 * add a recognizer to the manager 14635 * existing recognizers with the same event name will be removed 14636 * @param {Recognizer} recognizer 14637 * @returns {Recognizer|Manager} 14638 */ 14639 add: function(recognizer) { 14640 if (invokeArrayArg(recognizer, 'add', this)) { 14641 return this; 14642 } 14643 14644 // remove existing 14645 var existing = this.get(recognizer.options.event); 14646 if (existing) { 14647 this.remove(existing); 14648 } 14649 14650 this.recognizers.push(recognizer); 14651 recognizer.manager = this; 14652 14653 this.touchAction.update(); 14654 return recognizer; 14655 }, 14656 14657 /** 14658 * remove a recognizer by name or instance 14659 * @param {Recognizer|String} recognizer 14660 * @returns {Manager} 14661 */ 14662 remove: function(recognizer) { 14663 if (invokeArrayArg(recognizer, 'remove', this)) { 14664 return this; 14665 } 14666 14667 recognizer = this.get(recognizer); 14668 14669 // let's make sure this recognizer exists 14670 if (recognizer) { 14671 var recognizers = this.recognizers; 14672 var index = inArray(recognizers, recognizer); 14673 14674 if (index !== -1) { 14675 recognizers.splice(index, 1); 14676 this.touchAction.update(); 14677 } 14678 } 14679 14680 return this; 14681 }, 14682 14683 /** 14684 * bind event 14685 * @param {String} events 14686 * @param {Function} handler 14687 * @returns {EventEmitter} this 14688 */ 14689 on: function(events, handler) { 14690 if (events === undefined) { 14691 return; 14692 } 14693 if (handler === undefined) { 14694 return; 14695 } 14696 14697 var handlers = this.handlers; 14698 each(splitStr(events), function(event) { 14699 handlers[event] = handlers[event] || []; 14700 handlers[event].push(handler); 14701 }); 14702 return this; 14703 }, 14704 14705 /** 14706 * unbind event, leave emit blank to remove all handlers 14707 * @param {String} events 14708 * @param {Function} [handler] 14709 * @returns {EventEmitter} this 14710 */ 14711 off: function(events, handler) { 14712 if (events === undefined) { 14713 return; 14714 } 14715 14716 var handlers = this.handlers; 14717 each(splitStr(events), function(event) { 14718 if (!handler) { 14719 delete handlers[event]; 14720 } else { 14721 handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1); 14722 } 14723 }); 14724 return this; 14725 }, 14726 14727 /** 14728 * emit event to the listeners 14729 * @param {String} event 14730 * @param {Object} data 14731 */ 14732 emit: function(event, data) { 14733 // we also want to trigger dom events 14734 if (this.options.domEvents) { 14735 triggerDomEvent(event, data); 14736 } 14737 14738 // no handlers, so skip it all 14739 var handlers = this.handlers[event] && this.handlers[event].slice(); 14740 if (!handlers || !handlers.length) { 14741 return; 14742 } 14743 14744 data.type = event; 14745 data.preventDefault = function() { 14746 data.srcEvent.preventDefault(); 14747 }; 14748 14749 var i = 0; 14750 while (i < handlers.length) { 14751 handlers[i](data); 14752 i++; 14753 } 14754 }, 14755 14756 /** 14757 * destroy the manager and unbinds all events 14758 * it doesn't unbind dom events, that is the user own responsibility 14759 */ 14760 destroy: function() { 14761 this.element && toggleCssProps(this, false); 14762 14763 this.handlers = {}; 14764 this.session = {}; 14765 this.input.destroy(); 14766 this.element = null; 14767 } 14768}; 14769 14770/** 14771 * add/remove the css properties as defined in manager.options.cssProps 14772 * @param {Manager} manager 14773 * @param {Boolean} add 14774 */ 14775function toggleCssProps(manager, add) { 14776 var element = manager.element; 14777 if (!element.style) { 14778 return; 14779 } 14780 var prop; 14781 each(manager.options.cssProps, function(value, name) { 14782 prop = prefixed(element.style, name); 14783 if (add) { 14784 manager.oldCssProps[prop] = element.style[prop]; 14785 element.style[prop] = value; 14786 } else { 14787 element.style[prop] = manager.oldCssProps[prop] || ''; 14788 } 14789 }); 14790 if (!add) { 14791 manager.oldCssProps = {}; 14792 } 14793} 14794 14795/** 14796 * trigger dom event 14797 * @param {String} event 14798 * @param {Object} data 14799 */ 14800function triggerDomEvent(event, data) { 14801 var gestureEvent = document.createEvent('Event'); 14802 gestureEvent.initEvent(event, true, true); 14803 gestureEvent.gesture = data; 14804 data.target.dispatchEvent(gestureEvent); 14805} 14806 14807assign(NGHammer, { 14808 INPUT_START: INPUT_START, 14809 INPUT_MOVE: INPUT_MOVE, 14810 INPUT_END: INPUT_END, 14811 INPUT_CANCEL: INPUT_CANCEL, 14812 14813 STATE_POSSIBLE: STATE_POSSIBLE, 14814 STATE_BEGAN: STATE_BEGAN, 14815 STATE_CHANGED: STATE_CHANGED, 14816 STATE_ENDED: STATE_ENDED, 14817 STATE_RECOGNIZED: STATE_RECOGNIZED, 14818 STATE_CANCELLED: STATE_CANCELLED, 14819 STATE_FAILED: STATE_FAILED, 14820 14821 DIRECTION_NONE: DIRECTION_NONE, 14822 DIRECTION_LEFT: DIRECTION_LEFT, 14823 DIRECTION_RIGHT: DIRECTION_RIGHT, 14824 DIRECTION_UP: DIRECTION_UP, 14825 DIRECTION_DOWN: DIRECTION_DOWN, 14826 DIRECTION_HORIZONTAL: DIRECTION_HORIZONTAL, 14827 DIRECTION_VERTICAL: DIRECTION_VERTICAL, 14828 DIRECTION_ALL: DIRECTION_ALL, 14829 14830 Manager: Manager, 14831 Input: Input, 14832 TouchAction: TouchAction, 14833 14834 TouchInput: TouchInput, 14835 MouseInput: MouseInput, 14836 PointerEventInput: PointerEventInput, 14837 TouchMouseInput: TouchMouseInput, 14838 SingleTouchInput: SingleTouchInput, 14839 14840 Recognizer: Recognizer, 14841 AttrRecognizer: AttrRecognizer, 14842 Tap: TapRecognizer, 14843 Pan: PanRecognizer, 14844 Swipe: SwipeRecognizer, 14845 Pinch: PinchRecognizer, 14846 Rotate: RotateRecognizer, 14847 Press: PressRecognizer, 14848 14849 on: addEventListeners, 14850 off: removeEventListeners, 14851 each: each, 14852 merge: merge, 14853 extend: extend, 14854 assign: assign, 14855 inherit: inherit, 14856 bindFn: bindFn, 14857 prefixed: prefixed 14858}); 14859 14860// this prevents errors when NGHammer is loaded in the presence of an AMD 14861// style loader but by script tag, not by the loader. 14862var freeGlobal = (typeof window !== 'undefined' ? window : (typeof self !== 'undefined' ? self : {})); // jshint ignore:line 14863freeGlobal.NGHammer = NGHammer; 14864 14865if (typeof define === 'function' && define.amdDISABLED) { 14866 define(function() { 14867 return NGHammer; 14868 }); 14869} else if (typeof module != 'undefined' && module.exports) { 14870 module.exports = NGHammer; 14871} else { 14872 window[exportName] = NGHammer; 14873} 14874 14875})(window, document, 'NGHammer'); 14876 14877 14878 14879 14880 14881 14882// END NANOGALLERY2 14883// }( jQuery ))); 14884})); 14885 14886 14887//########################################################################################################################## 14888//########################################################################################################################## 14889//########################################################################################################################## 14890//########################################################################################################################## 14891//########################################################################################################################## 14892 14893// nanogallery2 auto start whithout javascript call 14894(function(){ 14895 'use strict';
vendor: 38,223 bytes, lines 14896-15949
14896 jQuery(document).ready(function () { 14897 14898 // var t=document.querySelectorAll('[data-nanogallery2-portable]'); 14899 // if( t.length > 0 ) { 14900 // portable mode 14901 // var link = document.createElement('link'); 14902 // link.setAttribute("rel", "stylesheet"); 14903 // link.setAttribute("type", "text/css"); 14904 // link.onload = function(){ 14905 // for( var i=0; i < t.length; i++ ) { 14906 // jQuery(t[i]).nanogallery2(jQuery(t[i]).data('nanogallery2-portable')); 14907 // } 14908 // } 14909 // link.setAttribute("href", '//nano.gallery/css/nanogallery2.css'); 14910 // document.getElementsByTagName("head")[0].appendChild(link); 14911 // } 14912 // else { 14913 // standard mode 14914 var t=document.querySelectorAll('[data-nanogallery2]'); 14915 for( var i=0; i < t.length; i++ ) { 14916 jQuery( t[i] ).nanogallery2( jQuery(t[i]).data('nanogallery2') ); 14917 } 14918 // } 14919 14920 }); 14921}).call(null); 14922 14923 14924/**! 14925 * @preserve nanogallery2 - NANOPHOTOSPROVIDER2 data provider 14926 * Homepage: http://nanogallery2.nanostudio.org 14927 * Sources: https://github.com/nanostudio-org/nanogallery2 14928 * 14929 * License: GPLv3 and commercial licence 14930 * 14931*/ 14932 14933// ######################################################## 14934// ##### nanogallery2 - module NANOPHOTOSPROVIDER2 ##### 14935// ######################################################## 14936 14937 14938(function (factory) { 14939 "use strict"; 14940 if (typeof define === 'function' && define.amd) { 14941 // AMD. Register as an anonymous module. 14942 define(['jquery', 'nanogallery2'], factory); 14943 } else if (typeof exports === 'object' && typeof require === 'function') { 14944 // Browserify 14945 factory(require(['jquery', 'nanogallery2'])); 14946 } else { 14947 // Browser globals 14948 factory(jQuery); 14949 } 14950}(function ($) { 14951// ;(function ($) { 14952 14953 jQuery.nanogallery2.data_nano_photos_provider2 = function (instance, fnName){ 14954 var G = instance; // current nanogallery2 instance 14955 14956 /** @function AlbumGetContent */ 14957 var AlbumGetContent = function(albumID, fnToCall, fnParam1, fnParam2) { 14958 14959 var albumIdx = NGY2Item.GetIdx(G, albumID); 14960 14961 // title is identical to ID (only for albums) 14962 if( instance.I[albumIdx].title == '' ) { 14963 instance.I[albumIdx].title = JsonConvertCharset(albumID); 14964 } 14965 14966 // Build the URL 14967 var url = G.O.dataProvider + '?albumID='+albumID; // which album 14968 // all thumbnails sizes (for responsive display) 14969 url += '&hxs=' + G.tn.settings.getH(G.GOM.curNavLevel, 'xs'); 14970 url += '&wxs=' + G.tn.settings.getW(G.GOM.curNavLevel, 'xs'); 14971 url += '&hsm=' + G.tn.settings.getH(G.GOM.curNavLevel, 'sm'); 14972 url += '&wsm=' + G.tn.settings.getW(G.GOM.curNavLevel, 'sm'); 14973 url += '&hme=' + G.tn.settings.getH(G.GOM.curNavLevel, 'me'); 14974 url += '&wme=' + G.tn.settings.getW(G.GOM.curNavLevel, 'me'); 14975 url += '&hla=' + G.tn.settings.getH(G.GOM.curNavLevel, 'la'); 14976 url += '&wla=' + G.tn.settings.getW(G.GOM.curNavLevel, 'la'); 14977 url += '&hxl=' + G.tn.settings.getH(G.GOM.curNavLevel, 'xl'); 14978 url += '&wxl=' + G.tn.settings.getW(G.GOM.curNavLevel, 'xl'); 14979 // url += '&wxs=' + G.tn.settings.width[G.GOM.curNavLevel].xs; 14980 // url += '&hxs=' + G.tn.settings.height[G.GOM.curNavLevel].xs; 14981 // url += '&wsm=' + G.tn.settings.width[G.GOM.curNavLevel].sm; 14982 // url += '&hsm=' + G.tn.settings.height[G.GOM.curNavLevel].sm; 14983 // url += '&wme=' + G.tn.settings.width[G.GOM.curNavLevel].me; 14984 // url += '&hme=' + G.tn.settings.height[G.GOM.curNavLevel].me; 14985 // url += '&wla=' + G.tn.settings.width[G.GOM.curNavLevel].la; 14986 // url += '&hla=' + G.tn.settings.height[G.GOM.curNavLevel].la; 14987 // url += '&wxl=' + G.tn.settings.width[G.GOM.curNavLevel].xl; 14988 // url += '&hxl=' + G.tn.settings.height[G.GOM.curNavLevel].xl; 14989 14990 PreloaderDisplay( true ); 14991 jQuery.ajaxSetup({ cache: false }); 14992 jQuery.support.cors = true; 14993 try { 14994 14995 var tId = setTimeout( function() { 14996 // workaround to handle JSONP (cross-domain) errors 14997 PreloaderDisplay(false); 14998 NanoAlert(G, 'Could not retrieve nanoPhotosProvider2 data (timeout).'); 14999 }, 60000 ); 15000 15001 if( G.O.debugMode ) { console.log('nanoPhotosProvider2 URL: ' + url); } 15002 15003 jQuery.getJSON(url, function(data, status, xhr) { 15004 clearTimeout( tId ); 15005 PreloaderDisplay( false ); 15006 15007 JsonParseData(albumIdx, data); 15008 15009 if( data.nano_status == 'ok' ) { 15010 AlbumPostProcess( albumID ); 15011 if( fnToCall !== null && fnToCall !== undefined) { 15012 fnToCall( fnParam1, fnParam2, null ); 15013 } 15014 } 15015 else { 15016 NanoAlert(G, 'Could not retrieve nanoPhotosProvider2 data. Error: ' + data.nano_status + ' - ' + data.nano_message); 15017 } 15018 }) 15019 .fail( function(jqxhr, textStatus, error) { 15020 clearTimeout( tId ); 15021 PreloaderDisplay( false ); 15022 15023 var k='' 15024 for(var key in jqxhr) { 15025 k+= key + '=' + jqxhr[key] +'<br>'; 15026 } 15027 var err = textStatus + ', ' + error + ' ' + k + '<br><br>URL:'+url; 15028 NanoAlert(G, 'Could not retrieve nanoPhotosProvider2 data. Error: ' + err); 15029 15030 }); 15031 15032 } 15033 catch(e) { 15034 NanoAlert(G, 'Could not retrieve nanoPhotosProvider2 data. Error: ' + e); 15035 } 15036 } 15037 15038 15039 function JsonConvertCharset( str ) { 15040 15041 return decodeURIComponent(str); 15042 15043 15044 // Pb %C3%A9 --> %E9 15045 // in UTF-8: \u00e9=\xe9 (e9 = hex value) 15046 switch( G.O.dataCharset.toUpperCase() ) { 15047 case 'UTF-8': // Apache Windows 15048 return decodeURI(str); // do not use decodeURIComponent (would convert slash also) 15049 break; 15050 case 'Latin': // Apache Linux 15051 default : 15052 return escape(str); 15053 break; 15054 } 15055 } 15056 15057 function JsonParseData(albumIdx, data) { 15058 if( G.O.debugMode ) { 15059 console.log('nanoPhotosProvider2 parse data:'); 15060 console.dir(data); 15061 } 15062 15063 var foundAlbumID = false; 15064 var nb = 0; 15065 15066 // loop each item 15067 jQuery.each( data.album_content, function( i, item ){ 15068 15069 // base URL where the images are stored 15070 var baseURL = G.O.dataProvider.substring(0, G.O.dataProvider.indexOf('nano_photos_provider2.php')); 15071 15072 // image URL 15073 var src = baseURL + JsonConvertCharset( item.src ); 15074 15075 // item title 15076 var title = item.title; 15077 15078 // item description ( '_' are replaced with ' ' ) 15079 var description = item.description.split('_').join(' '); 15080 15081 // item kind ('album' or 'image') 15082 var kind = 'image'; 15083 if( item.kind !== undefined && item.kind.length > 0 ) { 15084 kind = item.kind; 15085 } 15086 15087 // item ID 15088 var ID=item.ID; 15089 15090 var filterAlbum = false; 15091 if( kind == 'album' ) { 15092 // check if 15093 if( !FilterAlbumName(title, ID) ) { filterAlbum = true; } 15094 } 15095 15096 if( kind == 'image' || (kind == 'album' && FilterAlbumName(title, ID)) ) { 15097 var albumID = 0; 15098 if( item.albumID !== undefined ) { 15099 albumID = item.albumID; 15100 foundAlbumID = true; 15101 } 15102 15103 var tags = (item.tags === undefined) ? '' : item.tags; 15104 15105 var newItem = NGY2Item.New( G, title.split('_').join(' ') , description, ID, albumID, kind, tags ); 15106 newItem.setMediaURL( src, 'img', description); 15107 15108 // dominant colorS as a gif 15109 if( item.dcGIF !== undefined ) { 15110 newItem.imageDominantColors='data:image/gif;base64,'+item.dcGIF; 15111 } 15112 // dominant color as hex rgb value 15113 if( item.dc !== undefined && item.dc !== '' ) { 15114 newItem.imageDominantColor=item.dc; 15115 } 15116 15117 if( kind == 'album' ) { 15118 // number of items in album 15119 newItem.numberItems = item.cnt; 15120 } 15121 else { 15122 // image size 15123 newItem.imageWidth = item.imgWidth; 15124 newItem.imageHeight = item.imgHeight; 15125 } 15126 15127 // item download URL 15128 if( item.originalURL != '' ) { 15129 newItem.downloadURL = baseURL+JsonConvertCharset(item.originalURL); 15130 } 15131 15132 // retrieve responsive thumbnails urls and sizes 15133 var cnl = G.GOM.curNavLevel; // current navigation level ('L1' or 'LN'); 15134 var l=['xs', 'sm', 'me', 'la', 'xl']; 15135 for( var n = 0; n < l.length; n++ ) { 15136 newItem.thumbs.url[cnl][l[n]] = baseURL + JsonConvertCharset(item.t_url[n]); 15137 newItem.thumbs.width[cnl][l[n]] = parseInt(item.t_width[n]); 15138 newItem.thumbs.height[cnl][l[n]] = parseInt(item.t_height[n]); 15139 } 15140 15141 // post-process callback 15142 var fu = G.O.fnProcessData; 15143 if( fu !== null ) { 15144 typeof fu == 'function' ? fu(newItem, G.O.dataProvider, data) : window[fu](newItem, G.O.dataProvider, data); 15145 } 15146 15147 } 15148 }); 15149 15150 G.I[albumIdx].contentIsLoaded = true; // album's content is ready 15151 } 15152 15153 15154 // ----------- 15155 // Initialize 15156 function Init() { 15157 15158 } 15159 15160 15161 // shortcuts to NGY2Tools functions (with context) 15162 var PreloaderDisplay = NGY2Tools.PreloaderDisplay.bind(G); 15163 // var NanoAlert = NGY2Tools.NanoAlert.bind(G); 15164 var NanoAlert = NGY2Tools.NanoAlert; 15165 var GetImageTitleFromURL = NGY2Tools.GetImageTitleFromURL.bind(G); 15166 var FilterAlbumName = NGY2Tools.FilterAlbumName.bind(G); 15167 var AlbumPostProcess = NGY2Tools.AlbumPostProcess.bind(G); 15168 15169 switch( fnName ){ 15170 case 'GetHiddenAlbums': 15171 break; 15172 case 'AlbumGetContent': 15173 var albumID = arguments[2], 15174 callback = arguments[3], 15175 cbParam1 = arguments[4], 15176 cbParam2 = arguments[5]; 15177 AlbumGetContent(albumID, callback, cbParam1, cbParam2); 15178 break; 15179 case 'Init': 15180 Init(); 15181 break; 15182 case '': 15183 break; 15184 } 15185 15186 }; 15187 15188// END NANOPHOTOSPROVIDER DATA SOURCE FOR NANOGALLERY2 15189// }( jQuery )); 15190})); 15191 15192 15193 15194 15195/**! 15196 * @preserve nanogallery2 - GOOGLE PHOTOS data provider 15197 * Homepage: http://nanogallery2.nanostudio.org 15198 * Sources: https://github.com/nanostudio-org/nanogallery2 15199 * 15200 * License: GPLv3 and commercial licence 15201 * 15202*/ 15203 15204// ################################################### 15205// ##### nanogallery2 - module for GOOGLE PHOTOS ##### 15206// ##### requires nanogp ##### 15207// ################################################### 15208 15209 15210(function (factory) { 15211 "use strict"; 15212 if (typeof define === 'function' && define.amd) { 15213 // AMD. Register as an anonymous module. 15214 define(['jquery', 'nanogallery2'], factory); 15215 } else if (typeof exports === 'object' && typeof require === 'function') { 15216 // Browserify 15217 factory(require(['jquery', 'nanogallery2'])); 15218 } else { 15219 // Browser globals 15220 factory(jQuery); 15221 } 15222}(function ($) { 15223// ;(function ($) { 15224 15225 jQuery.nanogallery2.data_google2 = function (instance, fnName){ 15226 var G=instance; // current nanogallery2 instance 15227 15228 15229 /** @function AlbumGetContent */ 15230 var AlbumGetContent = function(albumID, fnToCall, fnParam1, fnParam2) { 15231 15232 var url = ''; 15233 var kind = 'image'; 15234 var albumIdx = NGY2Item.GetIdx(G, albumID); 15235 15236 var maxResults=''; 15237 if( G.galleryMaxItems.Get() > 0 ) { 15238 maxResults = '&max-results=' + G.galleryMaxItems.Get(); 15239 } 15240 15241 var gat=''; // global authorization (using the BUILDER) 15242 if( typeof ngy2_pwa_at !== 'undefined' ) { 15243 gat=ngy2_pwa_at; 15244 } 15245 15246 if( albumID == 0 ) { 15247 // RETRIEVE THE LIST OF ALBUMS 15248 if( gat != '' ) { 15249 // in builder 15250 // url += '?alt=json&v=3&kind=album&deprecation-extension=true&thumbsize='+G.picasa.thumbSizes+maxResults+'&rnd=' + (new Date().getTime()) + '&access_token=' + gat; 15251 url = 'https://photoslibrary.googleapis.com/v1/albums'; 15252 } 15253 else { 15254 // NANOGP2 15255 // url=G.O.google2URL + '?nguserid='+G.O.userID+'&alt=json&v=3&kind=album&thumbsize='+G.picasa.thumbSizes+maxResults+'&rnd=' + (new Date().getTime()); 15256 url = G.O.google2URL + '?nguserid=' + G.O.userID + '&alt=json&v=3&kind=album' + maxResults + '&rnd=' + (new Date().getTime()); 15257 } 15258 kind='album'; 15259 15260 } 15261 else { 15262 // RETRIEVE THE CONTENT OF ONE ALBUM (=MEDIAS) 15263 if( gat != '' ) { 15264 // in builder 15265 // url += '/albumid/'+albumID+'?alt=json&kind=photo&deprecation-extension=true&thumbsize='+G.picasa.thumbSizes+maxResults+'&imgmax=d&access_token=' + gat; 15266 // url += '/albumid/'+albumID+'?alt=json&kind=photo&deprecation-extension=true&thumbsize='+G.picasa.thumbSizes+maxResults+'&imgmax=d&access_token=' + gat; 15267 url = 'https://photoslibrary.googleapis.com/v1/mediaItems:search'; 15268 } 15269 else { 15270 // nanogp 15271 // url = G.O.google2URL + '?nguserid='+G.O.userID+'&ngalbumid='+albumID+'&alt=json&v=3&kind=photo&thumbsize='+G.picasa.thumbSizes+maxResults+'&imgmax=d'; 15272 url = G.O.google2URL + '?nguserid=' + G.O.userID + '&ngalbumid=' + albumID + '&alt=json&v=3&kind=photo&' + maxResults; 15273 } 15274 } 15275 15276 if( G.O.debugMode ) { console.log('Google Photos URL: ' + url); } 15277 15278 PreloaderDisplay(true); 15279 jQuery.ajaxSetup({ cache: false }); 15280 jQuery.support.cors = true; 15281 try { 15282 var tId = setTimeout( function() { 15283 // workaround to handle JSONP (cross-domain) errors 15284 PreloaderDisplay(false); 15285 NanoAlert('Could not retrieve AJAX data...'); 15286 }, 60000 ); 15287 15288 jQuery.getJSON( url + '&callback=?', function(data) { 15289 15290 if( data.nano_status == 'error' ) { 15291 clearTimeout(tId); 15292 PreloaderDisplay(false); 15293 NanoAlert(G, "Could not retrieve Google data. Error: " + data.nano_message); 15294 return; 15295 } 15296 clearTimeout(tId); 15297 PreloaderDisplay(false); 15298 GoogleParseData( albumIdx, kind, data ); 15299 AlbumPostProcess(albumID); 15300 if( fnToCall !== null && fnToCall !== undefined) { 15301 fnToCall( fnParam1, fnParam2, null ); 15302 } 15303 15304 }) 15305 .fail( function(jqxhr, textStatus, error) { 15306 clearTimeout(tId); 15307 PreloaderDisplay(false); 15308 15309 var k='' 15310 for(var key in jqxhr) { 15311 k+= key + '=' + jqxhr[key] +'<br>'; 15312 } 15313 var err = textStatus + ', ' + error + ' ' + k + '<br><br>URL:'+url; 15314 NanoAlert(G, "Could not retrieve Google data. Error: " + err); 15315 }); 15316 } 15317 catch(e) { 15318 NanoAlert(G, "Could not retrieve Google data. Error: " + e); 15319 } 15320 } 15321 15322 15323 // ----------- 15324 // Retrieve items from a Google Photos data stream 15325 // items can be images/viedos or albums 15326 function GoogleParseData(albumIdx, kind, data) { 15327 15328 if( G.O.debugMode ) { 15329 console.log('Google Photos data:'); 15330 console.dir(data); 15331 } 15332 var albumID = G.I[albumIdx].GetID(); 15333 15334 // iterate and parse each item 15335 jQuery.each(data, function(i,data){ 15336 15337 if( typeof data === 'object' && data !== null ) { // only objects 15338 15339 var itemDescription = ''; 15340 var itemTitle = ''; 15341 if( kind == 'image') { 15342 itemTitle = data.description; 15343 } 15344 else { 15345 itemTitle = data.title; 15346 } 15347 if( itemTitle == undefined ) { 15348 // may happen... 15349 itemTitle = ''; 15350 } 15351 15352 var itemID = data.id; 15353 if( kind == 'album' ) { 15354 if( !FilterAlbumName(itemTitle, itemID) || data.coverPhotoBaseUrl == undefined ) { 15355 return true; 15356 } 15357 } 15358 15359 // create ngy2 item 15360 var newItem = NGY2Item.New( G, itemTitle, itemDescription, itemID, albumID, kind, '' ); 15361 15362 var width = 0; 15363 var height = 0; 15364 15365 // set the image src 15366 var src = ''; 15367 if( kind == 'image' ) { 15368 src = data.baseUrl; 15369 if( !G.O.viewerZoom && G.O.viewerZoom != undefined ) { 15370 if( window.screen.width > window.screen.height ) { 15371 src += '=w' + window.screen.width; 15372 } 15373 else { 15374 src = s + '=h' + window.screen.height; 15375 } 15376 } 15377 else { 15378 // use full resolution image 15379 src += '=h' + data.mediaMetadata.height + '-w' + data.mediaMetadata.width; 15380 15381 // use original image 15382 // src += '=d'; 15383 } 15384 15385 // image's URL 15386 newItem.setMediaURL( src, 'img', description); 15387 15388 // image size 15389 if( data.mediaMetadata.width !== undefined ) { 15390 newItem.imageWidth = parseInt(data.mediaMetadata.width); 15391 width = newItem.imageWidth; 15392 } 15393 if( data.mediaMetadata.height !== undefined ) { 15394 newItem.imageHeight=parseInt(data.mediaMetadata.height); 15395 height = newItem.imageHeight; 15396 } 15397 15398 // if( data.media$group != null && data.media$group.media$credit != null && data.media$group.media$credit.length > 0 ) { 15399 // newItem.author=data.media$group.media$credit[0].$t; 15400 // } 15401 15402 // Photo 15403 if( data.mediaMetadata.photo !== undefined ) { 15404 // exif data 15405 if( data.mediaMetadata.photo.exposureTime != undefined ) { 15406 newItem.exif.exposure = data.mediaMetadata.photo.exposureTime; 15407 } 15408 if( data.mediaMetadata.photo.focalLength != undefined ) { 15409 newItem.exif.focallength = data.mediaMetadata.photo.focalLength; 15410 } 15411 if( data.mediaMetadata.photo.apertureFNumber != undefined ) { 15412 newItem.exif.fstop = data.mediaMetadata.photo.apertureFNumber; 15413 } 15414 if( data.mediaMetadata.photo.isoEquivalent != undefined ) { 15415 newItem.exif.iso = data.mediaMetadata.photo.isoEquivalent; 15416 } 15417 if( data.mediaMetadata.photo.cameraModel != undefined ) { 15418 newItem.exif.model = data.mediaMetadata.photo.cameraModel; 15419 } 15420 } 15421 15422 // Video 15423 if( data.mediaMetadata.video !== undefined ) { 15424 if( data.mediaMetadata.video.cameraModel != undefined ) { 15425 newItem.exif.model = data.mediaMetadata.video.cameraModel; 15426 } 15427 15428 newItem.downloadURL = data.baseUrl + '=dv'; // set the download URL for the video 15429 15430 // newItem.mediaKind = 'selfhosted'; 15431 // newItem.mediaMarkup = '<video controls class="nGY2ViewerMedia"><source src="'+ newItem.src +'" type="video/'+ 'video/mp4' +'" preload="auto">Your browser does not support the video tag (HTML 5).</video>'; 15432 } 15433 15434 } 15435 else { 15436 // newItem.author = data.author[0].name.$t; 15437 newItem.numberItems = data.mediaItemsCount; 15438 } 15439 15440 // set the URL of the thumbnails images 15441 newItem.thumbs=GoogleThumbSetSizes2('l1', newItem.thumbs, data, kind, height, width ); 15442 newItem.thumbs=GoogleThumbSetSizes2('lN', newItem.thumbs, data, kind,height ,width ); 15443 15444 // post-process callback 15445 var fu = G.O.fnProcessData; 15446 if( fu !== null ) { 15447 typeof fu == 'function' ? fu(newItem, 'google2', data) : window[fu](newItem, 'google2', data); 15448 } 15449 15450 } 15451 }); 15452 15453 G.I[albumIdx].contentIsLoaded = true; // album's content is ready 15454 } 15455 15456 // ----------- 15457 // Set thumbnail sizes (width and height) and URLs (for all resolutions (xs, sm, me, la, xl) and levels (l1, lN) 15458 function GoogleThumbSetSizes2(level, tn, data, kind, height, width ) { 15459 var sizes=['xs','sm','me','la','xl']; 15460 15461 for(var i=0; i<sizes.length; i++ ) { 15462 15463 // media 15464 if( kind == 'image' ) { 15465 if( G.tn.settings.width[level][sizes[i]] == 'auto' ) { 15466 var ratio1 = width / height; 15467 tn.height[level][sizes[i]] = G.tn.settings.getH(level, sizes[i]); 15468 tn.width[level][sizes[i]] = G.tn.settings.getH(level, sizes[i]) * ratio1; 15469 tn.url[level][sizes[i]] = data.baseUrl + '=h' + G.tn.settings.getH(level, sizes[i]); 15470 continue; 15471 } 15472 if( G.tn.settings.height[level][sizes[i]] == 'auto' ) { 15473 var ratio1 = height / width; 15474 tn.width[level][sizes[i]] = G.tn.settings.getW(level, sizes[i]); 15475 tn.height[level][sizes[i]] = G.tn.settings.getW(level, sizes[i]) * ratio1; 15476 tn.url[level][sizes[i]] = data.baseUrl + '=w' + G.tn.settings.getW(level, sizes[i]); 15477 continue; 15478 } 15479 15480 tn.height[level][sizes[i]] = G.tn.settings.getH(level, sizes[i]); 15481 tn.width[level][sizes[i]] = G.tn.settings.getW(level, sizes[i]); 15482 tn.url[level][sizes[i]] = data.baseUrl + '=w' + G.tn.settings.getW(level, sizes[i]); 15483 15484 } 15485 15486 // album 15487 if( kind == 'album' ) { 15488 if( G.tn.settings.width[level][sizes[i]] == 'auto' ) { 15489 tn.url[level][sizes[i]]= data.coverPhotoBaseUrl + '=h' + G.tn.settings.getH(level, sizes[i]); 15490 continue; 15491 } 15492 if( G.tn.settings.height[level][sizes[i]] == 'auto' ) { 15493 tn.url[level][sizes[i]]= data.coverPhotoBaseUrl + '=w' + G.tn.settings.getW(level, sizes[i]); 15494 continue; 15495 } 15496 var w=G.tn.settings.mosaic[level + 'Factor']['w'][sizes[i]]; 15497 tn.url[level][sizes[i]]= data.coverPhotoBaseUrl + '=h' + G.tn.settings.getH(level, sizes[i]) + '-w' + G.tn.settings.getW(level, sizes[i]); 15498 15499 } 15500 } 15501 15502 return tn; 15503 } 15504 15505 15506 15507 // ----------- 15508 // Initialization 15509 function Init() { 15510 } 15511 15512 15513 // shortcuts to NGY2Tools functions (with context) 15514 var PreloaderDisplay = NGY2Tools.PreloaderDisplay.bind(G); 15515 // var NanoAlert = NGY2Tools.NanoAlert.bind(G); 15516 var NanoAlert = NGY2Tools.NanoAlert; 15517 var GetImageTitleFromURL = NGY2Tools.GetImageTitleFromURL.bind(G); 15518 var FilterAlbumName = NGY2Tools.FilterAlbumName.bind(G); 15519 var AlbumPostProcess = NGY2Tools.AlbumPostProcess.bind(G); 15520 15521 switch( fnName ){ 15522 case 'AlbumGetContent': 15523 var albumID = arguments[2], 15524 callback2 = arguments[3], 15525 cbParam1 = arguments[4], 15526 cbParam2 = arguments[5]; 15527 AlbumGetContent(albumID, callback2, cbParam1, cbParam2); 15528 break; 15529 case 'Init': 15530 Init(); 15531 break; 15532 case '': 15533 break; 15534 } 15535 15536 }; 15537 15538// END GOOGLE DATA SOURCE FOR NANOGALLERY2 15539// }( jQuery )); 15540})); 15541 15542 15543 15544 15545/**! 15546 * @preserve nanogallery2 - FLICKR data provider 15547 * Homepage: http://nanogallery2.nanostudio.org 15548 * Sources: https://github.com/nanostudio-org/nanogallery2 15549 * 15550 * License: GPLv3 and commercial licence 15551 * 15552*/ 15553 15554// ############################################ 15555// ##### nanogallery2 - module for FLICKR ##### 15556// ############################################ 15557 15558 15559(function (factory) { 15560 "use strict"; 15561 if (typeof define === 'function' && define.amd) { 15562 // AMD. Register as an anonymous module. 15563 define(['jquery', 'nanogallery2'], factory); 15564 } else if (typeof exports === 'object' && typeof require === 'function') { 15565 // Browserify 15566 factory(require(['jquery', 'nanogallery2'])); 15567 } else { 15568 // Browser globals 15569 factory(jQuery); 15570 } 15571}(function ($) { 15572// ;(function ($) { 15573 15574 jQuery.nanogallery2.data_flickr = function (instance, fnName){ 15575 var G = instance; // current nanogallery2 instance 15576 15577 // ### Flickr 15578 // Details: http://www.flickr.com/services/api/misc.urls.html 15579 var Flickr = { 15580 url: function() { 15581 // Flickr API Going SSL-Only on June 27th, 2014 15582 return 'https://api.flickr.com/services/rest/'; 15583 }, 15584 thumbSize:' sq', 15585 thumbAvailableSizes : new Array(75, 100, 150, 240, 500, 640), 15586 thumbAvailableSizesStr : new Array('sq', 't', 'q', 's', 'm', 'z'), 15587 photoSize : '0', 15588 photoAvailableSizes : new Array(75, 100, 150, 240, 500, 640, 1024, 1024, 1600, 2048, 10000), 15589 photoAvailableSizesStr : new Array('sq', 't', 'q', 's', 'm', 'z', 'b', 'l', 'h', 'k', 'o'), 15590 ApiKey : "2f0e634b471fdb47446abcb9c5afebdc" 15591 }; 15592 15593 15594 /** @function AlbumGetContent */ 15595 var AlbumGetContent = function(albumID, fnToCall, fnParam1, fnParam2) { 15596 15597 var albumIdx = NGY2Item.GetIdx(G, albumID); 15598 var url = ''; 15599 var kind = 'image'; 15600 // photos 15601 if( G.O.photoset.toUpperCase() == 'NONE' || G.O.album.toUpperCase() == 'NONE' ) { 15602 // get photos from full photostream 15603 url = Flickr.url() + "?&method=flickr.people.getPublicPhotos&api_key=" + Flickr.ApiKey + "&user_id="+G.O.userID+"&extras=description,views,tags,url_o,url_sq,url_t,url_q,url_s,url_m,url_z,url_b,url_h,url_k&per_page=500&format=json"; 15604 } 15605 else 15606 if( G.I[albumIdx].GetID() == 0 ) { 15607 // retrieve the list of albums 15608 url = Flickr.url() + "?&method=flickr.photosets.getList&api_key=" + Flickr.ApiKey + "&user_id="+G.O.userID+"&per_page=500&primary_photo_extras=tags,url_o,url_sq,url_t,url_q,url_s,url_m,url_l,url_z,url_b,url_h,url_k&format=json"; 15609 kind='album'; 15610 } 15611 else { 15612 // photos from one specific photoset 15613 url = Flickr.url() + "?&method=flickr.photosets.getPhotos&api_key=" + Flickr.ApiKey + "&photoset_id="+G.I[albumIdx].GetID()+"&extras=description,views,tags,url_o,url_sq,url_t,url_q,url_s,url_m,url_l,url_z,url_b,url_h,url_k&format=json"; 15614 } 15615 15616 if( G.O.debugMode ) { console.log('Flickr URL: ' + url); } 15617 15618 PreloaderDisplay(true); 15619 jQuery.ajaxSetup({ cache: false }); 15620 jQuery.support.cors = true; 15621 15622 var tId = setTimeout( function() { 15623 // workaround to handle JSONP (cross-domain) errors 15624 PreloaderDisplay(false); 15625 NanoAlert(G, 'Could not retrieve AJAX data...'); 15626 }, 60000 ); 15627 15628 var sourceData=[]; 15629 15630 // Process the downloaded data 15631 var FlickrGetDone = function() { 15632 clearTimeout(tId); 15633 PreloaderDisplay(false); 15634 15635 if( kind == 'album' ) { 15636 FlickrParsePhotoSets(albumIdx, albumID, sourceData); 15637 } 15638 else { 15639 FlickrParsePhotos(albumIdx, albumID, sourceData); 15640 } 15641 15642 AlbumPostProcess(albumID); 15643 if( fnToCall !== null && fnToCall !== undefined) { 15644 fnToCall( fnParam1, fnParam2, null ); 15645 } 15646 } 15647 15648 // download one page of data (=500 entries) 15649 var FlickrGetOnePage = function( url, page ) { 15650 jQuery.getJSON( url + '&page=' + page + '&jsoncallback=?', function(data, status, xhr) { 15651 15652 var pages=0; 15653 if( kind == 'album' ) { 15654 if( data.stat !== undefined && data.stat === 'fail' ) { 15655 NanoAlert(G, "Could not retrieve Flickr album list: " + data.message + " (code: "+data.code+")."); 15656 return false; 15657 } 15658 sourceData=sourceData.concat(data.photosets.photoset); 15659 pages=data.photosets.pages; 15660 } 15661 else { 15662 if( G.O.photoset.toUpperCase() == 'NONE' || G.O.album.toUpperCase() == 'NONE' ) { 15663 // content of full photoset 15664 sourceData=sourceData.concat(data.photos.photo); 15665 pages=data.photos.pages; 15666 } 15667 else { 15668 // content of one album 15669 if( data.stat !== undefined && data.stat === 'fail' ) { 15670 NanoAlert(G, "Could not retrieve Flickr album: " + data.message + " (code: "+data.code+")."); 15671 return false; 15672 } 15673 if( G.I[albumIdx].title == '' ) { 15674 G.I[albumIdx].title=data.photoset.title; 15675 } 15676 sourceData=sourceData.concat(data.photoset.photo); 15677 pages=data.photoset.pages; 15678 } 15679 15680 } 15681 15682 if( pages > page ) { 15683 FlickrGetOnePage(url, page+1); 15684 } 15685 else { 15686 FlickrGetDone(); 15687 } 15688 }) 15689 .fail( function(jqxhr, textStatus, error) { 15690 clearTimeout(tId); 15691 PreloaderDisplay(false); 15692 NanoAlert(G, "Could not retrieve Flickr ajax data: " + textStatus + ', ' + error); 15693 }); 15694 15695 } 15696 15697 FlickrGetOnePage(url, 1); 15698 15699 } 15700 15701 15702 15703 // ----------- 15704 // Retrieve items for one Flickr photoset 15705 function FlickrParsePhotos( albumIdx, albumID, source ) { 15706 15707 if( G.O.debugMode ) { 15708 console.log('Flickr parse photos:'); 15709 console.dir(source); 15710 } 15711 15712 jQuery.each(source, function(i,item){ 15713 15714 var itemID = item.id; 15715 15716 var imgUrl=item.url_sq; //fallback size 15717 15718 // get the title 15719 var itemTitle = item.title; 15720 if( G.O.thumbnailLabel.get('title') != '' ) { 15721 itemTitle=GetImageTitleFromURL(imgUrl); 15722 } 15723 15724 // get the description 15725 var itemDescription=item.description._content; 15726 15727 // retrieve the image size with highest available resolution 15728 var imgW=75, imgH=75; 15729 var start=Flickr.photoAvailableSizesStr.length-1; 15730 if( G.O.flickrSkipOriginal ) { start--; } 15731 for( var i = start; i>=0 ; i-- ) { 15732 if( item['url_'+Flickr.photoAvailableSizesStr[i]] != undefined ) { 15733 imgUrl=item['url_'+Flickr.photoAvailableSizesStr[i]]; 15734 imgW=parseInt(item['width_'+Flickr.photoAvailableSizesStr[i]]); 15735 imgH=parseInt(item['height_'+Flickr.photoAvailableSizesStr[i]]); 15736 break; 15737 } 15738 } 15739 15740 var sizes = {}; 15741 for( var p in item ) { 15742 if( p.indexOf('height_') == 0 || p.indexOf('width_') == 0 || p.indexOf('url_') == 0 ) { 15743 sizes[p]=item[p]; 15744 } 15745 } 15746 15747 // tags 15748 var tags = item.tags !== undefined ? item.tags : ''; 15749 15750 // create item 15751 var newItem = NGY2Item.New( G, itemTitle, itemDescription, itemID, albumID, 'image', tags ); 15752 15753 // add image 15754 newItem.setMediaURL( imgUrl, 'img', description); 15755 newItem.imageWidth = imgW; 15756 newItem.imageHeight = imgH; 15757 15758 15759 // add thumbnails 15760 var tn = { 15761 url: { l1 : { xs:'', sm:'', me:'', la:'', xl:'' }, lN : { xs:'', sm:'', me:'', la:'', xl:'' } }, 15762 width: { l1 : { xs:0, sm:0, me:0, la:0, xl:0 }, lN : { xs:0, sm:0, me:0, la:0, xl:0 } }, 15763 height: { l1 : { xs:0, sm:0, me:0, la:0, xl:0 }, lN : { xs:0, sm:0, me:0, la:0, xl:0 } } 15764 }; 15765 tn=FlickrRetrieveImages(tn, item, 'l1' ); 15766 tn=FlickrRetrieveImages(tn, item, 'lN' ); 15767 newItem.thumbs=tn; 15768 15769 // post-process callback 15770 var fu = G.O.fnProcessData; 15771 if( fu !== null ) { 15772 typeof fu == 'function' ? fu(newItem, 'flickr', item) : window[fu](newItem, 'flickr', item); 15773 } 15774 15775 15776 }); 15777 G.I[albumIdx].contentIsLoaded=true; 15778 15779 } 15780 15781 15782 15783 // ----------- 15784 // Retrieve the list of Flickr photosets 15785 function FlickrParsePhotoSets( albumIdx, albumID, source ) { 15786 15787 if( G.O.debugMode ) { 15788 console.log('Flickr parse list of albums:'); 15789 console.dir(source); 15790 } 15791 15792 jQuery.each(source, function(i,item){ 15793 //Get the title 15794 var itemTitle = item.title._content; 15795 15796 if( item.visibility_can_see_set == 0 ) { return true; } // skip it 15797 15798 if( FilterAlbumName(itemTitle, item.id) ) { 15799 var itemID=item.id; 15800 //Get the description 15801 var itemDescription = item.description._content != undefined ? item.description._content : ''; 15802 15803 var sizes = {}; 15804 for( var p in item.primary_photo_extras) { 15805 sizes[p] = item.primary_photo_extras[p]; 15806 } 15807 var tags=''; 15808 if( item.primary_photo_extras !== undefined ) { 15809 if( item.primary_photo_extras.tags !== undefined ) { 15810 tags = item.primary_photo_extras.tags; 15811 } 15812 } 15813 15814 var newItem = NGY2Item.New( G, itemTitle, itemDescription, itemID, albumID, 'album', tags ); 15815 newItem.numberItems = item.photos; 15816 newItem.thumbSizes = sizes; 15817 15818 var tn = { 15819 url: { l1 : { xs:'', sm:'', me:'', la:'', xl:'' }, lN : { xs:'', sm:'', me:'', la:'', xl:'' } }, 15820 width: { l1 : { xs:0, sm:0, me:0, la:0, xl:0 }, lN : { xs:0, sm:0, me:0, la:0, xl:0 } }, 15821 height: { l1 : { xs:0, sm:0, me:0, la:0, xl:0 }, lN : { xs:0, sm:0, me:0, la:0, xl:0 } } 15822 }; 15823 tn = FlickrRetrieveImages(tn, item.primary_photo_extras, 'l1' ); 15824 tn = FlickrRetrieveImages(tn, item.primary_photo_extras, 'lN' ); 15825 newItem.thumbs = tn; 15826 15827 // post-process callback 15828 var fu = G.O.fnProcessData; 15829 if( fu !== null ) { 15830 typeof fu == 'function' ? fu(newItem, 'flickr', item) : window[fu](newItem, 'flickr', item); 15831 } 15832 15833 } 15834 }); 15835 15836 G.I[albumIdx].contentIsLoaded=true; 15837 } 15838 15839 function FlickrRetrieveImages(tn, item, level ) { 15840 15841 var sf=1; 15842 if( G.tn.opt[level].crop === true ) { 15843 sf=G.O.thumbnailCropScaleFactor; 15844 } 15845 15846 15847 var sizes=['xs','sm','me','la','xl']; 15848 for( var i=0; i<sizes.length; i++ ) { 15849 if( G.tn.settings.width[level][sizes[i]] == 'auto' || G.tn.settings.width[level][sizes[i]] == '' ) { 15850 var sdir='height_'; 15851 var tsize=Math.ceil( G.tn.settings.height[level][sizes[i]] * G.tn.scale * sf * G.tn.settings.mosaic[level+'Factor']['h'][sizes[i]] ); 15852 var one=FlickrRetrieveOneImage(sdir, tsize, item ); 15853 tn.url[level][sizes[i]]=one.url; 15854 tn.width[level][sizes[i]]=one.width; 15855 tn.height[level][sizes[i]]=one.height; 15856 } 15857 else 15858 if( G.tn.settings.height[level][sizes[i]] == 'auto' || G.tn.settings.height[level][sizes[i]] == '' ) { 15859 var sdir='width_'; 15860 var tsize=Math.ceil( G.tn.settings.width[level][sizes[i]] * G.tn.scale * sf * G.tn.settings.mosaic[level+'Factor']['w'][sizes[i]] ); 15861 var one=FlickrRetrieveOneImage(sdir, tsize, item ); 15862 tn.url[level][sizes[i]]=one.url; 15863 tn.width[level][sizes[i]]=one.width; 15864 tn.height[level][sizes[i]]=one.height; 15865 } 15866 else { 15867 var sdir='height_'; 15868 var tsize=Math.ceil( G.tn.settings.height[level][sizes[i]] * G.tn.scale * sf * G.tn.settings.mosaic[level+'Factor']['h'][sizes[i]] ); 15869 if( G.tn.settings.width[level][sizes[i]] > G.tn.settings.height[level][sizes[i]] ) { 15870 sdir='width_'; 15871 tsize=Math.ceil( G.tn.settings.width[level][sizes[i]] * G.tn.scale * sf * G.tn.settings.mosaic[level+'Factor']['w'][sizes[i]] ); 15872 } 15873 var one=FlickrRetrieveOneImage(sdir, tsize, item ); 15874 tn.url[level][sizes[i]]=one.url; 15875 tn.width[level][sizes[i]]=one.width; 15876 tn.height[level][sizes[i]]=one.height; 15877 } 15878 } 15879 return tn; 15880 } 15881 15882 function FlickrRetrieveOneImage(sdir, tsize, item ) { 15883 var one={ url: '', width: 0, height: 0 }; 15884 var tnIndex=0; 15885 for( var j=0; j < Flickr.thumbAvailableSizes.length; j++ ) { 15886 var size=item[sdir+Flickr.photoAvailableSizesStr[j]]; 15887 if( size != undefined ) { 15888 tnIndex=j; 15889 if( size >= tsize ) { 15890 break; 15891 } 15892 } 15893 } 15894 var fSize=Flickr.photoAvailableSizesStr[tnIndex]; 15895 one.url = item['url_'+fSize]; 15896 one.width = parseInt(item['width_'+fSize]); 15897 one.height = parseInt(item['height_'+fSize]); 15898 return one; 15899 } 15900 15901 15902 /** @function GetHiddenAlbums */ 15903 var GetHiddenAlbums = function( hiddenAlbums, callback ){ 15904 // not supported -> doesn't exit in Flickr 15905 callback(); 15906 } 15907 15908 // ----------- 15909 // Initialize thumbnail sizes 15910 function Init() { 15911 return; 15912 } 15913 15914 15915 // shortcuts to NGY2Tools functions (with context) 15916 var PreloaderDisplay = NGY2Tools.PreloaderDisplay.bind(G); 15917 var NanoAlert = NGY2Tools.NanoAlert; 15918 var GetImageTitleFromURL = NGY2Tools.GetImageTitleFromURL.bind(G); 15919 var FilterAlbumName = NGY2Tools.FilterAlbumName.bind(G); 15920 var AlbumPostProcess = NGY2Tools.AlbumPostProcess.bind(G); 15921 15922 switch( fnName ){ 15923 case 'GetHiddenAlbums': 15924 var hiddenAlbums = arguments[2], 15925 callback = arguments[3]; 15926 GetHiddenAlbums(hiddenAlbums, callback); 15927 break; 15928 case 'AlbumGetContent': 15929 var albumID = arguments[2], 15930 callback = arguments[3], 15931 cbParam1 = arguments[4], 15932 cbParam2 = arguments[5]; 15933 AlbumGetContent(albumID, callback, cbParam1, cbParam2); 15934 break; 15935 case 'Init': 15936 Init(); 15937 break; 15938 case '': 15939 break; 15940 } 15941 15942 }; 15943 15944// END FLICKR DATA SOURCE FOR NANOGALLERY2 15945// }( jQuery )); 15946})); 15947 15948 15949
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.