PageSourceSearch

http://ccastreetsboro.com/js/jquery.nanogallery.js

js ccastreetsboro.com collected 2026-10-03 02:31:42 UTC 331,986 bytes, 9,285 lines download raw bytes

1/**!
2 * @preserve nanoGALLERY v5.2.3
3 * Plugin for jQuery by Christophe Brisbois
4 * Demo: http://nanogallery.brisbois.fr
5 * Sources: https://github.com/Kris-B/nanoGALLERY
6 *
7 * License: For personal, non-profit organizations, or open source projects (without any kind of fee), you may use nanoGALLERY for jQuery for free. 
8 * -------- ALL OTHER USES REQUIRE THE PURCHASE OF A PROFESSIONAL LICENSE.
9 * 
10 * 
11 * Components:
12 *  - jQuery (http://www.jquery.com) - version >= 1.7.1
13 *  - jQuery Color plugin - is embedded
14 *  - imagesloaded (https://github.com/desandro/imagesloaded) - is embebed
15 *  - screenfull.js (https://github.com/sindresorhus/screenfull.js) - is embeded
16 *  - webfont generated by http://fontello.com - based on Font Awesome Copyright (C) 2012 by Dave Gandy (http://fortawesome.github.com/Font-Awesome/)
17 *  - http://closure-compiler.appspot.com/home - minifying javascript
18 *  - http://gpbmike.github.io/refresh-sf/ - minifying css
19 */
20 
21
22/*
23
24nanoGALLERY v5.2.3 release notes.
25
26##### New features
27- **picasaUseUrlCrossDomain**: access Picasa/Google+ using the cross domain URL instead of the standard one.
28  *boolean; Default: true*  
29- **supportIE8**: enable IE8 support.
30  *boolean; Default: true*  
31
32##### Misc
33- bugfix #53 (Chrome browser) scrollbar not enabled back after closing an image displayed in fullscreen
34- bugfix hover out thumbnail animation not triggered
35- bugfix image URL with spaces not supported
36- bugfix imageTransition:'fade' not working
37- bugfix #58 pagination issue when only used on second level
38
39**Visit nanoGALLERY homepage for usage details: [http://nanogallery.brisbois.fr](http://www.nanogallery.brisbois.fr/)**
40
41*/
42
43
44// ##########################################
45// ##### nanoGALLERY as a JQUERY PLUGIN #####
46// ##########################################
47
48;(function ($) {
49  var nO=null;
50
51  jQuery.fn.nanoGallery = function (options) {
52    var settings = $.extend(true, {
53      // default settings
54      userID : '',
55      kind : '',
56      album : '',
57      photoset : '',
58      blackList : 'scrapbook|profil',
59      whiteList : '',
60      albumList : '',
61      RTL : false,
62      picasaUseUrlCrossDomain : true,
63      galleryToolbarWidthAligned : true,
64      galleryToolbarHideIcons : false,
65      galleryFullpageButton : false,
66      galleryFullpageBgColor : '#111',
67      galleryRenderStep : 10,
68      breadcrumbAutoHideTopLevel : false,
69      displayBreadcrumb : false,
70      theme : 'default',
71      colorScheme : 'none',
72      colorSchemeViewer : 'default',
73      items : null,
74      itemsBaseURL : '',
75      paginationMaxLinesPerPage : 0,
76      maxWidth : 0,
77      viewer : 'internal',
78      fancyBoxOptions : null,
79      viewerDisplayLogo : false,
80      imageTransition : 'slide',
81      viewerToolbar : { display:true, position : 'bottom', style : 'innerImage', autoMinimize:800,
82          standard:'minimizeButton , previousButton, pageCounter ,nextButton,playPauseButton,fullscreenButton,infoButton,linkOriginalButton,closeButton,label',
83          minimized:'minimizeButton,label'  
84        },
85      thumbnailAlignment : 'center',
86      thumbnailWidth : 230,
87      thumbnailHeight : 154,
88      thumbnailGutterWidth : 2,
89      thumbnailGutterHeight : 2,
90      thumbnailAdjustLastRowHeight : true,
91      thumbnailFeatured : false,
92      thumbnailHoverEffect : null,
93      thumbnailLabel : {position : 'overImageOnBottom', display : true, displayDescription : true, titleMaxLength : 0, descriptionMaxLength : 0, hideIcons : false, title : '', itemsCount : '' },
94      thumbnailDisplayInterval : 30,
95      thumbnailDisplayTransition : true,
96      thumbnailLazyLoad : false,
97      thumbnailLazyLoadTreshold : 100,
98      thumbnailGlobalImageTitle : '',
99      thumbnailGlobalAlbumTitle : '',
100      thumbnailSizeSM : 480,
101      thumbnailSizeME : 992,
102      thumbnailSizeLA : 1200,
103      thumbnailSizeXL : 1800,
104      fnThumbnailInit : null,
105      fnThumbnailHoverInit : null,
106      fnThumbnailHoverResize : null,
107      fnThumbnailHover : null,
108      fnThumbnailHoverOut : null,
109      fnThumbnailDisplayEffect : null,
110      fnViewerInfo : null,
111      fnImgToolbarCustInit : null,
112      fnImgToolbarCustDisplay : null,
113      fnImgToolbarCustClick : null,
114      fnProcessData : null,
115      touchAnimation : true,
116      touchAutoOpenDelay : 0,
117      useTags : false,
118      preset : 'none',
119      locationHash : false,
120      slideshowDelay : 3000,
121      slideshowAutoStart : false,
122      photoSorting : '',
123      albumSorting : '',
124      dataSorting : '',
125      lazyBuild : 'none',
126      lazyBuildTreshold : 150,
127      flickrSkipOriginal : true,
128      i18n : {
129        'breadcrumbHome' : 'Galleries', 'breadcrumbHome_FR' : 'Galeries',
130        'paginationPrevious' : 'Previous', 'paginationPrevious_FR' : 'Précédent', 'paginationPrevious_DE' : 'Zurück', 'paginationPrevious_IT' : 'Indietro',
131        'paginationNext' : 'Next', 'paginationNext_FR' : 'Suivant', 'paginationNext_DE' : 'Weiter', 'paginationNext_IT' : 'Avanti',
132        'thumbnailLabelItemsCountPart1' : '', //'| ',
133        'thumbnailLabelItemsCountPart2' : '', //' photos', 'thumbnailLabelItemsCountPart2_DE' : ' Fotos',
134        'thumbnailImageTitle' : '',
135        'thumbnailAlbumTitle' : '',
136        'thumbnailImageDescription' : '',
137        'thumbnailAlbumDescription' : '',
138        'infoBoxPhoto' : 'Photo',
139        'infoBoxDate' : 'Date',
140        'infoBoxAlbum' : 'Album',
141        'infoBoxDimensions' : 'Dimensions',
142        'infoBoxFilename' : 'Filename',
143        'infoBoxFileSize' : 'File size',
144        'infoBoxCamera' : 'Camera',
145        'infoBoxFocalLength' : 'Focal length',
146        'infoBoxExposure' : 'Exposure',
147        'infoBoxFNumber' : 'F Number',
148        'infoBoxISO' : 'ISO',
149        'infoBoxMake' : 'Make',
150        'infoBoxFlash' : 'Flash',
151        'infoBoxViews' : 'Views',
152        'infoBoxComments' : 'Comments'
153      }
154    }, options );
155
156
157    return this.each( function () {
158      // var nanoGALLERY_obj = new nanoGALLERY();
159      nO = new nanoGALLERY();
160      nO.Initiate(this, settings );
161    });
162  };
163
164  jQuery.fn.nanoGallery.TEST = function() {
165    console.dir(nO);
166    //console.dir(this);
167    //alert(nO.N.v);
168    //renderGallery( albumIdx, pageNumber ) {
169  };
170  
171  
172}( jQuery ));
173
174
175// ##############################
176// ##### nanoGALLERY script #####
177// ##############################
178
179function nanoGALLERY() {
180
181    // [TODO] --> move all global variables here
182    // global
183    this.N = {
184      v:1
185    };
186
187    var N = this.N;
188    
189    var g_i18nTranslations = {'paginationPrevious':'Previous', 'paginationNext':'Next', 'breadcrumbHome':'List of Albums', 'thumbnailImageTitle':'', 'thumbnailAlbumTitle':'', 'thumbnailImageDescription':'', 'thumbnailAlbumDescription':'' };
190    var gO = null,
191    gI = [],                  // gallery items
192    $gE = { 
193      base: null,             // $g_baseControl = null,
194      conTnParent: null,      // $g_containerThumbnailsParent
195      conLoadingB: null,      // loading bar - nanoGalleryLBarOff
196      conConsole: null,       // $g_containerConsole
197      conTn: null,            // $g_containerThumbnails
198      conTnHid: null,         // $g_containerThumbnailsHidden
199      conPagin: null,         // $g_containerPagination
200      conBC: null,            // $g_containerBreadcrumb
201      conNavB: null,          // $g_containerNavigationbar
202      conNavBCon: null,       // $g_containerNavigationbarCont
203      conNavBFullpage: null,  // breadcrumb fullpage button
204      conVwCon: null,         // $g_containerViewerContainer
205      conVw: null,            // $g_containerViewer
206      conVwTb: null,          // $g_containerViewerToolbar
207      vwImgP: null,           // $g_ViewerImagePrevious
208      vwImgN: null,           // $g_ViewerImageNext
209      vwImgC: null,           // $g_ViewerImageCurrent
210      vwContent: null,        // $g_containerViewerContent
211      vwLogo: null            // $g_containerViewerLogo
212    },
213    $currentTouchedThumbnail = null,  // currently touched thumbnail
214    g_baseEltID = null,
215    g_containerTags = null,
216    g_containerNavigationbarContDisplayed = false,
217    g_containerViewerDisplayed = false,
218    g_containerThumbnailsDisplayed = false,
219    g_tn = {                          // GENERAL THUMBNAILS PROPERTIES
220      displayInterval: 30,            // delay between 2 thumbnails display
221      lazyLoadTreshold: 100,          // image lazy load treshold in pixel
222      scale: 1,                       // image scale depending of the hover effect
223      borderWidth: 0,                 // thumbnail container border width
224      borderHeight: 0,                // thumbnail container border height
225      imgcBorderHeight: 0,            // image container border height
226      imgcBorderWidth:0 ,             // image container border width
227      labelHeight: 0,                 // in case label on bottom, otherwise =0
228      outerWidth: {                   // default thumbnail outerWidths (not used in case thumbnailWidth='auto'
229        l1 : { xs:0, sm:0, me:0, la:0, xl:0 }, lN : { xs:0, sm:0, me:0, la:0, xl:0 } 
230      },
231      outerHeight: {                  // default thumbnail outerHeights (not used in case thumbnailHeight='auto'
232        l1 : { xs:0, sm:0, me:0, la:0, xl:0 }, lN : { xs:0, sm:0, me:0, la:0, xl:0 }
233      },
234      settings: {                     // user defined width/height to display depending on the screen size
235        width: {  l1 : { xs:0, sm:0, me:0, la:0, xl:0, xsc:'u', smc:'u', mec:'u', lac:'u', xlc:'u' },
236                  lN : { xs:0, sm:0, me:0, la:0, xl:0, xsc:'u', smc:'u', mec:'u', lac:'u', xlc:'u' } },
237        height: { l1 : { xs:0, sm:0, me:0, la:0, xl:0, xsc:'u', smc:'u', mec:'u', lac:'u', xlc:'u' }, 
238                  lN : { xs:0, sm:0, me:0, la:0, xl:0, xsc:'u', smc:'u', mec:'u', lac:'u', xlc:'u' } }
239      }
240    },
241    g_tnHE = [],                      // thumbnail hover effects
242    g_L = {                           // layout informations
243      nbMaxTnPerRow: 0
244      
245    },
246    g_blackList = null,
247    g_whiteList = null,
248    g_albumList = null,
249    g_galleryItemsCount = 0,
250    g_toolbarMode = 'std',            // image toolbar display mode
251    g_playSlideshow = false,          // slideshow mode
252    g_playSlideshowTimerID = 0,       // slideshow mode timer ID
253    g_slideshowDelay = 3000,          // slideshow mode delay
254    g_touchAutoOpenDelayTimerID = 0,
255    g_supportFullscreenAPI = false,
256    g_viewerIsFullscreen = false,
257    g_bodyOverflowInitial = null,
258    g_i18nLang = '',
259    g_timeImgChanged = 0,
260    g_timeLastTouchStart = 0,
261    g_pgMaxNbThumbnailsPerRow = 1,
262    g_pgMaxLinesPerPage = 0,
263    g_lastOpenAlbumID = -1,
264    g_lastLocationHash = '',
265    g_viewerImageIsChanged = false,
266    g_viewerResizeTimerID = -1,
267    g_viewerCurrentItemIdx = -1,
268    g_imageSwipePosX = 0,
269    g_albumIdxToOpenOnViewerClose = -1,
270    g_custGlobals = {},
271    g_delayedAlbumIdx = -1,
272    g_curAlbumIdx = -1,
273    g_delayedSetLocationHash = false,
274    g_viewerSwipe = null,
275    g_aengine = 'animate',
276    g_scrollTimeOut = 0,
277    g_scrollTimeOut2 = 0,
278    g_maxAlbums = 1000000,
279    g_maxPhotos = 1000000,
280    g_curNavLevel = 'l1',
281    g_curWidth = 'me',
282    g_gallerySwipeInitDone = false,
283    g_emptyGif = 'data:image/gif;base64,R0lGODlhEAAQAIAAAP///////yH5BAEKAAEALAAAAAAQABAAAAIOjI+py+0Po5y02ouzPgUAOw==',
284    g_CSStransformName = FirstSupportedPropertyName(["transform", "msTransform", "MozTransform", "WebkitTransform", "OTransform"]),
285    g_CSStransformStyle = FirstSupportedPropertyName(["transformStyle", "msTransformStyle", "MozTransformStyle", "WebkitTransformStyle", "OTransformStyle"]),
286    g_CSSperspective = FirstSupportedPropertyName(["perspective", "msPerspective", "MozPerspective", "WebkitPerspective", "OPerspective"]),
287    g_CSSbackfaceVisibilityName = FirstSupportedPropertyName(["backfaceVisibility", "msBackfaceVisibility", "MozBackfaceVisibility", "WebkitBackfaceVisibility", "OBackfaceVisibility"]),
288    g_CSStransitionName = FirstSupportedPropertyName(["transition", "msTransition", "MozTransition", "WebkitTransition", "OTransition"]),
289    g_CSSanimationName = FirstSupportedPropertyName(["animation", "msAnimation", "MozAnimation", "WebkitAnimation", "OAnimation"]);
290    /* IE detection. Copyright Julian Shapiro - Gist: https://gist.github.com/julianshapiro/9098609 */
291    var g_IE = (function() {
292      if (document.documentMode) {
293        return document.documentMode;
294      }
295      else {
296        for (var i = 7; i > 4; i--) {
297          var div = document.createElement("div");
298          div.innerHTML = "<!--[if IE " + i + "]><span></span><![endif]-->";
299          if (div.getElementsByTagName("span").length) {
300            div = null;
301            return i;
302          }
303        }
304      }
305      return undefined;
306    })();
307    var g_IOSversion = (function() {
308      if (/iP(hone|od|ad)/.test(navigator.platform)) {
309          var v = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/);
310          return [parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || 0, 10)];
311      }
312    })();
313    
314    var g_isIOS = /(iPad|iPhone|iPod)/g.test( navigator.userAgent );
315    var g_isGingerbread =/Android 2\.3\.[3-7]/i.test(navigator.userAgent),
316    g_openNoDelay=false;
317
318    
319    // ### Picasa/Google+
320    // square format : 32, 48, 64, 72, 104, 144, 150, 160 (cropped)
321    // details: https://developers.google.com/picasa-web/docs/2.0/reference
322    var g_picasa = {
323      url: function() {
324        // return (location.protocol=='https:' ? 'https://picasaweb.google.com/data/feed/api/' : 'https://picasaweb.google.com/data/feed/api/');
325        // return 'https://picasaweb.google.com/data/feed/api/';       // standard URL
326        // return 'https://photos.googleapis.com/data/feed/api/';      // cross domain URL
327        return ( gO.picasaUseUrlCrossDomain ? 'https://photos.googleapis.com/data/feed/api/' : 'https://picasaweb.google.com/data/feed/api/');
328      },
329      thumbSize:64,
330      thumbAvailableSizes : new Array(32, 48, 64, 72, 94, 104, 110, 128, 144, 150, 160, 200, 220, 288, 320, 400, 512, 576, 640, 720, 800, 912, 1024, 1152, 1280, 1440, 1600),
331      thumbAvailableSizesCropped : ' 32 48 64 72 104 144 150 160 '
332    };
333    
334    // ### Flickr
335    // Details: http://www.flickr.com/services/api/misc.urls.html
336    var g_flickr = {
337      url: function() {
338        // Flickr API Going SSL-Only on June 27th, 2014
339        return 'https://api.flickr.com/services/rest/';
340      },
341      thumbSize:'sq',
342      thumbSizeX2 : 'sq',
343      thumbAvailableSizes : new Array(75,100,150,240,500,640),
344      thumbAvailableSizesStr : new Array('sq','t','q','s','m','z'),
345      photoSize : 'sq',
346      photoAvailableSizes : new Array(75,100,150,240,500,640,1024,1024,1600,2048),
347      photoAvailableSizesStr : new Array('sq','t','q','s','m','z','b','l','h','k'),
348      ApiKey : "2f0e634b471fdb47446abcb9c5afebdc"
349    };
350    
351  // Color schemes - Gallery
352  var g_colorScheme_default = {
353    navigationbar : { background:'none', borderTop:'1px solid #555', borderBottom:'1px solid #555', borderRight:'', borderLeft:'', color:'#ccc', colorHover:'#fff' },
354    thumbnail : { background:'#000', border:'1px solid #000', labelBackground:'rgba(34, 34, 34, 0.75)', titleColor:'#eee', titleShadow:'', descriptionColor:'#ccc', descriptionShadow:''}
355  },
356  g_colorScheme_darkRed = {
357    // #ffa3a3 #ff7373 #ff4040 #ff0000 #a60000
358    navigationbar : { background:'#a60000', border:'1px dotted #ff0000', color:'#ccc', colorHover:'#fff' },
359    thumbnail : { background:'#a60000', border:'1px solid #ff0000', labelBackground:'rgba(134, 0, 0, 0.75)', titleColor:'#eee', titleShadow:'', descriptionColor:'#ccc', descriptionShadow:''}
360  },
361  g_colorScheme_darkGreen = {
362    // #97e697 #67e667 #39e639 #00cc00 #008500
363    navigationbar : { background:'#008500', border:'1px dotted #00cc00', color:'#ccc', colorHover:'#fff' },
364    thumbnail : { background:'#008500', border:'1px solid #00cc00', labelBackground:'rgba(0, 105, 0, 0.75)', titleColor:'#eee', titleShadow:'', descriptionColor:'#ccc', descriptionShadow:''}
365  },
366  g_colorScheme_darkBlue = {
367    // #a0b0d7 #7080d7 #4a60d7 #162ea2 #071871
368    navigationbar : { background:'#071871', border:'1px dotted #162ea2', color:'#ccc', colorHover:'#fff' },
369    thumbnail : { background:'#071871', border:'1px solid #162ea2', labelBackground:'rgba(7, 8, 81, 0.75)', titleColor:'#eee', titleShadow:'', descriptionColor:'#ccc', descriptionShadow:''}
370  },
371  g_colorScheme_darkOrange = {
372    // #ffd7b7 #ffd773 #ffc840 #ffb600 #a67600
373    navigationbar : { background:'#a67600', border:'1px dotted #ffb600', color:'#ccc', colorHover:'#fff' },
374    thumbnail : { background:'#a67600', border:'1px solid #ffb600', labelBackground:'rgba(134, 86, 0, 0.75)', titleColor:'#eee', titleShadow:'', descriptionColor:'#ccc', descriptionShadow:''}
375  },
376  g_colorScheme_light = {
377    navigationbar : { background:'none', borderTop:'1px solid #ddd', borderBottom:'1px solid #ddd', borderRight:'', borderLeft:'', color:'#777', colorHover:'#eee' },
378    thumbnail : { background:'#fff', border:'1px solid #fff', labelBackground:'rgba(60, 60, 60, 0.75)', titleColor:'#fff', titleShadow:'none', descriptionColor:'#eee', descriptionShadow:'none'}
379  },
380  g_colorScheme_lightBackground = {
381    navigationbar : { background:'none', border:'', color:'#000', colorHover:'#444' },
382    thumbnail : { background:'#000', border:'1px solid #000', labelBackground:'rgba(34, 34, 34, 0.85)', titleColor:'#fff', titleShadow:'', descriptionColor:'#eee', descriptionShadow:''}
383  };
384
385
386  // Color schemes - lightbox
387  var g_colorSchemeViewer_default = {
388    background:'#000', imageBorder:'4px solid #000', imageBoxShadow:'#888 0px 0px 0px', barBackground:'rgba(4, 4, 4, 0.7)', barBorder:'0px solid #111', barColor:'#eee', barDescriptionColor:'#aaa'
389    //background:'rgba(1, 1, 1, 0.75)', imageBorder:'4px solid #f8f8f8', imageBoxShadow:'#888 0px 0px 20px', barBackground:'rgba(4, 4, 4, 0.7)', barBorder:'0px solid #111', barColor:'#eee', barDescriptionColor:'#aaa'
390  },
391  g_colorSchemeViewer_dark = {
392    background:'rgba(1, 1, 1, 0.75)', imageBorder:'4px solid #f8f8f8', imageBoxShadow:'#888 0px 0px 20px', barBackground:'rgba(4, 4, 4, 0.7)', barBorder:'0px solid #111', barColor:'#eee', barDescriptionColor:'#aaa'
393  },
394  g_colorSchemeViewer_darkRed = {
395    // #ffa3a3 #ff7373 #ff4040 #ff0000 #a60000
396    background:'rgba(1, 1, 1, 0.75)', imageBorder:'4px solid #ffa3a3', imageBoxShadow:'#ff0000 0px 0px 20px', barBackground:'#a60000', barBorder:'2px solid #111', barColor:'#eee', barDescriptionColor:'#aaa'
397  },
398  g_colorSchemeViewer_darkGreen = {
399    // #97e697 #67e667 #39e639 #00cc00 #008500
400    background:'rgba(1, 1, 1, 0.75)', imageBorder:'4px solid #97e697', imageBoxShadow:'#00cc00 0px 0px 20px', barBackground:'#008500', barBorder:'2px solid #111', barColor:'#eee', barDescriptionColor:'#aaa'
401  },
402  g_colorSchemeViewer_darkBlue = {
403    // #a0b0d7 #7080d7 #4a60d7 #162ea2 #071871
404    background:'rgba(1, 1, 1, 0.75)', imageBorder:'4px solid #a0b0d7', imageBoxShadow:'#162ea2 0px 0px 20px', barBackground:'#071871', barBorder:'2px solid #111', barColor:'#eee', barDescriptionColor:'#aaa'
405  },
406  g_colorSchemeViewer_darkOrange = {
407    // #ffd7b7 #ffd773 #ffc840 #ffb600 #a67600
408    background:'rgba(1, 1, 1, 0.75)', imageBorder:'4px solid #ffd7b7', imageBoxShadow:'#ffb600 0px 0px 20px', barBackground:'#a67600', barBorder:'2px solid #111', barColor:'#eee', barDescriptionColor:'#aaa'
409  },
410  g_colorSchemeViewer_light = {
411    background:'rgba(187, 187, 187, 0.75)', imageBorder:'none', imageBoxShadow:'#888 0px 0px 0px', barBackground:'rgba(4, 4, 4, 0.7)', barBorder:'0px solid #111', barColor:'#eee', barDescriptionColor:'#aaa'
412  };
413  
414
415  
416  /* ##### THUMBNAIL ELEMENT STRUCTURE #####
417   
418  +--------------------------+  -> g_tn.borderHeight/2        -+
419  |                          |                                 | g_tn.outerHeight
420  |+------------------------+|  -> =0 (always)            -+   | item.thumbFullHeight
421  || subcontainer           ||                             |   |
422  ||                        ||                             |   |
423  || +--------------------+ ||  -> g_tn.imgcBorderHeight/2
424  || | imgContainer       | ||
425  || |+-------------------+ ||
426  || || image             | ||
427  || |+-------------------+ ||
428  || +--------------------+ ||
429  ||                        ||
430  || +--------------------+ ||  -+ --> g_tn.labelBorderHeight
431  || | labelImage         | ||   | g_tn.labelHeight
432  || |+------------------+| ||   | (=0 --> overImage)
433  || || labelTitle       || ||   | item.thumbLabelHeight
434  || |+------------------+| ||   |
435  || || labelDescription || ||
436  || |+------------------+| ||
437  || +--------------------+ ||
438  ||                        ||
439  |+------------------------+|
440  +--------------------------+
441
442  */
443  // **************************************************************************************
444  // Class to store one item (= one thumbnail)
445  // **************************************************************************************
446  var NGItems = (function () {
447    var nextId = 1;                   // private static --> all instances
448
449    // constructor
450    function NGItems( paramTitle, paramID ) {
451      var ID = 0;                     // private
452
453      // public (this instance only)
454      if( paramID === undefined || paramID === null ) {
455        ID = nextId++;
456      }
457      else {
458        ID = paramID;
459      }
460      this.GetID = function () { return ID; };
461      
462      // public
463      this.title = paramTitle;        // image title
464      this.description = '';          // image description
465      this.src = '';                  // full sized image URL
466      this.width = 0;                 // image width
467      this.height = 0;                // image height
468      this.destinationURL = '';       // thumbnail destination URL --> open URL instead of displaying image
469      this.kind = '';                 // 'image' or 'album'
470      this.author = '';               // image author
471this.thumbsrc = '';             // thumbnail image URL
472this.thumbX2src = '';           // double sized thumbnail image URL (featured image)
473this.thumbImgWidth = 0;            // thumbnail image width
474this.thumbImgHeight = 0;           // thumbnail image height
475      this.thumbFullWidth = 0;        // thumbnail full width
476      this.thumbFullHeight = 0;       // thumbnail full height
477      this.thumbLabelWidth = 0;
478      this.thumbLabelHeight = 0;
479      this.thumbSizes = {};           // store URLs for all available thumbnail sizes (flickr)
480      this.thumbs = {                 // URLs and sizes for user defined
481          url: { l1 : { xs:'', sm:'', me:'', la:'', xl:'' }, lN : { xs:'', sm:'', me:'', la:'', xl:'' } },
482          width: { l1 : { xs:0, sm:0, me:0, la:0, xl:0 }, lN : { xs:0, sm:0, me:0, la:0, xl:0 } },
483          height: { l1 : { xs:0, sm:0, me:0, la:0, xl:0 }, lN : { xs:0, sm:0, me:0, la:0, xl:0 } }
484        }
485      this.picasaThumbs = null;         // store URLs and sizes
486      this.hovered = false;           // is the thumbnail currently hovered?
487      this.hoverInitDone = false;
488      this.$elt = null;               // pointer to the corresponding DOM element
489      this.contentIsLoaded = false;   // album: are items already loaded?
490      this.contentLength = 0;         // album: number of items
491      this.imageNumber = 0;           // image number in the album
492      this.eltTransform = {};
493      //if( tags.length === 0 ) {
494      //  this.tags=null;
495      //}
496      //else {
497      //  this.tags=tags.split(' ');
498      //}
499      this.albumID = 0;               // ID of the parent album
500      this.paginationLastPage = 0;
501      this.paginationLastWidth = 0;
502      this.customData = {};
503    }
504
505    // public static
506    NGItems.get_nextId = function () {
507      return nextId;
508    };
509
510    // public (shared across instances)
511    NGItems.prototype = {
512      thumbSetImgHeight: function(h) {              // set thumbnail image real height for current level/resolution, and for all others level/resolutions having the same settings
513        var lst=['xs','sm','me','la','xl'];
514        for( var i=0; i< lst.length; i++ ) {
515          if( g_tn.settings.height.l1[lst[i]] == SettingsGetTnHeight() && g_tn.settings.width.l1[lst[i]] == SettingsGetTnWidth() ) {
516            this.thumbs.height.l1[lst[i]]=h;
517          }
518        }
519        for( var i=0; i< lst.length; i++ ) {
520          if( g_tn.settings.height.lN[lst[i]] == SettingsGetTnHeight() && g_tn.settings.width.l1[lst[i]] == SettingsGetTnWidth() ) {
521            this.thumbs.height.lN[lst[i]]=h;
522          }
523        }
524      },
525      
526      thumbSetImgWidth: function(w) {              // set thumbnail image real width for current level/resolution, and for all others level/resolutions having the same settings
527        var lst=['xs','sm','me','la','xl'];
528        for( var i=0; i< lst.length; i++ ) {
529          if( g_tn.settings.height.l1[lst[i]] == SettingsGetTnHeight() && g_tn.settings.width.l1[lst[i]] == SettingsGetTnWidth() ) {
530            this.thumbs.width.l1[lst[i]]=w;
531          }
532        }
533        for( var i=0; i< lst.length; i++ ) {
534          if( g_tn.settings.height.lN[lst[i]] == SettingsGetTnHeight() && g_tn.settings.width.l1[lst[i]] == SettingsGetTnWidth() ) {
535            this.thumbs.width.lN[lst[i]]=w;
536          }
537        }
538      },
539    
540      thumbImg: function () {   //g_thumbSize
541        var tnImg = { src:'', width:0, height:0 };
542
543        if( this.title == 'dummydummydummy' ) {
544          tnImg.src=g_emptyGif;
545          return tnImg;
546        }
547        tnImg.src=this.thumbs.url[g_curNavLevel][g_curWidth];
548        tnImg.width=this.thumbs.width[g_curNavLevel][g_curWidth];
549        tnImg.height=this.thumbs.height[g_curNavLevel][g_curWidth];
550        // tnImg.src=itemThumbURL;
551        // tnImg.width=this.thumbImgWidth;
552        // tnImg.height=this.thumbImgHeight;
553        return tnImg;
554      },
555      
556      // for future use...
557      responsiveURL: function () {
558        var url = '';
559        switch(gO.kind) {
560          case '':
561            url = this.src;
562            break;
563          case 'flickr':
564            url = this.src;
565            break;
566          case 'picasa':
567          default:
568            url = this.src;
569            break;
570        }
571        return url;
572      }
573    };
574    return NGItems;
575  })();
576
577  
578    
579  // ##########################
580  // ##### INITIALIZATION #####
581  // ##########################
582  
583  this.Initiate = function( element, params ) {
584    "use strict";
585    
586    //console.dir(N);
587    
588    gO = params;
589    $gE.base = jQuery(element);
590    g_baseEltID = $gE.base.attr('id');
591    
592    g_bodyOverflowInitial=jQuery('body').css('overflow');
593
594
595    //    [TODO] deep linking support only once per page
596    //    if( gO.locationHash ) {
597    //      alert(location.hash);
598    //      if( location.hash.length > 0 && location.hash.indexOf('#nanogallery/'+g_baseEltID) === 0 ) {
599    //        gO.locationHash=false;
600    //        nanoConsoleLog('locationHash has been disabled in:' + g_baseEltID +'. This option can only be used for one nanoGALLERY per page.');
601    //      }
602    //    }
603
604    // POLYFILL FOR BIND function --> for older Safari mobile
605    // found on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Compatibility
606    if (!Function.prototype.bind) {
607      Function.prototype.bind = function (oThis) {
608        if (typeof this !== "function") {
609          // closest thing possible to the ECMAScript 5
610          // internal IsCallable function
611          throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
612        }
613
614        var aArgs = Array.prototype.slice.call(arguments, 1), 
615            fToBind = this, 
616            fNOP = function () {},
617            fBound = function () {
618              return fToBind.apply(this instanceof fNOP && oThis
619                     ? this
620                     : oThis,
621                     aArgs.concat(Array.prototype.slice.call(arguments)));
622            };
623
624        fNOP.prototype = this.prototype;
625        fBound.prototype = new fNOP();
626
627        return fBound;
628      };
629    }
630    
631    String.prototype.replaceAll = function(search, replace) {
632    if (replace === undefined) {
633        return this.toString();
634    }
635    return this.split(search).join(replace);
636}
637    // detect the animation engine
638    // default is jQuery
639    if( toType(jQuery.velocity) == 'object' ) {
640      // Velocity.js
641      g_aengine='velocity';
642    }
643    else
644      // Transit.js
645      // if( jQuery.support.transition ) {    // conflict with bootstrap
646      if( toType(jQuery.transit) == 'object' ) {
647        g_aengine='transition';
648      }
649  
650    // Set theme and colorScheme
651    jQuery(element).addClass('nanogallery_theme_'+gO.theme);
652    SetColorScheme(element);
653
654    // Hide icons (thumbnails and breadcrumb)
655    if( gO.thumbnailLabel.hideIcons ) {
656      var s1 = '.nanogallery_thumbnails_icons_off ',
657      s = s1+'.nanoGalleryContainer .nanoGalleryThumbnailContainer .labelImageTitle:before { display:none !important; }'+'\n';
658      s += s1+'.nanoGalleryContainer .nanoGalleryThumbnailContainer .labelFolderTitle:before { display:none !important; }'+'\n';
659      jQuery('head').append('<style>'+s+'</style>');
660      jQuery(element).addClass('nanogallery_thumbnails_icons_off');
661    }
662    if( gO.galleryToolbarHideIcons ) {
663      var s1 = '.nanogallery_breadcrumb_icons_off ',
664      s=s1+'.nanoGalleryNavigationbar .folderHome:before { display:none !important; }'+'\n';
665      s += s1+'.nanoGalleryNavigationbar .folder:before { display:none !important; }'+'\n';
666      jQuery('head').append('<style>'+s+'</style>');
667      jQuery(element).addClass('nanogallery_breadcrumb_icons_off');
668    }
669
670    if( gO.thumbnailLabel.align == 'right' ) {
671      var s1 = '.nanogallery_thumbnails_label_align_right ',
672      s = s1+'.nanoGalleryContainer .nanoGalleryThumbnailContainer .labelImage { text-align : right !important; }'+'\n';
673      jQuery('head').append('<style>'+s+'</style>');
674      jQuery(element).addClass('nanogallery_thumbnails_label_align_right');
675    }
676
677    if( gO.thumbnailLabel.align == 'center' ) {
678      var s1 = '.nanogallery_thumbnails_label_align_center ',
679      s = s1+'.nanoGalleryContainer .nanoGalleryThumbnailContainer .labelImage { text-align : center !important; }'+'\n';
680      jQuery('head').append('<style>'+s+'</style>');
681      jQuery(element).addClass('nanogallery_thumbnails_label_align_center');
682    }
683      
684    if( gO.thumbnailLabel.align == 'left' ) {
685      var s1 = '.nanogallery_thumbnails_label_align_left ',
686      s = s1+'.nanoGalleryContainer .nanoGalleryThumbnailContainer .labelImage { text-align : left !important; }'+'\n';
687      jQuery('head').append('<style>'+s+'</style>');
688      jQuery(element).addClass('nanogallery_thumbnails_label_align_left');
689    }
690
691    // Build the gallery structure - add the containers
692    $gE.conNavBCon=jQuery('<div class="nanoGalleryNavigationbarContainer"></div>').appendTo(element);
693    $gE.conNavBCon.hide();//css('visibility','hidden');
694    $gE.conNavB=jQuery('<div class="nanoGalleryNavigationbar"></div>').appendTo($gE.conNavBCon);
695    
696    var sRTL='';
697    if( gO.RTL ) {
698      sRTL='style="text-align:right;direction:rtl;"';
699    }
700    $gE.conBC=jQuery('<div class="nanoGalleryBreadcrumb" '+sRTL+'></div>').appendTo($gE.conNavB);
701    $gE.conLoadingB=jQuery('<div class="nanoGalleryLBar" style="visibility:hidden;"><div></div><div></div><div></div><div></div><div></div></div>').appendTo(element);
702    $gE.conTnParent=jQuery('<div class="nanoGalleryContainerParent"></div>').appendTo(element);
703    $gE.conTn=jQuery('<div class="nanoGalleryContainer"></div>').appendTo($gE.conTnParent);
704    $gE.conConsole=jQuery('<div class="nanoGalleryConsoleParent"></div>').appendTo(element);
705    switch( gO.thumbnailAlignment ) {
706      case 'left':
707        $gE.conTnParent.css({'text-align':'left'});
708        $gE.conNavBCon.css({'margin-left':0 });
709        break;
710      case 'right':
711        $gE.conTnParent.css({'text-align':'right'});
712        $gE.conNavBCon.css({ 'margin-right':0});
713        break;
714    }
715
716    jQuery('head').append('<style>.nanogalleryHideElement {position: absolute !important; top: -9999px !important; left: -9999px !important;}</style>');
717    var t1=jQuery('<div class="nanogalleryHideElement '+jQuery(element).attr('class')+'"></div>').appendTo('body'),
718    t2=jQuery('<div class="nanoGalleryContainerParent"></div>').appendTo(t1);
719    $gE.conTnHid=jQuery('<div class="nanoGalleryContainer"></div>').appendTo(t2);
720
721    if( gO.supportIE8 ) {
722      // POLYFILL FOR addEventListener/removeEventListener function --> for IE8
723      // found on https://gist.github.com/jonathantneal/3748027
724      try {
725        !window.addEventListener && (function (WindowPrototype, DocumentPrototype, ElementPrototype, addEventListener, removeEventListener, dispatchEvent, registry) {
726          WindowPrototype[addEventListener] = DocumentPrototype[addEventListener] = ElementPrototype[addEventListener] = function (type, listener) {
727            var target = this;
728
729            registry.unshift([target, type, listener, function (event) {
730              event.currentTarget = target;
731              event.preventDefault = function () { event.returnValue = false };
732              event.stopPropagation = function () { event.cancelBubble = true };
733              event.target = event.srcElement || target;
734
735              listener.call(target, event);
736            }]);
737
738            this.attachEvent("on" + type, registry[0][3]);
739          };
740
741          WindowPrototype[removeEventListener] = DocumentPrototype[removeEventListener] = ElementPrototype[removeEventListener] = function (type, listener) {
742            for (var index = 0, register; register = registry[index]; ++index) {
743              if (register[0] == this && register[1] == type && register[2] == listener) {
744                return this.detachEvent("on" + type, registry.splice(index, 1)[0][3]);
745              }
746            }
747          };
748
749          WindowPrototype[dispatchEvent] = DocumentPrototype[dispatchEvent] = ElementPrototype[dispatchEvent] = function (eventObject) {
750            return this.fireEvent("on" + eventObject.type, eventObject);
751          };
752        })(Window.prototype, HTMLDocument.prototype, Element.prototype, "addEventListener", "removeEventListener", "dispatchEvent", []);
753      }
754      catch (e) {
755        BrowserNotification();
756        return false;
757      }
758    }
759    else {
760      if( g_IE <= 8 ) {
761        BrowserNotification();
762        return false;
763      }
764    }
765    
766    // check parameters consistency
767    checkPluginParameters();
768
769    // pagination container
770    var sRTL2='';
771    if( gO.RTL ) {
772      sRTL2='style="direction:rtl;"';
773    }
774    $gE.conPagin=jQuery('<div class="nanoGalleryPagination" '+sRTL2+'></div>').appendTo($gE.conTnParent);
775    $gE.conPagin.hide();
776
777    // attach events
778    var t= new userEventsGallery($gE.conTn[0]);
779
780    // i18n translations
781    i18n();
782
783    // fullscreen API support
784    if( document.fullscreenEnabled || document.webkitFullscreenEnabled || document.msFullscreenEnabled || document.mozFullScreenEnabled) {
785      g_supportFullscreenAPI=true;
786    } else {
787      nanoConsoleLog('Your browser does not support the fullscreen API. Fullscreen button will not be displayed.');
788    }
789    
790    // cache some elements sizes of the thumbnail
791    retrieveThumbnailSizes();
792    
793    g_L.nbMaxTnPerRow=NbThumbnailsPerRow();
794    
795    // lazy build the gallery
796    if( gO.lazyBuild != 'loadData' ) { NGFinalize(); }
797    
798    // GLOBAL EVENT MANAGEMENT
799    // Page resize
800    var g_resizeTimeOut=0;
801    jQuery(window).resize(function() { 
802      if(g_resizeTimeOut) clearTimeout(g_resizeTimeOut);
803      if( g_containerViewerDisplayed ) {
804          ResizeInternalViewer();
805      }
806      else {
807        g_resizeTimeOut = setTimeout(function () {
808          var nw=RetrieveCurWidth();
809          // if( g_curAlbumIdx != -1 && g_curWidth != nw ) {
810          if( g_curAlbumIdx != -1 && 
811                ( SettingsGetTnHeight() != g_tn.settings.height[g_curNavLevel][nw] || 
812                SettingsGetTnWidth() != g_tn.settings.width[g_curNavLevel][nw] ) ) {
813            // thumbnail size changed --> render the gallery with the new sizes
814            g_curWidth=nw;
815            renderGallery( g_curAlbumIdx, 0 );
816          }
817          else {
818            ResizeGallery();
819          }
820          return;
821        }, 50);
822      }
823    });
824    
825    // Event page scrolled
826    jQuery(window).on('scroll', function () {
827      if(g_scrollTimeOut) clearTimeout(g_scrollTimeOut);
828      g_scrollTimeOut = setTimeout(function () {
829
830        if( gO.lazyBuild == 'loadData' ) {
831          if( inViewportVert($gE.conTnParent,gO.lazyBuildTreshold) ){
832            gO.lazyBuild='none';
833            NGFinalize();
834          }
835        }
836      
837        if( g_delayedAlbumIdx != -1 ) {
838          if( inViewportVert($gE.conTnParent,gO.lazyBuildTreshold) ){
839          DisplayAlbumFinalize( g_delayedAlbumIdx, g_delayedSetLocationHash );
840          }
841        }
842        
843        thumbnailsLazySetSrc();
844        return;
845      }, 200);
846    });
847    
848    // [TODO] : check if this is still necessary
849    $gE.base.on('scroll', function () {
850      if(g_scrollTimeOut2) clearTimeout(g_scrollTimeOut2);
851      g_scrollTimeOut2 = setTimeout(function () {
852        thumbnailsLazySetSrc();
853      }, 200);
854    });
855
856  }
857  
858
859  
860  function ExposedObjects() {
861    return {
862      animationEngine:g_aengine,
863      t:'test'
864    };
865  }
866  
867  function BrowserNotification() {
868    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>';
869    m+='&nbsp;&nbsp;&nbsp; <a href="http://www.google.com/chrome/?hl=en-US)">Chrome</a><br>';
870    m+='&nbsp;&nbsp;&nbsp; <a href="http://www.mozilla.com/firefox/)">Firefox</a><br>';
871    m+='&nbsp;&nbsp;&nbsp; <a href="http://www.microsoft.com/windows/internet-explorer/default.aspx">Internet Explorer</a><br>';
872    m+='&nbsp;&nbsp;&nbsp; <a href="http://www.apple.com/safari/download/">Safari</a>';
873    nanoAlert(m, false);
874  }
875  
876  // 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
877  function FirstSupportedPropertyName(prefixedPropertyNames) {
878    var tempDiv = document.createElement("div");
879    for (var i = 0; i < prefixedPropertyNames.length; ++i) {
880      if (typeof tempDiv.style[prefixedPropertyNames[i]] != 'undefined')
881        return prefixedPropertyNames[i];
882    }
883    return null;
884  }
885
886  function NGFinalize() {
887  
888    var sizeImageMax=Math.max(window.screen.width, window.screen.height);
889    if( window.devicePixelRatio != undefined ) {
890      if( window.devicePixelRatio > 1 ) {
891        sizeImageMax=sizeImageMax*window.devicePixelRatio;
892      }
893    }
894
895		for( var j=0; j<g_tnHE.length; j++) {
896			switch(g_tnHE[j].name ) {
897				case 'imageScale150':
898				case 'imageScale150Outside':
899				case 'imageScaleIn80':
900				case 'imageSlide2Up':
901				case 'imageSlide2Down':
902				case 'imageSlide2Left':
903				case 'imageSlide2Right':
904				case 'imageSlide2UpRight':
905				case 'imageSlide2UpLeft':
906				case 'imageSlide2DownRight':
907				case 'imageSlide2DownLeft':
908				case 'imageSlide2Random':
909          g_tn.scale=Math.max(g_tn.scale, 1.5);
910					break;
911				case 'scale120':
912          g_tn.scale=Math.max(g_tn.scale, 1.2);
913					break;
914			}
915		}
916    var si=0;
917    if( gO.itemsBaseURL.length >0 ) {gO.itemsBaseURL+='/';}
918    
919    switch(gO.kind) {
920      // MARKUP / API
921      case '':
922        NGAddItem(g_i18nTranslations.breadcrumbHome, '', '', '', '', 'album', '', '0', '-1' );
923        
924        if( gO.items !== undefined && gO.items !== null ) {
925          ProcessItemOption();
926          if( !ProcessLocationHash(false) ) {
927            DisplayAlbum(0,false);
928          }
929        }
930        else {
931          var elements=jQuery($gE.base).children('a');
932          if( elements.length > 0 ) {
933            ProcessHREF(elements);
934            if( !ProcessLocationHash(false) ) {
935              DisplayAlbum(0,false);
936            }
937          }
938          else
939            nanoAlert('error: no image to process.');
940        }
941        break;
942      
943      // FLICKR STORAGE
944      case 'flickr':
945        if( !gO.flickrSkipOriginal ) {
946          g_flickr.photoAvailableSizes.push(10000);
947          g_flickr.photoAvailableSizesStr.push('o');
948        }
949        for( i=0; i<g_flickr.photoAvailableSizes.length; i++) {
950          g_flickr.photoSize=i; //g_flickr.photoAvailableSizesStr[i];
951          if( sizeImageMax <= g_flickr.photoAvailableSizes[i] ) {
952            break;
953          }
954        }
955        NGAddItem(g_i18nTranslations.breadcrumbHome, '', '', '', '', 'album', '', gO.photoset.length > 0 ? gO.photoset : '0', '-1' );
956        FlickrProcessItems(0,true,-1,false);
957        break;
958
959      // CUSTOM STORAGE
960      case 'json':
961        NGAddItem(g_i18nTranslations.breadcrumbHome, '', '', '', '', 'album', '', '0', '-1' );
962        CustomProcessItems(0,true,-1,false);
963        break;
964
965
966      // PICASA/GOOGLE+ STORAGE
967      case 'picasa':
968      default:
969        if( gO.album.length > 0 ) {
970          var p=gO.album.indexOf('&authkey=');
971          if( p >= 0 ) {
972            var albumId=gO.album.substring(0,p),
973            opt=gO.album.substring(p);
974            if( opt.indexOf('Gv1sRg') == -1 ) {
975              opt='&authkey=Gv1sRg'+opt.substring(9);
976            }
977            var newItem=NGAddItem(g_i18nTranslations.breadcrumbHome, '', '', '', '', 'album', '', albumId, '-1' );
978            newItem.customData.authkey=opt;
979          }
980          else {
981            NGAddItem(g_i18nTranslations.breadcrumbHome, '', '', '', '', 'album', '', gO.album, '-1' );
982          }
983          
984          
985        }
986        else {
987          NGAddItem(g_i18nTranslations.breadcrumbHome, '', '', '', '', 'album', '', '0', '-1' );
988        }
989        PicasaProcessItems(0,true,-1,false);
990        break;
991    }
992
993
994    // Keyboard management --> Image Viewer
995    jQuery(document).keyup(function(e) {
996      if( g_containerViewerDisplayed ) {
997        switch( e.keyCode) {
998          case 27:    // Esc key
999            CloseInternalViewer(true);
1000            break;
1001          case 32:    // SPACE
1002          case 13:    // ENTER
1003            SlideshowToggle();
1004            break;
1005          case 38:    // UP
1006          case 39:    // RIGHT
1007          case 33:    // PAGE UP
1008            DisplayNextImagePart1();
1009            break;
1010          case 40:    // DOWN
1011          case 37:    // LEFT
1012          case 34:    // PAGE DOWN
1013            DisplayPreviousImage();
1014            break;
1015          case 35:    // END
1016          case 36:    // BEGIN
1017        }
1018      }
1019    });
1020
1021    // browser back-button to close the image currently displayed
1022    jQuery(window).bind( 'hashchange', function( event ) {
1023
1024      if( gO.locationHash ) {
1025        ProcessLocationHash(true);
1026      }
1027      return;
1028      
1029    });
1030    
1031    // gallery fullpage
1032    if( gO.galleryFullpageButton ) {
1033      if( gO.RTL ) {
1034        $gE.conNavBFullpage =jQuery('<div class="nanoGalleryFullpage setFullPageButton"></div>').prependTo($gE.conNavB);
1035      }
1036      else {
1037        $gE.conNavBFullpage =jQuery('<div class="nanoGalleryFullpage setFullPageButton"></div>').appendTo($gE.conNavB);
1038      }
1039      $gE.conNavBFullpage.on('click', function(e){
1040        if( $gE.conNavBFullpage.hasClass('setFullPageButton') ) {
1041          // switch to fullpage display mode
1042          if( g_containerViewerDisplayed ) { return; }
1043          if( gO.maxWidth > 0 ) { 
1044            jQuery($gE.base).css({'maxWidth':''});
1045          }
1046          $gE.conNavBFullpage.removeClass('setFullPageButton').addClass('removeFullPageButton');
1047          setElementOnTop('', $gE.base);
1048          $gE.base.addClass('fullpage');
1049          jQuery('body').css({overflow:'hidden'});
1050          ResizeGallery();
1051        }
1052        else {
1053          // switch off fullpage mode
1054          if( g_containerViewerDisplayed ) { return; }
1055          $gE.conNavBFullpage.removeClass('removeFullPageButton').addClass('setFullPageButton');
1056          if( gO.maxWidth > 0 ) {
1057            jQuery($gE.base).css({'maxWidth':gO.maxWidth});
1058          }
1059          $gE.base.removeClass('fullpage');
1060          ScrollbarSetVisible();
1061          ResizeGallery();
1062        }
1063      });
1064     }
1065    
1066  }
1067  
1068  function ScrollbarSetVisible() {
1069    //jQuery('body').css({overflow:'initial'});
1070   jQuery('body').css({overflow:'visible'});
1071   return;
1072    if( g_bodyOverflowInitial != null && g_bodyOverflowInitial != undefined ) {
1073      jQuery('body').css({overflow:g_bodyOverflowInitial});
1074    }
1075    else {
1076      jQuery('body').css({overflow:'auto'});
1077    }
1078  }
1079  
1080  
1081  function ElementTranslateX( element, posX ) {
1082    jQuery(element).css({ 'left': posX }); 
1083    
1084    // [TODO] - translateX needs some code refactoring...
1085    //var transformStyle = 'translateX('+posX+'px)';
1086    //element.style.msTransform = transformStyle;
1087    //element.style.MozTransform = transformStyle;
1088    //element.style.webkitTransform = transformStyle;
1089    //element.style.transform = transformStyle;
1090  }
1091  
1092  // ##### USER EVENTS HANDLING ON GALLERY (click, hover, swipe, drag)
1093  // based on "Implement Custom Gestures" from Google
1094  // https://developers.google.com/web/fundamentals/input/touch-input/touchevents/
1095  function userEventsGallery(element) {
1096    var elementToSwipe=element,
1097    isAnimating=false,
1098    initialTouchPos=null,
1099    lastTouchPos=null,
1100    currentXPosition=0,
1101    onlyX=false,
1102    startViewport=null;
1103    
1104    var initialViewport=0;
1105    
1106    function OpenThumbnail( n ) {
1107      if( n == undefined ) { return; }
1108      
1109      if( $currentTouchedThumbnail != null) { ThumbnailHoverOut($currentTouchedThumbnail); }
1110      $currentTouchedThumbnail=null;
1111      
1112      // open URL
1113      if( gI[n].destinationURL !== undefined && gI[n].destinationURL.length >0 ) {
1114        window.location = gI[n].destinationURL;
1115        return;
1116      }
1117
1118      g_openNoDelay=false;
1119      if( gI[n].kind == 'album' ) {
1120        OpenAlbum(n, false, -1, true);
1121      }
1122      else {
1123        // Display image
1124        DisplayImage(n,false);
1125      }
1126    }
1127
1128    
1129    // Handle the start of gestures -->  click event
1130    this.handleGestureStartNoDelay = function(e) {
1131      // delay to ignore click event after touchstart event
1132      if( g_containerViewerDisplayed ) { 
1133        e.stopPropagation();
1134        e.eventDefault();
1135        return false;
1136      }
1137      if( (new Date().getTime()) - g_timeLastTouchStart < 400 ) { return; }
1138      g_openNoDelay=true;
1139      this.handleGestureStart(e);
1140    }.bind(this);
1141    
1142    // Handle the start of gestures
1143    this.handleGestureStart = function(e) {
1144      if( g_containerViewerDisplayed ) { 
1145        e.stopPropagation();
1146        e.eventDefault();
1147        return false;
1148      }
1149
1150      if( (new Date().getTime()) - g_timeImgChanged < 400 ) { return; }     // [TODO] --> remove!!!
1151      //e.preventDefault();
1152      
1153      if( (new Date().getTime()) - g_timeLastTouchStart < 400 ) { return; }
1154      g_timeLastTouchStart=new Date().getTime();
1155      
1156      var target = e.target || e.srcElement;
1157      var found=false;
1158      while( target != $gE.conTn[0] ) {       // go element parent up to find the thumbnail element
1159        if( target.getAttribute('class') == 'nanoGalleryThumbnailContainer' ) {
1160          // if( $currentTouchedThumbnail != jQuery(target) ) {
1161          if( $currentTouchedThumbnail != null && !$currentTouchedThumbnail.is(jQuery(target)) ) {
1162            ThumbnailHoverOutAll();
1163          }
1164          $currentTouchedThumbnail=jQuery(target);
1165          found=true;
1166        }
1167        target = target.parentNode;
1168      }
1169      
1170      if( !found ) { return; }
1171      
1172      initialViewport=getViewport();
1173      
1174      //if(e.touches && e.touches.length > 1) { return; }
1175      initialTouchPos = getGesturePointFromEvent(e);
1176
1177      initialOffsetTop=getViewport().t;
1178
1179      //if( g_gallerySwipeInitDone ) { return; }
1180      
1181      // Add the move and end listeners
1182      if (window.navigator.msPointerEnabled) {
1183        // Pointer events are supported.
1184        document.addEventListener('MSPointerMove', this.handleGestureMove, true);
1185        document.addEventListener('MSPointerUp', this.handleGestureEnd, true);
1186      } else {
1187        // Add Touch Listeners
1188        document.addEventListener('touchmove', this.handleGestureMove, true);
1189        document.addEventListener('touchend', this.handleGestureEnd, true);
1190        document.addEventListener('touchcancel', this.handleGestureEnd, true);
1191      
1192        // Add Mouse Listeners
1193        document.addEventListener('mousemove', this.handleGestureMove, true);
1194        document.addEventListener('mouseup', this.handleGestureEnd, true);
1195      }
1196      
1197      // makes content unselectable --> avoid image drag during 'mouse swipe'
1198      $gE.base.addClass('unselectable').find('*').attr('draggable', 'false').attr('unselectable', 'on');
1199      g_gallerySwipeInitDone=true;
1200      
1201    }.bind(this);
1202    
1203    // Handle move gestures
1204    this.handleGestureMove = function (e) {
1205      //e.preventDefault(); // --> uncomment this to avoid viewport scrolling on touchscreen
1206      lastTouchPos = getGesturePointFromEvent(e);
1207      
1208      if( isAnimating ) { return; }
1209
1210      if( g_pgMaxLinesPerPage > 0 && SettingsGetTnHeight() != 'auto' && SettingsGetTnWidth() != 'auto' ) {
1211        if( Math.abs(initialTouchPos.x - lastTouchPos.x) > 15 || onlyX ) {
1212          e.preventDefault(); // if swipe horizontaly the gallery, avoid moving page also
1213          onlyX=true;
1214          isAnimating = true;
1215          window.requestAnimationFrame(onAnimFrame);
1216        }
1217      }
1218
1219    }.bind(this);
1220
1221    
1222    // Handle end gestures
1223    this.handleGestureEnd = function(e) {
1224      if( e.cancelable ) { e.preventDefault(); }
1225      e.stopPropagation();
1226      
1227      // if(e.touches && e.touches.length > 0) {
1228      //   return;
1229      // }
1230      isAnimating = false;
1231      onlyX=false;
1232      
1233      // Remove Event Listeners
1234      if (window.navigator.msPointerEnabled) {
1235        // Remove Pointer Event Listeners
1236        document.removeEventListener('MSPointerMove', this.handleGestureMove, true);
1237        document.removeEventListener('MSPointerUp', this.handleGestureEnd, true);
1238      } else {
1239        // Remove Touch Listeners
1240        document.removeEventListener('touchmove', this.handleGestureMove, true);
1241        document.removeEventListener('touchend', this.handleGestureEnd, true);
1242        document.removeEventListener('touchcancel', this.handleGestureEnd, true);
1243      
1244        // Remove Mouse Listeners
1245        document.removeEventListener('mousemove', this.handleGestureMove, true);
1246        document.removeEventListener('mouseup', this.handleGestureEnd, true);
1247      }
1248
1249      // allow text + image selection again
1250      $gE.base.addClass('unselectable').find('*').attr('draggable', 'true').attr('unselectable', 'off');
1251
1252      updateSwipeRestPosition();
1253      
1254      initialTouchPos=null;
1255      lastTouchPos=null;
1256      currentXPosition=0;
1257      onlyX=false;
1258      startViewport=null;
1259
1260    }.bind(this);
1261    
1262    
1263    function OpenTouchedThumbnail() {
1264      currentXPosition=0;
1265      initialTouchPos=null;
1266      lastTouchPos=null;
1267      ElementTranslateX($gE.conTn[0],0);
1268      
1269      if( g_containerViewerDisplayed ) {
1270        $currentTouchedThumbnail=null;
1271        g_openNoDelay=false;
1272      }
1273      else {
1274        if( $currentTouchedThumbnail != null ) {
1275          
1276          if( Math.abs(initialViewport.t-getViewport().t) > 10 ) {
1277            // viewport has been scrolled (touchscreen)--> open is cancelled
1278            ThumbnailHoverOut($currentTouchedThumbnail);
1279            $currentTouchedThumbnail=null;
1280            g_openNoDelay=false;
1281            return;
1282          }
1283          
1284          var $t=$currentTouchedThumbnail;
1285          var n=$t.data('index');
1286          if( n == undefined ) { return; }
1287          
1288          if( gO.touchAnimation && !g_openNoDelay ) {
1289            // automatically opens the touched thumbnail (to disply an image or to open an album)
1290            if( gO.touchAutoOpenDelay > 0 ) { 
1291              ThumbnailHoverOutAll();
1292              ThumbnailHover($t);
1293              window.clearInterval(g_touchAutoOpenDelayTimerID);
1294              g_touchAutoOpenDelayTimerID=window.setInterval(function(){
1295                window.clearInterval(g_touchAutoOpenDelayTimerID);
1296                if( Math.abs(initialViewport.t-getViewport().t) > 10 ) {
1297                  // viewport has been scrolled after hover effect delay (touchscreen)--> open is cancelled
1298                  g_openNoDelay=false;
1299                  $currentTouchedThumbnail=null;
1300                  ThumbnailHoverOut($t);
1301                }
1302                else {
1303                  OpenThumbnail(n);
1304                }
1305              }, gO.touchAutoOpenDelay);
1306            }
1307            else {
1308              // 2 touch scenario
1309              if( !gI[n].hovered ) {
1310                // first touch
1311                ThumbnailHoverOutAll();
1312                ThumbnailHover($t);
1313              }
1314              else {
1315                // second touch
1316                OpenThumbnail(n);
1317              }
1318            }
1319          }
1320          else {
1321            OpenThumbnail(n);
1322          }
1323
1324        }
1325        else {
1326          g_openNoDelay=false;
1327        }
1328      }
1329      return;
1330    }
1331    
1332    function updateSwipeRestPosition() {
1333
1334      if( lastTouchPos == null || initialTouchPos == null ) {      // touchend without touchmove
1335        // currentXPosition=0;
1336        // initialTouchPos=null;
1337        OpenTouchedThumbnail();
1338        return;
1339      }
1340
1341      var differenceInX = initialTouchPos.x - lastTouchPos.x;
1342      var differenceInY = initialTouchPos.y - lastTouchPos.y;
1343      currentXPosition = currentXPosition - differenceInX;
1344      if( g_pgMaxLinesPerPage > 0 && SettingsGetTnHeight() != 'auto' && SettingsGetTnWidth() != 'auto' ) {
1345        // pagination
1346        if( Math.abs(differenceInX) > 30) {
1347          $currentTouchedThumbnail=null;
1348          currentXPosition=0;
1349          initialTouchPos=null;
1350          lastTouchPos=null;
1351          ThumbnailHoverOutAll();
1352          if( differenceInX < -30 ) {
1353            paginationPreviousPage();
1354          }
1355          else {
1356            paginationNextPage();
1357          }
1358        }
1359        else {
1360          OpenTouchedThumbnail();
1361        }
1362      }
1363      else {
1364        // no pagination
1365        OpenTouchedThumbnail();
1366      }
1367
1368      return;
1369    }
1370
1371    function getGesturePointFromEvent(e) {
1372      var point = {};
1373
1374      if(e.targetTouches) {
1375        point.x = e.targetTouches[0].clientX;
1376        point.y = e.targetTouches[0].clientY;
1377      } else {
1378        // Either Mouse event or Pointer Event
1379        point.x = e.clientX;
1380        point.y = e.clientY;
1381      }
1382
1383      return point;
1384    }
1385    
1386    function onAnimFrame() {
1387      if(!isAnimating) { return; }
1388      
1389      if( g_pgMaxLinesPerPage > 0 && SettingsGetTnHeight() != 'auto' && SettingsGetTnWidth() != 'auto'  ) {
1390        var differenceInX = initialTouchPos.x - lastTouchPos.x;
1391        ElementTranslateX(elementToSwipe,currentXPosition - differenceInX);
1392      }
1393
1394      isAnimating = false;
1395    }
1396
1397    function ThumbnailOnMouseenter(e) {
1398      if( g_containerViewerDisplayed ) { return; }
1399      var target = e.target || e.srcElement;
1400      if( target.getAttribute('class') == 'nanoGalleryThumbnailContainer' ) {
1401        //if( $currentTouchedThumbnail == null ) {
1402          ThumbnailHover(jQuery(target));
1403        //}
1404      }
1405    }
1406    
1407    function ThumbnailOnMouseleave(e) {
1408      var target = e.target || e.srcElement;
1409      if( target.getAttribute('class') == 'nanoGalleryThumbnailContainer' ) {
1410        ThumbnailHoverOut(jQuery(target));
1411      }
1412    }
1413
1414    
1415    // Check if pointer events are supported.
1416    if (window.navigator.msPointerEnabled) {
1417    // Add Pointer Event Listener
1418      elementToSwipe.addEventListener('MSPointerDown', this.handleGestureStartNoDelay, true);
1419    }
1420    else {
1421      // Add Touch Listener
1422      elementToSwipe.addEventListener('touchstart', this.handleGestureStart, true);
1423      
1424      // Add Mouse Listener
1425      if( !g_isIOS ) {
1426        elementToSwipe.addEventListener('mousedown', this.handleGestureStartNoDelay, true);
1427      }
1428    }
1429    
1430    // MOUSE OVER
1431    elementToSwipe.addEventListener('mouseenter', ThumbnailOnMouseenter, true);
1432    elementToSwipe.addEventListener('mouseleave', ThumbnailOnMouseleave, true);
1433    
1434  }
1435
1436
1437
1438  // requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
1439  // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
1440  // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
1441  // MIT license
1442  (function() {
1443      var lastTime = 0;
1444      var vendors = ['ms', 'moz', 'webkit', 'o'];
1445      for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
1446          window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
1447          window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] 
1448                                     || window[vendors[x]+'CancelRequestAnimationFrame'];
1449      }
1450   
1451      if (!window.requestAnimationFrame)
1452          window.requestAnimationFrame = function(callback, element) {
1453              var currTime = new Date().getTime();
1454              var timeToCall = Math.max(0, 16 - (currTime - lastTime));
1455              var id = window.setTimeout(function() { callback(currTime + timeToCall); }, 
1456                timeToCall);
1457              lastTime = currTime + timeToCall;
1458              return id;
1459          };
1460   
1461      if (!window.cancelAnimationFrame)
1462          window.cancelAnimationFrame = function(id) {
1463              clearTimeout(id);
1464          };
1465  }());
1466
1467  
1468  // ##### CHECK PLUGIN PARAMETERS CONSISTENCY
1469  function checkPluginParameters() {
1470
1471    if( gO.viewer == 'fancybox' ) {
1472      if( typeof(jQuery.fancybox) === 'undefined' ) {
1473        gO.viewer = 'internal';
1474        nanoConsoleLog('Fancybox could not be found. Fallback to internal viewer. Please check the file includes of the page.');
1475      }
1476    }
1477
1478    if( gO.userID.toUpperCase() == '[email protected]' || gO.userID == '111186676244625461692' ) {
1479      if( gO.blackList == '' || gO.blackList.toUpperCase() == 'SCRAPBOOK|PROFIL' ) { gO.blackList='profil|scrapbook|forhomepage'; }
1480    }
1481    
1482    if( gO.blackList != '' ) { g_blackList=gO.blackList.toUpperCase().split('|'); }
1483    if( gO.whiteList != '' ) { g_whiteList=gO.whiteList.toUpperCase().split('|'); }
1484    if( gO.albumList != '' ) { g_albumList=gO.albumList.toUpperCase().split('|'); }
1485
1486    if( gO.kind == 'picasa' || gO.kind == 'flickr' ) {
1487      gO.displayBreadcrumb=true;
1488    }
1489    // flickr
1490    if( gO.photoset !== undefined ) {
1491      if( gO.photoset.length > 0) { gO.displayBreadcrumb=false; }
1492    }
1493    else { gO.photoset=''; }
1494    // picasa
1495    if( gO.album !== undefined ) {
1496      if( gO.album.length > 0 ) { gO.displayBreadcrumb=false; }
1497    }
1498    else { gO.album=''; }
1499
1500    if( gO.maxWidth > 0 ) { 
1501      jQuery($gE.base).css({'maxWidth':gO.maxWidth});
1502      jQuery($gE.base).css({'margin-left':'auto'});
1503      jQuery($gE.base).css({'margin-right':'auto'});
1504    }
1505  
1506    if( toType(gO.slideshowDelay) == 'number' && gO.slideshowDelay >= 2000 ) {
1507      g_slideshowDelay=gO.slideshowDelay;
1508    }
1509    else {
1510      nanoConsoleLog('Parameter "slideshowDelay" must be an integer >= 2000 ms.');
1511    }
1512
1513    if( toType(gO.thumbnailDisplayInterval) == 'number' && gO.thumbnailDisplayInterval >= 0 ) {
1514      g_tn.displayInterval=gO.thumbnailDisplayInterval;
1515    }
1516    else {
1517      nanoConsoleLog('Parameter "thumbnailDisplayInterval" must be an integer.');
1518    }
1519
1520    if( toType(gO.thumbnailLazyLoadTreshold) == 'number' && gO.thumbnailLazyLoadTreshold >= 0 ) {
1521      g_tn.lazyLoadTreshold=gO.thumbnailLazyLoadTreshold;
1522    }
1523    else {
1524      nanoConsoleLog('Parameter "thumbnailLazyLoadTreshold" must be an integer.');
1525    }
1526
1527    if( toType(gO.paginationMaxLinesPerPage) == 'number' && gO.paginationMaxLinesPerPage >= 0 ) {
1528      g_pgMaxLinesPerPage=gO.paginationMaxLinesPerPage;
1529    }
1530    else {
1531      nanoConsoleLog('Parameter "paginationMaxLinesPerPage" must be an integer.');
1532    }
1533
1534    /*
1535    if( SettingsGetTnHeight() == 'auto' || SettingsGetTnWidth() == 'auto' ) {
1536      if( gO.paginationMaxLinesPerPage >0 ) {
1537        nanoConsoleLog('Parameters "paginationMaxLinesPerPage" and "thumbnailWidth/thumbnailHeight" value "auto" are not compatible.');
1538      }
1539      g_pgMaxLinesPerPage=0;
1540
1541    }
1542    */
1543    
1544    // random sorting
1545    var s1=gO.albumSorting.toUpperCase();
1546    if( s1.indexOf('RANDOM') == 0 && s1.length > 6 ) {
1547      n= parseInt(s1.substring(6));
1548      if( n > 0 ) {
1549        g_maxAlbums=n;
1550      }
1551      gO.albumSorting='random';
1552    }
1553    var s2=gO.photoSorting.toUpperCase();
1554    if( s2.indexOf('RANDOM') == 0 && s2.length > 6 ) {
1555      n= parseInt(s2.substring(6));
1556      if( n > 0 ) {
1557        g_maxPhotos=n;
1558      }
1559      gO.photoSorting='random';
1560    }
1561
1562    
1563    // thumbnails hover effects
1564    switch( toType(gO.thumbnailHoverEffect) ) {
1565      case 'string':
1566        var tmp=gO.thumbnailHoverEffect.split(',');
1567        for(var i=0; i<tmp.length; i++) {
1568          if( tmp[i] != 'none' && isAEngineSupported(tmp[i]) ) {
1569            var oDef=NewTHoverEffect();
1570            oDef.name=tmp[i];
1571            g_tnHE.push(oDef);
1572          }
1573        }
1574        break;
1575      case 'object':
1576        if( gO.thumbnailHoverEffect.name != 'none' && isAEngineSupported(gO.thumbnailHoverEffect.name) ) {
1577          var oDef=NewTHoverEffect();
1578          g_tnHE.push(jQuery.extend(oDef,gO.thumbnailHoverEffect));
1579        }
1580        break;
1581      case 'array':
1582        for(var i=0; i<gO.thumbnailHoverEffect.length; i++) {
1583          if( gO.thumbnailHoverEffect[i].name != 'none' && isAEngineSupported(gO.thumbnailHoverEffect[i].name) ) {
1584            var oDef=NewTHoverEffect();
1585            g_tnHE.push(jQuery.extend(oDef,gO.thumbnailHoverEffect[i]));
1586          }
1587        }
1588        break;
1589      case 'null':
1590        break;
1591      default:
1592        nanoAlert('incorrect parameter for "thumbnailHoverEffect".');
1593    }
1594    
1595    if( g_tnHE.length == 0 ) {
1596      gO.touchAnimation=false;
1597    }
1598
1599    // management of screen width
1600    g_curWidth=RetrieveCurWidth();
1601    
1602    // RETRIEVE ALL THUMBNAIL SIZES
1603    if( toType(gO.thumbnailWidth) == 'number' ) {
1604      ThumbnailsDefaultSize( 'width', 'l1', gO.thumbnailWidth, 'u');
1605      ThumbnailsDefaultSize( 'width', 'lN', gO.thumbnailWidth, 'u');
1606    }
1607    else {
1608      var ws=gO.thumbnailWidth.split(' ');
1609      var v='auto';
1610      if( ws[0].substring(0,4) != 'auto' ) { v=parseInt(ws[0]); }
1611      var c='u';
1612      if( ws[0].charAt(ws[0].length - 1) == 'C' ) { c='c'; }
1613      ThumbnailsDefaultSize( 'width', 'l1', v, c );   // default value for all resolutions and navigation levels
1614      ThumbnailsDefaultSize( 'width', 'lN', v, c );
1615      for( var i=1; i<ws.length; i++ ) {
1616        var r=ws[i].substring(0,2).toLowerCase();
1617        if( /xs|sm|me|la|xl/i.test(r) ) {
1618          var w=ws[i].substring(2);
1619          var v='auto';
1620          if( w.substring(0,4) != 'auto' ) { v=parseInt(w); }
1621          var c='u';
1622          if( w.charAt(w.length - 1) == 'C' ) { c='c'; }
1623          g_tn.settings.width['l1'][r]=v;
1624          g_tn.settings.width['lN'][r]=v;
1625          g_tn.settings.width['l1'][r+'c']=c;
1626          g_tn.settings.width['lN'][r+'c']=c;
1627        }
1628      }
1629    }
1630    if( gO.thumbnailL1Width != undefined ) {
1631      if( toType(gO.thumbnailL1Width) == 'number' ) {
1632        ThumbnailsDefaultSize( 'width', 'l1', gO.thumbnailL1Width, 'u');
1633      }
1634      else {
1635        var ws=gO.thumbnailL1Width.split(' ');
1636        var v='auto';
1637        if( ws[0].substring(0,4) != 'auto' ) { v=parseInt(ws[0]); }
1638        var c='u';
1639        if( ws[0].charAt(ws[0].length - 1) == 'C' ) { c='c'; }
1640        ThumbnailsDefaultSize( 'width', 'l1', v, c );
1641        for( var i=1; i<ws.length; i++ ) {
1642          var r=ws[i].substring(0,2).toLowerCase();
1643          if( /xs|sm|me|la|xl/i.test(r) ) {
1644            var w=ws[i].substring(2);
1645            var v='auto';
1646            if( w.substring(0,4) != 'auto' ) { v=parseInt(w); }
1647            var c='u';
1648            if( w.charAt(w.length - 1) == 'C' ) { c='c'; }
1649            g_tn.settings.width['l1'][r]=v;
1650            g_tn.settings.width['l1'][r+'c']=c;
1651          }
1652        }
1653      }
1654    }
1655    
1656    
1657    if( toType(gO.thumbnailHeight) == 'number' ) {
1658      ThumbnailsDefaultSize( 'height', 'l1', gO.thumbnailHeight, 'u');
1659      ThumbnailsDefaultSize( 'height', 'lN', gO.thumbnailHeight, 'u');
1660    }
1661    else {
1662      var ws=gO.thumbnailHeight.split(' ');
1663      var v='auto';
1664      if( ws[0].substring(0,4) != 'auto' ) { v=parseInt(ws[0]); }
1665      var c='u';
1666      if( ws[0].charAt(ws[0].length - 1) == 'C' ) { c='c'; }
1667      ThumbnailsDefaultSize( 'height', 'l1', v, c );   // default value for all resolutions and navigation levels
1668      ThumbnailsDefaultSize( 'height', 'lN', v, c );
1669      for( var i=1; i<ws.length; i++ ) {
1670        var r=ws[i].substring(0,2).toLowerCase();
1671        if( /xs|sm|me|la|xl/i.test(r) ) {
1672          var w=ws[i].substring(2);
1673          var v='auto';
1674          if( w.substring(0,4) != 'auto' ) { v=parseInt(w); }
1675          var c='u';
1676          if( w.charAt(w.length - 1) == 'C' ) { c='c'; }
1677          g_tn.settings.height['l1'][r]=v;
1678          g_tn.settings.height['lN'][r]=v;
1679          g_tn.settings.height['l1'][r+'c']=c;
1680          g_tn.settings.height['lN'][r+'c']=c;
1681        }
1682      }
1683    }
1684    if( gO.thumbnailL1Height != undefined ) {
1685      if( toType(gO.thumbnailL1Height) == 'number' ) {
1686        ThumbnailsDefaultSize( 'height', 'l1', gO.thumbnailL1Height, 'u');
1687      }
1688      else {
1689        var ws=gO.thumbnailL1Height.split(' ');
1690        var v='auto';
1691        if( ws[0].substring(0,4) != 'auto' ) { v=parseInt(ws[0]); }
1692        var c='u';
1693        if( ws[0].charAt(ws[0].length - 1) == 'C' ) { c='c'; }
1694        ThumbnailsDefaultSize( 'height', 'l1', v, c );
1695        for( var i=1; i<ws.length; i++ ) {
1696          var r=ws[i].substring(0,2).toLowerCase();
1697          if( /xs|sm|me|la|xl/i.test(r) ) {
1698            var w=ws[i].substring(2);
1699            var v='auto';
1700            if( w.substring(0,4) != 'auto' ) { v=parseInt(w); }
1701            var c='u';
1702            if( w.charAt(w.length - 1) == 'C' ) { c='c'; }
1703            g_tn.settings.height['l1'][r]=v;
1704            g_tn.settings.height['l1'][r+'c']=c;
1705          }
1706        }
1707      }
1708    }
1709  }
1710  
1711  // ##### THUMBNAIL SIZE MANAGEMENT
1712  function ThumbnailsDefaultSize( dir, level, v, crop ) {
1713    g_tn.settings[dir][level]['xs']=v;
1714    g_tn.settings[dir][level]['sm']=v;
1715    g_tn.settings[dir][level]['me']=v;
1716    g_tn.settings[dir][level]['la']=v;
1717    g_tn.settings[dir][level]['xl']=v;
1718    g_tn.settings[dir][level]['xsc']=crop;
1719    g_tn.settings[dir][level]['smc']=crop;
1720    g_tn.settings[dir][level]['mec']=crop;
1721    g_tn.settings[dir][level]['lac']=crop;
1722    g_tn.settings[dir][level]['xlc']=crop;
1723  }
1724  
1725  function SettingsGetTnWidth() {
1726    return g_tn.settings.width[g_curNavLevel][g_curWidth];
1727  }
1728  function SettingsGetTnHeight() {
1729    return g_tn.settings.height[g_curNavLevel][g_curWidth];
1730  }
1731  
1732  function ThumbnailOuterWidth() {
1733    return g_tn.outerWidth[g_curNavLevel][g_curWidth];
1734  }
1735  function ThumbnailOuterHeight() {
1736    return g_tn.outerHeight[g_curNavLevel][g_curWidth];
1737  }
1738
1739  function RetrieveCurWidth() {
1740    var vpW= getViewport().w;
1741    
1742    if( gO.thumbnailSizeSM > 0 && vpW < gO.thumbnailSizeSM) { return 'xs'; }
1743    if( gO.thumbnailSizeME > 0 && vpW < gO.thumbnailSizeME) { return 'sm'; }
1744    if( gO.thumbnailSizeLA > 0 && vpW < gO.thumbnailSizeLA) { return 'me'; }
1745    if( gO.thumbnailSizeXL > 0 && vpW < gO.thumbnailSizeXL) { return 'la'; }
1746    
1747    return 'xl';
1748  }
1749  
1750  
1751  // HOVER EFFECTS
1752  function NewTHoverEffect() {
1753    // easing : jQuery supports only 'swing' and 'linear'
1754    var oDef={'delay':0, 'delayBack':0, 'duration':400, 'durationBack':200, 'easing':'swing', 'easingBack': 'swing', 'animParam':null };
1755    if( g_aengine != 'animate' ) {
1756      oDef.easing='ease';
1757      oDef.easingBack='ease';
1758    }
1759    return oDef;
1760  }
1761  
1762  // check if effect is compatible to animation engine
1763  // check also consistency of thumbnail configuration with hover effect
1764  function isAEngineSupported( effect ) {
1765    
1766    // var isBasic = /labelOpacity50|borderLighter|borderDarker/i.test(effect),
1767    var isBasic = /labelOpacity50|borderLighter|borderDarker/i.test(effect),
1768    // isStd = /imageFlipVertical|imageFlipHorizontal|imageRotateCornerBR|imageRotateCornerBL|rotateCornerBL|rotateCornerBR|imageScale150|overScale|overScaleOutside|imageScaleIn80|imageScale150Outside|scale120|scaleLabelOverImage|slideUp|slideDown|slideLeft|slideRight|imageSlideUp|imageSlideDown|imageSlideLeft|imageSlideRight|labelAppear|labelAppear75|descriptionAppear|labelSlideDown|labelSlideUp|labelSlideUpTop|imageInvisible|imageOpacity50|descriptionSlideUp|labelSplitVert|labelSplit4|labelAppearSplitVert|labelAppearSplit4|imageSplitVert|imageSplit4|imageSlide2Up|imageSlide2Down|imageSlide2Left|imageSlide2Right|imageSlide2Random|imageSlide2UpRight|imageSlide2UpLeft|imageSlide2DownRight|imageSlide2DownLeft/i.test(effect),
1769    isStd = /imageFlipVertical|imageFlipHorizontal|imageRotateCornerBR|imageRotateCornerBL|rotateCornerBL|rotateCornerBR|imageScale150|overScale|overScaleOutside|imageScaleIn80|imageScale150Outside|scale120|scaleLabelOverImage|slideUp|slideDown|slideLeft|slideRight|imageSlideUp|imageSlideDown|imageSlideLeft|imageSlideRight|labelAppear|labelAppear75|descriptionAppear|labelSlideDown|labelSlideUp|labelSlideUpTop|imageInvisible|imageOpacity50|descriptionSlideUp|labelSplitVert|labelSplit4|labelAppearSplitVert|labelAppearSplit4|imageSplitVert|imageSplit4/i.test(effect),
1770    // isAdv = /imageScaleIn80|imageScale150|imageScale150Outside|scale120|overScale|overScaleOutside|scaleLabelOverImage|imageFlipHorizontal|imageFlipVertical|rotateCornerBR|rotateCornerBL|imageRotateCornerBR|imageRotateCornerBL|imageExplode/i.test(effect);
1771    isAdv = /imageExplode/i.test(effect);
1772    
1773    gO.touchAutoOpenDelay= parseInt(gO.touchAutoOpenDelay);
1774    if( gO.touchAutoOpenDelay == 0 ) {
1775      gO.touchAutoOpenDelay=1000;
1776    }
1777    
1778
1779    if( !isBasic && !isStd && !isAdv ) {
1780      nanoAlert('Unknow parameter value: thumbnailHoverEffect="'+effect+'".');
1781      return false;
1782    }
1783    
1784    if( gO.thumbnailLabel.position == 'onBottom' && !/borderLighter|borderDarker|imageOpacity50|imageScale150|imageScaleIn80|imageSlide2Up|imageSlide2Down|imageSlide2Left|imageSlide2Right|imageSlide2Random|imageSlide2UpRight|imageSlide2UpLeft|imageSlide2DownRight|imageSlide2DownLeft|imageScale150Outside|scale120/i.test(effect) ) {
1785      nanoAlert('The parameter combination thumbnailHoverEffect="'+effect+'" and thumbnailLabel.position="onBottom" is not supported.');
1786      return false;
1787    }
1788    
1789    //if( gO.thumbnailLabel.position == 'overImageOnBottom' && /descriptionSlideUp/i.test(effect) ) {
1790    //  nanoAlert('The parameter combination thumbnailHoverEffect="'+effect+'" and thumbnailLabel.position="overImageOnBottom" is not supported.');
1791    //  return false;
1792    //}
1793    if( (isAdv && (g_aengine == 'animate' || g_CSStransformName == null) ) ) {
1794      nanoConsoleLog('Parameter thumbnailHoverEffect="'+effect+'" requires one of the additionals jQuery plugins "Velocity" or "Transit".');
1795      return false;
1796    }
1797    
1798    return true;
1799    
1800  }
1801
1802  // define text translations
1803  function i18n() {
1804
1805    // browser language
1806    g_i18nLang = (navigator.language || navigator.userLanguage).toUpperCase();
1807    if( g_i18nLang === 'UNDEFINED') { g_i18nLang=''; }
1808
1809    var llang=-('_'+g_i18nLang).length;
1810    
1811    if( toType(gO.i18n) == 'object' ){
1812    
1813      for( var key in gO.i18n ) {
1814        //var value = gO.i18n[key];
1815        var s=key.substr(llang);
1816        if( s == ('_'+g_i18nLang) ) {
1817          g_i18nTranslations[key.substr(0,key.length-s.length)]=gO.i18n[key];
1818        } 
1819        else {
1820          g_i18nTranslations[key]=gO.i18n[key];
1821        }
1822      }
1823    }
1824  }
1825  
1826  function ProcessLocationHash(eventLocationHash) {
1827
1828    if( !gO.locationHash ) { return false; }
1829
1830    var albumID=null,
1831    imageID=null,
1832    curGal='#nanogallery/'+g_baseEltID+'/',
1833    hash=location.hash;
1834
1835    if( hash == g_lastLocationHash ) { return; }
1836    
1837    if( hash == '' ) {
1838      if( g_lastOpenAlbumID != -1 ) {
1839        // back button and no hash --> display first album
1840        g_lastLocationHash='';
1841        OpenAlbum(0,false,-1,false);
1842      }
1843    }
1844    
1845    if( hash.indexOf(curGal) == 0 ) {
1846      var s=hash.substring(curGal.length),
1847      p=s.indexOf('/'),
1848      albumIdx=-1,
1849      imageIdx=-1,
1850      l=gI.length;
1851      
1852      if( p > 0 ) {
1853        albumID=s.substring(0,p);
1854        imageID=s.substring(p+1);
1855        for(var i=0; i<l; i++ ) {
1856          if( gI[i].kind == 'image' && gI[i].GetID() == imageID ) {
1857            imageIdx=i;
1858            break;
1859          }
1860        }
1861      }
1862      else {
1863        albumID=s;
1864      }
1865      for(var i=0; i<l; i++ ) {
1866        if( gI[i].kind == 'album' && gI[i].GetID() == albumID ) {
1867          albumIdx=i;
1868          break;
1869        }
1870      }
1871
1872      if( imageID !== null ) {
1873        // process IMAGE
1874        if( !eventLocationHash ) {
1875          g_albumIdxToOpenOnViewerClose=albumIdx;
1876        }
1877        if( gO.kind == '' ) {
1878          DisplayImage(imageIdx);
1879        }
1880        else {
1881          if( imageIdx == -1 ) {
1882            OpenAlbum(albumIdx,false,imageID,eventLocationHash);
1883          }
1884          else {
1885            DisplayImage(imageIdx);
1886          }
1887        }
1888        return true;
1889
1890      }
1891      else {
1892        // process ALBUM
1893        OpenAlbum(albumIdx,false,-1,eventLocationHash);
1894        return true;
1895      }
1896    }
1897  
1898    //return {albumID:albID, imageID:imgID};
1899  }
1900
1901  // build a dummy thumbnail to get different sizes and cache them
1902  function retrieveThumbnailSizes() {
1903    gI=[];
1904
1905    var desc='';
1906    if( gO.thumbnailLabel.displayDescription ) { desc='d'; }
1907    var item=NGAddItem('dummydummydummy', g_emptyGif, g_emptyGif, desc, '', 'image', '', '1', '0' ),
1908    $newDiv=thumbnailBuild(item, 0, false).e$;
1909
1910    g_tn.borderWidth=$newDiv.outerWidth(true)-$newDiv.width();
1911    g_tn.borderHeight=$newDiv.outerHeight(true)-$newDiv.height();
1912
1913    g_tn.imgcBorderWidth=$newDiv.find('.imgContainer').outerWidth(true)-$newDiv.find('.imgContainer').width();
1914    g_tn.imgcBorderHeight=$newDiv.find('.imgContainer').outerHeight(true)-$newDiv.find('.imgContainer').height();
1915    
1916    g_tn.labelBorderHeight=$newDiv.find('.labelImage').outerHeight(true)-$newDiv.find('.labelImage').height();
1917    g_tn.labelBorderWidth=$newDiv.find('.labelImage').outerWidth(true)-$newDiv.find('.labelImage').width();
1918    
1919    if( gO.thumbnailLabel.position == 'onBottom' ) {
1920      g_tn.labelHeight=$newDiv.find('.labelImage').outerHeight(true);
1921    }
1922
1923    var lst=['xs','sm','me','la','xl'];
1924    for( var i=0; i< lst.length; i++ ) {
1925      var w=g_tn.settings.width['l1'][lst[i]];
1926      if( w != 'auto' ) {
1927        g_tn.outerWidth['l1'][lst[i]]=w+g_tn.borderWidth+g_tn.imgcBorderWidth;
1928      }
1929      else {
1930        g_tn.outerWidth['l1'][lst[i]]=0;
1931      }
1932      w=g_tn.settings.width['lN'][lst[i]];
1933      if( w != 'auto' ) {
1934        g_tn.outerWidth['lN'][lst[i]]=w+g_tn.borderWidth+g_tn.imgcBorderWidth;
1935      }
1936      else {
1937        g_tn.outerWidth['lN'][lst[i]]=0;
1938      }
1939    }
1940    for( var i=0; i< lst.length; i++ ) {
1941      var h=g_tn.settings.height['l1'][lst[i]];
1942      if( h != 'auto' ) {
1943        g_tn.outerHeight['l1'][lst[i]]=h+g_tn.borderHeight+g_tn.imgcBorderHeight+g_tn.labelHeight;
1944      }
1945      else {
1946        g_tn.outerHeight['l1'][lst[i]]=0;
1947      }
1948      h=g_tn.settings.height['lN'][lst[i]];
1949      if( h != 'auto' ) {
1950        g_tn.outerHeight['lN'][lst[i]]=h+g_tn.borderHeight+g_tn.imgcBorderHeight+g_tn.labelHeight;
1951      }
1952      else {
1953        g_tn.outerHeight['lN'][lst[i]]=0;
1954      }
1955    }
1956    
1957    // pagination
1958    g_pgMaxNbThumbnailsPerRow=NbThumbnailsPerRow();
1959    
1960    // backup values used in animations/transitions
1961    g_custGlobals.oldBorderColor=$newDiv.css('border-color');
1962    if( g_custGlobals.oldBorderColor == '' || g_custGlobals.oldBorderColor == null || g_custGlobals.oldBorderColor == undefined ) { g_custGlobals.oldBorderColor='#000'; }
1963    g_custGlobals.oldLabelOpacity=$newDiv.find('.labelImage').css('opacity');
1964    var c=jQuery.Color($newDiv.find('.labelImage'),'backgroundColor');
1965    g_custGlobals.oldLabelRed=c.red();
1966    g_custGlobals.oldLabelGreen=c.green();
1967    g_custGlobals.oldLabelBlue=c.blue();
1968
1969    gI=[];
1970  }
1971
1972  function GetI18nItem( item, property ) {
1973    var s='';
1974    if( g_i18nLang != '' ) {
1975      if( item[property+'_'+g_i18nLang] !== undefined && item[property+'_'+g_i18nLang].length>0 ) {
1976        s=item[property+'_'+g_i18nLang];
1977        return s;
1978      }
1979    }
1980    s=item[property];
1981    return s;
1982  }
1983
1984  
1985  // ####################################
1986  // ##### LIST OF ITEMS IN OPTIONS #####
1987  // ####################################
1988
1989  function GetImageTitle( imageSRC ) {
1990    if( gO.thumbnailLabel.title == '%filename' ) {
1991      return (imageSRC.split('/').pop()).replace('_',' ');
1992    }
1993    
1994    if( gO.thumbnailLabel.title == '%filenameNoExt' ) {
1995      var s=imageSRC.split('/').pop();
1996      return (s.split('.').shift()).replace('_',' ');
1997    }
1998    return imageSRC;
1999  }
2000    
2001  function ProcessItemOption() {
2002    
2003    var foundAlbumID=false;
2004    
2005		if( typeof gO.dataSorting !== 'undefined' ) {
2006			if( gO.dataSorting == 'random' ) {
2007				gO.items=AreaShuffle(gO.items);
2008			}else if( gO.dataSorting == 'reversed' ) {
2009        gO.items=gO.items.reverse();
2010			}
2011		}
2012    
2013    jQuery.each(gO.items, function(i,item){
2014      
2015      var title='';
2016      title=GetI18nItem(item,'title');
2017      if( title === undefined ) { title=''; }
2018      
2019      var src=gO.itemsBaseURL+item.src;
2020
2021      var thumbsrc='';
2022      if( item.srct !== undefined && item.srct.length>0 ) {
2023        thumbsrc=gO.itemsBaseURL+item.srct;
2024      }
2025      else {
2026        thumbsrc=src;
2027      }
2028      
2029      var thumbsrcX2='';
2030      if( item.srct2x !== undefined && item.srct2x.length>0 ) {
2031        thumbsrcX2=gO.itemsBaseURL+item.srct2x;
2032      }
2033      else {
2034        if( thumbsrc != '' ) {
2035          thumbsrcX2=thumbsrc;
2036        }
2037        else {
2038          thumbsrcX2=src;
2039        }
2040      }
2041
2042
2043      if( gO.thumbnailLabel.title != '' ) {
2044        title=GetImageTitle(src);
2045      }
2046
2047      var description='';     //'&nbsp;';
2048      description=GetI18nItem(item,'description');
2049      if( description === undefined ) { description=''; }
2050      //if( toType(item.description) == 'string' ) {
2051      //  description=item.description;
2052      //}
2053
2054      var destinationURL='';
2055      if( item.destURL !== undefined && item.destURL.length>0 ) {
2056        destinationURL=item.destURL;
2057      }
2058
2059      //if( item.tags !== undefined && item.tags.length>0 ) {
2060      //  tags=item.tags;
2061      //}
2062      var tags=GetI18nItem(item,'tags');
2063      if( tags === undefined ) { tags=''; }
2064
2065      var albumID=0;
2066      if( item.albumID !== undefined  ) {
2067        albumID=item.albumID;
2068        foundAlbumID=true;
2069      }
2070      var ID=null;
2071      if( item.ID !== undefined ) {
2072        ID=item.ID;
2073      }
2074      var kind='image';
2075      if( item.kind !== undefined && item.kind.length>0 ) {
2076        kind=item.kind;
2077      }
2078      
2079      var newItem=NGAddItem(title, thumbsrc, src, description, destinationURL, kind, tags, ID, albumID );
2080      newItem.thumbX2src=thumbsrcX2;
2081
2082      // thumbnail image size
2083      var tw=0;
2084      if( item.imgtWidth !== undefined && item.imgtWidth>0 ) {
2085        tw=item.imgtWidth;
2086        newItem.thumbImgWidth=tw;
2087      }
2088      var th=0;
2089      if( item.imgtHeight !== undefined && item.imgtHeight>0 ) {
2090        th=item.imgtHeight;
2091        newItem.thumbImgHeight=th;
2092      }
2093
2094      newItem.thumbs = {
2095        url: { l1 : { xs:thumbsrc, sm:thumbsrc, me:thumbsrc, la:thumbsrc, xl:thumbsrc }, lN : { xs:thumbsrc, sm:thumbsrc, me:thumbsrc, la:thumbsrc, xl:thumbsrc } },
2096        width: { l1 : { xs:tw, sm:tw, me:tw, la:tw, xl:tw }, lN : { xs:tw, sm:tw, me:tw, la:tw, xl:tw } },
2097        height: { l1 : { xs:th, sm:th, me:th, la:th, xl:th }, lN : { xs:th, sm:th, me:th, la:th, xl:th } }
2098      };
2099
2100      
2101      if( typeof gO.fnProcessData == 'function' ) {
2102        gO.fnProcessData(newItem, 'api', null);
2103      }
2104    });
2105    
2106    if( foundAlbumID ) {
2107      gO.displayBreadcrumb=true;
2108    }
2109
2110    // get the number of images per album for all the items
2111    var l=gI.length,
2112    nb=0,
2113    nbImages=0;
2114    for( var i=0; i<l; i++ ){
2115      nb=0;
2116      nbImages=0;
2117      for( var j=0; j<l; j++ ){
2118        if( i!=j && gI[i].GetID() == gI[j].albumID ) {
2119          nb++;
2120          if( gI[j].kind == 'image' ) {
2121            gI[j].imageNumber=nbImages++;
2122          }
2123        }
2124      }
2125      gI[i].contentLength=nb;
2126    }
2127
2128  }
2129
2130
2131  // ###################################
2132  // ##### LIST OF HREF ATTRIBUTES #####
2133  // ###################################
2134
2135  function ProcessHREF(elements) {
2136    var foundAlbumID=false;
2137    
2138    if( typeof gO.dataSorting !== 'undefined' ) {
2139			if( gO.dataSorting == 'random' ) {
2140				elements=AreaShuffle(elements);
2141			}else if( gO.dataSorting == 'reversed' ) {
2142        jQuery.fn.reverse = [].reverse;
2143				elements=elements.reverse();
2144			}
2145		}
2146
2147    
2148    jQuery.each(elements, function(i,item){
2149      var thumbsrc='';
2150      if( jQuery(item).attr('data-ngthumb') !== undefined && jQuery(item).attr('data-ngthumb').length>0 ) {
2151        thumbsrc=gO.itemsBaseURL+jQuery(item).attr('data-ngthumb');
2152      }
2153      if( jQuery(item).attr('data-ngThumb') !== undefined && jQuery(item).attr('data-ngThumb').length>0 ) {
2154        thumbsrc=gO.itemsBaseURL+jQuery(item).attr('data-ngThumb');
2155      }
2156      var thumbsrcX2='';
2157      if( jQuery(item).attr('data-ngthumb2x') !== undefined && jQuery(item).attr('data-ngthumb2x').length>0 ) {
2158        thumbsrcX2=gO.itemsBaseURL+jQuery(item).attr('data-ngthumb2x');
2159      }
2160      if( jQuery(item).attr('data-ngThumb2x') !== undefined && jQuery(item).attr('data-ngThumb2x').length>0 ) {
2161        thumbsrcX2=gO.itemsBaseURL+jQuery(item).attr('data-ngThumb2x');
2162      }
2163
2164      
2165      src=gO.itemsBaseURL+jQuery(item).attr('href');
2166      //newObj.description=jQuery(item).attr('data-ngdesc');
2167      var description='';
2168      if( jQuery(item).attr('data-ngdesc') !== undefined && jQuery(item).attr('data-ngdesc').length>0 ) {
2169        description=jQuery(item).attr('data-ngdesc');
2170      }
2171      if( jQuery(item).attr('data-ngDesc') !== undefined && jQuery(item).attr('data-ngDesc').length>0 ) {
2172        description=jQuery(item).attr('data-ngDesc');
2173      }
2174
2175      var destURL='';
2176      if( jQuery(item).attr('data-ngdest') !== undefined && jQuery(item).attr('data-ngdest').length>0 ) {
2177        destURL=jQuery(item).attr('data-ngdest');
2178      }
2179      if( jQuery(item).attr('data-ngDest') !== undefined && jQuery(item).attr('data-ngDest').length>0 ) {
2180        destURL=jQuery(item).attr('data-ngDest');
2181      }
2182
2183      var albumID=0;
2184      if( jQuery(item).attr('data-ngalbumid') !== undefined ) {
2185        albumID=jQuery(item).attr('data-ngalbumid');
2186        foundAlbumID=true;
2187      }
2188      if( jQuery(item).attr('data-ngAlbumID') !== undefined ) {
2189        albumID=jQuery(item).attr('data-ngAlbumID');
2190        foundAlbumID=true;
2191      }
2192      
2193      var ID=null;
2194      if( jQuery(item).attr('data-ngid') !== undefined ) {
2195        ID=jQuery(item).attr('data-ngid');
2196      }
2197      if( jQuery(item).attr('data-ngID') !== undefined ) {
2198        ID=jQuery(item).attr('data-ngID');
2199      }
2200
2201      var kind='image';
2202      if( jQuery(item).attr('data-ngkind') !== undefined && jQuery(item).attr('data-ngkind').length>0 ) {
2203        kind=jQuery(item).attr('data-ngkind');
2204      }
2205      if( jQuery(item).attr('data-ngKind') !== undefined && jQuery(item).attr('data-ngKind').length>0 ) {
2206        kind=jQuery(item).attr('data-ngKind');
2207      }
2208
2209      var title=jQuery(item).text();
2210      if( !(gO.thumbnailLabel.title == '' || gO.thumbnailLabel.title == undefined) ) {
2211        title=GetImageTitle(src);
2212      }
2213
2214      //NGAddItem(jQuery(item).text(), thumbsrc, src, description, destURL, 'image', '' );
2215      var newItem=NGAddItem(title, thumbsrc, src, description, destURL, kind, '', ID, albumID );
2216      newItem.thumbX2src=thumbsrcX2;
2217      
2218      // thumbnail image size
2219      var tw=0;
2220      if( jQuery(item).attr('data-ngthumbImgWidth') !== undefined && jQuery(item).attr('data-ngthumbImgWidth').length>0 ) {
2221        tw=jQuery(item).attr('data-ngthumbImgWidth');
2222        newItem.thumbImgWidth=tw;
2223      }
2224      var th=0;
2225      if( jQuery(item).attr('data-ngthumbImgHeight') !== undefined && jQuery(item).attr('data-ngthumbImgHeight').length>0 ) {
2226        th=jQuery(item).attr('data-ngthumbImgHeight');
2227        newItem.thumbImgHeight=th;
2228      }
2229
2230      newItem.thumbs = {
2231        url: { l1 : { xs:thumbsrc, sm:thumbsrc, me:thumbsrc, la:thumbsrc, xl:thumbsrc }, lN : { xs:thumbsrc, sm:thumbsrc, me:thumbsrc, la:thumbsrc, xl:thumbsrc } },
2232        width: { l1 : { xs:tw, sm:tw, me:tw, la:tw, xl:tw }, lN : { xs:tw, sm:tw, me:tw, la:tw, xl:tw } },
2233        height: { l1 : { xs:th, sm:th, me:th, la:th, xl:th }, lN : { xs:th, sm:th, me:th, la:th, xl:th } }
2234      };
2235      
2236      if( typeof gO.fnProcessData == 'function' ) {
2237        gO.fnProcessData(newItem, 'markup', null);
2238      }
2239
2240    });
2241    
2242    jQuery.each(elements, function(i,item){ jQuery(item).remove(); });
2243    
2244    if( foundAlbumID ) {
2245      gO.displayBreadcrumb=true;
2246    }
2247
2248    // get the number of images per album for all the items
2249    var l=gI.length,
2250    nb=0,
2251    nbImages=0;
2252    for( var i=0; i<l; i++ ){
2253      nb=0;
2254      nbImages=0;
2255      for( var j=0; j<l; j++ ){
2256        if( i!=j && gI[i].GetID() == gI[j].albumID ) {
2257          nb++;
2258          if( gI[j].kind == 'image' ) {
2259            gI[j].imageNumber=nbImages++;
2260          }
2261        }
2262      }
2263      gI[i].contentLength=nb;
2264    }
2265    
2266  }
2267
2268  
2269  // ##########################
2270  // ##### CUSTOM STORAGE #####
2271  // ##########################
2272
2273  function CustomProcessItems( albumIdx, processLocationHash, imageID, setLocationHash) {
2274
2275    manageGalleryToolbar(albumIdx);
2276  
2277    if( gI[albumIdx].contentIsLoaded ) {    // already loaded?
2278      DisplayAlbum(albumIdx,setLocationHash);
2279      return;
2280    }
2281
2282    var url = gO.customSourceProvider + '?albumID='+encodeURIComponent(gI[albumIdx].GetID());
2283    PreloaderShow();
2284
2285    jQuery.ajaxSetup({ cache: false });
2286    jQuery.support.cors = true;
2287    
2288    var tId = setTimeout( function() {
2289      // workaround to handle JSONP (cross-domain) errors
2290      PreloaderHide();
2291      nanoAlert('Could not retrieve Custom data...');
2292    }, 60000 );
2293    jQuery.getJSON(url, function(data, status, xhr) {
2294      clearTimeout(tId);
2295      PreloaderHide();
2296
2297      CustomParseData(albumIdx, data);
2298      if( processLocationHash ) {
2299        if( !ProcessLocationHash(false) ) {
2300          DisplayAlbum(albumIdx,setLocationHash);
2301        }
2302      }
2303      else {
2304        if( imageID != -1 ) {
2305          var imageIdx=-1,
2306          l=gI.length;
2307          for(var i=0; i<l; i++ ) {
2308            if( gI[i].kind == 'image' && gI[i].GetID() == imageID ) {
2309              imageIdx=i;
2310              break;
2311            }
2312          }
2313          DisplayImage(imageIdx,true);
2314        }
2315        else {
2316          DisplayAlbum(albumIdx,setLocationHash);
2317        }
2318      }
2319    })
2320    .fail( function(jqxhr, textStatus, error) {
2321      clearTimeout(tId);
2322      PreloaderHide();
2323      var err = textStatus + ', ' + error;
2324      nanoAlert("Could not retrieve Custom items list (jQuery): " + 
2324err);
2325    });
2326  
2327  }
2328
2329  function CustomParseData(albumIdx, data) {
2330    var foundAlbumID=false;
2331   
2332    var nb=0;
2333    jQuery.each(data, function(i,item){
2334      
2335      var title='';
2336      title=GetI18nItem(item,'title');
2337      if( title === undefined ) { title=''; }
2338//      title=decodeURIComponent(title);
2339      
2340      var src=gO.itemsBaseURL+item.src;
2341      src=src.replaceAll('%2F', '/');
2342      // var thumbsrc=decodeURIComponent(gO.itemsBaseURL+item.srct);
2343      // var thumbsrc=gO.itemsBaseURL+decodeURIComponent(item.srct);
2344      // var thumbsrc=decodeURI(gO.itemsBaseURL+item.srct);
2345      var thumbsrc=gO.itemsBaseURL+item.srct;
2346       // thumbsrc=thumbsrc.replaceAll('é', '%E9');
2347       // thumbsrc=thumbsrc.replaceAll('%C3%A9', '%E9');
2348      // thumbsrc=thumbsrc.replaceAll('+', '%20');
2349      thumbsrc=thumbsrc.replaceAll('%2F', '/');
2350      if( gO.thumbnailLabel.title != '' ) {
2351        title=GetImageTitle(src);
2352      }
2353
2354      var description='';     //'&nbsp;';
2355      description=GetI18nItem(item,'description');
2356      if( description === undefined ) { description=''; }
2357
2358      var destinationURL='';
2359      if( item.destURL !== undefined && item.destURL.length>0 ) {
2360        destinationURL=item.destURL;
2361      }
2362
2363      var albumID=0;
2364      if( item.albumID !== undefined  ) {
2365        albumID=item.albumID;
2366        foundAlbumID=true;
2367      }
2368      var ID=null;
2369      if( item.ID !== undefined ) {
2370        ID=item.ID;
2371      }
2372      
2373      var kind='image';
2374      if( item.kind !== undefined && item.kind.length>0 ) {
2375        kind=item.kind;
2376      }
2377
2378      var newItem=NGAddItem(title, thumbsrc, src, description, destinationURL, kind, '', ID, albumID );
2379      // thumbnail image size
2380      var tw=item.imgtWidth;
2381      var th=item.imgtHeight;
2382
2383      newItem.thumbs = {
2384        url: { l1 : { xs:thumbsrc, sm:thumbsrc, me:thumbsrc, la:thumbsrc, xl:thumbsrc }, lN : { xs:thumbsrc, sm:thumbsrc, me:thumbsrc, la:thumbsrc, xl:thumbsrc } },
2385        width: { l1 : { xs:tw, sm:tw, me:tw, la:tw, xl:tw }, lN : { xs:tw, sm:tw, me:tw, la:tw, xl:tw } },
2386        height: { l1 : { xs:th, sm:th, me:th, la:th, xl:th }, lN : { xs:th, sm:th, me:th, la:th, xl:th } }
2387      };
2388
2389      if( typeof gO.fnProcessData == 'function' ) {
2390        gO.fnProcessData(newItem, 'api', null);
2391      }
2392      
2393      if( kind == 'image' ) {
2394        newItem.imageNumber=nb;
2395        nb++;
2396        if( nb >= g_maxAlbums ) {
2397          return false;
2398        }
2399      }
2400    });
2401    
2402    if( foundAlbumID ) {
2403      gO.displayBreadcrumb=true;
2404    }
2405
2406    gI[albumIdx].contentIsLoaded=true;
2407    gI[albumIdx].contentLength=nb;
2408    
2409  }
2410  
2411
2412  // ##########################
2413  // ##### FLICKR STORAGE #####
2414  // ##########################
2415
2416  function FlickrProcessItems( albumIdx, processLocationHash, imageID, setLocationHash) {
2417
2418    manageGalleryToolbar(albumIdx);
2419  
2420    //if( gI[albumIdx].contentLength != 0 ) {    // already loaded?
2421    if( gI[albumIdx].contentIsLoaded ) {    // already loaded?
2422      DisplayAlbum(albumIdx,setLocationHash);
2423      return;
2424    }
2425
2426    var url = '',
2427    kind='album';
2428    if( gI[albumIdx].GetID() == 0 ) {
2429      // albums
2430      //url = g_flickr.url() + "?&method=flickr.photosets.getList&api_key=" + g_flickr.ApiKey + "&user_id="+gO.userID+"&primary_photo_extras=url_"+g_flickr.thumbSize+"&format=json&jsoncallback=?";
2431      url = g_flickr.url() + "?&method=flickr.photosets.getList&api_key=" + g_flickr.ApiKey + "&user_id="+gO.userID+"&per_page=500&primary_photo_extras=url_o,url_sq,url_t,url_q,url_s,url_m,url_z,url_b,url_h,url_k&format=json&jsoncallback=?";
2432    }
2433    else {
2434      // photos
2435      if( gI[albumIdx].GetID() == 'none' ) {
2436        // get photos from full photostream
2437        url = g_flickr.url() + "?&method=flickr.people.getPublicPhotos&api_key=" + g_flickr.ApiKey + "&user_id="+gO.userID+"&extras=description,views,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&jsoncallback=?";
2438      }
2439      else {
2440        // photos from one specific photoset
2441        //url = g_flickr.url() + "?&method=flickr.photosets.getPhotos&api_key=" + g_flickr.ApiKey + "&photoset_id="+gI[albumIdx].GetID()+"&extras=description,views,url_o,url_z,url_"+g_flickr.photoSize+",url_"+g_flickr.thumbSize+"&format=json&jsoncallback=?";
2442        url = g_flickr.url() + "?&method=flickr.photosets.getPhotos&api_key=" + g_flickr.ApiKey + "&photoset_id="+gI[albumIdx].GetID()+"&extras=description,views,url_o,url_sq,url_t,url_q,url_s,url_m,url_z,url_b,url_h,url_k&format=json&jsoncallback=?";
2443      }
2444      kind='image';
2445    }
2446
2447    PreloaderShow();
2448
2449    jQuery.ajaxSetup({ cache: false });
2450    jQuery.support.cors = true;
2451    
2452    var tId = setTimeout( function() {
2453      // workaround to handle JSONP (cross-domain) errors
2454      PreloaderHide();
2455      nanoAlert('Could not retrieve Flickr data...');
2456    }, 60000 );
2457    jQuery.getJSON(url, function(data, status, xhr) {
2458      clearTimeout(tId);
2459      PreloaderHide();
2460      if( kind == 'album' ) {
2461        FlickrParsePhotoSets(albumIdx, data);
2462      }
2463      else {
2464        FlickrParsePhotos(albumIdx, data);
2465      }
2466      if( processLocationHash ) {
2467        if( !ProcessLocationHash(false) ) {
2468          DisplayAlbum(albumIdx,setLocationHash);
2469        }
2470      }
2471      else {
2472        if( imageID != -1 ) {
2473          var imageIdx=-1,
2474          l=gI.length;
2475          for(var i=0; i<l; i++ ) {
2476            if( gI[i].kind == 'image' && gI[i].GetID() == imageID ) {
2477              imageIdx=i;
2478              break;
2479            }
2480          }
2481          DisplayImage(imageIdx,true);
2482        }
2483        else {
2484          DisplayAlbum(albumIdx,setLocationHash);
2485        }
2486      }
2487    })
2488    .fail( function(jqxhr, textStatus, error) {
2489      clearTimeout(tId);
2490      PreloaderHide();
2491      var err = textStatus + ', ' + error;
2492      nanoAlert("Could not retrieve Flickr photoset list (jQuery): " + err);
2493    });
2494  
2495  }
2496
2497  
2498  function FlickrParsePhotoSets( albumIdx, data ) {
2499    var ok=true;
2500    if( data.stat !== undefined ) {
2501      if( data.stat === 'fail' ) {
2502        nanoAlert("Could not retrieve Flickr photoset list: " + data.message + " (code: "+data.code+").");
2503        ok=false;
2504      }
2505    }
2506
2507    if( ok ) {
2508      var nb=0;
2509      
2510      var source = data.photosets.photoset;
2511      switch( gO.albumSorting ) {
2512        case 'random':
2513          source = AreaShuffle(source);
2514          break;
2515        case 'reversed':
2516          source = source.reverse();
2517          break;
2518        case 'titleAsc':
2519          source.sort(function (a, b) {
2520            var x = a.title._content.toUpperCase();
2521            var y =  b.title._content.toUpperCase();
2522            return( (x < y) ? -1 : ((x > y) ? 1 : 0) );
2523          });
2524          break;
2525        case 'titleDesc':
2526          source.sort(function (a, b) {
2527            var x = a.title._content.toUpperCase();
2528            var y =  b.title._content.toUpperCase();
2529            return( (x > y) ? -1 : ((x < y) ? 1 : 0) );
2530          });
2531      }
2532
2533      jQuery.each(source, function(i,item){
2534        //Get the title
2535        itemTitle = item.title._content;
2536        
2537        if( CheckAlbumName(itemTitle, item.id) ) {
2538          itemID=item.id;
2539          //Get the description
2540          itemDescription='';
2541          if (item.description._content != undefined) {
2542            itemDescription=item.description._content;
2543          }
2544
2545          var sizes = {};
2546          for (var p in item.primary_photo_extras) {
2547            sizes[p]=item.primary_photo_extras[p];
2548          }
2549          tags='';
2550          if( item.primary_photo_extras !== undefined ) {
2551            if( item.primary_photo_extras.tags !== undefined ) {
2552              tags=item.primary_photo_extras.tags;
2553            }
2554          }
2555        
2556          var newItem=NGAddItem(itemTitle, '', '', itemDescription, '', 'album', tags, itemID, gI[albumIdx].GetID() );
2557          //newItem.thumbImgWidth=item.primary_photo_extras['width_'+g_flickr.thumbSize];
2558          //newItem.thumbImgHeight=item.primary_photo_extras['height_'+g_flickr.thumbSize];
2559          newItem.contentLength=item.photos;
2560          newItem.thumbSizes=sizes;
2561          
2562          var tn = {
2563            url: { l1 : { xs:'', sm:'', me:'', la:'', xl:'' }, lN : { xs:'', sm:'', me:'', la:'', xl:'' } },
2564            width: { l1 : { xs:0, sm:0, me:0, la:0, xl:0 }, lN : { xs:0, sm:0, me:0, la:0, xl:0 } },
2565            height: { l1 : { xs:0, sm:0, me:0, la:0, xl:0 }, lN : { xs:0, sm:0, me:0, la:0, xl:0 } }
2566          };
2567          tn=FlickrRetrieveImages(tn, item.primary_photo_extras, 'l1' );
2568          tn=FlickrRetrieveImages(tn, item.primary_photo_extras, 'lN' );
2569          newItem.thumbs=tn;
2570          
2571          nb++;
2572          if( nb >= g_maxAlbums ) {
2573            return false;
2574          }
2575        }
2576      });
2577      
2578      gI[albumIdx].contentIsLoaded=true;
2579      gI[albumIdx].contentLength=nb;
2580    }
2581  }
2582  
2583  function FlickrRetrieveImages(tn, item, level ) {
2584    var sizes=['xs','sm','me','la','xl'];
2585    for(var i=0; i<sizes.length; i++ ) {
2586      if( g_tn.settings.width[level][sizes[i]] == 'auto' ) {
2587        var sdir='height_';
2588        var tsize=Math.ceil(g_tn.settings.height[level][sizes[i]]*g_tn.scale);
2589        var one=FlickrRetrieveOneImage(sdir, tsize, item );
2590        tn.url[level][sizes[i]]=one.url;
2591        tn.width[level][sizes[i]]=one.width;
2592        tn.height[level][sizes[i]]=one.height;
2593      }
2594      else 
2595        if( g_tn.settings.height[level][sizes[i]] == 'auto' ) {
2596          var sdir='width_';
2597          var tsize=Math.ceil(g_tn.settings.width[level][sizes[i]]*g_tn.scale);
2598          var one=FlickrRetrieveOneImage(sdir, tsize, item );
2599          tn.url[level][sizes[i]]=one.url;
2600          tn.width[level][sizes[i]]=one.width;
2601          tn.height[level][sizes[i]]=one.height;
2602        }
2603        else {
2604          var sdir='height_';
2605          var tsize=Math.ceil(g_tn.settings.height[level][sizes[i]]*g_tn.scale);
2606          if( g_tn.settings.width[level][sizes[i]] > g_tn.settings.height[level][sizes[i]] ) {
2607            sdir='width_';
2608            tsize=Math.ceil(g_tn.settings.width[level][sizes[i]]*g_tn.scale);
2609          }
2610          var one=FlickrRetrieveOneImage(sdir, tsize, item );
2611          tn.url[level][sizes[i]]=one.url;
2612          tn.width[level][sizes[i]]=one.width;
2613          tn.height[level][sizes[i]]=one.height;
2614        }
2615    }
2616    return tn;
2617  }
2618  
2619  function FlickrRetrieveOneImage(sdir, tsize, item ) {
2620    var one={ url:'', width:0, height:0 };
2621    var tnIndex=0;
2622    for(var j=0; j<g_flickr.thumbAvailableSizes.length; j++ ) {
2623      var size=item[sdir+g_flickr.photoAvailableSizesStr[j]];
2624      if(  size != undefined ) {
2625        tnIndex=j;
2626        if( size >= tsize ) {
2627          break;
2628        }
2629      }
2630    }
2631    var fSize=g_flickr.photoAvailableSizesStr[tnIndex];
2632    one.url= item['url_'+fSize];
2633    one.width= parseInt(item['width_'+fSize]);
2634    one.height=parseInt(item['height_'+fSize]);
2635    return one;
2636  }
2637
2638  function FlickrParsePhotos( albumIdx, data ) {
2639    var source = '';
2640    if( gI[albumIdx].GetID() == 'none' ) {
2641      source = data.photos.photo;
2642    }
2643    else {
2644      source = data.photoset.photo;
2645    }
2646
2647    switch( gO.photoSorting ) {
2648      case 'random':
2649				source = AreaShuffle(source);
2650        break;
2651      case 'reversed':
2652				source = source.reverse();
2653        break;
2654      case 'titleAsc':
2655        source.sort(function (a, b) {
2656          // var x = a.title.toUpperCase();
2657          // var y =  b.title.toUpperCase();
2658          var x='', y='';
2659          if( gO.thumbnailLabel.title != '' ) {
2660            x=GetImageTitle(a.url_sq);
2661            y=GetImageTitle(b.url_sq);
2662          }
2663          else {
2664            x = a.title.toUpperCase();
2665            y =  b.title.toUpperCase();
2666          }
2667          return( (x < y) ? -1 : ((x > y) ? 1 : 0) );
2668        });
2669        break;
2670      case 'titleDesc':
2671        source.sort(function (a, b) {
2672          // var x = a.title.toUpperCase();
2673          // var y =  b.title.toUpperCase();
2674          var x='', y='';
2675          if( gO.thumbnailLabel.title != '' ) {
2676            x=GetImageTitle(a.url_sq);
2677            y=GetImageTitle(b.url_sq);
2678          }
2679          else {
2680            x = a.title.toUpperCase();
2681            y =  b.title.toUpperCase();
2682          }
2683          return( (x > y) ? -1 : ((x < y) ? 1 : 0) );
2684        });
2685        break;
2686    }
2687
2688    var albumID=gI[albumIdx].GetID(),
2689    nb=0;
2690    jQuery.each(source, function(i,item){
2691      //Get the title
2692      var itemTitle = item.title,
2693      itemID=item.id,
2694      itemDescription=item.description._content;    // Get the description
2695      
2696      var imgUrl=item.url_sq;  //fallback size
2697      for(var i=g_flickr.photoSize; i>=0; i-- ) {
2698        if( item['url_'+g_flickr.photoAvailableSizesStr[i]] != undefined ) {
2699          imgUrl=item['url_'+g_flickr.photoAvailableSizesStr[i]];
2700          break;
2701        }
2702      }
2703
2704      var sizes = {};
2705      for (var p in item) {
2706        if( p.indexOf('height_') == 0 || p.indexOf('width_') == 0 || p.indexOf('url_') == 0 ) {
2707          sizes[p]=item[p];
2708        }
2709      }
2710      
2711      if( gO.thumbnailLabel.title != '' ) {
2712        itemTitle=GetImageTitle(imgUrl);
2713      }
2714
2715      var newItem=NGAddItem(itemTitle, '', imgUrl, itemDescription, '', 'image', '', itemID, albumID );
2716      newItem.imageNumber=nb;
2717      if( item.url_o !== undefined ) {
2718        newItem.width=item.width_o;
2719        newItem.height=item.height_o;
2720      }
2721      else {
2722        newItem.width=item.width_z;
2723        newItem.height=item.height_z;
2724      }
2725      //newItem.thumbImgWidth=item['width_'+g_flickr.photoAvailableSizesStr[tnIndex]];
2726      //newItem.thumbImgHeight=item['height_'+g_flickr.photoAvailableSizesStr[tnIndex]];
2727//      newItem.thumbSizes=sizes;
2728
2729
2730        var tn = {
2731          url: { l1 : { xs:'', sm:'', me:'', la:'', xl:'' }, lN : { xs:'', sm:'', me:'', la:'', xl:'' } },
2732          width: { l1 : { xs:0, sm:0, me:0, la:0, xl:0 }, lN : { xs:0, sm:0, me:0, la:0, xl:0 } },
2733          height: { l1 : { xs:0, sm:0, me:0, la:0, xl:0 }, lN : { xs:0, sm:0, me:0, la:0, xl:0 } }
2734        };
2735        tn=FlickrRetrieveImages(tn, item, 'l1' );
2736        tn=FlickrRetrieveImages(tn, item, 'lN' );
2737        newItem.thumbs=tn;
2738
2739      nb++;
2740      if( nb >= g_maxPhotos ) {
2741        return false;
2742      }
2743      
2744    });
2745    gI[albumIdx].contentIsLoaded=true;
2746    gI[albumIdx].contentLength=nb;
2747
2748  }
2749  
2750
2751  // ##########################
2752  // ##### PICASA STORAGE #####
2753  // ##########################
2754
2755  function AddOneThumbSize(thumbSizes, v1, v2, c1, c2 ) {
2756    var v=Math.ceil(v2*g_tn.scale)+c2;
2757    if( v1 == 'auto' ) {
2758      v=Math.ceil(v2*g_tn.scale)+c2;
2759    }
2760    else if( v2 == 'auto' ) {
2761        v=Math.ceil(v1*g_tn.scale)+c1;
2762      }
2763      else if( v1 > v2 ) {
2764        v=Math.ceil(v1*g_tn.scale)+c1;
2765      }
2766      
2767    if( thumbSizes.length > 0 ) {
2768      thumbSizes+=',';
2769    }
2770    thumbSizes+=v;
2771    return thumbSizes;
2772  }
2773  
2774  function PicasaProcessItems( albumIdx, processLocationHash, imageID, setLocationHash ) {
2775
2776    manageGalleryToolbar(albumIdx);
2777  
2778    if( gI[albumIdx].contentIsLoaded ) {    // already loaded?
2779      //renderGallery(albumIdx,0);
2780      DisplayAlbum(albumIdx,setLocationHash);
2781      return;
2782    }
2783    
2784    var url='',
2785    kind='album';
2786   
2787    var thumbSizes='';
2788    thumbSizes=AddOneThumbSize(thumbSizes, g_tn.settings.width.l1.xs, g_tn.settings.height.l1.xs, g_tn.settings.width.l1.xsc, g_tn.settings.height.l1.xsc );
2789    thumbSizes=AddOneThumbSize(thumbSizes, g_tn.settings.width.l1.sm, g_tn.settings.height.l1.sm, g_tn.settings.width.l1.smc, g_tn.settings.height.l1.smc );
2790    thumbSizes=AddOneThumbSize(thumbSizes, g_tn.settings.width.l1.me, g_tn.settings.height.l1.me, g_tn.settings.width.l1.mec, g_tn.settings.height.l1.mec );
2791    thumbSizes=AddOneThumbSize(thumbSizes, g_tn.settings.width.l1.la, g_tn.settings.height.l1.la, g_tn.settings.width.l1.lac, g_tn.settings.height.l1.lac );
2792    thumbSizes=AddOneThumbSize(thumbSizes, g_tn.settings.width.l1.xl, g_tn.settings.height.l1.xl, g_tn.settings.width.l1.xlc, g_tn.settings.height.l1.xlc );
2793    thumbSizes=AddOneThumbSize(thumbSizes, g_tn.settings.width.lN.xs, g_tn.settings.height.lN.xs, g_tn.settings.width.lN.xsc, g_tn.settings.height.lN.xsc );
2794    thumbSizes=AddOneThumbSize(thumbSizes, g_tn.settings.width.lN.sm, g_tn.settings.height.lN.sm, g_tn.settings.width.lN.smc, g_tn.settings.height.lN.smc );
2795    thumbSizes=AddOneThumbSize(thumbSizes, g_tn.settings.width.lN.me, g_tn.settings.height.lN.me, g_tn.settings.width.lN.mec, g_tn.settings.height.lN.mec );
2796    thumbSizes=AddOneThumbSize(thumbSizes, g_tn.settings.width.lN.la, g_tn.settings.height.lN.la, g_tn.settings.width.lN.lac, g_tn.settings.height.lN.lac );
2797    thumbSizes=AddOneThumbSize(thumbSizes, g_tn.settings.width.lN.xl, g_tn.settings.height.lN.xl, g_tn.settings.width.lN.xlc, g_tn.settings.height.lN.xlc );
2798    if( gI[albumIdx].GetID() == 0 ) {
2799      // albums
2800      //url = g_picasa.url() + 'user/'+gO.userID+'?alt=json&kind=album&imgmax=d&thumbsize='+g_picasa.thumbSize;
2801      // url = g_picasa.url() + 'user/'+gO.userID+'?alt=json&kind=album&imgmax=d&thumbsize=320';
2802      // url = g_picasa.url() + 'user/'+gO.userID+'?alt=json&kind=album&imgmax=d&thumbsize='+thumbSizes;
2803      // url = g_picasa.url() + 'user/'+gO.userID+'?alt=json&kind=album&access=public&thumbsize='+thumbSizes+'&rnd=' + (new Date().getTime());
2804      url = g_picasa.url() + 'user/'+gO.userID+'?alt=json&kind=album&thumbsize='+thumbSizes+'&rnd=' + (new Date().getTime());
2805    }
2806    else {
2807      // photos
2808      var opt='';
2809      if( typeof gI[albumIdx].customData.authkey !== 'undefined' ) { opt=gI[albumIdx].customData.authkey; }
2810      // url = g_picasa.url() + 'user/'+gO.userID+'/albumid/'+gI[albumIdx].GetID()+'?alt=json&kind=photo'+opt+'&thumbsize='+g_picasa.thumbSize+'&imgmax=d';
2811      url = g_picasa.url() + 'user/'+gO.userID+'/albumid/'+gI[albumIdx].GetID()+'?alt=json&kind=photo'+opt+'&thumbsize='+thumbSizes+'&imgmax=d';
2812      // url = g_picasa.url() + 'user/'+gO.userID+'/albumid/'+gI[albumIdx].GetID()+'?alt=json&kind=photo'+opt+'&thumbsize=320&imgmax=d';
2813      kind='image';
2814    }
2815//A    url = url + "&callback=?";
2816    PreloaderShow();
2817
2818    // get the content and display it
2819    jQuery.ajaxSetup({ cache: false });
2820    jQuery.support.cors = true;
2821    var tId = setTimeout( function() {
2822      // workaround to handle JSONP (cross-domain) errors
2823      PreloaderHide();
2824      nanoAlert('Could not retrieve Picasa/Google+ data...');
2825    }, 60000 );
2826    jQuery.getJSON(url, 'callback=?', function(data) {
2827      
2828      clearTimeout(tId);
2829      PreloaderHide();
2830      PicasaParseData(albumIdx,data,kind);
2831      //renderGallery(albumIdx,0);
2832      if( processLocationHash ) {
2833        if( !ProcessLocationHash(false) ) {
2834          DisplayAlbum(albumIdx,setLocationHash);
2835        }
2836      }
2837      else {
2838        if( imageID != -1 ) {
2839            var imageIdx=-1,
2840            l=gI.length;
2841            for(var i=0; i<l; i++ ) {
2842              if( gI[i].kind == 'image' && gI[i].GetID() == imageID ) {
2843                imageIdx=i;
2844                break;
2845              }
2846            }
2847            DisplayImage(imageIdx,true);
2848          }
2849          else {
2850            DisplayAlbum(albumIdx,setLocationHash);
2851          }
2852        }
2853    })
2854    .fail( function(jqxhr, textStatus, error) {
2855      clearTimeout(tId);
2856      PreloaderHide();
2857      
2858      //alertObject(jqxhr);
2859      var k=''
2860      for(var key in jqxhr) {
2861        k+= key + '=' + jqxhr[key] +'<br>';
2862      }
2863      var err = textStatus + ', ' + error + ' ' + k + '<br><br>URL:'+url;
2864      nanoAlert("Could not retrieve Picasa/Google+ data. Error: " + err);
2865    });
2866    
2867  }
2868  
2869  function PicasaParseData( albumIdx, data, kind ) {
2870    var nb=0,
2871    albumID=gI[albumIdx].GetID();
2872    
2873    var source = data.feed.entry;
2874    var sortOrder=gO.albumSorting;
2875    if (kind =='image'){
2876      sortOrder=gO.photoSorting;
2877    }
2878
2879    switch( sortOrder ) {
2880      case 'random':
2881        source = AreaShuffle(source);
2882        break;
2883      case 'reversed':
2884        source = source.reverse();
2885        break;
2886      case 'titleAsc':
2887        source.sort(function (a, b) {
2888          // var x =  kind == 'image' ? a.media$group.media$description.$t.toUpperCase() : a.media$group.media$title.$t.toUpperCase();
2889          // var y =  kind == 'image' ? b.media$group.media$description.$t.toUpperCase() : b.media$group.media$title.$t.toUpperCase();
2890          var x='', y='';
2891          if( kind == 'image' ) {
2892            if( gO.thumbnailLabel.title != '' ) {
2893              x=GetImageTitle(unescape(unescape(unescape(unescape(a.media$group.media$content[0].url)))));
2894              y=GetImageTitle(unescape(unescape(unescape(unescape(b.media$group.media$content[0].url)))));
2895            }
2896            else {
2897              x = a.media$group.media$description.$t.toUpperCase();
2898              y = b.media$group.media$description.$t.toUpperCase();
2899            }
2900          }
2901          else {
2902            x = a.media$group.media$title.$t.toUpperCase();
2903            y = b.media$group.media$title.$t.toUpperCase();
2904          }
2905          return( (x < y) ? -1 : ((x > y) ? 1 : 0) );
2906        });
2907        break;
2908      case 'titleDesc':
2909        source.sort(function (a, b) {
2910          // var x =  kind == 'image' ? a.media$group.media$description.$t.toUpperCase() : a.media$group.media$title.$t.toUpperCase();
2911          // var y =  kind == 'image' ? b.media$group.media$description.$t.toUpperCase() : b.media$group.media$title.$t.toUpperCase();
2912          var x='', y='';
2913          if( kind == 'image' ) {
2914            if( gO.thumbnailLabel.title != '' ) {
2915              x=GetImageTitle(unescape(unescape(unescape(unescape(a.media$group.media$content[0].url)))));
2916              y=GetImageTitle(unescape(unescape(unescape(unescape(b.media$group.media$content[0].url)))));
2917            }
2918            else {
2919              x = a.media$group.media$description.$t.toUpperCase();
2920              y = b.media$group.media$description.$t.toUpperCase();
2921            }
2922          }
2923          else {
2924            x = a.media$group.media$title.$t.toUpperCase();
2925            y = b.media$group.media$title.$t.toUpperCase();
2926          }
2927          return( (x > y) ? -1 : ((x < y) ? 1 : 0) );
2928        });
2929        break;
2930    }
2931
2932    jQuery.each(source, function(i,data){
2933      
2934      //Get the title 
2935      var itemTitle = data.media$group.media$title.$t;
2936      //Get the URL of the thumbnail
2937      var itemThumbURL = data.media$group.media$thumbnail[0].url;
2938      //Get the ID 
2939      var itemID = data.gphoto$id.$t;
2940      //Get the description
2941      var filename='';
2942      var itemDescription = data.media$group.media$description.$t;
2943      if( kind == 'image') { 
2944        filename=itemTitle;
2945        itemTitle=itemDescription;
2946        itemDescription='';
2947      }
2948      
2949      var imgUrl=data.media$group.media$content[0].url;
2950      
2951      if( kind == 'image') { 
2952        if( gO.thumbnailLabel.title != '' ) {
2953          itemTitle=GetImageTitle(unescape(unescape(unescape(unescape(imgUrl)))));
2954        }
2955      }
2956
2957      var ok=true;
2958      if( kind == 'album' ) {
2959        if( !CheckAlbumName(itemTitle, itemID) ) { ok=false; }
2960      }
2961     
2962      var tags='';
2963      
2964      if( ok ) {
2965
2966        var picasaThumbBaseURL='';
2967        // var picasaThumbBaseURL=itemThumbURL.substring(0, itemThumbURL.lastIndexOf('/'));
2968        // picasaThumbBaseURL=picasaThumbBaseURL.substring(0, picasaThumbBaseURL.lastIndexOf('/')) + '/';
2969
2970        var src='';
2971        if( kind == 'album' ) {
2972          src=itemID;
2973        }
2974        else {
2975          src=imgUrl;
2976          var s=imgUrl.substring(0, imgUrl.lastIndexOf('/'));
2977          s=s.substring(0, s.lastIndexOf('/')) + '/';
2978
2979          if( window.screen.width >  window.screen.height ) {
2980            src=s+'w'+window.screen.width+'/'+filename;
2981          }
2982          else {
2983            src=s+'h'+window.screen.height+'/'+filename;
2984            }
2985        }
2986        var newItem= NGAddItem(itemTitle, itemThumbURL, src, itemDescription, '', kind, tags, itemID, albumID );
2987        newItem.picasaThumbBaseURL=picasaThumbBaseURL;
2988        newItem.imageNumber=nb;
2989        if( kind == 'album' ) {
2990          newItem.author=data.author[0].name.$t;
2991          newItem.contentLength=data.gphoto$numphotos.$t;
2992        }
2993
2994        
2995        // OLD METHOD
2996        /*
2997        if( kind == 'image' ) {
2998        newItem.width=data.gphoto$width.$t;
2999          newItem.height=data.gphoto$height.$t;
3000          newItem.thumbImgWidth=data.media$group.media$thumbnail[0].width;
3001          newItem.thumbImgHeight=data.media$group.media$thumbnail[0].height;
3002        }
3003        else {
3004          if( SettingsGetTnHeight() == 'auto' ) {
3005            newItem.thumbImgWidth=g_picasa.thumbSize;
3006          }
3007          else 
3008            if( SettingsGetTnWidth() == 'auto' ) {
3009              newItem.thumbImgHeight=g_picasa.thumbSize;
3010            }
3011            else {
3012              newItem.thumbImgWidth=g_picasa.thumbSize;
3013              newItem.thumbImgHeight=g_picasa.thumbSize;
3014            }
3015        }
3016        */
3017
3018        // NEW METHOD
3019        var tn = { 
3020          url: { l1 : { xs:'', sm:'', me:'', la:'', xl:'' }, lN : { xs:'', sm:'', me:'', la:'', xl:'' } },
3021          width: { l1 : { xs:0, sm:0, me:0, la:0, xl:0 }, lN : { xs:0, sm:0, me:0, la:0, xl:0 } },
3022          height: { l1 : { xs:0, sm:0, me:0, la:0, xl:0 }, lN : { xs:0, sm:0, me:0, la:0, xl:0 } }
3023        };
3024        tn=PicasaThumbSetSizes('l1', 0, tn, data, kind );
3025        tn=PicasaThumbSetSizes('lN', 5, tn, data, kind );
3026        /* for test purposes
3027        tn.width.l1.xs=0;
3028        tn.width.l1.sm=0;
3029        tn.width.l1.me=0;
3030        tn.width.l1.la=0;
3031        tn.width.l1.xl=0;
3032        tn.width.lN.xs=0;
3033        tn.width.lN.sm=0;
3034        tn.width.lN.me=0;
3035        tn.width.lN.la=0;
3036        tn.width.lN.xl=0;
3037        tn.height.l1.xs=0;
3038        tn.height.l1.sm=0;
3039        tn.height.l1.me=0;
3040        tn.height.l1.la=0;
3041        tn.height.l1.xl=0;
3042        tn.height.lN.xs=0;
3043        tn.height.lN.sm=0;
3044        tn.height.lN.me=0;
3045        tn.height.lN.la=0;
3046        tn.height.lN.xl=0;
3047        */        
3048        newItem.thumbs=tn;
3049        
3050        if( typeof gO.fnProcessData == 'function' ) {
3051          gO.fnProcessData(newItem, 'picasa', data);
3052        }
3053        
3054        nb++;
3055        if( nb >= (kind == 'album' ? g_maxAlbums : g_maxPhotos) ) {
3056          return false;
3057        }
3058
3059      }
3060      
3061    });
3062    
3063    gI[albumIdx].contentIsLoaded=true;
3064    gI[albumIdx].contentLength=nb;
3065    
3066  }
3067  
3068  function PicasaThumbSetSizes(level, startI, tn, data, kind ) {
3069    var sizes=['xs','sm','me','la','xl'];
3070    for(var i=0; i<sizes.length; i++ ) {
3071      tn.url[level][sizes[i]]=data.media$group.media$thumbnail[startI+i].url;
3072      if( kind == 'image' ) {
3073        tn.width[level][sizes[i]]=data.media$group.media$thumbnail[startI+i].width;
3074        tn.height[level][sizes[i]]=data.media$group.media$thumbnail[startI+i].height;
3075
3076        var gw=data.media$group.media$thumbnail[startI+i].width;
3077        var gh=data.media$group.media$thumbnail[startI+i].height;
3078        if( g_tn.settings.width[level][sizes[i]] == 'auto' ) {
3079          if( gh < g_tn.settings.height[level][sizes[i]] ) {
3080            // calculate new h/w and change URL
3081            var ratio=gw/gh;
3082            tn.width[level][sizes[i]]=gw*ratio;
3083            tn.height[level][sizes[i]]=gh*ratio;
3084            var url=tn.url[level][sizes[i]].substring(0, tn.url[level][sizes[i]].lastIndexOf('/'));
3085            url=url.substring(0, url.lastIndexOf('/')) + '/';
3086            tn.url[level][sizes[i]]=url+'h'+g_tn.settings.height[level][sizes[i]]+'/';
3087          }
3088        }
3089        if( g_tn.settings.height[level][sizes[i]] == 'auto' ) {
3090          if( gw < g_tn.settings.width[level][sizes[i]] ) {
3091            // calculate new h/w and change URL
3092            var ratio=gh/gw;
3093            tn.height[level][sizes[i]]=gh*ratio;
3094            tn.width[level][sizes[i]]=gw*ratio;
3095            var url=tn.url[level][sizes[i]].substring(0, tn.url[level][sizes[i]].lastIndexOf('/'));
3096            url=url.substring(0, url.lastIndexOf('/')) + '/';
3097            tn.url[level][sizes[i]]=url+'w'+g_tn.settings.width[level][sizes[i]]+'/';
3098          }
3099        }
3100      }
3101      else {
3102        if( g_tn.settings.width[level][sizes[i]] != 'auto' ) {
3103          tn.width[level][sizes[i]]=data.media$group.media$thumbnail[startI+i].width;
3104        }
3105        else {
3106          var url=tn.url[level][sizes[i]].substring(0, tn.url[level][sizes[i]].lastIndexOf('/'));
3107          url=url.substring(0, url.lastIndexOf('/')) + '/';
3108          tn.url[level][sizes[i]]=url+'h'+g_tn.settings.height[level][sizes[i]]+'/';
3109        }
3110        if( g_tn.settings.height[level][sizes[i]] != 'auto' ) { 
3111          tn.height[level][sizes[i]]=data.media$group.media$thumbnail[startI+i].height;
3112        }
3113        else {
3114            var url=tn.url[level][sizes[i]].substring(0, tn.url[level][sizes[i]].lastIndexOf('/'));
3115            url=url.substring(0, url.lastIndexOf('/')) + '/';
3116            tn.url[level][sizes[i]]=url+'w'+g_tn.settings.width[level][sizes[i]]+'/';
3117        }
3118      }
3119      // if( kind == 'image' || g_tn.settings.width.l1[sizes[i]] != 'auto' ) { tn.width.l1[sizes[i]]=data.media$group.media$thumbnail[i].width; }
3120      // if( kind == 'image' || g_tn.settings.height.l1[sizes[i]] != 'auto' ) { tn.height.l1[sizes[i]]=data.media$group.media$thumbnail[i].height; }
3121    }
3122    return tn;
3123  }
3124  
3125  
3126  // ################################
3127  // ##### NGITEMS MANIPULATION #####
3128  // ################################
3129  
3130  function NGAddItem(title, thumbSrc, imageSrc, description, destinationURL, kind, tags, ID, albumID ) {
3131    var newObj=new NGItems(title,ID);
3132    newObj.thumbsrc=thumbSrc;
3133    newObj.src=imageSrc;
3134    newObj.description=description;
3135    newObj.destinationURL=destinationURL;
3136    newObj.kind=kind;
3137    newObj.albumID=albumID;
3138    if( tags.length == 0 ) {
3139      newObj.tags=null;
3140    }
3141    else {
3142      newObj.tags=tags.split(' ');
3143    }
3144    gI.push(newObj);
3145    return newObj;
3146  }
3147
3148  function GetNGItem( ID ) {
3149    var l=gI.length;
3150    for( var i=0; i<l; i++ ) {
3151      if( gI[i].GetID() == ID ) {
3152        return gI[i];
3153      }
3154    }
3155    return null;
3156  }
3157
3158  
3159  // check album name - blackList/whiteList
3160  function CheckAlbumName( title, ID) {
3161    var s=title.toUpperCase();
3162
3163    if( g_albumList !== null ) {
3164      for( var j=0; j<g_albumList.length; j++) {
3165        if( s == g_albumList[j].toUpperCase() || ID == g_albumList[j] ) {
3166          return true;
3167        }
3168      }
3169    }
3170    else {
3171      var found=false;
3172      if( g_whiteList !== null ) {
3173        //whiteList : authorize only album cointaining one of the specified keyword in the title
3174        for( var j=0; j<g_whiteList.length; j++) {
3175          if( s.indexOf(g_whiteList[j]) !== -1 ) {
3176            found=true;
3177          }
3178        }
3179        if( !found ) { return false; }
3180      }
3181
3182
3183      if( g_blackList !== null ) {
3184        //blackList : ignore album cointaining one of the specified keyword in the title
3185        for( var j=0; j<g_blackList.length; j++) {
3186          if( s.indexOf(g_blackList[j]) !== -1 ) { 
3187            return false;
3188          }
3189        }
3190      }
3191      
3192      return true;
3193    }
3194  }
3195  
3196  // ###########################
3197  // ##### GALLERY TOOLBAR #####
3198  // ###########################
3199
3200  function DisplayAlbum( albumIdx, setLocationHash ) {
3201    if( gO.lazyBuild == 'display' ) {
3202      if( inViewportVert($gE.conTnParent,gO.lazyBuildTreshold) ){
3203        DisplayAlbumFinalize( albumIdx, setLocationHash );
3204      }
3205      else {
3206        g_delayedAlbumIdx=albumIdx;
3207        g_delayedSetLocationHash=setLocationHash;
3208      }
3209    }
3210    else{
3211      DisplayAlbumFinalize( albumIdx, setLocationHash );
3212    }
3213  }
3214  
3215  
3216  function DisplayAlbumFinalize( albumIdx, setLocationHash ) {
3217    gO.lazyBuild='none';
3218    g_delayedAlbumIdx=-1;
3219    g_albumIdxToOpenOnViewerClose=-1;
3220
3221    if( g_containerViewerDisplayed ) {
3222      CloseInternalViewer(false);
3223    }
3224
3225    if( albumIdx == g_lastOpenAlbumID ) {
3226      return;
3227    }
3228    
3229    if( gO.locationHash ) {
3230      if( setLocationHash ) {
3231        var s='nanogallery/'+g_baseEltID+'/'+gI[albumIdx].GetID();
3232        g_lastLocationHash='#'+s;
3233        top.location.hash=s;
3234      }
3235    }
3236    g_lastOpenAlbumID=gI[albumIdx].GetID();
3237    manageGalleryToolbar(albumIdx);
3238   
3239    var p=0;
3240    if( gI[albumIdx].paginationLastPage > 0 && gI[albumIdx].paginationLastWidth == $gE.conTnParent.width()) {
3241      p=gI[albumIdx].paginationLastPage;
3242    }
3243    renderGallery(albumIdx,p);
3244    
3245  }
3246  
3247  
3248  // add album to breadcrumb
3249  function breadcrumbAdd( albumIdx ) {
3250    
3251    var cl="folder";
3252    if(albumIdx == 0 ) {
3253      cl="folderHome";
3254    }
3255    var newDiv =jQuery('<div class="'+cl+' oneFolder">'+gI[albumIdx].title+'</div>').appendTo($gE.conBC);
3256    jQuery(newDiv).data('albumIdx',albumIdx);
3257    newDiv.click(function() {
3258      var cAlbumIdx=jQuery(this).data('albumIdx');
3259      jQuery(this).nextAll().remove();
3260      OpenAlbum(cAlbumIdx, false, -1, true);
3261      return;
3262    });
3263  }
3264
3265  // add separator to breadcrumb
3266  function breadcrumbAddSeparator( lastAlbumID ) {
3267    var newSep=jQuery('<div class="separator'+(gO.RTL ? 'RTL' : '')+'"></div>').appendTo($gE.conBC);
3268    jQuery(newSep).data('albumIdx',lastAlbumID);
3269    newSep.click(function() {
3270      var sepAlbumIdx=jQuery(this).data('albumIdx');
3271      jQuery(this).nextAll().remove();
3272      jQuery(this).remove();
3273      OpenAlbum(sepAlbumIdx, false, -1, true);
3274      return;
3275    });
3276  }
3277
3278  
3279  function manageGalleryToolbar( albumIdx ) {
3280    var displayToolbar=false;
3281  
3282    // Breadcrumb
3283    if( gO.displayBreadcrumb == true ) {
3284      if( $gE.conBC.children().length == 0 ) {
3285          $gE.conNavBCon.css({opacity:0, 'max-height':'0px'});
3286      }
3287
3288      displayToolbar=true;
3289      
3290      // $gE.conBC.children().not(':first').remove();
3291      $gE.conBC.children().remove();
3292      breadcrumbAdd(0);
3293      if( albumIdx != 0 ) {
3294        var l=gI.length,
3295        parentID=0,
3296        lstItems=[];
3297        
3298        lstItems.push(albumIdx);
3299        var curIdx=albumIdx;
3300        
3301        while ( gI[curIdx].albumID != 0 ) {
3302          for(i=1; i < l; i++ ) {
3303            if( gI[i].GetID() == gI[curIdx].albumID ) {
3304              curIdx=i;
3305              lstItems.push(curIdx);
3306              break;
3307            }
3308          }
3309        }
3310        
3311        breadcrumbAddSeparator(0);
3312        for( i=lstItems.length-1; i>=0 ; i-- ) {
3313          breadcrumbAdd(lstItems[i]);
3314          if( i > 0 ) {
3315            breadcrumbAddSeparator(lstItems[i-1]);
3316          }
3317        }
3318      }
3319      
3320      
3321      var bcItems=$gE.conBC.children(),
3322      l1=bcItems.length;
3323      if( l1 == 0 ) {
3324        g_curNavLevel='l1';
3325        if( gO.breadcrumbAutoHideTopLevel ) {
3326          $gE.conNavBCon.css({opacity:0, 'max-height':'0px'});
3327          displayToolbar=false;
3328        }
3329        //breadcrumbAdd(0);
3330      }
3331      else {
3332        if( l1 == 1 ) {
3333          g_curNavLevel='l1';
3334        }
3335        else {
3336          g_curNavLevel='lN';
3337        }
3338        if( l1 == 1 && gO.breadcrumbAutoHideTopLevel ) {
3339          $gE.conNavBCon.animate({'opacity':'0','max-height':'0px'});
3340        }
3341        else {
3342          $gE.conNavBCon.animate({'opacity':'1','max-height':'50px'});
3343        }
3344        //$gE.conBC.children().not(':first').remove();
3345      }
3346
3347      g_pgMaxNbThumbnailsPerRow=NbThumbnailsPerRow();
3348
3349    }
3350    
3351    // Tag-bar
3352    if( gO.useTags ) {
3353      displayToolbar=true;
3354      if( g_containerTags == null ) {
3355        g_containerTags =jQuery('<div class="nanoGalleryTags"></div>').appendTo($gE.conNavB);
3356      }
3357    }
3358    
3359    if( gO.galleryFullpageButton ) { displayToolbar=true; }
3360    
3361    if( !g_containerNavigationbarContDisplayed && displayToolbar ) {
3362      g_containerNavigationbarContDisplayed=true;
3363      $gE.conNavBCon.show();
3364    }
3365
3366    
3367  }
3368
3369  function PreloaderShow() {
3370    //if( gO.displayBreadcrumb == true ) { $gE.conBC.find('.oneFolder').last().addClass('loading'); }
3371    $gE.conLoadingB.css({visibility:'visible'});
3372  }
3373  
3374  function PreloaderHide() {
3375    //if( gO.displayBreadcrumb == true ) { $gE.conBC.find('.oneFolder').last().removeClass('loading'); }
3376    $gE.conLoadingB.css({visibility:'hidden'});
3377  }
3378
3379  
3380  // ##### Open one album
3381  function OpenAlbum ( albumIdx, processLocationHash, imageID, setLocationHash ) {
3382    switch(gO.kind) {
3383      case '':
3384        //renderGallery(albumIdx,0);
3385        DisplayAlbum(albumIdx,setLocationHash);
3386        break;
3387      case 'flickr':
3388        FlickrProcessItems(albumIdx, processLocationHash, imageID, setLocationHash);
3389        break;
3390      case 'json':
3391        CustomProcessItems(albumIdx, processLocationHash, imageID, setLocationHash);
3392        break;
3393      case 'picasa':
3394      default:
3395        PicasaProcessItems(albumIdx, processLocationHash, imageID, setLocationHash);
3396        break;
3397    }
3398  }
3399
3400  // ##### REPOSITION THUMBNAILS ON SCREEN RESIZE EVENT
3401  function ResizeGallery() {
3402    if( SettingsGetTnHeight() == 'auto' ) {
3403      ResizeGalleryHeightAuto();
3404    }
3405    else 
3406      if ( SettingsGetTnWidth() == 'auto' ) {
3407        ResizeGalleryWidthtAuto();
3408      }
3409      else {
3410        ResizeGalleryGrid();
3411      }
3412
3413    thumbnailsLazySetSrc();
3414    setGalleryToolbarWidth(0);
3415  }
3416
3417  // CASCADING LAYOUT
3418  function ResizeGalleryHeightAuto() {
3419    var areaW=$gE.conTnParent.width(),
3420    curCol=0,
3421    curRow=0,
3422    cnt=0,
3423    colHeight=[],
3424    maxCol=NbThumbnailsPerRow(),      //parseInt(areaW/g_tn.defaultFullWidth);
3425    gutterWidth=0,
3426    gutterHeight=gO.thumbnailGutterHeight,
3427    tnW=ThumbnailOuterWidth(),
3428    $thumbnails=$gE.conTn.find('.nanoGalleryThumbnailContainer');
3429
3430    if( gO.thumbnailAlignment == 'justified' ) {
3431      maxCol=Math.min(maxCol,$thumbnails.length);
3432      gutterWidth=(maxCol==1?0:(areaW-(maxCol*tnW))/(maxCol-1));
3433    }
3434    else {
3435      gutterWidth=gO.thumbnailGutterWidth;
3436    }
3437    
3438    var nbCol=0;
3439    // first loop to retrieve the width of the area
3440    $thumbnails.each(function() {
3441      var $this=jQuery(this),
3442      n=$this.data("index");
3443      
3444      if( n !== undefined ) {
3445        var curPosX=0,
3446        curPosY=0;
3447
3448        if( curRow == 0 ) {
3449          curPosX=curCol*(ThumbnailOuterWidth()+gutterWidth);
3450          colHeight[curCol]=gI[n].thumbFullHeight+gutterHeight;
3451          
3452          curCol++;
3453          nbCol++;
3454          if( curCol >= maxCol ) { 
3455            curCol=0;
3456            curRow++;
3457          }
3458        }
3459        else {
3460          return false;
3461        }
3462
3463        cnt++;
3464      }
3465    });
3466    var w=(((colHeight.length)*(tnW+gutterWidth))-gutterWidth);
3467    curRow=0;
3468
3469    // second loop to position the thumbnails
3470    $thumbnails.each(function() {
3471      var $this=jQuery(this),
3472      n=$this.data("index");
3473      
3474      if( n !== undefined ) {
3475        if( gO.thumbnailLabel.position == 'onBottom' ) {
3476          setThumbnailSize($this,gI[n]);      // [TODO] remove --> this should not be here but issue on labelHeight
3477        }
3478        var curPosX=0,
3479        curPosY=0;
3480
3481        if( curRow == 0 ) {
3482          curPosX=curCol*(ThumbnailOuterWidth()+gutterWidth);
3483          colHeight[curCol]=gI[n].thumbFullHeight+gutterHeight;
3484          
3485          curCol++;
3486//          nbCol++;
3487          if( curCol >= maxCol ) { 
3488            curCol=0;
3489            curRow++;
3490          }
3491        }
3492        else {
3493          var c=0,
3494          minColHeight=colHeight[0];
3495          for( i=1; i<maxCol; i++) {
3496            if( (colHeight[i]+5) < minColHeight ) {     // +5 --> threshold
3497              minColHeight=colHeight[i];
3498              c=i;
3499              break;
3500            }
3501          }
3502          curPosY=colHeight[c];
3503          curPosX=c*(ThumbnailOuterWidth()+gutterWidth);
3504          colHeight[c]=curPosY+gI[n].thumbFullHeight+gutterHeight;
3505        }
3506
3507        var x=curPosX;
3508        if( gO.RTL) {
3509          x=w-curPosX-ThumbnailOuterWidth();
3510        }
3511        $this.css({ top: curPosY, left: x });
3512        ThumbnailAppear($this, gI[n], cnt);
3513        
3514        cnt++;
3515      }
3516    });
3517
3518    var h=colHeight[0];
3519    for(i=1;i<nbCol;i++) {
3520      h=Math.max(h, colHeight[i]);
3521    }
3522    $gE.conTn.width(w).height(h);
3523
3524  }
3525
3526  // JUSTIFIED LAYOUT
3527  function ResizeGalleryWidthtAuto() {
3528    var areaW=$gE.conTnParent.width(),
3529    curWidth=0,
3530    lastPosX=0,
3531    curPosY=0,
3532    rowLastItem=[],
3533    rowNum=0,
3534    rowHeight=[],
3535    bNewRow=false,
3536    cnt=0,
3537    tnFeaturedH=0,
3538    tnFeaturedW=0,
3539    tnFeaturedW2=0,
3540    gutterWidth=gO.thumbnailGutterWidth,
3541    gutterHeight=gO.thumbnailGutterHeight;
3542    // by grief-of-these-days
3543    var maxRowHeightVertical=0;     // max height of a row with vertical thumbs
3544    var maxRowHeightHorizontal=0;   // max height of a row with horizontal thumbs
3545    var rowHasVertical=false;       // current row has vertical thumbs
3546    var rowHasHorizontal=false;     // current row has horizontal thumbs
3547    
3548    var $thumbnails=$gE.conTn.find('.nanoGalleryThumbnailContainer');
3549    $thumbnails.each(function() {
3550      var n=jQuery(this).data("index");
3551      if( n !== undefined && gI[n] != undefined ) {
3552        if( gI[n].thumbImg().width > 0 ) {
3553          var item=gI[n],
3554          w=Math.floor(item.thumbImg().width/item.thumbImg().height*SettingsGetTnHeight())+ g_tn.borderWidth+g_tn.imgcBorderWidth; // +gutterWidth;
3555          if( gO.thumbnailFeatured && cnt == 0 ) { 
3556            w=w*2;
3557            tnFeaturedW=w;
3558          }
3559          
3560          if( bNewRow ) {
3561            bNewRow=false;
3562            rowNum++;
3563            curWidth=0;
3564            rowHasVertical=false;
3565            rowHasHorizontal=false;
3566            if( rowNum == 1 && tnFeaturedW > 0 ) {
3567              curWidth=tnFeaturedW;
3568              tnFeaturedW=0;
3569            }
3570          }
3571
3572          // by grief-of-these-days
3573          if( item.thumbImg().height > item.thumbImg().width ) {
3574            rowHasVertical = true;
3575          }
3576          else {
3577            rowHasHorizontal = true;
3578          }
3579          
3580          // down scale image resolution
3581          if( (curWidth + w + gutterWidth) < areaW ) {
3582            // last row
3583            curWidth+=w+gutterWidth;
3584            rowHeight[rowNum]=SettingsGetTnHeight();
3585            // rowHeight[rowNum]=item.thumbFullHeight;
3586            
3587            // prevent incomplete row from being heigher than the previous ones.
3588            // by grief-of-these-days
3589            var rowHeightLimit=Math.max(rowHasVertical ? maxRowHeightVertical : 0, rowHasHorizontal ? maxRowHeightHorizontal : 0);
3590            if( gO.thumbnailAdjustLastRowHeight && rowHeightLimit > 0 ) {
3591              rowHeight[rowNum]=Math.min(rowHeight[rowNum],rowHeightLimit);
3592            }
3593            
3594            rowLastItem[rowNum]=n;
3595          }
3596          else {
3597            // new row after current item
3598            curWidth+=w;
3599            var rH=Math.floor(SettingsGetTnHeight()*areaW/curWidth);
3600            // var rH=Math.floor(item.thumbFullHeight*areaW/curWidth);
3601            rowHeight[rowNum]=rH;
3602            
3603            // save the max row height for each thumb orientation.
3604            // by grief-of-these-days
3605            if( rowHasVertical ) {
3606              maxRowHeightVertical=Math.max(maxRowHeightVertical,rH);
3607            }
3608            if( rowHasHorizontal ) {
3609              maxRowHeightHorizontal=Math.max(maxRowHeightHorizontal,rH);
3610            }
3611            
3612            rowLastItem[rowNum]=n;
3613            bNewRow=true;
3614          }
3615          
3616        cnt++;
3617        }
3618        else {
3619          // exit to conserve display order, and avoid displaying thumbnail before an existing one
3620          return false;
3621        }
3622      }
3623      
3624    });
3625    
3626    rowNum=0;
3627    curPosY=0;
3628    lastPosX=0;
3629    cnt=0;
3630    $thumbnails.each(function() {
3631      var $this=jQuery(this),
3632      n=$this.data("index");
3633      if( n !== undefined && gI[n] != undefined ) {
3634        if( gI[n].thumbImg().width > 0 ) {
3635          var item=gI[n],
3636          // w=Math.ceil(item.thumbImgWidth/item.thumbImg().height*rowHeight[rowNum]);//+g_tn.borderWidth+g_tn.imgContBorderWidth;
3637          w=Math.floor(item.thumbImg().width/item.thumbImg().height*rowHeight[rowNum]);//+g_tn.borderWidth+g_tn.imgContBorderWidth;
3638
3639          if( cnt == 0 && gO.thumbnailFeatured ) { 
3640            w=w*2;
3641            if( rowHeight.length == 1 ) {
3642              // only 1 row
3643              tnFeaturedH=parseInt(rowHeight[0])*2;
3644            }
3645            else {
3646              tnFeaturedH=parseInt(rowHeight[0])+parseInt(rowHeight[1])+g_tn.borderHeight+g_tn.imgcBorderHeight;
3647            }
3648          }
3649          
3650          if( n == rowLastItem[rowNum] ) {
3651            // last row item
3652            if( rowLastItem.length != (rowNum+1) ) {
3653              w=areaW-lastPosX- g_tn.borderWidth-g_tn.imgcBorderWidth;//-gutterWidth;
3654            }
3655            else {
3656              // very last item
3657              if( (lastPosX+w + g_tn.borderWidth+g_tn.imgcBorderWidth +gutterWidth) > areaW ) {
3658                // reduce size
3659                w=areaW-lastPosX-g_tn.borderWidth-g_tn.imgcBorderWidth;//-gutterWidth;
3660              }
3661            }
3662          }
3663          
3664          var rh=0;
3665          if( cnt == 0 && gO.thumbnailFeatured ) {
3666            rh=tnFeaturedH;
3667            tnFeaturedW2=w+ g_tn.borderWidth+g_tn.imgcBorderWidth;
3668            item.customData.featured=true;
3669            $this.find('img').attr('src',item.thumbX2src);
3670          }
3671          else {
3672            rh=rowHeight[rowNum];
3673          }
3674          
3675          rh=parseInt(rh);
3676          w=parseInt(w);
3677          $this.width(w+g_tn.imgcBorderWidth).height(rh+g_tn.imgcBorderHeight+g_tn.labelHeight);
3678          $this.find('.imgContainer').height(rh).width(w);
3679          $this.find('img').css({'max-height':rh+2, 'max-width':w+2});
3680          $this.find('.subcontainer').width(w+g_tn.imgcBorderWidth).height(rh+g_tn.imgcBorderHeight+g_tn.labelHeight);
3681          //$this.find('.labelImage').css({left:0, right:0});
3682          var x=lastPosX;
3683          if( gO.RTL) {
3684            x=areaW-lastPosX-(w+g_tn.borderWidth+g_tn.imgcBorderWidth);
3685          }
3686          $this.css({ top: curPosY , left: x });
3687          item.thumbFullWidth=w+g_tn.borderWidth+g_tn.imgcBorderWidth;
3688          item.thumbFullHeight=rh+g_tn.borderHeight+g_tn.imgcBorderHeight+g_tn.labelHeight;
3689          ThumbnailOverResize($this);
3690          ThumbnailAppear($this, item, cnt);
3691          
3692          lastPosX+=w+g_tn.borderWidth+g_tn.imgcBorderWidth+gutterWidth;
3693          
3694          if( n == rowLastItem[rowNum] ) {
3695            // curPosY+=rowHeight[rowNum]+g_tn.outerHeight+g_tn.labelHeight+gutterHeight;
3696            curPosY+=rowHeight[rowNum]+g_tn.labelHeight+gutterHeight+g_tn.imgcBorderHeight+g_tn.borderHeight;
3697            rowNum++;
3698            lastPosX=0;
3699            if( rowNum == 1 && tnFeaturedW2 > 0 ) { 
3700              lastPosX= tnFeaturedW2;
3701              tnFeaturedW2=0;
3702            }
3703          }
3704          cnt++;
3705        }
3706        else {
3707          return false;
3708        }
3709      }
3710    });
3711    
3712    if( rowNum > 0 ) {
3713      curPosY-=gutterHeight;
3714    }
3715    tnFeaturedH=tnFeaturedH+ThumbnailOuterHeight()+g_tn.labelHeight;
3716    $gE.conTn.width(areaW).height(curPosY>tnFeaturedH?curPosY:tnFeaturedH);  //+gO.thumbnailHeight);
3717  }
3718  
3719  function NbThumbnailsPerRow() {
3720  
3721    var tnW=SettingsGetTnWidth()+g_tn.borderWidth+g_tn.imgcBorderWidth;
3722    var areaW=$gE.conTnParent.width();
3723    
3724    var nbMaxTn=0;
3725    if( gO.thumbnailAlignment == 'justified' ) {
3726      nbMaxTn=Math.floor((areaW)/(tnW));
3727    }
3728    else {
3729      nbMaxTn=Math.floor((areaW+gO.thumbnailGutterWidth)/(tnW+gO.thumbnailGutterWidth));
3730    }
3731    
3732    if(  gO.maxItemsPerLine >0 && nbMaxTn >  gO.maxItemsPerLine ) {
3733      nbMaxTn=gO.maxItemsPerLine;
3734    }
3735    
3736    if( nbMaxTn < 1 ) { nbMaxTn=1; }
3737    
3738    return nbMaxTn
3739  }
3740  
3741  
3742  // GRID LAYOUT
3743  function ResizeGalleryGrid() {
3744    var curPosX=0,
3745    curPosY=0,   
3746    gutterWidth=0,
3747    gutterHeight=gO.thumbnailGutterHeight,
3748    areaW=$gE.conTnParent.width(),
3749    maxCol=NbThumbnailsPerRow(),
3750    cnt=0,
3751    h=0,
3752    w=0,
3753    cols=[],
3754    curCol=0;
3755
3756    g_L.nbMaxTnPerRow=NbThumbnailsPerRow();
3757    
3758    // pagination - max lines per page mode
3759    if( g_pgMaxLinesPerPage > 0 ) {
3760      if( ThumbnailOuterWidth() > 0 ) {
3761        if( maxCol != g_pgMaxNbThumbnailsPerRow ) {   // max number of thumbnails per line has changed --> render the gallery again
3762          g_pgMaxNbThumbnailsPerRow=maxCol;
3763          var aIdx=$gE.conPagin.data('galleryIdx');
3764          renderGallery(aIdx,0);
3765          return;
3766        }
3767      }
3768    }
3769
3770    var $thumbnails=$gE.conTn.find('.nanoGalleryThumbnailContainer'),
3771    nbTn=$thumbnails.length;
3772    
3773    if( gO.thumbnailAlignment == 'justified' ) {
3774      maxCol=Math.min(maxCol,nbTn);
3775      gutterWidth=(maxCol==1?0:(areaW-(maxCol*ThumbnailOuterWidth()))/(maxCol-1));
3776    }
3777    else {
3778      gutterWidth=gO.thumbnailGutterWidth;
3779    }
3780
3781    if( gO.RTL ) {
3782      // first loop to retrieve the width of the area
3783      $thumbnails.each(function() {
3784        var $this=jQuery(this);
3785
3786        var n=$this.data("index");
3787        if( n !== undefined ) {
3788          if( curPosY == 0 ) {
3789            curPosX=curCol*(ThumbnailOuterWidth()+gutterWidth);
3790            cols[curCol]=curPosX;
3791            w=curPosX;
3792          }
3793          else {
3794            return false;
3795          }
3796          
3797          curCol++;
3798          if( curCol >= maxCol ){
3799            curCol=0;
3800            curPosY+=ThumbnailOuterHeight()+gutterHeight;
3801          }
3802        }
3803      });
3804      areaW=w+ThumbnailOuterWidth();
3805      curPosY=0;
3806      curCol=0;
3807    }
3808    
3809    var lastN=0;
3810    $thumbnails.each(function() {
3811      var $this=jQuery(this);
3812
3813      var n=$this.data("index");
3814      if( n !== undefined ) {
3815        lastN=n;
3816        if( curPosY == 0 ) {
3817          curPosX=curCol*(ThumbnailOuterWidth()+gutterWidth)
3818          cols[curCol]=curPosX;
3819          w=curPosX;
3820        }
3821        else {
3822          curPosX=cols[curCol];
3823          h=curPosY;
3824        }
3825        
3826        var x=curPosX;
3827        if( gO.RTL ) {
3828          x=parseInt(areaW)-curPosX-ThumbnailOuterWidth();
3829        }
3830        $this.css({ top: curPosY , left: x });
3831        ThumbnailAppear($this, gI[n], cnt);
3832
3833        curCol++;
3834        if( curCol >= maxCol ){
3835          curCol=0;
3836          curPosY+=ThumbnailOuterHeight()+gutterHeight;
3837        }
3838        cnt++;
3839      }
3840    });
3841
3842    $gE.conTn.width(w+ThumbnailOuterWidth()).height(h+ThumbnailOuterHeight());
3843
3844  }
3845  
3846  function ThumbnailAppear($this, item, n) {
3847    if( $this.css('opacity') == 0 ) {
3848      $this.removeClass('nanogalleryHideElement');
3849      if( gO.thumbnailDisplayTransition ) {
3850        if( typeof gO.fnThumbnailDisplayEffect == 'function' ) { 
3851          gO.fnThumbnailDisplayEffect($this, item, 0);
3852        }
3853        else {
3854          $this.delay(n*g_tn.displayInterval).fadeTo(150, 1);
3855        }
3856      }
3857      else {
3858        $this.css({opacity:1});
3859      }
3860    }
3861  }
3862  
3863
3864  function setGalleryToolbarWidth(pageNumber) {
3865    if( gO.galleryToolbarWidthAligned ) {
3866      if( $gE.conNavBCon !== undefined ) {
3867        var w=$gE.conTn.outerWidth(true);
3868        //if( pageNumber > 0 ) {
3869          if( $gE.conNavBCon.width() < w ) {
3870            $gE.conNavBCon.width(w);
3871          }
3872        //}
3873        else {
3874          $gE.conNavBCon.width(w);
3875        }
3876      }
3877    }
3878  }
3879
3880  // thumbnail image lazy load
3881  function thumbnailsLazySetSrc() {
3882    var $eltInViewport=$gE.conTn.find('.nanoGalleryThumbnailContainer').filter(function() {
3883       return inViewport(jQuery(this), g_tn.lazyLoadTreshold);
3884    });
3885
3886    jQuery($eltInViewport).each(function(){
3887        var $image=jQuery(this).find('img');
3888        if( jQuery($image).attr('src') == g_emptyGif ) {
3889          var idx=jQuery(this).data('index');
3890          if( idx == undefined || gI[idx] == undefined ) { return; }
3891          // jQuery($image).attr('src',gI[idx].thumbsrc);
3892          jQuery($image).attr('src','');
3893          jQuery($image).attr('src',gI[idx].thumbImg().src);
3894        }
3895    });
3896  }
3897
3898  
3899  // ###################
3900  // ##### GALLERY #####
3901  // ###################
3902
3903  
3904  // Display pagination
3905  function managePagination( albumIdx, pageNumber ) {
3906    if( $gE.conPagin == undefined ) return;
3907    
3908    $gE.conPagin.children().remove();
3909
3910    //if( SettingsGetTnHeight() == 'auto' || SettingsGetTnWidth() == 'auto' ) { return; }
3911    if( g_pgMaxLinesPerPage == 0 || SettingsGetTnHeight() == 'auto' || SettingsGetTnWidth() == 'auto' ) {
3912      // Hide pagination container, if not used.
3913      $gE.conPagin.hide();
3914      return;
3915    }    
3916    
3917    // Must show the container for width calculation to work.
3918    $gE.conPagin.show();
3919    
3920    $gE.conPagin.data('galleryIdx',albumIdx);
3921    $gE.conPagin.data('currentPageNumber',pageNumber);
3922    var n2=0,
3923    w=0;
3924    if( pageNumber > 0 ) {
3925      var eltPrev=jQuery('<div class="paginationPrev">'+g_i18nTranslations.paginationPrevious+'</div>').appendTo($gE.conPagin);
3926      w+=jQuery(eltPrev).outerWidth(true);
3927      eltPrev.click(function(e) {
3928        paginationPreviousPage();
3929      });
3930    }
3931
3932    var firstPage=0;
3933    // pagination - max lines per page mode
3934    if( g_pgMaxLinesPerPage > 0 && SettingsGetTnHeight() != 'auto' && SettingsGetTnWidth() != 'auto' ) {
3935      n2=Math.ceil(gI[albumIdx].contentLength/(g_pgMaxLinesPerPage*g_pgMaxNbThumbnailsPerRow));
3936    }
3937
3938    if( pageNumber >= 5 ) {
3939      firstPage=pageNumber-5;
3940      if( n2 > pageNumber+6 ) {
3941        n2=pageNumber+6;
3942      }
3943    }
3944    else {
3945      if( n2 > 10 ) {
3946        n2=10;
3947      }
3948    }
3949    
3950    // only one page -> do not display anything
3951    // if( n2==1 ) { return; }
3952    if( n2==1 ) {
3953      // Hide pagination container, if not used.
3954      $gE.conPagin.hide ();
3955      return;
3956    }
3957    
3958    for(var i=firstPage; i < n2; i++ ) {
3959      var c='';
3960      if( i == pageNumber ) { c=' currentPage'; }
3961      var elt$=jQuery('<div class="paginationItem'+c+'">'+(i+1)+'</div>').appendTo($gE.conPagin);
3962      elt$.data('pageNumber',i);
3963      w+=elt$.outerWidth(true);
3964      elt$.click(function(e) {
3965        var aIdx=$gE.conPagin.data('galleryIdx'),
3966        pn=jQuery(this).data('pageNumber');
3967        if( !inViewportVert($gE.base, 0) ) {
3968          $('html, body').animate({scrollTop: $gE.base.offset().top}, 200);
3969        }
3970        renderGallery(aIdx,pn);
3971      });
3972
3973    }
3974
3975    if( (pageNumber+1) < n2 ) {
3976      var $eltNext=jQuery('<div class="paginationNext">'+g_i18nTranslations.paginationNext+'</div>').appendTo($gE.conPagin);
3977      w+=$eltNext.outerWidth(true);
3978      $eltNext.click(function(e) {
3979        paginationNextPage();
3980      });
3981    }
3982
3983    $gE.conPagin.width(w);
3984
3985  }
3986
3987  function paginationNextPage() {
3988    var aIdx=$gE.conPagin.data('galleryIdx'),
3989    n1=0;
3990    
3991    // pagination - max lines per page mode
3992    if( g_pgMaxLinesPerPage > 0 ) {
3993      n1=gI[aIdx].contentLength/(g_pgMaxLinesPerPage*g_pgMaxNbThumbnailsPerRow);
3994    }
3995    n2=Math.ceil(n1);
3996    
3997    var pn=$gE.conPagin.data('currentPageNumber');
3998    if( pn < (n2-1) ) {
3999      pn++;
4000    }
4001    else {
4002      pn=0;
4003    }
4004    
4005    if( !inViewportVert($gE.base, 0) ) {
4006      $('html, body').animate({scrollTop: $gE.base.offset().top }, 200);
4007    }
4008
4009    renderGallery(aIdx,pn);
4010  }
4011  
4012  function paginationPreviousPage() {
4013    var aIdx=$gE.conPagin.data('galleryIdx'),
4014    n1=0;
4015    
4016    // pagination - max lines per page mode
4017    if( g_pgMaxLinesPerPage > 0 ) {
4018      n1=gI[aIdx].contentLength/(g_pgMaxLinesPerPage*g_pgMaxNbThumbnailsPerRow);
4019    }
4020    n2=Math.ceil(n1);
4021    
4022    var pn=$gE.conPagin.data('currentPageNumber');
4023    if( pn > 0 ) {
4024      pn--;
4025    }
4026    else {
4027      pn=n2-1;
4028    }
4029
4030    if( !inViewportVert($gE.base, 0) ) {
4031      $('html, body').animate({scrollTop: $gE.base.offset().top }, 250);
4032    }
4033
4034    renderGallery(aIdx,pn);
4035  }
4036
4037  function renderGallery( albumIdx, pageNumber ) {
4038    g_curAlbumIdx=-1;
4039    var $elt=$gE.conTn.parent();
4040    $elt.animate({opacity: 0}, 100).promise().done(function(){
4041
4042      // remove gallery elements
4043      g_containerThumbnailsDisplayed=false;
4044//      $gE.conTn.off().empty();
4045      $gE.conTn.hide(0).off().show(0).html('');
4046      var l=gI.length;
4047      for( var i=0; i < l ; i++ ) {
4048        gI[i].hovered=false;
4049      }
4050
4051      $gE.conTnParent.css({ left:0, opacity:1 });
4052      ElementTranslateX($gE.conTn[0],0);
4053      renderGallery2(albumIdx, pageNumber, renderGallery2Complete);
4054    });
4055  }
4056
4057
4058  function renderGallery2( albumIdx, pageNumber, onComplete ) {
4059    if( albumIdx == -1 || gI[albumIdx] == undefined) { return; }
4060
4061    gI[albumIdx].paginationLastPage=pageNumber;
4062    gI[albumIdx].paginationLastWidth=$gE.conTnParent.width();
4063
4064
4065    var l=gI.length;
4066    // if one description is defined then put a value to those without
4067    var foundDesc=false;
4068    /*if( gO.thumbnailLabel.position == 'onBottom'  ) {
4069      for(var i=0; i<l; i++ ) {
4070        if( gI[i].albumID == gI[albumIdx].albumID && gI[i].description.length > 0 ) { foundDesc=true; }
4071      }
4072    }*/
4073    var g_galleryItemsCount=0,
4074    currentCounter=0,
4075    firstCounter=0,
4076    lastCounter=0;
4077    
4078    if( g_pgMaxLinesPerPage > 0 && SettingsGetTnHeight() != 'auto' && SettingsGetTnWidth() != 'auto' ) {
4079      firstCounter=pageNumber*g_pgMaxLinesPerPage*g_pgMaxNbThumbnailsPerRow;
4080      lastCounter=firstCounter+g_pgMaxLinesPerPage*g_pgMaxNbThumbnailsPerRow;
4081    }
4082
4083    PreloaderHide();
4084
4085    var endInViewportTest=false,
4086    startInViewportTest=false,
4087    idx=0;
4088    (function(){
4089
4090      for( var i=0; i<gO.galleryRenderStep; i++ ) {
4091        if( idx >= l ) {
4092          onComplete(albumIdx, pageNumber);
4093          return;
4094        }
4095
4096        var item=gI[idx];
4097        if( item.albumID == gI[albumIdx].GetID() ) {
4098          currentCounter++;
4099
4100          // pagination - max lines per page mode
4101          if( g_pgMaxLinesPerPage > 0 && SettingsGetTnHeight() != 'auto' && SettingsGetTnWidth() != 'auto' ) {
4102            if( (g_galleryItemsCount+1) > (g_pgMaxLinesPerPage*g_pgMaxNbThumbnailsPerRow) ) {
4103              onComplete(albumIdx, pageNumber);
4104              return;
4105            }
4106          }
4107          
4108          if( currentCounter > firstCounter ) {
4109            g_galleryItemsCount++;
4110            
4111            var r=thumbnailBuild(item, idx, foundDesc);
4112            var $newDiv=r.e$;
4113
4114            // image lazy load
4115            // if( gO.thumbnailLazyLoad  ) {
4116            if( gO.thumbnailLazyLoad && !r.cIS ) {
4117              if( !endInViewportTest ) {
4118                if( inViewport($newDiv, g_tn.lazyLoadTreshold) ) {
4119                  $newDiv.find('img').attr('src','');   // firefox bug workaround
4120                  $newDiv.find('img').attr('src',item.thumbImg().src);
4121                  startInViewportTest=true;
4122                }
4123                else {
4124                  if( startInViewportTest ) { endInViewportTest=true; }
4125                }
4126              }
4127            }
4128          }
4129        }
4130        idx++;
4131      }
4132
4133      if( idx < l ) {
4134        //setGalleryToolbarWidth(pageNumber);
4135        setTimeout(arguments.callee,2);
4136      }
4137      else {
4138        onComplete(albumIdx, pageNumber);
4139      }
4140    })();
4141  }
4142  
4143  function renderGallery2Complete( albumIdx, pageNumber ) {
4144  
4145    //if( gO.thumbnailHeight == 'auto' || gO.thumbnailWidth == 'auto' || gO.thumbnailWidth == 'autoUpScale' ) {
4146      ResizeGallery();
4147    //}
4148     
4149    // SetGalleryWidth(pageNumber);
4150    //setGalleryToolbarWidth(pageNumber);
4151    managePagination(albumIdx,pageNumber);
4152    g_containerThumbnailsDisplayed=true;
4153    g_curAlbumIdx=albumIdx;
4154
4155  }
4156
4157
4158  function thumbnailBuild( item, idx, foundDesc ) {
4159    var newElt=[],
4160    newEltIdx=0;
4161    
4162    var pos='';
4163    var ch=' nanogalleryHideElement'
4164    if( gO.thumbnailLazyLoad && SettingsGetTnWidth() == 'auto' ) {
4165      pos='top:0px;left:0px;';
4166      ch='';
4167    }
4168    // newElt[newEltIdx++]='<div class="nanoGalleryThumbnailContainer nanogalleryHideElement" style="display:inline-block,opacity:'+eltOpacity+'" ><div class="subcontainer" style="display: inline-block">';
4169    newElt[newEltIdx++]='<div class="nanoGalleryThumbnailContainer'+ch+'" style="display:block;opacity:0;'+pos+'" ><div class="subcontainer" style="display:block;">';
4170    
4171    var checkImageSize=false,
4172    src=g_emptyGif;
4173    if( (SettingsGetTnHeight() == 'auto' && gI[idx].thumbImg().height == 0) || (SettingsGetTnWidth() == 'auto' && gI[idx].thumbImg().width == 0) ) { checkImageSize=true; }
4174    if( !gO.thumbnailLazyLoad || checkImageSize) {
4175      src=item.thumbImg().src;
4176    }
4177
4178    var sTitle=getThumbnailTitle(item),
4179    sDesc=getTumbnailDescription(item);
4180    
4181    if( SettingsGetTnHeight() == 'auto' ) {
4182      newElt[newEltIdx++]='<div class="imgContainer" style="width:'+SettingsGetTnWidth()+'px;"><img class="image" src="'+src+'" alt=" " style="max-width:'+SettingsGetTnWidth()+'px;"></div>';
4183    }
4184    else if( SettingsGetTnWidth() == 'auto' ) {
4185        newElt[newEltIdx++]='<div class="imgContainer" style="height:'+SettingsGetTnHeight()+'px;"><img class="image" src="'+src+'" alt=" " style="max-height:'+SettingsGetTnHeight()+'px;" ></div>';
4186      }
4187      else {
4188        newElt[newEltIdx++]='<div class="imgContainer" style="width:'+SettingsGetTnWidth()+'px;height:'+SettingsGetTnHeight()+'px;"><img class="image" src="'+src+'" alt=" " style="max-width:'+SettingsGetTnWidth()+'px;max-height:'+SettingsGetTnHeight()+'px;" ></div>';
4189      }
4190
4191    if( item.kind == 'album' ) {
4192      // ALBUM
4193      if( gO.thumbnailLabel.display == true ) {
4194        var imageCount='';
4195        if( item.contentLength > 0 ) {
4196          // display content counter
4197          switch( gO.thumbnailLabel.itemsCount) {
4198            case 'title':
4199              //sTitle += ' <span>' + g_i18nTranslations.thumbnailLabelItemsCountPart1 + item.contentLength + g_i18nTranslations.thumbnailLabelItemsCountPart2 + '</span>';
4200              sTitle +=  ' ' + g_i18nTranslations.thumbnailLabelItemsCountPart1 + '<span>' +item.contentLength + '</span>' +g_i18nTranslations.thumbnailLabelItemsCountPart2;
4201              break;
4202            case 'description':
4203              sDesc += ' ' + g_i18nTranslations.thumbnailLabelItemsCountPart1 + '<span>' + item.contentLength + '</span>' + g_i18nTranslations.thumbnailLabelItemsCountPart2;
4204              break;
4205            }
4206          }
4207        //newElt[newEltIdx++]='<div class="labelImage" style="width:'+gO.thumbnailWidth+'px;max-height:'+gO.thumbnailHeight+'px;"><div class="labelFolderTitle labelTitle" >'+sTitle+'</div><div class="labelDescription" >'+sDesc+'</div></div>';
4208        newElt[newEltIdx++]='<div class="labelImage" style="width:'+SettingsGetTnWidth()+'px;'+(gO.RTL ? "direction:RTL;" :"")+'"><div class="labelFolderTitle labelTitle" >'+sTitle+'</div><div class="labelDescription" >'+sDesc+'</div></div>';
4209      }
4210    }
4211    else {
4212      // IMAGE
4213      if( gO.thumbnailLabel.display == true ) {
4214        if( foundDesc && sDesc.length == 0 && gO.thumbnailLabel.position == 'onBottom' ) { sDesc='&nbsp;'; }
4215        //newElt[newEltIdx++]='<div class="labelImage" style="width:'+gO.thumbnailWidth+'px;max-height:'+gO.thumbnailHeight+'px;"><div class="labelImageTitle labelTitle" >'+sTitle+'</div><div class="labelDescription" >'+sDesc+'</div></div>';
4216        newElt[newEltIdx++]='<div class="labelImage" style="width:'+SettingsGetTnWidth()+'px;'+(gO.RTL ? "direction:RTL;" :"")+'"><div class="labelImageTitle labelTitle" >'+sTitle+'</div><div class="labelDescription" >'+sDesc+'</div></div>';
4217      }
4218    }
4219    newElt[newEltIdx++]='</div></div>';
4220    
4221    var $newDiv =jQuery(newElt.join('')).appendTo($gE.conTnHid); //.animate({ opacity: 1},1000, 'swing');  //.show('slow'); //.fadeIn('slow').slideDown('slow');
4222    item.$elt=$newDiv;
4223    $newDiv.data('index',idx);
4224    $newDiv.find('img').data('index',idx);
4225
4226    thumbnailPositionContent($newDiv, item);
4227    var $p=$newDiv.detach();
4228    $p.appendTo( $gE.conTn );
4229
4230    ThumbnailInit($newDiv);
4231
4232    if( checkImageSize ) {
4233      var gi_imgLoad = ngimagesLoaded( $newDiv );
4234      gi_imgLoad.on( 'always', function( instance ) {
4235      //$newDiv.ngimagesLoaded().always( function( instance ) {
4236        var item=gI[jQuery(instance.images[0].img).data('index')];
4237        // if( item == undefined || jQuery(instance.images[0].img).attr('src') == g_emptyGif ) { return; }    // also fired for blank image --> ignore
4238        if( item == undefined || instance.images[0].img.src == g_emptyGif ) { return; }    // also fired for blank image --> ignore
4239        var b=false;
4240        if( item.thumbImg().height != instance.images[0].img.naturalHeight ) {
4241          item.thumbSetImgHeight(instance.images[0].img.naturalHeight);
4242          item.thumbSetImgWidth(instance.images[0].img.naturalWi
4242dth);
4243          b=true;
4244        }
4245        if( item.thumbImg().width != instance.images[0].img.naturalWidth ) {
4246          item.thumbSetImgHeight(instance.images[0].img.naturalHeight);
4247          item.thumbSetImgWidth(instance.images[0].img.naturalWidth);
4248          b=true;
4249        }
4250        
4251        if( b ) {
4252          setThumbnailSize(item.$elt, item);
4253          ThumbnailOverResize(item.$elt);
4254          ResizeGallery();      // [TODO] optimization to avoid too much resizing
4255        }
4256      });
4257    }
4258    else {
4259      setThumbnailSize($newDiv, item);
4260      ThumbnailOverResize($newDiv);
4261    }
4262
4263    
4264    // thumbnailPositionContent($newDiv, item);
4265
4266    return { e$:$newDiv, cIS:checkImageSize };
4267  }
4268  
4269  function getThumbnailTitle( item ) {
4270    var sTitle=item.title;
4271    if( gO.thumbnailLabel.display == true ) {
4272      if( sTitle === undefined || sTitle.length == 0 ) { sTitle='&nbsp;'; }
4273
4274      if( g_i18nTranslations.thumbnailImageTitle != '' ) {
4275        sTitle=g_i18nTranslations.thumbnailImageTitle;
4276      }
4277      if( gO.thumbnailLabel.titleMaxLength > 3 && sTitle.length > gO.thumbnailLabel.titleMaxLength ){
4278        sTitle=sTitle.substring(0,gO.thumbnailLabel.titleMaxLength)+'...';
4279      }
4280    }
4281    
4282    return sTitle;
4283  }
4284
4285  function getTumbnailDescription( item ) {
4286    var sDesc='';
4287    if( gO.thumbnailLabel.displayDescription == true ) { 
4288      if( item.kind == 'album' ) {
4289        if( g_i18nTranslations.thumbnailImageDescription != '' ) {
4290          sDesc=g_i18nTranslations.thumbnailAlbumDescription;
4291        }
4292        else {
4293          sDesc=item.description;
4294        }
4295      }
4296      else {
4297        if( g_i18nTranslations.thumbnailImageDescription != '' ) {
4298          sDesc=g_i18nTranslations.thumbnailImageDescription;
4299        }
4300        else {
4301          sDesc=item.description;
4302        }
4303      }
4304      if( gO.thumbnailLabel.descriptionMaxLength > 3 && sDesc.length > gO.thumbnailLabel.descriptionMaxLength ){
4305        sDesc=sDesc.substring(0,gO.thumbnailLabel.descriptionMaxLength)+'...';
4306      }
4307    }
4308    
4309    return sDesc;
4310  }
4311  
4312  
4313  function setThumbnailSize( $elt, item ) {
4314
4315    if( SettingsGetTnHeight() == 'auto' ) {
4316      // CASCADING LAYOUT
4317      if( item.thumbImg().height > 0 ) {
4318        var ratio=item.thumbImg().height/item.thumbImg().width;
4319        $elt.find('.imgContainer').height(SettingsGetTnWidth()*ratio); //.css({'overflow':'hidden'});
4320        if( gO.thumbnailLabel.position == 'onBottom' ) {
4321          item.thumbLabelHeight=$elt.find('.labelImage').outerHeight(true);
4322          item.thumbFullHeight=SettingsGetTnWidth()*ratio + item.thumbLabelHeight + g_tn.borderHeight+g_tn.imgcBorderHeight;
4323          $elt.width(ThumbnailOuterWidth()-g_tn.borderWidth).height(item.thumbFullHeight-g_tn.borderHeight);
4324          $elt.find('.labelImage').css({'position':'absolute', 'top':'', 'bottom':'0px'});
4325        }
4326        else {
4327          item.thumbFullHeight=SettingsGetTnWidth()*ratio + item.thumbLabelHeight + g_tn.borderHeight+g_tn.imgcBorderHeight;
4328          $elt.width(ThumbnailOuterWidth() - g_tn.borderWidth).height(item.thumbFullHeight-g_tn.borderHeight);
4329        }
4330      }
4331      item.thumbFullWidth=ThumbnailOuterWidth();
4332      $elt.find('.subcontainer').width(ThumbnailOuterWidth()-g_tn.borderWidth).height(item.thumbFullHeight-g_tn.borderHeight); //.css({'overflow':'hidden'});
4333    }
4334    else
4335
4336      // JUSTIFIED LAYOUT
4337      if( SettingsGetTnWidth() == 'auto' ) {
4338        return;
4339        
4340        // everything is done in ResizeGalleryWidthtAuto()
4341        if( item.thumbImg().width > 0 ) {
4342          // var ratio=item.thumbImg().height/item.thumbImg().width;
4343          var ratio=item.thumbImg().width/item.thumbImg().height;
4344          $elt.find('.imgContainer').width(SettingsGetTnHeight()*ratio).css({overflow:'hidden'});
4345          if( gO.thumbnailLabel.position == 'onBottom' ) {
4346            item.thumbFullWidth=SettingsGetTnHeight()*ratio + g_tn.borderWidth+g_tn.imgcBorderWidth ;
4347            $elt.width(item.thumbFullWidth).height(ThumbnailOuterHeight()+g_tn.labelHeight-ThumbnailOuterHeight());
4348          }
4349          else {
4350          }
4351        }
4352        item.thumbFullHeight=ThumbnailOuterHeight()+g_tn.labelHeight;
4353        $elt.find('.subcontainer').width(item.thumbFullWidth-g_tn.borderWidth).height(ThumbnailOuterHeight()-g_tn.borderHeight); //.css({'overflow':'hidden'});
4354      }
4355      
4356      else {
4357        // GRID LAYOUT
4358        item.thumbFullHeight=ThumbnailOuterHeight();  //+g_tn.labelHeight;  //g_tn.defaultFullHeight;
4359        item.thumbFullWidth=ThumbnailOuterWidth();   //g_tn.defaultFullWidth;
4360        if( gO.thumbnailLabel.position == 'onBottom' ) {
4361          $elt.width(item.thumbFullWidth-g_tn.borderWidth).height(item.thumbFullHeight-g_tn.borderHeight);
4362          //$elt.find('.labelImage').height(g_tn.labelHeight-g_tn.labelBorderHeight).css({overflow:'hidden'});
4363        }
4364        else {
4365          $elt.width(item.thumbFullWidth-g_tn.borderWidth).height(item.thumbFullHeight-g_tn.borderHeight);
4366        }
4367        $elt.find('.subcontainer').width(item.thumbFullWidth-g_tn.borderWidth).height(item.thumbFullHeight-g_tn.borderHeight); //.css({'overflow':'hidden'});
4368      }
4369  }
4370  
4371  
4372  function thumbnailPositionContent( $e, item ) {
4373  
4374    if( typeof gO.fnThumbnailInit == 'function' ) { 
4375      gO.fnThumbnailInit($e, item, ExposedObjects());
4376      return;
4377    }    
4378    
4379    if( !gO.thumbnailLabel.display ) { return; }
4380
4381    switch( gO.thumbnailLabel.position ){
4382      case 'onBottom':
4383        // $e.find('.labelImage').css({'top':'0', 'position':'relative', 'width':'100%'});
4384        $e.find('.labelImage').css({top:0, position:'relative', left:0, right:0});
4385        if( SettingsGetTnHeight() == 'auto' ) {
4386          $e.find('.labelImageTitle').css({'white-space':'normal'});    // line break
4387          $e.find('.labelFolderTitle').css({'white-space':'normal'});
4388          $e.find('.labelDescription').css({'white-space':'normal'});
4389        }
4390        else {
4391          $e.find('.labelImageTitle').css({'white-space':'nowrap'});    // no line break
4392          $e.find('.labelFolderTitle').css({'white-space':'nowrap'});
4393          $e.find('.labelDescription').css({'white-space':'nowrap'});
4394        }
4395        break;
4396      case 'overImageOnTop':
4397        // $e.find('.labelImage').css({'top':'0', 'height':'100%', 'width':'100%'});
4398        // $e.find('.labelImage').css({top:-g_tn.imgcBorderHeight/2, bottom:g_tn.imgcBorderWidth/2, left:0, right:0 });
4399        $e.find('.labelImage').css({top:0, bottom:0, left:0, right:0 });
4400        break;
4401      case 'overImageOnMiddle':
4402        // $e.find('.labelImage').css({'top':'0', 'height':'100%', 'width':'100%'});
4403        // $e.find('.labelImage').css({top:-g_tn.imgcBorderHeight/2, bottom:g_tn.imgcBorderWidth/2, left:0, right:0});
4404        $e.find('.labelImage').css({top:0, bottom:0, left:0, right:0});
4405        $e.find('.labelFolderTitle').css({left:0, right:0, position:'absolute', bottom:'50%'});
4406        $e.find('.labelImageTitle').css({left:0, right:0, position:'absolute', bottom:'50%'});
4407        $e.find('.labelDescription').css({left:0, right:0, position:'absolute', top:'50%'});
4408        break;
4409      case 'overImageOnBottom':
4410      default :
4411        gO.thumbnailLabel.position='overImageOnBottom';
4412        // $e.find('.labelImage').css({'bottom':'0', 'width':'100%'});
4413        // $e.find('.labelImage').css({bottom:g_tn.imgcBorderWidth/2, left:0, right:0});
4414        $e.find('.labelImage').css({bottom:0, left:0, right:0});
4415        break;
4416    }
4417  }
4418  
4419//  function ThumbnailClick( $e ) {
4420//    $currentTouchedThumbnail=null;
4421//    var n=$e.data('index');
4422//    if( n == undefined ) { return; }
4423//    
4424//    if( gO.touchAnimation == false || gI[n].hovered === true ) {
4425//      OpenThumbnail(n);
4426//    }
4427//    else {
4428//      // hover effect on click --> touchscreen
4429//      ThumbnailHoverOutAll();
4430//      ThumbnailHover($e);
4431//    }
4432//  }
4433  
4434  
4435  function ThumbnailHoverOutAll() {
4436  // [TODO] --> only check displayed items
4437    var l=gI.length;
4438    for( var i=0; i < l ; i++ ) {
4439      if( gI[i].hovered ) {
4440        ThumbnailHoverOut(gI[i].$elt);
4441      }
4442    }
4443  }
4444  
4445  function ThumbnailInit( $e ) {
4446    var n=$e.data("index");
4447    if( n == undefined ) { return; }    // required because can be fired on ghost elements
4448    var item=gI[n];
4449    if( typeof gO.fnThumbnailHoverInit == 'function' ) {
4450      gO.fnThumbnailHoverInit($e, item, ExposedObjects() );
4451    }
4452    removeCSSTransform(item);
4453    
4454    for( j=0; j<g_tnHE.length; j++) {
4455      switch( g_tnHE[j].name ) {
4456
4457        case 'imageSplit4':
4458          var $subCon=$e.find('.subcontainer'),
4459          $lI=$e.find('.labelImage'),
4460          $iC=$e.find('.imgContainer');
4461          $e.find('.imgContainer').css({position:'absolute'});
4462          $subCon.css({overflow:'hidden', position:'relative', width:'100%', height:'100%'});
4463          $subCon.prepend($iC.clone());
4464          $subCon.prepend($e.find('.imgContainer').clone());
4465          $iC=$e.find('.imgContainer');
4466          setElementOnTop('', $iC);
4467          
4468          newCSSTransform(item, 'imgContainer0', $iC.eq(0));
4469          SetCSSTransform(item, 'imgContainer0');
4470          newCSSTransform(item, 'imgContainer1', $iC.eq(1));
4471          SetCSSTransform(item, 'imgContainer1');
4472          newCSSTransform(item, 'imgContainer2', $iC.eq(2));
4473          SetCSSTransform(item, 'imgContainer2');
4474          newCSSTransform(item, 'imgContainer3', $iC.eq(3));
4475          SetCSSTransform(item, 'imgContainer3');
4476          break;
4477          
4478        case 'imageSplitVert':
4479          var $subCon=$e.find('.subcontainer'),
4480          $iC=$e.find('.imgContainer');
4481          $iC.css({position:'absolute'});
4482          $subCon.css({overflow:'hidden', position:'relative'});  //, width:'100%', height:'100%'});
4483          $subCon.prepend($iC.clone());
4484          $iC=$e.find('.imgContainer');
4485          setElementOnTop('', $iC);
4486
4487          newCSSTransform(item, 'imgContainer0', $iC.eq(0));
4488          SetCSSTransform(item, 'imgContainer0');
4489          newCSSTransform(item, 'imgContainer1', $iC.eq(1));
4490          SetCSSTransform(item, 'imgContainer1');
4491          break;
4492          
4493        case 'labelSplit4':
4494          var $subCon=$e.find('.subcontainer'),
4495          $lI=$e.find('.labelImage').css({top:0, bottom:0});
4496          $subCon.css({overflow:'hidden', position:'relative'});
4497          $lI.clone().appendTo($subCon);
4498          $e.find('.labelImage').clone().appendTo($subCon);
4499          $lI=$e.find('.labelImage');
4500          
4501          newCSSTransform(item, 'labelImage0', $lI.eq(0));
4502          SetCSSTransform(item, 'labelImage0');
4503          newCSSTransform(item, 'labelImage1', $lI.eq(1));
4504          SetCSSTransform(item, 'labelImage1');
4505          newCSSTransform(item, 'labelImage2', $lI.eq(2));
4506          SetCSSTransform(item, 'labelImage2');
4507          newCSSTransform(item, 'labelImage3', $lI.eq(3));
4508          SetCSSTransform(item, 'labelImage3');
4509
4510          break;
4511          
4512        case 'labelSplitVert':
4513          var $subCon=$e.find('.subcontainer'),
4514          $lI=$e.find('.labelImage');
4515          $subCon.css({overflow:'hidden', position:'relative'});
4516          $lI.clone().appendTo($subCon);
4517          $lI=$e.find('.labelImage');
4518          
4519          newCSSTransform(item, 'labelImage0', $lI.eq(0));
4520          SetCSSTransform(item, 'labelImage0');
4521          newCSSTransform(item, 'labelImage1', $lI.eq(1));
4522          SetCSSTransform(item, 'labelImage1');
4523          break;
4524
4525        case 'labelAppearSplit4':
4526          var $subCon=$e.find('.subcontainer');
4527          $lI=$e.find('.labelImage'),
4528          $lI.css({left:0, top:0, right:0, bottom:0});
4529          $subCon.css({overflow:'hidden', position:'relative'});
4530          $lI.clone().appendTo($subCon);
4531          $e.find('.labelImage').clone().appendTo($subCon);
4532          $lI=$e.find('.labelImage');
4533
4534          var o=newCSSTransform(item, 'labelImage0', $lI.eq(0));
4535          o.translateX=-item.thumbFullWidth/2;
4536          o.translateY=-item.thumbFullHeight/2;
4537          SetCSSTransform(item, 'labelImage0');
4538          o=newCSSTransform(item, 'labelImage1', $lI.eq(1));
4539          o.translateX=item.thumbFullWidth/2;
4540          o.translateY=-item.thumbFullHeight/2;
4541          SetCSSTransform(item, 'labelImage1');
4542          o=newCSSTransform(item, 'labelImage2', $lI.eq(2));
4543          o.translateX=item.thumbFullWidth/2;
4544          o.translateY=item.thumbFullHeight/2;
4545          SetCSSTransform(item, 'labelImage2');
4546          o=newCSSTransform(item, 'labelImage3', $lI.eq(3));
4547          o.translateX=-item.thumbFullWidth/2;
4548          o.translateY=item.thumbFullHeight/2;
4549          SetCSSTransform(item, 'labelImage3');
4550
4551          break;
4552          
4553        case 'labelAppearSplitVert':
4554          var $subCon=$e.find('.subcontainer'),
4555          $lI=$e.find('.labelImage');
4556          $subCon.css({overflow:'hidden', position:'relative'});
4557          $lI.clone().appendTo($subCon);
4558          $lI=$e.find('.labelImage');
4559
4560          newCSSTransform(item, 'labelImage0', $lI.eq(0)).translateX=-item.thumbFullWidth/2;
4561          SetCSSTransform(item, 'labelImage0');
4562          newCSSTransform(item, 'labelImage1', $lI.eq(1)).translateX=item.thumbFullWidth/2;
4563          SetCSSTransform(item, 'labelImage1');
4564          break;
4565          
4566        case 'imageScale150Outside':
4567          $gE.base.css({overflow: 'visible'});
4568          $gE.conTn.css({overflow: 'visible'});
4569          $e.css({overflow: 'visible'});
4570          $e.find('.subcontainer').css({overflow: 'visible'});
4571          $e.find('.imgContainer').css({overflow: 'visible'});
4572          newCSSTransform(item, 'img0', $e.find('img'));
4573          SetCSSTransform(item, 'img0');
4574          setElementOnTop($e.find('.imgContainer'), $e.find('.labelImage'));
4575          break;
4576          
4577        case 'scale120':
4578          $gE.base.css({overflow: 'visible'});
4579          $gE.conTn.css({overflow: 'visible'});
4580          newCSSTransform(item, 'base', $e);
4581          SetCSSTransform(item, 'base');
4582          break;
4583          
4584        case 'scaleLabelOverImage':
4585          var $t=$e.find('.imgContainer');
4586          var $l=$e.find('.labelImage');
4587          setElementOnTop($t, $l);
4588          $e.find('.labelImage').css({opacity:0}
4588);
4589
4590          newCSSTransform(item, 'labelImage0', $l).scale=50;
4591          SetCSSTransform(item, 'labelImage0');
4592          newCSSTransform(item, 'imgContainer0', $t);
4593          SetCSSTransform(item, 'imgContainer0');
4594          break;
4595          
4596        case 'overScale':
4597          $e.css({overflow: 'hidden'});
4598          var $t=$e.find('.imgContainer');
4599          var $l=$e.find('.labelImage');
4600          setElementOnTop('', $l);
4601          $l.css({opacity:0});
4602          $t.css({ opacity: 1});
4603
4604          newCSSTransform(item, 'labelImage0', $l).scale=150;
4605          SetCSSTransform(item, 'labelImage0');
4606          newCSSTransform(item, 'imgContainer0', $t);
4607          SetCSSTransform(item, 'imgContainer0');
4608          break;
4609          
4610        case 'overScaleOutside':
4611          $gE.base.css({overflow: 'visible'});
4612          $gE.conTn.css({overflow: 'visible'});
4613          $e.css({overflow: 'visible'});
4614          var $t=$e.find('.imgContainer');
4615          var $l=$e.find('.labelImage');
4616          setElementOnTop('', $l);
4617          $l.css({opacity:0 });
4618          $t.css({ opacity: 1});
4619
4620          newCSSTransform(item, 'labelImage0', $l).scale=150;
4621          SetCSSTransform(item, 'labelImage0');
4622          newCSSTransform(item, 'imgContainer0', $t);
4623          SetCSSTransform(item, 'imgContainer0');
4624          break;
4625          
4626        case 'rotateCornerBL':
4627          $e.css({overflow: 'hidden'});
4628          var $t=$e.find('.labelImage');
4629          $t.css({opacity:1});
4630          $t[0].style[g_CSStransformName+'Origin'] = '100% 100%';
4631          newCSSTransform(item, 'labelImage0', $t).rotateZ=-90;
4632          SetCSSTransform(item, 'labelImage0');
4633          $t=$e.find('.imgContainer');
4634          $t[0].style[g_CSStransformName+'Origin'] = '100% 100%';;
4635          newCSSTransform(item, 'imgContainer0', $t);
4636          SetCSSTransform(item, 'imgContainer0');
4637          break;
4638          
4639        case 'rotateCornerBR':
4640          $e.css({overflow: 'hidden'});
4641          var $t=$e.find('.labelImage');
4642          $t.css({opacity:1});
4643          $t[0].style[g_CSStransformName+'Origin'] = '0% 100%';
4644          newCSSTransform(item, 'labelImage0', $t).rotateZ=90;
4645          SetCSSTransform(item, 'labelImage0');
4646          $t=$e.find('.imgContainer');
4647          $t[0].style[g_CSStransformName+'Origin'] = '0 100%';
4648          newCSSTransform(item, 'imgContainer0', $t);
4649          SetCSSTransform(item, 'imgContainer0');
4650          break;
4651          
4652        case 'imageRotateCornerBL':
4653          var $t=$e.find('.imgContainer');
4654          setElementOnTop($e, $t);
4655          $e.css({overflow: 'hidden'});
4656          $e.find('.labelImage').css({opacity: 1});
4657          $t[0].style[g_CSStransformName+'Origin'] = 'bottom right';
4658          newCSSTransform(item, 'imgContainer0', $t);
4659          SetCSSTransform(item, 'imgContainer0');
4660          break;
4661          
4662        case 'imageRotateCornerBR':
4663          var $t=$e.find('.imgContainer');
4664          setElementOnTop($e, $t);
4665          $e.css({overflow: 'hidden'});
4666          $e.find('.labelImage').css({opacity: 1});
4667          $t[0].style[g_CSStransformName+'Origin'] = '0 100%';
4668          newCSSTransform(item, 'imgContainer0', $t);
4669          SetCSSTransform(item, 'imgContainer0');
4670          break;
4671          
4672        case 'slideUp':
4673          $e.css({overflow: 'hidden'});
4674          $t=$e.find('.labelImage');
4675          $t.css({opacity:1, top:0});
4676          newCSSTransform(item, 'labelImage0',$t).translateY=item.thumbFullHeight;
4677          SetCSSTransform(item, 'labelImage0');
4678          $t=$e.find('.imgContainer');
4679          $t.css({left:0, top:0});
4680          newCSSTransform(item, 'imgContainer0',$t);
4681          SetCSSTransform(item, 'imgContainer0');
4682          break;
4683          
4684        case 'slideDown':
4685          $e.css({overflow: 'hidden'});
4686          $t=$e.find('.labelImage');
4687          $t.css({opacity:1, top:0});
4688          newCSSTransform(item, 'labelImage0',$t).translateY=-item.thumbFullHeight;
4689          SetCSSTransform(item, 'labelImage0');
4690          $t=$e.find('.imgContainer');
4691          $t.css({left:0, top:0});
4692          newCSSTransform(item, 'imgContainer0',$t);
4693          SetCSSTransform(item, 'imgContainer0');
4694          break;
4695          
4696        case 'slideRight':
4697          $e.css({overflow: 'hidden'});
4698          $t=$e.find('.labelImage');
4699          $t.css({opacity:1, top:0});
4700          newCSSTransform(item, 'labelImage0',$t).translateX=-item.thumbFullWidth;
4701          SetCSSTransform(item, 'labelImage0');
4702          $t=$e.find('.imgContainer');
4703          $t.css({left:0, top:0});
4704          newCSSTransform(item, 'imgContainer0',$t);
4705          SetCSSTransform(item, 'imgContainer0');
4706          break;
4707          
4708        case 'slideLeft':
4709          $e.css({overflow: 'hidden'});
4710          $t=$e.find('.labelImage');
4711          $t.css({opacity:1, top:0});
4712          newCSSTransform(item, 'labelImage0',$t).translateX=item.thumbFullWidth;
4713          SetCSSTransform(item, 'labelImage0');
4714          $t=$e.find('.imgContainer');
4715          $t.css({left:0, top:0});
4716          newCSSTransform(item, 'imgContainer0',$t);
4717          SetCSSTransform(item, 'imgContainer0');
4718          break;
4719          
4720        case 'imageSlideUp':
4721        case 'imageSlideDown':
4722        case 'imageSlideRight':
4723        case 'imageSlideLeft':
4724          $t=$e.find('.imgContainer');
4725          setElementOnTop($e, $t);
4726          $e.css({overflow: 'visible'});
4727          $e.find('.labelImage').css({opacity: 1});
4728          $t.css({left:0, top:0});
4729          newCSSTransform(item, 'imgContainer0',$t);
4730          SetCSSTransform(item, 'imgContainer0');
4731          break;
4732          
4733        case 'labelAppear':
4734        case 'labelAppear75':
4735          var c='rgb('+g_custGlobals.oldLabelRed+','+g_custGlobals.oldLabelGreen+','+g_custGlobals.oldLabelBlue+',0)';
4736          $e.find('.labelImage').css({backgroundColor: c});
4737          //$e.find('.labelImage')[0].style.setProperty( 'backgroundColor',c, 'important' );
4738          $e.find('.labelImageTitle').css({opacity: 0});
4739          $e.find('.labelFolderTitle').css({opacity: 0});
4740          $e.find('.labelDescription').css({opacity: 0});
4741          break;
4742
4743        case 'descriptionAppear':
4744          $e.find('.labelDescription').css({opacity: 0});
4745          break;
4746          
4747        case 'labelSlideUpTop':
4748          $e.css({overflow: 'hidden'});
4749          $e.find('.labelImage').css({top:0, bottom:0});
4750          newCSSTransform(item, 'labelImage0',$e.find('.labelImage')).translateY=item.thumbFullHeight;
4751          SetCSSTransform(item, 'labelImage0');
4752          break;
4753          
4754        case 'labelSlideUp':
4755          $e.css({overflow: 'hidden'});
4756          newCSSTransform(item, 'labelImage0',$e.find('.labelImage')).translateY=item.thumbFullHeight;
4757          SetCSSTransform(item, 'labelImage0');
4758          break;
4759          
4760        case 'labelSlideDown':
4761          $e.css({overflow: 'hidden'});
4762          newCSSTransform(item, 'labelImage0',$e.find('.labelImage')).translateY=-item.thumbFullHeight;
4763          SetCSSTransform(item, 'labelImage0');
4764          break;
4765
4766        case 'descriptionSlideUp':
4767          $e.css({overflow: 'hidden'});
4768          var lh=(item.kind == 'album' ? $e.find('.labelFolderTitle').outerHeight(true) : $e.find('.labelImageTitle').outerHeight(true));
4769          $e.find('.labelDescription').css({opacity:0});
4770          $e.find('.labelImage').css({height:lh});
4771          newCSSTransform(item, 'labelImage0',$e.find('.labelImage'));//.translateY=-lh;
4772          SetCSSTransform(item, 'labelImage0');
4773          break;
4774
4775        case 'imageExplode':
4776          // $gE.base.css('overflow', 'visible');
4777          // $gE.conTn.css('overflow', 'visible');
4778          // $e.css('overflow', 'visible');
4779          
4780          setElementOnTop( '', $e);
4781          setElementOnTop( $e.find('.labelImage'), $e.find('.imgContainer'));
4782          var $subCon=$e.find('.subcontainer'),
4783          n=7,
4784          th=item.thumbFullHeight,      //$e.outerHeight(true);
4785          $iC=$e.find('.imgContainer'),
4786          w=$iC.outerWidth(true)/n,
4787          h=$iC.outerHeight(true),
4788          h=$iC.outerHeight(true)/n;
4789          for(var r=0; r<n; r++ ) {
4790            for(var c=0; c<n; c++ ) {
4791              var s='rect('+h*r+'px, '+w*(c+1)+'px, '+h*(r+1)+'px, '+w*c+'px)';
4792              $iC.clone().appendTo($subCon).css({top:0, scale:1, clip:s, left:0, position:'absolute'}).data('ngScale',1);
4793            }
4794          }
4795          $iC.remove();
4796          break;
4797          
4798        case 'imageFlipHorizontal':
4799          switch( gO.thumbnailLabel.position ){
4800            case 'overImageOnTop':
4801              $e.find('.labelImage').css({top:-g_tn.imgcBorderHeight/2, bottom:g_tn.imgcBorderWidth/2, left:0, right:0 });
4802              break;
4803            case 'overImageOnMiddle':
4804              $e.find('.labelImage').css({top:-g_tn.imgcBorderHeight/2, bottom:g_tn.imgcBorderWidth/2, left:0, right:0});
4805              break;
4806            case 'overImageOnBottom':
4807            default :
4808              $e.find('.labelImage').css({bottom:g_tn.imgcBorderWidth/2, left:0, right:0});
4809              break;
4810          }
4811          $gE.base.css({overflow: 'visible'});
4812          $gE.conTn.css({overflow: 'visible'});
4813          $e.css({overflow: 'visible'});
4814          setElementOnTop( '', $e);
4815          setElementOnTop( $e.find('.labelImage'), $e.find('.imgContainer'));
4816          var $t=$e.find('.subcontainer');
4817          $t.css({overflow: 'visible'});
4818          $t[0].style[g_CSStransformStyle] = 'preserve-3d'
4819          var n= Math.round(item.thumbFullHeight*1.2) + 'px';
4820          $t[0].style[g_CSSperspective] = n;
4821
4822          
4823          // $e.find('.imgContainer').data('ngRotateX','0');
4824          $t=$e.find('.imgContainer');
4825          $t[0].style[g_CSSbackfaceVisibilityName]= 'hidden';
4826          // $t[0].style[g_CSStransformName]= 'rotateX:(0deg)';
4827          newCSSTransform(item, 'imgContainer0', $t);
4828          SetCSSTransform(item, 'imgContainer0');
4829          
4830          $e.find('.image')[0].style[g_CSSbackfaceVisibilityName] = 'hidden';
4831          
4832          // $e.find('.labelImage').data('ngRotateX','180');
4833          $t=$e.find('.labelImage');
4834          $t[0].style[g_CSSbackfaceVisibilityName] = 'hidden';
4835          // $t[0].style[g_CSStransformName] = 'rotateX(180deg)';
4836          newCSSTransform(item, 'labelImage0',$t).rotateX=180;
4837          SetCSSTransform(item, 'labelImage0');
4838          break;
4839          
4840        case 'imageFlipVertical':
4841          switch( gO.thumbnailLabel.position ){
4842            case 'overImageOnTop':
4843              $e.find('.labelImage').css({top:-g_tn.imgcBorderHeight/2, bottom:g_tn.imgcBorderWidth/2, left:0, right:0 });
4844              break;
4845            case 'overImageOnMiddle':
4846              $e.find('.labelImage').css({top:-g_tn.imgcBorderHeight/2, bottom:g_tn.imgcBorderWidth/2, left:0, right:0});
4847              break;
4848            case 'overImageOnBottom':
4849            default :
4850              $e.find('.labelImage').css({bottom:g_tn.imgcBorderWidth/2, left:0, right:0});
4851              break;
4852          }
4853          $gE.base.css({overflow: 'visible'});
4854          $gE.conTn.css({overflow: 'visible'});
4855          $e.css({overflow: 'visible'});
4856          setElementOnTop( '', $e);
4857          setElementOnTop( $e.find('.labelImage'), $e.find('.imgContainer'));
4858          var $t=$e.find('.subcontainer');
4859          $t.css({overflow: 'visible'});
4860          $t[0].style[g_CSStransformStyle] = 'preserve-3d'
4861          var n= Math.round(item.thumbFullWidth*1.2) + 'px';
4862          $t[0].style[g_CSSperspective] = n;
4863
4864          $t=$e.find('.imgContainer');
4865          $t[0].style[g_CSSbackfaceVisibilityName]= 'hidden';
4866          newCSSTransform(item, 'imgContainer0', $t);
4867          SetCSSTransform(item, 'imgContainer0');
4868          
4869          $e.find('.image')[0].style[g_CSSbackfaceVisibilityName] = 'hidden';
4870          
4871          $t=$e.find('.labelImage');
4872          $t[0].style[g_CSSbackfaceVisibilityName] = 'hidden';
4873          newCSSTransform(item, 'labelImage0',$t).rotateY=180;
4874          SetCSSTransform(item, 'labelImage0');
4875          
4876          break;
4877          
4878        // case 'flipHorizontal':  // ONLY TO TEST --> hover issue
4879          // var n= Math.round(item.thumbFullHeight*1.2) + 'px';
4880          // $e.find('.labelImage').css({ perspective: n, rotateX: '180deg', 'backface-visibility': 'hidden', 'opacity':'1', 'height':'100%' });
4881          // break;
4882          
4883        // case 'flipVertical':  // OONLY TO TEST --> hover issue
4884          // var n= Math.round(item.thumbFullWidth*1.2) + 'px';
4885          // $e.find('.subcontainer').css({ perspective: n, rotateY: '0deg'});
4886          // $e.find('.labelImage').css({ perspective: n, rotateY: '180deg', 'backface-visibility': 'hidden', 'opacity':'1', 'height':'100%' });
4887          // break;
4888          
4889        case 'imageScale150':
4890          $e.css({overflow: 'hidden'});
4891          newCSSTransform(item, 'img0', $e.find('img'));
4892          SetCSSTransform(item, 'img0');
4893          break;
4894          
4895        case 'imageScaleIn80':
4896          $e.css({overflow: 'hidden'});
4897          newCSSTransform(item, 'img0', $e.find('img')).scale=120;
4898          SetCSSTransform(item, 'img0');
4899          break;
4900
4901        case 'imageSlide2Up':
4902        case 'imageSlide2Down':
4903        case 'imageSlide2Left':
4904        case 'imageSlide2Right':
4905        case 'imageSlide2UpRight':
4906        case 'imageSlide2UpLeft':
4907        case 'imageSlide2DownRight':
4908        case 'imageSlide2DownLeft':
4909          $e.css({overflow:'hidden'});
4910          item.customData.hoverEffectRDir=g_tnHE[j].name;
4911          ThumbnailInitImageSlide($e, item);
4912          break;
4913          
4914        case 'imageSlide2Random':
4915          $e.css({overflow:'hidden'});
4916          var dir= ['imageSlide2Up','imageSlide2Down','imageSlide2Left','imageSlide2Left','imageSlide2UpRight','imageSlide2UpLeft','imageSlide2DownRight','imageSlide2DownLeft'];
4917          item.customData.hoverEffectRDir=dir[Math.floor(Math.random()*dir.length)];
4918          ThumbnailInitImageSlide($e, item);
4919          break;
4920      }
4921    }
4922    item.hoverInitDone=true;
4923
4924  }
4925  
4926  function ThumbnailInitImageSlide( $e, item ) {
4927    // var w=item.thumbImgWidth;    //$e.outerWidth(true),
4928    // h=item.thumbImg().height,       //$e.outerHeight(true);
4929    var w=item.thumbFullWidth,    //$e.outerWidth(true),
4930    h=item.thumbFullHeight,       //$e.outerHeight(true);
4931    c=null;
4932    var t=newCSSTransform(item, 'img0', $e.find('img'));
4933    t.scale=140;
4934    switch( item.customData.hoverEffectRDir ){
4935      case 'imageSlide2Up':
4936        // c={top:h*.1, left: -w*.1};
4937        // t.translateY=h*.1;
4938        // t.translateX=-w*.1;
4939        t.translateY= item.thumbFullHeight < (item.thumbImg().height*1.4) ? ((item.thumbImg().height*1.4)-item.thumbFullHeight)/2 : 0;
4940        t.translateX= item.thumbFullWidth < (item.thumbImg().width*1.4) ? -((item.thumbImg().width*1.4)-item.thumbFullWidth)/2 : 0;
4941        break;
4942      case 'imageSlide2Down':
4943        // c={top:-h*.1, left: -w*.1};
4944        // t.translateY=-h*.1;
4945        var tY=item.thumbFullHeight < (item.thumbImg().height*1.4) ? Math.min(((item.thumbImg().height*1.4)-item.thumbFullHeight)/2*.1,h*.1) : 0;
4946        t.translateY = -tY;
4947        var tX=item.thumbFullWidth < (item.thumbImg().width*1.4) ? Math.min(((item.thumbImg().width*1.4)-item.thumbFullWidth)/2*.1,w*.1) : 0;
4948        t.translateX = tX;
4949        break;
4950      case 'imageSlide2Left':
4951        // c={top:-h*.1, left: w*.1};
4952        t.translateY=-h*.1;
4953        t.translateX=w*.1;
4954        break;
4955      case 'imageSlide2Right':
4956        // c={top:-h*.1, left: -w*.1};
4957        t.translateY=-h*.1;
4958        t.translateX=-w*.1;
4959        break;
4960
4961      case 'imageSlide2UpRight':
4962        // c={top:h*.05, left: -w*.05};
4963        t.translateY=h*.05;
4964        t.translateX=-w*.05;
4965        break;
4966      case 'imageSlide2UpLeft':
4967        // c={top:h*.05, left: w*.05};
4968        t.translateY=h*.05;
4969        t.translateX=w*.05;
4970        break;
4971      case 'imageSlide2DownRight':
4972        // c={top:-h*.05, left: -w*.05};
4973        t.translateY=-h*.05;
4974        t.translateX=-w*.05;
4975        break;
4976      case 'imageSlide2DownLeft':
4977        // c={top:-h*.05, left: w*.05};
4978        t.translateY=-h*.05;
4979        t.translateX=w*.05;
4980        break;
4981    }
4982    SetCSSTransform(item, 'img0');
4983    //$e.find('.subcontainer').width(w).height(h);
4984    // $e.find('img').css({'max-width':w*1.5, 'max-height':h*1.5});
4985    // $e.find('img')[0].style[g_CSStransformName] = 'scale(1.4)';
4986    // $e.find('img').css(c);  //.css({'width':w*1.5, 'height':h*1.5});
4987    //$e.find('.imgContainer').css(c).css({'width':w*1.5, 'height':h*1.5});
4988  }
4989  
4990
4991  function ThumbnailOverResize( $e ) {
4992    var n=$e.data("index");
4993    if( n == undefined ) { return; }    // required because can be fired on ghost elements
4994    var item=gI[n];
4995    if( !item.hoverInitDone ) {
4996      ThumbnailInit($e);
4997      return;
4998    }
4999    if( typeof gO.fnThumbnailHoverResize == 'function' ) {
5000      gO.fnThumbnailHoverResize($e, item, ExposedObjects() );
5001    }
5002    for( j=0; j<g_tnHE.length; j++) {
5003      switch( g_tnHE[j].name ) {
5004        case 'imageSplit4':
5005          var w=item.thumbFullWidth-g_tn.borderWidth-g_tn.imgcBorderWidth,
5006          h=item.thumbFullHeight-g_tn.borderHeight-g_tn.imgcBorderHeight,
5007          $iC=$e.find('.imgContainer'),
5008          s='rect(0px, '+Math.ceil(w/2)+'px, '+Math.ceil(h/2)+'px, 0px)';
5009          $iC.eq(0).css({ clip:s});
5010          s='rect(0px, '+w+'px, '+Math.ceil(h/2)+'px, '+Math.ceil(w/2)+'px)';
5011          $iC.eq(1).css({ clip:s });
5012          s='rect('+Math.ceil(h/2)+'px, '+w+'px, '+h+'px, '+Math.ceil(w/2)+'px)';
5013          $iC.eq(2).css({ clip:s });
5014          s='rect('+Math.ceil(h/2)+'px, '+Math.ceil(w/2)+'px, '+h+'px, 0px)';
5015          $iC.eq(3).css({ clip:s });
5016          break;
5017          
5018        case 'imageSplitVert':
5019          var $iC=$e.find('.imgContainer'),
5020          w=item.thumbFullWidth-g_tn.borderWidth-g_tn.imgcBorderWidth,
5021          h=item.thumbFullHeight-g_tn.borderHeight-g_tn.imgcBorderHeight,
5022          s='rect(0px, '+Math.ceil(w/2)+'px, '+h+'px, 0px)';
5023          $iC.eq(0).css({ clip:s });
5024          s='rect(0px, '+w+'px, '+h+'px, '+Math.ceil(w/2)+'px)';
5025          $iC.eq(1).css({clip:s });
5026          break;
5027          
5028        case 'labelSplit4':
5029          var w=item.thumbFullWidth-g_tn.borderWidth-g_tn.imgcBorderWidth,
5030          h=item.thumbFullHeight-g_tn.borderHeight-g_tn.imgcBorderHeight,
5031          $lI=$e.find('.labelImage');
5032          s='rect(0px, '+Math.ceil(w/2)+'px, '+Math.ceil(h/2)+'px, 0px)',
5033          $lI.eq(0).css({ clip:s });
5034          s='rect(0px, '+w+'px, '+Math.ceil(h/2)+'px, '+Math.ceil(w/2)+'px)';
5035          $lI.eq(1).css({ clip:s });
5036          s='rect('+Math.ceil(h/2)+'px, '+w+'px, '+h+'px, '+Math.ceil(w/2)+'px)';
5037          $lI.eq(2).css({ clip:s });
5038          s='rect('+Math.ceil(h/2)+'px, '+Math.ceil(w/2)+'px, '+h+'px, 0px)';
5039          $lI.eq(3).css({ clip:s });
5040          break;
5041          
5042        case 'labelSplitVert':
5043          var w=item.thumbFullWidth-g_tn.borderWidth-g_tn.imgcBorderWidth,
5044          h=item.thumbFullHeight-g_tn.borderHeight-g_tn.imgcBorderHeight,
5045          $lI=$e.find('.labelImage');
5046          var s='rect(0px, '+Math.ceil(w/2)+'px, '+h+'px, 0px)';
5047          $lI.eq(0).css({ clip:s});
5048          s='rect(0px, '+w+'px, '+h+'px, '+Math.ceil(w/2)+'px)';
5049          $lI.eq(1).css({ clip:s});
5050          break;
5051
5052        case 'labelAppearSplit4':
5053          var w=item.thumbFullWidth-g_tn.borderWidth-g_tn.imgcBorderWidth,
5054          h=item.thumbFullHeight-g_tn.borderHeight-g_tn.imgcBorderHeight;
5055          $lI=$e.find('.labelImage');
5056          var s='rect(0px, '+Math.ceil(w/2)+'px, '+Math.ceil(h/2)+'px, 0px)';
5057          $lI.eq(0).css({ clip:s });
5058          s='rect(0px, '+w+'px, '+Math.ceil(h/2)+'px, '+Math.ceil(w/2)+'px)';
5059          $lI.eq(1).css({ clip:s });
5060          s='rect('+Math.ceil(h/2)+'px, '+w+'px, '+h+'px, '+Math.ceil(w/2)+'px)';
5061          $lI.eq(2).css({ clip:s });
5062          s='rect('+Math.ceil(h/2)+'px, '+Math.ceil(w/2)+'px, '+h+'px, 0px)';
5063          $lI.eq(3).css({ clip:s });
5064          
5065          
5066          item.eltTransform['labelImage0'].translateX=-item.thumbFullWidth/2;
5067          item.eltTransform['labelImage0'].translateY=-item.thumbFullHeight/2;
5068          SetCSSTransform(item, 'labelImage0');
5069          item.eltTransform['labelImage1'].translateX=item.thumbFullWidth/2;
5070          item.eltTransform['labelImage1'].translateY=-item.thumbFullHeight/2;
5071          SetCSSTransform(item, 'labelImage1');
5072          item.eltTransform['labelImage2'].translateX=item.thumbFullWidth/2;
5073          item.eltTransform['labelImage2'].translateY=item.thumbFullHeight/2;
5074          SetCSSTransform(item, 'labelImage2');
5075          item.eltTransform['labelImage3'].translateX=-item.thumbFullWidth/2;
5076          item.eltTransform['labelImage3'].translateY=item.thumbFullHeight/2;
5077          SetCSSTransform(item, 'labelImage3');
5078          break;
5079          
5080        case 'labelAppearSplitVert':
5081          var w=item.thumbFullWidth-g_tn.borderWidth-g_tn.imgcBorderWidth,
5082          h=item.thumbFullHeight-g_tn.borderHeight-g_tn.imgcBorderHeight;
5083          $lI=$e.find('.labelImage');
5084          var s='rect(0px, '+Math.ceil(w/2)+'px, '+h+'px, 0px)';
5085          $lI.eq(0).css({ clip:s});
5086          s='rect(0px, '+w+'px, '+h+'px, '+Math.ceil(w/2)+'px)';
5087          $lI.eq(1).css({ clip:s});
5088          item.eltTransform['labelImage0'].translateX=-item.thumbFullWidth/2;
5089          SetCSSTransform(item, 'labelImage0');
5090          item.eltTransform['labelImage1'].translateX=item.thumbFullWidth/2;
5091          SetCSSTransform(item, 'labe
5091lImage1');
5092
5093          break;
5094          item.transformLabelImage[0].translateX=-item.thumbFullWidth/2;
5095          SetCSSTransform($lI.eq(0),item.transformLabelImage[0]);
5096          item.transformLabelImage[1].translateX=item.thumbFullWidth/2;
5097          SetCSSTransform($lI.eq(1),item.transformLabelImage[1]);
5098          break;
5099        
5100        case 'slideUp':
5101          // $e.find('.labelImage').css({top:item.thumbFullHeight});
5102          item.eltTransform['labelImage0'].translateY=item.thumbFullHeight;
5103          SetCSSTransform(item, 'labelImage0');
5104          break;
5105          
5106        case 'slideDown':
5107          // $e.find('.labelImage').css({bottom:item.thumbFullHeight});  //, 'background':'none'});
5108          item.eltTransform['labelImage0'].translateY=-item.thumbFullHeight;
5109          SetCSSTransform(item, 'labelImage0');
5110          break;
5111          
5112        case 'slideRight':
5113          // $e.find('.labelImage').css({left:-item.thumbFullWidth});
5114          item.eltTransform['labelImage0'].translateX=-item.thumbFullWidth;
5115          SetCSSTransform(item, 'labelImage0');
5116          break;
5117          
5118        case 'slideLeft':
5119          // $e.find('.labelImage').css({left:item.thumbFullWidth});
5120          item.eltTransform['labelImage0'].translateX=item.thumbFullWidth;
5121          SetCSSTransform(item, 'labelImage0');
5122          break;
5123          
5124        case 'imageExplode':
5125          var $subCon=$e.find('.subcontainer'),
5126          $iC=$e.find('.imgContainer'),
5127          n=Math.sqrt($iC.length),
5128          w=$iC.eq(0).outerWidth(true)/n,
5129          h=$iC.eq(0).outerHeight(true)/n,
5130          i=0;
5131          for(var r=0; r<n; r++ ) {
5132            for(var c=0; c<n; c++ ) {
5133              var s='rect('+h*r+'px, '+w*(c+1)+'px, '+h*(r+1)+'px, '+w*c+'px)';
5134              //$iC.eq(i++).css({ 'clip':s });
5135            }
5136          }
5137          break;
5138          
5139        case 'imageFlipHorizontal':
5140          var $t=$e.find('.subcontainer');
5141          var n= Math.round(item.thumbFullHeight*1.2) + 'px';
5142          $t[0].style[g_CSSperspective] = n;
5143          // $e.find('.imgContainer').css({perspective: n, rotateX: '0deg', 'backface-visibility': 'hidden'});
5144          // $e.find('.labelImage').css({ perspective: n, rotateX: '180deg', 'backface-visibility': 'hidden','height':item.thumbFullHeight,'opacity':'1' });
5145          break;
5146          
5147        case 'imageFlipVertical':
5148          var $t=$e.find('.subcontainer');
5149          var n= Math.round(item.thumbFullWidth*1.2) + 'px';
5150          $t[0].style[g_CSSperspective] = n;
5151          //$e.find('.imgContainer').css({perspective: n, rotateY: '0deg', 'backface-visibility': 'hidden'});
5152          //$e.find('.labelImage').css({ perspective: n, rotateY: '180deg', 'backface-visibility': 'hidden','height':item.thumbFullHeight,'opacity':'1' });
5153          break;
5154          
5155        case 'imageSlide2Up':
5156        case 'imageSlide2Down':
5157        case 'imageSlide2Left':
5158        case 'imageSlide2Right':
5159        case 'imageSlide2UpRight':
5160        case 'imageSlide2UpLeft':
5161        case 'imageSlide2DownRight':
5162        case 'imageSlide2DownLeft':
5163        case 'imageSlide2Random':
5164          ThumbnailInitImageSlide($e, item );
5165          break;
5166
5167        case 'slideUp':
5168          item.eltTransform['labelImage0'].translateY=item.thumbFullHeight;
5169          SetCSSTransform(item, 'labelImage0');
5170          break;
5171        case 'slideDown':
5172          item.eltTransform['labelImage0'].translateY=-item.thumbFullHeight;
5173          SetCSSTransform(item, 'labelImage0');
5174          break;
5175        case 'slideRight':
5176          item.eltTransform['labelImage0'].translateX=-item.thumbFullWidth;
5177          SetCSSTransform(item, 'labelImage0');
5178          break;
5179        case 'slideLeft':
5180          item.eltTransform['labelImage0'].translateX=item.thumbFullWidth;
5181          SetCSSTransform(item, 'labelImage0');
5182          break;
5183          
5184        case 'labelSlideUpTop':
5185        case 'labelSlideUp':
5186          item.eltTransform['labelImage0'].translateY=item.thumbFullHeight;
5187          SetCSSTransform(item, 'labelImage0');
5188          break;
5189          
5190        case 'labelSlideDown':
5191          $e.css({overflow: 'hidden'});
5192          // $e.find('.labelImage').css({top:-item.thumbFullHeight, bottom:''});
5193          item.eltTransform['labelImage0'].translateY=-item.thumbFullHeight;
5194          SetCSSTransform(item, 'labelImage0');
5195          break;
5196
5197        case 'descriptionSlideUp':
5198          // var lh=(item.kind == 'album' ? $e.find('.labelFolderTitle').outerHeight(true) : $e.find('.labelImageTitle').outerHeight(true));
5199          // var p=item.thumbFullHeight - lh -g_tn.borderHeight-g_tn.imgcBorderHeight;
5200          // var lh2=$e.find('.labelDescription').outerHeight(true);
5201          //item.eltTransform['labelImage0'].translateY=lh2;
5202          //SetCSSTransform(item, 'labelImage0');
5203
5204          
5205      }
5206    }
5207
5208  };
5209
5210  
5211
5212  function newCSSTransform(item, eltClass, $e) {
5213    if( item.eltTransform[eltClass] == undefined ) {
5214      item.eltTransform[eltClass]=InitCSSTransform();
5215      item.eltTransform[eltClass].$elt=$e;
5216    }
5217    return item.eltTransform[eltClass];
5218  }
5219  
5220  function removeCSSTransform(item) {
5221     for (var p in item.eltTransform) {
5222        delete item.eltTransform[p];
5223     }
5224  }
5225  
5226  function InitCSSTransform() {
5227    var obj={translateX:0, translateY:0, rotateX:0, rotateY:0, rotateZ:0, scale:100};
5228    return obj;
5229  }
5230  
5231  function SetCSSTransform(item, objClass) {
5232    var obj=item.eltTransform[objClass];
5233    // var v = 'translateX('+obj.translateX+'px) translateY('+obj.translateY+'px)  scale('+obj.scale/100+')';
5234    var v = 'translateX('+obj.translateX+'px) translateY('+obj.translateY+'px) scale('+obj.scale/100+')';
5235    if( !(g_IE <= 9) && !g_isGingerbread ) {
5236      v += ' rotateX('+obj.rotateX+'deg) rotateY('+obj.rotateY+'deg) rotateZ('+obj.rotateZ+'deg)';
5237    }
5238    else {
5239      v += ' rotate('+obj.rotateZ+'deg)';
5240    }
5241    if( obj.$elt[0] != undefined ) {
5242      obj.$elt[0].style[g_CSStransformName]= v;
5243    }
5244  }
5245  
5246
5247  function TnAni( $e, n, anime, item, eltClass) {
5248
5249    var transform=['translateX','translateY', 'scale', 'rotateX', 'rotateY', 'rotateZ'];
5250
5251    if( g_aengine == 'animate' ) {
5252      // internal support for CCS transform (not supported by jQuery)
5253      for( var i=0; i<transform.length; i++ ) {
5254      var tf=transform[i];
5255        if( anime[tf] !== undefined ) {
5256          var from = {v: parseInt(item.eltTransform[eltClass][tf]), item:item, tf:tf, eltClass:eltClass, to:parseInt(anime[tf]) };
5257          var to = {v: parseInt(anime[tf])};
5258          if( g_tnHE[n].delay > 0 ) {
5259            jQuery(from).delay(g_tnHE[n].delay).animate(to, { duration:g_tnHE[n].duration, easing:g_tnHE[n].easing, queue:false, step: function(currentValue) {
5260                if( this.item.hovered ) {
5261                  item.eltTransform[this.eltClass][this.tf]=currentValue;
5262                  SetCSSTransform(this.item, this.eltClass);
5263                }
5264              }, complete: function() {
5265                if( this.item.hovered ) {
5266                  item.eltTransform[this.eltClass][this.tf]=this.to;
5267                  SetCSSTransform(this.item, this.eltClass);
5268                }
5269              }
5270            });
5271          }
5272          else {
5273            jQuery(from).animate(to, { duration:g_tnHE[n].duration, easing:g_tnHE[n].easing, queue:false, step: function(currentValue) {
5274              // var i = Math.round(this.v);
5275              // this.curr || (this.curr = i);
5276              // if (!this.curr || this.curr != i) { 
5277                if( this.item.hovered ) {
5278                  item.eltTransform[this.eltClass][this.tf]=currentValue;
5279                  SetCSSTransform(this.item, this.eltClass);
5280                }
5281              }, complete: function() {
5282                if( this.item.hovered ) {
5283                  item.eltTransform[this.eltClass][this.tf]=this.to;
5284                  SetCSSTransform(this.item, this.eltClass);
5285                }
5286              }
5287            });
5288          }
5289          delete anime[tf];
5290        }
5291      
5292      }
5293    
5294    
5295    }
5296
5297    if( anime.length == 0 ) { return; }
5298 
5299    //$e.find('.imgContainer').eq(0).delay(g_tnHE[n].delay)[g_aengine]({'right':'50%', 'top':'-50%'},g_tnHE[n].duration, g_tnHE[n].easing);
5300    if( g_tnHE[n].delay > 0 ) {
5301      // $e.delay(g_tnHE[n].delay)[g_aengine](anime, g_tnHE[n].duration, g_tnHE[n].easing);
5302      if( g_aengine == 'transition' ) {
5303        // transit has a bug on queue --> we do not use it
5304        $e.delay(g_tnHE[n].delay)[g_aengine](anime, g_tnHE[n].duration , g_tnHE[n].easing );
5305      }
5306      else {
5307        $e.delay(g_tnHE[n].delay)[g_aengine](anime, {duration:g_tnHE[n].duration , easing:g_tnHE[n].easing, queue:false });
5308      }
5309    }
5310    else {
5311      // $e[g_aengine](anime, g_tnHE[n].duration, g_tnHE[n].easing);
5312      if( g_aengine == 'transition' ) {
5313        // transit has a bug on queue --> we do not use it
5314        //anime.queue=false;
5315        //anime.duration=5000;
5316        $e[g_aengine](anime, g_tnHE[n].duration, g_tnHE[n].easing );
5317      } 
5318      else {
5319        $e[g_aengine](anime, {duration:g_tnHE[n].duration , easing:g_tnHE[n].easing, queue:false});
5320      }
5321    }
5322  }
5323  
5324  
5325  function ThumbnailHover( $e ) {
5326    var n=$e.data('index');
5327    if( n == undefined ) { return; }    // required because can be fired on ghost elements
5328    if( g_aengine == 'velocity' ) {
5329      $e.find('*').velocity('stop', true);
5330    }
5331    else {
5332      $e.find('*').stop(true,false);
5333    }
5334    var item=gI[n];
5335    item.hovered=true;
5336    var dscale=(g_aengine == 'animate' ? 1 : 100);
5337    
5338    if( typeof gO.fnThumbnailHover == 'function' ) { 
5339      gO.fnThumbnailHover($e, item, ExposedObjects());
5340    }    
5341    
5342    try {
5343      for( j=0; j<g_tnHE.length; j++) {
5344        switch(g_tnHE[j].name ) {
5345          case 'imageSplit4':
5346            var $t=$e.find('.imgContainer');
5347            TnAni($t.eq(0), j, {translateX:-item.thumbFullWidth/2, translateY:-item.thumbFullHeight/2}, item, 'imgContainer0' );
5348            TnAni($t.eq(1), j, {translateX:item.thumbFullWidth/2, translateY:-item.thumbFullHeight/2}, item, 'imgContainer1' );
5349            TnAni($t.eq(2), j, {translateX:item.thumbFullWidth/2, translateY:item.thumbFullHeight/2}, item, 'imgContainer2' );
5350            TnAni($t.eq(3), j, {translateX:-item.thumbFullWidth/2, translateY:item.thumbFullHeight/2}, item, 'imgContainer3' );
5351            break;
5352            
5353          case 'imageSplitVert':
5354            var $t=$e.find('.imgContainer');
5355            TnAni($t.eq(0), j, {translateX:-item.thumbFullWidth/2}, item, 'imgContainer0' );
5356            TnAni($t.eq(1), j, {translateX:item.thumbFullWidth/2}, item, 'imgContainer1' );
5357            break;
5358            
5359          case 'labelSplit4':
5360            var $t=$e.find('.labelImage');
5361            TnAni($t.eq(0), j, {translateX:-item.thumbFullWidth/2, translateY:-item.thumbFullHeight/2}, item, 'labelImage0' );
5362            TnAni($t.eq(1), j, {translateX:item.thumbFullWidth/2, translateY:-item.thumbFullHeight/2}, item, 'labelImage1' );
5363            TnAni($t.eq(2), j, {translateX:item.thumbFullWidth/2, translateY:item.thumbFullHeight/2}, item, 'labelImage2' );
5364            TnAni($t.eq(3), j, {translateX:-item.thumbFullWidth/2, translateY:item.thumbFullHeight/2}, item, 'labelImage3' );
5365            break;
5366            
5367          case 'labelSplitVert':
5368            var $t=$e.find('.labelImage');
5369            TnAni($t.eq(0), j, {translateX:-item.thumbFullWidth/2}, item, 'labelImage0' );
5370            TnAni($t.eq(1), j, {translateX:item.thumbFullWidth/2}, item, 'labelImage1' );
5371            break;
5372            
5373          case 'labelAppearSplit4':
5374            var $t=$e.find('.labelImage');
5375            TnAni($t.eq(0), j, {translateX:0, translateY:0}, item, 'labelImage0' );
5376            TnAni($t.eq(1), j, {translateX:0, translateY:0}, item, 'labelImage1' );
5377            TnAni($t.eq(2), j, {translateX:0, translateY:0}, item, 'labelImage2' );
5378            TnAni($t.eq(3), j, {translateX:0, translateY:0}, item, 'labelImage3' );
5379            break;
5380            
5381          case 'labelAppearSplitVert':
5382            var $t=$e.find('.labelImage');
5383            TnAni($t.eq(0), j, {translateX:0}, item, 'labelImage0' );
5384            TnAni($t.eq(1), j, {translateX:0}, item, 'labelImage1' );
5385            break;
5386            
5387          case 'scaleLabelOverImage':
5388            TnAni($e.find('.labelImage'), j, { scale:100/dscale, opacity: 1}, item, 'labelImage0' );
5389            TnAni($e.find('.imgContainer'), j, { scale:50/dscale}, item, 'imgContainer0' );
5390            break;
5391            
5392          case 'overScale':
5393          case 'overScaleOutside':
5394            TnAni($e.find('.labelImage'), j, { opacity: 1, scale:100/dscale}, item, 'labelImage0' );
5395            TnAni($e.find('.imgContainer'), j, { opacity: 0, scale:50/dscale}, item, 'imgContainer0' );
5396            break;
5397            
5398          case 'imageInvisible':
5399            TnAni($e.find('.imgContainer'), j, { opacity: 0}, item );
5400            break;
5401
5402          case 'rotateCornerBL':
5403            var r=(g_aengine=='transition'?{rotate:'0deg'}:{rotateZ:'0'});
5404            TnAni($e.find('.labelImage'), j, r, item, 'labelImage0' );
5405            r=(g_aengine=='transition'?{rotate:'90deg'}:{rotateZ:'90'});
5406            TnAni($e.find('.imgContainer'), j, r, item, 'imgContainer0' );
5407            break;
5408            
5409          case 'rotateCornerBR':
5410            var r=(g_aengine=='transition'?{rotate:'0deg'}:{rotateZ:'0'});
5411            TnAni($e.find('.labelImage'), j, r, item, 'labelImage0' );
5412            r=(g_aengine=='transition'?{rotate:'-90deg'}:{rotateZ:'-90'});
5413            TnAni($e.find('.imgContainer'), j, r, item, 'imgContainer0' );
5414            break;
5415            
5416          case 'imageRotateCornerBL':
5417            var r=(g_aengine=='transition'?{rotate:'90deg'}:{rotateZ:'90'});
5418            TnAni($e.find('.imgContainer'), j, r, item, 'imgContainer0' );
5419            break;
5420            
5421          case 'imageRotateCornerBR':
5422            var r=(g_aengine=='transition'?{rotate:'-90deg'}:{rotateZ:'-90'});
5423            TnAni($e.find('.imgContainer'), j, r, item, 'imgContainer0' );
5424            break;
5425            
5426          case 'slideUp':
5427            TnAni($e.find('.imgContainer'), j, { translateY: -item.thumbFullHeight}, item, 'imgContainer0' );
5428            TnAni($e.find('.labelImage'), j, { translateY: 0}, item, 'labelImage0' );
5429            break;
5430            
5431          case 'slideDown':
5432            TnAni($e.find('.imgContainer'), j, { translateY: item.thumbFullHeight}, item, 'imgContainer0' );
5433            TnAni($e.find('.labelImage'), j, { translateY: 0}, item, 'labelImage0' );
5434            break;
5435            
5436          case 'slideRight':
5437            TnAni($e.find('.imgContainer'), j, { translateX: item.thumbFullWidth}, item, 'imgContainer0' );
5438            TnAni($e.find('.labelImage'), j, { translateX: 0}, item, 'labelImage0' );
5439            break;
5440            
5441          case 'slideLeft':
5442            TnAni($e.find('.imgContainer'), j, { translateX: -item.thumbFullWidth}, item, 'imgContainer0' );
5443            TnAni($e.find('.labelImage'), j, { translateX: 0}, item, 'labelImage0' );
5444            break;
5445            
5446          case 'imageSlideUp':
5447            TnAni($e.find('.imgContainer'), j, { translateY: -item.thumbFullHeight }, item, 'imgContainer0' );
5448            break;
5449            
5450          case 'imageSlideDown':
5451            TnAni($e.find('.imgContainer'), j, { translateY: item.thumbFullHeight }, item, 'imgContainer0' );
5452            break;
5453            
5454          case 'imageSlideLeft':
5455            TnAni($e.find('.imgContainer'), j, { translateX: -item.thumbFullWidth }, item, 'imgContainer0' );
5456            break;
5457            
5458          case 'imageSlideRight':
5459            TnAni($e.find('.imgContainer'), j, { translateX: item.thumbFullWidth }, item, 'imgContainer0' );
5460            break;
5461            
5462          case 'labelAppear':
5463            if( g_aengine == 'velocity' ) {
5464              TnAni($e.find('.labelImage'), j, { backgroundColorRed:g_custGlobals.oldLabelRed, backgroundColorGreen:g_custGlobals.oldLabelGreen, backgroundColorBlue:g_custGlobals.oldLabelBlue, backgroundColorAlpha:1 }, item );
5465            }
5466            else {
5467              var c='rgba('+g_custGlobals.oldLabelRed+','+g_custGlobals.oldLabelGreen+','+g_custGlobals.oldLabelBlue+',1)';
5468              TnAni($e.find('.labelImage'), j, { backgroundColor: c}, item );
5469            }
5470            TnAni($e.find('.labelImageTitle'), j, { opacity: 1}, item );
5471            TnAni($e.find('.labelFolderTitle'), j, { opacity: 1}, item );
5472            TnAni($e.find('.labelDescription'), j, { opacity: 1}, item );
5473            break;
5474            
5475          case 'labelAppear75':
5476            if( g_aengine == 'velocity' ) {
5477              TnAni($e.find('.labelImage'), j, { backgroundColorRed:g_custGlobals.oldLabelRed, backgroundColorGreen:g_custGlobals.oldLabelGreen, backgroundColorBlue:g_custGlobals.oldLabelBlue, backgroundColorAlpha:0.75 }, item );
5478            }
5479            else {
5480              var c='rgba('+g_custGlobals.oldLabelRed+','+g_custGlobals.oldLabelGreen+','+g_custGlobals.oldLabelBlue+',0.75)';
5481              TnAni($e.find('.labelImage'), j, { backgroundColor: c}, item );
5482            }
5483            TnAni($e.find('.labelImageTitle'), j, { opacity: 1}, item );
5484            TnAni($e.find('.labelFolderTitle'), j, { opacity: 1}, item );
5485            TnAni($e.find('.labelDescription'), j, { opacity: 1}, item );
5486            break;
5487            
5488          case 'descriptionAppear':
5489            TnAni($e.find('.labelDescription'), j, { opacity: 1}, item );
5490            break;
5491            
5492          case 'labelSlideDown':
5493            TnAni($e.find('.labelImage'), j, { translateY: 0}, item, 'labelImage0' );
5494            // TnAni($e.find('.labelImage'), j, { top: 0}, item );
5495            break;
5496            
5497          case 'labelSlideUpTop':
5498          case 'labelSlideUp':
5499            TnAni($e.find('.labelImage'), j, { translateY: 0}, item, 'labelImage0' );
5500            break;
5501            
5502          case 'descriptionSlideUp':
5503            var lh=(item.kind == 'album' ? $e.find('.labelFolderTitle').outerHeight(true) : $e.find('.labelImageTitle').outerHeight(true));
5504            var lh2=$e.find('.labelDescription').outerHeight(true);
5505            var p=item.thumbFullHeight - lh -lh2;
5506            if( p<0 ) { p=0; }
5507            TnAni($e.find('.labelImage'), j, { translateY:0, height:lh+lh2 }, item, 'labelImage0' );
5508            TnAni($e.find('.labelDescription'), j, { opacity: 1}, item );
5509            break;
5510            
5511          case 'labelOpacity50':
5512            TnAni($e.find('.labelImage'), j, { opacity: 0.5 }, item );
5513            break;
5514            
5515          case 'imageOpacity50':
5516            TnAni($e.find('.imgContainer'), j, { opacity: 0.5 }, item );
5517            break;
5518            
5519          case 'borderLighter':
5520            if( g_aengine == 'velocity' ) {
5521              var colorString=lighterColor(g_custGlobals.oldBorderColor,0.5),
5522              co = colorString.substring(colorString.indexOf('(') + 1, colorString.lastIndexOf(')')).split(/,\s*/);
5523              TnAni($e, j, { borderColorRed:co[0], borderColorGreen:co[1], borderColorBlue:co[2], colorAlpha:co[3] }, item );
5524            }
5525            else {
5526              TnAni($e, j, { borderColor: lighterColor(g_custGlobals.oldBorderColor,0.5) }, item );
5527            }
5528            break;
5529            
5530          case 'borderDarker':
5531            if( g_aengine == 'velocity' ) {
5532              var colorString=darkerColor(g_custGlobals.oldBorderColor,0.5),
5533              co = colorString.substring(colorString.indexOf('(') + 1, colorString.lastIndexOf(')')).split(/,\s*/);
5534              TnAni($e, j, { borderColorRed:co[0], borderColorGreen:co[1], borderColorBlue:co[2], colorAlpha:co[3] }, item );
5535            }
5536            else {
5537              TnAni($e, j, { borderColor: darkerColor(g_custGlobals.oldBorderColor,0.5) }, item );
5538            }
5539            break;
5540            
5541          case 'imageScale150':
5542            TnAni($e.find('img'), j, { scale: 150/dscale }, item, 'img0' );
5543            break;
5544
5545          case 'imageScaleIn80':
5546            TnAni($e.find('img'), j, { scale: 100/dscale }, item, 'img0' );
5547            break;
5548            
5549          case 'imageSlide2Up':
5550          case 'imageSlide2Down':
5551          case 'imageSlide2Left':
5552          case 'imageSlide2Right':
5553          case 'imageSlide2UpRight':
5554          case 'imageSlide2UpLeft':
5555          case 'imageSlide2DownRight':
5556          case 'imageSlide2DownLeft':
5557          case 'imageSlide2Random':
5558            switch(item.customData.hoverEffectRDir) {
5559              case 'imageSlide2Up':
5560                var tY=item.thumbFullHeight < (item.imgHeight*1.4) ? ((item.imgHeight*1.4)-item.thumbFullHeight)/2 : 0;
5561                TnAni($e.find('img'), j, { translateY: -tY }, item, 'img0' );
5562                break;
5563              case 'imageSlide2Down':
5564                var tY=item.thumbFullHeight < (item.imgHeight*1.4) ? ((item.imgHeight*1.4)-item.thumbFullHeight)/2 : 0;
5565                TnAni($e.find('img'), j, { translateY: tY }, item, 'img0' );
5566                break;
5567              case 'imageSlide2Left':
5568                TnAni($e.find('img'), j, { translateX: -item.thumbFullWidth*.1 }, item, 'img0' );
5569                break;
5570              case 'imageSlide2Right':
5571                TnAni($e.find('img'), j, { translateX: item.thumbFullWidth*.1 }, item, 'img0' );
5572                break;
5573                
5574              case 'imageSlide2UpRight':
5575                TnAni($e.find('img'), j, { translateY: -item.thumbFullHeight*.05, translateX: item.thumbFullWidth*.05 }, item, 'img0' );
5576                break;
5577              case 'imageSlide2UpLeft':
5578                TnAni($e.find('img'), j, { translateY: -item.thumbFullHeight*.05, translateX: -item.thumbFullWidth*.05 }, item, 'img0' );
5579                break;
5580              case 'imageSlide2DownRight':
5581                TnAni($e.find('img'), j, { translateY: item.thumbFullHeight*.05, translateX: item.thumbFullWidth*.05 }, item, 'img0' );
5582                break;
5583              case 'imageSlide2DownLeft':
5584                TnAni($e.find('img'), j, { translateY: item.thumbFullHeight*.05, translateX: -item.thumbFullWidth*.05 }, item, 'img0' );
5585                break;
5586            }
5587            break;
5588            
5589          case 'imageScale150Outside':
5590            setElementOnTop('', $e);
5591            TnAni($e.find('img'), j, { scale: 150/dscale }, item, 'img0');
5592            break;
5593            
5594          case 'scale120':
5595            setElementOnTop('', $e);
5596            TnAni($e, j, { scale: 120/dscale }, item, 'base' );
5597            break;
5598
5599          case 'imageExplode':
5600            setElementOnTop('', $e);
5601            var $iC=$e.find('.imgContainer');
5602            n=Math.sqrt($iC.length);
5603            var l = [];
5604            for(var i=0; i<=Math.PI; i+=Math.PI/(n-1) ){
5605              l.push(Math.sin(i));
5606            }
5607            var w=$iC.outerWidth(true)/n,
5608            h=$iC.outerHeight(true)/n,
5609            i=0;
5610            for(var r=0; r<n; r++ ) {
5611             for(var c=0; c<n; c++ ) {
5612                TnAni($iC.eq(i++), j, { top:((-h*n/3)+h*r-h)*l[c], left:((-w*n/3)+w*c-w)*l[r], scale:1.5, opacity:0}, item );
5613              }
5614            }
5615            break;
5616            
5617          case 'imageFlipHorizontal':
5618            setElementOnTop('', $e);
5619            TnAni($e.find('.imgContainer'), j, { rotateX: 180}, item, 'imgContainer0' );
5620            TnAni($e.find('.labelImage'), j, { rotateX: 360}, item, 'labelImage0' );
5621            break;
5622            
5623          case 'imageFlipVertical':
5624            setElementOnTop('', $e);
5625            TnAni($e.find('.imgContainer'), j, { rotateY: 180}, item, 'imgContainer0' );
5626            TnAni($e.find('.labelImage'), j, { rotateY: 360}, item, 'labelImage0' );
5627            break;
5628            
5629          // case 'flipHorizontal':
5630            // setElementOnTop('', $e);
5631            // var n= Math.round(item.thumbFullHeight*1.2) + 'px';
5632            // TnAni($e, j, { perspective: n, rotateX: '180deg'}, item );
5633            // break;
5634            
5635          // case 'flipVertical':
5636            // setElementOnTop('', $e);
5637            // var n= Math.round(item.thumbFullWidth*1.2) + 'px';
5638            // TnAni($e, j, { perspective: n, rotateY: '180deg'}, item );
5639            // break;
5640            
5641          case 'TEST':
5642            //$e.find('img').stop(true, true);
5643            // TnAni($e.find('.subcontainer'), j, { scale: 80//dscale }, item );
5644            break;
5645        }
5646      }
5647    }
5648    catch (e) { 
5649      nanoAlert( 'error on hover ' +e.message );
5650    }
5651  };
5652
5653
5654  
5655  function TnAniO( $e, n, anime, item, eltClass) {
5656    
5657    var transform=['translateX', 'translateY', 'scale', 'rotateX', 'rotateY', 'rotateZ'];
5658
5659    if( g_aengine == 'animate' ) {
5660      for( var i=0; i<transform.length; i++ ) {
5661        var tf=transform[i];
5662        if( anime[tf] !== undefined ) {
5663          var from = {v: parseInt(item.eltTransform[eltClass][tf]), item:item, tf:tf, eltClass:eltClass, to:parseInt(anime[tf]) };
5664          var to = {v: parseInt(anime[tf])};
5665          if( g_tnHE[n].delayBack > 0 ) {
5666            jQuery(from).delay(g_tnHE[n].delayBack).animate(to, { duration:g_tnHE[n].durationBack, easing:g_tnHE[n].easingBack, queue:false, step: function(currentValue) {
5667                item.eltTransform[this.eltClass][this.tf]=currentValue;
5668                SetCSSTransform(this.item, this.eltClass);
5669                // transformElt[this.tf]=currentValue;
5670                // SetCSSTransform(this.$e, this.transformElt);
5671              }, complete: function() {
5672                item.eltTransform[this.eltClass][this.tf]=this.to;
5673                SetCSSTransform(this.item, this.eltClass);
5674                // transformElt[this.tf]=this.to;
5675                // SetCSSTransform(this.$e, this.transformElt);
5676              }
5677            });
5678          }
5679          else {
5680            jQuery(from).animate(to, { duration:g_tnHE[n].durationBack, easing:g_tnHE[n].easingBack, queue:false, step: function(currentValue) {
5681              item.eltTransform[this.eltClass][this.tf]=currentValue;
5682              SetCSSTransform(this.item, this.eltClass);
5683              // var i = Math.round(this.v);
5684              // this.curr || (this.curr = i);
5685              // if (!this.curr || this.curr != i) { 
5686                // transformElt[this.tf]=currentValue;
5687                // transformElt[tf]=this.v;
5688                // SetCSSTransform(this.$e, this.transformElt);
5689              }, complete: function() {
5690                  item.eltTransform[this.eltClass][this.tf]=this.to;
5691                  SetCSSTransform(this.item, this.eltClass);
5692                // transformElt[this.tf]=this.to;
5693             // console.clear();
5694             // console.log(this.to);
5695                // SetCSSTransform(this.$e, this.transformElt);
5696              }
5697            });
5698          }
5699          delete anime[tf];
5700        }
5701      }
5702    }
5703    
5704
5705    if( anime.length == 0 ) { return; }
5706  
5707    //$e.find('.imgContainer').eq(0).delay(g_tnHE[j].delay)[g_aengine]({'right':'50%', 'top':'-50%'},g_tnHE[j].duration, g_tnHE[j].easing);
5708    if( g_tnHE[n].delay > 0 ) {
5709      if( g_aengine == 'transition' ) {
5710        // transit has a bug on queue --> we do not use it
5711        $e.delay(g_tnHE[n].delayBack)[g_aengine](anime, g_tnHE[n].durationBack, g_tnHE[n].easingBack);
5712      }
5713      else {
5714        $e.delay(g_tnHE[n].delayBack)[g_aengine](anime, {duration:g_tnHE[n].durationBack , easing:g_tnHE[n].easingBack, queue:false });
5715      }
5716    }
5717    else {
5718      if( g_aengine == 'transition' ) {
5719        // transit has a bug on queue --> we do not use it
5720        $e[g_aengine](anime, g_tnHE[n].durationBack, g_tnHE[n].easingBack);
5721      }
5722      else {
5723        $e[g_aengine](anime, {duration:g_tnHE[n].durationBack , easing:g_tnHE[n].easingBack, queue:false});
5724      }
5725    }
5726  }
5727  
5728
5729  function ThumbnailHoverOut( $e ) {
5730    if( g_containerViewerDisplayed ) { return; }
5731
5732    var n=$e.data("index");
5733    if( n == undefined ) { return; }    // required because can be fired on ghost elements
5734
5735    if( g_aengine == 'velocity' ) {
5736      $e.find('*').velocity('stop', true);
5737    }
5738    else {
5739      $e.find('*').filter(":animated").stop(true,false);
5740    }
5741    var item=gI[n];
5742    
5743    item.hovered=false;
5744    var dscale=(g_aengine == 'animate' ? 1 : 100);
5745
5746    if( typeof gO.fnThumbnailHoverOut == 'function' ) { 
5747      gO.fnThumbnailHoverOut($e, item, ExposedObjects());
5748    }    
5749
5750    try {
5751      for( j=0; j<g_tnHE.length; j++) {
5752        switch(g_tnHE[j].name ) {
5753          case 'imageSplit4':
5754            var $t=$e.find('.imgContainer');
5755            TnAniO($t.eq(0), j, {translateX:0, translateY:0}, item, 'imgContainer0' );
5756            TnAniO($t.eq(1), j, {translateX:0, translateY:0}, item, 'imgContainer1' );
5757            TnAniO($t.eq(2), j, {translateX:0, translateY:0}, item, 'imgContainer2' );
5758            TnAniO($t.eq(3), j, {translateX:0, translateY:0}, item, 'imgContainer3' );
5759            break;
5760            TnAniO($t.eq(0), j, {right:'0%', top:'0%'} );
5761            TnAniO($t.eq(1), j, {left:'0%', top:'0%'} );
5762            TnAniO($t.eq(2), j, {left:'0%', bottom:'0%'} );
5763            TnAniO($t.eq(3), j, {right:'0%', bottom:'0%'} );
5764            break;
5765            
5766          case 'imageSplitVert':
5767            var $t=$e.find('.imgContainer');
5768            TnAniO($t.eq(0), j, {translateX:0}, item, 'imgContainer0' );
5769            TnAniO($t.eq(1), j, {translateX:0}, item, 'imgContainer1' );
5770            break;
5771            
5772          case 'labelSplit4':
5773            var $t=$e.find('.labelImage');
5774            TnAniO($t.eq(0), j, {translateX:0, translateY:0}, item, 'labelImage0' );
5775            TnAniO($t.eq(1), j, {translateX:0, translateY:0}, item, 'labelImage1' );
5776            TnAniO($t.eq(2), j, {translateX:0, translateY:0}, item, 'labelImage2' );
5777            TnAniO($t.eq(3), j, {translateX:0, translateY:0}, item, 'labelImage3' );
5778            break;
5779            
5780          case 'labelSplitVert':
5781            var $t=$e.find('.labelImage');
5782            TnAniO($t.eq(0), j, {translateX:0}, item, 'labelImage0' );
5783            TnAniO($t.eq(1), j, {translateX:0}, item, 'labelImage1' );
5784            break;
5785            
5786          case 'labelAppearSplit4':
5787            var $t=$e.find('.labelImage');
5788            TnAniO($t.eq(0), j, {translateX:-item.thumbFullWidth/2, translateY:-item.thumbFullHeight/2}, item, 'labelImage0' );
5789            TnAniO($t.eq(1), j, {translateX:item.thumbFullWidth/2, translateY:-item.thumbFullHeight/2}, item, 'labelImage1' );
5790            TnAniO($t.eq(2), j, {translateX:item.thumbFullWidth/2, translateY:item.thumbFullHeight/2}, item, 'labelImage2' );
5791            TnAniO($t.eq(3), j, {translateX:-item.thumbFullWidth/2, translateY:item.thumbFullHeight/2}, item, 'labelImage3' );
5792            break;
5793            
5794          case 'labelAppearSplitVert':
5795            var $t=$e.find('.labelImage');
5796            TnAniO($t.eq(0), j, {translateX:-item.thumbFullWidth/2}, item, 'labelImage0' );
5797            TnAniO($t.eq(1), j, {translateX:item.thumbFullWidth/2}, item, 'labelImage1' );
5798            break;
5799
5800          case 'scaleLabelOverImage':
5801            TnAniO($e.find('.labelImage'), j, { opacity: 0, scale: 50/dscale}, item, 'labelImage0' );
5802            TnAniO($e.find('.imgContainer'), j, { scale: 100/dscale }, item, 'imgContainer0' );
5803            break;
5804            
5805          case 'overScale':
5806          case 'overScaleOutside':
5807            TnAniO($e.find('.labelImage'), j, { opacity: 0, scale:150/dscale}, item, 'labelImage0' );
5808            TnAniO($e.find('.imgContainer'), j, { opacity: 1, scale:100/dscale}, item, 'imgContainer0' );
5809            break;
5810            
5811          case 'imageInvisible':
5812            TnAniO($e.find('.imgContainer'), j, { opacity: 1} );
5813            break;
5814            
5815          case 'rotateCornerBL':
5816            var r=(g_aengine=='transition'?{rotate:'-90deg'}:{rotateZ:'-90'});
5817            TnAniO($e.find('.labelImage'), j, r, item, 'labelImage0' );
5818            r=(g_aengine=='transition'?{rotate:'0deg'}:{rotateZ:'0'});
5819            TnAniO($e.find('.imgContainer'), j, r, item, 'imgContainer0' );
5820            break;
5821            
5822          case 'rotateCornerBR':
5823            var r=(g_aengine=='transition'?{rotate:'90deg'}:{rotateZ:'90'});
5824            TnAniO($e.find('.labelImage'), j, r, item, 'labelImage0' );
5825            r=(g_aengine=='transition'?{rotate:'0deg'}:{rotateZ:'0'});
5826            TnAniO($e.find('.imgContainer'), j, r, item, 'imgContainer0' );
5827            break;
5828            
5829          case 'imageRotateCornerBL':
5830          case 'imageRotateCornerBR':
5831            var r=(g_aengine=='transition'?{rotate:'0deg'}:{rotateZ:'0'});
5832            TnAniO($e.find('.imgContainer'), j, r, item, 'imgContainer0' );
5833            break;
5834            
5835          case 'slideUp':
5836            TnAniO($e.find('.imgContainer'), j, { translateY: 0}, item, 'imgContainer0' );
5837            TnAniO($e.find('.labelImage'), j, { translateY: item.thumbFullHeight}, item, 'labelImage0' );
5838            break;
5839            
5840          case 'slideDown':
5841            TnAniO($e.find('.imgContainer'), j, { translateY: 0}, item, 'imgContainer0' );
5842            TnAniO($e.find('.labelImage'), j, { translateY: -item.thumbFullHeight}, item, 'labelImage0' );
5843            break;
5844            
5845          case 'slideRight':
5846            TnAniO($e.find('.imgContainer'), j, { translateX: 0}, item, 'imgContainer0' );
5847            TnAniO($e.find('.labelImage'), j, { translateX: -item.thumbFullWidth}, item, 'labelImage0' );
5848            break;
5849            
5850          case 'slideLeft':
5851            TnAniO($e.find('.imgContainer'), j, { translateX: 0}, item, 'imgContainer0' );
5852            TnAniO($e.find('.labelImage'), j, { translateX: item.thumbFullWidth}, item, 'labelImage0' );
5853            break;
5854            
5855          case 'imageSlideUp':
5856          case 'imageSlideDown':
5857            TnAniO($e.find('.imgContainer'), j, { translateY: 0 }, item, 'imgContainer0' );
5858            break;
5859            
5860          case 'imageSlideLeft':
5861          case 'imageSlideRight':
5862            TnAniO($e.find('.imgContainer'), j, { translateX: 0 }, item, 'imgContainer0' );
5863            break;
5864            
5865          case 'labelAppear':
5866          case 'labelAppear75':
5867            if( g_aengine == 'velocity' ) {
5868              TnAniO($e.find('.labelImage'), j, { backgroundColorRed:g_custGlobals.oldLabelRed, backgroundColorGreen:g_custGlobals.oldLabelGreen, backgroundColorBlue:g_custGlobals.oldLabelBlue, backgroundColorAlpha:0 } );
5869            }
5870            else {
5871              var c='rgb('+g_custGlobals.oldLabelRed+','+g_custGlobals.oldLabelGreen+','+g_custGlobals.oldLabelBlue+',0)';
5872              TnAniO($e.find('.labelImage'), j, { backgroundColor: c} );
5873            }
5874            TnAniO($e.find('.labelImageTitle'), j, { opacity: 0 } );
5875            TnAniO($e.find('.labelFolderTitle'), j, { opacity: 0 } );
5876            TnAniO($e.find('.labelDescription'), j, { opacity: 0 } );
5877            break;
5878
5879          case 'descriptionAppear':
5880            TnAniO($e.find('.labelDescription'), j, { opacity: 0 } );
5881            break;
5882            
5883          case 'labelSlideDown':
5884            TnAniO($e.find('.labelImage'), j, { translateY:-item.thumbFullHeight}, item, 'labelImage0' );
5885            break;
5886            
5887          case 'labelSlideUpTop':
5888          case 'labelSlideUp':
5889            TnAniO($e.find('.labelImage'), j, { translateY: item.thumbFullHeight}, item, 'labelImage0' );
5890            break;
5891
5892          case 'descriptionSlideUp':
5893            var lh=(item.kind == 'album' ? $e.find('.labelFolderTitle').outerHeight(true) : $e.find('.labelImageTitle').outerHeight(true));
5894            var p=item.thumbFullHeight - lh -g_tn.borderHeight-g_tn.imgcBorderHeight;
5895            TnAniO($e.find('.labelImage'), j, {translateY:0, height:lh}, item, 'labelImage0' );
5896            break;
5897            
5898            
5899          case 'labelOpacity50':
5900            TnAniO($e.find('.labelImage'), j, { opacity: g_custGlobals.oldLabelOpacity } );
5901            break;
5902            
5903          case 'imageOpacity50':
5904            TnAniO($e.find('.imgContainer'), j, { opacity: 1 } );
5905            break;
5906            
5907          case 'borderLighter':
5908          case 'borderDarker':
5909            if( g_aengine == 'velocity' ) {
5910              var colorString=g_custGlobals.oldBorderColor;
5911              var co = colorString.substring(colorString.indexOf('(') + 1, colorString.lastIndexOf(')')).split(/,\s*/);
5912              TnAniO($e, j, { borderColorRed:co[0], borderColorGreen:co[1], borderColorBlue:co[2], colorAlpha:co[3] } );
5913            }
5914            else {
5915              TnAniO($e, j, { borderColor: g_custGlobals.oldBorderColor } );
5916            }
5917            break;
5918            
5919          case 'imageScale150':
5920          case 'imageScale150Outside':
5921            TnAniO($e.find('img'), j, { scale: 100/dscale }, item, 'img0');
5922            break;
5923
5924          case 'imageScaleIn80':
5925            TnAniO($e.find('img'), j, { scale: 120/dscale }, item, 'img0');
5926            break;
5927
5928          case 'imageSlide2Up':
5929          case 'imageSlide2Down':
5930          case 'imageSlide2Left':
5931          case 'imageSlide2Right':
5932          case 'imageSlide2UpRight':
5933          case 'imageSlide2UpLeft':
5934          case 'imageSlide2DownRight':
5935          case 'imageSlide2DownLeft':
5936          case 'imageSlide2Random':
5937            switch(item.customData.hoverEffectRDir) {
5938              case 'imageSlide2Up':
5939                var tY=item.thumbFullHeight < (item.imgHeight*1.4) ? ((item.imgHeight*1.4)-item.thumbFullHeight)/2 : 0;
5940                TnAniO($e.find('img'), j, { translateY: tY }, item, 'img0' );
5941                break;
5942              case 'imageSlide2Down':
5943                var tY=item.thumbFullHeight < (item.imgHeight*1.4) ? ((item.imgHeight*1.4)-item.thumbFullHeight)/2 : 0;
5944                TnAniO($e.find('img'), j, { translateY: -tY }, item, 'img0' );
5945                break;
5946              case 'imageSlide2Left':
5947                TnAniO($e.find('img'), j, { translateX: item.thumbFullWidth*.1 }, item, 'img0' );
5948                break;
5949              case 'imageSlide2Right':
5950                TnAniO($e.find('img'), j, { translateX: -item.thumbFullWidth*.1 }, item, 'img0' );
5951                break;
5952                
5953              case 'imageSlide2UpRight':
5954                TnAniO($e.find('img'), j, { translateY: item.thumbFullHeight*.05, translateX: -item.thumbFullWidth*.05 }, item, 'img0' );
5955                break;
5956              case 'imageSlide2UpLeft':
5957                TnAniO($e.find('img'), j, { translateY: item.thumbFullHeight*.05, translateX: item.thumbFullWidth*.05 }, item, 'img0' );
5958                break;
5959              case 'imageSlide2DownRight':
5960                TnAniO($e.find('img'), j, { translateY: -item.thumbFullHeight*.05, translateX: -item.thumbFullWidth*.05 }, item, 'img0' );
5961                break;
5962              case 'imageSlide2DownLeft':
5963                TnAniO($e.find('img'), j, { translateY: -item.thumbFullHeight*.05, translateX: item.thumbFullWidth*.05 }, item, 'img0' );
5964                break;
5965            }
5966            break;
5967
5968
5969            
5970          case 'scale120':
5971            TnAniO($e, j, { scale: 100/dscale }, item, 'base' );
5972            break;
5973
5974          case 'imageExplode':
5975            var $iC=$e.find('.imgContainer');
5976            n=Math.sqrt($iC.length);
5977            var i=0;
5978            for(var r=0; r<n; r++ ) {
5979              for(var c=0; c<n; c++ ) {
5980                TnAniO($iC.eq(i++), j, { top:0, left:0, scale:1, opacity:1} );
5981              }
5982            }
5983            break;
5984
5985            
5986          case 'imageFlipHorizontal':
5987            // var n= Math.round(item.thumbFullHeight*1.2) + 'px';
5988            TnAniO($e.find('.imgContainer'), j, { rotateX: 0}, item, 'imgContainer0' );
5989            TnAniO($e.find('.labelImage'), j, { rotateX: 180}, item, 'labelImage0' );
5990            break;
5991            
5992          case 'imageFlipVertical':
5993            // var n= Math.round(item.thumbFullWidth*1.2) + 'px';
5994            TnAniO($e.find('.imgContainer'), j, { rotateY: 0}, item, 'imgContainer0' );
5995            TnAniO($e.find('.labelImage'), j, { rotateY: 180}, item, 'labelImage0' );
5996            break;
5997            
5998          // case 'flipHorizontal':
5999            // var n= Math.round(item.thumbFullHeigh*1.2) + 'px';
6000            // TnAniO($e, j, { rotateX: '0deg'} );
6001            // break;
6002            
6003          // case 'flipVertical':
6004            // var n= Math.round(item.thumbFullWidth*1.2) + 'px';
6005            // TnAniO($e, j, { rotateY: '0deg'} );
6006            // break;
6007            
6008          case 'TEST':
6009            // TnAniO($e.find('.subcontainer'), j, { scale: 0.85 } );
6010            break;
6011        }
6012      }
6013    }
6014    catch (e) { 
6015      nanoAlert( 'error on hoverOut ' +e.message );
6016    }
6017  };
6018
6019    
6020
6021  // #########################
6022  // ##### IMAGE DISPLAY #####
6023  // #########################
6024
6025  function DisplayImage( imageIdx ) {
6026
6027    if( gO.viewer == 'fancybox' ) {
6028      OpenFancyBox(imageIdx);
6029    }
6030    else {
6031      if( !g_containerViewerDisplayed ) {
6032        OpenInternalViewer(imageIdx);
6033      }
6034      else {
6035        DisplayInternalViewer(imageIdx, '');
6036      }
6037    }
6038  };
6039  
6040  function OpenInternalViewer( imageIdx ) {
6041
6042    //if( !gO.locationHash ) {
6043    //  top.location.hash='nanogallery/'+g_baseEltID+'/v';
6044    //}
6045    // if( gO.viewerScrollBarHidden ) {
6046      jQuery('body').css({overflow:'hidden'});  //avoid scrollbars
6047    // }
6048    
6049    g_containerViewerDisplayed=true;
6050    
6051    $gE.conVwCon=jQuery('<div  class="nanoGalleryViewerContainer" style="visibility:visible"></div>').appendTo('body');
6052    $gE.conVwCon.addClass('nanogallery_theme_'+gO.theme);
6053    SetColorSchemeViewer($gE.conVwCon);
6054
6055    $gE.conVw=jQuery('<div  id="nanoGalleryViewer" class="nanoGalleryViewer" style="visibility:visible" itemscope itemtype="http://schema.org/ImageObject"></div>').appendTo($gE.conVwCon);
6056    $gE.conVw.css({
6057      visibility:'visible',
6058      position: 'fixed'    //"absolute",
6059    });
6060
6061    var sImg='',
6062    l=gI.length;
6063
6064    sImg+='<img class="image" src="'+gI[imageIdx].responsiveURL()+'" alt=" " style="visibility:visible;opacity:0;position:absolute;top:0;bottom:0;left:0;right:0;margin:auto;zoom:1;" itemprop="contentURL">';
6065    sImg+='<img class="image" src="'+gI[imageIdx].responsiveURL()+'" alt=" " style="visibility:visible;opacity:0;position:absolute;top:0;bottom:0;left:0;right:0;margin:auto;zoom:1;" itemprop="contentURL">';
6066    sImg+='<img class="image" src="'+gI[imageIdx].responsiveURL()+'" alt=" " style="visibility:visible;opacity:0;position:absolute;top:0;bottom:0;left:0;right:0;margin:auto;zoom:1;" itemprop="contentURL">';
6067
6068    $gE.vwContent=jQuery('<div class="content">'+sImg+'<div class="contentAreaPrevious"></div><div class="contentAreaNext"></div></div>').appendTo($gE.conVw);
6069    $gE.vwImgP=$gE.conVw.find('.image').eq(0);
6070    $gE.vwImgC=$gE.conVw.find('.image').eq(1);
6071    $gE.vwImgN=$gE.conVw.find('.image').eq(2);
6072
6073    /*
6074    if( gO.enableElevatezoom && toType(jQuery().elevateZoom) == 'function' ) {
6075      $gE.vwImgP.elevateZoom({ zoomType : "lens", lensShape : "round", lensSize : 200, onZoomedImageLoaded: function(){
6076        setElementOnTop('','.zoomContainer');
6077      } });
6078      $gE.vwImgN.elevateZoom({ zoomType : "lens", lensShape : "round", lensSize : 200, onZoomedImageLoaded: function(){
6079        setElementOnTop('','.zoomContainer');} 
6080      });
6081      $gE.vwImgC.elevateZoom({ zoomType : "lens", lensShape : "round", lensSize : 200, onZoomedImageLoaded: function(){
6082        setElementOnTop('','.zoomContainer');
6083      } });
6084    }
6085    */
6086    
6087    // makes content unselectable --> avoid image drag effect during 'mouse swipe'
6088    $gE.conVwCon.find('*').attr('draggable', 'false').attr('unselectable', 'on');
6089
6090    
6091    var $closeB=jQuery('<div class="closeButtonFloating"></div>').appendTo($gE.conVw);
6092    $closeB.on("touchstart click",function(e){
6093    // $closeB.on( (g_isIOS ? "touchstart" : "click") ,function(e){     // IPAD
6094      e.preventDefault();
6095      e.stopPropagation();
6096      if( (new Date().getTime()) - g_timeImgChanged < 400 ) { return; }
6097      CloseInternalViewer(true);
6098      return false;
6099    });
6100
6101    // build image toolbar
6102    var sTB='<div class="toolbarContainer" style="visibility:'+(gO.viewerToolbar.display ? "visible" : "hidden")+';"><div class="toolbar">';
6103    var tstd = gO.viewerToolbar.standard.split(',');
6104    for( var i=0, lstd=tstd.length; i<lstd; i++) {
6105      sTB+=ToolbarAddElt( tstd[i] );
6106    }
6107    var tmin = gO.viewerToolbar.minimized.split(',');
6108    for( var i=0, lmin=tmin.length; i<lmin; i++) {
6109      if( gO.viewerToolbar.standard.indexOf(tmin[i]) == -1 ) {
6110        sTB+=ToolbarAddElt( tmin[i] );
6111      }
6112    }
6113    sTB+='</div></div>';
6114    $gE.conVwTb=jQuery(sTB).appendTo($gE.conVw);
6115    
6116    if( g_toolbarMode == 'min' || (gO.viewerToolbar.autoMinimize > 0 && gO.viewerToolbar.autoMinimize >= getViewport().w) ) {
6117      ToolbarVisibilityMin();
6118    }
6119    else {
6120      ToolbarVisibilityStd();
6121    }
6122
6123
6124    if( gO.viewerDisplayLogo ) {
6125      $gE.vwLogo=jQuery('<div class="nanoLogo"></div>').appendTo($gE.conVw);
6126    }
6127
6128    setElementOnTop('',$gE.conVw);
6129    setElementOnTop($gE.conVw,$closeB);
6130    ResizeInternalViewer();
6131
6132    g_timeImgChanged=new Date().getTime();
6133    
6134    $gE.conVwTb.find('.closeButton').on("touchstart click",function(e){
6135      e.preventDefault();
6136      e.stopPropagation();
6137      if( (new Date().getTime()) - g_timeImgChanged < 400 ) { return; }
6138      CloseInternalViewer(true);
6139    });
6140
6141
6142    $gE.conVwTb.find('.playPauseButton').on("touchstart click",function(e){ 
6143      e.stopPropagation();
6144      SlideshowToggle();
6145    });
6146    
6147    $gE.conVwTb.find('.minimizeButton').on("touchstart click",function(e){ 
6148      e.stopPropagation();
6149      ToolbarVisibilityToggle();
6150    });
6151    
6152    $gE.conVwTb.find('.fullscreenButton').on("touchstart click",function(e){ 
6153      e.stopPropagation();
6154      ViewerFullscreenToggle();
6155    });
6156    
6157    $gE.conVwTb.find('.infoButton').on("touchstart click",function(e){ 
6158      e.stopPropagation();
6159      if( typeof gO.fnViewerInfo == 'function' ) {
6160        gO.fnViewerInfo(gI[g_viewerCurrentItemIdx], ExposedObjects());
6161      }
6162    });
6163
6164
6165    // custom button click
6166    $gE.conVwTb.find('.ngCustomBtn').on("touchstart click",function(e){ 
6167      e.stopPropagation();
6168      if( typeof gO.fnImgToolbarCustClick == 'function' ) {
6169        var target = e.target || e.srcElement;
6170        while( target == null || target.getAttribute('class') == null || target.getAttribute('class').indexOf('ngCustomBtn') == -1 ) {       // go element parent up to find the custom element
6171          target = target.parentNode;
6172        }
6173        var v=target.getAttribute('class');
6174        if( v.indexOf('ngCustomBtn') >=0 ) {
6175          var vs=v.split(' ');
6176          for( var i=0, l=vs.length; i<l; i++) {
6177            if( vs[i].indexOf('custom') == 0 ) {
6178              gO.fnImgToolbarCustClick(vs[i], jQuery(target), gI[g_viewerCurrentItemIdx], ExposedObjects())
6179            }
6180          }
6181        }
6182      }
6183    });
6184    
6185    
6186    $gE.conVwTb.find('.linkOriginalButton').on("touchstart click",function(e){ 
6187      e.stopPropagation();
6188      if( gO.kind == 'picasa') {
6189        var sU='https://plus.google.com/photos/'+gO.userID+'/albums/'+gI[g_viewerCurrentItemIdx].albumID+'/'+gI[g_viewerCurrentItemIdx].GetID();
6190        window.open(sU,'_blank');
6191      }
6192      
6193      if( gO.kind == 'flickr') {
6194        var sU='https://www.flickr.com/photos/'+gO.userID+'/'+gI[g_viewerCurrentItemIdx].GetID();
6195        window.open(sU,'_blank');
6196      }
6197    });
6198
6199    $gE.conVwTb.find('.nextButton').on("touchstart click",function(e){ e.stopPropagation(); DisplayNextImagePart1(); });
6200    $gE.conVwTb.find('.previousButton').on("touchstart click",function(e){ e.stopPropagation(); DisplayPreviousImage(); });
6201    $gE.vwContent.find('.contentAreaNext').on("touchstart click",function(e){ e.stopPropagation(); DisplayNextImagePart1(); });
6202    $gE.vwContent.find('.contentAreaPrevious').on("touchstart click",function(e){ e.stopPropagation(); DisplayPreviousImage(); });
6203
6204    $gE.vwContent.on("click",function(e){
6205      if( (new Date().getTime()) - g_timeImgChanged < 400 ) { return; }
6206      e.preventDefault();
6207      e.stopPropagation();
6208      CloseInternalViewer(true);
6209      return false;
6210    });
6211    
6212    // makes images unselectable (avoid blue overlay)
6213    $gE.conVw.find('.image').attr('draggable', 'false').attr('unselectable', 'on').css({ '-moz-user-select':'none', '-khtml-user-select': 'none', '-webkit-user-select': 'none', '-o-user-select': 'none', 'user-select': 'none'});
6214
6215    DisplayInternalViewer(imageIdx, '');
6216
6217    if( g_viewerSwipe == null ) {
6218      g_viewerSwipe = new ViewerSwipeSupport($gE.conVwCon[0]);
6219    }
6220    
6221    if( gO.slideshowAutoStart ) {
6222      g_playSlideshow=true;
6223      $gE.conVwTb.find('.playPauseButton').removeClass('playButton').addClass('pauseButton');
6224      DisplayNextImage();
6225      g_playSlideshowTimerID=window.setInterval(function(){DisplayNextImage();},g_slideshowDelay);
6226    }
6227
6228  };
6229  
6230  
6231  function ToolbarAddElt( elt ) {
6232    var r='',
6233    e=elt.replace(/^\s+|\s+$/g, '');    // remove trailing/leading whitespace
6234    switch( e ) {
6235      case 'minimizeButton':
6236        r='<div class="ngbt minimizeButton hideToolbarButton"></div>';
6237        break;
6238      case 'previousButton':
6239        r='<div class="ngbt previousButton"></div>';
6240        break;
6241      case 'pageCounter':
6242        r='<div class="pageCounter"></div>';
6243        break;
6244      case 'nextButton':
6245        r='<div class="ngbt nextButton"></div>';
6246        break;
6247      case 'playPauseButton':
6248        r='<div class="ngbt playButton playPauseButton"></div>';
6249        break;
6250      case 'fullscreenButton':
6251        if( g_supportFullscreenAPI ) {
6252          r='<div class="ngbt setFullscreenButton fullscreenButton"></div>';
6253        }
6254        break;
6255      case 'infoButton':
6256        if( typeof gO.fnViewerInfo == 'function' ) {
6257          r='<div class="ngbt infoButton"></div>';
6258        }
6259        break;
6260      case 'linkOriginalButton':
6261        if( gO.kind == 'flickr' || gO.kind == 'picasa' ) {
6262          r='<div class="ngbt linkOriginalButton"></div>';
6263        }
6264        break;
6265      case 'closeButton':
6266        r='<div class="ngbt closeButton"></div>';
6267        break;
6268      case 'label':
6269        r='<div class="label"><div class="title" itemprop="name"></div><div class="description" itemprop="description"></div></div>';
6270        break;
6271      default:
6272        if( e.indexOf('custom') == 0 ) {
6273          r='<div class="ngbt ngCustomBtn '+e+'">' + (typeof gO.fnImgToolbarCustInit == 'function' ? gO.fnImgToolbarCustInit(e) : '') + '</div>';
6274        }
6275        break;
6276    }
6277    return r;
6278  }
6279  
6280  // based on "Implement Custom Gestures" from Google
6281  // https://developers.google.com/web/fundamentals/input/touch-input/touchevents/
6282  function ViewerSwipeSupport(element) {
6283    var elementToSwipe=element,
6284    isAnimating=false,
6285    initialTouchPos=null,
6286    lastTouchPos=null,
6287    currentXPosition=0,
6288    me=this;
6289    
6290
6291
6292    function handleGestureStart(e) {
6293      if( !g_containerViewerDisplayed ) { return; }
6294      
6295      e.preventDefault();
6296
6297      if(e.touches && e.touches.length > 1) { return; }
6298
6299      initialTouchPos = getGesturePointFromEvent(e);
6300
6301      // Add the move and end listeners
6302      if (window.navigator.msPointerEnabled) {
6303        // Pointer events are supported.
6304        document.addEventListener('MSPointerMove', handleGestureMove, true);
6305        document.addEventListener('MSPointerUp', handleGestureEnd, true);
6306      } else {
6307        // Add Touch Listeners
6308        document.addEventListener('touchmove', handleGestureMove, true);
6309        document.addEventListener('touchend', handleGestureEnd, true);
6310        document.addEventListener('touchcancel', handleGestureEnd, true);
6311      
6312        // Add Mouse Listeners
6313        document.addEventListener('mousemove', handleGestureMove, true);
6314        document.addEventListener('mouseup', handleGestureEnd, true);
6315      }
6316    }
6317
6318    function handleGestureMove(e) {
6319    e.preventDefault();
6320
6321      lastTouchPos = getGesturePointFromEvent(e);
6322      
6323      if(isAnimating) { return; }
6324      
6325      isAnimating = true;
6326      
6327      // window.requestAnimFrame(onAnimFrame);
6328      window.requestAnimationFrame(onAnimFrame);
6329    }
6330
6331    
6332    // Handle end gestures
6333    function handleGestureEnd(e) {
6334      if( e.cancelable ) { e.preventDefault(); }
6335      if(e.touches && e.touches.length > 0) { return; }
6336
6337      isAnimating = false;
6338      
6339      // Remove Event Listeners
6340      if (window.navigator.msPointerEnabled) {
6341        // Remove Pointer Event Listeners
6342        document.removeEventListener('MSPointerMove', handleGestureMove, true);
6343        document.removeEventListener('MSPointerUp', handleGestureEnd, true);
6344      } else {
6345      // Remove Touch Listeners
6346        document.removeEventListener('touchmove', handleGestureMove, true);
6347        document.removeEventListener('touchend', handleGestureEnd, true);
6348        document.removeEventListener('touchcancel', handleGestureEnd, true);
6349      
6350        // Remove Mouse Listeners
6351        document.removeEventListener('mousemove', handleGestureMove, true);
6352        document.removeEventListener('mouseup', handleGestureEnd, true);
6353      }
6354      
6355      updateSwipeRestPosition();
6356    }
6357
6358
6359
6360
6361
6362
6363
6364
6365
6366
6367
6368
6369
6370    // Handle the start of gestures
6371    this.handleGestureStartOLD = function(e) {
6372      if( !g_containerViewerDisplayed ) { return; }
6373      
6374      e.preventDefault();
6375
6376      if(e.touches && e.touches.length > 1) { return; }
6377
6378      initialTouchPos = getGesturePointFromEvent(e);
6379
6380      // Add the move and end listeners
6381      if (window.navigator.msPointerEnabled) {
6382        // Pointer events are supported.
6383        document.addEventListener('MSPointerMove', this.handleGestureMove, true);
6384        document.addEventListener('MSPointerUp', this.handleGestureEnd, true);
6385      } else {
6386        // Add Touch Listeners
6387        document.addEventListener('touchmove', this.handleGestureMove, true);
6388        document.addEventListener('touchend', this.handleGestureEnd, true);
6389        document.addEventListener('touchcancel', this.handleGestureEnd, true);
6390      
6391        // Add Mouse Listeners
6392        document.addEventListener('mousemove', this.handleGestureMove, true);
6393        document.addEventListener('mouseup', this.handleGestureEnd, true);
6394      }
6395      
6396    }.bind(this);
6397    
6398    // Handle move gestures
6399    this.handleGestureMoveOLD = function (e) {
6400    e.preventDefault();
6401
6402      lastTouchPos = getGesturePointFromEvent(e);
6403      
6404      if(isAnimating) { return; }
6405      
6406      isAnimating = true;
6407      
6408      // window.requestAnimFrame(onAnimFrame);
6409      window.requestAnimationFrame(onAnimFrame);
6410    }.bind(this);
6411
6412    
6413    // Handle end gestures
6414    this.handleGestureEndOLD = function(e) {
6415      if( e.cancelable ) { e.preventDefault(); }
6416      if(e.touches && e.touches.length > 0) { return; }
6417
6418      isAnimating = false;
6419      
6420      // Remove Event Listeners
6421      if (window.navigator.msPointerEnabled) {
6422        // Remove Pointer Event Listeners
6423        document.removeEventListener('MSPointerMove', this.handleGestureMove, true);
6424        document.removeEventListener('MSPointerUp', this.handleGestureEnd, true);
6425      } else {
6426        // Remove Touch Listeners
6427        document.removeEventListener('touchmove', this.handleGestureMove, true);
6428        document.removeEventListener('touchend', this.handleGestureEnd, true);
6429        document.removeEventListener('touchcancel', this.handleGestureEnd, true);
6430      
6431        // Remove Mouse Listeners
6432        document.removeEventListener('mousemove', this.handleGestureMove, true);
6433        document.removeEventListener('mouseup', this.handleGestureEnd, true);
6434      }
6435      
6436      updateSwipeRestPosition(this);
6437    }.bind(this);
6438    
6439    // function updateSwipeRestPositionOLD(me) {
6440    function updateSwipeRestPosition() {
6441      if( lastTouchPos == null ) {      // touchend without touchmove
6442        currentXPosition=0;
6443        initialTouchPos=null;
6444        return; 
6445      }
6446
6447      var differenceInX = initialTouchPos.x - lastTouchPos.x;
6448      currentXPosition = currentXPosition - differenceInX;
6449    
6450      if( differenceInX < -50 ) {
6451        DisplayPreviousImage();
6452      }
6453      if( differenceInX > 50 ) {
6454        DisplayNextImagePart1();
6455      }
6456      currentXPosition=0;
6457      initialTouchPos=null;
6458      lastTouchPos=null;
6459        
6460      if(Math.abs(differenceInX) < 50) {
6461        ImageSwipeTranslateX(currentXPosition);
6462      }
6463      return;
6464    }
6465    
6466
6467    function getGesturePointFromEvent(e) {
6468      var point = {};
6469
6470      if(e.targetTouches) {
6471        point.x = e.targetTouches[0].clientX;
6472        point.y = e.targetTouches[0].clientY;
6473      } else {
6474        // Either Mouse event or Pointer Event
6475        point.x = e.clientX;
6476        point.y = e.clientY;
6477      }
6478
6479      return point;
6480    }
6481    
6482
6483    function onAnimFrame() {
6484      if(!isAnimating) { return; }
6485      
6486      var differenceInX = initialTouchPos.x - lastTouchPos.x;
6487      
6488      ImageSwipeTranslateX(currentXPosition - differenceInX);
6489      
6490      isAnimating = false;
6491    }
6492
6493    this.removeEventListeners = function() {
6494      // we need to remove all the event listeners (becauase of an issue with the close button)
6495      if (window.navigator.msPointerEnabled) {
6496        elementToSwipe.removeEventListener('MSPointerDown', handleGestureStart, true);
6497        document.removeEventListener('MSPointerMove', handleGestureMove, true);
6498        document.removeEventListener('MSPointerUp', handleGestureEnd, true);
6499      }
6500      else {
6501        // Remove Touch Listeners
6502        elementToSwipe.removeEventListener('touchstart', handleGestureStart, true);
6503        document.removeEventListener('touchmove', handleGestureMove, true);
6504        document.removeEventListener('touchend', handleGestureEnd, true);
6505        document.removeEventListener('touchcancel', handleGestureEnd, true);
6506      
6507        // Remove Mouse Listeners
6508        document.removeEventListener('mousemove', handleGestureMove, true);
6509        document.removeEventListener('mouseup', handleGestureEnd, true);
6510        //elementToSwipe.removeEventListener('mousedown', this.handleGestureStart, true);
6511      }
6512    }
6513    
6514    // Check if pointer events are supported.
6515    if (window.navigator.msPointerEnabled) {
6516      // Add Pointer Event Listener
6517      // elementToSwipe.addEventListener('MSPointerDown', this.handleGestureStart, true);
6518      elementToSwipe.addEventListener('MSPointerDown', handleGestureStart, true);
6519    }
6520    else {
6521      // Add Touch Listener
6522      // elementToSwipe.addEventListener('touchstart', this.handleGestureStart, true);
6523      elementToSwipe.addEventListener('touchstart', handleGestureStart, true);
6524      
6525      // Add Mouse Listener
6526      //elementToSwipe.addEventListener('mousedown', this.handleGestureStart, true);
6527    }
6528    
6529  }
6530
6531  function ImageSwipeTranslateX( posX ) {
6532    g_imageSwipePosX=posX;
6533    if( g_CSStransformName == null ) {
6534      $gE.vwImgC.css({ left: posX }); 
6535    }
6536    else {
6537      $gE.vwImgC[0].style[g_CSStransformName]= 'translateX('+posX+'px)';
6538      if(  gO.imageTransition == 'slide' ) {
6539        if( posX > 0 ) {
6540          var $new=$gE.vwImgP;
6541          var dir=getViewport().w;
6542          $gE.vwImgP.css({visibility:'visible', left:0, opacity:1});
6543          $gE.vwImgP[0].style[g_CSStransformName]= 'translateX('+(-dir+posX)+'px) '
6544          $gE.vwImgN[0].style[g_CSStransformName]= 'translateX('+(-dir)+'px) '
6545        }
6546        else {
6547          var $new=$gE.vwImgN;
6548          var dir=-getViewport().w;
6549          $gE.vwImgN.css({visibility:'visible', left:0, opacity:1});
6550          $gE.vwImgN[0].style[g_CSStransformName]= 'translateX('+(-dir+posX)+'px) '
6551          $gE.vwImgP[0].style[g_CSStransformName]= 'translateX('+(-dir)+'px) '
6552        }
6553      }
6554    }
6555  }
6556  
6557  // Toggle viewer fullscreen mode on/off
6558  function ViewerFullscreenToggle(){
6559    if( ngscreenfull.enabled ) {
6560      // ngscreenfull.toggle($gE.conVwCon[0]);    // --> issue on scrollbar visibility
6561      ngscreenfull.toggle();
6562      if( g_viewerIsFullscreen ) {
6563        g_viewerIsFullscreen=false;
6564        $gE.conVwTb.find('.fullscreenButton').removeClass('removeFullscreenButton').addClass('setFullscreenButton');
6565      }
6566      else {
6567        g_viewerIsFullscreen=true;
6568        $gE.conVwTb.find('.fullscreenButton').removeClass('setFullscreenButton').addClass('removeFullscreenButton');
6569      }
6570    }
6571  }
6572
6573  function ViewerFullscreenToggleOLD(){
6574    if( g_viewerIsFullscreen ) {
6575      ViewerFullscreenOff();
6576    }
6577    else {
6578      if ($gE.conVwCon[0].requestFullscreen) {
6579                  $gE.conVwCon[0].requestFullscreen();
6580              } else if ($gE.conVwCon[0].webkitRequestFullscreen) {
6581                $gE.conVwCon[0].webkitRequestFullscreen();
6582              } else if ($gE.conVwCon[0].msRequestFullscreen) {
6583                $gE.conVwCon[0].msRequestFullscreen();
6584              } else if ($gE.conVwCon[0].mozRequestFullScreen) {
6585                $gE.conVwCon[0].mozRequestFullScreen();
6586              }
6587      g_viewerIsFullscreen=true;
6588      $gE.conVwTb.find('.fullscreenButton').removeClass('setFullscreenButton').addClass('removeFullscreenButton');
6589    }
6590  }
6591  
6592  function ViewerFullscreenOff() {
6593    if (document.exitFullscreen) {
6594            document.exitFullscreen();
6595          } else if (document.webkitExitFullscreen) {
6596            document.webkitExitFullscreen();
6597          } else if (document.msExitFullscreen) {
6598            document.msExitFullscreen();
6599          } else if (document.mozCancelFullScreen) {
6600            document.mozCancelFullScreen();
6601          }
6602
6603    g_viewerIsFullscreen=false;
6604    $gE.conVwTb.find('.fullscreenButton').removeClass('removeFullscreenButton').addClass('setFullscreenButton');
6605  }
6606
6607  // toggle slideshow mode on/off
6608  function SlideshowToggle(){
6609    if( g_playSlideshow ) {
6610      window.clearInterval(g_playSlideshowTimerID);
6611      g_playSlideshow=false;
6612      $gE.conVwTb.find('.playPauseButton').removeClass('pauseButton').addClass('playButton');
6613    }
6614    else {
6615      g_playSlideshow=true;
6616      $gE.conVwTb.find('.playPauseButton').removeClass('playButton').addClass('pauseButton');
6617      DisplayNextImage();
6618      g_playSlideshowTimerID=window.setInterval(function(){DisplayNextImage();},g_slideshowDelay);
6619    }
6620  }
6621  
6622  // toggle toolbar visibility
6623  function ToolbarVisibilityToggle(){
6624    if( g_toolbarMode == 'std' ) {
6625      ToolbarVisibilityMin();
6626    }
6627    else {
6628      ToolbarVisibilityStd();
6629    }
6630  }
6631  
6632  function ToolbarVisibilityStd() {
6633    g_toolbarMode='std';
6634    $gE.conVwTb.find('.minimizeButton').removeClass('viewToolbarButton').addClass('hideToolbarButton');
6635    ToolbarSetItemsVisibility('std');
6636    ResizeInternalViewer();
6637  }
6638  function ToolbarVisibilityMin() {
6639    g_toolbarMode='min';
6640    $gE.conVwTb.find('.minimizeButton').removeClass('hideToolbarButton').addClass('viewToolbarButton');
6641    ToolbarSetItemsVisibility('min');
6642    ResizeInternalViewer();
6643  }
6644  
6645  function ToolbarSetItemsVisibility( curMode ) {
6646    var t=gO.viewerToolbar;
6647    var t= ( curMode == 'std' ? gO.viewerToolbar.standard : gO.viewerToolbar.minimized);
6648    
6649    // standard elements
6650    var v=['minimizeButton', 'previousButton', 'pageCounter', 'nextButton', 'playPauseButton', 'fullscreenButton', 'infoButton', 'linkOriginalButton', 'closeButton', 'label'];
6651    for( var i=0, l=v.length; i<l; i++) {
6652      if( v[i] == 'label' ) {
6653        if( $gE.conVwTb.find('.title').text() == '' && $gE.conVwTb.find('.description').text() == '' ) {
6654          $gE.conVwTb.find('.'+v[i]).css({ display: 'none' });
6655        }
6656        else {
6657          $gE.conVwTb.find('.'+v[i]).css({ display:( t.indexOf(v[i]) >= 0 ? 'table-cell' : 'none') });
6658        }
6659      }
6660      else {
6661        $gE.conVwTb.find('.'+v[i]).css({ display:( t.indexOf(v[i]) >= 0 ? 'table-cell' : 'none') });
6662      }
6663    }
6664    
6665    // custom elements
6666    $gE.conVwTb.find('.ngCustomBtn').css({ display: 'none' });
6667    var vC=t.split(',');
6668    for( var i=0, l=vC.length; i<l; i++) {
6669      var vCT=vC[i].replace(/^\s+|\s+$/g, '');     // remove trailing/leading whitespace
6670      if( vCT.indexOf('custom') == 0 ) {
6671        $gE.conVwTb.find('.'+vCT).css({ display: 'table-cell' });
6672      }
6673    }
6674  }
6675  
6676  
6677  // Display next image
6678  function DisplayNextImagePart1() {
6679    if( g_playSlideshow ) {
6680      window.clearInterval(g_playSlideshowTimerID);
6681      g_playSlideshowTimerID=window.setInterval(function(){DisplayNextImage();},g_slideshowDelay);
6682    }
6683    DisplayNextImage();
6684  }
6685  function DisplayNextImage() {
6686    if( g_viewerImageIsChanged ) { return; }
6687    if( (new Date().getTime()) - g_timeImgChanged < 300 ) { return; }
6688    var l=gI.length;
6689
6690    var newImageIdx=GetNextImageIdx(g_viewerCurrentItemIdx);
6691    DisplayInternalViewer(newImageIdx, 'nextImage');
6692  };
6693  
6694  // Display previous image
6695  function DisplayPreviousImage() {
6696    if( g_viewerImageIsChanged ) { return; }
6697    if( (new Date().getTime()) - g_timeImgChanged < 300 ) { return; }
6698    if( g_playSlideshow ) {
6699      SlideshowToggle();
6700    }
6701    
6702    var newImageIdx=GetPreviousImageIdx(g_viewerCurrentItemIdx);
6703    DisplayInternalViewer(newImageIdx, 'previousImage');
6704  };
6705
6706  // Display image (and run animation)
6707  function DisplayInternalViewer( imageIdx, displayType ) {
6708    g_timeImgChanged=new Date().getTime();
6709    g_viewerImageIsChanged=true;
6710    var displayNext=true;
6711    
6712    if( gO.locationHash ) {
6713      var s ='nanogallery/'+g_baseEltID+'/'+gI[imageIdx].albumID+"/"+gI[imageIdx].GetID();
6714      if( ('#'+s) != location.hash ) {
6715        g_lastLocationHash='#'+s;
6716        top.location.hash = s;
6717      }
6718      else {
6719        g_lastLocationHash=top.location.hash;
6720      }
6721    }
6722    
6723    //DisplayInternalViewerToolbar(imageIdx);
6724    ResizeInternalViewer();
6725    window.cancelAnimationFrame(g_viewerResizeTimerID);
6726    //HideInternalViewerToolbar();
6727    
6728    g_viewerCurrentItemIdx=imageIdx;
6729    
6730    if( displayType == '' ) {
6731      // first image --> just appear / no animation
6732      $gE.vwImgC.css({ opacity:0, left:0, visibility: 'visible'}).attr('src',g_emptyGif).attr('src',gI[imageIdx].responsiveURL());
6733
6734      /*
6735      if( gO.enableElevatezoom && toType(jQuery().elevateZoom) == 'function' ) {
6736        $gE.vwImgC.attr('data-zoom-image',gI[imageIdx].responsiveURL());
6737      }
6738      */
6739
6740      jQuery.when(
6741        $gE.vwImgC.animate({ opacity: 1 }, 300)
6742      ).done(function () {
6743        DisplayInternalViewerComplete(imageIdx, displayType);
6744      });
6745    }
6746    else {
6747      // animate image change
6748      switch( gO.imageTransition ) {
6749        case 'fade':
6750          var $new=(displayType == 'nextImage' ? $gE.vwImgN : $gE.vwImgP);
6751          $new.css({ opacity:0, left:0, visibility:'visible'});
6752          jQuery.when(
6753            $gE.vwImgC.animate({ opacity: 0 }, 500), 
6754            $new.animate({ opacity: 1 }, 300)
6755          ).done(function () {
6756            DisplayInternalViewerComplete(imageIdx, displayType);
6757          });
6758          break;
6759          
6760        case 'slideBETA':
6761          var $new=(displayType == 'nextImage' ? $gE.vwImgN : $gE.vwImgP);
6762          $new.css({ opacity:1, left:0, visibility:'visible'});
6763          if( g_CSStransformName == null ) {
6764            // animate LEFT
6765            jQuery.when(
6766              $gE.vwImgC.animate({ left: (displayType == 'nextImage' ? -getViewport().w : getViewport().w)+'px', opacity: 0 }, 500), 
6767              $new.animate({ opacity: 1 }, 300)
6768            ).done(function () {
6769              DisplayInternalViewerComplete(imageIdx, displayType);
6770            });
6771          }
6772          else {
6773            // animate TRANSLATEX
6774            var dir=(displayType == 'nextImage' ? - getViewport().w : getViewport().w);
6775            $new[0].style[g_CSStransformName]= 'translateX('+(-dir)+'px) '
6776            var from = {v: g_imageSwipePosX };
6777            var to = {v: (displayType == 'nextImage' ? - getViewport().w : getViewport().w)};
6778            jQuery(from).animate(to, { duration:500, step: function(currentValue) {
6779                $gE.vwImgC[0].style[g_CSStransformName]= 'translateX('+currentValue+'px)';
6780                $gE.vwImgC.css({ opacity: (1-Math.abs(currentValue/dir)) });
6781                $new[0].style[g_CSStransformName]= 'translateX('+(-dir+currentValue)+'px) '
6782              }, complete: function() {
6783                $gE.vwImgC[0].style[g_CSStransformName]= '';
6784                $gE.vwImgC.css({ opacity:0 });
6785                DisplayInternalViewerComplete(imageIdx, displayType);
6786              }
6787            });
6788          }
6789          break;
6790
6791        case 'slide':
6792          var $new=(displayType == 'nextImage' ? $gE.vwImgN : $gE.vwImgP);
6793          // if( g_CSStransformName == null || ( g_isIOS && g_IOSversion < 6 ) ) {
6794          if( g_CSStransformName == null  ) {
6795            // animate LEFT
6796            $new.css({ opacity:0, left:0, visibility:'visible'});
6797            jQuery.when(
6798              $gE.vwImgC.animate({ left: (displayType == 'nextImage' ? -getViewport().w : getViewport().w)+'px' }, 500), 
6799              $new.animate({ opacity: 1 }, 300)
6800            ).done(function () {
6801              DisplayInternalViewerComplete(imageIdx, displayType);
6802            });
6803          }
6804          else {
6805            // animate TRANSLATEX
6806            $new.css({ opacity:1, left:0, visibility:'visible'});
6807            var dir=(displayType == 'nextImage' ? - getViewport().w : getViewport().w);
6808            $new[0].style[g_CSStransformName]= 'translateX('+(-dir)+'px) '
6809            var from = {v: g_imageSwipePosX };
6810            var to = {v: (displayType == 'nextImage' ? - getViewport().w : getViewport().w)};
6811            jQuery(from).animate(to, { duration:400, easing:'linear', step: function(currentValue) {
6812                window.requestAnimationFrame( function() {
6813                  $gE.vwImgC[0].style[g_CSStransformName]= 'translateX('+currentValue+'px)';
6814                  //$gE.vwImgC.css({ opacity: (1-Math.abs(currentValue/dir)) });
6815                  $new[0].style[g_CSStransformName]= 'translateX('+(-dir+currentValue)+'px) '
6816                });
6817              }, complete: function() {
6818                window.requestAnimationFrame( function() {
6819                  $gE.vwImgC[0].style[g_CSStransformName]= '';
6820                  //$gE.vwImgC.css({ opacity:0 });
6821                  DisplayInternalViewerComplete(imageIdx, displayType);
6822                });
6823              }
6824            });
6825          }
6826          break;
6827          
6828        case 'slideAppear':
6829        default:
6830          var dir= getViewport().w+'px';
6831          var $new=$gE.vwImgP;
6832          if( displayType == 'nextImage' ) {
6833            dir='-'+dir;
6834            $new=$gE.vwImgN;
6835          }
6836          $new.css({ opacity:0, left:0, visibility:'visible'});
6837          jQuery.when(
6838            $gE.vwImgC.animate({ left: dir, opacity: 0 }, 500), 
6839            $new.animate({ opacity: 1 }, 300)
6840          ).done(function () {
6841            ImageSwipeTranslateX(0);
6842            DisplayInternalViewerComplete(imageIdx, displayType);
6843          });
6844          break;
6845      }
6846    }
6847  }
6848  
6849
6850  function DisplayInternalViewerComplete( imageIdx, displayType ) {
6851    DisplayInternalViewerToolbar(imageIdx);
6852
6853    g_imageSwipePosX=0;
6854    
6855    $gE.vwImgC.off("click");
6856    $gE.vwImgC.removeClass('imgCurrent');
6857  
6858    var $tmp=$gE.vwImgC;
6859    switch( displayType ) {
6860      case 'nextImage':
6861        $gE.vwImgC=$gE.vwImgN;
6862        $gE.vwImgN=$tmp;
6863        break;
6864      case 'previousImage':
6865        $gE.vwImgC=$gE.vwImgP;
6866        $gE.vwImgP=$tmp;
6867        break;
6868    }
6869    $gE.vwImgC.addClass('imgCurrent');
6870    
6871    $gE.vwImgN.css({ opacity:0, left:0, visibility:'hidden' }).attr('src',g_emptyGif).attr('src',gI[GetNextImageIdx(imageIdx)].responsiveURL());
6872    //$gE.vwImgN.css({'opacity':0, 'right':'0', 'left':'0', visibility: 'hidden'}).attr('src',gI[GetNextImageIdx(imageIdx)].responsiveURL());
6873    $gE.vwImgP.css({ opacity:0, left:0, visibility:'hidden'}).attr('src',g_emptyGif).attr('src',gI[GetPreviousImageIdx(imageIdx)].responsiveURL());
6874    //$gE.vwImgP.css({'opacity':0, 'right':'0', 'left':'0', visibility: 'hidden'}).attr('src',gI[GetPreviousImageIdx(imageIdx)].responsiveURL());
6875
6876
6877    $gE.vwImgC.on("click",function(e){
6878      e.stopPropagation();
6879      if( e.pageX < (jQuery(window).width()/2) ) {
6880        DisplayPreviousImage();
6881      }
6882      else {
6883        DisplayNextImagePart1();
6884      }
6885    });
6886
6887    /*
6888    if( gO.enableElevatezoom && toType(jQuery().elevateZoom) == 'function' ) {
6889      $gE.vwImgN.attr('data-zoom-image',gI[GetNextImageIdx(imageIdx)].responsiveURL());
6890      $gE.vwImgP.attr('data-zoom-image',gI[GetPreviousImageIdx(imageIdx)].responsiveURL());
6891      jQuery('.zoomContainer').remove();
6892      var ez = $gE.vwImgC.data('elevateZoom');
6893      ez.imageSrc=$gE.vwImgC.attr('src');
6894      ez.zoomImage=$gE.vwImgC.attr('src');
6895      ez.fetch(ez.imageSrc);
6896    }
6897    */
6898  
6899    ResizeInternalViewer();
6900
6901    // TODO: this code does not work
6902    //jQuery(g_containerViewerContent).find('img').on('resize', function(){ 
6903    //  ResizeInternalViewer('.imgCurrent');
6904    //  console.log('resized');
6905    //});
6906
6907
6908    g_viewerImageIsChanged=false;
6909  }
6910
6911  function GetNextImageIdx( imageIdx ) {
6912    var l=gI.length;
6913    var newImageIdx=-1;
6914
6915    for(var i=imageIdx+1; i<l; i++ ){
6916      if( gI[i].albumID == gI[imageIdx].albumID && gI[i].kind == 'image' ) {
6917        newImageIdx=i;
6918        break;
6919      }
6920    }
6921    if( newImageIdx == -1 ) {
6922      for(var i=0; i<=imageIdx; i++ ){
6923        if( gI[i].albumID == gI[imageIdx].albumID && gI[i].kind == 'image' ) {
6924          newImageIdx=i;
6925          break;
6926        }
6927      }
6928    }
6929    
6930    return newImageIdx;
6931  }
6932
6933  function GetPreviousImageIdx( imageIdx ) {
6934    var newImageIdx=-1;
6935    for(var i=imageIdx-1; i>=0; i-- ){
6936      if( gI[i].albumID == gI[imageIdx].albumID && gI[i].kind == 'image' ) {
6937        newImageIdx=i;
6938        break;
6939      }
6940    }
6941    if( newImageIdx == -1 ) {
6942      for(var i=gI.length-1; i>=imageIdx; i-- ){
6943        if( gI[i].albumID == gI[imageIdx].albumID && gI[i].kind == 'image' ) {
6944          newImageIdx=i;
6945          break;
6946        }
6947      }
6948    }
6949    
6950    return newImageIdx;
6951  }
6952
6953  function HideInternalViewerToolbar() {
6954    $gE.conVwTb.css({'visibility':'hidden'});
6955  }
6956  
6957  function DisplayInternalViewerToolbar( imageIdx ) {
6958    
6959    if( !gO.viewerToolbar.display ) { return; }
6960
6961    $gE.conVwTb.css({'visibility':'visible'});
6962    var setTxt=false;
6963    // set title
6964    if( gI[imageIdx].title !== undefined && gI[imageIdx].title != '' ) {
6965      $gE.conVwTb.find('.title').html(gI[imageIdx].title);
6966      setTxt=true;
6967    }
6968    else {
6969      $gE.conVwTb.find('.title').html('');
6970    }
6971    // set description
6972    if( gI[imageIdx].description !== undefined && gI[imageIdx].description != '' ) {
6973      $gE.conVwTb.find('.description').html(gI[imageIdx].description);
6974      setTxt=true;
6975    }
6976    else {
6977      $gE.conVwTb.find('.description').html('');
6978    }
6979    
6980    // custom elements
6981    var $cu=$gE.conVwTb.find('.ngCustomBtn');
6982    if( $cu.length > 0 && typeof gO.fnImgToolbarCustDisplay == 'function' ) {
6983      gO.fnImgToolbarCustDisplay( $cu, gI[imageIdx], ExposedObjects());
6984    }
6985
6986    // if( setTxt && (gO.viewerToolbar.label == 'always' || gO.viewerToolbar.label == g_toolbarMode) ) {
6987    if( setTxt && (g_toolbarMode == 'std' ? gO.viewerToolbar.standard : gO.viewerToolbar.minimized).indexOf('label') >= 0 ) {
6988      $gE.conVwTb.find('.label').show();
6989    }
6990    else {
6991      $gE.conVwTb.find('.label').hide();
6992    }
6993
6994    // set page number
6995    var viewerMaxImages=0;
6996    var l=gI.length;
6997    for( var i=0; i <  l ; i++ ) {
6998      if( gI[i].albumID == gI[imageIdx].albumID && gI[i].kind == 'image' ) {
6999        viewerMaxImages++;
7000      }
7001    }
7002    if( viewerMaxImages > 0 ) {
7003      $gE.conVwTb.find('.pageCounter').html((gI[imageIdx].imageNumber+1)+'/'+viewerMaxImages);
7004    }
7005    
7006    //ResizeInternalViewer();
7007  }
7008  
7009  function CloseInternalViewer( setLocationHash ) {
7010
7011    if( g_viewerImageIsChanged ) {
7012      $gE.vwContent.find('*').stop(true,true);
7013      //return;
7014    }
7015    g_viewerImageIsChanged=false;
7016
7017    if( g_containerViewerDisplayed ) {
7018      g_viewerSwipe.removeEventListeners();
7019      g_viewerSwipe=null;
7020
7021      // window.clearInterval(g_viewerResizeTimerID);
7022      window.cancelAnimationFrame(g_viewerResizeTimerID);
7023      if( g_playSlideshow ) {
7024        window.clearInterval(g_playSlideshowTimerID);
7025        g_playSlideshow=false;
7026      }
7027      if( !(gO.galleryFullpageButton && $gE.base.hasClass('fullpage')) ) {      // avoid displaying scrollbar when gallery is in fullpage mode
7028        ScrollbarSetVisible();
7029      }
7030      if( g_viewerIsFullscreen ) {
7031        ViewerFullscreenToggle();
7032      }
7033      
7034      $gE.conVwCon.hide(0).off().show(0).html('').remove();
7035
7036      if( g_albumIdxToOpenOnViewerClose != -1 ) {
7037        DisplayAlbum(g_albumIdxToOpenOnViewerClose,true);
7038      }
7039      else {
7040        if( gO.locationHash && setLocationHash ) {
7041          var albumID=gI[g_viewerCurrentItemIdx].albumID,
7042          s='nanogallery/'+g_baseEltID+'/'+albumID;
7043          g_lastLocationHash='#'+s;
7044          top.location.hash=s;
7045        }
7046        ThumbnailHoverOutAll();
7047      }
7048      g_timeImgChanged=new Date().getTime();
7049      g_containerViewerDisplayed=false;
7050    }
7051  };
7052  
7053  function ResizeInternalViewer() {
7054    
7055    //window.clearInterval(g_viewerResizeTimerID);
7056    window.cancelAnimationFrame(g_viewerResizeTimerID);
7057    //g_viewerResizeTimerID=window.setInterval(function(){ResizeInternalViewer()},200);
7058    g_viewerResizeTimerID=window.requestAnimationFrame( ResizeInternalViewer );
7059
7060    var windowsW=$gE.conVw.width();
7061    var windowsH=$gE.conVw.height();
7062
7063    var $elt=$gE.vwImgC,
7064    vwImgC_H=$elt.height(),
7065    vwImgC_W=$elt.width(),
7066    vwImgC_OHt=$elt.outerHeight(true),
7067    vwImgC_OHf=$elt.outerHeight(false);
7068
7069    var $tb=$gE.conVwTb.find('.toolbar'),
7070    tb_OHt=$tb.outerHeight(true);
7071    
7072    if( vwImgC_H <= 40 || !gO.viewerToolbar.display ) {
7073      $gE.conVwTb.css({visibility:'hidden'});
7074    }
7075    else {
7076      $gE.conVwTb.css({visibility:'visible'});
7077    }
7078
7079    var contentOuterWidthV=Math.abs($gE.vwContent.outerHeight(true)-$gE.vwContent.height()),  // vertical margin+border+padding
7080    contentOuterWidthH=Math.abs($gE.vwContent.outerWidth(true)-$gE.vwContent.width()),  // horizontal margin+border+padding
7081    imgBorderV=vwImgC_OHf-$elt.innerHeight(),
7082    imgBorderH=Math.abs($elt.outerWidth(false)-$elt.innerWidth()),
7083    imgPaddingV=Math.abs($elt.innerHeight()-vwImgC_H),
7084    imgPaddingH=Math.abs($elt.innerWidth()-vwImgC_W),
7085    tV=imgBorderV+imgPaddingV,  //+tmargin;
7086    tH=imgBorderH+imgPaddingH,  //+tmargin;
7087    toolbarH=0;
7088    if( gO.viewerToolbar.style != 'innerImage' ) {
7089      toolbarH=tb_OHt;
7090    }
7091    var h=windowsH-toolbarH-contentOuterWidthV,
7092    w=windowsW-contentOuterWidthH;
7093        
7094    switch( gO.viewerToolbar.position ) {
7095      case 'top':
7096        $gE.vwContent.css({height:h, width:w, top:toolbarH  });
7097        var posY=0;
7098        if( gO.viewerToolbar.style == 'innerImage' ) {
7099          posY= Math.abs(vwImgC_OHt-vwImgC_H)/2 +5;
7100        }
7101        if( gO.viewerToolbar.style == 'stuckImage' ) {
7102          posY= Math.abs(vwImgC_OHt-vwImgC_H)/2 -tV;
7103        }
7104        $gE.conVwTb.css({top: posY});
7105        break;
7106
7107      case 'bottom':
7108      default:
7109        $gE.vwContent.css({height:h, width:w });
7110        var posY=0;
7111        if( gO.viewerToolbar.style == 'innerImage' ) {
7112          posY= Math.abs(vwImgC_OHt-vwImgC_H)/2 +5;//- $gE.conVwTb.outerHeight(true) ;
7113        }
7114        if( gO.viewerToolbar.style == 'stuckImage' ) {
7115          posY= Math.abs(vwImgC_OHt-vwImgC_H)/2 -tV;
7116        }
7117        $gE.conVwTb.css({bottom: posY});
7118        break;
7119    }
7120    
7121    if( gO.viewerToolbar.style == 'innerImage' ) {
7122      $tb.css({'max-width': vwImgC_W});
7123    }
7124    
7125    if( gO.viewerToolbar.style == 'fullWidth' ) {
7126      $tb.css({width: w});
7127    }
7128
7129    $gE.conVwTb.css({ height: tb_OHt });   // resize toolbar container to toolbar size
7130    $gE.vwContent.children('img').css({'max-width':(w-tH), 'max-height':(h-tV) });
7131
7132  }
7133  
7134  
7135  function OpenFancyBox( imageIdx ) {
7136    var n=imageIdx,
7137    lstImages=[],
7138    current=0;
7139
7140    lstImages[current]=new Object;
7141    lstImages[current].href=gI[n].responsiveURL();
7142    lstImages[current].title=gI[n].title;
7143    
7144    var l=gI.length;
7145    for( var j=n+1; j<l ; j++) {
7146      if( gI[j].kind == 'image' && gI[j].albumID == gI[imageIdx].albumID && gI[j].destinationURL == '' ) {
7147        current++;
7148        lstImages[current]=new Object;
7149        lstImages[current].href=gI[j].responsiveURL();
7150        lstImages[current].title=gI[j].title;
7151      }
7152    }
7153    for( var j=0; j<n; j++) {
7154      if( gI[j].kind == 'image' && gI[j].albumID == gI[imageIdx].albumID && gI[j].destinationURL == '' ) {
7155        current++;
7156        lstImages[current]=new Object;
7157        lstImages[current].href=gI[j].responsiveURL();
7158        lstImages[current].title=gI[j].title;
7159      }
7160    }
7161    if( gO.fancyBoxOptions != null ) {
7162      jQuery.fancybox(lstImages, gO.fancyBoxOptions);
7163    }
7164    else {
7165      jQuery.fancybox(lstImages,{'autoPlay':false, 'nextEffect':'fade', 'prevEffect':'fade','scrolling':'no',
7166        helpers    : {  buttons  : { 'position' : 'bottom'} }
7167      });
7168    }
7169  };
7170  
7171  // ##### BREADCRUMB/THUMBNAIL COLOR SCHEME #####
7172  function SetColorScheme( element ) {
7173    var cs=null;
7174    switch(toType(gO.colorScheme)) {
7175      case 'object':    // user custom color scheme object 
7176        cs=g_colorScheme_default;
7177        jQuery.extend(true,cs,gO.colorScheme);
7178        g_colorSchemeLabel='nanogallery_colorscheme_custom_'+g_baseEltID;
7179        break;
7180      case 'string':    // name of an internal defined color scheme
7181        switch( gO.colorScheme ) {
7182          case 'none':
7183            return;
7184            break;
7185          case 'light':
7186            cs=g_colorScheme_light;
7187            g_colorSchemeLabel='nanogallery_colorscheme_light';
7188            break;
7189          case 'lightBackground':
7190            cs=g_colorScheme_lightBackground;
7191            g_colorSchemeLabel='nanogallery_colorscheme_lightBackground';
7192            break;
7193          case 'darkRed':
7194            cs=g_colorScheme_darkRed;
7195            g_colorSchemeLabel='nanogallery_colorscheme_darkred';
7196            break;
7197          case 'darkGreen':
7198            cs=g_colorScheme_darkGreen;
7199            g_colorSchemeLabel='nanogallery_colorscheme_darkgreen';
7200            break;
7201          case 'darkBlue':
7202            cs=g_colorScheme_darkBlue;
7203            g_colorSchemeLabel='nanogallery_colorscheme_darkblue';
7204            break;
7205          case 'darkOrange':
7206            cs=g_colorScheme_darkOrange;
7207            g_colorSchemeLabel='nanogallery_colorscheme_darkorange';
7208            break;
7209          case 'default':
7210          case 'dark':
7211          default:
7212            cs=g_colorScheme_default;
7213            g_colorSchemeLabel='nanogallery_colorscheme_default';
7214        }
7215        break;
7216      default:
7217        nanoAlert('Error in colorScheme parameter.');
7218        return;
7219    }
7220
7221    
7222 
7223    
7224    //var s1='.nanogallery_theme_'+gO.theme+' ';
7225    var s1='.' + g_colorSchemeLabel + ' ';
7226    var s=s1+'.nanoGalleryNavigationbar { background:'+cs.navigationbar.background+' !important; }'+'\n';
7227    if( cs.navigationbar.border !== undefined ) { s+=s1+'.nanoGalleryNavigationbar { border:'+cs.navigationbar.border+' !important; }'+'\n'; }
7228    if( cs.navigationbar.borderTop !== undefined ) { s+=s1+'.nanoGalleryNavigationbar { border-top:'+cs.navigationbar.borderTop+' !important; }'+'\n'; }
7229    if( cs.navigationbar.borderBottom !== undefined ) { s+=s1+'.nanoGalleryNavigationbar { border-bottom:'+cs.navigationbar.borderBottom+' !important; }'+'\n'; }
7230    if( cs.navigationbar.borderRight !== undefined ) { s+=s1+'.nanoGalleryNavigationbar { border-right:'+cs.navigationbar.borderRight+' !important; }'+'\n'; }
7231    if( cs.navigationbar.borderLeft !== undefined ) { s+=s1+'.nanoGalleryNavigationbar { border-left:'+cs.navigationbar.borderLeft+' !important; }'+'\n'; }
7232    s+=s1+'.nanoGalleryNavigationbar .oneFolder  { color:'+cs.navigationbar.color+' !important; }'+'\n';
7233    s+=s1+'.nanoGalleryNavigationbar .separator  { color:'+cs.navigationbar.color+' !important; }'+'\n';
7234    s+=s1+'.nanoGalleryNavigationbar .separatorRTL  { color:'+cs.navigationbar.color+' !important; }'+'\n';
7235    s+=s1+'.nanoGalleryNavigationbar .nanoGalleryTags { color:'+cs.navigationbar.color+' !important; }'+'\n';
7236    s+=s1+'.nanoGalleryNavigationbar .setFullPageButton { color:'+cs.navigationbar.color+' !important; }'+'\n';
7237    s+=s1+'.nanoGalleryNavigationbar .removeFullPageButton { color:'+cs.navigationbar.color+' !important; }'+'\n';
7238    s+=s1+'.nanoGalleryNavigationbar .oneFolder:hover { color:'+cs.navigationbar.colorHover+' !important; }'+'\n';
7239    s+=s1+'.nanoGalleryNavigationbar .separatorRTL:hover { color:'+cs.navigationbar.colorHover+' !important; }'+'\n';
7240    s+=s1+'.nanoGalleryNavigationbar .nanoGalleryTags:hover { color:'+cs.navigationbar.colorHover+' !important; }'+'\n';
7241    s+=s1+'.nanoGalleryNavigationbar .setFullPageButton:hover { color:'+cs.navigationbar.colorHover+' !important; }'+'\n';
7242    s+=s1+'.nanoGalleryNavigationbar .removeFullPageButton:hover { color:'+cs.navigationbar.colorHover+' !important; }'+'\n';
7243
7244    s+=s1+'.nanoGalleryContainer > .nanoGalleryThumbnailContainer { background:'+cs.thumbnail.background+' !important; border:'+cs.thumbnail.border+' !important; }'+'\n';
7245    s+=s1+'.nanoGalleryContainer > .nanoGalleryThumbnailContainer .imgContainer { background:'+cs.thumbnail.background+' !important; }'+'\n';
7246    // s+=s1+'.nanoGalleryContainer .nanoGalleryThumbnailContainer .labelImage { background:'+cs.thumbnail.labelBackground+' !important; }'+'\n';
7247    s+=s1+'.nanoGalleryContainer > .nanoGalleryThumbnailContainer .labelImage{ background:'+cs.thumbnail.labelBackground+' ; }'+'\n';
7248    s+=s1+'.nanoGalleryContainer > .nanoGalleryThumbnailContainer .labelImageTitle  { color:'+cs.thumbnail.titleColor+' !important; Text-Shadow:'+cs.thumbnail.titleShadow+' !important; }'+'\n';
7249    s+=s1+'.nanoGalleryContainer > .nanoGalleryThumbnailContainer .labelImageTitle:before { color:'+cs.thumbnail.titleColor+' !important; Text-Shadow:'+cs.thumbnail.titleShadow+' !important; }'+'\n';
7250    s+=s1+'.nanoGalleryContainer > .nanoGalleryThumbnailContainer .labelFolderTitle { color:'+cs.thumbnail.titleColor+' !important; Text-Shadow:'+cs.thumbnail.titleShadow+' !important; }'+'\n';
7251    var c=cs.thumbnail.labelBackground;
7252    if( c == 'transparent' ) { c=''; }
7253    s+=s1+'.nanoGalleryContainer > .nanoGalleryThumbnailContainer .labelFolderTitle > span { background-color:'+cs.thumbnail.titleColor+' !important; color:'+c+' !important; }'+'\n';
7254    s+=s1+'.nanoGalleryContainer > .nanoGalleryThumbnailContainer .labelFolderTitle:before { color:'+cs.thumbnail.titleColor+' !important; Text-Shadow:'+cs.thumbnail.titleShadow+' !important; }'+'\n';
7255    s+=s1+'.nanoGalleryContainer > .nanoGalleryThumbnailContainer .labelDescription { color:'+cs.thumbnail.descriptionColor+' !important; Text-Shadow:'+cs.thumbnail.descriptionShadow+' !important; }'+'\n';
7256    s+=s1+'.nanoGalleryContainer > .nanoGalleryThumbnailContainer .labelDescription > span { background-color:'+cs.thumbnail.titleColor+' !important; color:'+c+' !important; }'+'\n';
7257    // s+='.' + g_colorSchemeLabel +'.fullpage { background:'+gO.galleryFullpageBgColor+' !important; }'+'\n';
7258  
7259    // gallery fullpage background color
7260    var gbg='nanogallery_galleryfullpage_bgcolor_'+g_baseEltID;
7261    s+='.' + gbg +'.fullpage { background:'+gO.galleryFullpageBgColor+' !important; }'+'\n';
7262
7263    jQuery('head').append('<style>'+s+'</style>');
7264    jQuery(element).addClass(g_colorSchemeLabel);
7265    jQuery(element).addClass(gbg);
7266
7267  };
7268  
7269  // ##### VIEWER COLOR SCHEME #####
7270  function SetColorSchemeViewer( element ) {
7271
7272    var cs=null;
7273    switch(toType(gO.colorSchemeViewer)) {
7274      case 'object':    // user custom color scheme object 
7275        cs=g_colorSchemeViewer_default;
7276        jQuery.extend(true,cs,gO.colorSchemeViewer);
7277        g_colorSchemeLabel='nanogallery_colorschemeviewer_custom';
7278        break;
7279      case 'string':    // name of an internal defined color scheme
7280        switch( gO.colorSchemeViewer ) {
7281          case 'none':
7282            return;
7283            break;
7284          case 'light':
7285            cs=g_colorSchemeViewer_light;
7286            g_colorSchemeLabel='nanogallery_colorschemeviewer_light';
7287            break;
7288          case 'darkRed':
7289            cs=g_colorSchemeViewer_darkRed;
7290            g_colorSchemeLabel='nanogallery_colorschemeviewer_darkred';
7291            break;
7292          case 'darkGreen':
7293            cs=g_colorSchemeViewer_darkGreen;
7294            g_colorSchemeLabel='nanogallery_colorschemeviewer_darkgreen';
7295            break;
7296          case 'darkBlue':
7297            cs=g_colorSchemeViewer_darkBlue;
7298            g_colorSchemeLabel='nanogallery_colorschemeviewer_darkblue';
7299            break;
7300          case 'darkOrange':
7301            cs=g_colorSchemeViewer_darkOrange;
7302            g_colorSchemeLabel='nanogallery_colorschemeviewer_darkorange';
7303            break;
7304          case 'dark':
7305            cs=g_colorSchemeViewer_dark;
7306            g_colorSchemeLabel='nanogallery_colorschemeviewer_dark';
7307            break;
7308          case 'default':
7309          default:
7310            cs=g_colorSchemeViewer_default;
7311            g_colorSchemeLabel='nanogallery_colorschemeviewer_default';
7312        }
7313        break;
7314      default:
7315        nanoAlert('Error in colorSchemeViewer parameter.');
7316        return;
7317    }
7318
7319    
7320    //var s1='.nanogallery_theme_'+gO.theme+' ';
7321    var s1='.' + g_colorSchemeLabel + ' ';
7322    var s=s1+'.nanoGalleryViewer { background:'+cs.background+' !important; }'+'\n';
7323    //s+=s1+'.nanoGalleryViewer { background:'+cs.viewer.background+'; color:'+cs.viewer.color+'; }'+'\n';
7324    s+=s1+'.nanoGalleryViewer .content img { border:'+cs.imageBorder+' !important; box-shadow:'+cs.imageBoxShadow+' !important; }'+'\n';
7325    s+=s1+'.nanoGalleryViewer .toolbar { background:'+cs.barBackground+' !important; border:'+cs.barBorder+' !important; color:'+cs.barColor+' !important; }'+'\n';
7326    s+=s1+'.nanoGalleryViewer .toolbar .previousButton:after { color:'+cs.barColor+' !important; }'+'\n';
7327    s+=s1+'.nanoGalleryViewer .toolbar .nextButton:after { color:'+cs.barColor+' !important; }'+'\n';
7328    s+=s1+'.nanoGalleryViewer .toolbar .closeButton:after { color:'+cs.barColor+' !important; }'+'\n';
7329    //s+=s1+'.nanoGalleryViewer .toolbar .label { background:'+cs.barBackground+'; }'+'\n';
7330    s+=s1+'.nanoGalleryViewer .toolbar .label .title { color:'+cs.barColor+' !important; }'+'\n';
7331    s+=s1+'.nanoGalleryViewer .toolbar .label .description { color:'+cs.barDescriptionColor+' !important; }'+'\n';
7332    jQuery('head').append('<style>'+s+'</style>');
7333    jQuery(element).addClass(g_colorSchemeLabel);
7334  };
7335
7336
7337  
7338// #################
7339// ##### TOOLS #####
7340// #################
7341  
7342  // Display a message
7343  function nanoAlert( msg, verbose ) {
7344    nanoConsoleLog(msg);
7345    if( $gE.conConsole != null ) {
7346      $gE.conConsole.css({visibility:'visible', height:'auto'});
7347      if( verbose == false ) {
7348        $gE.conConsole.append('<p>'+ msg + '</p>');
7349      }
7350      else {
7351        $gE.conConsole.append('<p>nanoGALLERY: '+msg+ ' ['+g_baseEltID+']</p>');
7352      }
7353      //alert('nanoGALLERY: ' + msg);
7354    }
7355  };
7356  
7357  // write to console log
7358  function nanoConsoleLog( msg ) {
7359    if (window.console) { console.log('nanoGALLERY: ' + msg + ' ['+g_baseEltID+']'); }
7360  };
7361  
7362  // get viewport coordinates and size
7363  function getViewport() {
7364    var $win = jQuery(window);
7365    return {
7366      l: $win.scrollLeft(),
7367      t: $win.scrollTop(),
7368      w: $win.width(),
7369      h: $win.height()
7370    }
7371  }
7372
7373
7374  function inViewport( $elt, threshold ) {
7375    var wp=getViewport(),
7376    eltOS=$elt.offset(),
7377    th=$elt.outerHeight(true),
7378    tw=$elt.outerWidth(true);
7379    if( eltOS.top >= (wp.t-threshold) 
7380      && (eltOS.top+th) <= (wp.t+wp.h+threshold)
7381      && eltOS.left >= (wp.l-threshold) 
7382      && (eltOS.left+tw) <= (wp.l+wp.w+threshold) ) {
7383      return true;
7384    }
7385    else {
7386      return false;
7387    }
7388  }
7389
7390  function inViewportVert( $elt, threshold ) {
7391    var wp=getViewport(),
7392    eltOS=$elt.offset(),
7393    th=$elt.outerHeight(true),
7394    tw=$elt.outerWidth(true);
7395
7396    if( wp.t == 0 && (eltOS.top) <= (wp.t+wp.h ) ) { return true; }
7397
7398    if( eltOS.top >= (wp.t) 
7399      && (eltOS.top+th) <= (wp.t+wp.h-threshold) ) {
7400        return true;
7401    }
7402    else {
7403      return false;
7404    }
7405  }
7406
7407  
7408  // set z-index to display element on top of all others
7409  function setElementOnTop( start, elt ) {
7410    var highest_index = 0;
7411    if( start=='' ) { start= '*'; }
7412    jQuery(start).each(function() {
7413      var cur = parseInt(jQuery(this).css('z-index'));
7414      highest_index = cur > highest_index ? cur : highest_index;
7415    });
7416    highest_index++;
7417    jQuery(elt).css('z-index',highest_index);
7418  };
7419
7420  // set z-index to display 2 elements on top of all others
7421  function set2ElementsOnTop( start, elt1, elt2 ) {
7422    var highest_index = 0;
7423    if( start=='' ) { start= '*'; }
7424    jQuery(start).each(function() {
7425      var cur = parseInt(jQuery(this).css('z-index'));
7426      highest_index = cur > highest_index ? cur : highest_index;
7427    });
7428    highest_index++;
7429    jQuery(elt2).css('z-index',highest_index+1);
7430    jQuery(elt1).css('z-index',highest_index);
7431  };
7432
7433  
7434  // return the real type of the object
7435  var toType = function( obj ) {
7436    // by Angus Croll - http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/
7437    return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
7438  };
7439
7440  
7441  // return true if current jQuery version match the minimum required
7442  function jQueryMinVersion( version ) {
7443    var $vrs = window.jQuery.fn.jquery.split('.'),
7444    min = version.split('.');
7445    for (var i=0, len=$vrs.length; i<len; i++) {
7446      if (min[i] && (+$vrs[i]) < (+min[i])) {
7447        return false;
7448      }
7449    }
7450    return true;
7451  };
7452  
7453  
7454  //+ Jonas Raoni Soares Silva
7455  //@ http://jsfromhell.com/array/shuffle [v1.0]
7456  function AreaShuffle(o){ //v1.0
7457      for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
7458      return o;
7459  };
7460 
7461  
7462  // color lighter or darker
7463  // found on http://stackoverflow.com/questions/1507931/generate-lighter-darker-color-in-css-using-javascript/5747818#5747818
7464  // Ratio is between 0 and 1
7465  var changeColor = function( color, ratio, darker ) {
7466    // Trim trailing/leading whitespace
7467    color = color.replace(/^\s*|\s*$/, '');
7468
7469    // Expand three-digit hex
7470    color = color.replace(
7471      /^#?([a-f0-9])([a-f0-9])([a-f0-9])$/i,
7472      '#$1$1$2$2$3$3'
7473    );
7474
7475    // Calculate ratio
7476    var difference = Math.round(ratio * 256) * (darker ? -1 : 1),
7477      // Determine if input is RGB(A)
7478      rgb = color.match(new RegExp('^rgba?\\(\\s*' +
7479        '(\\d|[1-9]\\d|1\\d{2}|2[0-4][0-9]|25[0-5])' +
7480        '\\s*,\\s*' +
7481        '(\\d|[1-9]\\d|1\\d{2}|2[0-4][0-9]|25[0-5])' +
7482        '\\s*,\\s*' +
7483        '(\\d|[1-9]\\d|1\\d{2}|2[0-4][0-9]|25[0-5])' +
7484        '(?:\\s*,\\s*' +
7485        '(0|1|0?\\.\\d+))?' +
7486        '\\s*\\)$'
7487      , 'i')),
7488      alpha = !!rgb && rgb[4] != null ? rgb[4] : null,
7489
7490      // Convert hex to decimal
7491      decimal = !!rgb? [rgb[1], rgb[2], rgb[3]] : color.replace(
7492        /^#?([a-f0-9][a-f0-9])([a-f0-9][a-f0-9])([a-f0-9][a-f0-9])/i,
7493        function() {
7494          return parseInt(arguments[1], 16) + ',' +
7495            parseInt(arguments[2], 16) + ',' +
7496            parseInt(arguments[3], 16);
7497        }
7498      ).split(/,/),
7499      returnValue;
7500
7501    // Return RGB(A)
7502    return !!rgb ?
7503      'rgb' + (alpha !== null ? 'a' : '') + '(' +
7504        Math[darker ? 'max' : 'min'](
7505          parseInt(decimal[0], 10) + difference, darker ? 0 : 255
7506        ) + ', ' +
7507        Math[darker ? 'max' : 'min'](
7508          parseInt(decimal[1], 10) + difference, darker ? 0 : 255
7509        ) + ', ' +
7510        Math[darker ? 'max' : 'min'](
7511          parseInt(decimal[2], 10) + difference, darker ? 0 : 255
7512        ) +
7513        (alpha !== null ? ', ' + alpha : '') +
7514        ')' :
7515      // Return hex
7516      [
7517        '#',
7518        pad(Math[darker ? 'max' : 'min'](
7519          parseInt(decimal[0], 10) + difference, darker ? 0 : 255
7520        ).toString(16), 2),
7521        pad(Math[darker ? 'max' : 'min'](
7522          parseInt(decimal[1], 10) + difference, darker ? 0 : 255
7523        ).toString(16), 2),
7524        pad(Math[darker ? 'max' : 'min'](
7525          parseInt(decimal[2], 10) + difference, darker ? 0 : 255
7526        ).toString(16), 2)
7527      ].join('');
7528  };
7529  var lighterColor = function(color, ratio) {
7530    return changeColor(color, ratio, false);
7531  };
7532  var darkerColor = function(color, ratio) {
7533    return changeColor(color, ratio, true);
7534  };
7535  var pad = function(num, totalChars) {
7536    var pad = '0';
7537    num = num + '';
7538    while (num.length < totalChars) {
7539      num = pad + num;
7540    }
7541    return num;
7542  };
7543  
7544}
7545
7546
7547
7548
7549
7550/*!
7551 * jQuery Color Animations v2.1.2
7552 * https://github.com/jquery/jquery-color
7553 *
7554 * Copyright 2013 jQuery Foundation and other contributors
7555 * Released under the MIT license.
7556 * http://jquery.org/license
7557 *
7558 * Date: Wed Jan 16 08:47:09 2013 -0600
7559 */
7560(function( jQuery, undefined ) {
7561
7562  var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",
7563
7564  // plusequals test for += 100 -= 100
7565  rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
7566  // a set of RE's that can match strings and generate color tuples.
7567  stringParsers = [{
7568      re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
7569      parse: function( execResult ) {
7570        return [
7571          execResult[ 1 ],
7572          execResult[ 2 ],
7573          execResult[ 3 ],
7574          execResult[ 4 ]
7575        ];
7576      }
7577    }, {
vendor: 18,040 bytes, lines 7578-8266
7578      re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
7579      parse: function( execResult ) {
7580        return [
7581          execResult[ 1 ] * 2.55,
7582          execResult[ 2 ] * 2.55,
7583          execResult[ 3 ] * 2.55,
7584          execResult[ 4 ]
7585        ];
7586      }
7587    }, {
7588      // this regex ignores A-F because it's compared against an already lowercased string
7589      re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
7590      parse: function( execResult ) {
7591        return [
7592          parseInt( execResult[ 1 ], 16 ),
7593          parseInt( execResult[ 2 ], 16 ),
7594          parseInt( execResult[ 3 ], 16 )
7595        ];
7596      }
7597    }, {
7598      // this regex ignores A-F because it's compared against an already lowercased string
7599      re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
7600      parse: function( execResult ) {
7601        return [
7602          parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
7603          parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
7604          parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
7605        ];
7606      }
7607    }, {
7608      re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
7609      space: "hsla",
7610      parse: function( execResult ) {
7611        return [
7612          execResult[ 1 ],
7613          execResult[ 2 ] / 100,
7614          execResult[ 3 ] / 100,
7615          execResult[ 4 ]
7616        ];
7617      }
7618    }],
7619
7620  // jQuery.Color( )
7621  color = jQuery.Color = function( color, green, blue, alpha ) {
7622    return new jQuery.Color.fn.parse( color, green, blue, alpha );
7623  },
7624  spaces = {
7625    rgba: {
7626      props: {
7627        red: {
7628          idx: 0,
7629          type: "byte"
7630        },
7631        green: {
7632          idx: 1,
7633          type: "byte"
7634        },
7635        blue: {
7636          idx: 2,
7637          type: "byte"
7638        }
7639      }
7640    },
7641
7642    hsla: {
7643      props: {
7644        hue: {
7645          idx: 0,
7646          type: "degrees"
7647        },
7648        saturation: {
7649          idx: 1,
7650          type: "percent"
7651        },
7652        lightness: {
7653          idx: 2,
7654          type: "percent"
7655        }
7656      }
7657    }
7658  },
7659  propTypes = {
7660    "byte": {
7661      floor: true,
7662      max: 255
7663    },
7664    "percent": {
7665      max: 1
7666    },
7667    "degrees": {
7668      mod: 360,
7669      floor: true
7670    }
7671  },
7672  support = color.support = {},
7673
7674  // element for support tests
7675  supportElem = jQuery( "<p>" )[ 0 ],
7676
7677  // colors = jQuery.Color.names
7678  colors,
7679
7680  // local aliases of functions called often
7681  each = jQuery.each;
7682
7683// determine rgba support immediately
7684supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
7685support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
7686
7687// define cache name and alpha properties
7688// for rgba and hsla spaces
7689each( spaces, function( spaceName, space ) {
7690  space.cache = "_" + spaceName;
7691  space.props.alpha = {
7692    idx: 3,
7693    type: "percent",
7694    def: 1
7695  };
7696});
7697
7698function clamp( value, prop, allowEmpty ) {
7699  var type = propTypes[ prop.type ] || {};
7700
7701  if ( value == null ) {
7702    return (allowEmpty || !prop.def) ? null : prop.def;
7703  }
7704
7705  // ~~ is an short way of doing floor for positive numbers
7706  value = type.floor ? ~~value : parseFloat( value );
7707
7708  // IE will pass in empty strings as value for alpha,
7709  // which will hit this case
7710  if ( isNaN( value ) ) {
7711    return prop.def;
7712  }
7713
7714  if ( type.mod ) {
7715    // we add mod before modding to make sure that negatives values
7716    // get converted properly: -10 -> 350
7717    return (value + type.mod) % type.mod;
7718  }
7719
7720  // for now all property types without mod have min and max
7721  return 0 > value ? 0 : type.max < value ? type.max : value;
7722}
7723
7724function stringParse( string ) {
7725  var inst = color(),
7726    rgba = inst._rgba = [];
7727
7728  string = string.toLowerCase();
7729
7730  each( stringParsers, function( i, parser ) {
7731    var parsed,
7732      match = parser.re.exec( string ),
7733      values = match && parser.parse( match ),
7734      spaceName = parser.space || "rgba";
7735
7736    if ( values ) {
7737      parsed = inst[ spaceName ]( values );
7738
7739      // if this was an rgba parse the assignment might happen twice
7740      // oh well....
7741      inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
7742      rgba = inst._rgba = parsed._rgba;
7743
7744      // exit each( stringParsers ) here because we matched
7745      return false;
7746    }
7747  });
7748
7749  // Found a stringParser that handled it
7750  if ( rgba.length ) {
7751
7752    // if this came from a parsed string, force "transparent" when alpha is 0
7753    // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
7754    if ( rgba.join() === "0,0,0,0" ) {
7755      jQuery.extend( rgba, colors.transparent );
7756    }
7757    return inst;
7758  }
7759
7760  // named colors
7761  return colors[ string ];
7762}
7763
7764color.fn = jQuery.extend( color.prototype, {
7765  parse: function( red, green, blue, alpha ) {
7766    if ( red === undefined ) {
7767      this._rgba = [ null, null, null, null ];
7768      return this;
7769    }
7770    if ( red.jquery || red.nodeType ) {
7771      red = jQuery( red ).css( green );
7772      green = undefined;
7773    }
7774
7775    var inst = this,
7776      type = jQuery.type( red ),
7777      rgba = this._rgba = [];
7778
7779    // more than 1 argument specified - assume ( red, green, blue, alpha )
7780    if ( green !== undefined ) {
7781      red = [ red, green, blue, alpha ];
7782      type = "array";
7783    }
7784
7785    if ( type === "string" ) {
7786      return this.parse( stringParse( red ) || colors._default );
7787    }
7788
7789    if ( type === "array" ) {
7790      each( spaces.rgba.props, function( key, prop ) {
7791        rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
7792      });
7793      return this;
7794    }
7795
7796    if ( type === "object" ) {
7797      if ( red instanceof color ) {
7798        each( spaces, function( spaceName, space ) {
7799          if ( red[ space.cache ] ) {
7800            inst[ space.cache ] = red[ space.cache ].slice();
7801          }
7802        });
7803      } else {
7804        each( spaces, function( spaceName, space ) {
7805          var cache = space.cache;
7806          each( space.props, function( key, prop ) {
7807
7808            // if the cache doesn't exist, and we know how to convert
7809            if ( !inst[ cache ] && space.to ) {
7810
7811              // if the value was null, we don't need to copy it
7812              // if the key was alpha, we don't need to copy it either
7813              if ( key === "alpha" || red[ key ] == null ) {
7814                return;
7815              }
7816              inst[ cache ] = space.to( inst._rgba );
7817            }
7818
7819            // this is the only case where we allow nulls for ALL properties.
7820            // call clamp with alwaysAllowEmpty
7821            inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
7822          });
7823
7824          // everything defined but alpha?
7825          if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
7826            // use the default of 1
7827            inst[ cache ][ 3 ] = 1;
7828            if ( space.from ) {
7829              inst._rgba = space.from( inst[ cache ] );
7830            }
7831          }
7832        });
7833      }
7834      return this;
7835    }
7836  },
7837  is: function( compare ) {
7838    var is = color( compare ),
7839      same = true,
7840      inst = this;
7841
7842    each( spaces, function( _, space ) {
7843      var localCache,
7844        isCache = is[ space.cache ];
7845      if (isCache) {
7846        localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
7847        each( space.props, function( _, prop ) {
7848          if ( isCache[ prop.idx ] != null ) {
7849            same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
7850            return same;
7851          }
7852        });
7853      }
7854      return same;
7855    });
7856    return same;
7857  },
7858  _space: function() {
7859    var used = [],
7860      inst = this;
7861    each( spaces, function( spaceName, space ) {
7862      if ( inst[ space.cache ] ) {
7863        used.push( spaceName );
7864      }
7865    });
7866    return used.pop();
7867  },
7868  transition: function( other, distance ) {
7869    var end = color( other ),
7870      spaceName = end._space(),
7871      space = spaces[ spaceName ],
7872      startColor = this.alpha() === 0 ? color( "transparent" ) : this,
7873      start = startColor[ space.cache ] || space.to( startColor._rgba ),
7874      result = start.slice();
7875
7876    end = end[ space.cache ];
7877    each( space.props, function( key, prop ) {
7878      var index = prop.idx,
7879        startValue = start[ index ],
7880        endValue = end[ index ],
7881        type = propTypes[ prop.type ] || {};
7882
7883      // if null, don't override start value
7884      if ( endValue === null ) {
7885        return;
7886      }
7887      // if null - use end
7888      if ( startValue === null ) {
7889        result[ index ] = endValue;
7890      } else {
7891        if ( type.mod ) {
7892          if ( endValue - startValue > type.mod / 2 ) {
7893            startValue += type.mod;
7894          } else if ( startValue - endValue > type.mod / 2 ) {
7895            startValue -= type.mod;
7896          }
7897        }
7898        result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
7899      }
7900    });
7901    return this[ spaceName ]( result );
7902  },
7903  blend: function( opaque ) {
7904    // if we are already opaque - return ourself
7905    if ( this._rgba[ 3 ] === 1 ) {
7906      return this;
7907    }
7908
7909    var rgb = this._rgba.slice(),
7910      a = rgb.pop(),
7911      blend = color( opaque )._rgba;
7912
7913    return color( jQuery.map( rgb, function( v, i ) {
7914      return ( 1 - a ) * blend[ i ] + a * v;
7915    }));
7916  },
7917  toRgbaString: function() {
7918    var prefix = "rgba(",
7919      rgba = jQuery.map( this._rgba, function( v, i ) {
7920        return v == null ? ( i > 2 ? 1 : 0 ) : v;
7921      });
7922
7923    if ( rgba[ 3 ] === 1 ) {
7924      rgba.pop();
7925      prefix = "rgb(";
7926    }
7927
7928    return prefix + rgba.join() + ")";
7929  },
7930  toHslaString: function() {
7931    var prefix = "hsla(",
7932      hsla = jQuery.map( this.hsla(), function( v, i ) {
7933        if ( v == null ) {
7934          v = i > 2 ? 1 : 0;
7935        }
7936
7937        // catch 1 and 2
7938        if ( i && i < 3 ) {
7939          v = Math.round( v * 100 ) + "%";
7940        }
7941        return v;
7942      });
7943
7944    if ( hsla[ 3 ] === 1 ) {
7945      hsla.pop();
7946      prefix = "hsl(";
7947    }
7948    return prefix + hsla.join() + ")";
7949  },
7950  toHexString: function( includeAlpha ) {
7951    var rgba = this._rgba.slice(),
7952      alpha = rgba.pop();
7953
7954    if ( includeAlpha ) {
7955      rgba.push( ~~( alpha * 255 ) );
7956    }
7957
7958    return "#" + jQuery.map( rgba, function( v ) {
7959
7960      // default to 0 when nulls exist
7961      v = ( v || 0 ).toString( 16 );
7962      return v.length === 1 ? "0" + v : v;
7963    }).join("");
7964  },
7965  toString: function() {
7966    return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
7967  }
7968});
7969color.fn.parse.prototype = color.fn;
7970
7971// hsla conversions adapted from:
7972// https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
7973
7974function hue2rgb( p, q, h ) {
7975  h = ( h + 1 ) % 1;
7976  if ( h * 6 < 1 ) {
7977    return p + (q - p) * h * 6;
7978  }
7979  if ( h * 2 < 1) {
7980    return q;
7981  }
7982  if ( h * 3 < 2 ) {
7983    return p + (q - p) * ((2/3) - h) * 6;
7984  }
7985  return p;
7986}
7987
7988spaces.hsla.to = function ( rgba ) {
7989  if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
7990    return [ null, null, null, rgba[ 3 ] ];
7991  }
7992  var r = rgba[ 0 ] / 255,
7993    g = rgba[ 1 ] / 255,
7994    b = rgba[ 2 ] / 255,
7995    a = rgba[ 3 ],
7996    max = Math.max( r, g, b ),
7997    min = Math.min( r, g, b ),
7998    diff = max - min,
7999    add = max + min,
8000    l = add * 0.5,
8001    h, s;
8002
8003  if ( min === max ) {
8004    h = 0;
8005  } else if ( r === max ) {
8006    h = ( 60 * ( g - b ) / diff ) + 360;
8007  } else if ( g === max ) {
8008    h = ( 60 * ( b - r ) / diff ) + 120;
8009  } else {
8010    h = ( 60 * ( r - g ) / diff ) + 240;
8011  }
8012
8013  // chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
8014  // otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
8015  if ( diff === 0 ) {
8016    s = 0;
8017  } else if ( l <= 0.5 ) {
8018    s = diff / add;
8019  } else {
8020    s = diff / ( 2 - add );
8021  }
8022  return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
8023};
8024
8025spaces.hsla.from = function ( hsla ) {
8026  if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
8027    return [ null, null, null, hsla[ 3 ] ];
8028  }
8029  var h = hsla[ 0 ] / 360,
8030    s = hsla[ 1 ],
8031    l = hsla[ 2 ],
8032    a = hsla[ 3 ],
8033    q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
8034    p = 2 * l - q;
8035
8036  return [
8037    Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
8038    Math.round( hue2rgb( p, q, h ) * 255 ),
8039    Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
8040    a
8041  ];
8042};
8043
8044
8045each( spaces, function( spaceName, space ) {
8046  var props = space.props,
8047    cache = space.cache,
8048    to = space.to,
8049    from = space.from;
8050
8051  // makes rgba() and hsla()
8052  color.fn[ spaceName ] = function( value ) {
8053
8054    // generate a cache for this space if it doesn't exist
8055    if ( to && !this[ cache ] ) {
8056      this[ cache ] = to( this._rgba );
8057    }
8058    if ( value === undefined ) {
8059      return this[ cache ].slice();
8060    }
8061
8062    var ret,
8063      type = jQuery.type( value ),
8064      arr = ( type === "array" || type === "object" ) ? value : arguments,
8065      local = this[ cache ].slice();
8066
8067    each( props, function( key, prop ) {
8068      var val = arr[ type === "object" ? key : prop.idx ];
8069      if ( val == null ) {
8070        val = local[ prop.idx ];
8071      }
8072      local[ prop.idx ] = clamp( val, prop );
8073    });
8074
8075    if ( from ) {
8076      ret = color( from( local ) );
8077      ret[ cache ] = local;
8078      return ret;
8079    } else {
8080      return color( local );
8081    }
8082  };
8083
8084  // makes red() green() blue() alpha() hue() saturation() lightness()
8085  each( props, function( key, prop ) {
8086    // alpha is included in more than one space
8087    if ( color.fn[ key ] ) {
8088      return;
8089    }
8090    color.fn[ key ] = function( value ) {
8091      var vtype = jQuery.type( value ),
8092        fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
8093        local = this[ fn ](),
8094        cur = local[ prop.idx ],
8095        match;
8096
8097      if ( vtype === "undefined" ) {
8098        return cur;
8099      }
8100
8101      if ( vtype === "function" ) {
8102        value = value.call( this, cur );
8103        vtype = jQuery.type( value );
8104      }
8105      if ( value == null && prop.empty ) {
8106        return this;
8107      }
8108      if ( vtype === "string" ) {
8109        match = rplusequals.exec( value );
8110        if ( match ) {
8111          value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
8112        }
8113      }
8114      local[ prop.idx ] = value;
8115      return this[ fn ]( local );
8116    };
8117  });
8118});
8119
8120// add cssHook and .fx.step function for each named hook.
8121// accept a space separated string of properties
8122color.hook = function( hook ) {
8123  var hooks = hook.split( " " );
8124  each( hooks, function( i, hook ) {
8125    jQuery.cssHooks[ hook ] = {
8126      set: function( elem, value ) {
8127        var parsed, curElem,
8128          backgroundColor = "";
8129
8130        if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) {
8131          value = color( parsed || value );
8132          if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
8133            curElem = hook === "backgroundColor" ? elem.parentNode : elem;
8134            while (
8135              (backgroundColor === "" || backgroundColor === "transparent") &&
8136              curElem && curElem.style
8137            ) {
8138              try {
8139                backgroundColor = jQuery.css( curElem, "backgroundColor" );
8140                curElem = curElem.parentNode;
8141              } catch ( e ) {
8142              }
8143            }
8144
8145            value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
8146              backgroundColor :
8147              "_default" );
8148          }
8149
8150          value = value.toRgbaString();
8151        }
8152        try {
8153          elem.style[ hook ] = value;
8154        } catch( e ) {
8155          // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
8156        }
8157      }
8158    };
8159    jQuery.fx.step[ hook ] = function( fx ) {
8160      if ( !fx.colorInit ) {
8161        fx.start = color( fx.elem, hook );
8162        fx.end = color( fx.end );
8163        fx.colorInit = true;
8164      }
8165      jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
8166    };
8167  });
8168
8169};
8170
8171color.hook( stepHooks );
8172
8173jQuery.cssHooks.borderColor = {
8174  expand: function( value ) {
8175    var expanded = {};
8176
8177    each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
8178      expanded[ "border" + part + "Color" ] = value;
8179    });
8180    return expanded;
8181  }
8182};
8183
8184// Basic color names only.
8185// Usage of any of the other color names requires adding yourself or including
8186// jquery.color.svg-names.js.
8187colors = jQuery.Color.names = {
8188  // 4.1. Basic color keywords
8189  aqua: "#00ffff",
8190  black: "#000000",
8191  blue: "#0000ff",
8192  fuchsia: "#ff00ff",
8193  gray: "#808080",
8194  green: "#008000",
8195  lime: "#00ff00",
8196  maroon: "#800000",
8197  navy: "#000080",
8198  olive: "#808000",
8199  purple: "#800080",
8200  red: "#ff0000",
8201  silver: "#c0c0c0",
8202  teal: "#008080",
8203  white: "#ffffff",
8204  yellow: "#ffff00",
8205
8206  // 4.2.3. "transparent" color keyword
8207  transparent: [ null, null, null, 0 ],
8208
8209  _default: "#ffffff"
8210};
8211
8212})( jQuery );
8213
8214
8215
8216/*!
8217 * imagesLoaded PACKAGED v3.1.8
8218 * JavaScript is all like "You images are done yet or what?"
8219 * MIT License
8220 */
8221
8222
8223/*!
8224 * EventEmitter v4.2.6 - git.io/ee
8225 * Oliver Caldwell
8226 * MIT license
8227 * @preserve
8228 */
8229
8230(function () {
8231	
8232
8233	/**
8234	 * Class for managing events.
8235	 * Can be extended to provide event functionality in other classes.
8236	 *
8237	 * @class ngEventEmitter Manages event registering and emitting.
8238	 */
8239	function ngEventEmitter() {}
8240
8241	// Shortcuts to improve speed and size
8242	var proto = ngEventEmitter.prototype;
8243	var exports = this;
8244	var originalGlobalValue = exports.ngEventEmitter;
8245
8246	/**
8247	 * Finds the index of the listener for the event in it's storage array.
8248	 *
8249	 * @param {Function[]} listeners Array of listeners to search through.
8250	 * @param {Function} listener Method to look for.
8251	 * @return {Number} Index of the specified listener, -1 if not found
8252	 * @api private
8253	 */
8254	function indexOfListener(listeners, listener) {
8255		var i = listeners.length;
8256		while (i--) {
8257			if (listeners[i].listener === listener) {
8258				return i;
8259			}
8260		}
8261
8262		return -1;
8263	}
8264
8265	/**
8266	 * Alias a method while keeping the context correct, to allow for over
vendor: 11,346 bytes, lines 8266-8574
8266writing of target method.
8267	 *
8268	 * @param {String} name The name of the target method.
8269	 * @return {Function} The aliased method
8270	 * @api private
8271	 */
8272	function alias(name) {
8273		return function aliasClosure() {
8274			return this[name].apply(this, arguments);
8275		};
8276	}
8277
8278	/**
8279	 * Returns the listener array for the specified event.
8280	 * Will initialise the event object and listener arrays if required.
8281	 * Will return an object if you use a regex search. The object contains keys for each matched event. So /ba[rz]/ might return an object containing bar and baz. But only if you have either defined them with defineEvent or added some listeners to them.
8282	 * Each property in the object response is an array of listener functions.
8283	 *
8284	 * @param {String|RegExp} evt Name of the event to return the listeners from.
8285	 * @return {Function[]|Object} All listener functions for the event.
8286	 */
8287	proto.getListeners = function getListeners(evt) {
8288		var events = this._getEvents();
8289		var response;
8290		var key;
8291
8292		// Return a concatenated array of all matching events if
8293		// the selector is a regular expression.
8294		if (typeof evt === 'object') {
8295			response = {};
8296			for (key in events) {
8297				if (events.hasOwnProperty(key) && evt.test(key)) {
8298					response[key] = events[key];
8299				}
8300			}
8301		}
8302		else {
8303			response = events[evt] || (events[evt] = []);
8304		}
8305
8306		return response;
8307	};
8308
8309	/**
8310	 * Takes a list of listener objects and flattens it into a list of listener functions.
8311	 *
8312	 * @param {Object[]} listeners Raw listener objects.
8313	 * @return {Function[]} Just the listener functions.
8314	 */
8315	proto.flattenListeners = function flattenListeners(listeners) {
8316		var flatListeners = [];
8317		var i;
8318
8319		for (i = 0; i < listeners.length; i += 1) {
8320			flatListeners.push(listeners[i].listener);
8321		}
8322
8323		return flatListeners;
8324	};
8325
8326	/**
8327	 * Fetches the requested listeners via getListeners but will always return the results inside an object. This is mainly for internal use but others may find it useful.
8328	 *
8329	 * @param {String|RegExp} evt Name of the event to return the listeners from.
8330	 * @return {Object} All listener functions for an event in an object.
8331	 */
8332	proto.getListenersAsObject = function getListenersAsObject(evt) {
8333		var listeners = this.getListeners(evt);
8334		var response;
8335
8336		if (listeners instanceof Array) {
8337			response = {};
8338			response[evt] = listeners;
8339		}
8340
8341		return response || listeners;
8342	};
8343
8344	/**
8345	 * Adds a listener function to the specified event.
8346	 * The listener will not be added if it is a duplicate.
8347	 * If the listener returns true then it will be removed after it is called.
8348	 * If you pass a regular expression as the event name then the listener will be added to all events that match it.
8349	 *
8350	 * @param {String|RegExp} evt Name of the event to attach the listener to.
8351	 * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
8352	 * @return {Object} Current instance of EventEmitter for chaining.
8353	 */
8354	proto.addListener = function addListener(evt, listener) {
8355		var listeners = this.getListenersAsObject(evt);
8356		var listenerIsWrapped = typeof listener === 'object';
8357		var key;
8358
8359		for (key in listeners) {
8360			if (listeners.hasOwnProperty(key) && indexOfListener(listeners[key], listener) === -1) {
8361				listeners[key].push(listenerIsWrapped ? listener : {
8362					listener: listener,
8363					once: false
8364				});
8365			}
8366		}
8367
8368		return this;
8369	};
8370
8371	/**
8372	 * Alias of addListener
8373	 */
8374	proto.on = alias('addListener');
8375
8376	/**
8377	 * Semi-alias of addListener. It will add a listener that will be
8378	 * automatically removed after it's first execution.
8379	 *
8380	 * @param {String|RegExp} evt Name of the event to attach the listener to.
8381	 * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
8382	 * @return {Object} Current instance of EventEmitter for chaining.
8383	 */
8384	proto.addOnceListener = function addOnceListener(evt, listener) {
8385		return this.addListener(evt, {
8386			listener: listener,
8387			once: true
8388		});
8389	};
8390
8391	/**
8392	 * Alias of addOnceListener.
8393	 */
8394	proto.once = alias('addOnceListener');
8395
8396	/**
8397	 * Defines an event name. This is required if you want to use a regex to add a listener to multiple events at once. If you don't do this then how do you expect it to know what event to add to? Should it just add to every possible match for a regex? No. That is scary and bad.
8398	 * You need to tell it what event names should be matched by a regex.
8399	 *
8400	 * @param {String} evt Name of the event to create.
8401	 * @return {Object} Current instance of EventEmitter for chaining.
8402	 */
8403	proto.defineEvent = function defineEvent(evt) {
8404		this.getListeners(evt);
8405		return this;
8406	};
8407
8408	/**
8409	 * Uses defineEvent to define multiple events.
8410	 *
8411	 * @param {String[]} evts An array of event names to define.
8412	 * @return {Object} Current instance of EventEmitter for chaining.
8413	 */
8414	proto.defineEvents = function defineEvents(evts) {
8415		for (var i = 0; i < evts.length; i += 1) {
8416			this.defineEvent(evts[i]);
8417		}
8418		return this;
8419	};
8420
8421	/**
8422	 * Removes a listener function from the specified event.
8423	 * When passed a regular expression as the event name, it will remove the listener from all events that match it.
8424	 *
8425	 * @param {String|RegExp} evt Name of the event to remove the listener from.
8426	 * @param {Function} listener Method to remove from the event.
8427	 * @return {Object} Current instance of EventEmitter for chaining.
8428	 */
8429	proto.removeListener = function removeListener(evt, listener) {
8430		var listeners = this.getListenersAsObject(evt);
8431		var index;
8432		var key;
8433
8434		for (key in listeners) {
8435			if (listeners.hasOwnProperty(key)) {
8436				index = indexOfListener(listeners[key], listener);
8437
8438				if (index !== -1) {
8439					listeners[key].splice(index, 1);
8440				}
8441			}
8442		}
8443
8444		return this;
8445	};
8446
8447	/**
8448	 * Alias of removeListener
8449	 */
8450	proto.off = alias('removeListener');
8451
8452	/**
8453	 * Adds listeners in bulk using the manipulateListeners method.
8454	 * If you pass an object as the second argument you can add to multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. You can also pass it an event name and an array of listeners to be added.
8455	 * You can also pass it a regular expression to add the array of listeners to all events that match it.
8456	 * Yeah, this function does quite a bit. That's probably a bad thing.
8457	 *
8458	 * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add to multiple events at once.
8459	 * @param {Function[]} [listeners] An optional array of listener functions to add.
8460	 * @return {Object} Current instance of EventEmitter for chaining.
8461	 */
8462	proto.addListeners = function addListeners(evt, listeners) {
8463		// Pass through to manipulateListeners
8464		return this.manipulateListeners(false, evt, listeners);
8465	};
8466
8467	/**
8468	 * Removes listeners in bulk using the manipulateListeners method.
8469	 * If you pass an object as the second argument you can remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
8470	 * You can also pass it an event name and an array of listeners to be removed.
8471	 * You can also pass it a regular expression to remove the listeners from all events that match it.
8472	 *
8473	 * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to remove from multiple events at once.
8474	 * @param {Function[]} [listeners] An optional array of listener functions to remove.
8475	 * @return {Object} Current instance of EventEmitter for chaining.
8476	 */
8477	proto.removeListeners = function removeListeners(evt, listeners) {
8478		// Pass through to manipulateListeners
8479		return this.manipulateListeners(true, evt, listeners);
8480	};
8481
8482	/**
8483	 * Edits listeners in bulk. The addListeners and removeListeners methods both use this to do their job. You should really use those instead, this is a little lower level.
8484	 * The first argument will determine if the listeners are removed (true) or added (false).
8485	 * If you pass an object as the second argument you can add/remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
8486	 * You can also pass it an event name and an array of listeners to be added/removed.
8487	 * You can also pass it a regular expression to manipulate the listeners of all events that match it.
8488	 *
8489	 * @param {Boolean} remove True if you want to remove listeners, false if you want to add.
8490	 * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add/remove from multiple events at once.
8491	 * @param {Function[]} [listeners] An optional array of listener functions to add/remove.
8492	 * @return {Object} Current instance of EventEmitter for chaining.
8493	 */
8494	proto.manipulateListeners = function manipulateListeners(remove, evt, listeners) {
8495		var i;
8496		var value;
8497		var single = remove ? this.removeListener : this.addListener;
8498		var multiple = remove ? this.removeListeners : this.addListeners;
8499
8500		// If evt is an object then pass each of it's properties to this method
8501		if (typeof evt === 'object' && !(evt instanceof RegExp)) {
8502			for (i in evt) {
8503				if (evt.hasOwnProperty(i) && (value = evt[i])) {
8504					// Pass the single listener straight through to the singular method
8505					if (typeof value === 'function') {
8506						single.call(this, i, value);
8507					}
8508					else {
8509						// Otherwise pass back to the multiple function
8510						multiple.call(this, i, value);
8511					}
8512				}
8513			}
8514		}
8515		else {
8516			// So evt must be a string
8517			// And listeners must be an array of listeners
8518			// Loop over it and pass each one to the multiple method
8519			i = listeners.length;
8520			while (i--) {
8521				single.call(this, evt, listeners[i]);
8522			}
8523		}
8524
8525		return this;
8526	};
8527
8528	/**
8529	 * Removes all listeners from a specified event.
8530	 * If you do not specify an event then all listeners will be removed.
8531	 * That means every event will be emptied.
8532	 * You can also pass a regex to remove all events that match it.
8533	 *
8534	 * @param {String|RegExp} [evt] Optional name of the event to remove all listeners for. Will remove from every event if not passed.
8535	 * @return {Object} Current instance of EventEmitter for chaining.
8536	 */
8537	proto.removeEvent = function removeEvent(evt) {
8538		var type = typeof evt;
8539		var events = this._getEvents();
8540		var key;
8541
8542		// Remove different things depending on the state of evt
8543		if (type === 'string') {
8544			// Remove all listeners for the specified event
8545			delete events[evt];
8546		}
8547		else if (type === 'object') {
8548			// Remove all events matching the regex.
8549			for (key in events) {
8550				if (events.hasOwnProperty(key) && evt.test(key)) {
8551					delete events[key];
8552				}
8553			}
8554		}
8555		else {
8556			// Remove all listeners in all events
8557			delete this._events;
8558		}
8559
8560		return this;
8561	};
8562
8563	/**
8564	 * Alias of removeEvent.
8565	 *
8566	 * Added to mirror the node API.
8567	 */
8568	proto.removeAllListeners = alias('removeEvent');
8569
8570	/**
8571	 * Emits an event of your choice.
8572	 * When emitted, every listener attached to that event will be executed.
8573	 * If you pass the optional argument array then those arguments will be passed to every listener upon execution.
8574	 * Because it uses `apply`, your array of arguments will be passed as if you wrote them out separately.
8575	 * So they will not arrive within the array on the other side, they will be separate.
8576	 * You can also pass a regular expression to emit to all events that match it.
8577	 *
8578	 * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
8579	 * @param {Array} [args] Optional array of arguments to be passed to each listener.
8580	 * @return {Object} Current instance of EventEmitter for chaining.
8581	 */
8582	proto.emitEvent = function emitEvent(evt, args) {
8583		var listeners = this.getListenersAsObject(evt);
8584		var listener;
8585		var i;
8586		var key;
8587		var response;
8588
8589		for (key in listeners) {
8590			if (listeners.hasOwnProperty(key)) {
8591				i = listeners[key].length;
8592
8593				while (i--) {
8594					// If the listener returns true then it shall be removed from the event
8595					// The function is executed either with a basic call or an apply if there is an args array
8596					listener = listeners[key][i];
8597
8598					if (listener.once === true) {
8599						this.removeListener(evt, listener.listener);
8600					}
8601
8602					response = listener.listener.apply(this, args || []);
8603
8604					if (response === this._getOnceReturnValue()) {
8605						this.removeListener(evt, listener.listener);
8606					}
8607				}
8608			}
8609		}
8610
8611		return this;
8612	};
8613
8614	/**
8615	 * Alias of emitEvent
8616	 */
8617	proto.trigger = alias('emitEvent');
8618
8619	/**
8620	 * Subtly different from emitEvent in that it will pass its arguments on to the listeners, as opposed to taking a single array of arguments to pass on.
8621	 * As with emitEvent, you can pass a regex in place of the event name to emit to all events that match it.
8622	 *
8623	 * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
8624	 * @param {...*} Optional additional arguments to be passed to each listener.
8625	 * @return {Object} Current instance of EventEmitter for chaining.
8626	 */
8627	proto.emit = function emit(evt) {
8628		var args = Array.prototype.slice.call(arguments, 1);
8629		return this.emitEvent(evt, args);
8630	};
8631
8632	/**
8633	 * Sets the current value to check against when executing listeners. If a
8634	 * listeners return value matches the one set here then it will be removed
8635	 * after execution. This value defaults to true.
8636	 *
8637	 * @param {*} value The new value to check for when executing listeners.
8638	 * @return {Object} Current instance of EventEmitter for chaining.
8639	 */
8640	proto.setOnceReturnValue = function setOnceReturnValue(value) {
8641		this._onceReturnValue = value;
8642		return this;
8643	};
8644
8645	/**
8646	 * Fetches the current value to check against when executing listeners. If
8647	 * the listeners return value matches this one then it should be removed
8648	 * automatically. It will return true by default.
8649	 *
8650	 * @return {*|Boolean} The current value to check for or the default, true.
8651	 * @api private
8652	 */
8653	proto._getOnceReturnValue = function _getOnceReturnValue() {
8654		if (this.hasOwnProperty('_onceReturnValue')) {
8655			return this._onceReturnValue;
8656		}
8657		else {
8658			return true;
8659		}
8660	};
8661
8662	/**
8663	 * Fetches the events object and creates one if required.
8664	 *
8665	 * @return {Object} The events storage object.
8666	 * @api private
8667	 */
8668	proto._getEvents = function _getEvents() {
8669		return this._events || (this._events = {});
8670	};
8671
8672	/**
8673	 * Reverts the global {@link ngEventEmitter} to its previous value and returns a reference to this version.
8674	 *
8675	 * @return {Function} Non conflicting ngEventEmitter class.
8676	 */
8677	ngEventEmitter.noConflict = function noConflict() {
8678		exports.ngEventEmitter = originalGlobalValue;
8679		return ngEventEmitter;
8680	};
8681
8682	// Expose the class either via AMD, CommonJS or the global object
8683	if (typeof define === 'function' && define.amd) {
8684		define('ngEventEmitter/ngEventEmitter',[],function () {
8685			return ngEventEmitter;
8686		});
8687	}
8688	else if (typeof module === 'object' && module.exports){
8689		module.exports = ngEventEmitter;
8690	}
8691	else {
8692		this.ngEventEmitter = ngEventEmitter;
8693	}
8694}.call(this));
8695
8696/*!
8697 * eventie v1.0.4
8698 * event binding helper
8699 *   eventie.bind( elem, 'click', myFn )
8700 *   eventie.unbind( elem, 'click', myFn )
8701 */
8702
8703/*jshint browser: true, undef: true, unused: true */
8704/*global define: false */
8705
8706( function( window ) {
8707
8708
8709
8710var docElem = document.documentElement;
8711
8712var bind = function() {};
8713
8714function getIEEvent( obj ) {
8715  var event = window.event;
8716  // add event.target
8717  event.target = event.target || event.srcElement || obj;
8718  return event;
8719}
8720
8721if ( docElem.addEventListener ) {
8722  bind = function( obj, type, fn ) {
8723    obj.addEventListener( type, fn, false );
8724  };
8725} else if ( docElem.attachEvent ) {
8726  bind = function( obj, type, fn ) {
8727    obj[ type + fn ] = fn.handleEvent ?
8728      function() {
8729        var event = getIEEvent( obj );
8730        fn.handleEvent.call( fn, event );
8731      } :
8732      function() {
8733        var event = getIEEvent( obj );
8734        fn.call( obj, event );
8735      };
8736    obj.attachEvent( "on" + type, obj[ type + fn ] );
8737  };
8738}
8739
8740var unbind = function() {};
8741
8742if ( docElem.removeEventListener ) {
8743  unbind = function( obj, type, fn ) {
8744    obj.removeEventListener( type, fn, false );
8745  };
8746} else if ( docElem.detachEvent ) {
8747  unbind = function( obj, type, fn ) {
8748    obj.detachEvent( "on" + type, obj[ type + fn ] );
8749    try {
8750      delete obj[ type + fn ];
8751    } catch ( err ) {
8752      // can't delete window object properties
8753      obj[ type + fn ] = undefined;
8754    }
8755  };
8756}
8757
8758var eventie = {
8759  bind: bind,
8760  unbind: unbind
8761};
8762
8763// transport
8764if ( typeof define === 'function' && define.amd ) {
8765  // AMD
8766  define( 'eventie/eventie',eventie );
8767} else {
8768  // browser global
8769  window.eventie = eventie;
8770}
8771
8772})( this );
8773
8774/*!
8775 * imagesLoaded v3.1.8
8776 * JavaScript is all like "You images are done yet or what?"
8777 * MIT License
8778 */
8779
8780( function( window, factory ) { 
8781  // universal module definition
8782
8783  /*global define: false, module: false, require: false */
8784
8785  if ( typeof define === 'function' && define.amd ) {
8786    // AMD
8787    define( [
8788      'ngEventEmitter/ngEventEmitter',
8789      'eventie/eventie'
8790    ], function( ngEventEmitter, eventie ) {
8791      return factory( window, ngEventEmitter, eventie );
8792    });
8793  } else if ( typeof exports === 'object' ) {
8794    // CommonJS
8795    module.exports = factory(
8796      window,
8797      require('wolfy87-eventemitter'),
8798      require('eventie')
8799    );
8800  } else {
8801    // browser global
8802    window.ngimagesLoaded = factory(
8803      window,
8804      window.ngEventEmitter,
8805      window.eventie
8806    );
8807  }
8808
8809})( window,
8810
8811// --------------------------  factory -------------------------- //
8812
8813function factory( window, ngEventEmitter, eventie ) {
8814
8815
8816
8817var $ = window.jQuery;
8818var console = window.console;
8819var hasConsole = typeof console !== 'undefined';
8820
8821// -------------------------- helpers -------------------------- //
8822
8823// extend objects
8824function extend( a, b ) {
8825  for ( var prop in b ) {
8826    a[ prop ] = b[ prop ];
8827  }
8828  return a;
8829}
8830
8831var objToString = Object.prototype.toString;
8832function isArray( obj ) {
8833  return objToString.call( obj ) === '[object Array]';
8834}
8835
8836// turn element or nodeList into an array
8837function makeArray( obj ) {
8838  var ary = [];
8839  if ( isArray( obj ) ) {
8840    // use object if already an array
8841    ary = obj;
8842  } else if ( typeof obj.length === 'number' ) {
8843    // convert nodeList to array
8844    for ( var i=0, len = obj.length; i < len; i++ ) {
8845      ary.push( obj[i] );
8846    }
8847  } else {
8848    // array of single index
8849    ary.push( obj );
8850  }
8851  return ary;
8852}
8853
8854  // -------------------------- imagesLoaded -------------------------- //
8855
8856  /**
8857   * @param {Array, Element, NodeList, String} elem
8858   * @param {Object or Function} options - if function, use as callback
8859   * @param {Function} onAlways - callback function
8860   */
8861  function ngImagesLoaded( elem, options, onAlways ) {
8862    // coerce ImagesLoaded() without new, to be new ImagesLoaded()
8863    if ( !( this instanceof ngImagesLoaded ) ) {
8864      return new ngImagesLoaded( elem, options );
8865    }
8866    // use elem as selector string
8867    if ( typeof elem === 'string' ) {
8868      elem = document.querySelectorAll( elem );
8869    }
8870
8871    this.elements = makeArray( elem );
8872    this.options = extend( {}, this.options );
8873
8874    if ( typeof options === 'function' ) {
8875      onAlways = options;
8876    } else {
8877      extend( this.options, options );
8878    }
8879
8880    if ( onAlways ) {
8881      this.on( 'always', onAlways );
8882    }
8883
8884    this.getImages();
8885
8886    if ( $ ) {
8887      // add jQuery Deferred object
8888      this.jqDeferred = new $.Deferred();
8889    }
8890
8891    // HACK check async to allow time to bind listeners
8892    var _this = this;
8893    setTimeout( function() {
8894      _this.check();
8895    });
8896  }
8897
8898  ngImagesLoaded.prototype = new ngEventEmitter();
8899
8900  ngImagesLoaded.prototype.options = {};
8901
8902  ngImagesLoaded.prototype.getImages = function() {
8903    this.images = [];
8904
8905    // filter & find items if we have an item selector
8906    for ( var i=0, len = this.elements.length; i < len; i++ ) {
8907      var elem = this.elements[i];
8908      // filter siblings
8909      if ( elem.nodeName === 'IMG' ) {
8910        this.addImage( elem );
8911      }
8912      // find children
8913      // no non-element nodes, #143
8914      var nodeType = elem.nodeType;
8915      if ( !nodeType || !( nodeType === 1 || nodeType === 9 || nodeType === 11 ) ) {
8916        continue;
8917      }
8918      var childElems = elem.querySelectorAll('img');
8919      // concat childElems to filterFound array
8920      for ( var j=0, jLen = childElems.length; j < jLen; j++ ) {
8921        var img = childElems[j];
8922        this.addImage( img );
8923      }
8924    }
8925  };
8926
8927  /**
8928   * @param {Image} img
8929   */
8930  ngImagesLoaded.prototype.addImage = function( img ) {
8931    var loadingImage = new LoadingImage( img );
8932    this.images.push( loadingImage );
8933  };
8934
8935  ngImagesLoaded.prototype.check = function() {
8936    var _this = this;
8937    var checkedCount = 0;
8938    var length = this.images.length;
8939    this.hasAnyBroken = false;
8940    // complete if no images
8941    if ( !length ) {
8942      this.complete();
8943      return;
8944    }
8945
8946    function onConfirm( image, message ) {
8947      if ( _this.options.debug && hasConsole ) {
8948        console.log( 'confirm', image, message );
8949      }
8950
8951      _this.progress( image );
8952      checkedCount++;
8953      if ( checkedCount === length ) {
8954        _this.complete();
8955      }
8956      return true; // bind once
8957    }
8958
8959    for ( var i=0; i < length; i++ ) {
8960      var loadingImage = this.images[i];
8961      loadingImage.on( 'confirm', onConfirm );
8962      loadingImage.check();
8963    }
8964  };
8965
8966  ngImagesLoaded.prototype.progress = function( image ) {
8967    this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded;
8968    // HACK - Chrome triggers event before object properties have changed. #83
8969    var _this = this;
8970    setTimeout( function() {
8971      _this.emit( 'progress', _this, image );
8972      if ( _this.jqDeferred && _this.jqDeferred.notify ) {
8973        _this.jqDeferred.notify( _this, image );
8974      }
8975    });
8976  };
8977
8978  ngImagesLoaded.prototype.complete = function() {
8979    var eventName = this.hasAnyBroken ? 'fail' : 'done';
8980    this.isComplete = true;
8981    var _this = this;
8982    // HACK - another setTimeout so that confirm happens after progress
8983    setTimeout( function() {
8984      _this.emit( eventName, _this );
8985      _this.emit( 'always', _this );
8986      if ( _this.jqDeferred ) {
8987        var jqMethod = _this.hasAnyBroken ? 'reject' : 'resolve';
8988        _this.jqDeferred[ jqMethod ]( _this );
8989      }
8990    });
8991  };
8992
8993  // -------------------------- jquery -------------------------- //
8994
8995  if ( $ ) {
8996    $.fn.ngimagesLoaded = function( options, callback ) {
8997      var instance = new ngImagesLoaded( this, options, callback );
8998      return instance.jqDeferred.promise( $(this) );
8999    };
9000  }
9001
9002
9003  // --------------------------  -------------------------- //
9004
9005  function LoadingImage( img ) {
9006    this.img = img;
9007  }
9008
9009  LoadingImage.prototype = new ngEventEmitter();
9010
9011  LoadingImage.prototype.check = function() {
9012    // first check cached any previous images that have same src
9013    var resource = cache[ this.img.src ] || new Resource( this.img.src );
9014    if ( resource.isConfirmed ) {
9015      this.confirm( resource.isLoaded, 'cached was confirmed' );
9016      return;
9017    }
9018
9019    // If complete is true and browser supports natural sizes,
9020    // try to check for image status manually.
9021    if ( this.img.complete && this.img.naturalWidth !== undefined ) {
9022      // report based on naturalWidth
9023      this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' );
9024      return;
9025    }
9026
9027    // If none of the checks above matched, simulate loading on detached element.
9028    var _this = this;
9029    resource.on( 'confirm', function( resrc, message ) {
9030      _this.confirm( resrc.isLoaded, message );
9031      return true;
9032    });
9033
9034    resource.check();
9035  };
9036
9037  LoadingImage.prototype.confirm = function( isLoaded, message ) {
9038    this.isLoaded = isLoaded;
9039    this.emit( 'confirm', this, message );
9040  };
9041
9042  // -------------------------- Resource -------------------------- //
9043
9044  // Resource checks each src, only once
9045  // separate class from LoadingImage to prevent memory leaks. See #115
9046
9047  var cache = {};
9048
9049  function Resource( src ) {
9050    this.src = src;
9051    // add to cache
9052    cache[ src ] = this;
9053  }
9054
9055  Resource.prototype = new ngEventEmitter();
9056
9057  Resource.prototype.check = function() {
9058    // only trigger checking once
9059    if ( this.isChecked ) {
9060      return;
9061    }
9062    // simulate loading on detached element
9063    var proxyImage = new Image();
9064    eventie.bind( proxyImage, 'load', this );
9065    eventie.bind( proxyImage, 'error', this );
9066    proxyImage.src = this.src;
9067    // set flag
9068    this.isChecked = true;
9069  };
9070
9071  // ----- events ----- //
9072
9073  // trigger specified handler for event type
9074  Resource.prototype.handleEvent = function( event ) {
9075    var method = 'on' + event.type;
9076    if ( this[ method ] ) {
9077      this[ method ]( event );
9078    }
9079  };
9080
9081  Resource.prototype.onload = function( event ) {
9082    this.confirm( true, 'onload' );
9083    this.unbindProxyEvents( event );
9084  };
9085
9086  Resource.prototype.onerror = function( event ) {
9087    this.confirm( false, 'onerror' );
9088    this.unbindProxyEvents( event );
9089  };
9090
9091  // ----- confirm ----- //
9092
9093  Resource.prototype.confirm = function( isLoaded, message ) {
9094    this.isConfirmed = true;
9095    this.isLoaded = isLoaded;
9096    this.emit( 'confirm', this, message );
9097  };
9098
9099  Resource.prototype.unbindProxyEvents = function( event ) {
9100    eventie.unbind( event.target, 'load', this );
9101    eventie.unbind( event.target, 'error', this );
9102  };
9103
9104  // -----  ----- //
9105
9106  return ngImagesLoaded;
9107
9108});
9109
9110
9111
9112
9113
9114// screenfull.js
9115// v1.1.0
9116// by sindresorhus - https://github.com/sindresorhus
9117// from: https://github.com/sindresorhus/screenfull.js
9118
9119(function () {
9120	'use strict';
9121
9122	var isCommonjs = typeof module !== 'undefined' && module.exports;
9123	var keyboardAllowed = typeof Element !== 'undefined' && 'ALLOW_KEYBOARD_INPUT' in Element;
9124
9125	var fn = (function () {
9126		var val;
9127		var valLength;
9128
9129		var fnMap = [
9130			[
9131				'requestFullscreen',
9132				'exitFullscreen',
9133				'fullscreenElement',
9134				'fullscreenEnabled',
9135				'fullscreenchange',
9136				'fullscreenerror'
9137			],
9138			// new WebKit
9139			[
9140				'webkitRequestFullscreen',
9141				'webkitExitFullscreen',
9142				'webkitFullscreenElement',
9143				'webkitFullscreenEnabled',
9144				'webkitfullscreenchange',
9145				'webkitfullscreenerror'
9146
9147			],
9148			// old WebKit (Safari 5.1)
9149			[
9150				'webkitRequestFullScreen',
9151				'webkitCancelFullScreen',
9152				'webkitCurrentFullScreenElement',
9153				'webkitCancelFullScreen',
9154				'webkitfullscreenchange',
9155				'webkitfullscreenerror'
9156
9157			],
9158			[
9159				'mozRequestFullScreen',
9160				'mozCancelFullScreen',
9161				'mozFullScreenElement',
9162				'mozFullScreenEnabled',
9163				'mozfullscreenchange',
9164				'mozfullscreenerror'
9165			],
9166			[
9167				'msRequestFullscreen',
9168				'msExitFullscreen',
9169				'msFullscreenElement',
9170				'msFullscreenEnabled',
9171				'MSFullscreenChange',
9172				'MSFullscreenError'
9173			]
9174		];
9175
9176		var i = 0;
9177		var l = fnMap.length;
9178		var ret = {};
9179
9180		for (; i < l; i++) {
9181			val = fnMap[i];
9182			if (val && val[1] in document) {
9183				for (i = 0, valLength = val.length; i < valLength; i++) {
9184					ret[fnMap[0][i]] = val[i];
9185				}
9186				return ret;
9187			}
9188		}
9189
9190		return false;
9191	})();
9192
9193	var ngscreenfull = {
9194		request: function (elem) {
9195			var request = fn.requestFullscreen;
9196
9197			elem = elem || document.documentElement;
9198
9199			// Work around Safari 5.1 bug: reports support for
9200			// keyboard in fullscreen even though it doesn't.
9201			// Browser sniffing, since the alternative with
9202			// setTimeout is even worse.
9203			if (/5\.1[\.\d]* Safari/.test(navigator.userAgent)) {
9204				elem[request]();
9205			} else {
9206				elem[request](keyboardAllowed && Element.ALLOW_KEYBOARD_INPUT);
9207			}
9208		},
9209		exit: function () {
9210			document[fn.exitFullscreen]();
9211		},
9212		toggle: function (elem) {
9213			if (this.isFullscreen) {
9214				this.exit();
9215			} else {
9216				this.request(elem);
9217			}
9218		},
9219		onchange: function () {},
9220		onerror: function () {},
9221		raw: fn
9222	};
9223
9224	if (!fn) {
9225		if (isCommonjs) {
9226			module.exports = false;
9227		} else {
9228			window.ngscreenfull = false;
9229		}
9230
9231		return;
9232	}
9233
9234	Object.defineProperties(ngscreenfull, {
9235		isFullscreen: {
9236			get: function () {
9237				return !!document[fn.fullscreenElement];
9238			}
9239		},
9240		element: {
9241			enumerable: true,
9242			get: function () {
9243				return document[fn.fullscreenElement];
9244			}
9245		},
9246		enabled: {
9247			enumerable: true,
9248			get: function () {
9249				// Coerce to boolean in case of old WebKit
9250				return !!document[fn.fullscreenEnabled];
9251			}
9252		}
9253	});
9254
9255	document.addEventListener(fn.fullscreenchange, function (e) {
9256		ngscreenfull.onchange.call(ngscreenfull, e);
9257	});
9258
9259	document.addEventListener(fn.fullscreenerror, function (e) {
9260		ngscreenfull.onerror.call(ngscreenfull, e);
9261	});
9262
9263	if (isCommonjs) {
9264		module.exports = ngscreenfull;
9265	} else {
9266		window.ngscreenfull = ngscreenfull;
9267	}
9268})();
9269
9270
9271
9272
9273
9274
9275
9276
9277
9278
9279
9280
9281
9282
9283
9284
9285//}( jQuery ));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.