PageSourceSearch

https://member.mappi.or.id/album/jquery.nanogallery.js

js mappi.or.id collected 2026-10-04 12:51:35 UTC 323,944 bytes, 8,974 lines download raw bytes

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

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.