PageSourceSearch

https://mumbaipressclub.com/assets/js/jquery.nanogallery.js

js mumbaipressclub.com collected 2026-10-03 00:57:01 UTC 331,746 bytes, 9,277 lines download raw bytes

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